From 57f0eb558fed22ab09ac9937f264f8d1952fca6d Mon Sep 17 00:00:00 2001 From: Alexey Berezhok Date: Wed, 3 May 2023 13:03:16 +0300 Subject: [PATCH] Added yandex backend support(prebuild) --- .gitignore | 1 + CMakeLists.txt | 11 + config.h.in | 6 + 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/libedataserver/CMakeLists.txt | 2 + .../e-dataserver-autocleanups.h | 1 + src/libedataserver/e-oauth2-service-yandex.c | 227 +++++++ src/libedataserver/e-oauth2-service-yandex.h | 62 ++ src/libedataserver/e-oauth2-services.c | 2 + 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 | 594 ++++++++++++++++++ 21 files changed, 1052 insertions(+), 1 deletion(-) 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/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/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/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..19e9453 --- /dev/null +++ b/src/libedataserver/e-oauth2-service-yandex.c @@ -0,0 +1,227 @@ +/* + * 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 */ + "ogin:email login:info mail:imap_full mail:imap_ro mail:smtp"; + + 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_title && *page_title) { + /* Known response, but no authorization code */ + if (g_ascii_strncasecmp (page_title, "Denied ", 7) == 0) + return TRUE; + + if (g_ascii_strncasecmp (page_title, "Success code=", 13) == 0) { + *out_authorization_code = g_strdup (page_title + 13); + return TRUE; + } + } + + 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, "response"); + if (response && g_ascii_strncasecmp (response, "code=", 5) == 0) { + *out_authorization_code = g_strdup (response + 5); + known = TRUE; + } else if (response && g_ascii_strncasecmp (response, "error", 5) == 0) { + 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/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..31e6f2e --- /dev/null +++ b/src/modules/yandex-backend/module-yandex-backend.c @@ -0,0 +1,594 @@ +/* + * 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 +#include + +#ifdef HAVE_LIBGDATA +#include +#endif + +/* 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) + +#define YANDEX_OAUTH2_METHOD "Yandex" + +/* 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) + +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 gboolean +yandex_backend_can_use_yandex_auth (ESource *source) +{ + ESourceRegistryServer *registry; + ESourceAuthentication *auth_extension; + gboolean res; + + g_return_val_if_fail (E_IS_SERVER_SIDE_SOURCE (source), FALSE); + + registry = e_server_side_source_get_server (E_SERVER_SIDE_SOURCE (source)); + if (!e_oauth2_services_is_oauth2_alias (e_source_registry_server_get_oauth2_services (registry), YANDEX_OAUTH2_METHOD)) + return FALSE; + + g_object_ref (source); + + while (source && e_source_get_parent (source)) { + ESource *adept_source; + + adept_source = e_source_registry_server_ref_source (registry, e_source_get_parent (source)); + if (adept_source) { + g_object_unref (source); + source = adept_source; + } else { + break; + } + } + + auth_extension = e_source_get_extension (source, E_SOURCE_EXTENSION_AUTHENTICATION); + res = !e_source_authentication_get_is_external (auth_extension); + + g_object_unref (source); + + return res; +} + +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; + gboolean requires_oauth2; + gchar *host; + + g_return_val_if_fail (E_IS_SOURCE_AUTHENTICATION (auth_extension), FALSE); + + host = e_source_authentication_dup_host (auth_extension); + + requires_oauth2 = (host != NULL); + + is_yandex = requires_oauth2 || (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 = requires_oauth2; + + return is_yandex; +} + +static gboolean +yandex_backend_is_oauth2 (const gchar *method) +{ + return g_strcmp0 (method, YANDEX_OAUTH2_METHOD) == 0 || + g_strcmp0 (method, "OAuth2") == 0 || + g_strcmp0 (method, "XOAUTH2") == 0; +} + +static gboolean +yandex_backend_can_change_auth_method (ESourceAuthentication *auth_extension, + const gchar *new_method) +{ + gchar *cur_method; + gboolean can_change; + + g_return_val_if_fail (E_IS_SOURCE_AUTHENTICATION (auth_extension), FALSE); + + if (!new_method) + return FALSE; + + cur_method = e_source_authentication_dup_method (auth_extension); + + /* Only when turning off OAuth2 */ + can_change = yandex_backend_is_oauth2 (cur_method) && !yandex_backend_is_oauth2 (new_method); + + g_free (cur_method); + + return can_change; +} + +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; + gboolean can_use_yandex_auth; + + 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)); + + can_use_yandex_auth = yandex_backend_can_use_yandex_auth (child_source); + if (!can_use_yandex_auth && master_source) + can_use_yandex_auth = yandex_backend_can_use_yandex_auth (master_source); + + if (oauth2_support && !can_use_yandex_auth) { + method = "XOAUTH2"; + } else if (can_use_yandex_auth) { + method = YANDEX_OAUTH2_METHOD; + } else { + method = NULL; + } + + if (method && (e_collection_backend_is_new_source (collection_backend, child_source) || + yandex_backend_can_change_auth_method (auth_extension, method))) + 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_remove_unknown_sources_cb (gpointer resource_id, + gpointer uid, + gpointer user_data) +{ + ESourceRegistryServer *server = user_data; + ESource *source; + + source = e_source_registry_server_ref_source (server, uid); + + if (source) { + e_source_remove_sync (source, NULL, NULL); + g_object_unref (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; + GHashTable *known_sources; + GList *sources; + ENamedParameters *credentials_copy = NULL; + + 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); + + 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); + + if (credentials && !e_named_parameters_get (credentials, E_SOURCE_CREDENTIAL_USERNAME)) { + credentials_copy = e_named_parameters_new_clone (credentials); + e_named_parameters_set (credentials_copy, E_SOURCE_CREDENTIAL_USERNAME, e_source_collection_get_identity (collection_extension)); + credentials = credentials_copy; + } + + /* resource-id => source's UID */ + known_sources = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, g_free); + + sources = e_collection_backend_list_calendar_sources (collection); + g_list_free_full (sources, g_object_unref); + + /* When the WebDAV extension is created, the auth method can be reset, thus ensure + it's there before setting correct authentication method on the master source. */ + (void) e_source_get_extension (source, E_SOURCE_EXTENSION_WEBDAV_BACKEND); + + result = E_SOURCE_AUTHENTICATION_ACCEPTED; + + if (result == E_SOURCE_AUTHENTICATION_ACCEPTED) { + ESourceRegistryServer *server; + + server = e_collection_backend_ref_server (collection); + + if (server) { + g_hash_table_foreach (known_sources, yandex_remove_unknown_sources_cb, server); + g_object_unref (server); + } + } + + g_hash_table_destroy (known_sources); + e_named_parameters_free (credentials_copy); + + e_collection_backend_thaw_populate (collection); + + return result; +} + +static void +yandex_backend_populate (ECollectionBackend *backend) +{ + ESourceAuthentication *authentication_extension; + ESource *source; + + source = e_backend_get_source (E_BACKEND (backend)); + authentication_extension = e_source_get_extension (source, E_SOURCE_EXTENSION_AUTHENTICATION); + + /* When the WebDAV extension is created, the auth method can be reset, thus ensure + it's there before setting correct authentication method on the master source. */ + (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, "OAuth2"); + + /* Chain up to parent's method. */ + E_COLLECTION_BACKEND_CLASS (e_yandex_backend_parent_class)->populate (backend); + +} + +static void +yandex_backend_child_added (ECollectionBackend *backend, + ESource *child_source) +{ + ESource *collection_source; + const gchar *extension_name; + gboolean is_mail = 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); + + + 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); + } + } + +} + +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; + + 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->populate = yandex_backend_populate; + collection_backend_class->child_added = yandex_backend_child_added; + collection_backend_class->child_removed = yandex_backend_child_removed; + +} + +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