Make autoconfiguration non-blocking
authorColin Leroy <colin@colino.net>
Wed, 30 Apr 2014 08:48:20 +0000 (10:48 +0200)
committerColin Leroy <colin@colino.net>
Fri, 2 May 2014 07:20:39 +0000 (09:20 +0200)
src/common/utils.c
src/common/utils.h
src/gtk/gtkutils.c
src/gtk/gtkutils.h
src/prefs_account.c

index 52f6bac..472b1cc 100644 (file)
@@ -5515,45 +5515,3 @@ int cm_canonicalize_filename(const gchar *filename, gchar **canonical_name) {
        slist_free_strings_full(canonical_parts);
        return 0;
 }
-
-#if (defined USE_GNUTLS && GLIB_CHECK_VERSION(2,22,0))
-gboolean auto_configure_service(const gchar *service, const gchar *domain, gchar **srvhost, guint16 *srvport)
-{
-       GResolver *resolver;
-       GList *answers, *cur;
-       GError *error = NULL;
-       gboolean result = FALSE;
-
-       cm_return_val_if_fail(service != NULL, FALSE);
-       cm_return_val_if_fail(domain != NULL, FALSE);
-
-       resolver = g_resolver_get_default();
-       if (resolver == NULL)
-               return FALSE;
-       
-       answers = g_resolver_lookup_service(resolver, service, "tcp", domain, NULL, &error);
-
-       *srvhost = NULL;
-       *srvport = 0;
-
-       if (answers) {
-               for (cur = g_srv_target_list_sort(answers); cur; cur = cur->next) {
-                       GSrvTarget *target = (GSrvTarget *)cur->data;
-                       const gchar *hostname = g_srv_target_get_hostname(target);
-                       guint16 port = g_srv_target_get_port(target);
-                       if (hostname && strcmp(hostname,"") && port > 0) {
-                               result = TRUE;
-                               *srvhost = g_strdup(hostname);
-                               *srvport = port;
-                               break;
-                       }
-               }
-               g_resolver_free_targets(answers);
-       } else if (error) {
-               g_error_free(error);
-       }
-
-       g_object_unref(resolver);
-       return result;
-}
-#endif
\ No newline at end of file
index d15c66a..434b684 100644 (file)
@@ -599,10 +599,6 @@ void cm_mutex_free(GMutex *mutex);
 
 int cm_canonicalize_filename(const gchar *filename, gchar **canonical_name);
 
-#if (defined USE_GNUTLS && GLIB_CHECK_VERSION(2,22,0))
-gboolean auto_configure_service(const gchar *service, const gchar *domain, gchar **srvhost, guint16 *srvport);
-#endif
-
 #ifdef __cplusplus
 }
 #endif
index 1f05268..07b8b31 100644 (file)
@@ -1877,3 +1877,99 @@ GdkPixbuf *claws_load_pixbuf_fitting(GdkPixbuf *src_pixbuf, int box_width,
 
        return pixbuf;
 }
