From 1c142459975a8703b6a5771f576a1e3736360954 Mon Sep 17 00:00:00 2001 From: Alexey Berezhok Date: Mon, 15 May 2023 19:07:27 +0300 Subject: [PATCH] Added yandex backend support(prebuild) --- .gitignore | 1 + .vscode/c_cpp_properties.json | 3 +- CMakeLists.txt | 11 + config.h.in | 6 + ...gnome.evolution-data-server.gschema.xml.in | 10 + po/POTFILES.in | 3 + src/camel/CMakeLists.txt | 2 + src/camel/camel-autocleanups.h | 1 + src/camel/camel-sasl-xoauth2-yandex.c | 44 ++ src/camel/camel-sasl-xoauth2-yandex.h | 63 ++ src/camel/camel-sasl.c | 2 + src/camel/camel.h | 1 + src/libebackend/e-webdav-collection-backend.c | 2 +- src/libedataserver/CMakeLists.txt | 2 + .../e-dataserver-autocleanups.h | 1 + src/libedataserver/e-oauth2-service-yandex.c | 214 ++++++ src/libedataserver/e-oauth2-service-yandex.h | 62 ++ src/libedataserver/e-oauth2-services.c | 2 + src/libedataserver/e-soup-auth-bearer.c | 22 +- src/libedataserver/e-soup-auth-bearer.h | 3 + src/libedataserver/e-soup-session.c | 22 + src/libedataserver/libedataserver.h | 1 + src/modules/CMakeLists.txt | 1 + .../module-gnome-online-accounts.c | 3 + src/modules/yandex-backend/CMakeLists.txt | 23 + .../yandex-backend/module-yandex-backend.c | 727 ++++++++++++++++++ 26 files changed, 1229 insertions(+), 3 deletions(-) create mode 100644 src/camel/camel-sasl-xoauth2-yandex.c create mode 100644 src/camel/camel-sasl-xoauth2-yandex.h create mode 100644 src/libedataserver/e-oauth2-service-yandex.c create mode 100644 src/libedataserver/e-oauth2-service-yandex.h create mode 100644 src/modules/yandex-backend/CMakeLists.txt create mode 100644 src/modules/yandex-backend/module-yandex-backend.c diff --git a/.gitignore b/.gitignore index 726890e..575a9f0 100644 --- a/.gitignore +++ b/.gitignore @@ -3,3 +3,4 @@ _build .build *.orig *.rej +.vscode/ diff --git a/.vscode/c_cpp_properties.json b/.vscode/c_cpp_properties.json index a070080..6cc85df 100644 --- a/.vscode/c_cpp_properties.json +++ b/.vscode/c_cpp_properties.json @@ -6,7 +6,8 @@ "${workspaceFolder}/**", "/usr/include/goa-1.0", "/usr/include/glib-2.0", - "/usr/lib64/glib-2.0/include/" + "/usr/lib64/glib-2.0/include/", + "/usr/include/libsoup-2.4" ], "defines": [], "compilerPath": "/usr/bin/gcc", diff --git a/CMakeLists.txt b/CMakeLists.txt index 2bad65e..d7deb4a 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -435,6 +435,17 @@ if(ENABLE_OAUTH2) if(WITH_YAHOO_CLIENT_SECRET STREQUAL "") set(WITH_YAHOO_CLIENT_SECRET "35f49f199dd754ec5e86d3c7cd576a1341c9bc0b") endif(WITH_YAHOO_CLIENT_SECRET STREQUAL "") + + add_printable_variable(WITH_YANDEX_CLIENT_ID "Yandex OAuth 2.0 client id" "") + add_printable_variable(WITH_YANDEX_CLIENT_SECRET "Yandex OAuth 2.0 client secret" "") + + if(WITH_YANDEX_CLIENT_ID STREQUAL "") + set(WITH_YANDEX_CLIENT_ID "8ef866ab7a1f4d3b8fcb58510d422fbc") + endif(WITH_YANDEX_CLIENT_ID STREQUAL "") + + if(WITH_YANDEX_CLIENT_SECRET STREQUAL "") + set(WITH_YANDEX_CLIENT_SECRET "aa8be6b692dc4465b1fa40ae6ec7a377") + endif(WITH_YANDEX_CLIENT_SECRET STREQUAL "") endif(ENABLE_OAUTH2) # ****************************************** diff --git a/config.h.in b/config.h.in index bd2978a..cdd864e 100644 --- a/config.h.in +++ b/config.h.in @@ -51,6 +51,12 @@ /* Define Yahoo! OAuth 2.0 Client Secret to use */ #define YAHOO_CLIENT_SECRET "@WITH_YAHOO_CLIENT_SECRET@" +/* Define Yandex OAuth 2.0 Client ID to use */ +#define YANDEX_CLIENT_ID "@WITH_YANDEX_CLIENT_ID@" + +/* Define Yandex OAuth 2.0 Client Secret to use */ +#define YANDEX_CLIENT_SECRET "@WITH_YANDEX_CLIENT_SECRET@" + /* Path to a sendmail binary, or equivalent */ #define SENDMAIL_PATH "@SENDMAIL_PATH@" diff --git a/data/org.gnome.evolution-data-server.gschema.xml.in b/data/org.gnome.evolution-data-server.gschema.xml.in index 98defd4..5d5b2bb 100644 --- a/data/org.gnome.evolution-data-server.gschema.xml.in +++ b/data/org.gnome.evolution-data-server.gschema.xml.in @@ -81,5 +81,15 @@ <_summary>An OAuth2 client secret to use to connect to Yahoo! servers, instead of the one provided during build time <_description>User-specified OAuth2 client secret for Yahoo! servers. Empty string means to use the one provided during build time. Change of this requires restart. + + '' + <_summary>An OAuth2 client ID to use to connect to Yandex servers, instead of the one provided during build time + <_description>User-specified OAuth2 client ID for Yandex servers. Empty string means to use the one provided during build time. Change of this requires restart. + + + '' + <_summary>An OAuth2 client secret to use to connect to Yandex servers, instead of the one provided during build time + <_description>User-specified OAuth2 client secret for Yandex servers. Empty string means to use the one provided during build time. Change of this requires restart. + diff --git a/po/POTFILES.in b/po/POTFILES.in index 9a25ab5..10dfcf8 100644 --- a/po/POTFILES.in +++ b/po/POTFILES.in @@ -118,6 +118,7 @@ src/camel/camel-sasl-xoauth2.c src/camel/camel-sasl-xoauth2-google.c src/camel/camel-sasl-xoauth2-outlook.c src/camel/camel-sasl-xoauth2-yahoo.c +src/camel/camel-sasl-xoauth2-yandex.c src/camel/camel-search-private.c src/camel/camel-service.c src/camel/camel-session.c @@ -200,6 +201,7 @@ src/libedataserver/e-oauth2-service.c src/libedataserver/e-oauth2-service-google.c src/libedataserver/e-oauth2-service-outlook.c src/libedataserver/e-oauth2-service-yahoo.c +src/libedataserver/e-oauth2-service-yandex.c src/libedataserver/e-soup-session.c src/libedataserver/e-source.c src/libedataserver/e-source-credentials-provider-impl.c @@ -225,6 +227,7 @@ src/modules/google-backend/module-google-backend.c src/modules/trust-prompt/module-trust-prompt.c src/modules/trust-prompt/trust-prompt-gtk.c src/modules/yahoo-backend/module-yahoo-backend.c +src/modules/yandex-backend/module-yandex-backend.c src/services/evolution-addressbook-factory/evolution-addressbook-factory.c src/services/evolution-alarm-notify/e-alarm-notify.c src/services/evolution-calendar-factory/evolution-calendar-factory.c diff --git a/src/camel/CMakeLists.txt b/src/camel/CMakeLists.txt index 2ba609b..26b2033 100644 --- a/src/camel/CMakeLists.txt +++ b/src/camel/CMakeLists.txt @@ -108,6 +108,7 @@ set(SOURCES camel-sasl-xoauth2-google.c camel-sasl-xoauth2-outlook.c camel-sasl-xoauth2-yahoo.c + camel-sasl-xoauth2-yandex.c camel-sasl.c camel-search-private.c camel-search-sql-sexp.c @@ -250,6 +251,7 @@ set(HEADERS camel-sasl-xoauth2-google.h camel-sasl-xoauth2-outlook.h camel-sasl-xoauth2-yahoo.h + camel-sasl-xoauth2-yandex.h camel-sasl.h camel-search-private.h camel-search-sql-sexp.h diff --git a/src/camel/camel-autocleanups.h b/src/camel/camel-autocleanups.h index 0ff82a2..fb6595a 100644 --- a/src/camel/camel-autocleanups.h +++ b/src/camel/camel-autocleanups.h @@ -111,6 +111,7 @@ G_DEFINE_AUTOPTR_CLEANUP_FUNC(CamelSaslPlain, g_object_unref) G_DEFINE_AUTOPTR_CLEANUP_FUNC(CamelSaslPOPB4SMTP, g_object_unref) G_DEFINE_AUTOPTR_CLEANUP_FUNC(CamelSaslXOAuth2, g_object_unref) G_DEFINE_AUTOPTR_CLEANUP_FUNC(CamelSaslXOAuth2Google, g_object_unref) +G_DEFINE_AUTOPTR_CLEANUP_FUNC(CamelSaslXOAuth2Yandex, g_object_unref) G_DEFINE_AUTOPTR_CLEANUP_FUNC(CamelSaslXOAuth2Outlook, g_object_unref) G_DEFINE_AUTOPTR_CLEANUP_FUNC(CamelService, g_object_unref) G_DEFINE_AUTOPTR_CLEANUP_FUNC(CamelServiceAuthType, camel_service_auth_type_free) diff --git a/src/camel/camel-sasl-xoauth2-yandex.c b/src/camel/camel-sasl-xoauth2-yandex.c new file mode 100644 index 0000000..234839a --- /dev/null +++ b/src/camel/camel-sasl-xoauth2-yandex.c @@ -0,0 +1,44 @@ +/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */ +/* + * This library is free software: you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation. + * + * This library is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License + * for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this library. If not, see . + */ + +#include "evolution-data-server-config.h" + +#include + +#include "camel-sasl-xoauth2-yandex.h" + +static CamelServiceAuthType sasl_xoauth2_yandex_auth_type = { + N_("OAuth2 (Yandex)"), + N_("This option will use an OAuth 2.0 " + "access token to connect to the yandex server"), + "Yandex", + FALSE +}; + +G_DEFINE_TYPE (CamelSaslXOAuth2Yandex, camel_sasl_xoauth2_yandex, CAMEL_TYPE_SASL_XOAUTH2) + +static void +camel_sasl_xoauth2_yandex_class_init (CamelSaslXOAuth2YandexClass *klass) +{ + CamelSaslClass *sasl_class; + + sasl_class = CAMEL_SASL_CLASS (klass); + sasl_class->auth_type = &sasl_xoauth2_yandex_auth_type; +} + +static void +camel_sasl_xoauth2_yandex_init (CamelSaslXOAuth2Yandex *sasl) +{ +} diff --git a/src/camel/camel-sasl-xoauth2-yandex.h b/src/camel/camel-sasl-xoauth2-yandex.h new file mode 100644 index 0000000..0141ef9 --- /dev/null +++ b/src/camel/camel-sasl-xoauth2-yandex.h @@ -0,0 +1,63 @@ +/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */ +/* + * This library is free software: you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation. + * + * This library is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License + * for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this library. If not, see . + */ + +#if !defined (__CAMEL_H_INSIDE__) && !defined (CAMEL_COMPILATION) +#error "Only can be included directly." +#endif + +#ifndef CAMEL_SASL_XOAUTH2_YANDEX_H +#define CAMEL_SASL_XOAUTH2_YANDEX_H + +#include + +/* Standard GObject macros */ +#define CAMEL_TYPE_SASL_XOAUTH2_YANDEX \ + (camel_sasl_xoauth2_yandex_get_type ()) +#define CAMEL_SASL_XOAUTH2_YANDEX(obj) \ + (G_TYPE_CHECK_INSTANCE_CAST \ + ((obj), CAMEL_TYPE_SASL_XOAUTH2_YANDEX, CamelSaslXOAuth2Yandex)) +#define CAMEL_SASL_XOAUTH2_YANDEX_CLASS(cls) \ + (G_TYPE_CHECK_CLASS_CAST \ + ((cls), CAMEL_TYPE_SASL_XOAUTH2_YANDEX, CamelSaslXOAuth2YandexClass)) +#define CAMEL_IS_SASL_XOAUTH2_YANDEX(obj) \ + (G_TYPE_CHECK_INSTANCE_TYPE \ + ((obj), CAMEL_TYPE_SASL_XOAUTH2_YANDEX)) +#define CAMEL_IS_SASL_XOAUTH2_YANDEX_CLASS(cls) \ + (G_TYPE_CHECK_CLASS_TYPE \ + ((cls), CAMEL_TYPE_SASL_XOAUTH2_YANDEX)) +#define CAMEL_SASL_XOAUTH2_YANDEX_GET_CLASS(obj) \ + (G_TYPE_INSTANCE_GET_CLASS \ + ((obj), CAMEL_TYPE_SASL_XOAUTH2_YANDEX, CamelSaslXOAuth2YandexClass)) + +G_BEGIN_DECLS + +typedef struct _CamelSaslXOAuth2Yandex CamelSaslXOAuth2Yandex; +typedef struct _CamelSaslXOAuth2YandexClass CamelSaslXOAuth2YandexClass; +typedef struct _CamelSaslXOAuth2YandexPrivate CamelSaslXOAuth2YandexPrivate; + +struct _CamelSaslXOAuth2Yandex { + CamelSaslXOAuth2 parent; + CamelSaslXOAuth2YandexPrivate *priv; +}; + +struct _CamelSaslXOAuth2YandexClass { + CamelSaslXOAuth2Class parent_class; +}; + +GType camel_sasl_xoauth2_yandex_get_type (void) G_GNUC_CONST; + +G_END_DECLS + +#endif /* CAMEL_SASL_XOAUTH2_YANDEX_H */ diff --git a/src/camel/camel-sasl.c b/src/camel/camel-sasl.c index 0a09c55..82cc3cb 100644 --- a/src/camel/camel-sasl.c +++ b/src/camel/camel-sasl.c @@ -35,6 +35,7 @@ #include "camel-sasl-xoauth2-google.h" #include "camel-sasl-xoauth2-outlook.h" #include "camel-sasl-xoauth2-yahoo.h" +#include "camel-sasl-xoauth2-yandex.h" #include "camel-sasl.h" #include "camel-service.h" @@ -133,6 +134,7 @@ sasl_build_class_table (void) g_type_ensure (CAMEL_TYPE_SASL_XOAUTH2_GOOGLE); g_type_ensure (CAMEL_TYPE_SASL_XOAUTH2_OUTLOOK); g_type_ensure (CAMEL_TYPE_SASL_XOAUTH2_YAHOO); + g_type_ensure (CAMEL_TYPE_SASL_XOAUTH2_YANDEX); class_table = g_hash_table_new_full ( (GHashFunc) g_str_hash, diff --git a/src/camel/camel.h b/src/camel/camel.h index 0df1baf..9c71ab7 100644 --- a/src/camel/camel.h +++ b/src/camel/camel.h @@ -110,6 +110,7 @@ #include #include #include +#include #include #include #include diff --git a/src/libebackend/e-webdav-collection-backend.c b/src/libebackend/e-webdav-collection-backend.c index b99f175..abc3829 100644 --- a/src/libebackend/e-webdav-collection-backend.c +++ b/src/libebackend/e-webdav-collection-backend.c @@ -360,7 +360,7 @@ webdav_collection_backend_populate (ECollectionBackend *collection) auth_extension = e_source_get_extension (source, E_SOURCE_EXTENSION_AUTHENTICATION); method = e_source_authentication_dup_method (auth_extension); user = e_source_authentication_dup_user (auth_extension); - needs_credentials = user && *user && g_strcmp0 (method, "OAuth2") != 0 && + needs_credentials = user && *user && (g_strcmp0 (method, "OAuth2") != 0 || g_strcmp0 (method, "Yandex") != 0) && !e_oauth2_services_is_oauth2_alias (e_source_registry_server_get_oauth2_services (server), method); g_free (method); g_free (user); diff --git a/src/libedataserver/CMakeLists.txt b/src/libedataserver/CMakeLists.txt index b196ae2..49a8471 100644 --- a/src/libedataserver/CMakeLists.txt +++ b/src/libedataserver/CMakeLists.txt @@ -69,6 +69,7 @@ set(SOURCES e-oauth2-service-google.c e-oauth2-service-outlook.c e-oauth2-service-yahoo.c + e-oauth2-service-yandex.c e-oauth2-services.c e-operation-pool.c e-proxy.c @@ -160,6 +161,7 @@ set(HEADERS e-oauth2-service-google.h e-oauth2-service-outlook.h e-oauth2-service-yahoo.h + e-oauth2-service-yandex.h e-oauth2-services.h e-operation-pool.h e-proxy.h diff --git a/src/libedataserver/e-dataserver-autocleanups.h b/src/libedataserver/e-dataserver-autocleanups.h index bc3c81a..93b4e14 100644 --- a/src/libedataserver/e-dataserver-autocleanups.h +++ b/src/libedataserver/e-dataserver-autocleanups.h @@ -39,6 +39,7 @@ G_DEFINE_AUTOPTR_CLEANUP_FUNC(ENetworkMonitor, g_object_unref) G_DEFINE_AUTOPTR_CLEANUP_FUNC(EOAuth2Service, g_object_unref) G_DEFINE_AUTOPTR_CLEANUP_FUNC(EOAuth2ServiceBase, g_object_unref) G_DEFINE_AUTOPTR_CLEANUP_FUNC(EOAuth2ServiceGoogle, g_object_unref) +G_DEFINE_AUTOPTR_CLEANUP_FUNC(EOAuth2ServiceYandex, g_object_unref) G_DEFINE_AUTOPTR_CLEANUP_FUNC(EOAuth2ServiceOutlook, g_object_unref) G_DEFINE_AUTOPTR_CLEANUP_FUNC(EOAuth2Services, g_object_unref) G_DEFINE_AUTOPTR_CLEANUP_FUNC(EOperationPool, e_operation_pool_free) diff --git a/src/libedataserver/e-oauth2-service-yandex.c b/src/libedataserver/e-oauth2-service-yandex.c new file mode 100644 index 0000000..64ac387 --- /dev/null +++ b/src/libedataserver/e-oauth2-service-yandex.c @@ -0,0 +1,214 @@ +/* + * Copyright (C) 2018 Red Hat, Inc. (www.redhat.com) + * + * This library is free software: you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation. + * + * This library is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License + * for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this library. If not, see . + */ + +#include "evolution-data-server-config.h" + +#include + +#include "e-oauth2-service.h" +#include "e-oauth2-service-base.h" + +#include "e-oauth2-service-yandex.h" + +/* Forward Declarations */ +static void e_oauth2_service_yandex_oauth2_service_init (EOAuth2ServiceInterface *iface); + +G_DEFINE_TYPE_WITH_CODE (EOAuth2ServiceYandex, e_oauth2_service_yandex, E_TYPE_OAUTH2_SERVICE_BASE, + G_IMPLEMENT_INTERFACE (E_TYPE_OAUTH2_SERVICE, e_oauth2_service_yandex_oauth2_service_init)) + +static gboolean +eos_yandex_guess_can_process (EOAuth2Service *service, + const gchar *protocol, + const gchar *hostname) +{ + return hostname && ( + e_util_utf8_strstrcase (hostname, ".yandex.ru") || + e_util_utf8_strstrcase (hostname, ".ya.ru") || + e_util_utf8_strstrcase (hostname, ".yandex.com")); +} + +static const gchar * +eos_yandex_get_name (EOAuth2Service *service) +{ + return "Yandex"; +} + +static const gchar * +eos_yandex_get_display_name (EOAuth2Service *service) +{ + /* Translators: This is a user-visible string, display name of an OAuth2 service. */ + return C_("OAuth2Service", "yandex"); +} + +static const gchar * +eos_yandex_read_settings (EOAuth2Service *service, + const gchar *key_name) +{ + G_LOCK_DEFINE_STATIC (user_settings); + gchar *value; + + G_LOCK (user_settings); + + value = g_object_get_data (G_OBJECT (service), key_name); + if (!value) { + GSettings *settings; + + settings = g_settings_new ("org.gnome.evolution-data-server"); + value = g_settings_get_string (settings, key_name); + g_object_unref (settings); + + if (value && *value) { + g_object_set_data_full (G_OBJECT (service), key_name, value, g_free); + } else { + g_free (value); + value = (gchar *) ""; + + g_object_set_data (G_OBJECT (service), key_name, value); + } + } + + G_UNLOCK (user_settings); + + return value; +} + +static const gchar * +eos_yandex_get_client_id (EOAuth2Service *service, + ESource *source) +{ + const gchar *client_id; + + client_id = eos_yandex_read_settings (service, "oauth2-yandex-client-id"); + + if (client_id && *client_id) + return client_id; + + return YANDEX_CLIENT_ID; +} + +static const gchar * +eos_yandex_get_client_secret (EOAuth2Service *service, + ESource *source) +{ + const gchar *client_secret; + + client_secret = eos_yandex_read_settings (service, "oauth2-yandex-client-secret"); + + if (client_secret && *client_secret) + return client_secret; + + return YANDEX_CLIENT_SECRET; +} + +static const gchar * +eos_yandex_get_authentication_uri (EOAuth2Service *service, + ESource *source) +{ + return "https://oauth.yandex.ru/authorize"; +} + +static const gchar * +eos_yandex_get_refresh_uri (EOAuth2Service *service, + ESource *source) +{ + return "https://oauth.yandex.ru/token"; +} + +static void +eos_yandex_prepare_authentication_uri_query (EOAuth2Service *service, + ESource *source, + GHashTable *uri_query) +{ + const gchar *YANDEX_SCOPE = + /* GMail IMAP and SMTP access */ + "login:email login:info mail:imap_full mail:imap_ro mail:smtp calendar:all"; + + g_return_if_fail (uri_query != NULL); + + e_oauth2_service_util_set_to_form (uri_query, "scope", YANDEX_SCOPE); + e_oauth2_service_util_set_to_form (uri_query, "include_granted_scopes", "false"); +} + +static gboolean +eos_yandex_extract_authorization_code (EOAuth2Service *service, + ESource *source, + const gchar *page_title, + const gchar *page_uri, + const gchar *page_content, + gchar **out_authorization_code) +{ + g_return_val_if_fail (out_authorization_code != NULL, FALSE); + + *out_authorization_code = NULL; + + if (page_uri && *page_uri) { + SoupURI *suri; + + suri = soup_uri_new (page_uri); + if (suri) { + const gchar *query = soup_uri_get_query (suri); + gboolean known = FALSE; + + if (query && *query) { + GHashTable *params; + + params = soup_form_decode (query); + if (params) { + const gchar *response; + + response = g_hash_table_lookup (params, "code"); + if (response) { + *out_authorization_code = g_strdup (response); + known = TRUE; + } + + g_hash_table_destroy (params); + } + } + + soup_uri_free (suri); + + if (known) + return TRUE; + } + } + + return FALSE; +} + +static void +e_oauth2_service_yandex_oauth2_service_init (EOAuth2ServiceInterface *iface) +{ + iface->guess_can_process = eos_yandex_guess_can_process; + iface->get_name = eos_yandex_get_name; + iface->get_display_name = eos_yandex_get_display_name; + iface->get_client_id = eos_yandex_get_client_id; + iface->get_client_secret = eos_yandex_get_client_secret; + iface->get_authentication_uri = eos_yandex_get_authentication_uri; + iface->get_refresh_uri = eos_yandex_get_refresh_uri; + iface->prepare_authentication_uri_query = eos_yandex_prepare_authentication_uri_query; + iface->extract_authorization_code = eos_yandex_extract_authorization_code; +} + +static void +e_oauth2_service_yandex_class_init (EOAuth2ServiceYandexClass *klass) +{ +} + +static void +e_oauth2_service_yandex_init (EOAuth2ServiceYandex *oauth2_google) +{ +} diff --git a/src/libedataserver/e-oauth2-service-yandex.h b/src/libedataserver/e-oauth2-service-yandex.h new file mode 100644 index 0000000..0011fd9 --- /dev/null +++ b/src/libedataserver/e-oauth2-service-yandex.h @@ -0,0 +1,62 @@ +/* + * Copyright (C) 2018 Red Hat, Inc. (www.redhat.com) + * + * This library is free software: you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation. + * + * This library is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License + * for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this library. If not, see . + */ + +#if !defined (__LIBEDATASERVER_H_INSIDE__) && !defined (LIBEDATASERVER_COMPILATION) +#error "Only should be included directly." +#endif + +#ifndef E_OAUTH2_SERVICE_YANDEX_H +#define E_OAUTH2_SERVICE_YANDEX_H + +#include + +/* Standard GObject macros */ +#define E_TYPE_OAUTH2_SERVICE_YANDEX \ + (e_oauth2_service_yandex_get_type ()) +#define E_OAUTH2_SERVICE_YANDEX(obj) \ + (G_TYPE_CHECK_INSTANCE_CAST \ + ((obj), E_TYPE_OAUTH2_SERVICE_YANDEX, EOAuth2ServiceYandex)) +#define E_OAUTH2_SERVICE_YANDEX_CLASS(cls) \ + (G_TYPE_CHECK_CLASS_CAST \ + ((cls), E_TYPE_OAUTH2_SERVICE_YANDEX, EOAuth2ServiceYandexClass)) +#define E_IS_OAUTH2_SERVICE_YANDEX(obj) \ + (G_TYPE_CHECK_INSTANCE_TYPE \ + ((obj), E_TYPE_OAUTH2_SERVICE_YANDEX)) +#define E_IS_OAUTH2_SERVICE_YANDEX_CLASS(cls) \ + (G_TYPE_CHECK_CLASS_TYPE \ + ((cls), E_TYPE_OAUTH2_SERVICE_YANDEX)) +#define E_OAUTH2_SERVICE_YANDEX_GET_CLASS(obj) \ + (G_TYPE_INSTANCE_GET_CLASS \ + ((obj), E_TYPE_OAUTH2_SERVICE_YANDEX, EOAuth2ServiceYandexClass)) + +G_BEGIN_DECLS + +typedef struct _EOAuth2ServiceYandex EOAuth2ServiceYandex; +typedef struct _EOAuth2ServiceYandexClass EOAuth2ServiceYandexClass; + +struct _EOAuth2ServiceYandex { + EOAuth2ServiceBase parent; +}; + +struct _EOAuth2ServiceYandexClass { + EOAuth2ServiceBaseClass parent_class; +}; + +GType e_oauth2_service_yandex_get_type (void) G_GNUC_CONST; + +G_END_DECLS + +#endif /* E_OAUTH2_SERVICE_YANDEX_H */ diff --git a/src/libedataserver/e-oauth2-services.c b/src/libedataserver/e-oauth2-services.c index 838b007..3679cea 100644 --- a/src/libedataserver/e-oauth2-services.c +++ b/src/libedataserver/e-oauth2-services.c @@ -36,6 +36,7 @@ /* Known built-in implementations */ #include "e-oauth2-service-google.h" +#include "e-oauth2-service-yandex.h" #include "e-oauth2-service-outlook.h" #include "e-oauth2-service-yahoo.h" @@ -136,6 +137,7 @@ e_oauth2_services_class_init (EOAuth2ServicesClass *klass) /* Ensure built-in service types are registered */ g_type_ensure (E_TYPE_OAUTH2_SERVICE_GOOGLE); + g_type_ensure (E_TYPE_OAUTH2_SERVICE_YANDEX); g_type_ensure (E_TYPE_OAUTH2_SERVICE_OUTLOOK); g_type_ensure (E_TYPE_OAUTH2_SERVICE_YAHOO); } diff --git a/src/libedataserver/e-soup-auth-bearer.c b/src/libedataserver/e-soup-auth-bearer.c index 78fd131..0d4ee85 100644 --- a/src/libedataserver/e-soup-auth-bearer.c +++ b/src/libedataserver/e-soup-auth-bearer.c @@ -39,11 +39,14 @@ #define AUTH_STRENGTH 1 #define EXPIRY_INVALID ((time_t) -1) +#define DEFAULT_BEARER_AUTH (gchar)0 +#define MAX_CUSTOM_BEARER_ID 16 struct _ESoupAuthBearerPrivate { GMutex property_lock; gchar *access_token; time_t expiry; + gchar is_custom_bearer[MAX_CUSTOM_BEARER_ID]; }; G_DEFINE_TYPE_WITH_PRIVATE ( @@ -138,7 +141,10 @@ e_soup_auth_bearer_get_authorization (SoupAuth *auth, g_mutex_lock (&bearer->priv->property_lock); - res = g_strdup_printf ("Bearer %s", bearer->priv->access_token); + if (!bearer->priv->is_custom_bearer[0]) + res = g_strdup_printf ("Bearer %s", bearer->priv->access_token); + else + res = g_strdup_printf ("%s %s", bearer->priv->is_custom_bearer, bearer->priv->access_token); g_mutex_unlock (&bearer->priv->property_lock); @@ -171,6 +177,7 @@ e_soup_auth_bearer_init (ESoupAuthBearer *bearer) { bearer->priv = e_soup_auth_bearer_get_instance_private (bearer); bearer->priv->expiry = EXPIRY_INVALID; + bearer->priv->is_custom_bearer[0] = DEFAULT_BEARER_AUTH; g_mutex_init (&bearer->priv->property_lock); } @@ -249,3 +256,16 @@ e_soup_auth_bearer_is_expired (ESoupAuthBearer *bearer) return expired; } + +gboolean +e_soup_auth_bearer_set_custom_bearer_name (ESoupAuthBearer *bearer, const gchar *bearer_name) +{ + if (!bearer) return TRUE; + g_return_val_if_fail (E_IS_SOUP_AUTH_BEARER (bearer), TRUE); + + g_mutex_lock (&bearer->priv->property_lock); + g_utf8_strncpy(bearer->priv->is_custom_bearer, bearer_name, MAX_CUSTOM_BEARER_ID-1); + g_mutex_unlock (&bearer->priv->property_lock); + + return TRUE; +} diff --git a/src/libedataserver/e-soup-auth-bearer.h b/src/libedataserver/e-soup-auth-bearer.h index 545bf02..b08fd37 100644 --- a/src/libedataserver/e-soup-auth-bearer.h +++ b/src/libedataserver/e-soup-auth-bearer.h @@ -73,6 +73,9 @@ void e_soup_auth_bearer_set_access_token const gchar *access_token, gint expires_in_seconds); gboolean e_soup_auth_bearer_is_expired (ESoupAuthBearer *bearer); +gboolean +e_soup_auth_bearer_set_custom_bearer_name (ESoupAuthBearer *bearer, +const gchar *bearer_name); G_END_DECLS diff --git a/src/libedataserver/e-soup-session.c b/src/libedataserver/e-soup-session.c index 381b9f8..a633988 100644 --- a/src/libedataserver/e-soup-session.c +++ b/src/libedataserver/e-soup-session.c @@ -163,16 +163,32 @@ e_soup_session_maybe_prepare_bearer_auth (ESoupSession *session, GError **error) { gboolean success; + gchar *auth_method = NULL; + ESource *source; g_return_val_if_fail (E_IS_SOUP_SESSION (session), FALSE); g_return_val_if_fail (soup_uri != NULL, FALSE); g_mutex_lock (&session->priv->property_lock); + source = e_soup_session_get_source (session); + if (source && e_source_has_extension (source, E_SOURCE_EXTENSION_AUTHENTICATION)) { + ESourceAuthentication *extension; + + extension = e_source_get_extension (source, E_SOURCE_EXTENSION_AUTHENTICATION); + auth_method = e_source_authentication_dup_method (extension); + } + if (session->priv->using_bearer_auth) { ESoupAuthBearer *using_bearer_auth = g_object_ref (session->priv->using_bearer_auth); g_mutex_unlock (&session->priv->property_lock); + if (auth_method){ + if (!g_strcmp0(auth_method, "Yandex")){ + e_soup_auth_bearer_set_custom_bearer_name (using_bearer_auth, "OAuth"); + } + } + success = e_soup_session_setup_bearer_auth (session, message, FALSE, using_bearer_auth, cancellable, error); g_clear_object (&using_bearer_auth); @@ -185,6 +201,12 @@ e_soup_session_maybe_prepare_bearer_auth (ESoupSession *session, E_TYPE_SOUP_AUTH_BEARER, SOUP_AUTH_HOST, soup_uri->host, NULL); + if (auth_method){ + if (!g_strcmp0(auth_method, "Yandex")){ + e_soup_auth_bearer_set_custom_bearer_name (E_SOUP_AUTH_BEARER(soup_auth), "OAuth"); + } + } + success = e_soup_session_setup_bearer_auth (session, message, FALSE, E_SOUP_AUTH_BEARER (soup_auth), cancellable, error); if (success) { g_mutex_lock (&session->priv->property_lock); diff --git a/src/libedataserver/libedataserver.h b/src/libedataserver/libedataserver.h index 34e71cb..e573344 100644 --- a/src/libedataserver/libedataserver.h +++ b/src/libedataserver/libedataserver.h @@ -41,6 +41,7 @@ #include #include #include +#include #include #include #include diff --git a/src/modules/CMakeLists.txt b/src/modules/CMakeLists.txt index d43357c..f4fafce 100644 --- a/src/modules/CMakeLists.txt +++ b/src/modules/CMakeLists.txt @@ -67,6 +67,7 @@ add_subdirectory(google-backend) add_subdirectory(outlook-backend) add_subdirectory(webdav-backend) add_subdirectory(yahoo-backend) +add_subdirectory(yandex-backend) if(ENABLE_OAUTH2) add_subdirectory(oauth2-services) diff --git a/src/modules/gnome-online-accounts/module-gnome-online-accounts.c b/src/modules/gnome-online-accounts/module-gnome-online-accounts.c index 03c4930..9d10d6d 100644 --- a/src/modules/gnome-online-accounts/module-gnome-online-accounts.c +++ b/src/modules/gnome-online-accounts/module-gnome-online-accounts.c @@ -120,6 +120,9 @@ gnome_online_accounts_get_backend_name (const gchar *goa_provider_type) if (g_str_equal (goa_provider_type, "google")) eds_backend_name = "google"; + + if (g_str_equal (goa_provider_type, "yandex")) + eds_backend_name = "yandex"; if (g_str_equal (goa_provider_type, "imap_smtp")) eds_backend_name = "none"; diff --git a/src/modules/yandex-backend/CMakeLists.txt b/src/modules/yandex-backend/CMakeLists.txt new file mode 100644 index 0000000..2416fa1 --- /dev/null +++ b/src/modules/yandex-backend/CMakeLists.txt @@ -0,0 +1,23 @@ +set(extra_deps) +if(HAVE_LIBGDATA) + set(sources + module-yandex-backend.c + ) +else(HAVE_LIBGDATA) + set(sources + module-yandex-backend.c + ) +endif(HAVE_LIBGDATA) +set(extra_defines) +set(extra_cflags ${LIBGDATA_CFLAGS}) +set(extra_incdirs ${LIBGDATA_INCLUDE_DIRS}) +set(extra_ldflags ${LIBGDATA_LDFLAGS}) + +add_source_registry_module(module-yandex-backend + sources + extra_deps + extra_defines + extra_cflags + extra_incdirs + extra_ldflags +) diff --git a/src/modules/yandex-backend/module-yandex-backend.c b/src/modules/yandex-backend/module-yandex-backend.c new file mode 100644 index 0000000..bb6dfc1 --- /dev/null +++ b/src/modules/yandex-backend/module-yandex-backend.c @@ -0,0 +1,727 @@ +/* + * module-yandex-backend.c + * + * This library is free software: you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation. + * + * This library is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License + * for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this library. If not, see . + * + */ + +#include "evolution-data-server-config.h" + +#include + +#include + +/* Standard GObject macros */ +#define E_TYPE_YANDEX_BACKEND \ + (e_yandex_backend_get_type ()) +#define E_YANDEX_BACKEND(obj) \ + (G_TYPE_CHECK_INSTANCE_CAST \ + ((obj), E_TYPE_YANDEX_BACKEND, EYandexBackend)) + +/* Just for readability... */ +#define METHOD(x) (CAMEL_NETWORK_SECURITY_METHOD_##x) + +/* IMAP Configuration Details */ +#define YANDEX_IMAP_BACKEND_NAME "imapx" +#define YANDEX_IMAP_HOST "imap.yandex.ru" +#define YANDEX_IMAP_PORT 993 +#define YANDEX_IMAP_SECURITY_METHOD METHOD (SSL_ON_ALTERNATE_PORT) + +/* SMTP Configuration Details */ +#define YANDEX_SMTP_BACKEND_NAME "smtp" +#define YANDEX_SMTP_HOST "smtp.yandex.ru" +#define YANDEX_SMTP_PORT 465 +#define YANDEX_SMTP_SECURITY_METHOD METHOD (SSL_ON_ALTERNATE_PORT) + +/* WebDAV Configuration Details */ +#define YANDEX_CALDAV_URL "https://caldav.yandex.ru/" +#define YANDEX_CARDDAV_URL "https://carddav.yandex.ru/" + +#define YANDEX_CONTACTS_RESOURCE_ID "Contacts" +#define YANDEX_CONTACTS_BACKEND_NAME "yandex" +#define YANDEX_CONTACTS_HOST "carddav.yandex.ru" + +#define YANDEX_OAUTH2_METHOD_CUSTOM "Yandex" + +typedef struct _EYandexBackend EYandexBackend; +typedef struct _EYandexBackendClass EYandexBackendClass; + +typedef struct _EYandexBackendFactory EYandexBackendFactory; +typedef struct _EYandexBackendFactoryClass EYandexBackendFactoryClass; + +struct _EYandexBackend { + EWebDAVCollectionBackend parent; +}; + +struct _EYandexBackendClass { + EWebDAVCollectionBackendClass parent_class; +}; + +struct _EYandexBackendFactory { + ECollectionBackendFactory parent; +}; + +struct _EYandexBackendFactoryClass { + ECollectionBackendFactoryClass parent_class; +}; + +/* Module Entry Points */ +void e_module_load (GTypeModule *type_module); +void e_module_unload (GTypeModule *type_module); + +/* Forward Declarations */ +GType e_yandex_backend_get_type (void); +GType e_yandex_backend_factory_get_type (void); + +G_DEFINE_DYNAMIC_TYPE ( + EYandexBackend, + e_yandex_backend, + E_TYPE_WEBDAV_COLLECTION_BACKEND) + +G_DEFINE_DYNAMIC_TYPE ( + EYandexBackendFactory, + e_yandex_backend_factory, + E_TYPE_COLLECTION_BACKEND_FACTORY) + +static void +yandex_backend_calendar_update_auth_method (ECollectionBackend *collection_backend, + ESource *child_source, + ESource *master_source); + +static ESourceAuthenticationResult +yandex_backend_authenticate_sync (EBackend *backend, + const ENamedParameters *credentials, + gchar **out_certificate_pem, + GTlsCertificateFlags *out_certificate_errors, + GCancellable *cancellable, + GError **error) +{ + ECollectionBackend *collection = E_COLLECTION_BACKEND (backend); + ESourceCollection *collection_extension; + ESource *source; + ESourceAuthenticationResult result = E_SOURCE_AUTHENTICATION_ERROR; + ESourceGoa *goa_extension = NULL; + const gchar *calendar_url; + const gchar *carddav_url; + + g_return_val_if_fail (collection != NULL, E_SOURCE_AUTHENTICATION_ERROR); + + source = e_backend_get_source (backend); + collection_extension = e_source_get_extension (source, E_SOURCE_EXTENSION_COLLECTION); + if (e_source_has_extension (source, E_SOURCE_EXTENSION_GOA)) + goa_extension = e_source_get_extension (source, E_SOURCE_EXTENSION_GOA); + + g_return_val_if_fail (e_source_collection_get_calendar_enabled (collection_extension) || + e_source_collection_get_contacts_enabled (collection_extension), E_SOURCE_AUTHENTICATION_ERROR); + + e_collection_backend_freeze_populate (collection); + + (void) e_source_get_extension (source, E_SOURCE_EXTENSION_WEBDAV_BACKEND); + yandex_backend_calendar_update_auth_method (collection, source, NULL); + + if (goa_extension) { + calendar_url = e_source_goa_get_calendar_url (goa_extension); + carddav_url = e_source_goa_get_contacts_url (goa_extension); + } else { + calendar_url = "https://caldav.yandex.ru/"; + carddav_url = "https://carddav.yandex.ru"; + } + + if (e_source_collection_get_calendar_enabled (collection_extension) && (calendar_url || carddav_url)) { + result = e_webdav_collection_backend_discover_sync (E_WEBDAV_COLLECTION_BACKEND (backend), + calendar_url, carddav_url, credentials, + out_certificate_pem, out_certificate_errors, cancellable, error); + } else { + result = E_SOURCE_AUTHENTICATION_ACCEPTED; + } + + if (result == E_SOURCE_AUTHENTICATION_ACCEPTED) { + ESourceRegistryServer *server; + + server = e_collection_backend_ref_server (collection); + + if (server) { + g_object_unref (server); + } + } + + e_collection_backend_thaw_populate (collection); + + return result; +} + +static void +yandex_backend_add_contacts (ECollectionBackend *backend) +{ + ESource *source; + ESource *collection_source; + ESourceRegistryServer *server; + ESourceExtension *extension; + ESourceCollection *collection_extension; + const gchar *backend_name; + const gchar *extension_name; + const gchar *resource_id; + + collection_source = e_backend_get_source (E_BACKEND (backend)); + + resource_id = YANDEX_CONTACTS_RESOURCE_ID; + source = e_collection_backend_new_child (backend, resource_id); + e_source_set_display_name (source, _("Contacts")); + + /* Add the address book source to the collection. */ + collection_extension = e_source_get_extension ( + collection_source, E_SOURCE_EXTENSION_COLLECTION); + + /* Configure the address book source. */ + + backend_name = YANDEX_CONTACTS_BACKEND_NAME; + + extension_name = E_SOURCE_EXTENSION_ADDRESS_BOOK; + extension = e_source_get_extension (source, extension_name); + + e_source_backend_set_backend_name ( + E_SOURCE_BACKEND (extension), backend_name); + + extension_name = E_SOURCE_EXTENSION_AUTHENTICATION; + extension = e_source_get_extension (source, extension_name); + + e_source_authentication_set_host ( + E_SOURCE_AUTHENTICATION (extension), + YANDEX_CONTACTS_HOST); + + e_binding_bind_property ( + collection_extension, "identity", + extension, "user", + G_BINDING_SYNC_CREATE); + + server = e_collection_backend_ref_server (backend); + e_source_registry_server_add_source (server, source); + g_object_unref (server); + + g_object_unref (source); +} + +static gchar * +yandex_backend_get_resource_id (EWebDAVCollectionBackend *webdav_backend, + ESource *source) +{ + g_return_val_if_fail (E_IS_SOURCE (source), NULL); + + if (e_source_has_extension (source, E_SOURCE_EXTENSION_ADDRESS_BOOK)) + return g_strdup (YANDEX_CONTACTS_RESOURCE_ID); + + /* Chain up to parent's method. */ + return E_WEBDAV_COLLECTION_BACKEND_CLASS (e_yandex_backend_parent_class)->get_resource_id (webdav_backend, source); +} + +static gboolean +yandex_backend_is_custom_source (EWebDAVCollectionBackend *webdav_backend, + ESource *source) +{ + g_return_val_if_fail (E_IS_SOURCE (source), FALSE); + + if (e_source_has_extension (source, E_SOURCE_EXTENSION_ADDRESS_BOOK) || + e_source_has_extension (source, E_SOURCE_EXTENSION_TASK_LIST)) + return TRUE; + + /* Chain up to parent's method. */ + return E_WEBDAV_COLLECTION_BACKEND_CLASS (e_yandex_backend_parent_class)->is_custom_source (webdav_backend, source); +} + +static void +yandex_backend_populate (ECollectionBackend *backend) +{ + ESourceCollection *collection_extension; + ESourceAuthentication *authentication_extension; + ESource *source; + + source = e_backend_get_source (E_BACKEND (backend)); + collection_extension = e_source_get_extension (source, E_SOURCE_EXTENSION_COLLECTION); + authentication_extension = e_source_get_extension (source, E_SOURCE_EXTENSION_AUTHENTICATION); + + (void) e_source_get_extension (source, E_SOURCE_EXTENSION_WEBDAV_BACKEND); + + if (e_source_authentication_get_is_external (authentication_extension)) + e_source_authentication_set_method (authentication_extension, YANDEX_OAUTH2_METHOD_CUSTOM); + + /* Chain up to parent's method. */ + E_COLLECTION_BACKEND_CLASS (e_yandex_backend_parent_class)->populate (backend); + + if (e_source_collection_get_contacts_enabled (collection_extension)) { + GList *list; + + list = e_collection_backend_list_contacts_sources (backend); + if (list == NULL) + yandex_backend_add_contacts (backend); + g_list_free_full (list, (GDestroyNotify) g_object_unref); + } +} + +static gboolean +host_ends_with (const gchar *host, + const gchar *ends_with) +{ + gint host_len, ends_with_len; + + if (!host || !ends_with) + return FALSE; + + host_len = strlen (host); + ends_with_len = strlen (ends_with); + + if (host_len <= ends_with_len) + return FALSE; + + return g_ascii_strcasecmp (host + host_len - ends_with_len, ends_with) == 0; +} + +static gboolean +yandex_backend_is_yandex_host (ESourceAuthentication *auth_extension, + gboolean *out_requires_oauth2) +{ + gboolean is_yandex; + gchar *host; + + g_return_val_if_fail (E_IS_SOURCE_AUTHENTICATION (auth_extension), FALSE); + + host = e_source_authentication_dup_host (auth_extension); + + is_yandex = (host && ( + host_ends_with (host, "yandex.ru") || + host_ends_with (host, "yandex.com") || + host_ends_with (host, "ya.ru"))); + + g_free (host); + + if (out_requires_oauth2) + *out_requires_oauth2 = TRUE; + + return is_yandex; +} + +static void +yandex_backend_mail_update_auth_method (ECollectionBackend *collection_backend, + ESource *child_source, + ESource *master_source) +{ + ESourceAuthentication *auth_extension; + EOAuth2Support *oauth2_support; + const gchar *method; + + auth_extension = e_source_get_extension (child_source, E_SOURCE_EXTENSION_AUTHENTICATION); + + if (!yandex_backend_is_yandex_host (auth_extension, NULL)) + return; + + oauth2_support = e_server_side_source_ref_oauth2_support (E_SERVER_SIDE_SOURCE (child_source)); + if (!oauth2_support && master_source) + oauth2_support = e_server_side_source_ref_oauth2_support (E_SERVER_SIDE_SOURCE (master_source)); + + if (oauth2_support) { + method = "XOAUTH2"; + } else { + method = NULL; + } + + if (method && e_collection_backend_is_new_source (collection_backend, child_source)) + e_source_authentication_set_method (auth_extension, method); + + g_clear_object (&oauth2_support); +} + +static void +yandex_backend_mail_update_auth_method_cb (ESource *child_source, + GParamSpec *param, + EBackend *backend) +{ + yandex_backend_mail_update_auth_method (E_COLLECTION_BACKEND (backend), child_source, e_backend_get_source (backend)); +} + +static void +yandex_backend_calendar_update_auth_method (ECollectionBackend *collection_backend, + ESource *child_source, + ESource *master_source) +{ + EOAuth2Support *oauth2_support; + ESourceAuthentication *auth_extension; + const gchar *method; + + auth_extension = e_source_get_extension (child_source, E_SOURCE_EXTENSION_AUTHENTICATION); + + if (!yandex_backend_is_yandex_host (auth_extension, NULL)) + return; + + oauth2_support = e_server_side_source_ref_oauth2_support (E_SERVER_SIDE_SOURCE (child_source)); + if (!oauth2_support && master_source) + oauth2_support = e_server_side_source_ref_oauth2_support (E_SERVER_SIDE_SOURCE (master_source)); + + if (oauth2_support) { + method = YANDEX_OAUTH2_METHOD_CUSTOM; + } else { + method = NULL; + } + + if (e_collection_backend_is_new_source (collection_backend, child_source)) + e_source_authentication_set_method (auth_extension, method); + + g_clear_object (&oauth2_support); +} + +static void +yandex_backend_calendar_update_auth_method_cb (ESource *child_source, + GParamSpec *param, + EBackend *backend) +{ + yandex_backend_calendar_update_auth_method (E_COLLECTION_BACKEND (backend), child_source, e_backend_get_source (backend)); +} + +static void +yandex_backend_contacts_update_auth_method (ESource *child_source, + ESource *master_source) +{ + EOAuth2Support *oauth2_support; + ESourceAuthentication *extension; + const gchar *method; + + extension = e_source_get_extension (child_source, E_SOURCE_EXTENSION_AUTHENTICATION); + + if (!yandex_backend_is_yandex_host (extension, NULL)) + return; + + oauth2_support = e_server_side_source_ref_oauth2_support (E_SERVER_SIDE_SOURCE (child_source)); + if (!oauth2_support && master_source) + oauth2_support = e_server_side_source_ref_oauth2_support (E_SERVER_SIDE_SOURCE (master_source)); + + if (oauth2_support) + method = "OAuth2"; + else + method = NULL; + + e_source_authentication_set_method (extension, method); + + g_clear_object (&oauth2_support); +} + +static void +yandex_backend_contacts_update_auth_method_cb (ESource *child_source, + GParamSpec *param, + EBackend *backend) +{ + yandex_backend_contacts_update_auth_method (child_source, e_backend_get_source (backend)); +} + +static gchar * +yandex_backend_dup_resource_id (ECollectionBackend *backend, + ESource *child_source) +{ + if (e_source_has_extension (child_source, E_SOURCE_EXTENSION_CALENDAR) || + e_source_has_extension (child_source, E_SOURCE_EXTENSION_MEMO_LIST) || + e_source_has_extension (child_source, E_SOURCE_EXTENSION_TASK_LIST)) + return E_COLLECTION_BACKEND_CLASS (e_yandex_backend_parent_class)->dup_resource_id (backend, child_source); + + if (e_source_has_extension (child_source, E_SOURCE_EXTENSION_ADDRESS_BOOK)) + return g_strdup (YANDEX_CONTACTS_RESOURCE_ID); + + return NULL; +} + +static void +yandex_backend_child_added (ECollectionBackend *backend, + ESource *child_source) +{ + ESource *collection_source; + const gchar *extension_name; + gboolean is_mail = FALSE; + gboolean has_external_auth = FALSE; + + /* Chain up to parent's child_added() method. */ + E_COLLECTION_BACKEND_CLASS (e_yandex_backend_parent_class)-> + child_added (backend, child_source); + + collection_source = e_backend_get_source (E_BACKEND (backend)); + + extension_name = E_SOURCE_EXTENSION_MAIL_ACCOUNT; + is_mail |= e_source_has_extension (child_source, extension_name); + + extension_name = E_SOURCE_EXTENSION_MAIL_IDENTITY; + is_mail |= e_source_has_extension (child_source, extension_name); + + extension_name = E_SOURCE_EXTENSION_MAIL_TRANSPORT; + is_mail |= e_source_has_extension (child_source, extension_name); + + /* Synchronize mail-related user with the collection identity. */ + extension_name = E_SOURCE_EXTENSION_AUTHENTICATION; + if (is_mail && e_source_has_extension (child_source, extension_name)) { + ESourceAuthentication *auth_child_extension; + ESourceCollection *collection_extension; + const gchar *collection_identity; + const gchar *auth_child_user; + + extension_name = E_SOURCE_EXTENSION_COLLECTION; + collection_extension = e_source_get_extension ( + collection_source, extension_name); + collection_identity = e_source_collection_get_identity ( + collection_extension); + + extension_name = E_SOURCE_EXTENSION_AUTHENTICATION; + auth_child_extension = e_source_get_extension ( + child_source, extension_name); + auth_child_user = e_source_authentication_get_user ( + auth_child_extension); + has_external_auth = e_source_authentication_get_is_external ( + auth_child_extension); + + if (auth_child_user == NULL) + e_source_authentication_set_user ( + auth_child_extension, + collection_identity); + + if (e_source_has_extension (child_source, E_SOURCE_EXTENSION_MAIL_ACCOUNT) || + e_source_has_extension (child_source, E_SOURCE_EXTENSION_MAIL_TRANSPORT)) { + yandex_backend_mail_update_auth_method (backend, child_source, collection_source); + g_signal_connect ( + child_source, "notify::oauth2-support", + G_CALLBACK (yandex_backend_mail_update_auth_method_cb), + backend); + } + } + + extension_name = E_SOURCE_EXTENSION_CALENDAR; + if (e_source_has_extension (child_source, extension_name)) { + ESourceAlarms *alarms_extension; + + /* To not notify about past reminders. */ + alarms_extension = e_source_get_extension (child_source, E_SOURCE_EXTENSION_ALARMS); + if (!e_source_alarms_get_last_notified (alarms_extension)) { + GTimeVal today_tv; + gchar *today; + + g_get_current_time (&today_tv); + today = g_time_val_to_iso8601 (&today_tv); + e_source_alarms_set_last_notified (alarms_extension, today); + g_free (today); + } + + yandex_backend_calendar_update_auth_method (backend, child_source, collection_source); + g_signal_connect ( + child_source, "notify::oauth2-support", + G_CALLBACK (yandex_backend_calendar_update_auth_method_cb), + backend); + } + + extension_name = E_SOURCE_EXTENSION_ADDRESS_BOOK; + if (e_source_has_extension (child_source, extension_name)) { + yandex_backend_contacts_update_auth_method (child_source, collection_source); + g_signal_connect ( + child_source, "notify::oauth2-support", + G_CALLBACK (yandex_backend_contacts_update_auth_method_cb), + backend); + + if (!has_external_auth) { + /* Even the book is part of the collection it can be removed + separately, if not configured through GOA or UOA. */ + e_server_side_source_set_removable (E_SERVER_SIDE_SOURCE (child_source), TRUE); + } + } +} + +static void +yandex_backend_child_removed (ECollectionBackend *backend, + ESource *child_source) +{ + ESource *collection_source; + gboolean has_external_auth = FALSE; + + /* Chain up to parent's method. */ + E_COLLECTION_BACKEND_CLASS (e_yandex_backend_parent_class)->child_removed (backend, child_source); + + collection_source = e_backend_get_source (E_BACKEND (backend)); + + if (e_source_has_extension (child_source, E_SOURCE_EXTENSION_AUTHENTICATION)) { + ESourceAuthentication *auth_child_extension; + + auth_child_extension = e_source_get_extension (child_source, E_SOURCE_EXTENSION_AUTHENTICATION); + has_external_auth = e_source_authentication_get_is_external (auth_child_extension); + } + + if (e_source_has_extension (child_source, E_SOURCE_EXTENSION_ADDRESS_BOOK) && + e_source_has_extension (collection_source, E_SOURCE_EXTENSION_COLLECTION) && + !has_external_auth) { + ESourceCollection *collection_extension; + + collection_extension = e_source_get_extension (collection_source, E_SOURCE_EXTENSION_COLLECTION); + + e_source_collection_set_contacts_enabled (collection_extension, FALSE); + } +} + +static void +e_yandex_backend_class_init (EYandexBackendClass *class) +{ + EBackendClass *backend_class; + ECollectionBackendClass *collection_backend_class; + EWebDAVCollectionBackendClass *webdav_collection_backend_class; + + backend_class = E_BACKEND_CLASS (class); + backend_class->authenticate_sync = yandex_backend_authenticate_sync; + + collection_backend_class = E_COLLECTION_BACKEND_CLASS (class); + collection_backend_class->child_added = yandex_backend_child_added; + collection_backend_class->populate = yandex_backend_populate; + collection_backend_class->dup_resource_id = yandex_backend_dup_resource_id; + collection_backend_class->child_removed = yandex_backend_child_removed; + + webdav_collection_backend_class = E_WEBDAV_COLLECTION_BACKEND_CLASS (class); + webdav_collection_backend_class->get_resource_id = yandex_backend_get_resource_id; + webdav_collection_backend_class->is_custom_source = yandex_backend_is_custom_source; +} + +static void +e_yandex_backend_class_finalize (EYandexBackendClass *class) +{ +} + +static void +e_yandex_backend_init (EYandexBackend *backend) +{ +} + +static void +yandex_backend_prepare_mail_account_source (ESource *source) +{ + ESourceCamel *camel_extension; + ESourceExtension *extension; + CamelSettings *settings; + const gchar *backend_name; + const gchar *extension_name; + + backend_name = YANDEX_IMAP_BACKEND_NAME; + + extension_name = E_SOURCE_EXTENSION_MAIL_ACCOUNT; + extension = e_source_get_extension (source, extension_name); + + e_source_backend_set_backend_name ( + E_SOURCE_BACKEND (extension), backend_name); + + extension_name = e_source_camel_get_extension_name (backend_name); + camel_extension = e_source_get_extension (source, extension_name); + settings = e_source_camel_get_settings (camel_extension); + + /* The "auth-mechanism" should be determined elsewhere. */ + + camel_network_settings_set_host ( + CAMEL_NETWORK_SETTINGS (settings), + YANDEX_IMAP_HOST); + + camel_network_settings_set_port ( + CAMEL_NETWORK_SETTINGS (settings), + YANDEX_IMAP_PORT); + + camel_network_settings_set_security_method ( + CAMEL_NETWORK_SETTINGS (settings), + YANDEX_IMAP_SECURITY_METHOD); +} + +static void +yandex_backend_prepare_mail_transport_source (ESource *source) +{ + ESourceCamel *camel_extension; + ESourceExtension *extension; + CamelSettings *settings; + const gchar *backend_name; + const gchar *extension_name; + + /* Configure the mail transport source. */ + + backend_name = YANDEX_SMTP_BACKEND_NAME; + + extension_name = E_SOURCE_EXTENSION_MAIL_TRANSPORT; + extension = e_source_get_extension (source, extension_name); + + e_source_backend_set_backend_name ( + E_SOURCE_BACKEND (extension), backend_name); + + extension_name = e_source_camel_get_extension_name (backend_name); + camel_extension = e_source_get_extension (source, extension_name); + settings = e_source_camel_get_settings (camel_extension); + + /* The "auth-mechanism" should be determined elsewhere. */ + + camel_network_settings_set_host ( + CAMEL_NETWORK_SETTINGS (settings), + YANDEX_SMTP_HOST); + + camel_network_settings_set_port ( + CAMEL_NETWORK_SETTINGS (settings), + YANDEX_SMTP_PORT); + + camel_network_settings_set_security_method ( + CAMEL_NETWORK_SETTINGS (settings), + YANDEX_SMTP_SECURITY_METHOD); +} + +static void +yandex_backend_factory_prepare_mail (ECollectionBackendFactory *factory, + ESource *mail_account_source, + ESource *mail_identity_source, + ESource *mail_transport_source) +{ + ECollectionBackendFactoryClass *parent_class; + + /* Chain up to parent's prepare_mail() method. */ + parent_class = + E_COLLECTION_BACKEND_FACTORY_CLASS ( + e_yandex_backend_factory_parent_class); + parent_class->prepare_mail ( + factory, + mail_account_source, + mail_identity_source, + mail_transport_source); + + yandex_backend_prepare_mail_account_source (mail_account_source); + yandex_backend_prepare_mail_transport_source (mail_transport_source); +} + +static void +e_yandex_backend_factory_class_init (EYandexBackendFactoryClass *class) +{ + ECollectionBackendFactoryClass *factory_class; + + factory_class = E_COLLECTION_BACKEND_FACTORY_CLASS (class); + factory_class->factory_name = "yandex"; + factory_class->backend_type = E_TYPE_YANDEX_BACKEND; + factory_class->prepare_mail = yandex_backend_factory_prepare_mail; +} + +static void +e_yandex_backend_factory_class_finalize (EYandexBackendFactoryClass *class) +{ +} + +static void +e_yandex_backend_factory_init (EYandexBackendFactory *factory) +{ +} + +G_MODULE_EXPORT void +e_module_load (GTypeModule *type_module) +{ + e_yandex_backend_register_type (type_module); + e_yandex_backend_factory_register_type (type_module); +} + +G_MODULE_EXPORT void +e_module_unload (GTypeModule *type_module) +{ +} + -- 2.39.1