diff --git a/SOURCES/0001-Added-yandex-backend-support-prebuild.patch b/SOURCES/0001-Added-yandex-backend-support-prebuild.patch index 01bdb3f..fc20a4a 100644 --- a/SOURCES/0001-Added-yandex-backend-support-prebuild.patch +++ b/SOURCES/0001-Added-yandex-backend-support-prebuild.patch @@ -1,30 +1,42 @@ -From 57f0eb558fed22ab09ac9937f264f8d1952fca6d Mon Sep 17 00:00:00 2001 +From ee73290b8ce8d9f3b1b42dc4ddc8a3c8d649778d Mon Sep 17 00:00:00 2001 From: Alexey Berezhok -Date: Wed, 3 May 2023 13:03:16 +0300 +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 + + .../backends/google/e-book-backend-google.c | 1 + + .../backends/google/e-book-google-utils.c | 2 +- + src/addressbook/libebook-contacts/e-contact.c | 10 +- + src/addressbook/libebook-contacts/e-contact.h | 7 + + src/addressbook/libebook-contacts/e-vcard.h | 1 + + .../libedata-book/e-book-backend-sexp.c | 11 + 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 | 227 +++++++ + 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 | 594 ++++++++++++++++++ - 21 files changed, 1052 insertions(+), 1 deletion(-) + .../yandex-backend/module-yandex-backend.c | 727 ++++++++++++++++++ + 32 files changed, 1259 insertions(+), 5 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 @@ -41,6 +53,20 @@ index 726890e..575a9f0 100644 *.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 @@ -80,6 +106,26 @@ index bd2978a..cdd864e 100644 /* 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 @@ -108,6 +154,132 @@ index 9a25ab5..10dfcf8 100644 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/addressbook/backends/google/e-book-backend-google.c b/src/addressbook/backends/google/e-book-backend-google.c +index 4597169..043f174 100644 +--- a/src/addressbook/backends/google/e-book-backend-google.c ++++ b/src/addressbook/backends/google/e-book-backend-google.c +@@ -1159,6 +1159,7 @@ ebb_google_get_backend_property (EBookBackend *book_backend, + e_contact_field_name (E_CONTACT_IM_AIM), + e_contact_field_name (E_CONTACT_IM_JABBER), + e_contact_field_name (E_CONTACT_IM_YAHOO), ++ e_contact_field_name (E_CONTACT_IM_YANDEX), + e_contact_field_name (E_CONTACT_IM_MSN), + e_contact_field_name (E_CONTACT_IM_ICQ), + e_contact_field_name (E_CONTACT_IM_SKYPE), +diff --git a/src/addressbook/backends/google/e-book-google-utils.c b/src/addressbook/backends/google/e-book-google-utils.c +index 3b14a4e..ed70a4b 100644 +--- a/src/addressbook/backends/google/e-book-google-utils.c ++++ b/src/addressbook/backends/google/e-book-google-utils.c +@@ -1095,7 +1095,7 @@ static gboolean + is_known_google_im_protocol (const gchar *protocol) + { + const gchar *known_protocols[] = { +- "AIM", "MSN", "YAHOO", "SKYPE", "QQ", ++ "AIM", "MSN", "YAHOO", "YANDEX", "SKYPE", "QQ", + "GOOGLE-TALK", "ICQ", "JABBER" + }; + guint i; +diff --git a/src/addressbook/libebook-contacts/e-contact.c b/src/addressbook/libebook-contacts/e-contact.c +index 54f14fd..b0f1534 100644 +--- a/src/addressbook/libebook-contacts/e-contact.c ++++ b/src/addressbook/libebook-contacts/e-contact.c +@@ -75,7 +75,8 @@ static AttrTypeValue glob_attr_type_values[] = { + { EVC_X_JABBER, "WORK;HOME" }, + { EVC_X_MSN, "WORK;HOME" }, + { EVC_X_SKYPE, "WORK;HOME" }, +- { EVC_X_YAHOO, "WORK;HOME" } ++ { EVC_X_YAHOO, "WORK;HOME" }, ++ { EVC_X_YANDEX, "WORK;HOME" } + }; + + #define E_CONTACT_FIELD_TYPE_STRING 0x00000001 /* used for simple single valued attributes */ +@@ -249,6 +250,12 @@ static const EContactFieldInfo field_info[] = { + ATTR_TYPE_STR_FIELD (E_CONTACT_IM_YAHOO_WORK_1, EVC_X_YAHOO, "im_yahoo_work_1", N_("Yahoo! Work Screen Name 1"), FALSE, "WORK", 0), + ATTR_TYPE_STR_FIELD (E_CONTACT_IM_YAHOO_WORK_2, EVC_X_YAHOO, "im_yahoo_work_2", N_("Yahoo! Work Screen Name 2"), FALSE, "WORK", 1), + ATTR_TYPE_STR_FIELD (E_CONTACT_IM_YAHOO_WORK_3, EVC_X_YAHOO, "im_yahoo_work_3", N_("Yahoo! Work Screen Name 3"), FALSE, "WORK", 2), ++ ATTR_TYPE_STR_FIELD (E_CONTACT_IM_YANDEX_HOME_1, EVC_X_YANDEX, "im_yandex_home_1", N_("Yandex Home Screen Name 1"), FALSE, "HOME", 0), ++ ATTR_TYPE_STR_FIELD (E_CONTACT_IM_YANDEX_HOME_2, EVC_X_YANDEX, "im_yandex_home_2", N_("Yandex Home Screen Name 2"), FALSE, "HOME", 1), ++ ATTR_TYPE_STR_FIELD (E_CONTACT_IM_YANDEX_HOME_3, EVC_X_YANDEX, "im_yandex_home_3", N_("Yandex Home Screen Name 3"), FALSE, "HOME", 2), ++ ATTR_TYPE_STR_FIELD (E_CONTACT_IM_YANDEX_WORK_1, EVC_X_YANDEX, "im_yandex_work_1", N_("Yandex Work Screen Name 1"), FALSE, "WORK", 0), ++ ATTR_TYPE_STR_FIELD (E_CONTACT_IM_YANDEX_WORK_2, EVC_X_YANDEX, "im_yandex_work_2", N_("Yandex Work Screen Name 2"), FALSE, "WORK", 1), ++ ATTR_TYPE_STR_FIELD (E_CONTACT_IM_YANDEX_WORK_3, EVC_X_YANDEX, "im_yandex_work_3", N_("Yandex Work Screen Name 3"), FALSE, "WORK", 2), + ATTR_TYPE_STR_FIELD (E_CONTACT_IM_MSN_HOME_1, EVC_X_MSN, "im_msn_home_1", N_("MSN Home Screen Name 1"), FALSE, "HOME", 0), + ATTR_TYPE_STR_FIELD (E_CONTACT_IM_MSN_HOME_2, EVC_X_MSN, "im_msn_home_2", N_("MSN Home Screen Name 2"), FALSE, "HOME", 1), + ATTR_TYPE_STR_FIELD (E_CONTACT_IM_MSN_HOME_3, EVC_X_MSN, "im_msn_home_3", N_("MSN Home Screen Name 3"), FALSE, "HOME", 2), +@@ -292,6 +299,7 @@ static const EContactFieldInfo field_info[] = { + MULTI_LIST_FIELD (E_CONTACT_IM_GROUPWISE, EVC_X_GROUPWISE, "im_groupwise", N_("GroupWise ID List"), FALSE), + MULTI_LIST_FIELD (E_CONTACT_IM_JABBER, EVC_X_JABBER, "im_jabber", N_("Jabber ID List"), FALSE), + MULTI_LIST_FIELD (E_CONTACT_IM_YAHOO, EVC_X_YAHOO, "im_yahoo", N_("Yahoo! Screen Name List"), FALSE), ++ MULTI_LIST_FIELD (E_CONTACT_IM_YANDEX, EVC_X_YANDEX, "im_yandex", N_("Yandex Screen Name List"), FALSE), + MULTI_LIST_FIELD (E_CONTACT_IM_MSN, EVC_X_MSN, "im_msn", N_("MSN Screen Name List"), FALSE), + MULTI_LIST_FIELD (E_CONTACT_IM_ICQ, EVC_X_ICQ, "im_icq", N_("ICQ ID List"), FALSE), + +diff --git a/src/addressbook/libebook-contacts/e-contact.h b/src/addressbook/libebook-contacts/e-contact.h +index 3f1e084..b5d9ef5 100644 +--- a/src/addressbook/libebook-contacts/e-contact.h ++++ b/src/addressbook/libebook-contacts/e-contact.h +@@ -159,6 +159,12 @@ typedef enum { + E_CONTACT_IM_YAHOO_WORK_1, /* Synthetic string field */ + E_CONTACT_IM_YAHOO_WORK_2, /* Synthetic string field */ + E_CONTACT_IM_YAHOO_WORK_3, /* Synthetic string field */ ++ E_CONTACT_IM_YANDEX_HOME_1, /* Synthetic string field */ ++ E_CONTACT_IM_YANDEX_HOME_2, /* Synthetic string field */ ++ E_CONTACT_IM_YANDEX_HOME_3, /* Synthetic string field */ ++ E_CONTACT_IM_YANDEX_WORK_1, /* Synthetic string field */ ++ E_CONTACT_IM_YANDEX_WORK_2, /* Synthetic string field */ ++ E_CONTACT_IM_YANDEX_WORK_3, /* Synthetic string field */ + E_CONTACT_IM_MSN_HOME_1, /* Synthetic string field */ + E_CONTACT_IM_MSN_HOME_2, /* Synthetic string field */ + E_CONTACT_IM_MSN_HOME_3, /* Synthetic string field */ +@@ -198,6 +204,7 @@ typedef enum { + E_CONTACT_IM_GROUPWISE, /* Multi-valued */ + E_CONTACT_IM_JABBER, /* Multi-valued */ + E_CONTACT_IM_YAHOO, /* Multi-valued */ ++ E_CONTACT_IM_YANDEX, /* Multi-valued */ + E_CONTACT_IM_MSN, /* Multi-valued */ + E_CONTACT_IM_ICQ, /* Multi-valued */ + +diff --git a/src/addressbook/libebook-contacts/e-vcard.h b/src/addressbook/libebook-contacts/e-vcard.h +index 9efcb3d..9a31ad7 100644 +--- a/src/addressbook/libebook-contacts/e-vcard.h ++++ b/src/addressbook/libebook-contacts/e-vcard.h +@@ -135,6 +135,7 @@ G_BEGIN_DECLS + #define EVC_X_VIDEO_URL "X-EVOLUTION-VIDEO-URL" + #define EVC_X_WANTS_HTML "X-MOZILLA-HTML" + #define EVC_X_YAHOO "X-YAHOO" ++#define EVC_X_YANDEX "X-YANDEX" + + /** + * EVC_X_BOOK_UID: +diff --git a/src/addressbook/libedata-book/e-book-backend-sexp.c b/src/addressbook/libedata-book/e-book-backend-sexp.c +index 5f971cf..9977311 100644 +--- a/src/addressbook/libedata-book/e-book-backend-sexp.c ++++ b/src/addressbook/libedata-book/e-book-backend-sexp.c +@@ -127,6 +127,16 @@ compare_im_yahoo (EContact *contact, + return compare_im (contact, str, region, compare, E_CONTACT_IM_YAHOO); + } + ++static gboolean ++compare_im_yandex (EContact *contact, ++ const gchar *str, ++ const gchar *region, ++ CompareFunc compare) ++{ ++ return compare_im (contact, str, region, compare, E_CONTACT_IM_YANDEX); ++} ++ ++ + static gboolean + compare_im_gadugadu (EContact *contact, + const gchar *str, +@@ -384,6 +394,7 @@ static struct prop_info { + LIST_PROP ( "im_icq", compare_im_icq ), + LIST_PROP ( "im_jabber", compare_im_jabber ), + LIST_PROP ( "im_yahoo", compare_im_yahoo ), ++ LIST_PROP ( "im_yandex", compare_im_yandex ), + LIST_PROP ( "im_gadugadu", compare_im_gadugadu ), + LIST_PROP ( "im_groupwise", compare_im_groupwise ), + LIST_PROP ( "email", compare_email ), diff --git a/src/camel/CMakeLists.txt b/src/camel/CMakeLists.txt index 2ba609b..26b2033 100644 --- a/src/camel/CMakeLists.txt @@ -291,6 +463,19 @@ index 0df1baf..9c71ab7 100644 #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 @@ -325,10 +510,10 @@ index bc3c81a..93b4e14 100644 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 +index 0000000..64ac387 --- /dev/null +++ b/src/libedataserver/e-oauth2-service-yandex.c -@@ -0,0 +1,227 @@ +@@ -0,0 +1,214 @@ +/* + * Copyright (C) 2018 Red Hat, Inc. (www.redhat.com) + * @@ -465,7 +650,7 @@ index 0000000..19e9453 +{ + const gchar *YANDEX_SCOPE = + /* GMail IMAP and SMTP access */ -+ "ogin:email login:info mail:imap_full mail:imap_ro mail:smtp"; ++ "login:email login:info mail:imap_full mail:imap_ro mail:smtp calendar:all"; + + g_return_if_fail (uri_query != NULL); + @@ -485,17 +670,6 @@ index 0000000..19e9453 + + *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; + @@ -511,11 +685,9 @@ index 0000000..19e9453 + 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) { ++ response = g_hash_table_lookup (params, "code"); ++ if (response) { ++ *out_authorization_code = g_strdup (response); + known = TRUE; + } + @@ -644,6 +816,126 @@ index 838b007..3679cea 100644 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 @@ -713,10 +1005,10 @@ index 0000000..2416fa1 +) 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 +index 0000000..bb6dfc1 --- /dev/null +++ b/src/modules/yandex-backend/module-yandex-backend.c -@@ -0,0 +1,594 @@ +@@ -0,0 +1,727 @@ +/* + * module-yandex-backend.c + * @@ -739,11 +1031,6 @@ index 0000000..31e6f2e +#include + +#include -+#include -+ -+#ifdef HAVE_LIBGDATA -+#include -+#endif + +/* Standard GObject macros */ +#define E_TYPE_YANDEX_BACKEND \ @@ -755,20 +1042,28 @@ index 0000000..31e6f2e +/* 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_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_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; + @@ -809,39 +1104,178 @@ index 0000000..31e6f2e + e_yandex_backend_factory, + E_TYPE_COLLECTION_BACKEND_FACTORY) + -+static gboolean -+yandex_backend_can_use_yandex_auth (ESource *source) ++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) +{ -+ ESourceRegistryServer *registry; -+ ESourceAuthentication *auth_extension; -+ gboolean res; ++ 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 (E_IS_SERVER_SIDE_SOURCE (source), FALSE); ++ g_return_val_if_fail (collection != NULL, E_SOURCE_AUTHENTICATION_ERROR); + -+ 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; ++ 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); + -+ g_object_ref (source); ++ (void) e_source_get_extension (source, E_SOURCE_EXTENSION_WEBDAV_BACKEND); ++ yandex_backend_calendar_update_auth_method (collection, source, NULL); + -+ while (source && e_source_get_parent (source)) { -+ ESource *adept_source; ++ 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"; ++ } + -+ 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; ++ 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); + } + } + -+ auth_extension = e_source_get_extension (source, E_SOURCE_EXTENSION_AUTHENTICATION); -+ res = !e_source_authentication_get_is_external (auth_extension); ++ 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); + -+ return res; ++ 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 @@ -867,16 +1301,13 @@ index 0000000..31e6f2e + 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 && ( ++ is_yandex = (host && ( + host_ends_with (host, "yandex.ru") || + host_ends_with (host, "yandex.com") || + host_ends_with (host, "ya.ru"))); @@ -884,41 +1315,11 @@ index 0000000..31e6f2e + g_free (host); + + if (out_requires_oauth2) -+ *out_requires_oauth2 = requires_oauth2; ++ *out_requires_oauth2 = TRUE; + + 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, @@ -927,7 +1328,6 @@ index 0000000..31e6f2e + 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); + @@ -938,20 +1338,13 @@ index 0000000..31e6f2e + 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) { ++ if (oauth2_support) { + 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))) ++ 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); @@ -966,112 +1359,101 @@ index 0000000..31e6f2e +} + +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) ++yandex_backend_calendar_update_auth_method (ECollectionBackend *collection_backend, ++ ESource *child_source, ++ ESource *master_source) +{ -+ 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); ++ EOAuth2Support *oauth2_support; ++ ESourceAuthentication *auth_extension; ++ const gchar *method; + -+ source = e_backend_get_source (backend); -+ collection_extension = e_source_get_extension (source, E_SOURCE_EXTENSION_COLLECTION); ++ auth_extension = e_source_get_extension (child_source, E_SOURCE_EXTENSION_AUTHENTICATION); + -+ 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); ++ if (!yandex_backend_is_yandex_host (auth_extension, NULL)) ++ return; + -+ e_collection_backend_freeze_populate (collection); ++ 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 (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; ++ if (oauth2_support) { ++ method = YANDEX_OAUTH2_METHOD_CUSTOM; ++ } else { ++ method = NULL; + } + -+ /* resource-id => source's UID */ -+ known_sources = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, g_free); ++ if (e_collection_backend_is_new_source (collection_backend, child_source)) ++ e_source_authentication_set_method (auth_extension, method); + -+ sources = e_collection_backend_list_calendar_sources (collection); -+ g_list_free_full (sources, g_object_unref); ++ g_clear_object (&oauth2_support); ++} + -+ /* 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); ++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)); ++} + -+ result = E_SOURCE_AUTHENTICATION_ACCEPTED; ++static void ++yandex_backend_contacts_update_auth_method (ESource *child_source, ++ ESource *master_source) ++{ ++ EOAuth2Support *oauth2_support; ++ ESourceAuthentication *extension; ++ const gchar *method; + -+ if (result == E_SOURCE_AUTHENTICATION_ACCEPTED) { -+ ESourceRegistryServer *server; ++ extension = e_source_get_extension (child_source, E_SOURCE_EXTENSION_AUTHENTICATION); + -+ server = e_collection_backend_ref_server (collection); ++ if (!yandex_backend_is_yandex_host (extension, NULL)) ++ return; + -+ if (server) { -+ g_hash_table_foreach (known_sources, yandex_remove_unknown_sources_cb, server); -+ g_object_unref (server); -+ } -+ } ++ 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)); + -+ g_hash_table_destroy (known_sources); -+ e_named_parameters_free (credentials_copy); ++ if (oauth2_support) ++ method = "OAuth2"; ++ else ++ method = NULL; + -+ e_collection_backend_thaw_populate (collection); ++ e_source_authentication_set_method (extension, method); + -+ return result; ++ g_clear_object (&oauth2_support); +} + +static void -+yandex_backend_populate (ECollectionBackend *backend) ++yandex_backend_contacts_update_auth_method_cb (ESource *child_source, ++ GParamSpec *param, ++ EBackend *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); ++ yandex_backend_contacts_update_auth_method (child_source, e_backend_get_source (backend)); ++} + -+ /* 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); ++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_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); ++ 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 *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)-> @@ -1107,7 +1489,8 @@ index 0000000..31e6f2e + 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 ( @@ -1124,6 +1507,43 @@ index 0000000..31e6f2e + } + } + ++ 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 @@ -1161,15 +1581,20 @@ index 0000000..31e6f2e +{ + 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->populate = yandex_backend_populate; + 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 @@ -1258,9 +1683,9 @@ index 0000000..31e6f2e + +static void +yandex_backend_factory_prepare_mail (ECollectionBackendFactory *factory, -+ ESource *mail_account_source, -+ ESource *mail_identity_source, -+ ESource *mail_transport_source) ++ ESource *mail_account_source, ++ ESource *mail_identity_source, ++ ESource *mail_transport_source) +{ + ECollectionBackendFactoryClass *parent_class; +