+
+#if (defined USE_GNUTLS && GLIB_CHECK_VERSION(2,22,0))
+static void auto_configure_done(const gchar *hostname, gint port, gboolean ssl, AutoConfigureData *data)
+{
+       if (hostname != NULL) {
+               if (data->hostname_entry)
+                       gtk_entry_set_text(data->hostname_entry, hostname);
+               if (data->set_port)
+                       gtk_toggle_button_set_active(data->set_port,
+                               (ssl && port == data->default_ssl_port) || (!ssl && port == data->default_port));
+               if (data->port)
+                       gtk_spin_button_set_value(data->port, port);
+               else if (data->hostname_entry) {
+                       gchar *tmp = g_strdup_printf("%s:%d", hostname, port);
+                       gtk_entry_set_text(data->hostname_entry, tmp);
+                       g_free(tmp);
+               }
+
+               if (ssl && data->ssl_checkbtn)
+                       gtk_toggle_button_set_active(data->ssl_checkbtn, TRUE);
+               else if (data->tls_checkbtn)
+                       gtk_toggle_button_set_active(data->tls_checkbtn, TRUE);
+
+               gtk_label_set_text(data->info_label, _("Done."));
+       } else {
+       gtk_label_set_text(data->info_label, _("Failed."));
+       }
+       gtk_widget_show(GTK_WIDGET(data->configure_button));
+       gtk_widget_hide(GTK_WIDGET(data->cancel_button));
+       g_free(data->domain);
+       g_free(data);
+}
+
+static void resolve_done(GObject *source, GAsyncResult *result, gpointer user_data)
+{
+       AutoConfigureData *data = (AutoConfigureData *)user_data;
+       GResolver *resolver = (GResolver *)source;
+       GError *error = NULL;
+       gchar *hostname = NULL;
+       guint16 port;
+       GList *answers, *cur;
+       gboolean found = FALSE;
+       gboolean abort = FALSE;
+
+       answers = g_resolver_lookup_service_finish(resolver, result, &error);
+
+       if (answers) {
+               for (cur = g_srv_target_list_sort(answers); cur; cur = cur->next) {
+                       GSrvTarget *target = (GSrvTarget *)cur->data;
+                       const gchar *h = g_srv_target_get_hostname(target);
+                       port = g_srv_target_get_port(target);
+                       if (h && strcmp(h,"") && port > 0) {
+                               hostname = g_strdup(h);
+                               found = TRUE;
+                               break;
+                       }
+               }
+               g_resolver_free_targets(answers);
+       } else if (error) {
+               if (error->code == G_IO_ERROR_CANCELLED)
+                       abort = TRUE;
+               debug_print("error %s\n", error->message);
+               g_error_free(error);
+       }
+
+       if (found) {
+               auto_configure_done(hostname, port, TRUE, data);
+       } else if (data->ssl_service && !abort) {
+               /* Fallback to TLS */
+               data->ssl_service = NULL;
+               auto_configure_service(data);
+       } else {
+               auto_configure_done(NULL, 0, FALSE, data);
+       }
+       g_free(hostname);
+       g_object_unref(resolver);
+}
+
+void auto_configure_service(AutoConfigureData *data)
+{
+       GResolver *resolver;
+       const gchar *cur_service = data->ssl_service != NULL ? data->ssl_service : data->tls_service;
+
+       cm_return_if_fail(cur_service != NULL);
+       cm_return_if_fail(data->domain != NULL);
+
+       resolver = g_resolver_get_default();
+       if (resolver != NULL) {
+               gtk_label_set_text(data->info_label, _("Configuring..."));
+               gtk_widget_hide(GTK_WIDGET(data->configure_button));
+               gtk_widget_show(GTK_WIDGET(data->cancel_button));
+               g_resolver_lookup_service_async(resolver, cur_service, "tcp", data->domain,
+                                       data->cancel, resolve_done, data);
+       }
+}
+#endif
\ No newline at end of file
index a961dd8..43649a2 100644 (file)
@@ -209,6 +209,30 @@ claws_input_add    (gint         source,
                gtk_widget_set_has_tooltip(GTK_WIDGET(widget), FALSE);  \
 }
 
+#if (defined USE_GNUTLS && GLIB_CHECK_VERSION(2,22,0))
+typedef struct _AutoConfigureData {
+       const gchar *ssl_service;
+       const gchar *tls_service;
+       gchar *domain;
+
+       GtkEntry *hostname_entry;
+       GtkToggleButton *set_port;
+       GtkSpinButton *port;
+       gint default_port;
+       gint default_ssl_port;
+       GtkToggleButton *tls_checkbtn;
+       GtkToggleButton *ssl_checkbtn;
+       GtkLabel *info_label;
+       GtkButton *configure_button;
+       GtkButton *cancel_button;
+       GCancellable *cancel;
+       GMainLoop *main_loop;
+} AutoConfigureData;
+
+void auto_configure_service(AutoConfigureData *data);
+#endif
+
+
 #if GTK_CHECK_VERSION (3, 2, 0)
 #define GTK_TYPE_VBOX GTK_TYPE_BOX
 #define GtkVBox GtkBox
index d539f35..7740264 100644 (file)
@@ -120,6 +120,7 @@ typedef struct BasicPage
        GtkWidget *uid_entry;
        GtkWidget *pass_entry;
        GtkWidget *auto_configure_btn;
+       GtkWidget *auto_configure_cancel_btn;
        GtkWidget *auto_configure_lbl;
 } BasicPage;
 
