[evolution-ews/wip/mcrha/soup3] Cover Autodiscover API (untested)



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]