Blob Blame History Raw
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 <Protocol> 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 <OABUrl> 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 <ASUrl> and <OABUrl> in autodiscover response"));
+			_("Failed to find <ASUrl> 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,