@@ -1007,6 +1008,7 @@ static void basic_create_widget_func(PrefsPage * _page,
        GtkWidget *uid_entry;
        GtkWidget *pass_entry;
        GtkWidget *auto_configure_btn;
+       GtkWidget *auto_configure_cancel_btn;
        GtkWidget *auto_configure_lbl;
        GtkListStore *menu;
        GtkTreeIter iter;
@@ -1119,6 +1121,8 @@ static void basic_create_widget_func(PrefsPage * _page,
 
        auto_configure_btn = gtk_button_new_with_label(_("Auto-configure"));
        gtk_box_pack_start(GTK_BOX (optmenubox), auto_configure_btn, FALSE, FALSE, 0);
+       auto_configure_cancel_btn = gtk_button_new_with_label(_("Cancel"));
+       gtk_box_pack_start(GTK_BOX (optmenubox), auto_configure_cancel_btn, FALSE, FALSE, 0);
        auto_configure_lbl = gtk_label_new("");
        gtk_label_set_justify(GTK_LABEL(optlabel), GTK_JUSTIFY_LEFT);
        gtk_box_pack_start(GTK_BOX (optmenubox), auto_configure_lbl, FALSE, FALSE, 0);
@@ -1127,6 +1131,8 @@ static void basic_create_widget_func(PrefsPage * _page,
        gtk_widget_show(auto_configure_lbl);
        g_signal_connect (G_OBJECT (auto_configure_btn), "clicked",
                          G_CALLBACK (auto_configure_cb), NULL);
+       g_signal_connect (G_OBJECT (auto_configure_cancel_btn), "clicked",
+                         G_CALLBACK (auto_configure_cb), NULL);
 #endif
 
        no_imap_warn_icon = gtk_image_new_from_stock
@@ -1314,6 +1320,7 @@ static void basic_create_widget_func(PrefsPage * _page,
        page->uid_entry        = uid_entry;
        page->pass_entry       = pass_entry;
        page->auto_configure_btn = auto_configure_btn;
+       page->auto_configure_cancel_btn = auto_configure_cancel_btn;
        page->auto_configure_lbl = auto_configure_lbl;
 
        if (new_account) {
@@ -3793,17 +3800,30 @@ static void prefs_account_select_folder_cb(GtkWidget *widget, gpointer data)
 static void auto_configure_cb (GtkWidget *widget, gpointer data)
 {
        gchar *address = NULL;
-       const gchar *service = NULL;
-       const gchar *secure_service = NULL;
        const gchar *domain = NULL;
-       gchar *hostname;
-       guint16 port = 0;
-       gboolean r = FALSE;
-       gboolean ssl = FALSE;
+       AutoConfigureData *recv_data;
+       AutoConfigureData *send_data;
+       static GCancellable *recv_cancel = NULL;
+       static GCancellable *send_cancel = NULL;
        RecvProtocol protocol;
        struct BasicProtocol *protocol_optmenu = (struct BasicProtocol *) basic_page.protocol_optmenu;
        GtkWidget *optmenu = protocol_optmenu->combobox;
 
+       if (!recv_cancel) {
+               recv_cancel = g_cancellable_new();
+               send_cancel = g_cancellable_new();
+       }
+
+       if (widget == basic_page.auto_configure_cancel_btn) {
+               g_cancellable_cancel(recv_cancel);
+               g_cancellable_cancel(send_cancel);
+               g_object_unref(recv_cancel);
+               g_object_unref(send_cancel);
+               recv_cancel = NULL;
+               send_cancel = NULL;
+               return;
+       }
+
        protocol = combobox_get_active_data(GTK_COMBO_BOX(optmenu));
 
        address = gtk_editable_get_chars(GTK_EDITABLE(basic_page.addr_entry), 0, -1);
@@ -3815,74 +3835,62 @@ static void auto_configure_cb (GtkWidget *widget, gpointer data)
        }
        domain = strchr(address, '@') + 1;
 
-       switch(protocol) {
-       case A_POP3:
-               secure_service = "pop3s";
-               service = "pop3";
-               break;
-       case A_IMAP4:
-               secure_service = "imaps";
-               service = "imap";
-               break;
-       default:
-               secure_service = NULL;
-               service = NULL;
-       }
-
-       gtk_label_set_text(GTK_LABEL(basic_page.auto_configure_lbl),
-                          _("Configuring..."));
-       GTK_EVENTS_FLUSH();
-
-       if (service) {
-               r = auto_configure_service(secure_service, domain, &hostname, &port);
-               if (r)
-                       ssl = TRUE;
-               else
-                       r = auto_configure_service(service, domain, &hostname, &port);
-       }
-
-       if (r) {
+       if (protocol == A_POP3 || protocol == A_IMAP4) {
+               recv_data = g_new0(AutoConfigureData, 1);
+               recv_data->configure_button = GTK_BUTTON(basic_page.auto_configure_btn);
+               recv_data->cancel_button = GTK_BUTTON(basic_page.auto_configure_cancel_btn);
+               recv_data->info_label = GTK_LABEL(basic_page.auto_configure_lbl);
+               recv_data->cancel = recv_cancel;
                switch(protocol) {
                case A_POP3:
-                       gtk_entry_set_text(GTK_ENTRY(basic_page.recvserv_entry), hostname);
-                       gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(advanced_page.popport_checkbtn),
-                               (ssl && port == 995) || (!ssl && port == 110));
-                       gtk_spin_button_set_value(GTK_SPIN_BUTTON(advanced_page.popport_spinbtn), port);
-                       gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(
-                               ssl ? ssl_page.pop_ssltunnel_radiobtn : ssl_page.pop_starttls_radiobtn),
-                               TRUE);
+                       recv_data->ssl_service = "pop3s";
+                       recv_data->tls_service = "pop3";
+                       recv_data->domain = g_strdup(domain);
+                       recv_data->hostname_entry = GTK_ENTRY(basic_page.recvserv_entry);
+                       recv_data->set_port = GTK_TOGGLE_BUTTON(advanced_page.popport_checkbtn);
+                       recv_data->port = GTK_SPIN_BUTTON(advanced_page.popport_spinbtn);
+                       recv_data->tls_checkbtn = GTK_TOGGLE_BUTTON(ssl_page.pop_starttls_radiobtn);
+                       recv_data->ssl_checkbtn = GTK_TOGGLE_BUTTON(ssl_page.pop_ssltunnel_radiobtn);
+                       recv_data->default_port = 110;
+                       recv_data->default_ssl_port = 995;
                        break;
                case A_IMAP4:
-                       gtk_entry_set_text(GTK_ENTRY(basic_page.recvserv_entry), hostname);
-                       gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(advanced_page.imapport_checkbtn),
-                               (ssl && port == 993) || (!ssl && port == 143));
-                       gtk_spin_button_set_value(GTK_SPIN_BUTTON(advanced_page.imapport_spinbtn), port);
-                       gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(
-                               ssl ? ssl_page.imap_ssltunnel_radiobtn : ssl_page.imap_starttls_radiobtn),
-                               TRUE);
+                       recv_data->ssl_service = "imaps";
+                       recv_data->tls_service = "imap";
+                       recv_data->domain = g_strdup(domain);
+                       recv_data->hostname_entry = GTK_ENTRY(basic_page.recvserv_entry);
+                       recv_data->set_port = GTK_TOGGLE_BUTTON(advanced_page.imapport_checkbtn);
+                       recv_data->port = GTK_SPIN_BUTTON(advanced_page.imapport_spinbtn);
+                       recv_data->tls_checkbtn = GTK_TOGGLE_BUTTON(ssl_page.imap_starttls_radiobtn);
+                       recv_data->ssl_checkbtn = GTK_TOGGLE_BUTTON(ssl_page.imap_ssltunnel_radiobtn);
+                       recv_data->default_port = 143;
+                       recv_data->default_ssl_port = 993;
                        break;
                default:
-                       secure_service = NULL;
-                       service = NULL;
+                       cm_return_if_fail(FALSE);
                }
-               g_free(hostname);
-       }
-       
-       r = auto_configure_service("submission", domain, &hostname, &port);
-
-       if (r) {
-               gtk_entry_set_text(GTK_ENTRY(basic_page.smtpserv_entry), hostname);
-               gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(advanced_page.smtpport_checkbtn),
-                       port == 25);
-               gtk_spin_button_set_value(GTK_SPIN_BUTTON(advanced_page.smtpport_spinbtn), port);
-               gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(ssl_page.smtp_starttls_radiobtn), TRUE);
-               gtk_label_set_text(GTK_LABEL(basic_page.auto_configure_lbl),
-                          _("Done."));
-       } else {
-               gtk_label_set_text(GTK_LABEL(basic_page.auto_configure_lbl),
-                          _("Auto-configuration failed."));
+               auto_configure_service(recv_data);
        }
        
+       send_data = g_new0(AutoConfigureData, 1);
+       send_data->configure_button = GTK_BUTTON(basic_page.auto_configure_btn);
+       send_data->cancel_button = GTK_BUTTON(basic_page.auto_configure_cancel_btn);
+       send_data->info_label = GTK_LABEL(basic_page.auto_configure_lbl);
+       send_data->cancel = send_cancel;
+
+       send_data->ssl_service = "submissions";
+       send_data->tls_service = "submission";
+       send_data->domain = g_strdup(domain);
+       send_data->hostname_entry = GTK_ENTRY(basic_page.smtpserv_entry);
+       send_data->set_port = GTK_TOGGLE_BUTTON(advanced_page.smtpport_checkbtn);
+       send_data->port = GTK_SPIN_BUTTON(advanced_page.smtpport_spinbtn);
+       send_data->tls_checkbtn = GTK_TOGGLE_BUTTON(ssl_page.smtp_starttls_radiobtn);
+       send_data->ssl_checkbtn = NULL;
+       send_data->default_port = 25;
+       send_data->default_ssl_port = -1;
+
+       auto_configure_service(send_data);
+
        g_free(address);
 }
 #endif