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,