[evolution-ews/wip/mcrha/soup3] Cover Autodiscover API (untested)
- From: Milan Crha <mcrha src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [evolution-ews/wip/mcrha/soup3] Cover Autodiscover API (untested)
- Date: Thu, 16 Dec 2021 16:25:55 +0000 (UTC)
commit 047d5a6b2475c840869c1f63b0d2c2758de75d60
Author: Milan Crha <mcrha redhat com>
Date: Thu Dec 16 17:25:23 2021 +0100
Cover Autodiscover API (untested)
src/EWS/common/e-ews-connection.c | 792 ++++++++++++++++++--------------------
1 file changed, 370 insertions(+), 422 deletions(-)
---
diff --git a/src/EWS/common/e-ews-connection.c b/src/EWS/common/e-ews-connection.c
index ca442735..ce2a4f4f 100644
--- a/src/EWS/common/e-ews-connection.c
+++ b/src/EWS/common/e-ews-connection.c
@@ -2307,57 +2307,41 @@ e_ews_autodiscover_ws_xml (const gchar *email_address)
return doc;
}
-struct _autodiscover_data {
- EEwsConnection *cnc;
- xmlOutputBuffer *buf;
- SoupMessage *msgs[6];
+typedef struct _AutodiscoverData {
+ guint n_pending;
+ /* Borrowed */
+ GMainLoop *main_loop;
+ CamelEwsSettings *settings;
+ ESoupSession *session;
GCancellable *cancellable;
- gulong cancel_id;
- GError *error;
+ /* Allocated */
+ xmlOutputBuffer *buf
+
gchar *redirect_addr;
gchar *redirect_url;
gint n_redirects;
/* Results */
+ gchar *certificate_pem;
+ GTlsCertificateFlags certificate_errors;
+ GError *error;
gchar *as_url;
gchar *oab_url;
-};
+} AutodiscoverData;
static void
-autodiscover_data_free (struct _autodiscover_data *ad)
+autodiscover_data_clear (AutodiscoverData *ad)
{
- xmlOutputBufferClose (ad->buf);
-
- if (ad->cancellable != NULL) {
- g_cancellable_disconnect (ad->cancellable, ad->cancel_id);
- g_object_unref (ad->cancellable);
- }
-
- /* Unref the connection after the cancellable is disconnected,
- to avoid race condition when the connection is freed inside
- the g_cancellable_disconnect() function, which holds the
- cancellable lock and blocks all other threads, while at
- the same time the connection can wait for the finish of
- its worker thread. */
- g_object_unref (ad->cnc);
-
+ g_clear_pointer (&ad->buf, xmlOutputBufferClose);
g_clear_error (&ad->error);
g_free (ad->redirect_addr);
g_free (ad->redirect_url);
+ g_free (ad->certificate_pem);
g_free (ad->as_url);
g_free (ad->oab_url);
-
- g_slice_free (struct _autodiscover_data, ad);
-}
-
-static void
-autodiscover_cancelled_cb (GCancellable *cancellable,
- EEwsConnection *cnc)
-{
- ews_connection_schedule_abort (cnc);
}
/* Frees only the content, not the 'urls' structure itself */
@@ -2377,78 +2361,38 @@ ews_urls_free_content (EwsUrls *urls)
}
static gboolean
-e_ews_discover_prepare_messages_and_send (GSimpleAsyncResult *simple,
- const gchar *email_address,
- const gchar *override_url,
- GError **error);
+e_ews_autodiscover_prepare_requests_and_send_sync (AutodiscoverData *ad,
+ const gchar *email_address,
+ const gchar *override_url,
+ GCancellable *cancellable);
-/* Called when each soup message completes */
static void
-autodiscover_response_cb (SoupSession *session,
- SoupMessage *msg,
- gpointer data)
+ews_process_autodiscover_response (AutodiscoverData *ad,
+ GBytes *bytes,
+ GError **error)
{
- GSimpleAsyncResult *simple = data;
- struct _autodiscover_data *ad;
EwsUrls exch_urls, expr_urls;
- guint status = soup_message_get_status (request);
xmlDoc *doc;
xmlNode *node;
gchar *str;
gint idx;
- 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 < 6; idx++) {
- if (ad->msgs[idx] == msg)
- break;
- }
- if (idx == 6 || (idx == 5 && !ad->msgs[5])) {
- /* We already got removed (cancelled). Do nothing */
- goto unref;
- }
-
- ad->msgs[idx] = NULL;
-
- if (status != SOUP_STATUS_OK) {
- gboolean expired = FALSE;
- gchar *service_url = NULL;
-
- if (e_ews_connection_utils_check_x_ms_credential_headers (request, NULL, &expired,
&service_url) && expired) {
- e_ews_connection_utils_expired_password_to_error (service_url, &error);
- } else {
- g_set_error (
- &error, E_SOUP_SESSION_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);
-
- goto failed;
- }
-
doc = xmlReadMemory (
- msg->response_body->data,
- msg->response_body->length,
+ g_bytes_get_data (bytes, NULL),
+ g_bytes_get_size (bytes),
"autodiscover.xml", NULL, 0);
if (!doc) {
- g_set_error (
- &error, EWS_CONNECTION_ERROR, -1,
+ g_set_error_literal (error, EWS_CONNECTION_ERROR, -1,
_("Failed to parse autodiscover response XML"));
- goto failed;
+ return;
}
node = xmlDocGetRootElement (doc);
if (strcmp ((gchar *) node->name, "Autodiscover")) {
- g_set_error (
- &error, EWS_CONNECTION_ERROR, -1,
+ g_set_error_literal (error, EWS_CONNECTION_ERROR, -1,
_("Failed to find <Autodiscover> element"));
goto failed;
}
@@ -2458,8 +2402,7 @@ autodiscover_response_cb (SoupSession *session,
break;
}
if (!node) {
- g_set_error (
- &error, EWS_CONNECTION_ERROR, -1,
+ g_set_error_literal (error, EWS_CONNECTION_ERROR, -1,
_("Failed to find <Response> element"));
goto failed;
}
@@ -2469,8 +2412,7 @@ autodiscover_response_cb (SoupSession *session,
break;
}
if (!node) {
- g_set_error (
- &error, EWS_CONNECTION_ERROR, -1,
+ g_set_error_literal (error, EWS_CONNECTION_ERROR, -1,
_("Failed to find <Account> element"));
goto failed;
}
@@ -2515,38 +2457,27 @@ autodiscover_response_cb (SoupSession *session,
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,
+ g_set_error_literal (error, EWS_CONNECTION_ERROR, -1,
_("Failed to find <ASUrl> in autodiscover response"));
goto failed;
}
- /* We have a good response; cancel all the others */
- for (idx = 0; idx < 6; idx++) {
- if (ad->msgs[idx]) {
- SoupMessage *m = ad->msgs[idx];
- ad->msgs[idx] = NULL;
- ews_connection_schedule_cancel_message (ad->cnc, m);
- }
- }
+ /* It's a good response, cancel all pending */
+ g_cancellable_cancel (ad->cancellable);
if (expr_urls.as_url) {
- if (ad->as_url)
- g_free (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);
+ 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);
+ 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);
+ g_free (ad->oab_url);
ad->oab_url = g_strdup ((gchar *) exch_urls.oab_url);
}
@@ -2556,59 +2487,11 @@ autodiscover_response_cb (SoupSession *session,
goto exit;
failed:
- for (idx = 0; idx < 6; idx++) {
- if (ad->msgs[idx]) {
- /* Preserve any Unauthorized/SSL failed errors */
- if (!g_error_matches (error, E_SOUP_SESSION_ERROR, SOUP_STATUS_NONE) &&
- !g_error_matches (ad->error, E_SOUP_SESSION_ERROR, SOUP_STATUS_UNAUTHORIZED) &&
- !g_error_matches (ad->error, E_SOUP_SESSION_ERROR, SOUP_STATUS_SSL_FAILED) &&
- (!ad->error ||
- g_error_matches (error, E_SOUP_SESSION_ERROR, SOUP_STATUS_UNAUTHORIZED) ||
- g_error_matches (error, E_SOUP_SESSION_ERROR, SOUP_STATUS_SSL_FAILED) ||
- g_error_matches (ad->error, E_SOUP_SESSION_ERROR, SOUP_STATUS_NONE) ||
- g_error_matches (ad->error, E_SOUP_SESSION_ERROR, SOUP_STATUS_CANT_RESOLVE) ||
- g_error_matches (ad->error, E_SOUP_SESSION_ERROR, SOUP_STATUS_CANT_RESOLVE_PROXY)
||
- g_error_matches (ad->error, E_SOUP_SESSION_ERROR, SOUP_STATUS_CANT_CONNECT) ||
- g_error_matches (ad->error, E_SOUP_SESSION_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. */
- goto unref;
- }
- }
-
- /* Preserve any Unauthorized/SSL failed errors */
- if (!g_error_matches (error, E_SOUP_SESSION_ERROR, SOUP_STATUS_NONE) &&
- !g_error_matches (ad->error, E_SOUP_SESSION_ERROR, SOUP_STATUS_UNAUTHORIZED) &&
- !g_error_matches (ad->error, E_SOUP_SESSION_ERROR, SOUP_STATUS_SSL_FAILED) &&
- (!ad->error ||
- g_error_matches (error, E_SOUP_SESSION_ERROR, SOUP_STATUS_UNAUTHORIZED) ||
- g_error_matches (error, E_SOUP_SESSION_ERROR, SOUP_STATUS_SSL_FAILED) ||
- g_error_matches (ad->error, E_SOUP_SESSION_ERROR, SOUP_STATUS_NONE) ||
- g_error_matches (ad->error, E_SOUP_SESSION_ERROR, SOUP_STATUS_CANT_RESOLVE) ||
- g_error_matches (ad->error, E_SOUP_SESSION_ERROR, SOUP_STATUS_CANT_RESOLVE_PROXY) ||
- g_error_matches (ad->error, E_SOUP_SESSION_ERROR, SOUP_STATUS_CANT_CONNECT) ||
- g_error_matches (ad->error, E_SOUP_SESSION_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, E_SOUP_SESSION_ERROR, SOUP_STATUS_UNAUTHORIZED) &&
- !g_error_matches (ad->error, E_SOUP_SESSION_ERROR, SOUP_STATUS_SSL_FAILED)) {
- CamelEwsSettings *settings = NULL;
- gboolean re_scheduled;
+ (!ad->error || (ad->error->domain != G_IO_ERROR && ad->error->domain != G_TLS_ERROR))) {
const gchar *host_url;
gchar *redirect_addr, *redirect_url;
GError *local_error;
@@ -2624,71 +2507,19 @@ autodiscover_response_cb (SoupSession *session,
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);
-
- re_scheduled = e_ews_discover_prepare_messages_and_send (simple, redirect_addr, host_url,
NULL);
+ host_url = camel_ews_settings_get_hosturl (ad->settings);
- g_clear_object (&settings);
+ e_ews_autodiscover_prepare_requests_and_send_sync (ad, redirect_addr, host_url,
ad->cancellable);
+ g_clear_error (&local_error);
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, ad->error);
-
- ad->error = NULL;
-
exit:
- g_simple_async_result_complete_in_idle (simple);
-
- unref:
- /* This function is processed within e_ews_soup_thread() and the 'simple'
- * holds reference to EEwsConnection. For cases when this is the last
- * reference to 'simple' the unref would cause crash, because of g_thread_join()
- * in connection's dispose, trying to wait on the end of itself, thus it's
- * safer to unref the 'simple' in a dedicated thread.
- */
- e_ews_connection_utils_unref_in_thread (simple);
-}
-
-static void
-post_restarted (SoupMessage *msg,
- gpointer data)
-{
- xmlOutputBuffer *buf = data;
-
- /* Not all restarts are due to a redirect; some are for auth */
- if (msg->status_code == SOUP_STATUS_UNAUTHORIZED)
- return;
-
- /* In violation of RFC2616, libsoup will change a POST request to
- * a GET on receiving a 302 redirect. */
- printf ("Working around libsoup bug with redirect\n");
- g_object_set (request, SOUP_MESSAGE_METHOD, "POST", NULL);
-
- soup_message_set_request (
- msg, "text/xml; charset=utf-8", SOUP_MEMORY_COPY,
- (gchar *)
- #ifdef LIBXML2_NEW_BUFFER
- xmlOutputBufferGetContent (buf), xmlOutputBufferGetSize (buf)
- #else
- buf->buffer->content, buf->buffer->use
- #endif
- );
+ xmlFreeDoc (doc);
}
static ESoapRequest *
@@ -2729,147 +2560,37 @@ e_ews_create_request_for_url (const gchar *url,
return request;
}
-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) {
- GUri *uri;
-
- uri = uri = g_uri_parse (new_uri, SOUP_HTTP_URI_FLAGS | G_URI_FLAGS_PARSE_RELAXED, NULL);
- if (uri) {
- soup_message_set_uri (ad->msgs[5], uri);
- /* The autodiscover_response_cb will free the 'simple' */
- ews_connection_schedule_queue_message (ad->cnc, ad->msgs[5],
autodiscover_response_cb, simple);
- g_uri_unref (uri);
- } else {
- success = FALSE;
- }
- } else {
- success = FALSE;
- }
-
- 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,
- const gchar *email_address,
- const gchar *password,
- gchar **out_certificate_pem,
- GTlsCertificateFlags *out_certificate_errors,
- GCancellable *cancellable,
- GError **error)
-{
- ESoapRequest *request;
- ESoapResponse *response;
- gboolean success;
-
- g_return_val_if_fail (CAMEL_IS_EWS_SETTINGS (settings), FALSE);
- g_return_val_if_fail (email_address != NULL, FALSE);
- g_return_val_if_fail (password != NULL, FALSE);
-
- closure = e_async_closure_new ();
-
- e_ews_autodiscover_ws_url (source, settings, email_address, password, cancellable,
- e_async_closure_callback, closure);
-
- result = e_async_closure_wait (closure);
-
- success = e_ews_autodiscover_ws_url_finish (settings, result, out_certificate_pem,
out_certificate_errors, error);
-
- response = e_ews_connection_send_request_sync (cnc, request, cancellable, error);
-
- if (!response) {
- g_clear_object (&request);
- return FALSE;
- }
-
- success = e_ews_process__response (cnc, response, , error);
-
- g_clear_object (&request);
- g_clear_object (&response);
-
- return success;
-}
-
-static gboolean
-e_ews_discover_prepare_messages_and_send (GSimpleAsyncResult *simple,
- const gchar *email_address,
- const gchar *override_url,
- GError **error)
+static GSList * /* ESoapRequest * */
+e_ews_autodiscover_prepare_requests (AutodiscoverData *ad,
+ const gchar *email_address,
+ const gchar *override_url,
+ gchar **out_srv_lookup_domain,
+ GError **error)
{
GUri *uri = NULL;
gboolean use_secure = TRUE;
gboolean is_outlook = FALSE;
gchar *url1, *url2, *url3, *url4;
const gchar *url5, *domain = NULL;
- struct _autodiscover_data *ad;
+ GSList *requests = NULL;
+ xmlDoc *doc;
+ xmlOutputBuffer *buf;
GError *local_error = NULL;
- ad = g_simple_async_result_get_op_res_gpointer (simple);
- g_return_val_if_fail (ad != NULL, FALSE);
-
- if (email_address) {
- xmlDoc *doc;
-
- 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);
+ g_return_val_if_fail (email_address && *email_address, FALSE);
- xmlFreeDoc (doc);
-
- domain = strchr (email_address, '@');
- if (domain)
- domain++;
- }
+ domain = strchr (email_address, '@');
+ if (domain)
+ domain++;
- g_return_val_if_fail (ad->buf != NULL, FALSE);
g_return_val_if_fail ((domain && *domain) || (override_url && *override_url), FALSE);
+ doc = e_ews_autodiscover_ws_xml (email_address);
+ buf = xmlAllocOutputBuffer (NULL);
+ xmlNodeDumpOutput (buf, doc, xmlDocGetRootElement (doc), 0, 1, NULL);
+ xmlOutputBufferFlush (buf);
+ xmlFreeDoc (doc);
+
url1 = NULL;
url2 = NULL;
url3 = NULL;
@@ -2921,94 +2642,295 @@ e_ews_discover_prepare_messages_and_send (GSimpleAsyncResult *simple,
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_create_request_for_url (url1, ad->buf, &local_error);
- ad->msgs[1] = e_ews_create_request_for_url (url2, ad->buf, local_error ? NULL : &local_error);
- ad->msgs[2] = e_ews_create_request_for_url (url3, ad->buf, local_error ? NULL : &local_error);
- ad->msgs[3] = e_ews_create_request_for_url (url4, ad->buf, local_error ? NULL : &local_error);
- ad->msgs[4] = e_ews_create_request_for_url (url5, ad->buf, local_error ? NULL : &local_error);
-
- if (!is_outlook && *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);
+ #define process_url(_url) G_STMT_START { \
+ if (_url) { \
+ ESoapRequest *request = e_ews_create_request_for_url (_url, buf, local_error ? NULL :
&local_error); \
+ if (request) \
+ requests = g_slist_prepend (requests, request); \
+ } \
+ } G_STMT_END
- /* Fake SoupMessage, for the autodiscovery with SRV record help */
- ad->msgs[5] = e_ews_create_request_for_url (tmp, ad->buf, local_error ? NULL : &local_error);
+ process_url (url1);
+ process_url (url2);
+ process_url (url3);
+ process_url (url4);
+ process_url (url5);
- 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));
- }
+ #undef process_url
- g_free (tmp);
- } else {
- ad->msgs[5] = NULL;
- }
+ if (!is_outlook && *domain && requests && out_srv_lookup_domain)
+ *out_srv_lookup_domain = g_strdup (domain);
+ else if (out_srv_lookup_domain)
+ *out_srv_lookup_domain = NULL;
- if (local_error && (ad->msgs[0] || ad->msgs[1] || ad->msgs[2] || ad->msgs[3] || ad->msgs[4]))
+ if (local_error && requests)
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) {
+ xmlOutputBufferClose (buf);
g_propagate_error (error, local_error);
return FALSE;
}
+ g_clear_pointer (&ad->buf, xmlOutputBufferClose);
+ ad->buf = buf;
+
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)
+static void
+ews_autodiscover_response_ready_cb (GObject source_object,
+ GAsyncResult *result,
+ gpointer user_data)
{
- GSimpleAsyncResult *simple;
- struct _autodiscover_data *ad;
+ AutodiscoverData *ad = user_data;
+ gchar *certificate_pem = NULL;
+ GTlsCertificateFlags certificate_errors = 0;
+ GInputStream *input_stream;
+ GError *local_error = NULL;
+
+ g_return_if_fail (ad != NULL);
+
+ input_stream = e_soup_session_send_message_finish (E_SOUP_SESSION (source_object), result,
+ &certificate_pem, &certificate_errors, &local_error);
+
+ if (input_stream) {
+ GByteArray *bytes;
+ gint expected_length;
+ gpointer buffer;
+ gsize nread = 0;
+ gboolean success;
+
+ expected_length = soup_message_headers_get_content_length (soup_message_get_response_headers
(message));
+ if (expected_length > 0)
+ bytes = g_byte_array_sized_new (expected_length > 1024 * 1024 * 10 ? 1024 * 1024 * 10
: expected_length);
+ else
+ bytes = g_byte_array_new ();
+
+ buffer = g_malloc (BUFFER_SIZE);
+
+ while (success = g_input_stream_read_all (input_stream, buffer, BUFFER_SIZE, &nread,
cancellable, &local_error),
+ success && nread > 0) {
+ g_byte_array_append (bytes, buffer, nread);
+ }
+
+ if (success)
+ ews_process_autodiscover_response (ad, bytes, &local_error);
+
+ g_free (buffer);
+ g_byte_array_free (bytes, TRUE);
+ g_object_unref (input_stream);
+ }
+
+ if (local_error && (!ad->error ||
+ (!g_error_matches (ad->error, E_SOUP_SESSION_ERROR, SOUP_STATUS_UNAUTHORIZED) &&
+ (!ad->error || (ad->error->domain != G_IO_ERROR && ad->error->domain != G_TLS_ERROR))))) {
+ g_clear_pointer (&ad->certificate_pem, g_free);
+ ad->certificate_pem = certificate_pem;
+ ad->certificate_errors = certificate_errors;
+ ad->error = local_error;
+ } else {
+ g_free (certificate_pem);
+ g_clear_error (&local_error);
+ }
+
+ if (!g_atomic_int_dec_and_test (&ad->n_requests))
+ g_main_loop_quit (ad->main_loop);
+}
+
+static void
+ews_autodiscover_send_request (AutodiscoverData *ad,
+ ESoapRequest *request,
+ GCancellable *cancellable,
+ GError **error)
+{
+ SoupMessage *message;
+
+ message = e_soap_request_persist (request, ad->session, ad->settings, error);
+
+ if (message) {
+ gpointer prepare_data;
+
+ prepare_data = e_soup_session_prepare_message_send_sync (ad->session, message, cancellable,
error);
+
+ if (prepare_data) {
+ g_atomic_int_inc (&ad->n_requests);
+
+ e_soup_session_send_message (ad->session, message, G_PRIORITY_DEFAULT, prepare_data,
+ ad->cancellable, ews_autodiscover_response_ready_cb, ad);
+ }
+
+ g_object_unref (message);
+ }
+}
+
+static gboolean
+e_ews_autodiscover_prepare_requests_and_send_sync (AutodiscoverData *ad,
+ const gchar *email_address,
+ const gchar *override_url,
+ GCancellable *cancellable)
+{
+ GSList *requests, *link;
+ gchar *srv_lookup_domain = NULL;
+ GError *local_error = NULL;
+
+ g_return_val_if_fail (ad != NULL, FALSE);
+
+ /* to not have the main loop quit while doing this */
+ g_atomic_int_inc (&ad->n_requests);
+
+ /* This is starting a new round, the last error and others are obsolete */
+ g_clear_error (&ad->error);
+ g_clear_pointer (&ad->as_url, g_free);
+ g_clear_pointer (&ad->oab_url, g_free);
+ g_clear_pointer (&ad->certificate_pem, g_free);
+ ad->certificate_errors = 0;
+
+ requests = e_ews_autodiscover_prepare_requests (ad, email_address, override_url, &srv_lookup_domain,
&local_error);
+
+ if (!local_error && srv_lookup_domain && *srv_lookup_domain) {
+ g_atomic_int_inc (&ad->n_requests);
+
+ g_resolver_lookup_service_async (g_resolver_get_default (), "autodiscover", "tcp", domain,
cancellable,
+ autodiscover_srv_record_resolved_cb, ad);
+ }
+
+ for (link = requests; link; link = g_slist_next (link)) {
+ ESoapRequest *request = link->data;
+
+ ews_autodiscover_send_request (ad, request, cancellable, local_error ? NULL : &local_error);
+ }
+
+ if (local_error && (!ad->error ||
+ (!g_error_matches (ad->error, E_SOUP_SESSION_ERROR, SOUP_STATUS_UNAUTHORIZED) &&
+ (!ad->error || (ad->error->domain != G_IO_ERROR && ad->error->domain != G_TLS_ERROR))))) {
+ g_clear_error (&ad->error);
+ g_propagate_error (&ad->error, local_error);
+ } else {
+ g_clear_error (&local_error);
+ }
+
+ /* pair decrement for the increment at the beginning of the function */
+ if (!g_atomic_int_dec_and_test (&ad->n_requests))
+ g_main_loop_quit (ad->main_loop);
+
+ g_slist_free_full (requests, g_object_unref);
+ g_free (srv_lookup_domain);
+
+ return requests != NULL;
+}
+
+static void
+autodiscover_srv_record_resolved_cb (GObject *source,
+ GAsyncResult *result,
+ gpointer user_data)
+{
+ GList *targets, *link;
+ AutodiscoverData *ad = user_data;
+ gchar *new_uri = NULL;
+ gboolean success;
+
+ g_return_if_fail (ad != NULL);
+
+ targets = g_resolver_lookup_service_finish (G_RESOLVER (source), result, NULL);
+
+ success = !g_cancellable_is_cancelled (ad->cancellable) && 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) {
+ ESoapRequest *request = e_ews_create_request_for_url (new_uri, ad->buf, ad->error ? NULL :
&ad->error);
+ if (request)
+ ews_autodiscover_send_request (ad, request, ad->cancellable, ad->error ? NULL :
&ad->error);
+ g_object_unref (request);
+ } else {
+ success = FALSE;
+ }
+ } else {
+ success = FALSE;
+ }
+
+ if (!success && !g_atomic_int_dec_and_test (&ad->n_requests))
+ g_main_loop_quit (ad->main_loop);
+
+ g_free (new_uri);
+}
+
+static void
+autodiscover_cancelled_cb (GCancellable *cancellable,
+ gpointer user_data)
+{
+ GCancellable *op_cancellable = user_data;
+
+ g_cancellable_cancel (op_cancellable);
+}
+
+gboolean
+e_ews_autodiscover_ws_url_sync (ESource *source,
+ CamelEwsSettings *settings,
+ const gchar *email_address,
+ const gchar *password,
+ gchar **out_certificate_pem,
+ GTlsCertificateFlags *out_certificate_errors,
+ GCancellable *cancellable,
+ GError **error)
+{
+ AutodiscoverData ad;
+ gulong cancelled_id = 0;
+ EEwsConnection *tmp_cnc;
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);
+ GError *local_error = NULL;
- simple = g_simple_async_result_new (
- G_OBJECT (settings), callback,
- user_data, e_ews_autodiscover_ws_url);
+ g_return_val_if_fail (CAMEL_IS_EWS_SETTINGS (settings), FALSE);
+ g_return_val_if_fail (email_address != NULL, FALSE);
+ g_return_val_if_fail (password != NULL, FALSE);
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;
+ g_set_error_literal (error, EWS_CONNECTION_ERROR, -1, _("Email address is missing a domain
part"));
+ return FALSE;
+ }
+
+ memset (&ad, 0, sizeof (AutodiscoverData));
+
+ ad.settings = settings;
+ ad.cancellable = g_cancellable_new ();
+
+ if (G_IS_CANCELLABLE (cancellable)) {
+ cancelled_id = g_cancellable_connect (cancellable, G_CALLBACK (autodiscover_cancelled_cb),
+ ad.cancellable, NULL);
}
+ ad.main_context = g_main_context_new ();
+ ad.main_loop = g_main_loop_new (ad.main_context, FALSE);
+ g_main_context_push_thread_default (ad.main_context);
+
/*
* http://msdn.microsoft.com/en-us/library/ee332364.aspx says we are
* supposed to try $domain and then autodiscover.$domain. But some
@@ -3017,35 +2939,61 @@ e_ews_autodiscover_ws_url (ESource *source,
* to time out. So run both queries in parallel and let the fastest
* (successful) one win.
*/
- ad = g_slice_new0 (struct _autodiscover_data);
- 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);
+ tmp_cnc = e_ews_connection_new_full (source, "https://autodiscover.domain", settings, FALSE);
+ ad.session = e_ews_connection_create_soup_session (tmp_cnc);
+ g_object_set (ad.session, "timeout", 20, NULL);
+ e_ews_connection_set_password (tmp_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 (ad->cnc),
- g_object_unref);
- }
+ host_url = camel_ews_settings_get_hosturl (settings);
- g_simple_async_result_set_op_res_gpointer (
- simple, ad, (GDestroyNotify) autodiscover_data_free);
+ if (e_ews_autodiscover_prepare_requests_and_send_sync (&ad, email_address, host_url, cancellable))
+ g_main_loop_run (ad.main_loop);
- host_url = camel_ews_settings_get_hosturl (settings);
+ g_main_context_pop_thread_default (ad.main_context);
- if (!e_ews_discover_prepare_messages_and_send (simple, email_address, host_url, &error)) {
- g_propagate_error (error, local_error);
- g_simple_async_result_complete_in_idle (simple);
- } else {
- g_clear_error (&error);
+ g_main_context_unref (ad.main_context);
+ g_main_loop_unref (ad.main_loop);
+ g_clear_object (&tmp_cnc);
+ g_clear_object (&ad.session);
+
+ if (cancelled_id)
+ g_cancellable_disconnect (cancellable, cancelled_id);
- /* each request holds a reference to 'simple',
- * thus remove one, to have it actually freed */
- g_object_unref (simple);
+ if (ad.success) {
+ camel_ews_settings_set_hosturl (settings, ad.as_url);
+
+ if (ad.oab_url && !has_suffix_icmp (ad.oab_url, "oab.xml")) {
+ gchar *tmp;
+
+ if (g_str_has_suffix (ad.oab_url, "/"))
+ tmp = g_strconcat (ad.oab_url, "oab.xml", NULL);
+ else
+ tmp = g_strconcat (ad.oab_url, "/", "oab.xml", NULL);
+
+ camel_ews_settings_set_oaburl (settings, tmp);
+ g_free (tmp);
+ } else {
+ camel_ews_settings_set_oaburl (settings, ad.oab_url);
+ }
+
+ if (out_certificate_pem)
+ *out_certificate_pem = NULL;
+ if (out_certificate_errors)
+ *out_certificate_errors = 0;
+ } else {
+ if (ad.error) {
+ g_propagate_error (error, ad.error);
+ ad.error = NULL;
+ }
+ if (out_certificate_pem)
+ *out_certificate_pem = g_steal_pointer (&ad.certificate_pem);
+ if (out_certificate_errors)
+ *out_certificate_errors = ad.certificate_errors;
}
+
+ autodiscover_data_clear (&ad);
+
+ return ad.success;
}
static gboolean
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]