commit e85884af32d83bacafc588476b0117bc83667b8d Author: MSVSphere Packaging Team Date: Fri Mar 29 15:32:48 2024 +0300 import evolution-ews-3.28.5-15.el8 diff --git a/.evolution-ews.metadata b/.evolution-ews.metadata new file mode 100644 index 0000000..a1d024b --- /dev/null +++ b/.evolution-ews.metadata @@ -0,0 +1 @@ +22caa0a3798f51757f88fd19c9ca74284f62b0d3 SOURCES/evolution-ews-3.28.5.tar.xz diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..87291d0 --- /dev/null +++ b/.gitignore @@ -0,0 +1 @@ +SOURCES/evolution-ews-3.28.5.tar.xz diff --git a/SOURCES/0001-I-250-Mail-Calendar-attachments-can-be-broken-by-the.patch b/SOURCES/0001-I-250-Mail-Calendar-attachments-can-be-broken-by-the.patch new file mode 100644 index 0000000..afd15bd --- /dev/null +++ b/SOURCES/0001-I-250-Mail-Calendar-attachments-can-be-broken-by-the.patch @@ -0,0 +1,57 @@ +diff -up evolution-ews-3.28.5/src/camel/camel-ews-folder.c.workaround-office365.com-bug evolution-ews-3.28.5/src/camel/camel-ews-folder.c +--- evolution-ews-3.28.5/src/camel/camel-ews-folder.c.workaround-office365.com-bug 2018-07-30 16:01:00.000000000 +0200 ++++ evolution-ews-3.28.5/src/camel/camel-ews-folder.c 2023-12-13 13:38:31.707627927 +0100 +@@ -355,7 +355,7 @@ ews_get_calendar_mime_part (CamelMimePar + } + + static gchar * +-ews_update_mgtrequest_mime_calendar_itemid (const gchar *mime_fname, ++ews_update_mtgrequest_mime_calendar_itemid (const gchar *mime_fname, + const EwsId *calendar_item_id, + gboolean is_calendar_UID, + const EwsId *mail_item_id, +@@ -414,6 +414,35 @@ ews_update_mgtrequest_mime_calendar_item + if (ba && ba->len) { + g_byte_array_append (ba, (guint8 *) "\0", 1); + icalcomp = icalparser_parse_string ((gchar *) ba->data); ++ if (!icalcomp) { ++ const gchar *content = (const gchar *) ba->data; ++ const gchar *begin_vcalendar, *end_vcalendar; ++ ++ /* Workaround Office365.com error, which returns invalid iCalendar object (without 'END:VCALENDAR'), ++ in the MimeContent's text/calendar attachments as of 2023-12-12. */ ++ begin_vcalendar = camel_strstrcase (content, "BEGIN:VCALENDAR"); ++ end_vcalendar = camel_strstrcase (content, "END:VCALENDAR"); ++ ++ /* If it exists, then it should be alone on a separate line */ ++ if (!(begin_vcalendar && (begin_vcalendar == content || begin_vcalendar[-1] == '\n') && ++ (begin_vcalendar[15 /* strlen ("BEGIN:VCALENDAR") */] == '\r' || begin_vcalendar[15] == '\n'))) ++ begin_vcalendar = NULL; ++ ++ /* If it exists, then it should be alone on a separate line and not at the very beginning of the mime_content */ ++ if (!(end_vcalendar && end_vcalendar > content && end_vcalendar[-1] == '\n' && ++ (end_vcalendar[13 /* strlen ("END:VCALENDAR") */] == '\r' || end_vcalendar[13] == '\n' || end_vcalendar[13] == '\0'))) ++ end_vcalendar = NULL; ++ ++ if (begin_vcalendar && !end_vcalendar) { ++ g_byte_array_remove_index (ba, ba->len - 1); ++ #define add_str(_str) g_byte_array_append (ba, (guint8 *) _str, strlen (_str)) ++ add_str ("\r\nEND:VCALENDAR\r\n"); ++ #undef add_str ++ g_byte_array_append (ba, (guint8 *) "\0", 1); ++ ++ icalcomp = icalparser_parse_string ((gchar *) ba->data); ++ } ++ } + } + if (icalcomp) { + icalcomponent *subcomp; +@@ -888,7 +917,7 @@ camel_ews_folder_get_message (CamelFolde + calendar_item_accept_id = e_ews_item_get_id (items->data); + is_calendar_UID = FALSE; + } +- mime_fname_new = ews_update_mgtrequest_mime_calendar_itemid (mime_content, calendar_item_accept_id, is_calendar_UID, e_ews_item_get_id (items->data), error); ++ mime_fname_new = ews_update_mtgrequest_mime_calendar_itemid (mime_content, calendar_item_accept_id, is_calendar_UID, e_ews_item_get_id (items->data), error); + if (mime_fname_new) + mime_content = (const gchar *) mime_fname_new; + diff --git a/SOURCES/0002-deadlock-on-op-cancel.patch b/SOURCES/0002-deadlock-on-op-cancel.patch new file mode 100644 index 0000000..8d7455f --- /dev/null +++ b/SOURCES/0002-deadlock-on-op-cancel.patch @@ -0,0 +1,28 @@ +diff -up evolution-ews-3.28.5/src/server/e-ews-connection.c.deadlock evolution-ews-3.28.5/src/server/e-ews-connection.c +--- evolution-ews-3.28.5/src/server/e-ews-connection.c.deadlock 2024-02-06 15:56:24.994038118 +0100 ++++ evolution-ews-3.28.5/src/server/e-ews-connection.c 2024-02-06 15:57:45.656178096 +0100 +@@ -698,22 +698,20 @@ ews_next_request (gpointer _cnc) + /* Add to active job queue */ + cnc->priv->active_job_queue = g_slist_append (cnc->priv->active_job_queue, node); + ++ QUEUE_UNLOCK (cnc); ++ + if (cnc->priv->soup_session) { + SoupMessage *msg = SOUP_MESSAGE (node->msg); + + if (!e_ews_connection_utils_prepare_message (cnc, msg, node->cancellable)) { + e_ews_debug_dump_raw_soup_request (msg); +- QUEUE_UNLOCK (cnc); + + ews_response_cb (cnc->priv->soup_session, msg, node); + } else { + e_ews_debug_dump_raw_soup_request (msg); + soup_session_queue_message (cnc->priv->soup_session, msg, ews_response_cb, node); +- QUEUE_UNLOCK (cnc); + } + } else { +- QUEUE_UNLOCK (cnc); +- + ews_cancel_request (NULL, node); + } + diff --git a/SOURCES/evolution-ews-3.28.5-autodiscover-improvements.patch b/SOURCES/evolution-ews-3.28.5-autodiscover-improvements.patch new file mode 100644 index 0000000..6b41ec3 --- /dev/null +++ b/SOURCES/evolution-ews-3.28.5-autodiscover-improvements.patch @@ -0,0 +1,783 @@ +diff -up evolution-ews-3.28.5/src/configuration/e-mail-config-ews-backend.c.autodiscover-improvements evolution-ews-3.28.5/src/configuration/e-mail-config-ews-backend.c +--- evolution-ews-3.28.5/src/configuration/e-mail-config-ews-backend.c.autodiscover-improvements 2022-05-06 09:52:48.565933224 +0200 ++++ evolution-ews-3.28.5/src/configuration/e-mail-config-ews-backend.c 2022-05-06 09:52:48.570933226 +0200 +@@ -661,8 +661,10 @@ mail_config_ews_backend_setup_defaults ( + camel_ews_settings_set_hosturl (ews_settings, hosturl); + camel_ews_settings_set_email (ews_settings, email_address); + ++ /* Prefill email address as the user name, it's needed for office365.com ++ server, but also on-premise servers support it. */ + network_settings = CAMEL_NETWORK_SETTINGS (settings); +- camel_network_settings_set_user (network_settings, parts[0]); ++ camel_network_settings_set_user (network_settings, email_address); + + g_free (hosturl); + } +diff -up evolution-ews-3.28.5/src/server/e-ews-connection.c.autodiscover-improvements evolution-ews-3.28.5/src/server/e-ews-connection.c +--- evolution-ews-3.28.5/src/server/e-ews-connection.c.autodiscover-improvements 2022-05-06 09:52:48.567933225 +0200 ++++ evolution-ews-3.28.5/src/server/e-ews-connection.c 2022-05-06 12:23:47.370716242 +0200 +@@ -468,6 +468,38 @@ autodiscover_parse_protocol (xmlNode *no + return FALSE; + } + ++static xmlChar * ++autodiscover_get_protocol_type (xmlNode *node) ++{ ++ for (node = node->children; node; node = node->next) { ++ if (node->type == XML_ELEMENT_NODE && ++ !strcmp ((gchar *) node->name, "Type")) { ++ return xmlNodeGetContent (node); ++ } ++ } ++ ++ return NULL; ++} ++ ++static gchar * ++autodiscover_dup_element_value (xmlNode *node, ++ const gchar *element_name) ++{ ++ for (node = node->children; node; node = node->next) { ++ if (node->type == XML_ELEMENT_NODE && ++ !g_strcmp0 ((gchar *) node->name, element_name)) { ++ xmlChar *str = xmlNodeGetContent (node); ++ gchar *res; ++ ++ res = g_strdup ((const gchar *) str); ++ xmlFree (str); ++ return res; ++ } ++ } ++ ++ return NULL; ++} ++ + static gint + comp_func (gconstpointer a, + gconstpointer b) +@@ -2916,11 +2948,16 @@ e_ews_autodiscover_ws_xml (const gchar * + struct _autodiscover_data { + EEwsConnection *cnc; + xmlOutputBuffer *buf; +- SoupMessage *msgs[5]; ++ SoupMessage *msgs[6]; + + GCancellable *cancellable; + gulong cancel_id; + ++ GError *error; ++ gchar *redirect_addr; ++ gchar *redirect_url; ++ gint n_redirects; ++ + /* Results */ + gchar *as_url; + gchar *oab_url; +@@ -2944,6 +2981,10 @@ autodiscover_data_free (struct _autodisc + its worker thread. */ + g_object_unref (ad->cnc); + ++ g_clear_error (&ad->error); ++ ++ g_free (ad->redirect_addr); ++ g_free (ad->redirect_url); + g_free (ad->as_url); + g_free (ad->oab_url); + +@@ -2957,6 +2998,28 @@ autodiscover_cancelled_cb (GCancellable + ews_connection_schedule_abort (cnc); + } + ++/* Frees only the content, not the 'urls' structure itself */ ++static void ++ews_urls_free_content (EwsUrls *urls) ++{ ++ if (!urls) ++ return; ++ ++ if (urls->as_url) ++ xmlFree (urls->as_url); ++ urls->as_url = NULL; ++ ++ if (urls->oab_url) ++ xmlFree (urls->oab_url); ++ urls->oab_url = NULL; ++} ++ ++static gboolean ++e_ews_discover_prepare_messages_and_send (GSimpleAsyncResult *simple, ++ const gchar *email_address, ++ const gchar *override_url, ++ GError **error); ++ + /* Called when each soup message completes */ + static void + autodiscover_response_cb (SoupSession *session, +@@ -2966,21 +3029,24 @@ autodiscover_response_cb (SoupSession *s + { + GSimpleAsyncResult *simple = data; + struct _autodiscover_data *ad; +- EwsUrls *urls = NULL; ++ EwsUrls exch_urls, expr_urls; + guint status = msg->status_code; + xmlDoc *doc; + xmlNode *node; ++ gchar *str; + gint idx; +- gboolean success = FALSE; + GError *error = NULL; + ++ memset (&exch_urls, 0, sizeof (EwsUrls)); ++ memset (&expr_urls, 0, sizeof (EwsUrls)); ++ + ad = g_simple_async_result_get_op_res_gpointer (simple); + +- for (idx = 0; idx < 5; idx++) { ++ for (idx = 0; idx < 6; idx++) { + if (ad->msgs[idx] == msg) + break; + } +- if (idx == 5) { ++ if (idx == 6 || (idx == 5 && !ad->msgs[5])) { + /* We already got removed (cancelled). Do nothing */ + goto unref; + } +@@ -3048,33 +3114,54 @@ autodiscover_response_cb (SoupSession *s + goto failed; + } + +- urls = g_new0 (EwsUrls, 1); ++ str = autodiscover_dup_element_value (node, "RedirectAddr"); ++ if (str) { ++ g_free (ad->redirect_addr); ++ ad->redirect_addr = str; ++ } ++ ++ str = autodiscover_dup_element_value (node, "RedirectUrl"); ++ if (str) { ++ g_free (ad->redirect_url); ++ ad->redirect_url = str; ++ } ++ + for (node = node->children; node; node = node->next) { + if (node->type == XML_ELEMENT_NODE && + !strcmp ((gchar *) node->name, "Protocol")) { +- success = autodiscover_parse_protocol (node, urls); +- /* Since the server may send back multiple nodes +- * don't break unless we found the both URLs. +- */ +- if (success) +- break; ++ xmlChar *protocol_type = autodiscover_get_protocol_type (node); ++ ++ if (g_strcmp0 ((const gchar *) protocol_type, "EXCH") == 0) { ++ ews_urls_free_content (&exch_urls); ++ autodiscover_parse_protocol (node, &exch_urls); ++ } else if (g_strcmp0 ((const gchar *) protocol_type, "EXPR") == 0) { ++ ews_urls_free_content (&expr_urls); ++ autodiscover_parse_protocol (node, &expr_urls); ++ ++ /* EXPR has precedence, thus stop once found both there */ ++ if (expr_urls.as_url && expr_urls.oab_url) { ++ xmlFree (protocol_type); ++ break; ++ } ++ } ++ ++ if (protocol_type) ++ xmlFree (protocol_type); + } + } + +- if (!success) { +- if (urls->as_url != NULL) +- xmlFree (urls->as_url); +- if (urls->oab_url != NULL) +- xmlFree (urls->oab_url); +- g_free (urls); ++ /* Make the optional */ ++ if (!exch_urls.as_url && !expr_urls.as_url) { ++ ews_urls_free_content (&exch_urls); ++ ews_urls_free_content (&expr_urls); + g_set_error ( + &error, EWS_CONNECTION_ERROR, -1, +- _("Failed to find and in autodiscover response")); ++ _("Failed to find in autodiscover response")); + goto failed; + } + + /* We have a good response; cancel all the others */ +- for (idx = 0; idx < 5; idx++) { ++ for (idx = 0; idx < 6; idx++) { + if (ad->msgs[idx]) { + SoupMessage *m = ad->msgs[idx]; + ad->msgs[idx] = NULL; +@@ -3082,35 +3169,130 @@ autodiscover_response_cb (SoupSession *s + } + } + +- if (urls->as_url != NULL) { +- ad->as_url = g_strdup ((gchar *) urls->as_url); +- xmlFree (urls->as_url); +- } +- +- if (urls->oab_url != NULL) { +- ad->oab_url = g_strdup ((gchar *) urls->oab_url); +- xmlFree (urls->oab_url); ++ if (expr_urls.as_url) { ++ if (ad->as_url) ++ g_free (ad->as_url); ++ ad->as_url = g_strdup ((gchar *) expr_urls.as_url); ++ } else if (exch_urls.as_url) { ++ if (ad->as_url) ++ g_free (ad->as_url); ++ ad->as_url = g_strdup ((gchar *) exch_urls.as_url); ++ } ++ ++ if (expr_urls.as_url && expr_urls.oab_url) { ++ if (ad->oab_url) ++ g_free (ad->oab_url); ++ ad->oab_url = g_strdup ((gchar *) expr_urls.oab_url); ++ } else if (!expr_urls.as_url && exch_urls.oab_url) { ++ if (ad->oab_url) ++ g_free (ad->oab_url); ++ ad->oab_url = g_strdup ((gchar *) exch_urls.oab_url); + } + +- g_free (urls); ++ ews_urls_free_content (&exch_urls); ++ ews_urls_free_content (&expr_urls); + + goto exit; + + failed: +- for (idx = 0; idx < 5; idx++) { ++ for (idx = 0; idx < 6; idx++) { + if (ad->msgs[idx]) { ++ /* Preserve any Unauthorized/SSL failed errors */ ++ if (!g_error_matches (error, SOUP_HTTP_ERROR, SOUP_STATUS_NONE) && ++ !g_error_matches (ad->error, SOUP_HTTP_ERROR, SOUP_STATUS_UNAUTHORIZED) && ++ !g_error_matches (ad->error, SOUP_HTTP_ERROR, SOUP_STATUS_SSL_FAILED) && ++ (!ad->error || ++ g_error_matches (error, SOUP_HTTP_ERROR, SOUP_STATUS_UNAUTHORIZED) || ++ g_error_matches (error, SOUP_HTTP_ERROR, SOUP_STATUS_SSL_FAILED) || ++ g_error_matches (ad->error, SOUP_HTTP_ERROR, SOUP_STATUS_NONE) || ++ g_error_matches (ad->error, SOUP_HTTP_ERROR, SOUP_STATUS_CANT_RESOLVE) || ++ g_error_matches (ad->error, SOUP_HTTP_ERROR, SOUP_STATUS_CANT_RESOLVE_PROXY) || ++ g_error_matches (ad->error, SOUP_HTTP_ERROR, SOUP_STATUS_CANT_CONNECT) || ++ g_error_matches (ad->error, SOUP_HTTP_ERROR, SOUP_STATUS_CANT_CONNECT_PROXY))) { ++ g_clear_error (&ad->error); ++ ad->error = error; ++ error = NULL; ++ } else { ++ g_clear_error (&error); ++ } ++ + /* There's another request outstanding. + * Hope that it has better luck. */ +- g_clear_error (&error); + goto unref; + } + } + ++ /* Preserve any Unauthorized/SSL failed errors */ ++ if (!g_error_matches (error, SOUP_HTTP_ERROR, SOUP_STATUS_NONE) && ++ !g_error_matches (ad->error, SOUP_HTTP_ERROR, SOUP_STATUS_UNAUTHORIZED) && ++ !g_error_matches (ad->error, SOUP_HTTP_ERROR, SOUP_STATUS_SSL_FAILED) && ++ (!ad->error || ++ g_error_matches (error, SOUP_HTTP_ERROR, SOUP_STATUS_UNAUTHORIZED) || ++ g_error_matches (error, SOUP_HTTP_ERROR, SOUP_STATUS_SSL_FAILED) || ++ g_error_matches (ad->error, SOUP_HTTP_ERROR, SOUP_STATUS_NONE) || ++ g_error_matches (ad->error, SOUP_HTTP_ERROR, SOUP_STATUS_CANT_RESOLVE) || ++ g_error_matches (ad->error, SOUP_HTTP_ERROR, SOUP_STATUS_CANT_RESOLVE_PROXY) || ++ g_error_matches (ad->error, SOUP_HTTP_ERROR, SOUP_STATUS_CANT_CONNECT) || ++ g_error_matches (ad->error, SOUP_HTTP_ERROR, SOUP_STATUS_CANT_CONNECT_PROXY))) { ++ g_clear_error (&ad->error); ++ ad->error = error; ++ error = NULL; ++ } ++ ++ g_clear_error (&error); ++ ++ if (!g_cancellable_is_cancelled (ad->cancellable) && ++ (!ad->as_url || !ad->oab_url) && ad->n_redirects < 11 && ++ (ad->redirect_url || ad->redirect_addr) && ++ !g_error_matches (ad->error, SOUP_HTTP_ERROR, SOUP_STATUS_UNAUTHORIZED) && ++ !g_error_matches (ad->error, SOUP_HTTP_ERROR, SOUP_STATUS_SSL_FAILED)) { ++ CamelEwsSettings *settings = NULL; ++ gboolean re_scheduled; ++ const gchar *host_url; ++ gchar *redirect_addr, *redirect_url; ++ GError *local_error; ++ ++ redirect_addr = ad->redirect_addr; ++ redirect_url = ad->redirect_url; ++ local_error = ad->error; ++ ++ /* To avoid infinite recursion */ ++ ad->redirect_addr = NULL; ++ ad->redirect_url = NULL; ++ ad->n_redirects++; ++ ad->error = NULL; ++ ++ host_url = redirect_url; ++ settings = e_ews_connection_ref_settings (ad->cnc); ++ ++ if (!host_url) ++ host_url = camel_ews_settings_get_hosturl (settings); ++ ++ if (redirect_addr && *redirect_addr) ++ camel_network_settings_set_user (CAMEL_NETWORK_SETTINGS (settings), redirect_addr); ++ ++ re_scheduled = e_ews_discover_prepare_messages_and_send (simple, redirect_addr, host_url, NULL); ++ ++ g_clear_object (&settings); ++ ++ g_free (redirect_addr); ++ g_free (redirect_url); ++ ++ if (re_scheduled) { ++ g_clear_error (&local_error); ++ goto unref; ++ } ++ ++ ad->error = local_error; ++ } ++ + /* FIXME: We're actually returning the *last* error here, + * and in some cases (stupid firewalls causing timeouts) + * that's going to be the least interesting one. We probably + * want the *first* error */ +- g_simple_async_result_take_error (simple, error); ++ g_simple_async_result_take_error (simple, ad->error); ++ ++ ad->error = NULL; + + exit: + g_simple_async_result_complete_in_idle (simple); +@@ -3125,7 +3307,10 @@ autodiscover_response_cb (SoupSession *s + e_ews_connection_utils_unref_in_thread (simple); + } + +-static void post_restarted (SoupMessage *msg, gpointer data) ++ ++static void ++post_restarted (SoupMessage *msg, ++ gpointer data) + { + xmlOutputBuffer *buf = data; + +@@ -3151,12 +3336,12 @@ static void post_restarted (SoupMessage + + static SoupMessage * + e_ews_get_msg_for_url (EEwsConnection *cnc, +- CamelEwsSettings *settings, + const gchar *url, + xmlOutputBuffer *buf, + GError **error) + { + SoupMessage *msg; ++ CamelEwsSettings *settings; + + if (url == NULL) { + g_set_error_literal ( +@@ -3178,7 +3363,9 @@ e_ews_get_msg_for_url (EEwsConnection *c + + e_ews_message_attach_chunk_allocator (msg); + ++ settings = e_ews_connection_ref_settings (cnc); + e_ews_message_set_user_agent_header (msg, settings); ++ g_clear_object (&settings); + + if (buf != NULL) { + soup_message_set_request ( +@@ -3189,7 +3376,7 @@ e_ews_get_msg_for_url (EEwsConnection *c + #else + buf->buffer->content, buf->buffer->use + #endif +- ); ++ ); + g_signal_connect ( + msg, "restarted", + G_CALLBACK (post_restarted), buf); +@@ -3200,6 +3387,69 @@ e_ews_get_msg_for_url (EEwsConnection *c + return msg; + } + ++static void ++autodiscover_srv_record_resolved_cb (GObject *source, ++ GAsyncResult *result, ++ gpointer user_data) ++{ ++ GList *targets, *link; ++ GSimpleAsyncResult *simple = user_data; ++ struct _autodiscover_data *ad; ++ gchar *new_uri = NULL; ++ gboolean success; ++ ++ ad = g_simple_async_result_get_op_res_gpointer (simple); ++ ++ g_return_if_fail (ad != NULL); ++ ++ targets = g_resolver_lookup_service_finish (G_RESOLVER (source), result, NULL); ++ ++ success = ad->msgs[5] && targets; ++ ++ for (link = targets; link && success; link = g_list_next (link)) { ++ GSrvTarget *target = link->data; ++ const gchar *hostname; ++ ++ hostname = g_srv_target_get_hostname (target); ++ ++ switch (g_srv_target_get_port (target)) { ++ case 80: ++ link = NULL; ++ new_uri = g_strdup_printf ("http://%s/autodiscover/autodiscover.xml", hostname); ++ break; ++ case 443: ++ link = NULL; ++ new_uri = g_strdup_printf ("https://%s/autodiscover/autodiscover.xml", hostname); ++ break; ++ } ++ } ++ ++ g_list_free_full (targets, (GDestroyNotify) g_srv_target_free); ++ ++ if (new_uri && success) { ++ SoupURI *suri; ++ ++ suri = soup_uri_new (new_uri); ++ if (suri) { ++ soup_message_set_uri (ad->msgs[5], suri); ++ /* The autodiscover_response_cb will free the 'simple' */ ++ ews_connection_schedule_queue_message (ad->cnc, ad->msgs[5], autodiscover_response_cb, simple); ++ soup_uri_free (suri); ++ } else { ++ success = FALSE; ++ } ++ } else { ++ success = FALSE; ++ } ++ ++ g_free (new_uri); ++ ++ if (!success) { ++ /* The callback also frees the 'simple' */ ++ autodiscover_response_cb (NULL, ad->msgs[5], simple); ++ } ++} ++ + gboolean + e_ews_autodiscover_ws_url_sync (ESource *source, + CamelEwsSettings *settings, +@@ -3232,50 +3482,43 @@ e_ews_autodiscover_ws_url_sync (ESource + return success; + } + +-void +-e_ews_autodiscover_ws_url (ESource *source, +- CamelEwsSettings *settings, +- const gchar *email_address, +- const gchar *password, +- GCancellable *cancellable, +- GAsyncReadyCallback callback, +- gpointer user_data) ++static gboolean ++e_ews_discover_prepare_messages_and_send (GSimpleAsyncResult *simple, ++ const gchar *email_address, ++ const gchar *override_url, ++ GError **error) + { +- GSimpleAsyncResult *simple; +- struct _autodiscover_data *ad; +- xmlOutputBuffer *buf; +- gchar *url1, *url2, *url3, *url4, *url5; +- gchar *domain; +- xmlDoc *doc; +- EEwsConnection *cnc; + SoupURI *soup_uri = NULL; + gboolean use_secure = TRUE; +- const gchar *host_url; +- GError *error = NULL; ++ gboolean is_outlook = FALSE; ++ gchar *url1, *url2, *url3, *url4; ++ const gchar *url5, *domain = NULL; ++ struct _autodiscover_data *ad; ++ GError *local_error = NULL; + +- g_return_if_fail (CAMEL_IS_EWS_SETTINGS (settings)); +- g_return_if_fail (email_address != NULL); +- g_return_if_fail (password != NULL); ++ ad = g_simple_async_result_get_op_res_gpointer (simple); ++ g_return_val_if_fail (ad != NULL, FALSE); + +- simple = g_simple_async_result_new ( +- G_OBJECT (settings), callback, +- user_data, e_ews_autodiscover_ws_url); ++ if (email_address) { ++ xmlDoc *doc; + +- domain = strchr (email_address, '@'); +- if (domain == NULL || *domain == '\0') { +- g_simple_async_result_set_error ( +- simple, EWS_CONNECTION_ERROR, -1, +- "%s", _("Email address is missing a domain part")); +- g_simple_async_result_complete_in_idle (simple); +- g_object_unref (simple); +- return; ++ if (ad->buf) ++ xmlOutputBufferClose (ad->buf); ++ ++ doc = e_ews_autodiscover_ws_xml (email_address); ++ ad->buf = xmlAllocOutputBuffer (NULL); ++ xmlNodeDumpOutput (ad->buf, doc, xmlDocGetRootElement (doc), 0, 1, NULL); ++ xmlOutputBufferFlush (ad->buf); ++ ++ xmlFreeDoc (doc); ++ ++ domain = strchr (email_address, '@'); ++ if (domain) ++ domain++; + } +- domain++; + +- doc = e_ews_autodiscover_ws_xml (email_address); +- buf = xmlAllocOutputBuffer (NULL); +- xmlNodeDumpOutput (buf, doc, xmlDocGetRootElement (doc), 0, 1, NULL); +- xmlOutputBufferFlush (buf); ++ g_return_val_if_fail (ad->buf != NULL, FALSE); ++ g_return_val_if_fail ((domain && *domain) || (override_url && *override_url), FALSE); + + url1 = NULL; + url2 = NULL; +@@ -3283,11 +3526,10 @@ e_ews_autodiscover_ws_url (ESource *sour + url4 = NULL; + url5 = NULL; + +- host_url = camel_ews_settings_get_hosturl (settings); +- if (host_url != NULL) +- soup_uri = soup_uri_new (host_url); ++ if (override_url) ++ soup_uri = soup_uri_new (override_url); + +- if (soup_uri != NULL) { ++ if (soup_uri) { + const gchar *host = soup_uri_get_host (soup_uri); + const gchar *scheme = soup_uri_get_scheme (soup_uri); + +@@ -3296,20 +3538,126 @@ e_ews_autodiscover_ws_url (ESource *sour + url1 = g_strdup_printf ("http%s://%s/autodiscover/autodiscover.xml", use_secure ? "s" : "", host); + url2 = g_strdup_printf ("http%s://autodiscover.%s/autodiscover/autodiscover.xml", use_secure ? "s" : "", host); + ++ is_outlook = host && g_ascii_strcasecmp (host, "outlook.office365.com") == 0; ++ + /* outlook.office365.com has its autodiscovery at outlook.com */ +- if (host && g_ascii_strcasecmp (host, "outlook.office365.com") == 0 && +- domain && g_ascii_strcasecmp (host, "outlook.com") != 0) { +- url5 = g_strdup_printf ("https://outlook.com/autodiscover/autodiscover.xml"); ++ if (is_outlook && domain && g_ascii_strcasecmp (domain, "outlook.com") != 0) { ++ url5 = "https://outlook.com/autodiscover/autodiscover.xml"; ++ } else if (!is_outlook && domain) { ++ #define ON_MICROSOFT_COM_TEXT "onmicrosoft.com" ++ gint len = strlen (domain); ++ gint onmslen = strlen (ON_MICROSOFT_COM_TEXT); ++ ++ if (len >= onmslen) { ++ const gchar *test_domain; ++ ++ test_domain = domain + len - onmslen; ++ ++ /* onmicrosoft.com addresses might be handled on the outlook.com/office365.com as well */ ++ if (g_ascii_strcasecmp (test_domain, ON_MICROSOFT_COM_TEXT) == 0 && ++ (len == onmslen || (len > onmslen && domain[len - onmslen - 1] == '.'))) ++ url5 = "https://outlook.com/autodiscover/autodiscover.xml"; ++ } ++ #undef ON_MICROSOFT_COM_TEXT + } + + soup_uri_free (soup_uri); + } + +- url3 = g_strdup_printf ("http%s://%s/autodiscover/autodiscover.xml", use_secure ? "s" : "", domain); +- url4 = g_strdup_printf ("http%s://autodiscover.%s/autodiscover/autodiscover.xml", use_secure ? "s" : "", domain); ++ is_outlook = is_outlook || (domain && g_ascii_strcasecmp (domain, "outlook.com") == 0); ++ ++ if (domain) { ++ url3 = g_strdup_printf ("http%s://%s/autodiscover/autodiscover.xml", use_secure ? "s" : "", domain); ++ url4 = g_strdup_printf ("http%s://autodiscover.%s/autodiscover/autodiscover.xml", use_secure ? "s" : "", domain); ++ } ++ ++ /* Passing a NULL URL string returns NULL. */ ++ ad->msgs[0] = e_ews_get_msg_for_url (ad->cnc, url1, ad->buf, &local_error); ++ ad->msgs[1] = e_ews_get_msg_for_url (ad->cnc, url2, ad->buf, local_error ? NULL : &local_error); ++ ad->msgs[2] = e_ews_get_msg_for_url (ad->cnc, url3, ad->buf, local_error ? NULL : &local_error); ++ ad->msgs[3] = e_ews_get_msg_for_url (ad->cnc, url4, ad->buf, local_error ? NULL : &local_error); ++ ad->msgs[4] = e_ews_get_msg_for_url (ad->cnc, url5, ad->buf, local_error ? NULL : &local_error); ++ ++ if (!is_outlook && domain && *domain && (ad->msgs[0] || ad->msgs[1] || ad->msgs[2] || ad->msgs[3] || ad->msgs[4])) { ++ gchar *tmp; ++ ++ tmp = g_strdup_printf ("http%s://%s/", use_secure ? "s" : "", domain); ++ ++ /* Fake SoupMessage, for the autodiscovery with SRV record help */ ++ ad->msgs[5] = e_ews_get_msg_for_url (ad->cnc, tmp, ad->buf, local_error ? NULL : &local_error); ++ ++ if (ad->msgs[5]) { ++ g_resolver_lookup_service_async (g_resolver_get_default (), "autodiscover", "tcp", domain, ad->cancellable, ++ autodiscover_srv_record_resolved_cb, g_object_ref (simple)); ++ } ++ ++ g_free (tmp); ++ } else { ++ ad->msgs[5] = NULL; ++ } ++ ++ if (local_error && (ad->msgs[0] || ad->msgs[1] || ad->msgs[2] || ad->msgs[3] || ad->msgs[4])) ++ g_clear_error (&local_error); ++ ++ /* These have to be submitted only after they're both set in ad->msgs[] ++ * or there will be races with fast completion */ ++ if (ad->msgs[0] != NULL) ++ ews_connection_schedule_queue_message (ad->cnc, ad->msgs[0], autodiscover_response_cb, g_object_ref (simple)); ++ if (ad->msgs[1] != NULL) ++ ews_connection_schedule_queue_message (ad->cnc, ad->msgs[1], autodiscover_response_cb, g_object_ref (simple)); ++ if (ad->msgs[2] != NULL) ++ ews_connection_schedule_queue_message (ad->cnc, ad->msgs[2], autodiscover_response_cb, g_object_ref (simple)); ++ if (ad->msgs[3] != NULL) ++ ews_connection_schedule_queue_message (ad->cnc, ad->msgs[3], autodiscover_response_cb, g_object_ref (simple)); ++ if (ad->msgs[4] != NULL) ++ ews_connection_schedule_queue_message (ad->cnc, ad->msgs[4], autodiscover_response_cb, g_object_ref (simple)); ++ ++ g_free (url1); ++ g_free (url2); ++ g_free (url3); ++ g_free (url4); ++ ++ if (local_error) { ++ g_propagate_error (error, local_error); ++ return FALSE; ++ } ++ ++ return TRUE; ++} ++ ++void ++e_ews_autodiscover_ws_url (ESource *source, ++ CamelEwsSettings *settings, ++ const gchar *email_address, ++ const gchar *password, ++ GCancellable *cancellable, ++ GAsyncReadyCallback callback, ++ gpointer user_data) ++{ ++ GSimpleAsyncResult *simple; ++ struct _autodiscover_data *ad; ++ const gchar *domain; ++ const gchar *host_url; ++ GError *error = NULL; ++ ++ g_return_if_fail (CAMEL_IS_EWS_SETTINGS (settings)); ++ g_return_if_fail (email_address != NULL); ++ g_return_if_fail (password != NULL); ++ ++ simple = g_simple_async_result_new ( ++ G_OBJECT (settings), callback, ++ user_data, e_ews_autodiscover_ws_url); + +- cnc = e_ews_connection_new (source, url3, settings); +- e_ews_connection_set_password (cnc, password); ++ domain = strchr (email_address, '@'); ++ /* if it's non-NULL, then domain[0] == '@' */ ++ if (!domain || !domain[1]) { ++ g_simple_async_result_set_error ( ++ simple, EWS_CONNECTION_ERROR, -1, ++ "%s", _("Email address is missing a domain part")); ++ g_simple_async_result_complete_in_idle (simple); ++ g_object_unref (simple); ++ return; ++ } + + /* + * http://msdn.microsoft.com/en-us/library/ee332364.aspx says we are +@@ -3320,48 +3668,25 @@ e_ews_autodiscover_ws_url (ESource *sour + * (successful) one win. + */ + ad = g_slice_new0 (struct _autodiscover_data); +- ad->cnc = cnc; /* takes ownership */ +- ad->buf = buf; /* takes ownership */ ++ ad->cnc = e_ews_connection_new (source, domain + 1, settings); /* Fake URI, it's not used here */ ++ g_object_set (ad->cnc->priv->soup_session, SOUP_SESSION_TIMEOUT, 20, NULL); ++ e_ews_connection_set_password (ad->cnc, password); + + if (G_IS_CANCELLABLE (cancellable)) { + ad->cancellable = g_object_ref (cancellable); + ad->cancel_id = g_cancellable_connect ( + ad->cancellable, + G_CALLBACK (autodiscover_cancelled_cb), +- g_object_ref (cnc), ++ g_object_ref (ad->cnc), + g_object_unref); + } + + g_simple_async_result_set_op_res_gpointer ( + simple, ad, (GDestroyNotify) autodiscover_data_free); + +- /* Passing a NULL URL string returns NULL. */ +- ad->msgs[0] = e_ews_get_msg_for_url (cnc, settings, url1, buf, &error); +- ad->msgs[1] = e_ews_get_msg_for_url (cnc, settings, url2, buf, NULL); +- ad->msgs[2] = e_ews_get_msg_for_url (cnc, settings, url3, buf, NULL); +- ad->msgs[3] = e_ews_get_msg_for_url (cnc, settings, url4, buf, NULL); +- ad->msgs[4] = e_ews_get_msg_for_url (cnc, settings, url5, buf, NULL); +- +- /* These have to be submitted only after they're both set in ad->msgs[] +- * or there will be races with fast completion */ +- if (ad->msgs[0] != NULL) +- ews_connection_schedule_queue_message (cnc, ad->msgs[0], autodiscover_response_cb, g_object_ref (simple)); +- if (ad->msgs[1] != NULL) +- ews_connection_schedule_queue_message (cnc, ad->msgs[1], autodiscover_response_cb, g_object_ref (simple)); +- if (ad->msgs[2] != NULL) +- ews_connection_schedule_queue_message (cnc, ad->msgs[2], autodiscover_response_cb, g_object_ref (simple)); +- if (ad->msgs[3] != NULL) +- ews_connection_schedule_queue_message (cnc, ad->msgs[3], autodiscover_response_cb, g_object_ref (simple)); +- if (ad->msgs[4] != NULL) +- ews_connection_schedule_queue_message (cnc, ad->msgs[4], autodiscover_response_cb, g_object_ref (simple)); +- +- xmlFreeDoc (doc); +- g_free (url1); +- g_free (url2); +- g_free (url3); +- g_free (url4); ++ host_url = camel_ews_settings_get_hosturl (settings); + +- if (error && !ad->msgs[0] && !ad->msgs[1] && !ad->msgs[2] && !ad->msgs[3] && !ad->msgs[4]) { ++ if (!e_ews_discover_prepare_messages_and_send (simple, email_address, host_url, &error)) { + g_simple_async_result_take_error (simple, error); + g_simple_async_result_complete_in_idle (simple); + } else { +@@ -3735,7 +4060,7 @@ e_ews_connection_get_oal_list (EEwsConne + + g_return_if_fail (E_IS_EWS_CONNECTION (cnc)); + +- soup_message = e_ews_get_msg_for_url (cnc, cnc->priv->settings, cnc->priv->uri, NULL, &error); ++ soup_message = e_ews_get_msg_for_url (cnc, cnc->priv->uri, NULL, &error); + + simple = g_simple_async_result_new ( + G_OBJECT (cnc), callback, user_data, +@@ -3856,7 +4181,7 @@ e_ews_connection_get_oal_detail (EEwsCon + + g_return_if_fail (E_IS_EWS_CONNECTION (cnc)); + +- soup_message = e_ews_get_msg_for_url (cnc, cnc->priv->settings, cnc->priv->uri, NULL, &error); ++ soup_message = e_ews_get_msg_for_url (cnc, cnc->priv->uri, NULL, &error); + + simple = g_simple_async_result_new ( + G_OBJECT (cnc), callback, user_data, +@@ -4073,7 +4398,7 @@ e_ews_connection_download_oal_file (EEws + + g_return_if_fail (E_IS_EWS_CONNECTION (cnc)); + +- soup_message = e_ews_get_msg_for_url (cnc, cnc->priv->settings, cnc->priv->uri, NULL, &error); ++ soup_message = e_ews_get_msg_for_url (cnc, cnc->priv->uri, NULL, &error); + + simple = g_simple_async_result_new ( + G_OBJECT (cnc), callback, user_data, diff --git a/SOURCES/evolution-ews-3.28.5-birthday-date.patch b/SOURCES/evolution-ews-3.28.5-birthday-date.patch new file mode 100644 index 0000000..751d0ee --- /dev/null +++ b/SOURCES/evolution-ews-3.28.5-birthday-date.patch @@ -0,0 +1,262 @@ +diff -up evolution-ews-3.28.5/src/addressbook/CMakeLists.txt.birthday-date evolution-ews-3.28.5/src/addressbook/CMakeLists.txt +--- evolution-ews-3.28.5/src/addressbook/CMakeLists.txt.birthday-date 2018-07-30 16:01:00.000000000 +0200 ++++ evolution-ews-3.28.5/src/addressbook/CMakeLists.txt 2019-10-23 14:13:13.158137514 +0200 +@@ -45,6 +45,7 @@ target_compile_options(ebookbackendews P + ${LIBEBOOK_CFLAGS} + ${LIBEDATABOOK_CFLAGS} + ${LIBEDATASERVER_CFLAGS} ++ ${LIBICAL_CFLAGS} + ${MSPACK_CFLAGS} + ${SOUP_CFLAGS} + ) +@@ -60,6 +61,7 @@ target_include_directories(ebookbackende + ${LIBEBOOK_INCLUDE_DIRS} + ${LIBEDATABOOK_INCLUDE_DIRS} + ${LIBEDATASERVER_INCLUDE_DIRS} ++ ${LIBICAL_INCLUDE_DIRS} + ${MSPACK_INCLUDE_DIRS} + ${SOUP_INCLUDE_DIRS} + ) +@@ -70,6 +72,7 @@ target_link_libraries(ebookbackendews + ${LIBEBOOK_LDFLAGS} + ${LIBEDATABOOK_LDFLAGS} + ${LIBEDATASERVER_LDFLAGS} ++ ${LIBICAL_LDFLAGS} + ${MATH_LDFLAGS} + ${MSPACK_LDFLAGS} + ${SOUP_LDFLAGS} +diff -up evolution-ews-3.28.5/src/addressbook/e-book-backend-ews.c.birthday-date evolution-ews-3.28.5/src/addressbook/e-book-backend-ews.c +--- evolution-ews-3.28.5/src/addressbook/e-book-backend-ews.c.birthday-date 2019-10-23 14:04:31.100144733 +0200 ++++ evolution-ews-3.28.5/src/addressbook/e-book-backend-ews.c 2019-10-23 14:15:41.719135459 +0200 +@@ -37,6 +37,8 @@ + #include + #include + ++#include ++ + #include + + #include "server/e-ews-item-change.h" +@@ -268,29 +270,36 @@ ebews_populate_nick_name (EBookBackendEw + } + + static void ++ebews_populate_date_value (EBookBackendEws *bbews, ++ EContact *contact, ++ EContactField field, ++ time_t value) ++{ ++ if (value > (time_t) 0) { ++ struct icaltimetype itt; ++ ++ itt = icaltime_from_timet_with_zone (value, TRUE, icaltimezone_get_utc_timezone ()); ++ ++ if (icaltime_is_valid_time (itt) && !icaltime_is_null_time (itt)) { ++ EContactDate edate = { 0 }; ++ ++ edate.year = itt.year; ++ edate.month = itt.month; ++ edate.day = itt.day; ++ ++ e_contact_set (contact, field, &edate); ++ } ++ } ++} ++ ++static void + ebews_populate_birth_date (EBookBackendEws *bbews, + EContact *contact, + EEwsItem *item, + GCancellable *cancellable, + GError **error) + { +- time_t bdate; +- GDate date; +- EContactDate edate; +- +- bdate = e_ews_item_get_birthday (item); +- +- if (bdate) { +- g_date_clear (&date, 1); +- g_date_set_time_t (&date, bdate); +- +- edate.year = date.year; +- edate.month = date.month; +- edate.day = date.day; +- +- if (g_date_valid (&date)) +- e_contact_set (contact, E_CONTACT_BIRTH_DATE, &edate); +- } ++ ebews_populate_date_value (bbews, contact, E_CONTACT_BIRTH_DATE, e_ews_item_get_birthday (item)); + } + + static void +@@ -300,23 +309,7 @@ ebews_populate_anniversary (EBookBackend + GCancellable *cancellable, + GError **error) + { +- time_t bdate; +- GDate date; +- EContactDate edate; +- +- bdate = e_ews_item_get_wedding_anniversary (item); +- +- if (bdate) { +- g_date_clear (&date, 1); +- g_date_set_time_t (&date, bdate); +- +- edate.year = date.year; +- edate.month = date.month; +- edate.day = date.day; +- +- if (g_date_valid (&date)) +- e_contact_set (contact, E_CONTACT_ANNIVERSARY, &edate); +- } ++ ebews_populate_date_value (bbews, contact, E_CONTACT_ANNIVERSARY, e_ews_item_get_wedding_anniversary (item)); + } + + static EContactPhoto * +@@ -600,34 +593,41 @@ ebews_set_full_name (ESoapMessage *msg, + e_contact_name_free (name); + } + +-/* TODO Set birth and anniversary dates */ + static void +-ebews_set_birth_date (ESoapMessage *message, +- EContact *contact) ++ebews_set_date_value (ESoapMessage *message, ++ EContact *contact, ++ EContactField field, ++ const gchar *element_name) + { + EContactDate *date; +- gchar *birthday; ++ gchar *value; + +- date = e_contact_get (contact, E_CONTACT_BIRTH_DATE); ++ date = e_contact_get (contact, field); + + if (!date) + return; + +- birthday = g_strdup_printf ( +- "%04d-%02d-%02dT00:00:00", ++ value = g_strdup_printf ("%04d-%02d-%02dT00:00:00Z", + date->year, date->month, date->day); + +- e_ews_message_write_string_parameter (message, "Birthday", NULL, birthday); ++ e_ews_message_write_string_parameter (message, element_name, NULL, value); + +- g_free (birthday); ++ e_contact_date_free (date); ++ g_free (value); ++} + ++static void ++ebews_set_birth_date (ESoapMessage *message, ++ EContact *contact) ++{ ++ ebews_set_date_value (message, contact, E_CONTACT_BIRTH_DATE, "Birthday"); + } + + static void + ebews_set_anniversary (ESoapMessage *message, + EContact *contact) + { +- ++ ebews_set_date_value (message, contact, E_CONTACT_ANNIVERSARY, "WeddingAnniversary"); + } + + static void +@@ -838,30 +838,33 @@ ebews_set_full_name_changes (EBookBacken + } + + static void +-ebews_set_birth_date_changes (EBookBackendEws *bbews, +- ESoapMessage *message, ++ebews_set_date_value_changes (ESoapMessage *message, + EContact *new, + EContact *old, +- gchar **out_new_change_key, +- GCancellable *cancellable, +- GError **error) ++ EContactField field, ++ const gchar *element_name) + { + EContactDate *new_date, *old_date; +- gchar *birthday; + + if (!message) + return; + +- new_date = e_contact_get (new, E_CONTACT_BIRTH_DATE); +- old_date = e_contact_get (old, E_CONTACT_BIRTH_DATE); ++ new_date = e_contact_get (new, field); ++ old_date = e_contact_get (old, field); + + if (!e_contact_date_equal (new_date, old_date)) { +- birthday = g_strdup_printf ( +- "%04d-%02d-%02dT00:00:00", +- new_date->year, new_date->month, new_date->day); ++ if (new_date) { ++ gchar *value; ++ ++ value = g_strdup_printf ("%04d-%02d-%02dT00:00:00Z", ++ new_date->year, new_date->month, new_date->day); ++ ++ convert_contact_property_to_updatexml (message, element_name, value, "contacts", NULL, NULL); + +- convert_contact_property_to_updatexml (message, "Birthday", birthday, "contacts", NULL, NULL); +- g_free (birthday); ++ g_free (value); ++ } else { ++ e_ews_message_add_delete_item_field (message, element_name, "contacts"); ++ } + } + + e_contact_date_free (new_date); +@@ -869,6 +872,18 @@ ebews_set_birth_date_changes (EBookBacke + } + + static void ++ebews_set_birth_date_changes (EBookBackendEws *bbews, ++ ESoapMessage *message, ++ EContact *new, ++ EContact *old, ++ gchar **out_new_change_key, ++ GCancellable *cancellable, ++ GError **error) ++{ ++ ebews_set_date_value_changes (message, new, old, E_CONTACT_BIRTH_DATE, "Birthday"); ++} ++ ++static void + ebews_set_anniversary_changes (EBookBackendEws *bbews, + ESoapMessage *message, + EContact *new, +@@ -877,7 +892,7 @@ ebews_set_anniversary_changes (EBookBack + GCancellable *cancellable, + GError **error) + { +- ++ ebews_set_date_value_changes (message, new, old, E_CONTACT_ANNIVERSARY, "WeddingAnniversary"); + } + + static void +@@ -1373,7 +1388,7 @@ static const struct field_element_mappin + { E_CONTACT_SPOUSE, ELEMENT_TYPE_SIMPLE, "SpouseName", e_ews_item_get_spouse_name}, + { E_CONTACT_FAMILY_NAME, ELEMENT_TYPE_SIMPLE, "Surname", e_ews_item_get_surname}, + { E_CONTACT_GIVEN_NAME, ELEMENT_TYPE_COMPLEX, "GivenName", NULL, ebews_populate_givenname, ebews_set_givenname, ebews_set_givenname_changes}, +- { E_CONTACT_BIRTH_DATE, ELEMENT_TYPE_COMPLEX, "WeddingAnniversary", NULL, ebews_populate_anniversary, ebews_set_anniversary, ebews_set_anniversary_changes }, ++ { E_CONTACT_ANNIVERSARY, ELEMENT_TYPE_COMPLEX, "WeddingAnniversary", NULL, ebews_populate_anniversary, ebews_set_anniversary, ebews_set_anniversary_changes }, + { E_CONTACT_PHOTO, ELEMENT_TYPE_COMPLEX, "Photo", NULL, ebews_populate_photo, ebews_set_photo, ebews_set_photo_changes }, + + /* Should take of uid and changekey (REV) */ +@@ -3515,6 +3530,7 @@ ebb_ews_get_backend_property (EBookBacke + e_contact_field_name (E_CONTACT_ADDRESS_WORK), + e_contact_field_name (E_CONTACT_ADDRESS_HOME), + e_contact_field_name (E_CONTACT_ADDRESS_OTHER), ++ e_contact_field_name (E_CONTACT_ANNIVERSARY), + e_contact_field_name (E_CONTACT_BIRTH_DATE), + e_contact_field_name (E_CONTACT_NOTE), + e_contact_field_name (E_CONTACT_PHOTO), diff --git a/SOURCES/evolution-ews-3.28.5-cmake-variable-name-comparison.patch b/SOURCES/evolution-ews-3.28.5-cmake-variable-name-comparison.patch new file mode 100644 index 0000000..88b9a11 --- /dev/null +++ b/SOURCES/evolution-ews-3.28.5-cmake-variable-name-comparison.patch @@ -0,0 +1,44 @@ +diff -up evolution-data-server-3.28.5/cmake/modules/PrintableOptions.cmake.cmake-variable-name-comparison evolution-data-server-3.28.5/cmake/modules/PrintableOptions.cmake +--- evolution-data-server-3.28.5/cmake/modules/PrintableOptions.cmake.cmake-variable-name-comparison 2021-06-16 16:45:58.554763738 +0200 ++++ evolution-data-server-3.28.5/cmake/modules/PrintableOptions.cmake 2021-06-16 16:47:13.343021509 +0200 +@@ -19,32 +19,32 @@ + # prints all the build options previously added with the above functions + + macro(add_printable_variable_bare _name) +- if(_name STREQUAL "") ++ if("${_name}" STREQUAL "") + message(FATAL_ERROR "variable name cannot be empty") +- endif(_name STREQUAL "") ++ endif("${_name}" STREQUAL "") + list(APPEND _printable_options ${_name}) + endmacro() + + macro(add_printable_option _name _description _default_value) +- if(_name STREQUAL "") ++ if("${_name}" STREQUAL "") + message(FATAL_ERROR "option name cannot be empty") +- endif(_name STREQUAL "") ++ endif("${_name}" STREQUAL "") + option(${_name} ${_description} ${_default_value}) + add_printable_variable_bare(${_name}) + endmacro() + + macro(add_printable_variable _name _description _default_value) +- if(_name STREQUAL "") ++ if("${_name}" STREQUAL "") + message(FATAL_ERROR "variable name cannot be empty") +- endif(_name STREQUAL "") ++ endif("${_name}" STREQUAL "") + set(${_name} ${_default_value} CACHE STRING ${_description}) + add_printable_variable_bare(${_name}) + endmacro() + + macro(add_printable_variable_path _name _description _default_value) +- if(_name STREQUAL "") ++ if("${_name}" STREQUAL "") + message(FATAL_ERROR "path variable name cannot be empty") +- endif(_name STREQUAL "") ++ endif("${_name}" STREQUAL "") + set(${_name} ${_default_value} CACHE PATH ${_description}) + add_printable_variable_bare(${_name}) + endmacro() diff --git a/SOURCES/evolution-ews-3.28.5-contact-country-forgotten.patch b/SOURCES/evolution-ews-3.28.5-contact-country-forgotten.patch new file mode 100644 index 0000000..16b365a --- /dev/null +++ b/SOURCES/evolution-ews-3.28.5-contact-country-forgotten.patch @@ -0,0 +1,32 @@ +diff -up evolution-ews-3.28.5/src/addressbook/e-book-backend-ews.c.contact-country-forgotten evolution-ews-3.28.5/src/addressbook/e-book-backend-ews.c +--- evolution-ews-3.28.5/src/addressbook/e-book-backend-ews.c.contact-country-forgotten 2018-07-30 16:01:00.000000000 +0200 ++++ evolution-ews-3.28.5/src/addressbook/e-book-backend-ews.c 2018-09-27 18:31:47.485039155 +0200 +@@ -700,6 +700,7 @@ add_physical_address (ESoapMessage *msg, + e_ews_message_write_string_parameter (msg, "Street", NULL, contact_addr->street); + e_ews_message_write_string_parameter (msg, "City", NULL, contact_addr->locality); + e_ews_message_write_string_parameter (msg, "State", NULL, contact_addr->region); ++ e_ews_message_write_string_parameter (msg, "CountryOrRegion", NULL, contact_addr->country); + e_ews_message_write_string_parameter (msg, "PostalCode", NULL, contact_addr->code); + + e_soap_message_end_element (msg); +@@ -1191,6 +1192,8 @@ compare_address (ESoapMessage *message, + convert_indexed_contact_property_to_updatexml_physical_address (message, "PhysicalAddress", "City", new_address->locality, "contacts", "PhysicalAddresses", key); + if (set || g_strcmp0 (new_address->region, old_address->region) != 0) + convert_indexed_contact_property_to_updatexml_physical_address (message, "PhysicalAddress", "State", new_address->region, "contacts", "PhysicalAddresses", key); ++ if (set || g_strcmp0 (new_address->country, old_address->country) != 0) ++ convert_indexed_contact_property_to_updatexml_physical_address (message, "PhysicalAddress", "CountryOrRegion", new_address->country, "contacts", "PhysicalAddresses", key); + if (set || g_strcmp0 (new_address->code, old_address->code) != 0) + convert_indexed_contact_property_to_updatexml_physical_address (message, "PhysicalAddress", "PostalCode", new_address->code, "contacts", "PhysicalAddresses", key); + +diff -up evolution-ews-3.28.5/src/server/e-ews-item.c.contact-country-forgotten evolution-ews-3.28.5/src/server/e-ews-item.c +--- evolution-ews-3.28.5/src/server/e-ews-item.c.contact-country-forgotten 2018-07-30 16:01:00.000000000 +0200 ++++ evolution-ews-3.28.5/src/server/e-ews-item.c 2018-09-27 18:31:47.485039155 +0200 +@@ -686,7 +686,7 @@ ews_get_physical_address (ESoapParameter + if (subparam) + address->state = e_soap_parameter_get_string_value (subparam); + +- subparam = e_soap_parameter_get_first_child_by_name (param, "Country"); ++ subparam = e_soap_parameter_get_first_child_by_name (param, "CountryOrRegion"); + if (subparam) + address->country = e_soap_parameter_get_string_value (subparam); + diff --git a/SOURCES/evolution-ews-3.28.5-cve-2019-3890.patch b/SOURCES/evolution-ews-3.28.5-cve-2019-3890.patch new file mode 100644 index 0000000..65b055d --- /dev/null +++ b/SOURCES/evolution-ews-3.28.5-cve-2019-3890.patch @@ -0,0 +1,782 @@ +diff -up evolution-ews-3.28.5/src/addressbook/e-book-backend-ews.c.cve-2019-3890 evolution-ews-3.28.5/src/addressbook/e-book-backend-ews.c +--- evolution-ews-3.28.5/src/addressbook/e-book-backend-ews.c.cve-2019-3890 2019-04-15 09:43:49.672771516 +0200 ++++ evolution-ews-3.28.5/src/addressbook/e-book-backend-ews.c 2019-04-15 09:43:49.683771516 +0200 +@@ -2901,7 +2901,8 @@ ebb_ews_connect_sync (EBookMetaBackend * + bbews->priv->cnc, "proxy-resolver", + G_BINDING_SYNC_CREATE); + +- *out_auth_result = e_ews_connection_try_credentials_sync (bbews->priv->cnc, credentials, cancellable, error); ++ *out_auth_result = e_ews_connection_try_credentials_sync (bbews->priv->cnc, credentials, NULL, ++ out_certificate_pem, out_certificate_errors, cancellable, error); + + if (*out_auth_result == E_SOURCE_AUTHENTICATION_ACCEPTED) { + ESource *source = e_backend_get_source (E_BACKEND (bbews)); +diff -up evolution-ews-3.28.5/src/calendar/e-cal-backend-ews.c.cve-2019-3890 evolution-ews-3.28.5/src/calendar/e-cal-backend-ews.c +--- evolution-ews-3.28.5/src/calendar/e-cal-backend-ews.c.cve-2019-3890 2019-04-15 09:43:49.676771516 +0200 ++++ evolution-ews-3.28.5/src/calendar/e-cal-backend-ews.c 2019-04-15 09:43:49.684771516 +0200 +@@ -1394,7 +1394,8 @@ ecb_ews_connect_sync (ECalMetaBackend *m + cbews->priv->cnc, "proxy-resolver", + G_BINDING_SYNC_CREATE); + +- *out_auth_result = e_ews_connection_try_credentials_sync (cbews->priv->cnc, credentials, cancellable, error); ++ *out_auth_result = e_ews_connection_try_credentials_sync (cbews->priv->cnc, credentials, NULL, ++ out_certificate_pem, out_certificate_errors, cancellable, error); + + if (*out_auth_result == E_SOURCE_AUTHENTICATION_ACCEPTED) { + ESource *source = e_backend_get_source (E_BACKEND (cbews)); +diff -up evolution-ews-3.28.5/src/camel/camel-ews-store.c.cve-2019-3890 evolution-ews-3.28.5/src/camel/camel-ews-store.c +--- evolution-ews-3.28.5/src/camel/camel-ews-store.c.cve-2019-3890 2018-07-30 16:01:00.000000000 +0200 ++++ evolution-ews-3.28.5/src/camel/camel-ews-store.c 2019-04-15 09:43:49.684771516 +0200 +@@ -1831,6 +1831,8 @@ ews_authenticate_sync (CamelService *ser + const gchar *password; + gchar *hosturl; + gchar *old_sync_state = NULL, *new_sync_state = NULL; ++ gchar *certificate_pem = NULL; ++ GTlsCertificateFlags certificate_errors = 0; + GError *local_error = NULL; + + ews_store = CAMEL_EWS_STORE (service); +@@ -1959,6 +1961,18 @@ ews_authenticate_sync (CamelService *ser + + g_slist_free_full (created_folder_ids, g_free); + ++ if (g_error_matches (local_error, SOUP_HTTP_ERROR, SOUP_STATUS_SSL_FAILED) && ++ e_ews_connection_get_ssl_error_details (connection, &certificate_pem, &certificate_errors)) { ++ source = e_ews_connection_get_source (connection); ++ ++ if (source) { ++ e_source_emit_credentials_required (source, E_SOURCE_CREDENTIALS_REASON_SSL_FAILED, ++ certificate_pem, certificate_errors, local_error); ++ } ++ ++ g_free (certificate_pem); ++ } ++ + if (local_error == NULL) { + result = CAMEL_AUTHENTICATION_ACCEPTED; + } else if (g_error_matches (local_error, EWS_CONNECTION_ERROR, EWS_CONNECTION_ERROR_AUTHENTICATION_FAILED)) { +diff -up evolution-ews-3.28.5/src/collection/e-ews-backend.c.cve-2019-3890 evolution-ews-3.28.5/src/collection/e-ews-backend.c +--- evolution-ews-3.28.5/src/collection/e-ews-backend.c.cve-2019-3890 2019-04-15 09:43:49.679771516 +0200 ++++ evolution-ews-3.28.5/src/collection/e-ews-backend.c 2019-04-15 09:43:49.685771516 +0200 +@@ -727,6 +727,15 @@ ews_backend_constructed (GObject *object + /* Reset the connectable, it steals data from Authentication extension, + where is written incorrect address */ + e_backend_set_connectable (backend, NULL); ++ ++ /* Eventually unset temporary SSL trust, but only once, when the process started. ++ It might bee too often anywhere lease (like in the authenticate callback) */ ++ if (e_source_has_extension (source, E_SOURCE_EXTENSION_WEBDAV_BACKEND)) { ++ ESourceWebdav *webdav_extension; ++ ++ webdav_extension = e_source_get_extension (source, E_SOURCE_EXTENSION_WEBDAV_BACKEND); ++ e_source_webdav_unset_temporary_ssl_trust (webdav_extension); ++ } + } + + static void +@@ -930,7 +939,7 @@ ews_backend_create_resource_sync (EColle + } + + if (!success) { +- connection = e_ews_backend_ref_connection_sync (E_EWS_BACKEND (backend), NULL, cancellable, error); ++ connection = e_ews_backend_ref_connection_sync (E_EWS_BACKEND (backend), NULL, NULL, NULL, cancellable, error); + if (connection == NULL) + return FALSE; + +@@ -1037,7 +1046,7 @@ ews_backend_delete_resource_sync (EColle + const gchar *extension_name; + gboolean success = FALSE; + +- connection = e_ews_backend_ref_connection_sync (E_EWS_BACKEND (backend), NULL, cancellable, error); ++ connection = e_ews_backend_ref_connection_sync (E_EWS_BACKEND (backend), NULL, NULL, NULL, cancellable, error); + if (connection == NULL) + return FALSE; + +@@ -1142,7 +1151,7 @@ ews_backend_authenticate_sync (EBackend + ews_backend->priv->credentials = e_named_parameters_new_clone (credentials); + g_mutex_unlock (&ews_backend->priv->connection_lock); + +- connection = e_ews_backend_ref_connection_sync (ews_backend, &result, cancellable, error); ++ connection = e_ews_backend_ref_connection_sync (ews_backend, &result, out_certificate_pem, out_certificate_errors, cancellable, error); + g_clear_object (&connection); + + if (result == E_SOURCE_AUTHENTICATION_ACCEPTED) { +@@ -1223,7 +1232,7 @@ ews_backend_ref_connection_thread (GSimp + EEwsConnection *connection; + GError *error = NULL; + +- connection = e_ews_backend_ref_connection_sync (E_EWS_BACKEND (object), NULL, cancellable, &error); ++ connection = e_ews_backend_ref_connection_sync (E_EWS_BACKEND (object), NULL, NULL, NULL, cancellable, &error); + + /* Sanity check. */ + g_return_if_fail ( +@@ -1241,6 +1250,8 @@ ews_backend_ref_connection_thread (GSimp + EEwsConnection * + e_ews_backend_ref_connection_sync (EEwsBackend *backend, + ESourceAuthenticationResult *result, ++ gchar **out_certificate_pem, ++ GTlsCertificateFlags *out_certificate_errors, + GCancellable *cancellable, + GError **error) + { +@@ -1272,7 +1283,8 @@ e_ews_backend_ref_connection_sync (EEwsB + connection, "proxy-resolver", + G_BINDING_SYNC_CREATE); + +- local_result = e_ews_connection_try_credentials_sync (connection, backend->priv->credentials, cancellable, error); ++ local_result = e_ews_connection_try_credentials_sync (connection, backend->priv->credentials, NULL, ++ out_certificate_pem, out_certificate_errors, cancellable, error); + if (result) + *result = local_result; + +@@ -1413,7 +1425,7 @@ e_ews_backend_sync_folders_sync (EEwsBac + return TRUE; + } + +- connection = e_ews_backend_ref_connection_sync (backend, NULL, cancellable, error); ++ connection = e_ews_backend_ref_connection_sync (backend, NULL, NULL, NULL, cancellable, error); + + if (connection == NULL) { + backend->priv->need_update_folders = TRUE; +diff -up evolution-ews-3.28.5/src/collection/e-ews-backend.h.cve-2019-3890 evolution-ews-3.28.5/src/collection/e-ews-backend.h +--- evolution-ews-3.28.5/src/collection/e-ews-backend.h.cve-2019-3890 2018-07-30 16:01:00.000000000 +0200 ++++ evolution-ews-3.28.5/src/collection/e-ews-backend.h 2019-04-15 09:43:49.685771516 +0200 +@@ -63,6 +63,8 @@ EEwsConnection * + e_ews_backend_ref_connection_sync + (EEwsBackend *backend, + ESourceAuthenticationResult *result, ++ gchar **out_certificate_pem, ++ GTlsCertificateFlags *out_certificate_errors, + GCancellable *cancellable, + GError **error); + void e_ews_backend_ref_connection (EEwsBackend *backend, +diff -up evolution-ews-3.28.5/src/configuration/e-ews-config-lookup.c.cve-2019-3890 evolution-ews-3.28.5/src/configuration/e-ews-config-lookup.c +--- evolution-ews-3.28.5/src/configuration/e-ews-config-lookup.c.cve-2019-3890 2018-07-30 16:01:00.000000000 +0200 ++++ evolution-ews-3.28.5/src/configuration/e-ews-config-lookup.c 2019-04-15 09:43:49.685771516 +0200 +@@ -344,9 +344,54 @@ ews_config_lookup_worker_run (EConfigLoo + + if (password) { + const gchar *servers; ++ gchar *certificate_host = NULL; ++ gchar *certificate_pem = NULL; ++ GTlsCertificateFlags certificate_errors = 0; ++ GError *local_error = NULL; ++ ++ if (e_named_parameters_exists (params, E_CONFIG_LOOKUP_PARAM_CERTIFICATE_PEM) && ++ e_named_parameters_exists (params, E_CONFIG_LOOKUP_PARAM_CERTIFICATE_TRUST) && ++ e_named_parameters_exists (params, E_CONFIG_LOOKUP_PARAM_CERTIFICATE_HOST)) { ++ GTlsCertificate *certificate; ++ const gchar *param_certificate_pem; ++ ++ param_certificate_pem = e_named_parameters_get (params, E_CONFIG_LOOKUP_PARAM_CERTIFICATE_PEM); ++ certificate = g_tls_certificate_new_from_pem (param_certificate_pem, -1, NULL); ++ ++ if (certificate) { ++ ETrustPromptResponse trust_response; ++ ++ trust_response = e_config_lookup_decode_certificate_trust ( ++ e_named_parameters_get (params, E_CONFIG_LOOKUP_PARAM_CERTIFICATE_TRUST)); ++ ++ if (trust_response != E_TRUST_PROMPT_RESPONSE_UNKNOWN) { ++ ESourceWebdav *webdav_extension; ++ ++ webdav_extension = e_source_get_extension (source, E_SOURCE_EXTENSION_WEBDAV_BACKEND); ++ e_source_webdav_update_ssl_trust (webdav_extension, ++ e_named_parameters_get (params, E_CONFIG_LOOKUP_PARAM_CERTIFICATE_HOST), ++ certificate, trust_response); ++ } ++ ++ g_object_unref (certificate); ++ } ++ } + +- if (e_ews_autodiscover_ws_url_sync (source, ews_settings, email_address, password, cancellable, NULL)) { ++ if (e_ews_autodiscover_ws_url_sync (source, ews_settings, email_address, password, &certificate_pem, &certificate_errors, cancellable, &local_error)) { + ews_config_lookup_worker_result_from_settings (lookup_worker, config_lookup, email_address, ews_settings, params); ++ } else if (g_error_matches (local_error, SOUP_HTTP_ERROR, SOUP_STATUS_SSL_FAILED)) { ++ const gchar *hosturl; ++ SoupURI *suri; ++ ++ hosturl = camel_ews_settings_get_hosturl (ews_settings); ++ suri = soup_uri_new (hosturl); ++ if (suri) { ++ certificate_host = g_strdup (soup_uri_get_host (suri)); ++ ++ soup_uri_free (suri); ++ } ++ } else { ++ g_clear_error (&local_error); + } + + servers = e_named_parameters_get (params, E_CONFIG_LOOKUP_PARAM_SERVERS); +@@ -357,7 +402,7 @@ ews_config_lookup_worker_run (EConfigLoo + + servers_strv = g_strsplit (servers, ";", 0); + +- for (ii = 0; servers_strv && servers_strv[ii] && !g_cancellable_is_cancelled (cancellable); ii++) { ++ for (ii = 0; servers_strv && servers_strv[ii] && !g_cancellable_is_cancelled (cancellable) && !local_error; ii++) { + const gchar *server = servers_strv[ii]; + gchar *tmp = NULL; + +@@ -368,8 +413,21 @@ ews_config_lookup_worker_run (EConfigLoo + + camel_ews_settings_set_hosturl (ews_settings, server); + +- if (e_ews_autodiscover_ws_url_sync (source, ews_settings, email_address, password, cancellable, NULL)) { ++ if (e_ews_autodiscover_ws_url_sync (source, ews_settings, email_address, password, &certificate_pem, &certificate_errors, cancellable, &local_error)) { + ews_config_lookup_worker_result_from_settings (lookup_worker, config_lookup, email_address, ews_settings, params); ++ } else if (g_error_matches (local_error, SOUP_HTTP_ERROR, SOUP_STATUS_SSL_FAILED)) { ++ const gchar *hosturl; ++ SoupURI *suri; ++ ++ hosturl = camel_ews_settings_get_hosturl (ews_settings); ++ suri = soup_uri_new (hosturl); ++ if (suri) { ++ certificate_host = g_strdup (soup_uri_get_host (suri)); ++ ++ soup_uri_free (suri); ++ } ++ } else { ++ g_clear_error (&local_error); + } + + g_free (tmp); +@@ -378,7 +436,31 @@ ews_config_lookup_worker_run (EConfigLoo + g_strfreev (servers_strv); + } + +- if (out_restart_params) ++ if (g_error_matches (local_error, SOUP_HTTP_ERROR, SOUP_STATUS_SSL_FAILED) && ++ certificate_pem && *certificate_pem && certificate_errors) { ++ gchar *description = e_trust_prompt_describe_certificate_errors (certificate_errors); ++ ++ if (description) { ++ g_set_error_literal (error, E_CONFIG_LOOKUP_WORKER_ERROR, ++ E_CONFIG_LOOKUP_WORKER_ERROR_CERTIFICATE, description); ++ ++ g_free (description); ++ ++ if (out_restart_params) { ++ if (!*out_restart_params) ++ *out_restart_params = e_named_parameters_new_clone (params); ++ ++ e_named_parameters_set (*out_restart_params, E_CONFIG_LOOKUP_PARAM_CERTIFICATE_PEM, certificate_pem); ++ e_named_parameters_set (*out_restart_params, E_CONFIG_LOOKUP_PARAM_CERTIFICATE_HOST, certificate_host); ++ } ++ } ++ } ++ ++ g_clear_error (&local_error); ++ g_free (certificate_host); ++ g_free (certificate_pem); ++ ++ if (out_restart_params && !*out_restart_params) + *out_restart_params = e_named_parameters_new_clone (params); + } + +diff -up evolution-ews-3.28.5/src/configuration/e-ews-config-utils.c.cve-2019-3890 evolution-ews-3.28.5/src/configuration/e-ews-config-utils.c +--- evolution-ews-3.28.5/src/configuration/e-ews-config-utils.c.cve-2019-3890 2018-07-30 16:01:00.000000000 +0200 ++++ evolution-ews-3.28.5/src/configuration/e-ews-config-utils.c 2019-04-15 09:43:49.686771516 +0200 +@@ -317,7 +317,7 @@ ews_config_utils_try_credentials_sync (E + if (data->try_credentials_func) + auth_result = data->try_credentials_func (data->conn, credentials, data->user_data, cancellable, error); + else +- auth_result = e_ews_connection_try_credentials_sync (data->conn, credentials, cancellable, error); ++ auth_result = e_ews_connection_try_credentials_sync (data->conn, credentials, NULL, NULL, NULL, cancellable, error); + + if (auth_result == E_SOURCE_AUTHENTICATION_ACCEPTED) { + *out_authenticated = TRUE; +@@ -377,7 +377,7 @@ e_ews_config_utils_open_connection_for ( + if (try_credentials_func) + result = try_credentials_func (conn, NULL, user_data, cancellable, &local_error); + else +- result = e_ews_connection_try_credentials_sync (conn, NULL, cancellable, &local_error); ++ result = e_ews_connection_try_credentials_sync (conn, NULL, NULL, NULL, NULL, cancellable, &local_error); + + if (result != E_SOURCE_AUTHENTICATION_ACCEPTED) { + g_clear_object (&conn); +diff -up evolution-ews-3.28.5/src/configuration/e-mail-config-ews-autodiscover.c.cve-2019-3890 evolution-ews-3.28.5/src/configuration/e-mail-config-ews-autodiscover.c +--- evolution-ews-3.28.5/src/configuration/e-mail-config-ews-autodiscover.c.cve-2019-3890 2018-07-30 16:01:00.000000000 +0200 ++++ evolution-ews-3.28.5/src/configuration/e-mail-config-ews-autodiscover.c 2019-04-15 09:43:49.686771516 +0200 +@@ -45,6 +45,8 @@ struct _AsyncContext { + ESource *source; + CamelEwsSettings *ews_settings; + gchar *email_address; ++ gchar *certificate_pem; ++ GTlsCertificateFlags certificate_errors; + }; + + enum { +@@ -67,6 +69,7 @@ async_context_free (gpointer ptr) + g_clear_object (&async_context->source); + g_clear_object (&async_context->ews_settings); + g_free (async_context->email_address); ++ g_free (async_context->certificate_pem); + + g_slice_free (AsyncContext, async_context); + } +@@ -87,6 +90,9 @@ mail_config_ews_autodiscover_finish (EMa + } + + static void ++mail_config_ews_autodiscover_run (EMailConfigEwsAutodiscover *autodiscover); ++ ++static void + mail_config_ews_autodiscover_run_cb (GObject *source_object, + GAsyncResult *result, + gpointer user_data) +@@ -111,17 +117,62 @@ mail_config_ews_autodiscover_run_cb (GOb + g_object_thaw_notify (G_OBJECT (settings)); + + if (e_activity_handle_cancellation (async_context->activity, error)) { +- g_error_free (error); ++ /* Do nothing, just free the error below */ ++ } else if (g_error_matches (error, SOUP_HTTP_ERROR, SOUP_STATUS_SSL_FAILED) && ++ async_context->certificate_pem && *async_context->certificate_pem && async_context->certificate_errors) { ++ ETrustPromptResponse response; ++ GtkWidget *parent; ++ const gchar *host; ++ ++ parent = gtk_widget_get_toplevel (GTK_WIDGET (autodiscover)); ++ if (!GTK_IS_WINDOW (parent)) ++ parent = NULL; ++ ++ host = camel_network_settings_get_host (CAMEL_NETWORK_SETTINGS (settings)); ++ ++ response = e_trust_prompt_run_modal (parent ? GTK_WINDOW (parent) : NULL, ++ E_SOURCE_EXTENSION_COLLECTION, _("Exchange Web Services"), ++ host, async_context->certificate_pem, async_context->certificate_errors, ++ error->message); ++ ++ g_clear_error (&error); ++ ++ if (response != E_TRUST_PROMPT_RESPONSE_UNKNOWN) { ++ GTlsCertificate *certificate; ++ ++ certificate = g_tls_certificate_new_from_pem (async_context->certificate_pem, -1, &error); ++ if (certificate) { ++ ESourceWebdav *extension_webdav; ++ ++ extension_webdav = e_source_get_extension (async_context->source, E_SOURCE_EXTENSION_WEBDAV_BACKEND); ++ ++ e_source_webdav_update_ssl_trust (extension_webdav, host, certificate, response); ++ ++ g_object_unref (certificate); ++ } ++ ++ if (error) { ++ e_alert_submit ( ++ alert_sink, ++ "ews:autodiscovery-error", ++ error->message, NULL); ++ } ++ } + ++ if (response == E_TRUST_PROMPT_RESPONSE_ACCEPT || ++ response == E_TRUST_PROMPT_RESPONSE_ACCEPT_TEMPORARILY) { ++ mail_config_ews_autodiscover_run (autodiscover); ++ } + } else if (error != NULL) { + e_alert_submit ( + alert_sink, + "ews:autodiscovery-error", + error->message, NULL); +- g_error_free (error); + } + + gtk_widget_set_sensitive (GTK_WIDGET (autodiscover), TRUE); ++ ++ g_clear_error (&error); + } + + static gboolean +@@ -141,6 +192,7 @@ mail_config_ews_autodiscover_sync (ECred + async_context->ews_settings, async_context->email_address, + credentials && e_named_parameters_get (credentials, E_SOURCE_CREDENTIAL_PASSWORD) ? + e_named_parameters_get (credentials, E_SOURCE_CREDENTIAL_PASSWORD) : "", ++ &async_context->certificate_pem, &async_context->certificate_errors, + cancellable, &local_error); + + if (local_error == NULL) { +@@ -173,6 +225,7 @@ mail_config_ews_autodiscover_run_thread + if (without_password) { + success = e_ews_autodiscover_ws_url_sync (async_context->source, + async_context->ews_settings, async_context->email_address, "", ++ &async_context->certificate_pem, &async_context->certificate_errors, + cancellable, &local_error); + } + +@@ -236,6 +289,8 @@ mail_config_ews_autodiscover_run (EMailC + async_context->source = g_object_ref (source); + async_context->ews_settings = g_object_ref (settings); + async_context->email_address = g_strdup (e_mail_config_service_page_get_email_address (page)); ++ async_context->certificate_pem = NULL; ++ async_context->certificate_errors = 0; + + /* + * The GTask will be run in a new thread, which will invoke +diff -up evolution-ews-3.28.5/src/server/e-ews-connection.c.cve-2019-3890 evolution-ews-3.28.5/src/server/e-ews-connection.c +--- evolution-ews-3.28.5/src/server/e-ews-connection.c.cve-2019-3890 2018-07-30 16:01:00.000000000 +0200 ++++ evolution-ews-3.28.5/src/server/e-ews-connection.c 2019-04-15 09:43:49.689771516 +0200 +@@ -111,6 +111,10 @@ struct _EEwsConnectionPrivate { + + /* Set to TRUE when this connection had been disconnected and cannot be used anymore */ + gboolean disconnected_flag; ++ ++ gboolean ssl_info_set; ++ gchar *ssl_certificate_pem; ++ GTlsCertificateFlags ssl_certificate_errors; + }; + + enum { +@@ -836,6 +840,37 @@ ews_connection_credentials_failed (EEwsC + return expired; + } + ++static void ++ews_connection_check_ssl_error (EEwsConnection *connection, ++ SoupMessage *message) ++{ ++ g_return_if_fail (E_IS_EWS_CONNECTION (connection)); ++ g_return_if_fail (SOUP_IS_MESSAGE (message)); ++ ++ if (message->status_code == SOUP_STATUS_SSL_FAILED) { ++ GTlsCertificate *certificate = NULL; ++ ++ g_mutex_lock (&connection->priv->property_lock); ++ ++ g_clear_pointer (&connection->priv->ssl_certificate_pem, g_free); ++ connection->priv->ssl_info_set = FALSE; ++ ++ g_object_get (G_OBJECT (message), ++ "tls-certificate", &certificate, ++ "tls-errors", &connection->priv->ssl_certificate_errors, ++ NULL); ++ ++ if (certificate) { ++ g_object_get (certificate, "certificate-pem", &connection->priv->ssl_certificate_pem, NULL); ++ connection->priv->ssl_info_set = TRUE; ++ ++ g_object_unref (certificate); ++ } ++ ++ g_mutex_unlock (&connection->priv->property_lock); ++ } ++} ++ + /* Response callbacks */ + + static void +@@ -852,8 +887,15 @@ ews_response_cb (SoupSession *session, + if (g_cancellable_is_cancelled (enode->cancellable)) + goto exit; + ++ ews_connection_check_ssl_error (enode->cnc, msg); ++ + if (ews_connection_credentials_failed (enode->cnc, msg, enode->simple)) { + goto exit; ++ } else if (msg->status_code == SOUP_STATUS_SSL_FAILED) { ++ g_simple_async_result_set_error ( ++ enode->simple, SOUP_HTTP_ERROR, SOUP_STATUS_SSL_FAILED, ++ "%s", msg->reason_phrase); ++ goto exit; + } else if (msg->status_code == SOUP_STATUS_UNAUTHORIZED) { + if (msg->response_headers) { + const gchar *diagnostics; +@@ -1855,6 +1897,9 @@ ews_connection_constructed (GObject *obj + cnc->priv->soup_thread = g_thread_new (NULL, e_ews_soup_thread, cnc); + + cnc->priv->soup_session = soup_session_async_new_with_options ( ++ SOUP_SESSION_TIMEOUT, 90, ++ SOUP_SESSION_SSL_STRICT, TRUE, ++ SOUP_SESSION_SSL_USE_SYSTEM_CA_FILE, TRUE, + SOUP_SESSION_ASYNC_CONTEXT, cnc->priv->soup_context, + NULL); + +@@ -1971,6 +2016,7 @@ ews_connection_finalize (GObject *object + g_free (priv->email); + g_free (priv->hash_key); + g_free (priv->impersonate_user); ++ g_free (priv->ssl_certificate_pem); + + g_clear_object (&priv->bearer_auth); + +@@ -2557,10 +2603,15 @@ e_ews_connection_update_credentials (EEw + ESourceAuthenticationResult + e_ews_connection_try_credentials_sync (EEwsConnection *cnc, + const ENamedParameters *credentials, ++ ESource *use_source, ++ gchar **out_certificate_pem, ++ GTlsCertificateFlags *out_certificate_errors, + GCancellable *cancellable, + GError **error) + { + ESourceAuthenticationResult result; ++ ESource *source; ++ gboolean de_set_source; + EwsFolderId *fid = NULL; + GSList *ids = NULL; + GError *local_error = NULL; +@@ -2574,14 +2625,31 @@ e_ews_connection_try_credentials_sync (E + fid->is_distinguished_id = TRUE; + ids = g_slist_append (ids, fid); + ++ source = e_ews_connection_get_source (cnc); ++ if (use_source && use_source != source) { ++ cnc->priv->source = g_object_ref (use_source); ++ de_set_source = TRUE; ++ } else { ++ source = NULL; ++ de_set_source = FALSE; ++ } ++ + e_ews_connection_get_folder_sync ( + cnc, EWS_PRIORITY_MEDIUM, "Default", + NULL, ids, NULL, cancellable, &local_error); + ++ if (de_set_source) { ++ g_clear_object (&cnc->priv->source); ++ cnc->priv->source = source; ++ } ++ + g_slist_free_full (ids, (GDestroyNotify) e_ews_folder_id_free); + + if (local_error == NULL) { + result = E_SOURCE_AUTHENTICATION_ACCEPTED; ++ } else if (g_error_matches (local_error, SOUP_HTTP_ERROR, SOUP_STATUS_SSL_FAILED) && ++ e_ews_connection_get_ssl_error_details (cnc, out_certificate_pem, out_certificate_errors)) { ++ result = E_SOURCE_AUTHENTICATION_ERROR_SSL_FAILED; + } else { + gboolean auth_failed; + +@@ -2618,6 +2686,29 @@ e_ews_connection_get_source (EEwsConnect + return cnc->priv->source; + } + ++gboolean ++e_ews_connection_get_ssl_error_details (EEwsConnection *cnc, ++ gchar **out_certificate_pem, ++ GTlsCertificateFlags *out_certificate_errors) ++{ ++ g_return_val_if_fail (E_IS_EWS_CONNECTION (cnc), FALSE); ++ g_return_val_if_fail (out_certificate_pem != NULL, FALSE); ++ g_return_val_if_fail (out_certificate_errors != NULL, FALSE); ++ ++ g_mutex_lock (&cnc->priv->property_lock); ++ if (!cnc->priv->ssl_info_set) { ++ g_mutex_unlock (&cnc->priv->property_lock); ++ return FALSE; ++ } ++ ++ *out_certificate_pem = g_strdup (cnc->priv->ssl_certificate_pem); ++ *out_certificate_errors = cnc->priv->ssl_certificate_errors; ++ ++ g_mutex_unlock (&cnc->priv->property_lock); ++ ++ return TRUE; ++} ++ + const gchar * + e_ews_connection_get_uri (EEwsConnection *cnc) + { +@@ -2906,6 +2997,9 @@ autodiscover_response_cb (SoupSession *s + g_set_error ( + &error, SOUP_HTTP_ERROR, status, + "%d %s", status, msg->reason_phrase); ++ ++ if (status == SOUP_STATUS_SSL_FAILED) ++ ews_connection_check_ssl_error (ad->cnc, msg); + } + + g_free (service_url); +@@ -3056,7 +3150,8 @@ static void post_restarted (SoupMessage + } + + static SoupMessage * +-e_ews_get_msg_for_url (CamelEwsSettings *settings, ++e_ews_get_msg_for_url (EEwsConnection *cnc, ++ CamelEwsSettings *settings, + const gchar *url, + xmlOutputBuffer *buf, + GError **error) +@@ -3078,6 +3173,9 @@ e_ews_get_msg_for_url (CamelEwsSettings + return NULL; + } + ++ if (cnc->priv->source) ++ e_soup_ssl_trust_connect (msg, cnc->priv->source); ++ + e_ews_message_attach_chunk_allocator (msg); + + e_ews_message_set_user_agent_header (msg, settings); +@@ -3107,6 +3205,8 @@ e_ews_autodiscover_ws_url_sync (ESource + CamelEwsSettings *settings, + const gchar *email_address, + const gchar *password, ++ gchar **out_certificate_pem, ++ GTlsCertificateFlags *out_certificate_errors, + GCancellable *cancellable, + GError **error) + { +@@ -3125,7 +3225,7 @@ e_ews_autodiscover_ws_url_sync (ESource + + result = e_async_closure_wait (closure); + +- success = e_ews_autodiscover_ws_url_finish (settings, result, error); ++ success = e_ews_autodiscover_ws_url_finish (settings, result, out_certificate_pem, out_certificate_errors, error); + + e_async_closure_free (closure); + +@@ -3236,11 +3336,11 @@ e_ews_autodiscover_ws_url (ESource *sour + simple, ad, (GDestroyNotify) autodiscover_data_free); + + /* Passing a NULL URL string returns NULL. */ +- ad->msgs[0] = e_ews_get_msg_for_url (settings, url1, buf, &error); +- ad->msgs[1] = e_ews_get_msg_for_url (settings, url2, buf, NULL); +- ad->msgs[2] = e_ews_get_msg_for_url (settings, url3, buf, NULL); +- ad->msgs[3] = e_ews_get_msg_for_url (settings, url4, buf, NULL); +- ad->msgs[4] = e_ews_get_msg_for_url (settings, url5, buf, NULL); ++ ad->msgs[0] = e_ews_get_msg_for_url (cnc, settings, url1, buf, &error); ++ ad->msgs[1] = e_ews_get_msg_for_url (cnc, settings, url2, buf, NULL); ++ ad->msgs[2] = e_ews_get_msg_for_url (cnc, settings, url3, buf, NULL); ++ ad->msgs[3] = e_ews_get_msg_for_url (cnc, settings, url4, buf, NULL); ++ ad->msgs[4] = e_ews_get_msg_for_url (cnc, settings, url5, buf, NULL); + + /* These have to be submitted only after they're both set in ad->msgs[] + * or there will be races with fast completion */ +@@ -3300,10 +3400,13 @@ has_suffix_icmp (const gchar *text, + gboolean + e_ews_autodiscover_ws_url_finish (CamelEwsSettings *settings, + GAsyncResult *result, ++ gchar **out_certificate_pem, ++ GTlsCertificateFlags *out_certificate_errors, + GError **error) + { + GSimpleAsyncResult *simple; + struct _autodiscover_data *ad; ++ GError *local_error = NULL; + + g_return_val_if_fail ( + g_simple_async_result_is_valid ( +@@ -3313,8 +3416,20 @@ e_ews_autodiscover_ws_url_finish (CamelE + simple = G_SIMPLE_ASYNC_RESULT (result); + ad = g_simple_async_result_get_op_res_gpointer (simple); + +- if (g_simple_async_result_propagate_error (simple, error)) ++ if (g_simple_async_result_propagate_error (simple, &local_error)) { ++ if (g_error_matches (local_error, SOUP_HTTP_ERROR, SOUP_STATUS_SSL_FAILED)) { ++ if (!e_ews_connection_get_ssl_error_details (ad->cnc, out_certificate_pem, out_certificate_errors)) { ++ if (out_certificate_pem) ++ *out_certificate_pem = NULL; ++ if (out_certificate_errors) ++ *out_certificate_errors = 0; ++ } ++ } ++ ++ g_propagate_error (error, local_error); ++ + return FALSE; ++ } + + g_warn_if_fail (ad->as_url != NULL); + g_warn_if_fail (ad->oab_url != NULL); +@@ -3473,6 +3588,8 @@ oal_response_cb (SoupSession *soup_sessi + simple = G_SIMPLE_ASYNC_RESULT (user_data); + data = g_simple_async_result_get_op_res_gpointer (simple); + ++ ews_connection_check_ssl_error (data->cnc, soup_message); ++ + if (ews_connection_credentials_failed (data->cnc, soup_message, simple)) { + goto exit; + } else if (soup_message->status_code != 200) { +@@ -3618,7 +3735,7 @@ e_ews_connection_get_oal_list (EEwsConne + + g_return_if_fail (E_IS_EWS_CONNECTION (cnc)); + +- soup_message = e_ews_get_msg_for_url (cnc->priv->settings, cnc->priv->uri, NULL, &error); ++ soup_message = e_ews_get_msg_for_url (cnc, cnc->priv->settings, cnc->priv->uri, NULL, &error); + + simple = g_simple_async_result_new ( + G_OBJECT (cnc), callback, user_data, +@@ -3739,7 +3856,7 @@ e_ews_connection_get_oal_detail (EEwsCon + + g_return_if_fail (E_IS_EWS_CONNECTION (cnc)); + +- soup_message = e_ews_get_msg_for_url (cnc->priv->settings, cnc->priv->uri, NULL, &error); ++ soup_message = e_ews_get_msg_for_url (cnc, cnc->priv->settings, cnc->priv->uri, NULL, &error); + + simple = g_simple_async_result_new ( + G_OBJECT (cnc), callback, user_data, +@@ -3826,6 +3943,8 @@ oal_download_response_cb (SoupSession *s + simple = G_SIMPLE_ASYNC_RESULT (user_data); + data = g_simple_async_result_get_op_res_gpointer (simple); + ++ ews_connection_check_ssl_error (data->cnc, soup_message); ++ + if (ews_connection_credentials_failed (data->cnc, soup_message, simple)) { + g_unlink (data->cache_filename); + } else if (soup_message->status_code != 200) { +@@ -3954,7 +4073,7 @@ e_ews_connection_download_oal_file (EEws + + g_return_if_fail (E_IS_EWS_CONNECTION (cnc)); + +- soup_message = e_ews_get_msg_for_url (cnc->priv->settings, cnc->priv->uri, NULL, &error); ++ soup_message = e_ews_get_msg_for_url (cnc, cnc->priv->settings, cnc->priv->uri, NULL, &error); + + simple = g_simple_async_result_new ( + G_OBJECT (cnc), callback, user_data, +diff -up evolution-ews-3.28.5/src/server/e-ews-connection.h.cve-2019-3890 evolution-ews-3.28.5/src/server/e-ews-connection.h +--- evolution-ews-3.28.5/src/server/e-ews-connection.h.cve-2019-3890 2018-07-30 16:01:00.000000000 +0200 ++++ evolution-ews-3.28.5/src/server/e-ews-connection.h 2019-04-15 09:43:49.689771516 +0200 +@@ -426,9 +426,16 @@ ESourceAuthenticationResult + e_ews_connection_try_credentials_sync + (EEwsConnection *cnc, + const ENamedParameters *credentials, ++ ESource *use_source, ++ gchar **out_certificate_pem, ++ GTlsCertificateFlags *out_certificate_errors, + GCancellable *cancellable, + GError **error); + ESource * e_ews_connection_get_source (EEwsConnection *cnc); ++gboolean e_ews_connection_get_ssl_error_details ++ (EEwsConnection *cnc, ++ gchar **out_certificate_pem, ++ GTlsCertificateFlags *out_certificate_errors); + const gchar * e_ews_connection_get_uri (EEwsConnection *cnc); + ESoupAuthBearer * + e_ews_connection_ref_bearer_auth(EEwsConnection *cnc); +@@ -469,6 +476,8 @@ gboolean e_ews_autodiscover_ws_url_sync + CamelEwsSettings *settings, + const gchar *email_address, + const gchar *password, ++ gchar **out_certificate_pem, ++ GTlsCertificateFlags *out_certificate_errors, + GCancellable *cancellable, + GError **error); + void e_ews_autodiscover_ws_url (ESource *source, +@@ -481,6 +490,8 @@ void e_ews_autodiscover_ws_url (ESource + gboolean e_ews_autodiscover_ws_url_finish + (CamelEwsSettings *settings, + GAsyncResult *result, ++ gchar **out_certificate_pem, ++ GTlsCertificateFlags *out_certificate_errors, + GError **error); + const gchar * e_ews_connection_get_mailbox (EEwsConnection *cnc); + void e_ews_connection_set_mailbox (EEwsConnection *cnc, +diff -up evolution-ews-3.28.5/src/server/e-ews-connection-utils.c.cve-2019-3890 evolution-ews-3.28.5/src/server/e-ews-connection-utils.c +--- evolution-ews-3.28.5/src/server/e-ews-connection-utils.c.cve-2019-3890 2018-07-30 16:01:00.000000000 +0200 ++++ evolution-ews-3.28.5/src/server/e-ews-connection-utils.c 2019-04-15 09:43:49.690771516 +0200 +@@ -522,8 +522,13 @@ e_ews_connection_utils_prepare_message ( + GCancellable *cancellable) + { + ESoupAuthBearer *using_bearer_auth; ++ ESource *source; + GError *local_error = NULL; + ++ source = e_ews_connection_get_source (cnc); ++ if (source) ++ e_soup_ssl_trust_connect (message, source); ++ + if (!ews_connection_utils_maybe_prepare_bearer_auth (cnc, message, cancellable)) + return FALSE; + diff --git a/SOURCES/evolution-ews-3.28.5-disable-reminder-types.patch b/SOURCES/evolution-ews-3.28.5-disable-reminder-types.patch new file mode 100644 index 0000000..31d269d --- /dev/null +++ b/SOURCES/evolution-ews-3.28.5-disable-reminder-types.patch @@ -0,0 +1,12 @@ +diff -up evolution-ews-3.28.5/src/calendar/e-cal-backend-ews.c.disable-reminder-types evolution-ews-3.28.5/src/calendar/e-cal-backend-ews.c +--- evolution-ews-3.28.5/src/calendar/e-cal-backend-ews.c.disable-reminder-types 2018-07-30 16:01:00.000000000 +0200 ++++ evolution-ews-3.28.5/src/calendar/e-cal-backend-ews.c 2018-09-27 18:30:08.889040518 +0200 +@@ -3675,6 +3675,8 @@ ecb_ews_get_backend_property (ECalBacken + return g_strjoin ( + ",", + CAL_STATIC_CAPABILITY_NO_EMAIL_ALARMS, ++ CAL_STATIC_CAPABILITY_NO_AUDIO_ALARMS, ++ CAL_STATIC_CAPABILITY_NO_PROCEDURE_ALARMS, + CAL_STATIC_CAPABILITY_ONE_ALARM_ONLY, + CAL_STATIC_CAPABILITY_REMOVE_ALARMS, + CAL_STATIC_CAPABILITY_NO_THISANDPRIOR, diff --git a/SOURCES/evolution-ews-3.28.5-double-collection-backend-populate.patch b/SOURCES/evolution-ews-3.28.5-double-collection-backend-populate.patch new file mode 100644 index 0000000..c9e4cdb --- /dev/null +++ b/SOURCES/evolution-ews-3.28.5-double-collection-backend-populate.patch @@ -0,0 +1,66 @@ +From f50530ad101b47d461a345ff2b8b295b86c05d3a Mon Sep 17 00:00:00 2001 +From: Milan Crha +Date: Thu, 27 Sep 2018 10:51:05 +0200 +Subject: Collection backend schedules two 'populate' requests after going + online + +ECollectionBackend base class makes sure the 'populate' method is called +after the backend itself goes online, thus there is no need to schedule +it in the descendant again. + +Related to https://gitlab.gnome.org/GNOME/evolution-data-server/issues/36 + +diff --git a/src/collection/e-ews-backend.c b/src/collection/e-ews-backend.c +index 651694b7..d9a973af 100644 +--- a/src/collection/e-ews-backend.c ++++ b/src/collection/e-ews-backend.c +@@ -48,7 +48,7 @@ struct _EEwsBackendPrivate { + + gboolean need_update_folders; + +- gulong notify_online_id; ++ gulong source_changed_id; + }; + + struct _SyncFoldersClosure { +@@ -647,15 +647,21 @@ static void + ews_backend_dispose (GObject *object) + { + EEwsBackendPrivate *priv; ++ ESource *source; + + priv = E_EWS_BACKEND_GET_PRIVATE (object); + ++ source = e_backend_get_source (E_BACKEND (object)); ++ if (source && priv->source_changed_id) { ++ g_signal_handler_disconnect (source, priv->source_changed_id); ++ priv->source_changed_id = 0; ++ } ++ + g_hash_table_remove_all (priv->folders); + +- if (priv->connection != NULL) { +- g_object_unref (priv->connection); +- priv->connection = NULL; +- } ++ g_mutex_lock (&priv->connection_lock); ++ g_clear_object (&priv->connection); ++ g_mutex_unlock (&priv->connection_lock); + + /* Chain up to parent's dispose() method. */ + G_OBJECT_CLASS (e_ews_backend_parent_class)->dispose (object); +@@ -770,12 +776,8 @@ ews_backend_populate (ECollectionBackend *backend) + + ews_backend->priv->need_update_folders = TRUE; + +- if (!ews_backend->priv->notify_online_id) { +- ews_backend->priv->notify_online_id = g_signal_connect ( +- backend, "notify::online", +- G_CALLBACK (ews_backend_populate), NULL); +- +- g_signal_connect ( ++ if (!ews_backend->priv->source_changed_id) { ++ ews_backend->priv->source_changed_id = g_signal_connect ( + source, "changed", + G_CALLBACK (ews_backend_source_changed_cb), ews_backend); + } diff --git a/SOURCES/evolution-ews-3.28.5-import-event-timezone.patch b/SOURCES/evolution-ews-3.28.5-import-event-timezone.patch new file mode 100644 index 0000000..5d30d3f --- /dev/null +++ b/SOURCES/evolution-ews-3.28.5-import-event-timezone.patch @@ -0,0 +1,259 @@ +diff -up evolution-ews-3.28.5/src/calendar/e-cal-backend-ews.c.import-event-timezone evolution-ews-3.28.5/src/calendar/e-cal-backend-ews.c +--- evolution-ews-3.28.5/src/calendar/e-cal-backend-ews.c.import-event-timezone 2018-07-30 16:01:00.000000000 +0200 ++++ evolution-ews-3.28.5/src/calendar/e-cal-backend-ews.c 2018-09-04 11:14:27.815647542 +0200 +@@ -579,7 +579,7 @@ ecb_ews_item_to_component_sync (ECalBack + if (start_zone != NULL) { + icalcomp = icalcomponent_get_first_component (vcomp, kind); + +- dt = e_cal_backend_ews_get_datetime_with_zone (timezone_cache, icalcomp, ICAL_DTSTART_PROPERTY, icalproperty_get_dtstart); ++ dt = e_cal_backend_ews_get_datetime_with_zone (timezone_cache, vcomp, icalcomp, ICAL_DTSTART_PROPERTY, icalproperty_get_dtstart); + dt = icaltime_convert_to_zone (dt, start_zone); + icalcomponent_set_dtstart (icalcomp, dt); + +@@ -587,7 +587,7 @@ ecb_ews_item_to_component_sync (ECalBack + e_timezone_cache_add_timezone (timezone_cache, start_zone); + + if (end_zone != NULL) { +- dt = e_cal_backend_ews_get_datetime_with_zone (timezone_cache, icalcomp, ICAL_DTEND_PROPERTY, icalproperty_get_dtend); ++ dt = e_cal_backend_ews_get_datetime_with_zone (timezone_cache, vcomp, icalcomp, ICAL_DTEND_PROPERTY, icalproperty_get_dtend); + dt = icaltime_convert_to_zone (dt, end_zone); + icalcomponent_set_dtend (icalcomp, dt); + +@@ -655,11 +655,11 @@ ecb_ews_item_to_component_sync (ECalBack + zone = icaltimezone_get_builtin_timezone (tzid); + + if (zone != NULL) { +- dt = e_cal_backend_ews_get_datetime_with_zone (timezone_cache, icalcomp, ICAL_DTSTART_PROPERTY, icalproperty_get_dtstart); ++ dt = e_cal_backend_ews_get_datetime_with_zone (timezone_cache, vcomp, icalcomp, ICAL_DTSTART_PROPERTY, icalproperty_get_dtstart); + dt = icaltime_convert_to_zone (dt, zone); + icalcomponent_set_dtstart (icalcomp, dt); + +- dt = e_cal_backend_ews_get_datetime_with_zone (timezone_cache, icalcomp, ICAL_DTEND_PROPERTY, icalproperty_get_dtend); ++ dt = e_cal_backend_ews_get_datetime_with_zone (timezone_cache, vcomp, icalcomp, ICAL_DTEND_PROPERTY, icalproperty_get_dtend); + dt = icaltime_convert_to_zone (dt, zone); + icalcomponent_set_dtend (icalcomp, dt); + } +@@ -2930,7 +2930,7 @@ ecb_ews_send_cancellation_email_sync (EC + icalcomponent_add_property (vevent, icalproperty_new_status (ICAL_STATUS_CANCELLED)); + prop = icalcomponent_get_first_property (vevent, ICAL_METHOD_PROPERTY); + if (prop != NULL) icalcomponent_remove_property (vevent, prop); +- dt = e_cal_backend_ews_get_datetime_with_zone (E_TIMEZONE_CACHE (cbews), vevent, ICAL_DTSTART_PROPERTY, icalproperty_get_dtstart); ++ dt = e_cal_backend_ews_get_datetime_with_zone (E_TIMEZONE_CACHE (cbews), NULL, vevent, ICAL_DTSTART_PROPERTY, icalproperty_get_dtstart); + icaltz = (icaltimezone *) + (dt.zone ? dt.zone : ecb_ews_get_timezone_from_ical_component (cbews, vevent)); + vtz = icaltimezone_get_component (icaltz); +@@ -2973,6 +2973,7 @@ ecb_ews_send_cancellation_email_sync (EC + + static void + ecb_ews_receive_objects_no_exchange_mail (ECalBackendEws *cbews, ++ icalcomponent *vcalendar, + icalcomponent *subcomp, + GSList **ids, + GCancellable *cancellable, +@@ -2984,6 +2985,7 @@ ecb_ews_receive_objects_no_exchange_mail + convert_data.connection = cbews->priv->cnc; + convert_data.timezone_cache = E_TIMEZONE_CACHE (cbews); + convert_data.icalcomp = subcomp; ++ convert_data.vcalendar = vcalendar; + convert_data.default_zone = icaltimezone_get_utc_timezone (); + + fid = e_ews_folder_id_new (cbews->priv->folder_id, NULL, FALSE); +@@ -3228,6 +3230,7 @@ ecb_ews_get_item_accept_id (ECalComponen + + static gboolean + ecb_ews_do_method_request_publish_reply (ECalBackendEws *cbews, ++ icalcomponent *vcalendar, + ECalComponent *comp, + icalcomponent *subcomp, + const gchar *response_type, +@@ -3258,7 +3261,7 @@ ecb_ews_do_method_request_publish_reply + while (pass < 2) { + /*in case we do not have item id we will create item with mime content only*/ + if (!item_id || (response_type && g_ascii_strcasecmp (response_type, "NEEDS-ACTION") == 0)) { +- ecb_ews_receive_objects_no_exchange_mail (cbews, subcomp, &ids, cancellable, &local_error); ++ ecb_ews_receive_objects_no_exchange_mail (cbews, vcalendar, subcomp, &ids, cancellable, &local_error); + } else { + EwsCalendarConvertData convert_data = { 0 }; + +@@ -3367,6 +3370,7 @@ ecb_ews_do_method_request_publish_reply + } + + convert_data.timezone_cache = E_TIMEZONE_CACHE (cbews); ++ convert_data.vcalendar = vcalendar; + + e_ews_connection_update_items_sync ( + cbews->priv->cnc, +@@ -3452,7 +3456,7 @@ ecb_ews_receive_objects_sync (ECalBacken + + comp = e_cal_component_new_from_icalcomponent (icalcomponent_new_clone (subcomp)); + +- success = ecb_ews_do_method_request_publish_reply (cbews, comp, subcomp, response_type, user_email, rsvp_requested, cancellable, error); ++ success = ecb_ews_do_method_request_publish_reply (cbews, icalcomp, comp, subcomp, response_type, user_email, rsvp_requested, cancellable, error); + + do_refresh = TRUE; + +diff -up evolution-ews-3.28.5/src/calendar/e-cal-backend-ews-utils.c.import-event-timezone evolution-ews-3.28.5/src/calendar/e-cal-backend-ews-utils.c +--- evolution-ews-3.28.5/src/calendar/e-cal-backend-ews-utils.c.import-event-timezone 2018-07-30 16:01:00.000000000 +0200 ++++ evolution-ews-3.28.5/src/calendar/e-cal-backend-ews-utils.c 2018-09-04 11:16:58.892645453 +0200 +@@ -178,7 +178,8 @@ e_cal_backend_ews_tz_util_get_msdn_equiv + { + const gchar *msdn_tz_location = NULL; + +- g_return_val_if_fail (ical_tz_location != NULL, NULL); ++ if (!ical_tz_location || !*ical_tz_location) ++ return NULL; + + g_rec_mutex_lock (&tz_mutex); + if (ical_to_msdn == NULL) { +@@ -199,7 +200,8 @@ e_cal_backend_ews_tz_util_get_ical_equiv + { + const gchar *ical_tz_location = NULL; + +- g_return_val_if_fail (msdn_tz_location != NULL, NULL); ++ if (!msdn_tz_location || !*msdn_tz_location) ++ return NULL; + + g_rec_mutex_lock (&tz_mutex); + if (msdn_to_ical == NULL) { +@@ -1113,11 +1115,11 @@ convert_vevent_calcomp_to_xml (ESoapMess + e_ews_message_write_string_parameter (msg, "ReminderIsSet", NULL, "false"); + + /* start time, end time and meeting time zone */ +- dtstart = e_cal_backend_ews_get_datetime_with_zone (convert_data->timezone_cache, icalcomp, ICAL_DTSTART_PROPERTY, icalproperty_get_dtstart); ++ dtstart = e_cal_backend_ews_get_datetime_with_zone (convert_data->timezone_cache, convert_data->vcalendar, icalcomp, ICAL_DTSTART_PROPERTY, icalproperty_get_dtstart); + tzid_start = (icaltimezone *) (dtstart.zone ? dtstart.zone : convert_data->default_zone); + ical_location_start = icaltimezone_get_location (tzid_start); + +- dtend = e_cal_backend_ews_get_datetime_with_zone (convert_data->timezone_cache, icalcomp, ICAL_DTEND_PROPERTY, icalproperty_get_dtend); ++ dtend = e_cal_backend_ews_get_datetime_with_zone (convert_data->timezone_cache, convert_data->vcalendar, icalcomp, ICAL_DTEND_PROPERTY, icalproperty_get_dtend); + tzid_end = (icaltimezone *) (dtend.zone ? dtend.zone : convert_data->default_zone); + ical_location_end = icaltimezone_get_location (tzid_end); + +@@ -1272,7 +1274,7 @@ convert_vtodo_calcomp_to_xml (ESoapMessa + + prop = icalcomponent_get_first_property (icalcomp, ICAL_DUE_PROPERTY); + if (prop) { +- dt = e_cal_backend_ews_get_datetime_with_zone (convert_data->timezone_cache, icalcomp, ICAL_DUE_PROPERTY, icalproperty_get_due); ++ dt = e_cal_backend_ews_get_datetime_with_zone (convert_data->timezone_cache, convert_data->vcalendar, icalcomp, ICAL_DUE_PROPERTY, icalproperty_get_due); + e_ews_cal_utils_set_time (msg, "DueDate", &dt, TRUE); + } + +@@ -1285,7 +1287,7 @@ convert_vtodo_calcomp_to_xml (ESoapMessa + + prop = icalcomponent_get_first_property (icalcomp, ICAL_DTSTART_PROPERTY); + if (prop) { +- dt = e_cal_backend_ews_get_datetime_with_zone (convert_data->timezone_cache, icalcomp, ICAL_DTSTART_PROPERTY, icalproperty_get_dtstart); ++ dt = e_cal_backend_ews_get_datetime_with_zone (convert_data->timezone_cache, convert_data->vcalendar, icalcomp, ICAL_DTSTART_PROPERTY, icalproperty_get_dtstart); + e_ews_cal_utils_set_time (msg, "StartDate", &dt, TRUE); + } + +@@ -1536,8 +1538,8 @@ convert_vevent_component_to_updatexml (E + } + /* Update other properties allowed only for meeting organizers*/ + /*meeting dates*/ +- dtstart = e_cal_backend_ews_get_datetime_with_zone (convert_data->timezone_cache, icalcomp, ICAL_DTSTART_PROPERTY, icalproperty_get_dtstart); +- dtstart_old = e_cal_backend_ews_get_datetime_with_zone (convert_data->timezone_cache, icalcomp_old, ICAL_DTSTART_PROPERTY, icalproperty_get_dtstart); ++ dtstart = e_cal_backend_ews_get_datetime_with_zone (convert_data->timezone_cache, convert_data->vcalendar, icalcomp, ICAL_DTSTART_PROPERTY, icalproperty_get_dtstart); ++ dtstart_old = e_cal_backend_ews_get_datetime_with_zone (convert_data->timezone_cache, convert_data->vcalendar, icalcomp_old, ICAL_DTSTART_PROPERTY, icalproperty_get_dtstart); + dt_start_changed = icaltime_compare (dtstart, dtstart_old) != 0; + if (dtstart.zone != NULL) { + tzid_start = (icaltimezone *) dtstart.zone; +@@ -1548,8 +1550,8 @@ convert_vevent_component_to_updatexml (E + dt_start_changed_timezone_name = TRUE; + } + +- dtend = e_cal_backend_ews_get_datetime_with_zone (convert_data->timezone_cache, icalcomp, ICAL_DTEND_PROPERTY, icalproperty_get_dtend); +- dtend_old = e_cal_backend_ews_get_datetime_with_zone (convert_data->timezone_cache, icalcomp_old, ICAL_DTEND_PROPERTY, icalproperty_get_dtend); ++ dtend = e_cal_backend_ews_get_datetime_with_zone (convert_data->timezone_cache, convert_data->vcalendar, icalcomp, ICAL_DTEND_PROPERTY, icalproperty_get_dtend); ++ dtend_old = e_cal_backend_ews_get_datetime_with_zone (convert_data->timezone_cache, convert_data->vcalendar, icalcomp_old, ICAL_DTEND_PROPERTY, icalproperty_get_dtend); + dt_end_changed = icaltime_compare (dtend, dtend_old) != 0; + if (dtend.zone != NULL) { + tzid_end = (icaltimezone *) dtend.zone; +@@ -1768,7 +1770,7 @@ convert_vtodo_component_to_updatexml (ES + + prop = icalcomponent_get_first_property (icalcomp, ICAL_DUE_PROPERTY); + if (prop) { +- dt = e_cal_backend_ews_get_datetime_with_zone (convert_data->timezone_cache, icalcomp, ICAL_DUE_PROPERTY, icalproperty_get_due); ++ dt = e_cal_backend_ews_get_datetime_with_zone (convert_data->timezone_cache, convert_data->vcalendar, icalcomp, ICAL_DUE_PROPERTY, icalproperty_get_due); + e_ews_message_start_set_item_field (msg, "DueDate", "task", "Task"); + e_ews_cal_utils_set_time (msg, "DueDate", &dt, TRUE); + e_ews_message_end_set_item_field (msg); +@@ -1787,7 +1789,7 @@ convert_vtodo_component_to_updatexml (ES + + prop = icalcomponent_get_first_property (icalcomp, ICAL_DTSTART_PROPERTY); + if (prop) { +- dt = e_cal_backend_ews_get_datetime_with_zone (convert_data->timezone_cache, icalcomp, ICAL_DTSTART_PROPERTY, icalproperty_get_dtstart); ++ dt = e_cal_backend_ews_get_datetime_with_zone (convert_data->timezone_cache, convert_data->vcalendar, icalcomp, ICAL_DTSTART_PROPERTY, icalproperty_get_dtstart); + e_ews_message_start_set_item_field (msg, "StartDate", "task", "Task"); + e_ews_cal_utils_set_time (msg, "StartDate", &dt, TRUE); + e_ews_message_end_set_item_field (msg); +@@ -2011,6 +2013,7 @@ e_cal_backend_ews_prepare_accept_item_re + + struct icaltimetype + e_cal_backend_ews_get_datetime_with_zone (ETimezoneCache *timezone_cache, ++ icalcomponent *vcalendar, + icalcomponent *comp, + icalproperty_kind prop_kind, + struct icaltimetype (* get_func) (const icalproperty *prop)) +@@ -2018,7 +2021,7 @@ e_cal_backend_ews_get_datetime_with_zone + struct icaltimetype dt = icaltime_null_time (); + icalproperty *prop; + icalparameter *param; +- const gchar *tzid; ++ const gchar *tzid, *eqv_tzid; + + g_return_val_if_fail (E_IS_TIMEZONE_CACHE (timezone_cache), dt); + g_return_val_if_fail (comp != NULL, dt); +@@ -2034,6 +2037,8 @@ e_cal_backend_ews_get_datetime_with_zone + icaltime_is_null_time (dt)) + return dt; + ++ dt.zone = NULL; ++ + param = icalproperty_get_first_parameter (prop, ICAL_TZID_PARAMETER); + if (!param) + return dt; +@@ -2042,7 +2047,23 @@ e_cal_backend_ews_get_datetime_with_zone + if (!tzid || !*tzid) + return dt; + +- dt.zone = e_timezone_cache_get_timezone (timezone_cache, tzid); ++ eqv_tzid = e_cal_backend_ews_tz_util_get_ical_equivalent (tzid); ++ ++ if (!eqv_tzid) { ++ /* Unlikely to work, but just in case */ ++ eqv_tzid = e_cal_backend_ews_tz_util_get_msdn_equivalent (tzid); ++ if (eqv_tzid) ++ eqv_tzid = e_cal_backend_ews_tz_util_get_ical_equivalent (eqv_tzid); ++ } ++ ++ if (eqv_tzid) ++ dt.zone = e_timezone_cache_get_timezone (timezone_cache, eqv_tzid); ++ ++ if (!dt.zone) ++ dt.zone = e_timezone_cache_get_timezone (timezone_cache, tzid); ++ ++ if (!dt.zone) ++ dt.zone = vcalendar ? icalcomponent_get_timezone (vcalendar, tzid) : NULL; + + return dt; + } +diff -up evolution-ews-3.28.5/src/calendar/e-cal-backend-ews-utils.h.import-event-timezone evolution-ews-3.28.5/src/calendar/e-cal-backend-ews-utils.h +--- evolution-ews-3.28.5/src/calendar/e-cal-backend-ews-utils.h.import-event-timezone 2018-07-30 16:01:00.000000000 +0200 ++++ evolution-ews-3.28.5/src/calendar/e-cal-backend-ews-utils.h 2018-09-04 11:14:27.814647542 +0200 +@@ -47,6 +47,7 @@ typedef struct { + ECalComponent *comp; + ECalComponent *old_comp; + icalcomponent *icalcomp; ++ icalcomponent *vcalendar; /* can be NULL, parent of icalcomp, where timezones can be eventually found */ + gchar *item_id; + gchar *change_key; + EEwsItemChangeType change_type; +@@ -82,6 +83,7 @@ guint e_cal_backend_ews_rid_to_index (ic + + struct icaltimetype + e_cal_backend_ews_get_datetime_with_zone (ETimezoneCache *timezone_cache, ++ icalcomponent *vcalendar, + icalcomponent *comp, + icalproperty_kind prop_kind, + struct icaltimetype (* get_func) (const icalproperty *prop)); diff --git a/SOURCES/evolution-ews-3.28.5-meeting-with-attachment.patch b/SOURCES/evolution-ews-3.28.5-meeting-with-attachment.patch new file mode 100644 index 0000000..b9191c4 --- /dev/null +++ b/SOURCES/evolution-ews-3.28.5-meeting-with-attachment.patch @@ -0,0 +1,114 @@ +diff -up evolution-ews-3.28.5/src/calendar/e-cal-backend-ews.c.meeting-with-attachment evolution-ews-3.28.5/src/calendar/e-cal-backend-ews.c +--- evolution-ews-3.28.5/src/calendar/e-cal-backend-ews.c.meeting-with-attachment 2018-09-27 18:32:25.783038625 +0200 ++++ evolution-ews-3.28.5/src/calendar/e-cal-backend-ews.c 2018-09-27 18:32:25.786038625 +0200 +@@ -2598,7 +2598,7 @@ ecb_ews_save_component_sync (ECalMetaBac + GHashTable *removed_indexes; + EwsCalendarConvertData convert_data = { 0 }; + EEwsItem *item = NULL; +- const EwsId *ews_id = NULL; ++ EwsId *ews_id = NULL; + const gchar *send_meeting_invitations; + icalcomponent *icalcomp; + icalproperty *prop; +@@ -2642,7 +2642,7 @@ ecb_ews_save_component_sync (ECalMetaBac + if (item) { + g_object_ref (item); + +- ews_id = e_ews_item_get_id (item); ++ ews_id = e_ews_id_copy (e_ews_item_get_id (item)); + } + } + +@@ -2666,6 +2666,8 @@ ecb_ews_save_component_sync (ECalMetaBac + g_clear_object (&item); + + item = items_req->data; ++ ++ e_ews_id_free (ews_id); + ews_id = NULL; + + if (e_ews_item_get_item_type (item) == E_EWS_ITEM_TYPE_ERROR) { +@@ -2674,7 +2676,7 @@ ecb_ews_save_component_sync (ECalMetaBac + success = FALSE; + } else { + item = g_object_ref (item); +- ews_id = e_ews_item_get_id (item); ++ ews_id = e_ews_id_copy (e_ews_item_get_id (item)); + } + } + +@@ -2689,13 +2691,21 @@ ecb_ews_save_component_sync (ECalMetaBac + g_warn_if_fail (ews_id != NULL); + + if (ews_id && ecb_ews_extract_attachments (icalcomp, &info_attachments)) { ++ gchar *changekey = NULL; + GSList *ids = NULL; + + success = e_ews_connection_create_attachments_sync (cbews->priv->cnc, EWS_PRIORITY_MEDIUM, +- ews_id, info_attachments, FALSE, NULL, &ids, cancellable, error); ++ ews_id, info_attachments, FALSE, &changekey, &ids, cancellable, error); + + g_slist_free_full (info_attachments, (GDestroyNotify) e_ews_attachment_info_free); + g_slist_free_full (ids, g_free); ++ ++ if (success && changekey) { ++ g_free (ews_id->change_key); ++ ews_id->change_key = changekey; ++ } else { ++ g_free (changekey); ++ } + } + } + +@@ -2730,6 +2740,7 @@ ecb_ews_save_component_sync (ECalMetaBac + } + + icalcomponent_free (icalcomp); ++ e_ews_id_free (ews_id); + g_clear_object (&item); + + for (link = (GSList *) instances; link && success; link = g_slist_next (link)) { +diff -up evolution-ews-3.28.5/src/server/e-ews-item.c.meeting-with-attachment evolution-ews-3.28.5/src/server/e-ews-item.c +--- evolution-ews-3.28.5/src/server/e-ews-item.c.meeting-with-attachment 2018-09-27 18:32:25.785038625 +0200 ++++ evolution-ews-3.28.5/src/server/e-ews-item.c 2018-09-27 18:32:25.787038625 +0200 +@@ -2695,3 +2695,28 @@ e_ews_item_util_strip_ex_address (const + + return ex_address; + } ++ ++EwsId * ++e_ews_id_copy (const EwsId *ews_id) ++{ ++ EwsId *copy; ++ ++ if (!ews_id) ++ return NULL; ++ ++ copy = g_new0 (EwsId, 1); ++ copy->id = g_strdup (ews_id->id); ++ copy->change_key = g_strdup (ews_id->change_key); ++ ++ return copy; ++} ++ ++void ++e_ews_id_free (EwsId *ews_id) ++{ ++ if (ews_id) { ++ g_free (ews_id->id); ++ g_free (ews_id->change_key); ++ g_free (ews_id); ++ } ++} +diff -up evolution-ews-3.28.5/src/server/e-ews-item.h.meeting-with-attachment evolution-ews-3.28.5/src/server/e-ews-item.h +--- evolution-ews-3.28.5/src/server/e-ews-item.h.meeting-with-attachment 2018-07-30 16:01:00.000000000 +0200 ++++ evolution-ews-3.28.5/src/server/e-ews-item.h 2018-09-27 18:32:25.787038625 +0200 +@@ -384,6 +384,8 @@ void e_ews_permissions_free (GSList *pe + /* Utility functions */ + const gchar * e_ews_item_util_strip_ex_address + (const gchar *ex_address); ++EwsId * e_ews_id_copy (const EwsId *ews_id); ++void e_ews_id_free (EwsId *ews_id); + + G_END_DECLS + diff --git a/SOURCES/evolution-ews-3.28.5-oauth2-endpoints.patch b/SOURCES/evolution-ews-3.28.5-oauth2-endpoints.patch new file mode 100644 index 0000000..6f3ad45 --- /dev/null +++ b/SOURCES/evolution-ews-3.28.5-oauth2-endpoints.patch @@ -0,0 +1,707 @@ +diff -up evolution-ews-3.28.5/CMakeLists.txt.oauth2-endpoints evolution-ews-3.28.5/CMakeLists.txt +--- evolution-ews-3.28.5/CMakeLists.txt.oauth2-endpoints 2020-10-07 17:08:15.761562791 +0200 ++++ evolution-ews-3.28.5/CMakeLists.txt 2020-10-07 17:08:15.762562790 +0200 +@@ -195,6 +195,12 @@ if(WITH_OFFICE365_REDIRECT_URI STREQUAL + set(WITH_OFFICE365_REDIRECT_URI "https://login.microsoftonline.com/common/oauth2/nativeclient") + endif(WITH_OFFICE365_REDIRECT_URI STREQUAL "") + ++add_printable_variable(WITH_OFFICE365_ENDPOINT_HOST "Office365.com OAuth 2.0 endpoint host" "") ++ ++if(WITH_OFFICE365_ENDPOINT_HOST STREQUAL "") ++ set(WITH_OFFICE365_ENDPOINT_HOST "login.microsoftonline.com") ++endif(WITH_OFFICE365_ENDPOINT_HOST STREQUAL "") ++ + # ****************************** + # Special directories + # ****************************** +diff -up evolution-ews-3.28.5/config.h.in.oauth2-endpoints evolution-ews-3.28.5/config.h.in +--- evolution-ews-3.28.5/config.h.in.oauth2-endpoints 2018-07-30 16:01:00.000000000 +0200 ++++ evolution-ews-3.28.5/config.h.in 2020-10-07 17:08:15.762562790 +0200 +@@ -29,3 +29,6 @@ + + /* Define Office365 OAuth 2.0 default Redirect URI to use */ + #define OFFICE365_REDIRECT_URI "@WITH_OFFICE365_REDIRECT_URI@" ++ ++/* Define Office365 OAuth 2.0 default Endpoint Host to use */ ++#define OFFICE365_ENDPOINT_HOST "@WITH_OFFICE365_ENDPOINT_HOST@" +diff -up evolution-ews-3.28.5/src/configuration/e-mail-config-ews-backend.c.oauth2-endpoints evolution-ews-3.28.5/src/configuration/e-mail-config-ews-backend.c +--- evolution-ews-3.28.5/src/configuration/e-mail-config-ews-backend.c.oauth2-endpoints 2020-10-07 17:08:15.761562791 +0200 ++++ evolution-ews-3.28.5/src/configuration/e-mail-config-ews-backend.c 2020-10-07 17:08:15.762562790 +0200 +@@ -29,6 +29,7 @@ + #include + + #include "server/camel-ews-settings.h" ++#include "server/e-oauth2-service-office365.h" + + #include "e-mail-config-ews-autodiscover.h" + #include "e-ews-config-utils.h" +@@ -50,6 +51,8 @@ struct _EMailConfigEwsBackendPrivate { + GtkWidget *oauth2_tenant_entry; + GtkWidget *oauth2_client_id_entry; + GtkWidget *oauth2_redirect_uri_entry; ++ GtkWidget *oauth2_resource_uri_entry; ++ GtkWidget *oauth2_endpoint_host_entry; + }; + + G_DEFINE_DYNAMIC_TYPE ( +@@ -156,6 +159,11 @@ mail_config_ews_backend_insert_widgets ( + GtkLabel *label; + GtkWidget *widget; + GtkWidget *container; ++ GtkWidget *expander; ++ GtkWidget *advanced_help; ++ GtkWidget *endpoint_host_label; ++ GtkWidget *redirect_uri_label; ++ GtkWidget *resource_uri_label; + const gchar *extension_name; + const gchar *text; + gchar *markup; +@@ -381,11 +389,62 @@ mail_config_ews_backend_insert_widgets ( + _("There is not set any default application ID"), + g_strdup_printf (_("Default application ID is “%s”"), OFFICE365_CLIENT_ID)); + +- widget = gtk_label_new_with_mnemonic (_("_Redirect URI:")); ++ container = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 6); ++ gtk_grid_attach (priv->oauth2_settings_grid, container, 0, 3, 2, 1); ++ ++ widget = gtk_expander_new_with_mnemonic (_("_Advanced Settings")); ++ gtk_widget_set_margin_left (widget, 12); ++ gtk_box_pack_start (GTK_BOX (container), widget, FALSE, FALSE, 0); ++ expander = widget; ++ ++ e_binding_bind_property ( ++ priv->oauth2_override_check, "active", ++ widget, "sensitive", ++ G_BINDING_SYNC_CREATE); ++ ++ markup = g_markup_printf_escaped ("(%s)", _("Help…")); ++ widget = gtk_label_new (markup); ++ gtk_label_set_use_markup (GTK_LABEL (widget), TRUE); ++ gtk_misc_set_alignment (GTK_MISC (widget), 0.0, 0.5); ++ gtk_box_pack_start (GTK_BOX (container), widget, FALSE, FALSE, 0); ++ g_free (markup); ++ advanced_help = widget; ++ ++ widget = gtk_label_new_with_mnemonic (_("_Endpoint host:")); ++ gtk_widget_set_margin_left (widget, 12); ++ gtk_misc_set_alignment (GTK_MISC (widget), 1.0, 0.5); ++ gtk_grid_attach (priv->oauth2_settings_grid, widget, 0, 4, 1, 1); ++ label = GTK_LABEL (widget); ++ endpoint_host_label = widget; ++ ++ e_binding_bind_property ( ++ priv->oauth2_override_check, "active", ++ widget, "sensitive", ++ G_BINDING_SYNC_CREATE); ++ ++ widget = gtk_entry_new (); ++ gtk_widget_set_hexpand (widget, TRUE); ++ gtk_label_set_mnemonic_widget (label, widget); ++ gtk_grid_attach (priv->oauth2_settings_grid, widget, 1, 4, 1, 1); ++ priv->oauth2_endpoint_host_entry = widget; ++ ++ e_binding_bind_property ( ++ priv->oauth2_override_check, "active", ++ widget, "sensitive", ++ G_BINDING_SYNC_CREATE); ++ ++ markup = g_strdup_printf (_("Default endpoint host is “%s”"), "login.microsoftonline.com"); ++ mail_config_ews_backend_set_oauth2_tooltip (widget, OFFICE365_ENDPOINT_HOST, ++ markup, ++ g_strdup_printf (_("Default endpoint host is “%s”"), OFFICE365_ENDPOINT_HOST)); ++ g_free (markup); ++ ++ widget = gtk_label_new_with_mnemonic (_("Red_irect URI:")); + gtk_widget_set_margin_left (widget, 12); + gtk_misc_set_alignment (GTK_MISC (widget), 1.0, 0.5); +- gtk_grid_attach (priv->oauth2_settings_grid, widget, 0, 3, 1, 1); ++ gtk_grid_attach (priv->oauth2_settings_grid, widget, 0, 5, 1, 1); + label = GTK_LABEL (widget); ++ redirect_uri_label = widget; + + e_binding_bind_property ( + priv->oauth2_override_check, "active", +@@ -395,7 +454,7 @@ mail_config_ews_backend_insert_widgets ( + widget = gtk_entry_new (); + gtk_widget_set_hexpand (widget, TRUE); + gtk_label_set_mnemonic_widget (label, widget); +- gtk_grid_attach (priv->oauth2_settings_grid, widget, 1, 3, 1, 1); ++ gtk_grid_attach (priv->oauth2_settings_grid, widget, 1, 5, 1, 1); + priv->oauth2_redirect_uri_entry = widget; + + e_binding_bind_property ( +@@ -409,8 +468,77 @@ mail_config_ews_backend_insert_widgets ( + g_strdup_printf (_("Default redirect URI is “%s”"), OFFICE365_REDIRECT_URI)); + g_free (markup); + ++ widget = gtk_label_new_with_mnemonic (_("Re_source URI:")); ++ gtk_widget_set_margin_left (widget, 12); ++ gtk_misc_set_alignment (GTK_MISC (widget), 1.0, 0.5); ++ gtk_grid_attach (priv->oauth2_settings_grid, widget, 0, 6, 1, 1); ++ label = GTK_LABEL (widget); ++ resource_uri_label = widget; ++ ++ e_binding_bind_property ( ++ priv->oauth2_override_check, "active", ++ widget, "sensitive", ++ G_BINDING_SYNC_CREATE); ++ ++ widget = gtk_entry_new (); ++ gtk_widget_set_hexpand (widget, TRUE); ++ gtk_label_set_mnemonic_widget (label, widget); ++ gtk_grid_attach (priv->oauth2_settings_grid, widget, 1, 6, 1, 1); ++ priv->oauth2_resource_uri_entry = widget; ++ ++ e_binding_bind_property ( ++ priv->oauth2_override_check, "active", ++ widget, "sensitive", ++ G_BINDING_SYNC_CREATE); ++ ++ markup = g_strdup_printf (_("Default resource URI is derived from the Host URL, or it can eventually fall back to “%s”."), OFFICE365_FALLBACK_RESOURCE_URI); ++ mail_config_ews_backend_set_oauth2_tooltip (widget, NULL, ++ markup, ++ NULL); ++ g_free (markup); ++ + gtk_widget_show_all (GTK_WIDGET (priv->oauth2_settings_grid)); + ++ gtk_expander_set_expanded (GTK_EXPANDER (expander), ++ e_ews_util_strcmp0 (camel_ews_settings_get_oauth2_endpoint_host (CAMEL_EWS_SETTINGS (settings)), NULL) != 0 || ++ e_ews_util_strcmp0 (camel_ews_settings_get_oauth2_redirect_uri (CAMEL_EWS_SETTINGS (settings)), NULL) != 0 || ++ e_ews_util_strcmp0 (camel_ews_settings_get_oauth2_resource_uri (CAMEL_EWS_SETTINGS (settings)), NULL) != 0); ++ ++ e_binding_bind_property ( ++ expander, "expanded", ++ advanced_help, "visible", ++ G_BINDING_SYNC_CREATE); ++ ++ e_binding_bind_property ( ++ expander, "expanded", ++ endpoint_host_label, "visible", ++ G_BINDING_SYNC_CREATE); ++ ++ e_binding_bind_property ( ++ expander, "expanded", ++ priv->oauth2_endpoint_host_entry, "visible", ++ G_BINDING_SYNC_CREATE); ++ ++ e_binding_bind_property ( ++ expander, "expanded", ++ redirect_uri_label, "visible", ++ G_BINDING_SYNC_CREATE); ++ ++ e_binding_bind_property ( ++ expander, "expanded", ++ priv->oauth2_redirect_uri_entry, "visible", ++ G_BINDING_SYNC_CREATE); ++ ++ e_binding_bind_property ( ++ expander, "expanded", ++ resource_uri_label, "visible", ++ G_BINDING_SYNC_CREATE); ++ ++ e_binding_bind_property ( ++ expander, "expanded", ++ priv->oauth2_resource_uri_entry, "visible", ++ G_BINDING_SYNC_CREATE); ++ + e_binding_bind_property_full ( + priv->auth_check, "active-mechanism", + priv->oauth2_settings_grid, "visible", +@@ -463,11 +591,23 @@ mail_config_ews_backend_insert_widgets ( + G_BINDING_SYNC_CREATE); + + e_binding_bind_object_text_property ( +- settings, "oauth2-redirect_uri", ++ settings, "oauth2-redirect-uri", + priv->oauth2_redirect_uri_entry, "text", + G_BINDING_BIDIRECTIONAL | + G_BINDING_SYNC_CREATE); + ++ e_binding_bind_object_text_property ( ++ settings, "oauth2-resource-uri", ++ priv->oauth2_resource_uri_entry, "text", ++ G_BINDING_BIDIRECTIONAL | ++ G_BINDING_SYNC_CREATE); ++ ++ e_binding_bind_object_text_property ( ++ settings, "oauth2-endpoint-host", ++ priv->oauth2_endpoint_host_entry, "text", ++ G_BINDING_BIDIRECTIONAL | ++ G_BINDING_SYNC_CREATE); ++ + extension_name = E_SOURCE_EXTENSION_COLLECTION; + source = e_mail_config_service_backend_get_collection (backend); + extension = e_source_get_extension (source, extension_name); +diff -up evolution-ews-3.28.5/src/server/camel-ews-settings.c.oauth2-endpoints evolution-ews-3.28.5/src/server/camel-ews-settings.c +--- evolution-ews-3.28.5/src/server/camel-ews-settings.c.oauth2-endpoints 2018-07-30 16:01:00.000000000 +0200 ++++ evolution-ews-3.28.5/src/server/camel-ews-settings.c 2020-10-07 17:08:15.762562790 +0200 +@@ -47,6 +47,8 @@ struct _CamelEwsSettingsPrivate { + gchar *oauth2_tenant; + gchar *oauth2_client_id; + gchar *oauth2_redirect_uri; ++ gchar *oauth2_resource_uri; ++ gchar *oauth2_endpoint_host; + }; + + enum { +@@ -74,7 +76,9 @@ enum { + PROP_OVERRIDE_OAUTH2, + PROP_OAUTH2_TENANT, + PROP_OAUTH2_CLIENT_ID, +- PROP_OAUTH2_REDIRECT_URI ++ PROP_OAUTH2_REDIRECT_URI, ++ PROP_OAUTH2_RESOURCE_URI, ++ PROP_OAUTH2_ENDPOINT_HOST + }; + + G_DEFINE_TYPE_WITH_CODE ( +@@ -263,6 +267,18 @@ ews_settings_set_property (GObject *obje + CAMEL_EWS_SETTINGS (object), + g_value_get_string (value)); + return; ++ ++ case PROP_OAUTH2_RESOURCE_URI: ++ camel_ews_settings_set_oauth2_resource_uri ( ++ CAMEL_EWS_SETTINGS (object), ++ g_value_get_string (value)); ++ return; ++ ++ case PROP_OAUTH2_ENDPOINT_HOST: ++ camel_ews_settings_set_oauth2_endpoint_host ( ++ CAMEL_EWS_SETTINGS (object), ++ g_value_get_string (value)); ++ return; + } + + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); +@@ -442,6 +458,20 @@ ews_settings_get_property (GObject *obje + camel_ews_settings_dup_oauth2_redirect_uri ( + CAMEL_EWS_SETTINGS (object))); + return; ++ ++ case PROP_OAUTH2_RESOURCE_URI: ++ g_value_take_string ( ++ value, ++ camel_ews_settings_dup_oauth2_resource_uri ( ++ CAMEL_EWS_SETTINGS (object))); ++ return; ++ ++ case PROP_OAUTH2_ENDPOINT_HOST: ++ g_value_take_string ( ++ value, ++ camel_ews_settings_dup_oauth2_endpoint_host ( ++ CAMEL_EWS_SETTINGS (object))); ++ return; + } + + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); +@@ -466,6 +496,8 @@ ews_settings_finalize (GObject *object) + g_free (priv->oauth2_tenant); + g_free (priv->oauth2_client_id); + g_free (priv->oauth2_redirect_uri); ++ g_free (priv->oauth2_resource_uri); ++ g_free (priv->oauth2_endpoint_host); + + /* Chain up to parent's finalize() method. */ + G_OBJECT_CLASS (camel_ews_settings_parent_class)->finalize (object); +@@ -740,6 +772,30 @@ camel_ews_settings_class_init (CamelEwsS + G_PARAM_READWRITE | + G_PARAM_CONSTRUCT | + G_PARAM_STATIC_STRINGS)); ++ ++ g_object_class_install_property ( ++ object_class, ++ PROP_OAUTH2_RESOURCE_URI, ++ g_param_spec_string ( ++ "oauth2-resource-uri", ++ "OAuth2 Resource URI", ++ "OAuth2 Resource URI to use, only if override-oauth2 is TRUE, otherwise the compile-time value is used", ++ NULL, ++ G_PARAM_READWRITE | ++ G_PARAM_CONSTRUCT | ++ G_PARAM_STATIC_STRINGS)); ++ ++ g_object_class_install_property ( ++ object_class, ++ PROP_OAUTH2_ENDPOINT_HOST, ++ g_param_spec_string ( ++ "oauth2-endpoint-host", ++ "OAuth2 Endpoint Host", ++ "OAuth2 endpoint host to use, only if override-oauth2 is TRUE, otherwise the compile-time value is used", ++ NULL, ++ G_PARAM_READWRITE | ++ G_PARAM_CONSTRUCT | ++ G_PARAM_STATIC_STRINGS)); + } + + static void +@@ -1543,3 +1599,97 @@ camel_ews_settings_set_oauth2_redirect_u + + g_object_notify (G_OBJECT (settings), "oauth2-redirect-uri"); + } ++ ++const gchar * ++camel_ews_settings_get_oauth2_resource_uri (CamelEwsSettings *settings) ++{ ++ g_return_val_if_fail (CAMEL_IS_EWS_SETTINGS (settings), NULL); ++ ++ return settings->priv->oauth2_resource_uri; ++} ++ ++gchar * ++camel_ews_settings_dup_oauth2_resource_uri (CamelEwsSettings *settings) ++{ ++ const gchar *protected; ++ gchar *duplicate; ++ ++ g_return_val_if_fail (CAMEL_IS_EWS_SETTINGS (settings), NULL); ++ ++ g_mutex_lock (&settings->priv->property_lock); ++ ++ protected = camel_ews_settings_get_oauth2_resource_uri (settings); ++ duplicate = g_strdup (protected); ++ ++ g_mutex_unlock (&settings->priv->property_lock); ++ ++ return duplicate; ++} ++ ++void ++camel_ews_settings_set_oauth2_resource_uri (CamelEwsSettings *settings, ++ const gchar *resource_uri) ++{ ++ g_return_if_fail (CAMEL_IS_EWS_SETTINGS (settings)); ++ ++ g_mutex_lock (&settings->priv->property_lock); ++ ++ if (g_strcmp0 (settings->priv->oauth2_resource_uri, resource_uri) == 0) { ++ g_mutex_unlock (&settings->priv->property_lock); ++ return; ++ } ++ ++ g_free (settings->priv->oauth2_resource_uri); ++ settings->priv->oauth2_resource_uri = e_util_strdup_strip (resource_uri); ++ ++ g_mutex_unlock (&settings->priv->property_lock); ++ ++ g_object_notify (G_OBJECT (settings), "oauth2-resource-uri"); ++} ++ ++const gchar * ++camel_ews_settings_get_oauth2_endpoint_host (CamelEwsSettings *settings) ++{ ++ g_return_val_if_fail (CAMEL_IS_EWS_SETTINGS (settings), NULL); ++ ++ return settings->priv->oauth2_endpoint_host; ++} ++ ++gchar * ++camel_ews_settings_dup_oauth2_endpoint_host (CamelEwsSettings *settings) ++{ ++ const gchar *protected; ++ gchar *duplicate; ++ ++ g_return_val_if_fail (CAMEL_IS_EWS_SETTINGS (settings), NULL); ++ ++ g_mutex_lock (&settings->priv->property_lock); ++ ++ protected = camel_ews_settings_get_oauth2_endpoint_host (settings); ++ duplicate = g_strdup (protected); ++ ++ g_mutex_unlock (&settings->priv->property_lock); ++ ++ return duplicate; ++} ++ ++void ++camel_ews_settings_set_oauth2_endpoint_host (CamelEwsSettings *settings, ++ const gchar *endpoint_host) ++{ ++ g_return_if_fail (CAMEL_IS_EWS_SETTINGS (settings)); ++ ++ g_mutex_lock (&settings->priv->property_lock); ++ ++ if (g_strcmp0 (settings->priv->oauth2_endpoint_host, endpoint_host) == 0) { ++ g_mutex_unlock (&settings->priv->property_lock); ++ return; ++ } ++ ++ g_free (settings->priv->oauth2_endpoint_host); ++ settings->priv->oauth2_endpoint_host = e_util_strdup_strip (endpoint_host); ++ ++ g_mutex_unlock (&settings->priv->property_lock); ++ ++ g_object_notify (G_OBJECT (settings), "oauth2-endpoint-host"); ++} +diff -up evolution-ews-3.28.5/src/server/camel-ews-settings.h.oauth2-endpoints evolution-ews-3.28.5/src/server/camel-ews-settings.h +--- evolution-ews-3.28.5/src/server/camel-ews-settings.h.oauth2-endpoints 2018-07-30 16:01:00.000000000 +0200 ++++ evolution-ews-3.28.5/src/server/camel-ews-settings.h 2020-10-07 17:08:15.763562790 +0200 +@@ -167,6 +167,20 @@ gchar * camel_ews_settings_dup_oauth2_r + void camel_ews_settings_set_oauth2_redirect_uri + (CamelEwsSettings *settings, + const gchar *redirect_uri); ++const gchar * camel_ews_settings_get_oauth2_resource_uri ++ (CamelEwsSettings *settings); ++gchar * camel_ews_settings_dup_oauth2_resource_uri ++ (CamelEwsSettings *settings); ++void camel_ews_settings_set_oauth2_resource_uri ++ (CamelEwsSettings *settings, ++ const gchar *resource_uri); ++const gchar * camel_ews_settings_get_oauth2_endpoint_host ++ (CamelEwsSettings *settings); ++gchar * camel_ews_settings_dup_oauth2_endpoint_host ++ (CamelEwsSettings *settings); ++void camel_ews_settings_set_oauth2_endpoint_host ++ (CamelEwsSettings *settings, ++ const gchar *endpoint_host); + + G_END_DECLS + +diff -up evolution-ews-3.28.5/src/server/e-ews-connection.c.oauth2-endpoints evolution-ews-3.28.5/src/server/e-ews-connection.c +--- evolution-ews-3.28.5/src/server/e-ews-connection.c.oauth2-endpoints 2020-10-07 17:08:43.161554484 +0200 ++++ evolution-ews-3.28.5/src/server/e-ews-connection.c 2020-10-07 17:09:20.338543223 +0200 +@@ -9914,13 +9914,7 @@ ews_connection_gather_auth_methods_cb (S + + if (!has_bearer) { + /* Special-case Office365 OAuth2, because outlook.office365.com doesn't advertise Bearer */ +- SoupURI *suri; +- +- suri = soup_message_get_uri (message); +- if (suri && soup_uri_get_host (suri) && +- g_ascii_strcasecmp (soup_uri_get_host (suri), "outlook.office365.com") == 0) { +- async_data->items = g_slist_prepend (async_data->items, g_strdup ("Bearer")); +- } ++ async_data->items = g_slist_prepend (async_data->items, g_strdup ("Bearer")); + } + + g_object_set_data (G_OBJECT (simple), EWS_OBJECT_KEY_AUTHS_GATHERED, GINT_TO_POINTER (1)); +diff -up evolution-ews-3.28.5/src/server/e-oauth2-service-office365.c.oauth2-endpoints evolution-ews-3.28.5/src/server/e-oauth2-service-office365.c +--- evolution-ews-3.28.5/src/server/e-oauth2-service-office365.c.oauth2-endpoints 2020-10-07 17:08:15.761562791 +0200 ++++ evolution-ews-3.28.5/src/server/e-oauth2-service-office365.c 2020-10-07 17:09:30.526540133 +0200 +@@ -28,8 +28,6 @@ + https://tsmatz.wordpress.com/2016/10/07/application-permission-with-v2-endpoint-and-microsoft-graph/ + */ + +-#define OFFICE365_RESOURCE "https://outlook.office.com" +- + struct _EOAuth2ServiceOffice365Private + { + GMutex string_cache_lock; +@@ -53,8 +51,10 @@ eos_office365_cache_string (EOAuth2Servi + if (!str) + return NULL; + +- if (!*str) ++ if (!*str) { ++ g_free (str); + return ""; ++ } + + g_mutex_lock (&oauth2_office365->priv->string_cache_lock); + +@@ -71,6 +71,24 @@ eos_office365_cache_string (EOAuth2Servi + return cached_str; + } + ++static const gchar * ++eos_office365_get_endpoint_host (EOAuth2ServiceOffice365 *oauth2_office365, ++ CamelEwsSettings *ews_settings) ++{ ++ if (ews_settings && camel_ews_settings_get_override_oauth2 (ews_settings)) { ++ gchar *endpoint_host; ++ ++ endpoint_host = camel_ews_settings_dup_oauth2_endpoint_host (ews_settings); ++ ++ if (endpoint_host && *endpoint_host) ++ return eos_office365_cache_string (oauth2_office365, endpoint_host); ++ ++ g_free (endpoint_host); ++ } ++ ++ return OFFICE365_ENDPOINT_HOST; ++} ++ + static CamelEwsSettings * + eos_office365_get_camel_settings (ESource *source) + { +@@ -92,8 +110,7 @@ eos_office365_guess_can_process (EOAuth2 + const gchar *hostname) + { + return e_oauth2_services_is_supported () && +- protocol && g_ascii_strcasecmp (protocol, "ews") == 0 && +- hostname && e_util_utf8_strstrcase (hostname, "outlook.office365.com"); ++ protocol && g_ascii_strcasecmp (protocol, "ews") == 0; + } + + static const gchar * +@@ -158,7 +175,8 @@ eos_office365_get_authentication_uri (EO + } + + res = eos_office365_cache_string (oauth2_office365, +- g_strdup_printf ("https://login.microsoftonline.com/%s/oauth2/authorize", ++ g_strdup_printf ("https://%s/%s/oauth2/authorize", ++ eos_office365_get_endpoint_host (oauth2_office365, ews_settings), + tenant ? tenant : OFFICE365_TENANT)); + + g_free (tenant); +@@ -166,7 +184,10 @@ eos_office365_get_authentication_uri (EO + return res; + } + +- return "https://login.microsoftonline.com/" OFFICE365_TENANT "/oauth2/authorize"; ++ return eos_office365_cache_string (oauth2_office365, ++ g_strdup_printf ("https://%s/%s/oauth2/authorize", ++ eos_office365_get_endpoint_host (oauth2_office365, ews_settings), ++ OFFICE365_TENANT)); + } + + static const gchar * +@@ -188,7 +209,8 @@ eos_office365_get_refresh_uri (EOAuth2Se + } + + res = eos_office365_cache_string (oauth2_office365, +- g_strdup_printf ("https://login.microsoftonline.com/%s/oauth2/token", ++ g_strdup_printf ("https://%s/%s/oauth2/token", ++ eos_office365_get_endpoint_host (oauth2_office365, ews_settings), + tenant ? tenant : OFFICE365_TENANT)); + + g_free (tenant); +@@ -196,7 +218,10 @@ eos_office365_get_refresh_uri (EOAuth2Se + return res; + } + +- return "https://login.microsoftonline.com/" OFFICE365_TENANT "/oauth2/token"; ++ return eos_office365_cache_string (oauth2_office365, ++ g_strdup_printf ("https://%s/%s/oauth2/token", ++ eos_office365_get_endpoint_host (oauth2_office365, ews_settings), ++ OFFICE365_TENANT)); + } + + static const gchar * +@@ -220,13 +245,67 @@ eos_office365_get_redirect_uri (EOAuth2S + + if (redirect_uri) + return eos_office365_cache_string (oauth2_office365, redirect_uri); ++ ++ if (e_ews_util_strcmp0 (camel_ews_settings_get_oauth2_endpoint_host (ews_settings), NULL) != 0) { ++ return eos_office365_cache_string (oauth2_office365, ++ g_strdup_printf ("https://%s/common/oauth2/nativeclient", ++ eos_office365_get_endpoint_host (oauth2_office365, ews_settings))); ++ } + } + + res = OFFICE365_REDIRECT_URI; + if (res && *res) + return res; + +- return "https://login.microsoftonline.com/common/oauth2/nativeclient"; ++ return eos_office365_cache_string (oauth2_office365, ++ g_strdup_printf ("https://%s/common/oauth2/nativeclient", ++ eos_office365_get_endpoint_host (oauth2_office365, ews_settings))); ++} ++ ++static const gchar * ++eos_office365_get_resource_uri (EOAuth2Service *service, ++ ESource *source) ++{ ++ EOAuth2ServiceOffice365 *oauth2_office365 = E_OAUTH2_SERVICE_OFFICE365 (service); ++ CamelEwsSettings *ews_settings; ++ ++ ews_settings = eos_office365_get_camel_settings (source); ++ if (ews_settings && camel_ews_settings_get_override_oauth2 (ews_settings)) { ++ gchar *resource_uri; ++ ++ resource_uri = camel_ews_settings_dup_oauth2_resource_uri (ews_settings); ++ ++ if (resource_uri && !*resource_uri) { ++ g_free (resource_uri); ++ resource_uri = NULL; ++ } ++ ++ if (resource_uri) ++ return eos_office365_cache_string (oauth2_office365, resource_uri); ++ } ++ ++ if (ews_settings) { ++ gchar *host_url; ++ ++ host_url = camel_ews_settings_dup_hosturl (ews_settings); ++ ++ if (host_url && *host_url) { ++ gchar *ptr; ++ ++ ptr = strstr (host_url, "://"); ++ ptr = ptr ? strchr (ptr + 3, '/') : NULL; ++ ++ if (ptr) { ++ *ptr = '\0'; ++ ++ return eos_office365_cache_string (oauth2_office365, host_url); ++ } ++ } ++ ++ g_free (host_url); ++ } ++ ++ return OFFICE365_FALLBACK_RESOURCE_URI; + } + + static void +@@ -238,7 +317,7 @@ eos_office365_prepare_authentication_uri + + e_oauth2_service_util_set_to_form (uri_query, "response_mode", "query"); + e_oauth2_service_util_set_to_form (uri_query, "prompt", "login"); +- e_oauth2_service_util_set_to_form (uri_query, "resource", OFFICE365_RESOURCE); ++ e_oauth2_service_util_set_to_form (uri_query, "resource", eos_office365_get_resource_uri (service, source)); + } + + static gboolean +@@ -305,7 +384,7 @@ eos_office365_prepare_refresh_token_form + { + g_return_if_fail (form != NULL); + +- e_oauth2_service_util_set_to_form (form, "resource", OFFICE365_RESOURCE); ++ e_oauth2_service_util_set_to_form (form, "resource", eos_office365_get_resource_uri (service, source)); + e_oauth2_service_util_set_to_form (form, "redirect_uri", e_oauth2_service_get_redirect_uri (service, source)); + } + +@@ -369,3 +448,16 @@ e_oauth2_service_office365_type_register + { + e_oauth2_service_office365_register_type (type_module); + } ++ ++gint ++e_ews_util_strcmp0 (const gchar *str1, ++ const gchar *str2) ++{ ++ if (str1 && !*str1) ++ str1 = NULL; ++ ++ if (str2 && !*str2) ++ str2 = NULL; ++ ++ return g_strcmp0 (str1, str2); ++} +diff -up evolution-ews-3.28.5/src/server/e-oauth2-service-office365.h.oauth2-endpoints evolution-ews-3.28.5/src/server/e-oauth2-service-office365.h +--- evolution-ews-3.28.5/src/server/e-oauth2-service-office365.h.oauth2-endpoints 2018-07-30 16:01:00.000000000 +0200 ++++ evolution-ews-3.28.5/src/server/e-oauth2-service-office365.h 2020-10-07 17:08:15.763562790 +0200 +@@ -39,6 +39,8 @@ + (G_TYPE_INSTANCE_GET_CLASS \ + ((obj), E_TYPE_OAUTH2_SERVICE_OFFICE365, EOAuth2ServiceOffice365Class)) + ++#define OFFICE365_FALLBACK_RESOURCE_URI "https://outlook.office365.com" ++ + G_BEGIN_DECLS + + typedef struct _EOAuth2ServiceOffice365 EOAuth2ServiceOffice365; +@@ -59,6 +61,9 @@ GType e_oauth2_service_office365_get_ty + void e_oauth2_service_office365_type_register + (GTypeModule *type_module); + ++gint e_ews_util_strcmp0 (const gchar *str1, ++ const gchar *str2); ++ + G_END_DECLS + + #endif /* E_OAUTH2_SERVICE_OFFICE365_H */ diff --git a/SOURCES/evolution-ews-3.28.5-oauth2-simplify.patch b/SOURCES/evolution-ews-3.28.5-oauth2-simplify.patch new file mode 100644 index 0000000..9505600 --- /dev/null +++ b/SOURCES/evolution-ews-3.28.5-oauth2-simplify.patch @@ -0,0 +1,119 @@ +From bf0f75de095dc7ecb7171ab61009fab2631b4bee Mon Sep 17 00:00:00 2001 +From: Luca Boccassi +Date: Tue, 2 Apr 2019 16:06:35 +0200 +Subject: [PATCH] M!1 - Simplify OAuth2 for outlook.office365.com server + +Two changes: +1) preset default tenant to "common" +2) do not pass 'scope' parameter in the requests, which is optional + and can cause errors like: + error:invalid_request description:AADSTS65002: + Consent between first party applications and resources must be + configured via preauthorization. + +Closes https://gitlab.gnome.org/GNOME/evolution-ews/merge_requests/1 +--- + CMakeLists.txt | 2 +- + src/configuration/e-mail-config-ews-backend.c | 15 +++------------ + src/server/e-oauth2-service-office365.c | 17 ----------------- + 3 files changed, 4 insertions(+), 30 deletions(-) + +diff --git a/CMakeLists.txt b/CMakeLists.txt +index e8ea9875..dbc58832 100644 +--- a/CMakeLists.txt ++++ b/CMakeLists.txt +@@ -180,7 +180,7 @@ endif(ENABLE_TESTS) + add_printable_variable(WITH_OFFICE365_TENANT "Office365.com OAuth 2.0 tenant" "") + + if(WITH_OFFICE365_TENANT STREQUAL "") +- set(WITH_OFFICE365_TENANT "") ++ set(WITH_OFFICE365_TENANT "common") + endif(WITH_OFFICE365_TENANT STREQUAL "") + + add_printable_variable(WITH_OFFICE365_CLIENT_ID "Office365.com OAuth 2.0 client ID" "") +diff --git a/src/configuration/e-mail-config-ews-backend.c b/src/configuration/e-mail-config-ews-backend.c +index fbf341b2..e62d3922 100644 +--- a/src/configuration/e-mail-config-ews-backend.c ++++ b/src/configuration/e-mail-config-ews-backend.c +@@ -348,9 +348,9 @@ mail_config_ews_backend_insert_widgets (EMailConfigServiceBackend *backend, + G_BINDING_SYNC_CREATE); + + mail_config_ews_backend_set_oauth2_tooltip (widget, OFFICE365_TENANT, +- /* Translators: 'Tenant' here means a term used by Microsoft to identify a company or organization in an Office 365 world. ++ /* Translators: 'Tenant' here means a term used by Microsoft to identify a company or organization in an Office 365 world. Same for 'common', it's a default URL path. + You probably do not want to translate it. More for example here: https://powerbi.microsoft.com/en-us/blog/what-is-a-tenant/ */ +- _("There is not set any default tenant"), ++ _("Default tenant is “common“"), + /* Translators: 'Tenant' here means a term used by Microsoft to identify a company or organization in an Office 365 world. + You probably do not want to translate it. More for example here: https://powerbi.microsoft.com/en-us/blog/what-is-a-tenant/ */ + g_strdup_printf (_("Default tenant is “%s”"), OFFICE365_TENANT)); +@@ -586,23 +586,14 @@ mail_config_ews_backend_check_complete (EMailConfigServiceBackend *backend) + e_util_set_entry_issue_hint (priv->user_entry, correct ? NULL : _("User name cannot be empty")); + + if (correct && camel_ews_settings_get_auth_mechanism (ews_settings) == EWS_AUTH_TYPE_OAUTH2) { +- const gchar *tenant, *client_id; ++ const gchar *client_id; + + if (camel_ews_settings_get_override_oauth2 (ews_settings)) { +- tenant = camel_ews_settings_get_oauth2_tenant (ews_settings); + client_id = camel_ews_settings_get_oauth2_client_id (ews_settings); + } else { +- tenant = OFFICE365_TENANT; + client_id = OFFICE365_CLIENT_ID; + } + +- correct = tenant && *tenant; +- complete = complete && correct; +- +- /* Translators: 'Tenant' here means a term used by Microsoft to identify a company or organization in an Office 365 world. +- You probably do not want to translate it. More for example here: https://powerbi.microsoft.com/en-us/blog/what-is-a-tenant/ */ +- e_util_set_entry_issue_hint (priv->oauth2_tenant_entry, correct ? NULL : _("Tenant cannot be empty")); +- + correct = client_id && *client_id; + complete = complete && correct; + +diff --git a/src/server/e-oauth2-service-office365.c b/src/server/e-oauth2-service-office365.c +index 4be84f6f..d660458c 100644 +--- a/src/server/e-oauth2-service-office365.c ++++ b/src/server/e-oauth2-service-office365.c +@@ -30,21 +30,6 @@ + + #define OFFICE365_RESOURCE "https://outlook.office.com" + +-#define OFFICE365_SCOPE "openid offline_access profile " \ +- "Mail.ReadWrite " \ +- "Mail.ReadWrite.Shared " \ +- "Mail.Send " \ +- "Mail.Send.Shared " \ +- "Calendars.ReadWrite " \ +- "Calendars.ReadWrite.Shared " \ +- "Contacts.ReadWrite " \ +- "Contacts.ReadWrite.Shared " \ +- "Tasks.ReadWrite " \ +- "Tasks.ReadWrite.Shared " \ +- "MailboxSettings.ReadWrite " \ +- "People.Read " \ +- "User.ReadBasic.All" +- + struct _EOAuth2ServiceOffice365Private + { + GMutex string_cache_lock; +@@ -253,7 +238,6 @@ eos_office365_prepare_authentication_uri_query (EOAuth2Service *service, + + e_oauth2_service_util_set_to_form (uri_query, "response_mode", "query"); + e_oauth2_service_util_set_to_form (uri_query, "prompt", "login"); +- e_oauth2_service_util_set_to_form (uri_query, "scope", OFFICE365_SCOPE); + e_oauth2_service_util_set_to_form (uri_query, "resource", OFFICE365_RESOURCE); + } + +@@ -321,7 +305,6 @@ eos_office365_prepare_refresh_token_form (EOAuth2Service *service, + { + g_return_if_fail (form != NULL); + +- e_oauth2_service_util_set_to_form (form, "scope", OFFICE365_SCOPE); + e_oauth2_service_util_set_to_form (form, "resource", OFFICE365_RESOURCE); + e_oauth2_service_util_set_to_form (form, "redirect_uri", e_oauth2_service_get_redirect_uri (service, source)); + } +-- +GitLab + diff --git a/SOURCES/evolution-ews-3.28.5-save-only-if-organizer.patch b/SOURCES/evolution-ews-3.28.5-save-only-if-organizer.patch new file mode 100644 index 0000000..5085cff --- /dev/null +++ b/SOURCES/evolution-ews-3.28.5-save-only-if-organizer.patch @@ -0,0 +1,75 @@ +diff -up evolution-ews-3.28.5/src/calendar/e-cal-backend-ews.c.save-only-if-organizer evolution-ews-3.28.5/src/calendar/e-cal-backend-ews.c +--- evolution-ews-3.28.5/src/calendar/e-cal-backend-ews.c.save-only-if-organizer 2019-11-13 07:44:37.295278517 +0100 ++++ evolution-ews-3.28.5/src/calendar/e-cal-backend-ews.c 2019-11-13 07:55:27.656269523 +0100 +@@ -147,6 +147,41 @@ ecb_ews_get_collection_settings (ECalBac + return CAMEL_EWS_SETTINGS (settings); + } + ++static GHashTable * ++ecb_ews_get_mail_aliases (ECalBackendEws *cbews) ++{ ++ ESource *source; ++ ESourceRegistry *registry; ++ GHashTable *aliases = NULL; ++ GList *identities, *link; ++ const gchar *parent_uid; ++ ++ source = e_backend_get_source (E_BACKEND (cbews)); ++ parent_uid = e_source_get_parent (source); ++ ++ if (!parent_uid || !*parent_uid) ++ return NULL; ++ ++ registry = e_cal_backend_get_registry (E_CAL_BACKEND (cbews)); ++ identities = e_source_registry_list_enabled (registry, E_SOURCE_EXTENSION_MAIL_IDENTITY); ++ ++ for (link = identities; link; link = g_list_next (link)) { ++ ESource *mail_identity = link->data; ++ ++ if (g_strcmp0 (parent_uid, e_source_get_parent (mail_identity)) == 0) { ++ ESourceMailIdentity *extension; ++ ++ extension = e_source_get_extension (mail_identity, E_SOURCE_EXTENSION_MAIL_IDENTITY); ++ aliases = e_source_mail_identity_get_aliases_as_hash_table (extension); ++ break; ++ } ++ } ++ ++ g_list_free_full (identities, g_object_unref); ++ ++ return aliases; ++} ++ + static void + ecb_ews_convert_error_to_edc_error (GError **perror) + { +@@ -1350,6 +1385,18 @@ ecb_ews_is_organizer (ECalBackendEws *cb + is_organizer = user_email && g_ascii_strcasecmp (email, user_email) == 0; + + g_free (user_email); ++ ++ if (!is_organizer) { ++ GHashTable *aliases; ++ ++ aliases = ecb_ews_get_mail_aliases (cbews); ++ ++ if (aliases) { ++ is_organizer = g_hash_table_contains (aliases, email); ++ ++ g_hash_table_unref (aliases); ++ } ++ } + } + + return is_organizer; +@@ -2595,6 +2642,10 @@ ecb_ews_save_component_sync (ECalMetaBac + g_slist_free_full (existing, g_object_unref); + g_slist_free_full (changed_instances, change_data_free); + g_slist_free_full (removed_instances, g_object_unref); ++ } else if (e_cal_component_has_organizer (master) && ++ !ecb_ews_is_organizer (cbews, master)) { ++ success = FALSE; ++ g_propagate_error (error, EDC_ERROR_EX (PermissionDenied, _("Cannot create meetings organized by other users in an Exchange Web Services calendar."))); + } else { + GHashTable *removed_indexes; + EwsCalendarConvertData convert_data = { 0 }; diff --git a/SOURCES/evolution-ews-3.28.5-sync-category-list.patch b/SOURCES/evolution-ews-3.28.5-sync-category-list.patch new file mode 100644 index 0000000..12cc76f --- /dev/null +++ b/SOURCES/evolution-ews-3.28.5-sync-category-list.patch @@ -0,0 +1,1168 @@ +diff -up evolution-ews-3.28.5/src/camel/camel-ews-store.c.sync-category-list evolution-ews-3.28.5/src/camel/camel-ews-store.c +--- evolution-ews-3.28.5/src/camel/camel-ews-store.c.sync-category-list 2019-10-24 09:39:08.336278207 +0200 ++++ evolution-ews-3.28.5/src/camel/camel-ews-store.c 2019-10-24 09:39:08.341278207 +0200 +@@ -673,6 +673,43 @@ ews_update_has_ooo_set (CamelSession *se + g_clear_object (&oof_settings); + } + ++static void ++ews_exchange_server_categories_cb (CamelSession *session, ++ GCancellable *cancellable, ++ gpointer user_data, ++ GError **error) ++{ ++ CamelEwsStore *ews_store = user_data; ++ EEwsConnection *cnc; ++ EwsFolderId fid = { 0 }; ++ gchar *properties = NULL; ++ GError *local_error = NULL; ++ ++ cnc = camel_ews_store_ref_connection (ews_store); ++ if (!cnc) ++ return; ++ ++ fid.id = (gchar *) "calendar"; ++ fid.is_distinguished_id = TRUE; ++ ++ if (e_ews_connection_get_user_configuration_sync (cnc, G_PRIORITY_DEFAULT, &fid, "CategoryList", ++ E_EWS_USER_CONFIGURATION_PROPERTIES_XMLDATA, &properties, cancellable, &local_error) && properties) { ++ guchar *data; ++ gsize data_len = 0; ++ ++ data = g_base64_decode (properties, &data_len); ++ ++ if (data && data_len > 0) ++ camel_ews_utils_merge_category_list (ews_store, data, data_len); ++ ++ g_free (data); ++ } ++ ++ g_clear_error (&local_error); ++ g_clear_object (&cnc); ++ g_free (properties); ++} ++ + struct ScheduleUpdateData + { + GCancellable *cancellable; +@@ -1252,6 +1289,12 @@ ews_connect_sync (CamelService *service, + g_object_ref (ews_store), + g_object_unref); + ++ camel_session_submit_job ( ++ session, _("Look up Exchange server categories"), ++ ews_exchange_server_categories_cb, ++ g_object_ref (ews_store), ++ g_object_unref); ++ + if (!priv->updates_cancellable) + priv->updates_cancellable = g_cancellable_new (); + +@@ -2377,6 +2420,17 @@ ews_get_folder_info_sync (CamelStore *st + ews_store = (CamelEwsStore *) store; + priv = ews_store->priv; + ++ if ((flags & CAMEL_STORE_FOLDER_INFO_REFRESH) != 0 && ++ camel_offline_store_get_online (CAMEL_OFFLINE_STORE (ews_store))) { ++ CamelSession *session; ++ ++ session = camel_service_ref_session (CAMEL_SERVICE (ews_store)); ++ if (session) { ++ ews_exchange_server_categories_cb (session, cancellable, ews_store, NULL); ++ g_object_unref (session); ++ } ++ } ++ + if ((flags & CAMEL_STORE_FOLDER_INFO_SUBSCRIPTION_LIST) != 0) { + gboolean includes_last_folder = TRUE; + GSList *folders = NULL, *to_check = NULL; +diff -up evolution-ews-3.28.5/src/camel/camel-ews-store-summary.c.sync-category-list evolution-ews-3.28.5/src/camel/camel-ews-store-summary.c +--- evolution-ews-3.28.5/src/camel/camel-ews-store-summary.c.sync-category-list 2018-07-30 16:01:00.000000000 +0200 ++++ evolution-ews-3.28.5/src/camel/camel-ews-store-summary.c 2019-10-24 09:39:08.341278207 +0200 +@@ -31,6 +31,7 @@ + #define S_UNLOCK(x) (g_rec_mutex_unlock(&(x)->priv->s_lock)) + + #define STORE_GROUP_NAME "##storepriv" ++#define CATEGORIES_KEY "Categories" + #define CURRENT_SUMMARY_VERSION 3 + + struct _CamelEwsStoreSummaryPrivate { +@@ -1047,3 +1048,159 @@ camel_ews_store_summary_has_folder (Came + + return ret; + } ++ ++static gchar * ++camel_ews_category_to_string (const CamelEwsCategory *cat) ++{ ++ gchar *guid, *name, *color_def = NULL, *str; ++ ++ g_return_val_if_fail (cat != NULL, NULL); ++ ++ guid = g_uri_escape_string (cat->guid, NULL, TRUE); ++ name = g_uri_escape_string (cat->name, NULL, TRUE); ++ ++ if (cat->color_def) ++ color_def = g_uri_escape_string (cat->color_def, NULL, TRUE); ++ ++ str = g_strconcat ( ++ guid ? guid : "", "\t", ++ name ? name : "", "\t", ++ color_def ? color_def : "", ++ NULL); ++ ++ g_free (guid); ++ g_free (name); ++ g_free (color_def); ++ ++ return str; ++} ++ ++static CamelEwsCategory * ++camel_ews_category_from_string (const gchar *str) ++{ ++ CamelEwsCategory *cat; ++ gchar **strv, *guid, *name, *color_def; ++ ++ g_return_val_if_fail (str != NULL, NULL); ++ ++ strv = g_strsplit (str, "\t", -1); ++ if (!strv || !strv[0] || !strv[1]) { ++ g_strfreev (strv); ++ return NULL; ++ } ++ ++ guid = g_uri_unescape_string (strv[0], NULL); ++ name = g_uri_unescape_string (strv[1], NULL); ++ color_def = (strv[2] && strv[2][0]) ? g_uri_unescape_string (strv[2], NULL) : NULL; ++ ++ cat = camel_ews_category_new (guid, name, color_def); ++ ++ g_free (guid); ++ g_free (name); ++ g_free (color_def); ++ g_strfreev (strv); ++ ++ return cat; ++} ++ ++GHashTable * /* gchar *guid ~> CamelEwsCategory * */ ++camel_ews_store_summary_get_categories (CamelEwsStoreSummary *ews_summary) ++{ ++ GHashTable *categories; ++ gchar **strv; ++ g_return_val_if_fail (CAMEL_IS_EWS_STORE_SUMMARY (ews_summary), NULL); ++ ++ S_LOCK (ews_summary); ++ ++ strv = g_key_file_get_string_list (ews_summary->priv->key_file, STORE_GROUP_NAME, CATEGORIES_KEY, NULL, NULL); ++ ++ S_UNLOCK (ews_summary); ++ ++ categories = g_hash_table_new_full (g_str_hash, g_str_equal, NULL, camel_ews_category_free); ++ ++ if (strv) { ++ gint ii; ++ ++ for (ii = 0; strv[ii]; ii++) { ++ CamelEwsCategory *cat; ++ ++ cat = camel_ews_category_from_string (strv[ii]); ++ if (cat) ++ g_hash_table_insert (categories, cat->guid, cat); ++ } ++ ++ g_strfreev (strv); ++ } ++ ++ return categories; ++} ++ ++void ++camel_ews_store_summary_set_categories (CamelEwsStoreSummary *ews_summary, ++ GHashTable *categories) /* gchar *guid ~> CamelEwsCategory * */ ++{ ++ GPtrArray *array; ++ GHashTableIter iter; ++ gpointer value; ++ ++ g_return_if_fail (CAMEL_IS_EWS_STORE_SUMMARY (ews_summary)); ++ g_return_if_fail (categories != NULL); ++ ++ array = g_ptr_array_new_full (g_hash_table_size (categories), g_free); ++ ++ g_hash_table_iter_init (&iter, categories); ++ while (g_hash_table_iter_next (&iter, NULL, &value)) { ++ CamelEwsCategory *cat = value; ++ ++ if (cat) { ++ gchar *str; ++ ++ str = camel_ews_category_to_string (cat); ++ ++ if (str) ++ g_ptr_array_add (array, str); ++ } ++ } ++ ++ S_LOCK (ews_summary); ++ ++ g_key_file_set_string_list (ews_summary->priv->key_file, STORE_GROUP_NAME, CATEGORIES_KEY, ++ (const gchar * const *) array->pdata, array->len); ++ ++ ews_summary->priv->dirty = TRUE; ++ ++ S_UNLOCK (ews_summary); ++ ++ g_ptr_array_free (array, TRUE); ++} ++ ++CamelEwsCategory * ++camel_ews_category_new (const gchar *guid, ++ const gchar *name, ++ const gchar *color_def) ++{ ++ CamelEwsCategory *cat; ++ ++ g_return_val_if_fail (guid != NULL, NULL); ++ g_return_val_if_fail (name != NULL, NULL); ++ ++ cat = g_new0 (CamelEwsCategory, 1); ++ cat->guid = g_strdup (guid); ++ cat->name = g_strdup (name); ++ cat->color_def = g_strdup (color_def); ++ ++ return cat; ++} ++ ++void ++camel_ews_category_free (gpointer ptr) ++{ ++ CamelEwsCategory *cat = ptr; ++ ++ if (cat) { ++ g_free (cat->guid); ++ g_free (cat->name); ++ g_free (cat->color_def); ++ g_free (cat); ++ } ++} +diff -up evolution-ews-3.28.5/src/camel/camel-ews-store-summary.h.sync-category-list evolution-ews-3.28.5/src/camel/camel-ews-store-summary.h +--- evolution-ews-3.28.5/src/camel/camel-ews-store-summary.h.sync-category-list 2018-07-30 16:01:00.000000000 +0200 ++++ evolution-ews-3.28.5/src/camel/camel-ews-store-summary.h 2019-10-24 09:39:08.341278207 +0200 +@@ -50,6 +50,12 @@ + + G_BEGIN_DECLS + ++typedef struct _CamelEwsCategory { ++ gchar *guid; ++ gchar *name; ++ gchar *color_def; ++} CamelEwsCategory; ++ + typedef struct _CamelEwsStoreSummary CamelEwsStoreSummary; + typedef struct _CamelEwsStoreSummaryClass CamelEwsStoreSummaryClass; + typedef struct _CamelEwsStoreSummaryPrivate CamelEwsStoreSummaryPrivate; +@@ -215,6 +221,17 @@ gchar * camel_ews_store_summary_get_fol + gboolean camel_ews_store_summary_has_folder + (CamelEwsStoreSummary *ews_summary, + const gchar *id); ++GHashTable * camel_ews_store_summary_get_categories /* gchar *guid ~> CamelEwsCategory * */ ++ (CamelEwsStoreSummary *ews_summary); ++void camel_ews_store_summary_set_categories ++ (CamelEwsStoreSummary *ews_summary, ++ GHashTable *categories); /* gchar *guid ~> CamelEwsCategory * */ ++ ++CamelEwsCategory * ++ camel_ews_category_new (const gchar *guid, ++ const gchar *name, ++ const gchar *color_def); ++void camel_ews_category_free (gpointer ptr); /* CamelEwsCategory * */ + + G_END_DECLS + +diff -up evolution-ews-3.28.5/src/camel/camel-ews-utils.c.sync-category-list evolution-ews-3.28.5/src/camel/camel-ews-utils.c +--- evolution-ews-3.28.5/src/camel/camel-ews-utils.c.sync-category-list 2018-07-30 16:01:00.000000000 +0200 ++++ evolution-ews-3.28.5/src/camel/camel-ews-utils.c 2019-10-24 09:39:08.341278207 +0200 +@@ -29,6 +29,7 @@ + #include + + #include ++#include + + #include "server/camel-ews-settings.h" + #include "server/e-ews-camel-common.h" +@@ -381,6 +382,43 @@ camel_ews_utils_sync_deleted_items (Came + } + + static const gchar * ++ews_utils_outlook_color_index_to_color_def (gint color_index) ++{ ++ const gchar *colors_array[] = { ++ "#ff1a36", /* Red */ ++ "#ff8c00", /* Orange */ ++ "#f4b10b", /* Peach */ ++ "#fff100", /* Yellow */ ++ "#009e48", /* Green */ ++ "#00b294", /* Teal */ ++ "#89933f", /* Olive */ ++ "#00bcf2", /* Blue */ ++ "#8e69df", /* Purple */ ++ "#f30092", /* Maroon */ ++ "#6c7e9a", /* Steel */ ++ "#425066", /* DarkSteel */ ++ "#969696", /* Gray */ ++ "#525552", /* DarkGray */ ++ "#282828", /* Black */ ++ "#a00023", /* DarkRed */ ++ "#c45502", /* DarkOrange */ ++ "#af7000", /* DarkPeach */ ++ "#b59b02", /* DarkYellow */ ++ "#176002", /* DarkGreen */ ++ "#00725c", /* DarkTeal */ ++ "#5c6022", /* DarkOlive */ ++ "#036393", /* DarkBlue */ ++ "#422f8e", /* DarkPurple */ ++ "#960269" /* DarkMaroon */ ++ }; ++ ++ if (color_index >= 0 && color_index < G_N_ELEMENTS (colors_array)) ++ return colors_array[color_index]; ++ ++ return NULL; ++} ++ ++static const gchar * + ews_utils_rename_label (const gchar *cat, + gboolean from_cat) + { +@@ -422,6 +460,58 @@ ews_utils_is_system_user_flag (const gch + g_str_equal (name, "$has-cal"); + } + ++/* From Exchange name (which allows spaces) to evolution-name */ ++static gchar * ++camel_ews_utils_encode_category_name (const gchar *name) ++{ ++ if (name && strchr (name, ' ')) { ++ GString *str; ++ ++ str = g_string_sized_new (strlen (name) + 16); ++ ++ while (*name) { ++ if (*name == '_') ++ g_string_append_c (str, '_'); ++ ++ g_string_append_c (str, *name == ' ' ? '_' : *name); ++ ++ name++; ++ } ++ ++ return g_string_free (str, FALSE); ++ } ++ ++ return g_strdup (name); ++} ++ ++/* From evolution-name to Exchange name (which allows spaces) */ ++static gchar * ++camel_ews_utils_decode_category_name (const gchar *flag) ++{ ++ if (flag && strchr (flag, '_')) { ++ GString *str = g_string_sized_new (strlen (flag)); ++ ++ while (*flag) { ++ if (*flag == '_') { ++ if (flag[1] == '_') { ++ g_string_append_c (str, '_'); ++ flag++; ++ } else { ++ g_string_append_c (str, ' '); ++ } ++ } else { ++ g_string_append_c (str, *flag); ++ } ++ ++ flag++; ++ } ++ ++ return g_string_free (str, FALSE); ++ } ++ ++ return g_strdup (flag); ++} ++ + /* free with g_slist_free_full (flags, g_free); + the lists' members are values for the String xml element. */ + GSList * +@@ -441,6 +531,7 @@ ews_utils_gather_server_user_flags (ESoa + * array of strings */ + for (ii = 0; ii < len; ii++) { + const gchar *n = ews_utils_rename_label (camel_named_flags_get (user_flags, ii), FALSE); ++ + if (*n == '\0') + continue; + +@@ -449,26 +540,7 @@ ews_utils_gather_server_user_flags (ESoa + if (ews_utils_is_system_user_flag (n)) + continue; + +- if (strchr (n, '_')) { +- GString *str = g_string_sized_new (strlen (n)); +- +- while (*n) { +- if (*n == '_') { +- if (n[1] == '_') +- g_string_append_c (str, '_'); +- else +- g_string_append_c (str, ' '); +- } else { +- g_string_append_c (str, *n); +- } +- +- n++; +- } +- +- out_user_flags = g_slist_prepend (out_user_flags, g_string_free (str, FALSE)); +- } else { +- out_user_flags = g_slist_prepend (out_user_flags, g_strdup (n)); +- } ++ out_user_flags = g_slist_prepend (out_user_flags, camel_ews_utils_decode_category_name (n)); + } + + camel_message_info_property_unlock (mi); +@@ -512,33 +584,17 @@ ews_utils_merge_server_user_flags (EEwsI + + /* now transfer over all the categories */ + for (p = e_ews_item_get_categories (item); p; p = p->next) { +- const gchar *flag = ews_utils_rename_label (p->data, 1); +- gchar *underscored = NULL; ++ const gchar *name = ews_utils_rename_label (p->data, 1); ++ gchar *flag; + +- if (!flag || !*flag) ++ if (!name || !*name) + continue; + +- if (strchr (flag, ' ')) { +- GString *str; +- +- str = g_string_sized_new (strlen (flag) + 16); +- +- while (*flag) { +- if (*flag == '_') +- g_string_append_c (str, '_'); +- +- g_string_append_c (str, *flag == ' ' ? '_' : *flag); +- +- flag++; +- } +- +- underscored = g_string_free (str, FALSE); +- flag = underscored; +- } ++ flag = camel_ews_utils_encode_category_name (name); + + camel_message_info_set_user_flag (mi, flag, TRUE); + +- g_free (underscored); ++ g_free (flag); + } + + camel_message_info_thaw_notifications (mi); +@@ -1281,3 +1337,279 @@ camel_ews_utils_ref_corresponding_source + + return source; + } ++ ++static gboolean ++ews_util_equal_label_tag_cb (gconstpointer ptr1, ++ gconstpointer ptr2) ++{ ++ const gchar *evo_label_def = ptr1; ++ const gchar *tag = ptr2; ++ const gchar *pos; ++ ++ if (!evo_label_def || !tag || !*tag) ++ return FALSE; ++ ++ pos = g_strrstr (evo_label_def, tag); ++ ++ return pos > evo_label_def && pos[-1] == '|' && !pos[strlen (tag)]; ++} ++ ++static gboolean ++ews_utils_find_in_ptr_array (GPtrArray *haystack, ++ gconstpointer needle, ++ GEqualFunc equal_func, ++ guint *out_index) ++{ ++ guint ii; ++ ++ if (!haystack) ++ return FALSE; ++ ++ if (!equal_func) ++ equal_func = g_direct_equal; ++ ++ for (ii = 0; ii < haystack->len; ii++) { ++ if (equal_func (haystack->pdata[ii], needle)) { ++ if (out_index) ++ *out_index = ii; ++ ++ return TRUE; ++ } ++ } ++ ++ return FALSE; ++} ++ ++/* Returns whether had been done any changes */ ++static gboolean ++ews_utils_save_category_changes (GHashTable *old_categories, /* gchar *guid ~> CamelEwsCategory * */ ++ GHashTable *new_categories) /* gchar *guid ~> CamelEwsCategory * */ ++{ ++ GHashTableIter iter; ++ GSettings *settings; ++ GPtrArray *evo_labels; /* gchar * (encoded label definition) */ ++ gchar **strv; ++ gint ii; ++ gpointer value; ++ gboolean changed = FALSE; ++ ++ if (!old_categories || !new_categories) ++ return new_categories != NULL; ++ ++ evo_labels = g_ptr_array_new_full (5, g_free); ++ ++ settings = e_util_ref_settings ("org.gnome.evolution.mail"); ++ strv = g_settings_get_strv (settings, "labels"); ++ ++ for (ii = 0; strv && strv[ii]; ii++) { ++ g_ptr_array_add (evo_labels, g_strdup (strv[ii])); ++ } ++ ++ g_strfreev (strv); ++ ++ g_hash_table_iter_init (&iter, new_categories); ++ while (g_hash_table_iter_next (&iter, NULL, &value)) { ++ CamelEwsCategory *new_cat = value, *old_cat; ++ gchar *tag = NULL; ++ ++ if (!new_cat) ++ continue; ++ ++ old_cat = g_hash_table_lookup (old_categories, new_cat->guid); ++ if (old_cat) { ++ if (g_strcmp0 (old_cat->name, new_cat->name) != 0 || ++ g_strcmp0 (old_cat->color_def, new_cat->color_def) != 0) { ++ /* Old category changed name or color */ ++ tag = camel_ews_utils_encode_category_name (new_cat->name); ++ } ++ } else { ++ /* This is a new category */ ++ tag = camel_ews_utils_encode_category_name (new_cat->name); ++ } ++ ++ if (tag && *tag) { ++ guint index = (guint) -1; ++ gchar *label_def; ++ ++ changed = TRUE; ++ ++ /* Sanitize value */ ++ for (ii = 0; tag[ii]; ii++) { ++ if (tag[ii] == '|') ++ tag[ii] = '-'; ++ } ++ ++ if (old_cat && g_strcmp0 (old_cat->name, new_cat->name) != 0) { ++ gchar *old_tag = camel_ews_utils_encode_category_name (old_cat->name); ++ ++ if (old_tag && *old_tag) { ++ if (!ews_utils_find_in_ptr_array (evo_labels, old_tag, ews_util_equal_label_tag_cb, &index)) ++ index = (guint) -1; ++ } ++ ++ g_free (old_tag); ++ } ++ ++ for (ii = 0; new_cat->name[ii]; ii++) { ++ if (new_cat->name[ii] == '|') ++ new_cat->name[ii] = '-'; ++ } ++ ++ if (index == (guint) -1 && ++ !ews_utils_find_in_ptr_array (evo_labels, tag, ews_util_equal_label_tag_cb, &index)) ++ index = (guint) -1; ++ ++ label_def = g_strconcat (new_cat->name, "|", new_cat->color_def ? new_cat->color_def : "#FF0000", "|", tag, NULL); ++ ++ if (index == (guint) -1 || index >= (gint) evo_labels->len) { ++ g_ptr_array_add (evo_labels, label_def); ++ } else { ++ g_free (evo_labels->pdata[index]); ++ evo_labels->pdata[index] = label_def; ++ } ++ } ++ ++ g_hash_table_remove (old_categories, new_cat->guid); ++ ++ g_free (tag); ++ } ++ ++ if (g_hash_table_size (old_categories) > 0) { ++ /* Some categories had been removed */ ++ changed = TRUE; ++ ++ g_hash_table_iter_init (&iter, old_categories); ++ while (g_hash_table_iter_next (&iter, NULL, &value)) { ++ CamelEwsCategory *old_cat = value; ++ gchar *old_tag; ++ guint index; ++ ++ if (!old_cat) ++ continue; ++ ++ old_tag = camel_ews_utils_encode_category_name (old_cat->name); ++ ++ for (ii = 0; old_tag && old_tag[ii]; ii++) { ++ if (old_tag[ii] == '|') ++ old_tag[ii] = '-'; ++ } ++ ++ if (old_tag && ++ ews_utils_find_in_ptr_array (evo_labels, old_tag, ews_util_equal_label_tag_cb, &index)) ++ g_ptr_array_remove_index (evo_labels, index); ++ ++ g_free (old_tag); ++ } ++ } ++ ++ if (changed) { ++ /* NULL-terminated array of strings */ ++ g_ptr_array_add (evo_labels, NULL); ++ ++ g_settings_set_strv (settings, "labels", (const gchar * const *) evo_labels->pdata); ++ } ++ ++ g_ptr_array_free (evo_labels, TRUE); ++ g_object_unref (settings); ++ ++ return changed; ++} ++ ++void ++camel_ews_utils_merge_category_list (CamelEwsStore *ews_store, ++ const guchar *xml_data, ++ gsize xml_data_len) ++{ ++ xmlDocPtr doc; ++ xmlXPathContextPtr xpath_ctx; ++ ++ g_return_if_fail (CAMEL_IS_EWS_STORE (ews_store)); ++ g_return_if_fail (xml_data != NULL); ++ ++ doc = e_xml_parse_data (xml_data, xml_data_len); ++ if (!doc) ++ return; ++ ++ xpath_ctx = e_xml_new_xpath_context_with_namespaces (doc, "C", "CategoryList.xsd", NULL); ++ ++ if (xpath_ctx) { ++ xmlXPathObjectPtr xpath_obj_categories; ++ ++ xpath_obj_categories = e_xml_xpath_eval (xpath_ctx, "%s", "/C:categories/C:category"); ++ ++ if (xpath_obj_categories) { ++ GHashTable *old_categories, *new_categories; ++ gint response_index, response_length; ++ ++ new_categories = g_hash_table_new_full (g_str_hash, g_str_equal, NULL, camel_ews_category_free); ++ ++ response_length = xmlXPathNodeSetGetLength (xpath_obj_categories->nodesetval); ++ ++ for (response_index = 0; response_index < response_length; response_index++) { ++ xmlXPathObjectPtr xpath_obj_category; ++ ++ xpath_obj_category = e_xml_xpath_eval (xpath_ctx, ++ "/C:categories/C:category[%d]", ++ response_index + 1); ++ ++ if (xpath_obj_category) { ++ gchar *name; ++ ++ name = e_xml_xpath_eval_as_string (xpath_ctx, "/C:categories/C:category[%d]/@name", response_index + 1); ++ ++ if (name && ews_utils_rename_label (name, 1) == name) { ++ const gchar *color_def = NULL; ++ gchar *color, *guid; ++ gint color_index = -1; ++ ++ color = e_xml_xpath_eval_as_string (xpath_ctx, "/C:categories/C:category[%d]/@color", response_index + 1); ++ if (color) { ++ gchar *endptr = NULL; ++ ++ color_index = (gint) g_ascii_strtoll (color, &endptr, 10); ++ ++ if (endptr == color) ++ color_index = -1; ++ } ++ ++ g_free (color); ++ ++ if (color_index >= 0) ++ color_def = ews_utils_outlook_color_index_to_color_def (color_index); ++ ++ guid = e_xml_xpath_eval_as_string (xpath_ctx, "/C:categories/C:category[%d]/@guid", response_index + 1); ++ ++ if (guid && *guid) { ++ CamelEwsCategory *cat; ++ ++ cat = camel_ews_category_new (guid, name, color_def); ++ if (cat) ++ g_hash_table_insert (new_categories, cat->guid, cat); ++ } ++ ++ g_free (guid); ++ } ++ ++ g_free (name); ++ xmlXPathFreeObject (xpath_obj_category); ++ } ++ } ++ ++ xmlXPathFreeObject (xpath_obj_categories); ++ ++ old_categories = camel_ews_store_summary_get_categories (ews_store->summary); ++ ++ if (ews_utils_save_category_changes (old_categories, new_categories)) { ++ camel_ews_store_summary_set_categories (ews_store->summary, new_categories); ++ camel_ews_store_summary_save (ews_store->summary, NULL); ++ } ++ ++ g_hash_table_destroy (new_categories); ++ g_hash_table_destroy (old_categories); ++ } ++ } ++ ++ if (xpath_ctx) ++ xmlXPathFreeContext (xpath_ctx); ++ xmlFreeDoc (doc); ++} +diff -up evolution-ews-3.28.5/src/camel/camel-ews-utils.h.sync-category-list evolution-ews-3.28.5/src/camel/camel-ews-utils.h +--- evolution-ews-3.28.5/src/camel/camel-ews-utils.h.sync-category-list 2018-07-30 16:01:00.000000000 +0200 ++++ evolution-ews-3.28.5/src/camel/camel-ews-utils.h 2019-10-24 09:39:08.341278207 +0200 +@@ -94,6 +94,10 @@ CamelMessageInfo * /* (transfer full) */ + EEwsConnection *cnc, + EEwsItem *item, + GCancellable *cancellable); ++void camel_ews_utils_merge_category_list ++ (CamelEwsStore *ews_store, ++ const guchar *xml_data, ++ gsize xml_data_len); + + G_END_DECLS + +diff -up evolution-ews-3.28.5/src/server/e-ews-connection.c.sync-category-list evolution-ews-3.28.5/src/server/e-ews-connection.c +--- evolution-ews-3.28.5/src/server/e-ews-connection.c.sync-category-list 2019-10-24 09:39:08.339278207 +0200 ++++ evolution-ews-3.28.5/src/server/e-ews-connection.c 2019-10-24 09:46:14.667272312 +0200 +@@ -155,7 +155,7 @@ struct _EwsAsyncData { + EwsDelegateDeliver deliver_to; + EEwsFolderType folder_type; + EEwsConnection *cnc; +- gchar *user_photo; /* base64-encoded, as GetUserPhoto result */ ++ gchar *custom_data; /* Can be re-used by operations, will be freed with g_free() */ + }; + + struct _EwsNode { +@@ -200,7 +200,7 @@ ews_connection_error_quark (void) + static void + async_data_free (EwsAsyncData *async_data) + { +- g_free (async_data->user_photo); ++ g_free (async_data->custom_data); + g_free (async_data); + } + +@@ -10830,10 +10830,10 @@ get_user_photo_response_cb (ESoapRespons + return; + } + +- async_data->user_photo = e_soap_parameter_get_string_value (param); +- if (async_data->user_photo && !*async_data->user_photo) { +- g_free (async_data->user_photo); +- async_data->user_photo = NULL; ++ async_data->custom_data = e_soap_parameter_get_string_value (param); ++ if (async_data->custom_data && !*async_data->custom_data) { ++ g_free (async_data->custom_data); ++ async_data->custom_data = NULL; + } + } + +@@ -10918,11 +10918,11 @@ e_ews_connection_get_user_photo_finish ( + if (g_simple_async_result_propagate_error (simple, error)) + return FALSE; + +- if (!async_data->user_photo) ++ if (!async_data->custom_data) + return FALSE; + +- *out_picture_data = async_data->user_photo; +- async_data->user_photo = NULL; ++ *out_picture_data = async_data->custom_data; ++ async_data->custom_data = NULL; + + return TRUE; + } +@@ -10953,5 +10953,256 @@ e_ews_connection_get_user_photo_sync (EE + + e_async_closure_free (closure); + ++ return success; ++} ++ ++static void ++get_user_configuration_response_cb (ESoapResponse *response, ++ GSimpleAsyncResult *simple) ++{ ++ EwsAsyncData *async_data; ++ ESoapParameter *param, *subparam; ++ GError *error = NULL; ++ ++ async_data = g_simple_async_result_get_op_res_gpointer (simple); ++ ++ param = e_soap_response_get_first_parameter_by_name (response, "ResponseMessages", &error); ++ ++ if (param) { ++ param = e_soap_parameter_get_first_child_by_name (param, "GetUserConfigurationResponseMessage"); ++ if (!param) { ++ g_set_error (&error, ++ SOUP_HTTP_ERROR, SOUP_STATUS_MALFORMED, ++ "Missing <%s> in SOAP response", "GetUserConfigurationResponseMessage"); ++ } ++ } ++ ++ if (param) { ++ param = e_soap_parameter_get_first_child_by_name (param, "UserConfiguration"); ++ if (!param) { ++ g_set_error (&error, ++ SOUP_HTTP_ERROR, SOUP_STATUS_MALFORMED, ++ "Missing <%s> in SOAP response", "UserConfiguration"); ++ } ++ } ++ ++ /* Sanity check */ ++ g_return_if_fail ( ++ (param != NULL && error == NULL) || ++ (param == NULL && error != NULL)); ++ ++ if (error != NULL) { ++ g_simple_async_result_take_error (simple, error); ++ return; ++ } ++ ++ subparam = e_soap_parameter_get_first_child_by_name (param, "ItemId"); ++ if (subparam) { ++ gchar *id, *changekey; ++ ++ id = e_soap_parameter_get_property (subparam, "Id"); ++ changekey = e_soap_parameter_get_property (subparam, "ChangeKey"); ++ ++ /* Encoded as: Id + "\n" + ChangeKey */ ++ async_data->custom_data = g_strconcat (id ? id : "", "\n", changekey, NULL); ++ ++ g_free (changekey); ++ g_free (id); ++ } ++ ++ if (!subparam) { ++ subparam = e_soap_parameter_get_first_child_by_name (param, "Dictionary"); ++ if (subparam) ++ async_data->custom_data = e_soap_response_dump_parameter (response, subparam); ++ } ++ ++ if (!subparam) { ++ subparam = e_soap_parameter_get_first_child_by_name (param, "XmlData"); ++ if (subparam) { ++ async_data->custom_data = e_soap_parameter_get_string_value (subparam); ++ } ++ } ++ ++ if (!subparam) { ++ subparam = e_soap_parameter_get_first_child_by_name (param, "BinaryData"); ++ if (subparam) { ++ async_data->custom_data = e_soap_parameter_get_string_value (subparam); ++ } ++ } ++ ++ if (async_data->custom_data && !*async_data->custom_data) { ++ g_free (async_data->custom_data); ++ async_data->custom_data = NULL; ++ } ++} ++ ++static void ++e_ews_folder_id_append_to_msg (ESoapMessage *msg, ++ const gchar *email, ++ const EwsFolderId *fid) ++{ ++ g_return_if_fail (msg != NULL); ++ g_return_if_fail (fid != NULL); ++ ++ if (fid->is_distinguished_id) ++ e_soap_message_start_element (msg, "DistinguishedFolderId", NULL, NULL); ++ else ++ e_soap_message_start_element (msg, "FolderId", NULL, NULL); ++ ++ e_soap_message_add_attribute (msg, "Id", fid->id, NULL, NULL); ++ if (fid->change_key) ++ e_soap_message_add_attribute (msg, "ChangeKey", fid->change_key, NULL, NULL); ++ ++ if (fid->is_distinguished_id && email) { ++ e_soap_message_start_element (msg, "Mailbox", NULL, NULL); ++ e_ews_message_write_string_parameter (msg, "EmailAddress", NULL, email); ++ e_soap_message_end_element (msg); ++ } ++ ++ e_soap_message_end_element (msg); ++} ++ ++void ++e_ews_connection_get_user_configuration (EEwsConnection *cnc, ++ gint pri, ++ const EwsFolderId *fid, ++ const gchar *config_name, ++ EEwsUserConfigurationProperties props, ++ GCancellable *cancellable, ++ GAsyncReadyCallback callback, ++ gpointer user_data) ++{ ++ ESoapMessage *msg; ++ GSimpleAsyncResult *simple; ++ EwsAsyncData *async_data; ++ EwsFolderId local_fid; ++ ++ g_return_if_fail (cnc != NULL); ++ g_return_if_fail (cnc->priv != NULL); ++ g_return_if_fail (fid != NULL); ++ g_return_if_fail (config_name != NULL); ++ ++ simple = g_simple_async_result_new (G_OBJECT (cnc), callback, user_data, e_ews_connection_get_user_configuration); ++ async_data = g_new0 (EwsAsyncData, 1); ++ g_simple_async_result_set_op_res_gpointer (simple, async_data, (GDestroyNotify) async_data_free); ++ ++ /* EWS server version earlier than 2010 doesn't support it. */ ++ if (!e_ews_connection_satisfies_server_version (cnc, E_EWS_EXCHANGE_2010)) { ++ g_simple_async_result_complete_in_idle (simple); ++ g_object_unref (simple); ++ return; ++ } ++ ++ local_fid = *fid; ++ local_fid.change_key = NULL; ++ ++ msg = e_ews_message_new_with_header ( ++ cnc->priv->settings, ++ cnc->priv->uri, ++ cnc->priv->impersonate_user, ++ "GetUserConfiguration", ++ NULL, ++ NULL, ++ cnc->priv->version, ++ E_EWS_EXCHANGE_2010, ++ FALSE, ++ TRUE); ++ ++ e_soap_message_start_element (msg, "UserConfigurationName", "messages", NULL); ++ e_soap_message_add_attribute (msg, "Name", config_name, NULL, NULL); ++ ++ e_ews_folder_id_append_to_msg (msg, cnc->priv->email, &local_fid); ++ ++ e_soap_message_end_element (msg); /* UserConfigurationName */ ++ ++ e_soap_message_start_element (msg, "UserConfigurationProperties", "messages", NULL); ++ ++ switch (props) { ++ case E_EWS_USER_CONFIGURATION_PROPERTIES_ID: ++ e_soap_message_write_string (msg, "Id"); ++ break; ++ case E_EWS_USER_CONFIGURATION_PROPERTIES_DICTIONARY: ++ e_soap_message_write_string (msg, "Dictionary"); ++ break; ++ case E_EWS_USER_CONFIGURATION_PROPERTIES_XMLDATA: ++ e_soap_message_write_string (msg, "XmlData"); ++ break; ++ case E_EWS_USER_CONFIGURATION_PROPERTIES_BINARYDATA: ++ e_soap_message_write_string (msg, "BinaryData"); ++ break; ++ /* case E_EWS_USER_CONFIGURATION_PROPERTIES_ALL: ++ e_soap_message_write_string (msg, "All"); ++ break; */ ++ default: ++ e_soap_message_write_string (msg, "Unknown"); ++ break; ++ } ++ ++ e_soap_message_end_element (msg); /* UserConfigurationProperties */ ++ ++ e_ews_message_write_footer (msg); ++ ++ e_ews_connection_queue_request (cnc, msg, get_user_configuration_response_cb, pri, cancellable, simple); ++ ++ g_object_unref (simple); ++} ++ ++gboolean ++e_ews_connection_get_user_configuration_finish (EEwsConnection *cnc, ++ GAsyncResult *result, ++ gchar **out_properties, ++ GError **error) ++{ ++ GSimpleAsyncResult *simple; ++ EwsAsyncData *async_data; ++ ++ g_return_val_if_fail (cnc != NULL, FALSE); ++ g_return_val_if_fail ( ++ g_simple_async_result_is_valid (result, G_OBJECT (cnc), e_ews_connection_get_user_configuration), ++ FALSE); ++ g_return_val_if_fail (out_properties != NULL, FALSE); ++ ++ simple = G_SIMPLE_ASYNC_RESULT (result); ++ async_data = g_simple_async_result_get_op_res_gpointer (simple); ++ ++ if (g_simple_async_result_propagate_error (simple, error)) ++ return FALSE; ++ ++ if (!async_data->custom_data) ++ return FALSE; ++ ++ *out_properties = async_data->custom_data; ++ async_data->custom_data = NULL; ++ ++ return TRUE; ++} ++ ++gboolean ++e_ews_connection_get_user_configuration_sync (EEwsConnection *cnc, ++ gint pri, ++ const EwsFolderId *fid, ++ const gchar *config_name, ++ EEwsUserConfigurationProperties props, ++ gchar **out_properties, ++ GCancellable *cancellable, ++ GError **error) ++{ ++ EAsyncClosure *closure; ++ GAsyncResult *result; ++ gboolean success; ++ ++ g_return_val_if_fail (cnc != NULL, FALSE); ++ ++ closure = e_async_closure_new (); ++ ++ e_ews_connection_get_user_configuration ( ++ cnc, pri, fid, config_name, props, cancellable, e_async_closure_callback, closure); ++ ++ result = e_async_closure_wait (closure); ++ ++ success = e_ews_connection_get_user_configuration_finish (cnc, result, out_properties, error); ++ ++ e_async_closure_free (closure); ++ + return success; + } +diff -up evolution-ews-3.28.5/src/server/e-ews-connection.h.sync-category-list evolution-ews-3.28.5/src/server/e-ews-connection.h +--- evolution-ews-3.28.5/src/server/e-ews-connection.h.sync-category-list 2019-10-24 09:39:08.339278207 +0200 ++++ evolution-ews-3.28.5/src/server/e-ews-connection.h 2019-10-24 09:39:08.342278207 +0200 +@@ -132,6 +132,15 @@ typedef enum { + E_EWS_SIZE_REQUESTED_648X648 = 648 + } EEwsSizeRequested; + ++typedef enum { ++ E_EWS_USER_CONFIGURATION_PROPERTIES_UNKNOWN = -1, ++ E_EWS_USER_CONFIGURATION_PROPERTIES_ID, ++ E_EWS_USER_CONFIGURATION_PROPERTIES_DICTIONARY, ++ E_EWS_USER_CONFIGURATION_PROPERTIES_XMLDATA, ++ E_EWS_USER_CONFIGURATION_PROPERTIES_BINARYDATA /*, ++ E_EWS_USER_CONFIGURATION_PROPERTIES_ALL - skip it, be specific */ ++} EEwsUserConfigurationProperties; ++ + typedef struct { + gchar *id; + gchar *dn; +@@ -1377,6 +1386,29 @@ gboolean e_ews_connection_get_user_photo + gchar **out_picture_data, /* base64-encoded */ + GCancellable *cancellable, + GError **error); ++void e_ews_connection_get_user_configuration ++ (EEwsConnection *cnc, ++ gint pri, ++ const EwsFolderId *fid, ++ const gchar *config_name, ++ EEwsUserConfigurationProperties props, ++ GCancellable *cancellable, ++ GAsyncReadyCallback callback, ++ gpointer user_data); ++gboolean e_ews_connection_get_user_configuration_finish ++ (EEwsConnection *cnc, ++ GAsyncResult *result, ++ gchar **out_properties, ++ GError **error); ++gboolean e_ews_connection_get_user_configuration_sync ++ (EEwsConnection *cnc, ++ gint pri, ++ const EwsFolderId *fid, ++ const gchar *config_name, ++ EEwsUserConfigurationProperties props, ++ gchar **out_properties, ++ GCancellable *cancellable, ++ GError **error); + + G_END_DECLS + +diff -up evolution-ews-3.28.5/src/server/e-soap-response.c.sync-category-list evolution-ews-3.28.5/src/server/e-soap-response.c +--- evolution-ews-3.28.5/src/server/e-soap-response.c.sync-category-list 2018-07-30 16:01:00.000000000 +0200 ++++ evolution-ews-3.28.5/src/server/e-soap-response.c 2019-10-24 09:39:08.342278207 +0200 +@@ -685,3 +685,29 @@ e_soap_response_dump_response (ESoapResp + + return ret; + } ++ ++gchar * ++e_soap_response_dump_parameter (ESoapResponse *response, ++ ESoapParameter *param) ++{ ++ xmlBuffer *buffer; ++ gint len; ++ gchar *data; ++ ++ g_return_val_if_fail (E_IS_SOAP_RESPONSE (response), NULL); ++ g_return_val_if_fail (param != NULL, NULL); ++ ++ buffer = xmlBufferCreate (); ++ len = xmlNodeDump (buffer, response->priv->xmldoc, param, 0, 0); ++ ++ if (len <= 0) { ++ xmlBufferFree (buffer); ++ return NULL; ++ } ++ ++ data = g_strndup ((const gchar *) buffer->content, len); ++ ++ xmlBufferFree (buffer); ++ ++ return data; ++} +diff -up evolution-ews-3.28.5/src/server/e-soap-response.h.sync-category-list evolution-ews-3.28.5/src/server/e-soap-response.h +--- evolution-ews-3.28.5/src/server/e-soap-response.h.sync-category-list 2018-07-30 16:01:00.000000000 +0200 ++++ evolution-ews-3.28.5/src/server/e-soap-response.h 2019-10-24 09:39:08.343278207 +0200 +@@ -101,6 +101,8 @@ ESoapParameter * + const gchar *name); + gint e_soap_response_dump_response (ESoapResponse *response, + FILE *buffer); ++gchar * e_soap_response_dump_parameter (ESoapResponse *response, ++ ESoapParameter *param); + + G_END_DECLS + diff --git a/SOURCES/evolution-ews-3.28.5-unknown-responsetype-as-needs-action.patch b/SOURCES/evolution-ews-3.28.5-unknown-responsetype-as-needs-action.patch new file mode 100644 index 0000000..e127d0b --- /dev/null +++ b/SOURCES/evolution-ews-3.28.5-unknown-responsetype-as-needs-action.patch @@ -0,0 +1,22 @@ +From 6d3dc9c50be654a9e250cfd53626f8526ff9eb70 Mon Sep 17 00:00:00 2001 +From: Milan Crha +Date: Tue, 25 Sep 2018 16:20:12 +0200 +Subject: Treat 'Unknown' ResponseType as Needs-Action + +When creating a new meeting the attendees have returned by the server +an 'Unknown' ResponseType, which turned into 'Unknown' in the Evolution +UI (meeting editor). Treat it as Needs-Action instead. + +diff --git a/src/calendar/e-cal-backend-ews.c b/src/calendar/e-cal-backend-ews.c +index bf995813..06d56fa0 100644 +--- a/src/calendar/e-cal-backend-ews.c ++++ b/src/calendar/e-cal-backend-ews.c +@@ -311,7 +311,7 @@ ecb_ews_responsetype_to_partstat (const gchar *responsetype) + else if (g_ascii_strcasecmp (responsetype, "NoResponseReceived") == 0) + param = icalparameter_new_partstat (ICAL_PARTSTAT_NEEDSACTION); + else if (g_ascii_strcasecmp (responsetype, "Unknown") == 0) +- param = icalparameter_new_partstat (ICAL_PARTSTAT_NONE); ++ param = icalparameter_new_partstat (ICAL_PARTSTAT_NEEDSACTION); + + if (!param) + param = icalparameter_new_partstat (ICAL_PARTSTAT_NONE); diff --git a/SPECS/evolution-ews.spec b/SPECS/evolution-ews.spec new file mode 100644 index 0000000..3cb7df5 --- /dev/null +++ b/SPECS/evolution-ews.spec @@ -0,0 +1,610 @@ +%global libmspack_version 0.4 + +Name: evolution-ews +Version: 3.28.5 +Release: 15%{?dist} +Group: Applications/Productivity +Summary: Evolution extension for Exchange Web Services +License: LGPLv2 +URL: https://wiki.gnome.org/Apps/Evolution +Source: http://download.gnome.org/sources/%{name}/3.28/%{name}-%{version}.tar.xz + +%global eds_evo_version %{version} + +Patch01: evolution-ews-3.28.5-import-event-timezone.patch + +# https://gitlab.gnome.org/GNOME/evolution-ews/commit/dc50ba5a50a7b74f1d2710e5f860a7e81ab60d0b +Patch02: evolution-ews-3.28.5-disable-reminder-types.patch + +# https://gitlab.gnome.org/GNOME/evolution-ews/commit/6d3dc9c50be654a9e250cfd53626f8526ff9eb70 +Patch03: evolution-ews-3.28.5-unknown-responsetype-as-needs-action.patch + +# https://gitlab.gnome.org/GNOME/evolution-ews/issues/19 +Patch04: evolution-ews-3.28.5-contact-country-forgotten.patch + +# https://gitlab.gnome.org/GNOME/evolution-ews/issues/21 +Patch05: evolution-ews-3.28.5-meeting-with-attachment.patch + +# https://gitlab.gnome.org/GNOME/evolution-ews/commit/f50530ad101b47d461a345ff2b8b295b86c05d3a +Patch06: evolution-ews-3.28.5-double-collection-backend-populate.patch + +# RH bug #1696761 +Patch07: evolution-ews-3.28.5-cve-2019-3890.patch + +# RH bug #1741091 +Patch08: evolution-ews-3.28.5-birthday-date.patch + +# RH bug #1764818 +Patch09: evolution-ews-3.28.5-sync-category-list.patch + +# RH bug #1765005 +Patch10: evolution-ews-3.28.5-save-only-if-organizer.patch + +# RH bug #1886026 +Patch11: evolution-ews-3.28.5-oauth2-simplify.patch + +# RH bug #1885229 +Patch12: evolution-ews-3.28.5-oauth2-endpoints.patch + +# RH bug #1972749 +Patch13: evolution-ews-3.28.5-cmake-variable-name-comparison.patch + +# RH bug #2082245 +Patch14: evolution-ews-3.28.5-autodiscover-improvements.patch + +Patch15: 0001-I-250-Mail-Calendar-attachments-can-be-broken-by-the.patch +Patch16: 0002-deadlock-on-op-cancel.patch + +Requires: evolution >= %{eds_evo_version} +Requires: evolution-data-server >= %{eds_evo_version} +Requires: %{name}-langpacks = %{version}-%{release} +Requires: libmspack >= %{libmspack_version} + +BuildRequires: cmake +BuildRequires: gcc +BuildRequires: intltool +BuildRequires: pkgconfig(camel-1.2) >= %{eds_evo_version} +BuildRequires: pkgconfig(evolution-data-server-1.2) >= %{eds_evo_version} +BuildRequires: pkgconfig(evolution-mail-3.0) >= %{eds_evo_version} +BuildRequires: pkgconfig(evolution-shell-3.0) >= %{eds_evo_version} +BuildRequires: pkgconfig(glib-2.0) +BuildRequires: pkgconfig(gtk+-3.0) +BuildRequires: pkgconfig(libebackend-1.2) >= %{eds_evo_version} +BuildRequires: pkgconfig(libebook-1.2) >= %{eds_evo_version} +BuildRequires: pkgconfig(libecal-1.2) >= %{eds_evo_version} +BuildRequires: pkgconfig(libedata-book-1.2) >= %{eds_evo_version} +BuildRequires: pkgconfig(libedata-cal-1.2) >= %{eds_evo_version} +BuildRequires: pkgconfig(libemail-engine) >= %{eds_evo_version} +BuildRequires: pkgconfig(libical) +BuildRequires: pkgconfig(libmspack) >= %{libmspack_version} +BuildRequires: pkgconfig(libsoup-2.4) + +%description +This package allows Evolution to interact with Microsoft Exchange servers, +versions 2007 and later, through its Exchange Web Services (EWS) interface. + +%package langpacks +Summary: Translations for %{name} +BuildArch: noarch +Requires: %{name} = %{version}-%{release} + +%description langpacks +This package contains translations for %{name}. + +%prep +%setup -q + +%patch01 -p1 -b .import-event-timezone +%patch02 -p1 -b .disable-reminder-types +%patch03 -p1 -b .unknown-responsetype-as-needs-action +%patch04 -p1 -b .contact-country-forgotten +%patch05 -p1 -b .meeting-with-attachment +%patch06 -p1 -b .double-collection-backend-populate +%patch07 -p1 -b .cve-2019-3890 +%patch08 -p1 -b .birthday-date +%patch09 -p1 -b .sync-category-list +%patch10 -p1 -b .save-only-if-organizer +%patch11 -p1 -b .oauth2-simplify +%patch12 -p1 -b .oauth2-endpoints +%patch13 -p1 -b .cmake-variable-name-comparison +%patch14 -p1 -b .autodiscover-improvements +%patch15 -p1 -b .workaround-office365.com-bug +%patch16 -p1 -b .deadlock-on-cancel + +%build + +mkdir _build +cd _build + +export CFLAGS="$RPM_OPT_FLAGS -Wno-deprecated-declarations" +%cmake -G "Unix Makefiles" .. +make %{?_smp_mflags} + +%install +rm -rf $RPM_BUILD_ROOT + +cd _build +make install DESTDIR=$RPM_BUILD_ROOT + +%find_lang %{name} + +%post -p /sbin/ldconfig + +%postun -p /sbin/ldconfig + +%files +%license COPYING +%doc NEWS README +%{_libdir}/evolution/modules/module-ews-configuration.so +%{_libdir}/evolution-data-server/camel-providers/libcamelews.so +%{_libdir}/evolution-data-server/camel-providers/libcamelews.urls +%{_libdir}/evolution-data-server/addressbook-backends/libebookbackendews.so +%{_libdir}/evolution-data-server/calendar-backends/libecalbackendews.so +%{_libdir}/evolution-data-server/registry-modules/module-ews-backend.so +%{_libdir}/evolution-ews/libcamelews-priv.so +%{_libdir}/evolution-ews/libevolution-ews.so +%{_datadir}/metainfo/org.gnome.Evolution-ews.metainfo.xml +%{_datadir}/evolution/errors/module-ews-configuration.error +%{_datadir}/evolution-data-server/ews/windowsZones.xml + +%files langpacks -f _build/%{name}.lang + +%changelog +* Thu Feb 08 2024 Milan Crha - 3.28.5-15 +- Resolves: RHEL-24829 (Release queue lock as soon as possible in ews_next_request()) + +* Wed Dec 13 2023 Milan Crha - 3.28.5-14 +- Resolves: RHEL-19376 (Backport upstream patch to workaround broken text/calendar mail attachments by the server) + +* Fri May 06 2022 Milan Crha - 3.28.5-13 +- Related: #2082245 (Fix issues in the patch found by Coverity scan) + +* Fri May 06 2022 Milan Crha - 3.28.5-12 +- Resolves: #2082245 (Backport fixes to improve autodiscovery) + +* Wed Jun 16 2021 Milan Crha - 3.28.5-11 +- Resolves: #1972749 (PrintableOptions.cmake: Correct variable name comparison) + +* Wed Oct 07 2020 Milan Crha - 3.28.5-10 +- Resolves: #1886026 (Simplify OAuth2 for outlook.office365.com server) +- Resolves: #1885229 (Allow change of the Microsoft 365 OAuth2 endpoints) + +* Wed Nov 13 2019 Milan Crha - 3.28.5-9 +- Remove patch for RH bug #1765005 (Reject creating meetings organized by other users) + +* Tue Oct 29 2019 Milan Crha - 3.28.5-8 +- Remove patch for RH bug #1765005 (Send meeting change notifications only if being the organizer) + +* Thu Oct 24 2019 Milan Crha - 3.28.5-7 +- Add patch for RH bug #1764818 (Sync CategoryList with mail Labels) +- Add patch for RH bug #1765005 (Send meeting change notifications only if being the organizer) + +* Wed Oct 23 2019 Milan Crha - 3.28.5-6 +- Add patch for RH bug #1741091 (Birthday date of Contact depends on system timezone) + +* Mon May 27 2019 Milan Crha - 3.28.5-5 +- Rebuild with added gating + +* Mon Apr 15 2019 Milan Crha - 3.28.5-4 +- Add patch for RH bug #1696761 (CVE-2019-3890 - SSL Certificates are not validated) + +* Mon Oct 01 2018 Milan Crha - 3.28.5-3 +- Add "Enable only Notification reminders in the calendar" upstream fix +- Add "Treat 'Unknown' ResponseType as Needs-Action" upstream fix +- Add "I#19 - Country is forgotten along the way to exchange server" upstream fix +- Add "I#21 - Meeting with attachment cannot be saved in 2010" upstream fix +- Add "Collection backend schedules two 'populate' requests after going online" upstream fix + +* Tue Sep 04 2018 Milan Crha - 3.28.5-2 +- Add patch for RH bug #1625162 (Calendar invites added with the wrong time zone) + +* Mon Jul 30 2018 Milan Crha - 3.28.5-1 +- Update to 3.28.5 + +* Mon Jul 16 2018 Milan Crha - 3.28.4-1 +- Update to 3.28.4 + +* Mon Jun 18 2018 Milan Crha - 3.28.3-1 +- Update to 3.28.3 + +* Mon Apr 09 2018 Milan Crha - 3.28.1-1 +- Update to 3.28.1 + +* Mon Mar 12 2018 Milan Crha - 3.28.0-1 +- Update to 3.28.0 + +* Mon Mar 05 2018 Milan Crha - 3.27.92-1 +- Update to 3.27.92 + +* Mon Feb 19 2018 Milan Crha - 3.27.91-1 +- Update to 3.27.91 + +* Tue Feb 06 2018 Milan Crha - 3.27.90-1 +- Update to 3.27.90 + +* Mon Jan 08 2018 Milan Crha - 3.27.4-1 +- Update to 3.27.4 + +* Mon Dec 11 2017 Milan Crha - 3.27.3-1 +- Update to 3.27.3 + +* Mon Nov 13 2017 Milan Crha - 3.27.2-1 +- Update to 3.27.2 + +* Wed Nov 08 2017 Milan Crha - 3.27.1-2 +- Rebuild for newer libical + +* Mon Oct 16 2017 Milan Crha - 3.27.1-1 +- Update to 3.27.1 + +* Mon Oct 02 2017 Milan Crha - 3.26.1-1 +- Update to 3.26.1 + +* Mon Sep 11 2017 Milan Crha - 3.26.0-1 +- Update to 3.26.0 + +* Mon Sep 04 2017 Milan Crha - 3.25.92.2-1 +- Update to 3.25.92.2 + +* Mon Sep 04 2017 Milan Crha - 3.25.92-1 +- Update to 3.25.92 + +* Tue Aug 22 2017 Ville Skyttä - 3.25.91-2 +- Install COPYING as %%license + +* Mon Aug 21 2017 Milan Crha - 3.25.91-1 +- Update to 3.25.91 + +* Mon Aug 07 2017 Milan Crha - 3.25.90-1 +- Update to 3.25.90 + +* Wed Aug 02 2017 Fedora Release Engineering - 3.25.4-4 +- Rebuilt for https://fedoraproject.org/wiki/Fedora_27_Binutils_Mass_Rebuild + +* Mon Jul 31 2017 Florian Weimer - 3.25.4-3 +- Rebuild with binutils fix for ppc64le (#1475636) + +* Wed Jul 26 2017 Fedora Release Engineering - 3.25.4-2 +- Rebuilt for https://fedoraproject.org/wiki/Fedora_27_Mass_Rebuild + +* Mon Jul 17 2017 Milan Crha - 3.25.4-1 +- Update to 3.25.4 + +* Mon Jun 19 2017 Milan Crha - 3.25.3-1 +- Update to 3.25.3 + +* Mon May 22 2017 Milan Crha - 3.25.2-1 +- Update to 3.25.2 + +* Thu Apr 27 2017 Milan Crha - 3.25.1-2 +- Split translations into separate package + +* Mon Apr 24 2017 Milan Crha - 3.25.1-1 +- Update to 3.25.1 + +* Mon Apr 10 2017 Milan Crha - 3.24.1-1 +- Update to 3.24.1 + +* Mon Mar 20 2017 Milan Crha - 3.24.0-1 +- Update to 3.24.0 + +* Mon Mar 13 2017 Milan Crha - 3.23.92-1 +- Update to 3.23.92 + +* Mon Feb 27 2017 Milan Crha - 3.23.91-1 +- Update to 3.23.91 + +* Mon Feb 13 2017 Milan Crha - 3.23.90-1 +- Update to 3.23.90 + +* Fri Feb 10 2017 Fedora Release Engineering - 3.23.4-2 +- Rebuilt for https://fedoraproject.org/wiki/Fedora_26_Mass_Rebuild + +* Mon Jan 16 2017 Milan Crha - 3.23.4-1 +- Update to 3.23.4 + +* Mon Dec 12 2016 Milan Crha - 3.23.3-1 +- Update to 3.23.3 + +* Mon Nov 21 2016 Milan Crha - 3.23.2-1 +- Update to 3.23.2 + +* Mon Oct 24 2016 Milan Crha - 3.23.1-1 +- Update to 3.23.1 + +* Mon Oct 10 2016 Milan Crha - 3.22.1-1 +- Update to 3.22.1 + +* Mon Sep 19 2016 Milan Crha - 3.22.0-1 +- Update to 3.22.0 + +* Mon Sep 12 2016 Milan Crha - 3.21.92-1 +- Update to 3.21.92 + +* Mon Aug 29 2016 Milan Crha - 3.21.91-1 +- Update to 3.21.91 + +* Mon Aug 15 2016 Milan Crha - 3.21.90-1 +- Update to 3.21.90 + +* Mon Jul 18 2016 Milan Crha - 3.21.4-1 +- Update to 3.21.4 + +* Mon Jun 20 2016 Milan Crha - 3.21.3-1 +- Update to 3.21.3 + +* Mon May 23 2016 Milan Crha - 3.21.2-1 +- Update to 3.21.2 + +* Mon Apr 25 2016 Milan Crha - 3.21.1-1 +- Update to 3.21.1 + +* Mon Apr 11 2016 Milan Crha - 3.20.1-1 +- Update to 3.20.1 + +* Mon Mar 21 2016 Milan Crha - 3.20.0-1 +- Update to 3.20.0 + +* Mon Mar 14 2016 Milan Crha - 3.19.92-1 +- Update to 3.19.92 + +* Mon Feb 29 2016 Milan Crha - 3.19.91-1 +- Update to 3.19.91 + +* Mon Feb 15 2016 Milan Crha - 3.19.90-1 +- Update to 3.19.90 + +* Wed Feb 03 2016 Fedora Release Engineering - 3.19.4-3 +- Rebuilt for https://fedoraproject.org/wiki/Fedora_24_Mass_Rebuild + +* Mon Jan 18 2016 David Tardon - 3.19.4-2 +- rebuild for libical 2.0.0 + +* Mon Jan 18 2016 Milan Crha - 3.19.4-1 +- Update to 3.19.4 + +* Mon Dec 14 2015 Milan Crha - 3.19.3-1 +- Update to 3.19.3 + +* Mon Nov 23 2015 Milan Crha - 3.19.2-1 +- Update to 3.19.2 + +* Mon Oct 26 2015 Milan Crha - 3.19.1-1 +- Update to 3.19.1 + +* Mon Oct 12 2015 Milan Crha - 3.18.1-1 +- Update to 3.18.1 + +* Mon Sep 21 2015 Milan Crha - 3.18.0-1 +- Update to 3.18.0 + +* Mon Sep 14 2015 Milan Crha - 3.17.92-1 +- Update to 3.17.92 + +* Mon Aug 31 2015 Milan Crha - 3.17.91-1 +- Update to 3.17.91 + +* Mon Aug 17 2015 Milan Crha - 3.17.90-1 +- Update to 3.17.90 + +* Mon Jul 20 2015 Milan Crha - 3.17.4-1 +- Update to 3.17.4 + +* Mon Jun 22 2015 Milan Crha - 3.17.3-1 +- Update to 3.17.3 + +* Wed Jun 17 2015 Fedora Release Engineering - 3.17.2-2 +- Rebuilt for https://fedoraproject.org/wiki/Fedora_23_Mass_Rebuild + +* Mon May 25 2015 Milan Crha - 3.17.2-1 +- Update to 3.17.2 + +* Mon Apr 27 2015 Milan Crha - 3.17.1-1 +- Update to 3.17.1 + +* Mon Apr 13 2015 Milan Crha - 3.16.1-1 +- Update to 3.16.1 + +* Mon Mar 23 2015 Milan Crha - 3.16.0-1 +- Update to 3.16.0 + +* Mon Mar 16 2015 Milan Crha - 3.15.92-1 +- Update to 3.15.92 + +* Mon Mar 02 2015 Milan Crha - 3.15.91-1 +- Update to 3.15.91 + +* Mon Feb 16 2015 Milan Crha - 3.13.90-1 +- Update to 3.13.90 + +* Mon Jan 26 2015 Milan Crha - 3.13.10-1 +- Update to 3.13.10 + +* Mon Dec 22 2014 Milan Crha - 3.13.9-1 +- Update to 3.13.9 + +* Mon Nov 24 2014 Milan Crha - 3.13.8-1 +- Update to 3.13.8 + +* Mon Oct 27 2014 Milan Crha - 3.13.7-1 +- Update to 3.13.7 + +* Mon Sep 22 2014 Milan Crha - 3.13.6-1 +- Update to 3.13.6 + +* Mon Aug 25 2014 Milan Crha - 3.13.5-1 +- Update to 3.13.5 + +* Sat Aug 16 2014 Fedora Release Engineering - 3.13.4-2 +- Rebuilt for https://fedoraproject.org/wiki/Fedora_21_22_Mass_Rebuild + +* Wed Jul 30 2014 Milan Crha - 3.13.4-1 +- Update to 3.13.4 + +* Mon Jul 14 2014 Milan Crha - 3.12.4-1 +- Update to 3.12.4 + +* Mon Jun 09 2014 Milan Crha - 3.12.3-1 +- Update to 3.12.3 + +* Sat Jun 07 2014 Fedora Release Engineering - 3.12.2-2 +- Rebuilt for https://fedoraproject.org/wiki/Fedora_21_Mass_Rebuild + +* Mon May 12 2014 Milan Crha - 3.12.2-1 +- Update to 3.12.2 + +* Mon Apr 14 2014 Milan Crha - 3.12.1-1 +- Update to 3.12.1 + +* Mon Mar 24 2014 Milan Crha - 3.12.0-1 +- Update to 3.12.0 + +* Mon Mar 17 2014 Milan Crha - 3.11.92-1 +- Update to 3.11.92 + +* Mon Mar 03 2014 Milan Crha - 3.11.91-1 +- Update to 3.11.91 + +* Mon Feb 17 2014 Milan Crha - 3.11.90-1 +- Update to 3.11.90 + +* Mon Feb 03 2014 Milan Crha - 3.11.5-1 +- Update to 3.11.5 + +* Mon Jan 13 2014 Milan Crha - 3.11.4-1 +- Update to 3.11.4 + +* Sun Dec 01 2013 Matthew Barnes - 3.11.2-3 +- Add $RPM_OPT_FLAGS to $CFLAGS (RH bug #1035930) + +* Thu Nov 21 2013 Milan Crha - 3.11.2-2 +- Rebuild for new libical (RH bug #1023020) + +* Mon Nov 18 2013 Milan Crha - 3.11.2-1 +- Update to 3.11.2 +- Disable compiler warnings about deprecated symbols + +* Tue Oct 22 2013 Matthew Barnes - 3.11.1-1 +- Update to 3.11.1 + +* Mon Oct 14 2013 Milan Crha - 3.10.1-1 +- Update to 3.10.1 + +* Mon Sep 23 2013 Milan Crha - 3.10.0-1 +- Update to 3.10.0 + +* Mon Sep 16 2013 Milan Crha - 3.9.92-1 +- Update to 3.9.92 + +* Mon Sep 02 2013 Milan Crha - 3.9.91-1 +- Update to 3.9.91 + +* Mon Aug 19 2013 Milan Crha - 3.9.90-1 +- Update to 3.9.90 + +* Mon Jul 29 2013 Milan Crha - 3.9.5-1 +- Update to 3.9.5 + +* Mon Jul 08 2013 Milan Crha - 3.9.4-1 +- Update to 3.9.4 + +* Mon Jun 17 2013 Milan Crha - 3.9.3-1 +- Update to 3.9.3 +- Add dependency on libmspack 0.4+ + +* Mon May 27 2013 Milan Crha - 3.9.2-1 +- Update to 3.9.2 + +* Sat May 25 2013 Rex Dieter 3.9.1-2 +- rebuild (libical) + +* Mon Apr 29 2013 Milan Crha - 3.9.1-1 +- Update to 3.9.1 + +* Mon Mar 25 2013 Milan Crha - 3.8.0-1 +- Update to 3.8.0 + +* Mon Mar 18 2013 Milan Crha - 3.7.92-1 +- Update to 3.7.92 + +* Mon Mar 04 2013 Milan Crha - 3.7.91-1 +- Update to 3.7.91 + +* Mon Feb 18 2013 Milan Crha - 3.7.90-1 +- Update to 3.7.90 + +* Mon Feb 04 2013 Milan Crha - 3.7.5-1 +- Update to 3.7.5 + +* Mon Jan 14 2013 Milan Crha - 3.7.4-1 +- Update to 3.7.4 + +* Thu Dec 20 2012 Matthew Barnes - 3.7.3.1-1 +- Update to 3.7.3.1 + +* Mon Nov 19 2012 Milan Crha - 3.7.2-1 +- Update to 3.7.2 + +* Mon Oct 22 2012 Milan Crha - 3.7.1-1 +- Update to 3.7.1 + +* Mon Sep 17 2012 Milan Crha - 3.5.92-1 +- Update to 3.5.92 + +* Mon Sep 03 2012 Milan Crha - 3.5.91-1 +- Update to 3.5.91 +- Remove patch for new xmlOutputBuffer API (fixed upstream) + +* Mon Aug 20 2012 Milan Crha - 3.5.90-1 +- Update to 3.5.90 +- Add patch for new xmlOutputBuffer API + +* Mon Aug 06 2012 Milan Crha - 3.5.5-1 +- Update to 3.5.5 + +* Thu Jul 19 2012 Fedora Release Engineering - 3.5.4-2 +- Rebuilt for https://fedoraproject.org/wiki/Fedora_18_Mass_Rebuild + +* Mon Jul 16 2012 Milan Crha - 3.5.4-1 +- Update to 3.5.4 + +* Mon Jun 25 2012 Matthew Barnes - 3.5.3-1 +- Update to 3.5.3 + +* Mon Jun 04 2012 Milan Crha - 3.5.2-1 +- Update to 3.5.2 + +* Sun Apr 29 2012 Matthew Barnes - 3.5.1-1 +- Update to 3.5.1 + +* Mon Mar 26 2012 Milan Crha - 3.4.0-1 +- Update to 3.4.0 + +* Mon Mar 19 2012 Milan Crha - 3.3.92-1 +- Update to 3.3.92 + +* Tue Mar 06 2012 Milan Crha - 3.3.91-1 +- Update to 3.3.91 + +* Mon Feb 20 2012 Milan Crha - 3.3.90-1 +- Update to 3.3.90 + +* Mon Feb 06 2012 Milan Crha - 3.3.5-1 +- Update to 3.3.5 + +* Mon Jan 16 2012 Milan Crha - 3.3.4-1 +- Update to 3.3.4 + +* Fri Jan 13 2012 Fedora Release Engineering - 3.3.3-2 +- Rebuilt for https://fedoraproject.org/wiki/Fedora_17_Mass_Rebuild + +* Mon Dec 19 2011 Milan Crha - 3.3.3-1 +- Update to 3.3.3 +- Removed the last patch (fixed upstream) + +* Mon Nov 21 2011 Milan Crha - 3.3.2-1 +- Update to 3.3.2 +- Removed obsolete patches (fixed upstream) + +* Mon Nov 07 2011 Matthew Barnes - 3.3.1-1 +- Initial packaging for Fedora 17.