From 76ad2ab7ff4e99d4a2f1173e6677e443a72fd948 Mon Sep 17 00:00:00 2001 From: Milan Crha Date: Tue, 22 Mar 2016 17:25:17 +0100 Subject: [PATCH] Bug 736808 - Fill lists of certificates asynchronously --- smime/gui/certificate-manager.c | 175 ++++++++++++++++++++++++++++---- 1 file changed, 155 insertions(+), 20 deletions(-) diff --git a/smime/gui/certificate-manager.c b/smime/gui/certificate-manager.c index 793c8c4670..daf28709af 100644 --- a/smime/gui/certificate-manager.c +++ b/smime/gui/certificate-manager.c @@ -167,6 +167,8 @@ struct _ECertManagerConfigPrivate { GtkTreeModel *mail_model; GtkTreeView *mail_tree_view; /* not referenced */ + + GCancellable *load_all_certs_cancellable; }; typedef struct { @@ -1709,6 +1711,153 @@ unload_certs (CertPage *cp) (GDestroyNotify) gtk_tree_iter_free); } +typedef struct _LoadAllCertsAsyncData +{ + ECertManagerConfig *ecmc; + GCancellable *cancellable; + GSList *ecerts; + gint tries; +} LoadAllCertsAsyncData; + +static void +load_all_certs_async_data_free (gpointer ptr) +{ + LoadAllCertsAsyncData *data = ptr; + + if (data) { + g_clear_object (&data->ecmc); + g_clear_object (&data->cancellable); + g_slist_free_full (data->ecerts, g_object_unref); + g_free (data); + } +} + +static gboolean +load_all_certs_done_idle_cb (gpointer user_data) +{ + LoadAllCertsAsyncData *data = user_data; + + g_return_val_if_fail (data != NULL, FALSE); + g_return_val_if_fail (E_IS_CERT_MANAGER_CONFIG (data->ecmc), FALSE); + + if (!g_cancellable_is_cancelled (data->cancellable)) { + ECertManagerConfig *ecmc = data->ecmc; + GSList *link; + + unload_certs (data->ecmc->priv->yourcerts_page); + unload_certs (data->ecmc->priv->contactcerts_page); + unload_certs (data->ecmc->priv->authoritycerts_page); + + for (link = data->ecerts; link; link = g_slist_next (link)) { + ECert *cert = link->data; + ECertType ct; + + if (!cert) + continue; + + ct = e_cert_get_cert_type (cert); + if (ct == data->ecmc->priv->yourcerts_page->cert_type) { + add_cert (data->ecmc->priv->yourcerts_page, g_object_ref (cert)); + } else if (ct == data->ecmc->priv->authoritycerts_page->cert_type) { + add_cert (data->ecmc->priv->authoritycerts_page, g_object_ref (cert)); + } else if (ct == data->ecmc->priv->contactcerts_page->cert_type || (ct != E_CERT_CA && ct != E_CERT_USER)) { + add_cert (data->ecmc->priv->contactcerts_page, g_object_ref (cert)); + } + } + + /* expand all three trees */ + gtk_tree_view_expand_all (ECMC_TREE_VIEW (yourcerts_page)); + gtk_tree_view_expand_all (ECMC_TREE_VIEW (contactcerts_page)); + gtk_tree_view_expand_all (ECMC_TREE_VIEW (authoritycerts_page)); + + /* Now load settings of each treeview */ + load_treeview_state (ECMC_TREE_VIEW (yourcerts_page)); + load_treeview_state (ECMC_TREE_VIEW (contactcerts_page)); + load_treeview_state (ECMC_TREE_VIEW (authoritycerts_page)); + } + + return FALSE; +} + +static gpointer +load_all_certs_thread (gpointer user_data) +{ + LoadAllCertsAsyncData *data = user_data; + CERTCertList *certList; + CERTCertListNode *node; + + g_return_val_if_fail (data != NULL, NULL); + + certList = PK11_ListCerts (PK11CertListUnique, NULL); + + for (node = CERT_LIST_HEAD (certList); + !CERT_LIST_END (node, certList) && !g_cancellable_is_cancelled (data->cancellable); + node = CERT_LIST_NEXT (node)) { + ECert *cert = e_cert_new (CERT_DupCertificate ((CERTCertificate *) node->cert)); + + data->ecerts = g_slist_prepend (data->ecerts, cert); + } + + CERT_DestroyCertList (certList); + + g_idle_add_full (G_PRIORITY_HIGH_IDLE, load_all_certs_done_idle_cb, data, load_all_certs_async_data_free); + + return NULL; +} + +static gboolean +load_all_threads_try_create_thread (gpointer user_data) +{ + LoadAllCertsAsyncData *data = user_data; + GThread *thread; + GError *error = NULL; + + g_return_val_if_fail (data != NULL, FALSE); + + if (data->tries > 10 || g_cancellable_is_cancelled (data->cancellable)) { + load_all_certs_async_data_free (data); + return FALSE; + } + + thread = g_thread_try_new (NULL, load_all_certs_thread, data, &error); + if (g_error_matches (error, G_THREAD_ERROR, G_THREAD_ERROR_AGAIN)) { + data->tries++; + + g_timeout_add (250, load_all_threads_try_create_thread, data); + } else if (!thread) { + g_warning ("%s: Failed to create thread: %s", G_STRFUNC, error ? error->message : "Unknown error"); + } else { + g_thread_unref (thread); + } + + g_clear_error (&error); + + return FALSE; +} + +static void +load_all_certs (ECertManagerConfig *ecmc) +{ + LoadAllCertsAsyncData *data; + + g_return_if_fail (E_IS_CERT_MANAGER_CONFIG (ecmc)); + + if (ecmc->priv->load_all_certs_cancellable) { + g_cancellable_cancel (ecmc->priv->load_all_certs_cancellable); + g_clear_object (&ecmc->priv->load_all_certs_cancellable); + } + + ecmc->priv->load_all_certs_cancellable = g_cancellable_new (); + + data = g_new0 (LoadAllCertsAsyncData, 1); + data->ecmc = g_object_ref (ecmc); + data->cancellable = g_object_ref (ecmc->priv->load_all_certs_cancellable); + data->ecerts = NULL; + data->tries = 0; + + load_all_threads_try_create_thread (data); +} + static void load_certs (CertPage *cp) { @@ -1741,28 +1890,9 @@ populate_ui (ECertManagerConfig *ecmc) { /* This is an idle callback. */ - ECertManagerConfigPrivate *priv = ecmc->priv; - - unload_certs (priv->yourcerts_page); - load_certs (priv->yourcerts_page); - - unload_certs (priv->contactcerts_page); - load_certs (priv->contactcerts_page); - - unload_certs (priv->authoritycerts_page); - load_certs (priv->authoritycerts_page); - + load_all_certs (ecmc); load_mail_certs (ecmc); - /* expand all three trees */ - gtk_tree_view_expand_all (ECMC_TREE_VIEW (yourcerts_page)); - gtk_tree_view_expand_all (ECMC_TREE_VIEW (contactcerts_page)); - gtk_tree_view_expand_all (ECMC_TREE_VIEW (authoritycerts_page)); - - /* Now load settings of each treeview */ - load_treeview_state (ECMC_TREE_VIEW (yourcerts_page)); - load_treeview_state (ECMC_TREE_VIEW (contactcerts_page)); - load_treeview_state (ECMC_TREE_VIEW (authoritycerts_page)); load_treeview_state (ecmc->priv->mail_tree_view); return FALSE; @@ -1898,6 +2028,11 @@ cert_manager_config_dispose (GObject *object) ecmc->priv->pref_window = NULL; } + if (ecmc->priv->load_all_certs_cancellable) { + g_cancellable_cancel (ecmc->priv->load_all_certs_cancellable); + g_clear_object (&ecmc->priv->load_all_certs_cancellable); + } + G_OBJECT_CLASS (e_cert_manager_config_parent_class)->dispose (object); }