[evolution-data-server] [IMAPx] Manage mailboxes and namespaces in CamelIMAPXStore



commit 87f27bbf0657463f6fe94aa40781dc391a182fff
Author: Milan Crha <mcrha redhat com>
Date:   Fri Apr 18 15:12:45 2014 +0200

    [IMAPx] Manage mailboxes and namespaces in CamelIMAPXStore
    
    The mailboxes and namespaces had been managed in CamelIMAPXServer,
    which was perfectly fine before the concurrent connections were added,
    because each of the connection could know only on part of the two,
    which is not enough. This could let to a failure of folder update
    (the case I face were when an empty folder had added old messages,
    where one connection new there are some messages, but the connection
    serving this update thought there are no messages, thus it didn't try
    to download anything).

 camel/providers/imapx/camel-imapx-conn-manager.c |  107 ----
 camel/providers/imapx/camel-imapx-conn-manager.h |   13 -
 camel/providers/imapx/camel-imapx-folder.c       |    4 +-
 camel/providers/imapx/camel-imapx-server.c       |  616 ++-----------------
 camel/providers/imapx/camel-imapx-server.h       |   17 -
 camel/providers/imapx/camel-imapx-store.c        |  699 +++++++++++++++++++---
 camel/providers/imapx/camel-imapx-store.h        |   35 +-
 7 files changed, 701 insertions(+), 790 deletions(-)
---
diff --git a/camel/providers/imapx/camel-imapx-conn-manager.c 
b/camel/providers/imapx/camel-imapx-conn-manager.c
index a83cacf..02a52fd 100644
--- a/camel/providers/imapx/camel-imapx-conn-manager.c
+++ b/camel/providers/imapx/camel-imapx-conn-manager.c
@@ -62,15 +62,6 @@ enum {
        PROP_STORE
 };
 
-enum {
-       MAILBOX_CREATED,
-       MAILBOX_RENAMED,
-       MAILBOX_UPDATED,
-       LAST_SIGNAL
-};
-
-static guint signals[LAST_SIGNAL];
-
 G_DEFINE_TYPE (
        CamelIMAPXConnManager,
        camel_imapx_conn_manager,
@@ -88,31 +79,6 @@ imapx_conn_mailbox_closed (CamelIMAPXServer *is,
                           CamelIMAPXMailbox *mailbox,
                           CamelIMAPXConnManager *con_man);
 
-static void
-imapx_conn_mailbox_created_cb (CamelIMAPXServer *server,
-                              CamelIMAPXMailbox *mailbox,
-                              CamelIMAPXConnManager *con_man)
-{
-       g_signal_emit (con_man, signals[MAILBOX_CREATED], 0, mailbox);
-}
-
-static void
-imapx_conn_mailbox_renamed_cb (CamelIMAPXServer *server,
-                              CamelIMAPXMailbox *mailbox,
-                              const gchar *oldname,
-                              CamelIMAPXConnManager *con_man)
-{
-       g_signal_emit (con_man, signals[MAILBOX_RENAMED], 0, mailbox, oldname);
-}
-
-static void
-imapx_conn_mailbox_updated_cb (CamelIMAPXServer *server,
-                              CamelIMAPXMailbox *mailbox,
-                              CamelIMAPXConnManager *con_man)
-{
-       g_signal_emit (con_man, signals[MAILBOX_UPDATED], 0, mailbox);
-}
-
 static ConnectionInfo *
 connection_info_new (CamelIMAPXServer *is)
 {
@@ -156,9 +122,6 @@ connection_info_unref (ConnectionInfo *cinfo)
                g_signal_handlers_disconnect_matched (cinfo->is, G_SIGNAL_MATCH_FUNC, 0, 0, NULL, 
imapx_conn_shutdown, NULL);
                g_signal_handlers_disconnect_matched (cinfo->is, G_SIGNAL_MATCH_FUNC, 0, 0, NULL, 
imapx_conn_update_select, NULL);
                g_signal_handlers_disconnect_matched (cinfo->is, G_SIGNAL_MATCH_FUNC, 0, 0, NULL, 
imapx_conn_mailbox_closed, NULL);
-               g_signal_handlers_disconnect_matched (cinfo->is, G_SIGNAL_MATCH_FUNC, 0, 0, NULL, 
imapx_conn_mailbox_created_cb, NULL);
-               g_signal_handlers_disconnect_matched (cinfo->is, G_SIGNAL_MATCH_FUNC, 0, 0, NULL, 
imapx_conn_mailbox_renamed_cb, NULL);
-               g_signal_handlers_disconnect_matched (cinfo->is, G_SIGNAL_MATCH_FUNC, 0, 0, NULL, 
imapx_conn_mailbox_updated_cb, NULL);
 
                g_mutex_clear (&cinfo->lock);
                g_object_unref (cinfo->is);
@@ -178,9 +141,6 @@ connection_info_cancel_and_unref (ConnectionInfo *cinfo)
        g_signal_handlers_disconnect_matched (cinfo->is, G_SIGNAL_MATCH_FUNC, 0, 0, NULL, 
imapx_conn_shutdown, NULL);
        g_signal_handlers_disconnect_matched (cinfo->is, G_SIGNAL_MATCH_FUNC, 0, 0, NULL, 
imapx_conn_update_select, NULL);
        g_signal_handlers_disconnect_matched (cinfo->is, G_SIGNAL_MATCH_FUNC, 0, 0, NULL, 
imapx_conn_mailbox_closed, NULL);
-       g_signal_handlers_disconnect_matched (cinfo->is, G_SIGNAL_MATCH_FUNC, 0, 0, NULL, 
imapx_conn_mailbox_created_cb, NULL);
-       g_signal_handlers_disconnect_matched (cinfo->is, G_SIGNAL_MATCH_FUNC, 0, 0, NULL, 
imapx_conn_mailbox_renamed_cb, NULL);
-       g_signal_handlers_disconnect_matched (cinfo->is, G_SIGNAL_MATCH_FUNC, 0, 0, NULL, 
imapx_conn_mailbox_updated_cb, NULL);
        camel_imapx_server_shutdown (cinfo->is);
        connection_info_unref (cinfo);
 }
@@ -456,34 +416,6 @@ camel_imapx_conn_manager_class_init (CamelIMAPXConnManagerClass *class)
                        G_PARAM_READWRITE |
                        G_PARAM_CONSTRUCT_ONLY |
                        G_PARAM_STATIC_STRINGS));
-
-       signals[MAILBOX_CREATED] = g_signal_new (
-               "mailbox-created",
-               G_OBJECT_CLASS_TYPE (class),
-               G_SIGNAL_RUN_FIRST,
-               G_STRUCT_OFFSET (CamelIMAPXConnManagerClass, mailbox_created),
-               NULL, NULL, NULL,
-               G_TYPE_NONE, 1,
-               CAMEL_TYPE_IMAPX_MAILBOX);
-
-       signals[MAILBOX_RENAMED] = g_signal_new (
-               "mailbox-renamed",
-               G_OBJECT_CLASS_TYPE (class),
-               G_SIGNAL_RUN_FIRST,
-               G_STRUCT_OFFSET (CamelIMAPXConnManagerClass, mailbox_renamed),
-               NULL, NULL, NULL,
-               G_TYPE_NONE, 2,
-               CAMEL_TYPE_IMAPX_MAILBOX,
-               G_TYPE_STRING);
-
-       signals[MAILBOX_UPDATED] = g_signal_new (
-               "mailbox-updated",
-               G_OBJECT_CLASS_TYPE (class),
-               G_SIGNAL_RUN_FIRST,
-               G_STRUCT_OFFSET (CamelIMAPXConnManagerClass, mailbox_updated),
-               NULL, NULL, NULL,
-               G_TYPE_NONE, 1,
-               CAMEL_TYPE_IMAPX_MAILBOX);
 }
 
 static void
@@ -782,18 +714,6 @@ imapx_create_new_connection_unlocked (CamelIMAPXConnManager *con_man,
                is, "mailbox-closed",
                G_CALLBACK (imapx_conn_mailbox_closed), con_man);
 
-       g_signal_connect (
-               is, "mailbox-created",
-               G_CALLBACK (imapx_conn_mailbox_created_cb), con_man);
-
-       g_signal_connect (
-               is, "mailbox-renamed",
-               G_CALLBACK (imapx_conn_mailbox_renamed_cb), con_man);
-
-       g_signal_connect (
-               is, "mailbox-updated",
-               G_CALLBACK (imapx_conn_mailbox_updated_cb), con_man);
-
        cinfo = connection_info_new (is);
 
        if (folder_name != NULL)
@@ -927,33 +847,6 @@ camel_imapx_conn_manager_update_con_info (CamelIMAPXConnManager *con_man,
        connection_info_unref (cinfo);
 }
 
-CamelIMAPXMailbox *
-camel_imapx_conn_manager_ref_mailbox (CamelIMAPXConnManager *con_man,
-                                     const gchar *mailbox_name)
-{
-       CamelIMAPXMailbox *mailbox = NULL;
-       GList *iter;
-
-       g_return_val_if_fail (CAMEL_IS_IMAPX_CONN_MANAGER (con_man), NULL);
-       g_return_val_if_fail (mailbox_name != NULL, NULL);
-
-       CON_READ_LOCK (con_man);
-
-       for (iter = con_man->priv->connections; iter != NULL; iter = g_list_next (iter)) {
-               ConnectionInfo *candidate = iter->data;
-
-               if (candidate->is) {
-                       mailbox = camel_imapx_server_ref_mailbox (candidate->is, mailbox_name);
-                       if (mailbox)
-                               break;
-               }
-       }
-
-       CON_READ_UNLOCK (con_man);
-
-       return mailbox;
-}
-
 void
 camel_imapx_conn_manager_close_connections (CamelIMAPXConnManager *con_man)
 {
diff --git a/camel/providers/imapx/camel-imapx-conn-manager.h 
b/camel/providers/imapx/camel-imapx-conn-manager.h
index 0b5e466..fc93a11 100644
--- a/camel/providers/imapx/camel-imapx-conn-manager.h
+++ b/camel/providers/imapx/camel-imapx-conn-manager.h
@@ -56,15 +56,6 @@ struct _CamelIMAPXConnManager {
 
 struct _CamelIMAPXConnManagerClass {
        GObjectClass parent_class;
-
-       /* Signals */
-       void            (*mailbox_created)      (CamelIMAPXConnManager *con_man,
-                                                CamelIMAPXMailbox *mailbox);
-       void            (*mailbox_renamed)      (CamelIMAPXConnManager *con_man,
-                                                CamelIMAPXMailbox *mailbox,
-                                                const gchar *oldname);
-       void            (*mailbox_updated)      (CamelIMAPXConnManager *con_man,
-                                                CamelIMAPXMailbox *mailbox);
 };
 
 GType          camel_imapx_conn_manager_get_type (void);
@@ -87,10 +78,6 @@ void         camel_imapx_conn_manager_update_con_info
                                                (CamelIMAPXConnManager *con_man,
                                                 CamelIMAPXServer *server,
                                                 const gchar *folder_name);
-CamelIMAPXMailbox *
-               camel_imapx_conn_manager_ref_mailbox
-                                               (CamelIMAPXConnManager *con_man,
-                                                const gchar *mailbox_name);
 
 G_END_DECLS
 
diff --git a/camel/providers/imapx/camel-imapx-folder.c b/camel/providers/imapx/camel-imapx-folder.c
index b96f314..a02eabc 100644
--- a/camel/providers/imapx/camel-imapx-folder.c
+++ b/camel/providers/imapx/camel-imapx-folder.c
@@ -1540,7 +1540,7 @@ camel_imapx_folder_list_mailbox (CamelIMAPXFolder *folder,
        if (server == NULL)
                goto exit;
 
-       mailbox = camel_imapx_server_ref_mailbox (server, mailbox_name);
+       mailbox = camel_imapx_store_ref_mailbox (imapx_store, mailbox_name);
        if (mailbox != NULL) {
                camel_imapx_folder_set_mailbox (folder, mailbox);
                goto exit;
@@ -1562,7 +1562,7 @@ camel_imapx_folder_list_mailbox (CamelIMAPXFolder *folder,
 
        /* This might still return NULL if the mailbox has a
         * /NonExistent attribute.  Otherwise this should work. */
-       mailbox = camel_imapx_server_ref_mailbox (server, mailbox_name);
+       mailbox = camel_imapx_store_ref_mailbox (imapx_store, mailbox_name);
        if (mailbox != NULL) {
                camel_imapx_folder_set_mailbox (folder, mailbox);
        } else {
diff --git a/camel/providers/imapx/camel-imapx-server.c b/camel/providers/imapx/camel-imapx-server.c
index 49ac83e..45d0cdc 100644
--- a/camel/providers/imapx/camel-imapx-server.c
+++ b/camel/providers/imapx/camel-imapx-server.c
@@ -357,12 +357,6 @@ struct _CamelIMAPXServerPrivate {
        GSource *inactivity_timeout;
        GMutex inactivity_timeout_lock;
 
-       CamelIMAPXNamespaceResponse *namespaces;
-       GMutex namespaces_lock;
-
-       GHashTable *mailboxes;
-       GMutex mailboxes_lock;
-
        /* Info on currently selected folder. */
        GMutex select_lock;
        GWeakRef select_mailbox;
@@ -410,16 +404,12 @@ struct _CamelIMAPXServerPrivate {
 
 enum {
        PROP_0,
-       PROP_NAMESPACES,
        PROP_STORE
 };
 
 enum {
        MAILBOX_SELECT,
        MAILBOX_CLOSED,
-       MAILBOX_CREATED,
-       MAILBOX_RENAMED,
-       MAILBOX_UPDATED,
        SHUTDOWN,
        LAST_SIGNAL
 };
@@ -1170,231 +1160,6 @@ imapx_server_reset_inactivity_timer (CamelIMAPXServer *is)
        g_mutex_unlock (&is->priv->inactivity_timeout_lock);
 }
 
-static void
-imapx_server_add_mailbox_unlocked (CamelIMAPXServer *is,
-                                   CamelIMAPXMailbox *mailbox)
-{
-       const gchar *mailbox_name;
-
-       /* Acquire "mailboxes_lock" before calling. */
-
-       mailbox_name = camel_imapx_mailbox_get_name (mailbox);
-       g_return_if_fail (mailbox_name != NULL);
-
-       /* Use g_hash_table_replace() here instead of g_hash_table_insert().
-        * The hash table key is owned by the hash table value, so if we're
-        * replacing an existing table item we want to replace both the key
-        * and value to avoid data corruption. */
-       g_hash_table_replace (
-               is->priv->mailboxes,
-               (gpointer) mailbox_name,
-               g_object_ref (mailbox));
-}
-
-static gboolean
-imapx_server_remove_mailbox_unlocked (CamelIMAPXServer *is,
-                                      CamelIMAPXMailbox *mailbox)
-{
-       const gchar *mailbox_name;
-
-       /* Acquire "mailboxes_lock" before calling. */
-
-       mailbox_name = camel_imapx_mailbox_get_name (mailbox);
-       g_return_val_if_fail (mailbox_name != NULL, FALSE);
-
-       return g_hash_table_remove (is->priv->mailboxes, mailbox_name);
-}
-
-static CamelIMAPXMailbox *
-imapx_server_ref_mailbox_unlocked (CamelIMAPXServer *is,
-                                   const gchar *mailbox_name)
-{
-       CamelIMAPXMailbox *mailbox;
-
-       /* Acquire "mailboxes_lock" before calling. */
-
-       g_return_val_if_fail (mailbox_name != NULL, NULL);
-
-       /* The INBOX mailbox is case-insensitive. */
-       if (g_ascii_strcasecmp (mailbox_name, "INBOX") == 0)
-               mailbox_name = "INBOX";
-
-       mailbox = g_hash_table_lookup (is->priv->mailboxes, mailbox_name);
-
-       /* Remove non-existent mailboxes as we find them. */
-       if (mailbox != NULL && !camel_imapx_mailbox_exists (mailbox)) {
-               imapx_server_remove_mailbox_unlocked (is, mailbox);
-               mailbox = NULL;
-       }
-
-       if (mailbox != NULL)
-               g_object_ref (mailbox);
-
-       return mailbox;
-}
-
-static GList *
-imapx_server_list_mailboxes_unlocked (CamelIMAPXServer *is,
-                                      CamelIMAPXNamespace *namespace,
-                                      const gchar *pattern)
-{
-       GHashTableIter iter;
-       GList *list = NULL;
-       gpointer value;
-
-       /* Acquire "mailboxes_lock" before calling. */
-
-       if (pattern == NULL)
-               pattern = "*";
-
-       g_hash_table_iter_init (&iter, is->priv->mailboxes);
-
-       while (g_hash_table_iter_next (&iter, NULL, &value)) {
-               CamelIMAPXMailbox *mailbox;
-               CamelIMAPXNamespace *mailbox_ns;
-
-               mailbox = CAMEL_IMAPX_MAILBOX (value);
-               mailbox_ns = camel_imapx_mailbox_get_namespace (mailbox);
-
-               if (!camel_imapx_mailbox_exists (mailbox))
-                       continue;
-
-               if (!camel_imapx_namespace_equal (namespace, mailbox_ns))
-                       continue;
-
-               if (!camel_imapx_mailbox_matches (mailbox, pattern))
-                       continue;
-
-               list = g_list_prepend (list, g_object_ref (mailbox));
-       }
-
-       /* Sort the list by mailbox name. */
-       return g_list_sort (list, (GCompareFunc) camel_imapx_mailbox_compare);
-}
-
-static CamelIMAPXMailbox *
-imapx_server_create_mailbox_unlocked (CamelIMAPXServer *is,
-                                      CamelIMAPXListResponse *response)
-{
-       CamelIMAPXNamespaceResponse *namespace_response;
-       CamelIMAPXNamespace *namespace;
-       CamelIMAPXMailbox *mailbox = NULL;
-       const gchar *mailbox_name;
-       gchar separator;
-
-       /* Acquire "mailboxes_lock" before calling. */
-
-       namespace_response = camel_imapx_server_ref_namespaces (is);
-       g_return_val_if_fail (namespace_response != NULL, FALSE);
-
-       mailbox_name = camel_imapx_list_response_get_mailbox_name (response);
-       separator = camel_imapx_list_response_get_separator (response);
-
-       namespace = camel_imapx_namespace_response_lookup (
-               namespace_response, mailbox_name, separator);
-
-       if (namespace != NULL) {
-               mailbox = camel_imapx_mailbox_new (response, namespace);
-               imapx_server_add_mailbox_unlocked (is, mailbox);
-               g_object_unref (namespace);
-
-       /* XXX Slight hack, mainly for Courier servers.  If INBOX does
-        *     not match any defined namespace, just create one for it
-        *     on the fly.  The namespace response won't know about it. */
-       } else if (camel_imapx_mailbox_is_inbox (mailbox_name)) {
-               namespace = camel_imapx_namespace_new (
-                       CAMEL_IMAPX_NAMESPACE_PERSONAL, "", separator);
-               mailbox = camel_imapx_mailbox_new (response, namespace);
-               imapx_server_add_mailbox_unlocked (is, mailbox);
-               g_object_unref (namespace);
-
-       } else {
-               g_warning (
-                       "%s: No matching namespace for \"%c\" %s",
-                       G_STRFUNC, separator, mailbox_name);
-       }
-
-       g_object_unref (namespace_response);
-
-       return mailbox;
-}
-
-static CamelIMAPXMailbox *
-imapx_server_rename_mailbox_unlocked (CamelIMAPXServer *is,
-                                      const gchar *old_mailbox_name,
-                                      const gchar *new_mailbox_name)
-{
-       CamelIMAPXMailbox *old_mailbox;
-       CamelIMAPXMailbox *new_mailbox;
-       CamelIMAPXNamespace *namespace;
-       gsize old_mailbox_name_length;
-       GList *list, *link;
-       gchar separator;
-       gchar *pattern;
-
-       /* Acquire "mailboxes_lock" before calling. */
-
-       g_return_val_if_fail (old_mailbox_name != NULL, NULL);
-       g_return_val_if_fail (new_mailbox_name != NULL, NULL);
-
-       old_mailbox = imapx_server_ref_mailbox_unlocked (is, old_mailbox_name);
-       if (old_mailbox == NULL)
-               return NULL;
-
-       old_mailbox_name_length = strlen (old_mailbox_name);
-       namespace = camel_imapx_mailbox_get_namespace (old_mailbox);
-       separator = camel_imapx_mailbox_get_separator (old_mailbox);
-
-       new_mailbox = camel_imapx_mailbox_clone (old_mailbox, new_mailbox_name);
-
-       /* Add the new mailbox, remove the old mailbox.
-        * Note we still have a reference on the old mailbox. */
-       imapx_server_add_mailbox_unlocked (is, new_mailbox);
-       imapx_server_remove_mailbox_unlocked (is, old_mailbox);
-
-       /* Rename any child mailboxes. */
-
-       pattern = g_strdup_printf ("%s%c*", old_mailbox_name, separator);
-       list = imapx_server_list_mailboxes_unlocked (is, namespace, pattern);
-
-       for (link = list; link != NULL; link = g_list_next (link)) {
-               CamelIMAPXMailbox *old_child;
-               CamelIMAPXMailbox *new_child;
-               const gchar *old_child_name;
-               gchar *new_child_name;
-
-               old_child = CAMEL_IMAPX_MAILBOX (link->data);
-               old_child_name = camel_imapx_mailbox_get_name (old_child);
-
-               /* Sanity checks. */
-               g_warn_if_fail (
-                       old_child_name != NULL &&
-                       strlen (old_child_name) > old_mailbox_name_length &&
-                       old_child_name[old_mailbox_name_length] == separator);
-
-               new_child_name = g_strconcat (
-                       new_mailbox_name,
-                       old_child_name + old_mailbox_name_length, NULL);
-               new_child = camel_imapx_mailbox_clone (
-                       old_child, new_child_name);
-
-               /* Add the new mailbox, remove the old mailbox.
-                * Note we still have a reference on the old mailbox. */
-               imapx_server_add_mailbox_unlocked (is, new_child);
-               imapx_server_remove_mailbox_unlocked (is, old_child);
-
-               g_object_unref (new_child);
-               g_free (new_child_name);
-       }
-
-       g_list_free_full (list, (GDestroyNotify) g_object_unref);
-       g_free (pattern);
-
-       g_object_unref (old_mailbox);
-
-       return new_mailbox;
-}
-
 /* Must hold QUEUE_LOCK */
 static void
 imapx_command_start (CamelIMAPXServer *is,
@@ -2253,6 +2018,7 @@ imapx_untagged_namespace (CamelIMAPXServer *is,
                           GError **error)
 {
        CamelIMAPXNamespaceResponse *response;
+       CamelIMAPXStore *imapx_store;
 
        g_return_val_if_fail (CAMEL_IS_IMAPX_SERVER (is), FALSE);
 
@@ -2262,13 +2028,10 @@ imapx_untagged_namespace (CamelIMAPXServer *is,
        if (response == NULL)
                return FALSE;
 
-       g_mutex_lock (&is->priv->namespaces_lock);
-
-       g_clear_object (&is->priv->namespaces);
-       is->priv->namespaces = g_object_ref (response);
-
-       g_mutex_unlock (&is->priv->namespaces_lock);
+       imapx_store = camel_imapx_server_ref_store (is);
+       camel_imapx_store_set_namespaces (imapx_store, response);
 
+       g_clear_object (&imapx_store);
        g_object_unref (response);
 
        return TRUE;
@@ -2677,9 +2440,8 @@ imapx_untagged_lsub (CamelIMAPXServer *is,
                      GError **error)
 {
        CamelIMAPXListResponse *response;
-       CamelIMAPXMailbox *mailbox;
+       CamelIMAPXStore *imapx_store;
        const gchar *mailbox_name;
-       gboolean emit_mailbox_updated = FALSE;
        gchar separator;
 
        g_return_val_if_fail (CAMEL_IS_IMAPX_SERVER (is), FALSE);
@@ -2700,36 +2462,10 @@ imapx_untagged_lsub (CamelIMAPXServer *is,
        if (camel_imapx_mailbox_is_inbox (mailbox_name))
                is->priv->inbox_separator = separator;
 
-       /* Fabricate a CamelIMAPXNamespaceResponse if the server lacks the
-        * NAMESPACE capability and this is the first LIST / LSUB response. */
-       if (CAMEL_IMAPX_LACK_CAPABILITY (is->cinfo, NAMESPACE)) {
-               g_mutex_lock (&is->priv->namespaces_lock);
-               if (is->priv->namespaces == NULL) {
-                       is->priv->namespaces =
-                               camel_imapx_namespace_response_faux_new (
-                               response);
-               }
-               g_mutex_unlock (&is->priv->namespaces_lock);
-       }
-
-       /* Update a corresponding CamelIMAPXMailbox.
-        *
-        * Note, don't create the CamelIMAPXMailbox like we do for a LIST
-        * response.  We always issue LIST before LSUB on a mailbox name,
-        * so if we don't already have a CamelIMAPXMailbox instance then
-        * this is a subscription on a non-existent mailbox.  Skip it. */
-       g_mutex_lock (&is->priv->mailboxes_lock);
-       mailbox = imapx_server_ref_mailbox_unlocked (is, mailbox_name);
-       if (mailbox != NULL) {
-               camel_imapx_mailbox_handle_lsub_response (mailbox, response);
-               emit_mailbox_updated = TRUE;
-       }
-       g_mutex_unlock (&is->priv->mailboxes_lock);
-
-       if (emit_mailbox_updated)
-               g_signal_emit (is, signals[MAILBOX_UPDATED], 0, mailbox);
+       imapx_store = camel_imapx_server_ref_store (is);
+       camel_imapx_store_handle_lsub_response (imapx_store, is, response);
 
-       g_clear_object (&mailbox);
+       g_clear_object (&imapx_store);
        g_clear_object (&response);
 
        return TRUE;
@@ -2742,12 +2478,8 @@ imapx_untagged_list (CamelIMAPXServer *is,
                      GError **error)
 {
        CamelIMAPXListResponse *response;
-       CamelIMAPXMailbox *mailbox = NULL;
-       gboolean emit_mailbox_created = FALSE;
-       gboolean emit_mailbox_renamed = FALSE;
-       gboolean emit_mailbox_updated = FALSE;
+       CamelIMAPXStore *imapx_store;
        const gchar *mailbox_name;
-       const gchar *old_mailbox_name;
        gchar separator;
 
        g_return_val_if_fail (CAMEL_IS_IMAPX_SERVER (is), FALSE);
@@ -2764,52 +2496,10 @@ imapx_untagged_list (CamelIMAPXServer *is,
        if (camel_imapx_mailbox_is_inbox (mailbox_name))
                is->priv->inbox_separator = separator;
 
-       /* Check for mailbox rename. */
-       old_mailbox_name = camel_imapx_list_response_get_oldname (response);
-
-       /* Fabricate a CamelIMAPXNamespaceResponse if the server lacks the
-        * NAMESPACE capability and this is the first LIST / LSUB response. */
-       if (CAMEL_IMAPX_LACK_CAPABILITY (is->cinfo, NAMESPACE)) {
-               g_mutex_lock (&is->priv->namespaces_lock);
-               if (is->priv->namespaces == NULL) {
-                       is->priv->namespaces =
-                               camel_imapx_namespace_response_faux_new (
-                               response);
-               }
-               g_mutex_unlock (&is->priv->namespaces_lock);
-       }
-
-       /* Create, rename, or update a corresponding CamelIMAPXMailbox. */
-       g_mutex_lock (&is->priv->mailboxes_lock);
-       if (old_mailbox_name != NULL) {
-               mailbox = imapx_server_rename_mailbox_unlocked (
-                       is, old_mailbox_name, mailbox_name);
-               emit_mailbox_renamed = (mailbox != NULL);
-       }
-       if (mailbox == NULL) {
-               mailbox = imapx_server_ref_mailbox_unlocked (is, mailbox_name);
-               emit_mailbox_updated = (mailbox != NULL);
-       }
-       if (mailbox == NULL) {
-               mailbox = imapx_server_create_mailbox_unlocked (is, response);
-               emit_mailbox_created = (mailbox != NULL);
-       } else {
-               camel_imapx_mailbox_handle_list_response (mailbox, response);
-       }
-       g_mutex_unlock (&is->priv->mailboxes_lock);
-
-       if (emit_mailbox_created)
-               g_signal_emit (is, signals[MAILBOX_CREATED], 0, mailbox);
-
-       if (emit_mailbox_renamed)
-               g_signal_emit (
-                       is, signals[MAILBOX_RENAMED], 0,
-                       mailbox, old_mailbox_name);
-
-       if (emit_mailbox_updated)
-               g_signal_emit (is, signals[MAILBOX_UPDATED], 0, mailbox);
+       imapx_store = camel_imapx_server_ref_store (is);
+       camel_imapx_store_handle_list_response (imapx_store, is, response);
 
-       g_clear_object (&mailbox);
+       g_clear_object (&imapx_store);
        g_clear_object (&response);
 
        return TRUE;
@@ -2855,6 +2545,7 @@ imapx_untagged_quotaroot (CamelIMAPXServer *is,
                           GCancellable *cancellable,
                           GError **error)
 {
+       CamelIMAPXStore *imapx_store;
        CamelIMAPXMailbox *mailbox;
        gchar *mailbox_name = NULL;
        gchar **quota_roots = NULL;
@@ -2872,7 +2563,9 @@ imapx_untagged_quotaroot (CamelIMAPXServer *is,
        if (!success)
                return FALSE;
 
-       mailbox = camel_imapx_server_ref_mailbox (is, mailbox_name);
+       imapx_store = camel_imapx_server_ref_store (is);
+       mailbox = camel_imapx_store_ref_mailbox (imapx_store, mailbox_name);
+       g_clear_object (&imapx_store);
 
        if (mailbox != NULL) {
                camel_imapx_mailbox_set_quota_roots (
@@ -2982,6 +2675,7 @@ imapx_untagged_status (CamelIMAPXServer *is,
                        GError **error)
 {
        CamelIMAPXStatusResponse *response;
+       CamelIMAPXStore *imapx_store;
        CamelIMAPXMailbox *mailbox;
        const gchar *mailbox_name;
 
@@ -2995,14 +2689,16 @@ imapx_untagged_status (CamelIMAPXServer *is,
 
        mailbox_name = camel_imapx_status_response_get_mailbox_name (response);
 
-       mailbox = camel_imapx_server_ref_mailbox (is, mailbox_name);
+       imapx_store = camel_imapx_server_ref_store (is);
+       mailbox = camel_imapx_store_ref_mailbox (imapx_store, mailbox_name);
 
        if (mailbox != NULL) {
                camel_imapx_mailbox_handle_status_response (mailbox, response);
-               g_signal_emit (is, signals[MAILBOX_UPDATED], 0, mailbox);
+               camel_imapx_store_emit_mailbox_updated (imapx_store, mailbox);
                g_object_unref (mailbox);
        }
 
+       g_clear_object (&imapx_store);
        g_object_unref (response);
 
        return TRUE;
@@ -7024,11 +6720,17 @@ imapx_command_delete_mailbox_done (CamelIMAPXServer *is,
                camel_imapx_job_take_error (job, local_error);
 
        } else {
+               CamelIMAPXStore *imapx_store;
+
                /* Perform the same processing as imapx_untagged_list()
                 * would if the server notified us of a deleted mailbox. */
 
+               imapx_store = camel_imapx_server_ref_store (is);
+
                camel_imapx_mailbox_deleted (data->mailbox);
-               g_signal_emit (is, signals[MAILBOX_UPDATED], 0, data->mailbox);
+               camel_imapx_store_emit_mailbox_updated (imapx_store, data->mailbox);
+
+               g_clear_object (&imapx_store);
        }
 
        imapx_unregister_job (is, job);
@@ -7040,6 +6742,7 @@ imapx_job_delete_mailbox_start (CamelIMAPXJob *job,
                                 GCancellable *cancellable,
                                 GError **error)
 {
+       CamelIMAPXStore *imapx_store;
        CamelIMAPXCommand *ic;
        MailboxData *data;
        CamelIMAPXMailbox *inbox;
@@ -7047,8 +6750,10 @@ imapx_job_delete_mailbox_start (CamelIMAPXJob *job,
        data = camel_imapx_job_get_data (job);
        g_return_val_if_fail (data != NULL, FALSE);
 
+       imapx_store = camel_imapx_server_ref_store (is);
        /* Keep going, even if this returns NULL. */
-       inbox = camel_imapx_server_ref_mailbox (is, "INBOX");
+       inbox = camel_imapx_store_ref_mailbox (imapx_store, "INBOX");
+       g_clear_object (&imapx_store);
 
        /* Make sure the to-be-deleted folder is not
         * selected by selecting INBOX for this operation. */
@@ -7091,31 +6796,15 @@ imapx_command_rename_mailbox_done (CamelIMAPXServer *is,
                camel_imapx_job_take_error (job, local_error);
 
        } else {
-               CamelIMAPXMailbox *old_mailbox;
-               CamelIMAPXMailbox *new_mailbox;
-               const gchar *old_mailbox_name;
-               const gchar *new_mailbox_name;
+               CamelIMAPXStore *imapx_store;
 
                /* Perform the same processing as imapx_untagged_list()
                 * would if the server notified us of a renamed mailbox. */
 
-               old_mailbox = data->mailbox;
-               new_mailbox_name = data->mailbox_name;
-
-               old_mailbox_name = camel_imapx_mailbox_get_name (old_mailbox);
-
-               g_mutex_lock (&is->priv->mailboxes_lock);
-               new_mailbox = imapx_server_rename_mailbox_unlocked (
-                       is, old_mailbox_name, new_mailbox_name);
-               g_mutex_unlock (&is->priv->mailboxes_lock);
+               imapx_store = camel_imapx_server_ref_store (is);
+               camel_imapx_store_handle_mailbox_rename (imapx_store, data->mailbox, data->mailbox_name);
 
-               g_warn_if_fail (new_mailbox != NULL);
-
-               g_signal_emit (
-                       is, signals[MAILBOX_RENAMED], 0,
-                       new_mailbox, old_mailbox_name);
-
-               g_clear_object (&new_mailbox);
+               g_clear_object (&imapx_store);
        }
 
        imapx_unregister_job (is, job);
@@ -7128,13 +6817,16 @@ imapx_job_rename_mailbox_start (CamelIMAPXJob *job,
                                 GError **error)
 {
        CamelIMAPXCommand *ic;
+       CamelIMAPXStore *imapx_store;
        CamelIMAPXMailbox *inbox;
        MailboxData *data;
 
        data = camel_imapx_job_get_data (job);
        g_return_val_if_fail (data != NULL, FALSE);
 
-       inbox = camel_imapx_server_ref_mailbox (is, "INBOX");
+       imapx_store = camel_imapx_server_ref_store (is);
+       inbox = camel_imapx_store_ref_mailbox (imapx_store, "INBOX");
+       g_clear_object (&imapx_store);
        g_return_val_if_fail (inbox != NULL, FALSE);
 
        camel_imapx_job_set_mailbox (job, inbox);
@@ -7178,11 +6870,17 @@ imapx_command_subscribe_mailbox_done (CamelIMAPXServer *is,
                camel_imapx_job_take_error (job, local_error);
 
        } else {
+               CamelIMAPXStore *imapx_store;
+
                /* Perform the same processing as imapx_untagged_list()
                 * would if the server notified us of a subscription. */
 
+               imapx_store = camel_imapx_server_ref_store (is);
+
                camel_imapx_mailbox_subscribed (data->mailbox);
-               g_signal_emit (is, signals[MAILBOX_UPDATED], 0, data->mailbox);
+               camel_imapx_store_emit_mailbox_updated (imapx_store, data->mailbox);
+
+               g_clear_object (&imapx_store);
        }
 
        imapx_unregister_job (is, job);
@@ -7238,11 +6936,17 @@ imapx_command_unsubscribe_mailbox_done (CamelIMAPXServer *is,
                camel_imapx_job_take_error (job, local_error);
 
        } else {
+               CamelIMAPXStore *imapx_store;
+
                /* Perform the same processing as imapx_untagged_list()
                 * would if the server notified us of an unsubscription. */
 
+               imapx_store = camel_imapx_server_ref_store (is);
+
                camel_imapx_mailbox_unsubscribed (data->mailbox);
-               g_signal_emit (is, signals[MAILBOX_UPDATED], 0, data->mailbox);
+               camel_imapx_store_emit_mailbox_updated (imapx_store, data->mailbox);
+
+               g_clear_object (&imapx_store);
        }
 
        imapx_unregister_job (is, job);
@@ -7936,13 +7640,6 @@ imapx_server_get_property (GObject *object,
                            GParamSpec *pspec)
 {
        switch (property_id) {
-               case PROP_NAMESPACES:
-                       g_value_take_object (
-                               value,
-                               camel_imapx_server_ref_namespaces (
-                               CAMEL_IMAPX_SERVER (object)));
-                       return;
-
                case PROP_STORE:
                        g_value_take_object (
                                value,
@@ -7987,9 +7684,6 @@ imapx_server_dispose (GObject *object)
 #if GLIB_CHECK_VERSION(2,39,0)
        g_clear_object (&server->priv->subprocess);
 #endif
-       g_clear_object (&server->priv->namespaces);
-
-       g_hash_table_remove_all (server->priv->mailboxes);
 
        /* Chain up to parent's dispose() method. */
        G_OBJECT_CLASS (camel_imapx_server_parent_class)->dispose (object);
@@ -8025,11 +7719,6 @@ imapx_server_finalize (GObject *object)
                g_source_unref (is->priv->inactivity_timeout);
        g_mutex_clear (&is->priv->inactivity_timeout_lock);
 
-       g_mutex_clear (&is->priv->namespaces_lock);
-
-       g_hash_table_destroy (is->priv->mailboxes);
-       g_mutex_clear (&is->priv->mailboxes_lock);
-
        g_free (is->priv->status_data_items);
        g_free (is->priv->list_return_opts);
 
@@ -8093,40 +7782,6 @@ imapx_server_mailbox_closed (CamelIMAPXServer *is,
 }
 
 static void
-imapx_server_mailbox_created (CamelIMAPXServer *is,
-                              CamelIMAPXMailbox *mailbox)
-{
-       e (
-               is->tagprefix,
-               "%s::mailbox-created (\"%s\")\n",
-               G_OBJECT_TYPE_NAME (is),
-               camel_imapx_mailbox_get_name (mailbox));
-}
-
-static void
-imapx_server_mailbox_renamed (CamelIMAPXServer *is,
-                              CamelIMAPXMailbox *mailbox,
-                              const gchar *oldname)
-{
-       e (
-               is->tagprefix,
-               "%s::mailbox-renamed (\"%s\" -> \"%s\")\n",
-               G_OBJECT_TYPE_NAME (is), oldname,
-               camel_imapx_mailbox_get_name (mailbox));
-}
-
-static void
-imapx_server_mailbox_updated (CamelIMAPXServer *is,
-                              CamelIMAPXMailbox *mailbox)
-{
-       e (
-               is->tagprefix,
-               "%s::mailbox-updated (\"%s\")\n",
-               G_OBJECT_TYPE_NAME (is),
-               camel_imapx_mailbox_get_name (mailbox));
-}
-
-static void
 camel_imapx_server_class_init (CamelIMAPXServerClass *class)
 {
        GObjectClass *object_class;
@@ -8142,20 +7797,6 @@ camel_imapx_server_class_init (CamelIMAPXServerClass *class)
 
        class->mailbox_select = imapx_server_mailbox_select;
        class->mailbox_closed = imapx_server_mailbox_closed;
-       class->mailbox_created = imapx_server_mailbox_created;
-       class->mailbox_renamed = imapx_server_mailbox_renamed;
-       class->mailbox_updated = imapx_server_mailbox_updated;
-
-       g_object_class_install_property (
-               object_class,
-               PROP_NAMESPACES,
-               g_param_spec_object (
-                       "namespaces",
-                       "Namespaces",
-                       "Known IMAP namespaces",
-                       CAMEL_TYPE_IMAPX_NAMESPACE_RESPONSE,
-                       G_PARAM_READABLE |
-                       G_PARAM_STATIC_STRINGS));
 
        g_object_class_install_property (
                object_class,
@@ -8187,34 +7828,6 @@ camel_imapx_server_class_init (CamelIMAPXServerClass *class)
                G_TYPE_NONE, 1,
                CAMEL_TYPE_IMAPX_MAILBOX);
 
-       signals[MAILBOX_CREATED] = g_signal_new (
-               "mailbox-created",
-               G_OBJECT_CLASS_TYPE (class),
-               G_SIGNAL_RUN_FIRST,
-               G_STRUCT_OFFSET (CamelIMAPXServerClass, mailbox_created),
-               NULL, NULL, NULL,
-               G_TYPE_NONE, 1,
-               CAMEL_TYPE_IMAPX_MAILBOX);
-
-       signals[MAILBOX_RENAMED] = g_signal_new (
-               "mailbox-renamed",
-               G_OBJECT_CLASS_TYPE (class),
-               G_SIGNAL_RUN_FIRST,
-               G_STRUCT_OFFSET (CamelIMAPXServerClass, mailbox_renamed),
-               NULL, NULL, NULL,
-               G_TYPE_NONE, 2,
-               CAMEL_TYPE_IMAPX_MAILBOX,
-               G_TYPE_STRING);
-
-       signals[MAILBOX_UPDATED] = g_signal_new (
-               "mailbox-updated",
-               G_OBJECT_CLASS_TYPE (class),
-               G_SIGNAL_RUN_FIRST,
-               G_STRUCT_OFFSET (CamelIMAPXServerClass, mailbox_updated),
-               NULL, NULL, NULL,
-               G_TYPE_NONE, 1,
-               CAMEL_TYPE_IMAPX_MAILBOX);
-
        /**
         * CamelIMAPXServer::shutdown
         * @server: the #CamelIMAPXServer which emitted the signal
@@ -8234,25 +7847,14 @@ camel_imapx_server_class_init (CamelIMAPXServerClass *class)
 static void
 camel_imapx_server_init (CamelIMAPXServer *is)
 {
-       GHashTable *mailboxes;
        GMainContext *main_context;
 
-       /* Hash table key is owned by the CamelIMAPXMailbox. */
-       mailboxes = g_hash_table_new_full (
-               (GHashFunc) g_str_hash,
-               (GEqualFunc) g_str_equal,
-               (GDestroyNotify) NULL,
-               (GDestroyNotify) g_object_unref);
-
        is->priv = CAMEL_IMAPX_SERVER_GET_PRIVATE (is);
 
        is->priv->untagged_handlers = create_initial_untagged_handler_table ();
-       is->priv->mailboxes = mailboxes;
 
        g_mutex_init (&is->priv->stream_lock);
        g_mutex_init (&is->priv->inactivity_timeout_lock);
-       g_mutex_init (&is->priv->namespaces_lock);
-       g_mutex_init (&is->priv->mailboxes_lock);
        g_mutex_init (&is->priv->select_lock);
        g_mutex_init (&is->priv->search_results_lock);
        g_mutex_init (&is->priv->known_alerts_lock);
@@ -8398,72 +8000,6 @@ camel_imapx_server_ref_output_stream (CamelIMAPXServer *is)
 }
 
 /**
- * camel_imapx_server_ref_namespaces:
- * @is: a #CamelIMAPXServer
- *
- * Returns the #CamelIMAPXNamespaceResponse for @is.  This is obtained
- * during the connection phase if the IMAP server lists the "NAMESPACE"
- * keyword in its CAPABILITY response, or else is fabricated from the
- * first LIST response.
- *
- * The returned #CamelIMAPXNamespaceResponse is reference for thread-safety
- * and must be unreferenced with g_object_unref() when finished with it.
- *
- * Returns: a #CamelIMAPXNamespaceResponse
- *
- * Since: 3.12
- **/
-CamelIMAPXNamespaceResponse *
-camel_imapx_server_ref_namespaces (CamelIMAPXServer *is)
-{
-       CamelIMAPXNamespaceResponse *namespaces = NULL;
-
-       g_return_val_if_fail (CAMEL_IS_IMAPX_SERVER (is), NULL);
-
-       g_mutex_lock (&is->priv->namespaces_lock);
-
-       if (is->priv->namespaces != NULL)
-               namespaces = g_object_ref (is->priv->namespaces);
-
-       g_mutex_unlock (&is->priv->namespaces_lock);
-
-       return namespaces;
-}
-
-/**
- * camel_imapx_server_ref_mailbox:
- * @is: a #CamelIMAPXServer
- * @mailbox_name: a mailbox name
- *
- * Looks up a #CamelMailbox by its name.  If no match is found, the function
- * returns %NULL.
- *
- * The returned #CamelIMAPXMailbox is referenced for thread-safety and
- * should be unreferenced with g_object_unref() when finished with it.
- *
- * Returns: a #CamelIMAPXMailbox, or %NULL
- *
- * Since: 3.12
- **/
-CamelIMAPXMailbox *
-camel_imapx_server_ref_mailbox (CamelIMAPXServer *is,
-                                const gchar *mailbox_name)
-{
-       CamelIMAPXMailbox *mailbox;
-
-       g_return_val_if_fail (CAMEL_IS_IMAPX_SERVER (is), NULL);
-       g_return_val_if_fail (mailbox_name != NULL, NULL);
-
-       g_mutex_lock (&is->priv->mailboxes_lock);
-
-       mailbox = imapx_server_ref_mailbox_unlocked (is, mailbox_name);
-
-       g_mutex_unlock (&is->priv->mailboxes_lock);
-
-       return mailbox;
-}
-
-/**
  * camel_imapx_server_ref_selected:
  * @is: a #CamelIMAPXServer
  *
@@ -8499,50 +8035,6 @@ camel_imapx_server_ref_selected (CamelIMAPXServer *is)
        return mailbox;
 }
 
-/**
- * camel_imapx_server_list_mailboxes:
- * @is: a #CamelIMAPXServer
- * @namespace_: a #CamelIMAPXNamespace
- * @pattern: mailbox name with possible wildcards, or %NULL
- *
- * Returns a list of #CamelIMAPXMailbox instances which match @namespace and
- * @pattern.  The @pattern may contain wildcard characters '*' and '%', which
- * are interpreted similar to the IMAP LIST command.  A %NULL @pattern lists
- * all mailboxes in @namespace; equivalent to passing "*".
- *
- * The mailboxes returned in the list are referenced for thread-safety.
- * They must each be unreferenced with g_object_unref() when finished with
- * them.  Free the returned list itself with g_list_free().
- *
- * An easy way to free the list properly in one step is as follows:
- *
- * |[
- *   g_list_free_full (list, g_object_unref);
- * ]|
- *
- * Returns: a list of #CamelIMAPXMailbox instances
- *
- * Since: 3.12
- **/
-GList *
-camel_imapx_server_list_mailboxes (CamelIMAPXServer *is,
-                                   CamelIMAPXNamespace *namespace,
-                                   const gchar *pattern)
-{
-       GList *list;
-
-       g_return_val_if_fail (CAMEL_IS_IMAPX_SERVER (is), NULL);
-       g_return_val_if_fail (CAMEL_IS_IMAPX_NAMESPACE (namespace), NULL);
-
-       g_mutex_lock (&is->priv->mailboxes_lock);
-
-       list = imapx_server_list_mailboxes_unlocked (is, namespace, pattern);
-
-       g_mutex_unlock (&is->priv->mailboxes_lock);
-
-       return list;
-}
-
 static void
 imapx_disconnect (CamelIMAPXServer *is)
 {
diff --git a/camel/providers/imapx/camel-imapx-server.h b/camel/providers/imapx/camel-imapx-server.h
index cb4b4d1..3a61ba1 100644
--- a/camel/providers/imapx/camel-imapx-server.h
+++ b/camel/providers/imapx/camel-imapx-server.h
@@ -135,13 +135,6 @@ struct _CamelIMAPXServerClass {
                                                 CamelIMAPXMailbox *mailbox);
        void            (*mailbox_closed)       (CamelIMAPXServer *is,
                                                 CamelIMAPXMailbox *mailbox);
-       void            (*mailbox_created)      (CamelIMAPXServer *is,
-                                                CamelIMAPXMailbox *mailbox);
-       void            (*mailbox_renamed)      (CamelIMAPXServer *is,
-                                                CamelIMAPXMailbox *mailbox,
-                                                const gchar *oldname);
-       void            (*mailbox_updated)      (CamelIMAPXServer *is,
-                                                CamelIMAPXMailbox *mailbox);
        void            (*shutdown)             (CamelIMAPXServer *is);
 };
 
@@ -156,18 +149,8 @@ GInputStream *     camel_imapx_server_ref_input_stream
                                                (CamelIMAPXServer *is);
 GOutputStream *        camel_imapx_server_ref_output_stream
                                                (CamelIMAPXServer *is);
-CamelIMAPXNamespaceResponse *
-               camel_imapx_server_ref_namespaces
-                                               (CamelIMAPXServer *is);
-CamelIMAPXMailbox *
-               camel_imapx_server_ref_mailbox  (CamelIMAPXServer *is,
-                                                const gchar *mailbox_name);
 CamelIMAPXMailbox *
                camel_imapx_server_ref_selected (CamelIMAPXServer *is);
-GList *                camel_imapx_server_list_mailboxes
-                                               (CamelIMAPXServer *is,
-                                                CamelIMAPXNamespace *namespace_,
-                                                const gchar *pattern);
 gboolean       camel_imapx_server_connect      (CamelIMAPXServer *is,
                                                 GCancellable *cancellable,
                                                 GError **error);
diff --git a/camel/providers/imapx/camel-imapx-store.c b/camel/providers/imapx/camel-imapx-store.c
index e7b962e..497558e 100644
--- a/camel/providers/imapx/camel-imapx-store.c
+++ b/camel/providers/imapx/camel-imapx-store.c
@@ -55,15 +55,14 @@
        (G_TYPE_INSTANCE_GET_PRIVATE \
        ((obj), CAMEL_TYPE_IMAPX_STORE, CamelIMAPXStorePrivate))
 
+#define e(...) camel_imapx_debug(extra, __VA_ARGS__)
+
 struct _CamelIMAPXStorePrivate {
        CamelIMAPXConnManager *con_man;
 
        CamelIMAPXServer *connecting_server;
        gboolean is_concurrent_connection;
 
-       gulong mailbox_created_handler_id;
-       gulong mailbox_renamed_handler_id;
-       gulong mailbox_updated_handler_id;
        GMutex server_lock;
 
        GHashTable *quota_info;
@@ -77,6 +76,12 @@ struct _CamelIMAPXStorePrivate {
        GMutex get_finfo_lock;
        time_t last_refresh_time;
        volatile gint syncing_folders;
+
+       CamelIMAPXNamespaceResponse *namespaces;
+       GMutex namespaces_lock;
+
+       GHashTable *mailboxes;
+       GMutex mailboxes_lock;
 };
 
 enum {
@@ -85,6 +90,15 @@ enum {
        PROP_HOST_REACHABLE
 };
 
+enum {
+       MAILBOX_CREATED,
+       MAILBOX_RENAMED,
+       MAILBOX_UPDATED,
+       LAST_SIGNAL
+};
+
+static guint signals[LAST_SIGNAL];
+
 static GInitableIface *parent_initable_interface;
 
 /* Forward Declarations */
@@ -567,34 +581,6 @@ imapx_store_process_mailbox_status (CamelIMAPXStore *imapx_store,
 }
 
 static void
-imapx_store_mailbox_created_cb (CamelIMAPXConnManager *con_man,
-                                CamelIMAPXMailbox *mailbox,
-                                CamelIMAPXStore *store)
-{
-       imapx_store_add_mailbox_to_folder (store, mailbox);
-       imapx_store_process_mailbox_attributes (store, mailbox, NULL);
-}
-
-static void
-imapx_store_mailbox_renamed_cb (CamelIMAPXConnManager *con_man,
-                                CamelIMAPXMailbox *mailbox,
-                                const gchar *oldname,
-                                CamelIMAPXStore *store)
-{
-       imapx_store_process_mailbox_attributes (store, mailbox, oldname);
-       imapx_store_process_mailbox_status (store, mailbox);
-}
-
-static void
-imapx_store_mailbox_updated_cb (CamelIMAPXConnManager *con_man,
-                                CamelIMAPXMailbox *mailbox,
-                                CamelIMAPXStore *store)
-{
-       imapx_store_process_mailbox_attributes (store, mailbox, NULL);
-       imapx_store_process_mailbox_status (store, mailbox);
-}
-
-static void
 imapx_store_connect_to_settings (CamelStore *store)
 {
        CamelIMAPXStorePrivate *priv;
@@ -686,29 +672,7 @@ imapx_store_dispose (GObject *object)
        /* Force disconnect so we don't have it run later,
         * after we've cleaned up some stuff. */
        if (imapx_store->priv->con_man != NULL) {
-               if (imapx_store->priv->mailbox_created_handler_id > 0) {
-                       g_signal_handler_disconnect (
-                               imapx_store->priv->con_man,
-                               imapx_store->priv->mailbox_created_handler_id);
-                       imapx_store->priv->mailbox_created_handler_id = 0;
-               }
-
-               if (imapx_store->priv->mailbox_renamed_handler_id > 0) {
-                       g_signal_handler_disconnect (
-                               imapx_store->priv->con_man,
-                               imapx_store->priv->mailbox_renamed_handler_id);
-                       imapx_store->priv->mailbox_renamed_handler_id = 0;
-               }
-
-               if (imapx_store->priv->mailbox_updated_handler_id > 0) {
-                       g_signal_handler_disconnect (
-                               imapx_store->priv->con_man,
-                               imapx_store->priv->mailbox_updated_handler_id);
-                       imapx_store->priv->mailbox_updated_handler_id = 0;
-               }
-
-               camel_service_disconnect_sync (
-                       CAMEL_SERVICE (imapx_store), TRUE, NULL, NULL);
+               camel_service_disconnect_sync (CAMEL_SERVICE (imapx_store), FALSE, NULL, NULL);
                g_clear_object (&imapx_store->priv->con_man);
        }
 
@@ -723,6 +687,9 @@ imapx_store_dispose (GObject *object)
 
        g_clear_object (&imapx_store->priv->connecting_server);
        g_clear_object (&imapx_store->priv->settings);
+       g_clear_object (&imapx_store->priv->namespaces);
+
+       g_hash_table_remove_all (imapx_store->priv->mailboxes);
 
        /* Chain up to parent's dispose() method. */
        G_OBJECT_CLASS (camel_imapx_store_parent_class)->dispose (object);
@@ -744,6 +711,11 @@ imapx_store_finalize (GObject *object)
 
        g_mutex_clear (&priv->settings_lock);
 
+       g_mutex_clear (&priv->namespaces_lock);
+
+       g_hash_table_destroy (priv->mailboxes);
+       g_mutex_clear (&priv->mailboxes_lock);
+
        /* Chain up to parent's finalize() method. */
        G_OBJECT_CLASS (camel_imapx_store_parent_class)->finalize (object);
 }
@@ -1232,7 +1204,7 @@ fetch_folder_info_for_pattern (CamelIMAPXServer *server,
 
        imapx_store = camel_imapx_server_ref_store (server);
 
-       list = camel_imapx_server_list_mailboxes (server, namespace, pattern);
+       list = camel_imapx_store_list_mailboxes (imapx_store, namespace, pattern);
 
        for (link = list; link != NULL; link = g_list_next (link)) {
                CamelIMAPXMailbox *mailbox;
@@ -1266,11 +1238,11 @@ fetch_folder_info_for_inbox (CamelIMAPXServer *server,
                CamelIMAPXStore *imapx_store;
                CamelIMAPXMailbox *mailbox;
 
-               mailbox = camel_imapx_server_ref_mailbox (server, "INBOX");
-               g_return_val_if_fail (mailbox != NULL, FALSE);
-
                imapx_store = camel_imapx_server_ref_store (server);
 
+               mailbox = camel_imapx_store_ref_mailbox (imapx_store, "INBOX");
+               g_return_val_if_fail (mailbox != NULL, FALSE);
+
                collect_folder_info_for_list (
                        imapx_store, mailbox, folder_info_results);
 
@@ -1281,7 +1253,8 @@ fetch_folder_info_for_inbox (CamelIMAPXServer *server,
 }
 
 static gboolean
-fetch_folder_info_for_namespace_category (CamelIMAPXServer *server,
+fetch_folder_info_for_namespace_category (CamelIMAPXStore *imapx_store,
+                                         CamelIMAPXServer *server,
                                           CamelIMAPXNamespaceCategory category,
                                           CamelStoreGetFolderInfoFlags flags,
                                           GHashTable *folder_info_results,
@@ -1292,7 +1265,7 @@ fetch_folder_info_for_namespace_category (CamelIMAPXServer *server,
        GList *list, *link;
        gboolean success = TRUE;
 
-       namespace_response = camel_imapx_server_ref_namespaces (server);
+       namespace_response = camel_imapx_store_ref_namespaces (imapx_store);
        g_return_val_if_fail (namespace_response != NULL, FALSE);
 
        list = camel_imapx_namespace_response_list (namespace_response);
@@ -1330,7 +1303,8 @@ fetch_folder_info_for_namespace_category (CamelIMAPXServer *server,
 }
 
 static gboolean
-fetch_folder_info_from_folder_path (CamelIMAPXServer *server,
+fetch_folder_info_from_folder_path (CamelIMAPXStore *imapx_store,
+                                   CamelIMAPXServer *server,
                                     const gchar *folder_path,
                                     CamelStoreGetFolderInfoFlags flags,
                                     GHashTable *folder_info_results,
@@ -1345,7 +1319,7 @@ fetch_folder_info_from_folder_path (CamelIMAPXServer *server,
        gchar separator;
        gboolean success = FALSE;
 
-       namespace_response = camel_imapx_server_ref_namespaces (server);
+       namespace_response = camel_imapx_store_ref_namespaces (imapx_store);
        g_return_val_if_fail (namespace_response != NULL, FALSE);
 
        /* Find a suitable IMAP namespace for the folder path. */
@@ -1412,14 +1386,14 @@ sync_folders (CamelIMAPXStore *imapx_store,
 
        if (root_folder_path != NULL && *root_folder_path != '\0') {
                success = fetch_folder_info_from_folder_path (
-                       server, root_folder_path, flags,
+                       imapx_store, server, root_folder_path, flags,
                        folder_info_results, cancellable, error);
        } else {
                gboolean have_folder_info_for_inbox;
 
                /* XXX We only fetch personal mailboxes at this time. */
                success = fetch_folder_info_for_namespace_category (
-                       server, CAMEL_IMAPX_NAMESPACE_PERSONAL, flags,
+                       imapx_store, server, CAMEL_IMAPX_NAMESPACE_PERSONAL, flags,
                        folder_info_results, cancellable, error);
 
                have_folder_info_for_inbox =
@@ -1535,7 +1509,7 @@ discover_inbox (CamelIMAPXStore *imapx_store,
        if (imapx_server == NULL)
                return;
 
-       mailbox = camel_imapx_server_ref_mailbox (imapx_server, "INBOX");
+       mailbox = camel_imapx_store_ref_mailbox (imapx_store, "INBOX");
 
        if (mailbox == NULL)
                goto exit;
@@ -1918,7 +1892,7 @@ check_namespace:
         *       This needs fixed to properly support IMAP namespaces.
         */
 
-       namespace_response = camel_imapx_server_ref_namespaces (imapx_server);
+       namespace_response = camel_imapx_store_ref_namespaces (imapx_store);
        g_return_val_if_fail (namespace_response != NULL, NULL);
 
        list = camel_imapx_namespace_response_list (namespace_response);
@@ -1949,7 +1923,7 @@ check_separator:
        }
 
        /* This also LISTs the mailbox after creating it, which
-        * triggers the CamelIMAPXServer::mailbox-created signal
+        * triggers the CamelIMAPXStore::mailbox-created signal
         * and all the local processing that goes along with it. */
        success = camel_imapx_server_create_mailbox (
                imapx_server, mailbox_name, cancellable, error);
@@ -2328,6 +2302,49 @@ exit:
 }
 
 static void
+imapx_store_mailbox_created (CamelIMAPXStore *imapx_store,
+                            CamelIMAPXMailbox *mailbox)
+{
+       e (
+               '*',
+               "%s::mailbox-created (\"%s\")\n",
+               G_OBJECT_TYPE_NAME (imapx_store),
+               camel_imapx_mailbox_get_name (mailbox));
+
+       imapx_store_add_mailbox_to_folder (imapx_store, mailbox);
+       imapx_store_process_mailbox_attributes (imapx_store, mailbox, NULL);
+}
+
+static void
+imapx_store_mailbox_renamed (CamelIMAPXStore *imapx_store,
+                            CamelIMAPXMailbox *mailbox,
+                            const gchar *oldname)
+{
+       e (
+               '*',
+               "%s::mailbox-renamed (\"%s\" -> \"%s\")\n",
+               G_OBJECT_TYPE_NAME (imapx_store), oldname,
+               camel_imapx_mailbox_get_name (mailbox));
+
+       imapx_store_process_mailbox_attributes (imapx_store, mailbox, oldname);
+       imapx_store_process_mailbox_status (imapx_store, mailbox);
+}
+
+static void
+imapx_store_mailbox_updated (CamelIMAPXStore *imapx_store,
+                            CamelIMAPXMailbox *mailbox)
+{
+       e (
+               '*',
+               "%s::mailbox-updated (\"%s\")\n",
+               G_OBJECT_TYPE_NAME (imapx_store),
+               camel_imapx_mailbox_get_name (mailbox));
+
+       imapx_store_process_mailbox_attributes (imapx_store, mailbox, NULL);
+       imapx_store_process_mailbox_status (imapx_store, mailbox);
+}
+
+static void
 camel_imapx_store_class_init (CamelIMAPXStoreClass *class)
 {
        GObjectClass *object_class;
@@ -2363,6 +2380,10 @@ camel_imapx_store_class_init (CamelIMAPXStoreClass *class)
        store_class->delete_folder_sync = imapx_store_delete_folder_sync;
        store_class->rename_folder_sync = imapx_store_rename_folder_sync;
 
+       class->mailbox_created = imapx_store_mailbox_created;
+       class->mailbox_renamed = imapx_store_mailbox_renamed;
+       class->mailbox_updated = imapx_store_mailbox_updated;
+
        /* Inherited from CamelNetworkService. */
        g_object_class_override_property (
                object_class,
@@ -2374,6 +2395,34 @@ camel_imapx_store_class_init (CamelIMAPXStoreClass *class)
                object_class,
                PROP_HOST_REACHABLE,
                "host-reachable");
+
+       signals[MAILBOX_CREATED] = g_signal_new (
+               "mailbox-created",
+               G_OBJECT_CLASS_TYPE (class),
+               G_SIGNAL_RUN_FIRST,
+               G_STRUCT_OFFSET (CamelIMAPXStoreClass, mailbox_created),
+               NULL, NULL, NULL,
+               G_TYPE_NONE, 1,
+               CAMEL_TYPE_IMAPX_MAILBOX);
+
+       signals[MAILBOX_RENAMED] = g_signal_new (
+               "mailbox-renamed",
+               G_OBJECT_CLASS_TYPE (class),
+               G_SIGNAL_RUN_FIRST,
+               G_STRUCT_OFFSET (CamelIMAPXStoreClass, mailbox_renamed),
+               NULL, NULL, NULL,
+               G_TYPE_NONE, 2,
+               CAMEL_TYPE_IMAPX_MAILBOX,
+               G_TYPE_STRING);
+
+       signals[MAILBOX_UPDATED] = g_signal_new (
+               "mailbox-updated",
+               G_OBJECT_CLASS_TYPE (class),
+               G_SIGNAL_RUN_FIRST,
+               G_STRUCT_OFFSET (CamelIMAPXStoreClass, mailbox_updated),
+               NULL, NULL, NULL,
+               G_TYPE_NONE, 1,
+               CAMEL_TYPE_IMAPX_MAILBOX);
 }
 
 static void
@@ -2402,14 +2451,17 @@ camel_subscribable_init (CamelSubscribableInterface *iface)
 static void
 camel_imapx_store_init (CamelIMAPXStore *store)
 {
-       gulong handler_id;
-
        store->priv = CAMEL_IMAPX_STORE_GET_PRIVATE (store);
 
        store->priv->con_man = camel_imapx_conn_manager_new (CAMEL_STORE (store));
 
        g_mutex_init (&store->priv->get_finfo_lock);
 
+       g_mutex_init (&store->priv->namespaces_lock);
+       g_mutex_init (&store->priv->mailboxes_lock);
+       /* Hash table key is owned by the CamelIMAPXMailbox. */
+       store->priv->mailboxes = g_hash_table_new_full (g_str_hash, g_str_equal, NULL, g_object_unref);
+
        /* Initialize to zero to ensure we always obtain fresh folder
         * info on startup.  See imapx_store_get_folder_info_sync(). */
        store->priv->last_refresh_time = 0;
@@ -2430,24 +2482,6 @@ camel_imapx_store_init (CamelIMAPXStore *store)
        g_signal_connect (
                store, "notify::settings",
                G_CALLBACK (imapx_store_update_store_flags), NULL);
-
-       handler_id = g_signal_connect (
-               store->priv->con_man, "mailbox-created",
-               G_CALLBACK (imapx_store_mailbox_created_cb),
-               store);
-       store->priv->mailbox_created_handler_id = handler_id;
-
-       handler_id = g_signal_connect (
-               store->priv->con_man, "mailbox-renamed",
-               G_CALLBACK (imapx_store_mailbox_renamed_cb),
-               store);
-       store->priv->mailbox_renamed_handler_id = handler_id;
-
-       handler_id = g_signal_connect (
-               store->priv->con_man, "mailbox-updated",
-               G_CALLBACK (imapx_store_mailbox_updated_cb),
-               store);
-       store->priv->mailbox_updated_handler_id = handler_id;
 }
 
 /**
@@ -2547,14 +2581,503 @@ camel_imapx_store_folder_op_done (CamelIMAPXStore *store,
                store->priv->con_man, server, folder_name);
 }
 
+/**
+ * camel_imapx_store_ref_namespaces:
+ * @imapx_store: a #CamelIMAPXStore
+ *
+ * Returns the #CamelIMAPXNamespaceResponse for @is. This is obtained
+ * during the connection phase if the IMAP server lists the "NAMESPACE"
+ * keyword in its CAPABILITY response, or else is fabricated from the
+ * first LIST response.
+ *
+ * The returned #CamelIMAPXNamespaceResponse is reference for thread-safety
+ * and must be unreferenced with g_object_unref() when finished with it.
+ *
+ * Returns: a #CamelIMAPXNamespaceResponse
+ *
+ * Since: 3.14
+ **/
+CamelIMAPXNamespaceResponse *
+camel_imapx_store_ref_namespaces (CamelIMAPXStore *imapx_store)
+{
+       CamelIMAPXNamespaceResponse *namespaces = NULL;
+
+       g_return_val_if_fail (CAMEL_IS_IMAPX_STORE (imapx_store), NULL);
+
+       g_mutex_lock (&imapx_store->priv->namespaces_lock);
+
+       if (imapx_store->priv->namespaces != NULL)
+               namespaces = g_object_ref (imapx_store->priv->namespaces);
+
+       g_mutex_unlock (&imapx_store->priv->namespaces_lock);
+
+       return namespaces;
+}
+
+void
+camel_imapx_store_set_namespaces (CamelIMAPXStore *imapx_store,
+                                 CamelIMAPXNamespaceResponse *namespaces)
+{
+       g_return_if_fail (CAMEL_IS_IMAPX_STORE (imapx_store));
+       if (namespaces)
+               g_return_if_fail (CAMEL_IS_IMAPX_NAMESPACE_RESPONSE (namespaces));
+
+       if (namespaces)
+               g_object_ref (namespaces);
+
+       g_mutex_lock (&imapx_store->priv->namespaces_lock);
+
+       g_clear_object (&imapx_store->priv->namespaces);
+       imapx_store->priv->namespaces = namespaces;
+
+       g_mutex_unlock (&imapx_store->priv->namespaces_lock);
+}
+
+static void
+imapx_store_add_mailbox_unlocked (CamelIMAPXStore *imapx_store,
+                                 CamelIMAPXMailbox *mailbox)
+{
+       const gchar *mailbox_name;
+
+       /* Acquire "mailboxes_lock" before calling. */
+
+       mailbox_name = camel_imapx_mailbox_get_name (mailbox);
+       g_return_if_fail (mailbox_name != NULL);
+
+       /* Use g_hash_table_replace() here instead of g_hash_table_insert().
+        * The hash table key is owned by the hash table value, so if we're
+        * replacing an existing table item we want to replace both the key
+        * and value to avoid data corruption. */
+       g_hash_table_replace (
+               imapx_store->priv->mailboxes,
+               (gpointer) mailbox_name,
+               g_object_ref (mailbox));
+}
+
+static gboolean
+imapx_store_remove_mailbox_unlocked (CamelIMAPXStore *imapx_store,
+                                    CamelIMAPXMailbox *mailbox)
+{
+       const gchar *mailbox_name;
+
+       /* Acquire "mailboxes_lock" before calling. */
+
+       mailbox_name = camel_imapx_mailbox_get_name (mailbox);
+       g_return_val_if_fail (mailbox_name != NULL, FALSE);
+
+       return g_hash_table_remove (imapx_store->priv->mailboxes, mailbox_name);
+}
+
+static CamelIMAPXMailbox *
+imapx_store_ref_mailbox_unlocked (CamelIMAPXStore *imapx_store,
+                                 const gchar *mailbox_name)
+{
+       CamelIMAPXMailbox *mailbox;
+
+       /* Acquire "mailboxes_lock" before calling. */
+
+       g_return_val_if_fail (mailbox_name != NULL, NULL);
+
+       /* The INBOX mailbox is case-insensitive. */
+       if (g_ascii_strcasecmp (mailbox_name, "INBOX") == 0)
+               mailbox_name = "INBOX";
+
+       mailbox = g_hash_table_lookup (imapx_store->priv->mailboxes, mailbox_name);
+
+       /* Remove non-existent mailboxes as we find them. */
+       if (mailbox != NULL && !camel_imapx_mailbox_exists (mailbox)) {
+               imapx_store_remove_mailbox_unlocked (imapx_store, mailbox);
+               mailbox = NULL;
+       }
+
+       if (mailbox != NULL)
+               g_object_ref (mailbox);
+
+       return mailbox;
+}
+
+static GList *
+imapx_store_list_mailboxes_unlocked (CamelIMAPXStore *imapx_store,
+                                    CamelIMAPXNamespace *namespace,
+                                    const gchar *pattern)
+{
+       GHashTableIter iter;
+       GList *list = NULL;
+       gpointer value;
+
+       /* Acquire "mailboxes_lock" before calling. */
+
+       if (pattern == NULL)
+               pattern = "*";
+
+       g_hash_table_iter_init (&iter, imapx_store->priv->mailboxes);
+
+       while (g_hash_table_iter_next (&iter, NULL, &value)) {
+               CamelIMAPXMailbox *mailbox;
+               CamelIMAPXNamespace *mailbox_ns;
+
+               mailbox = CAMEL_IMAPX_MAILBOX (value);
+               mailbox_ns = camel_imapx_mailbox_get_namespace (mailbox);
+
+               if (!camel_imapx_mailbox_exists (mailbox))
+                       continue;
+
+               if (!camel_imapx_namespace_equal (namespace, mailbox_ns))
+                       continue;
+
+               if (!camel_imapx_mailbox_matches (mailbox, pattern))
+                       continue;
+
+               list = g_list_prepend (list, g_object_ref (mailbox));
+       }
+
+       /* Sort the list by mailbox name. */
+       return g_list_sort (list, (GCompareFunc) camel_imapx_mailbox_compare);
+}
+
+static CamelIMAPXMailbox *
+imapx_store_create_mailbox_unlocked (CamelIMAPXStore *imapx_store,
+                                    CamelIMAPXListResponse *response)
+{
+       CamelIMAPXNamespaceResponse *namespace_response;
+       CamelIMAPXNamespace *namespace;
+       CamelIMAPXMailbox *mailbox = NULL;
+       const gchar *mailbox_name;
+       gchar separator;
+
+       /* Acquire "mailboxes_lock" before calling. */
+
+       namespace_response = camel_imapx_store_ref_namespaces (imapx_store);
+       g_return_val_if_fail (namespace_response != NULL, FALSE);
+
+       mailbox_name = camel_imapx_list_response_get_mailbox_name (response);
+       separator = camel_imapx_list_response_get_separator (response);
+
+       namespace = camel_imapx_namespace_response_lookup (
+               namespace_response, mailbox_name, separator);
+
+       if (namespace != NULL) {
+               mailbox = camel_imapx_mailbox_new (response, namespace);
+               imapx_store_add_mailbox_unlocked (imapx_store, mailbox);
+               g_object_unref (namespace);
+
+       /* XXX Slight hack, mainly for Courier servers.  If INBOX does
+        *     not match any defined namespace, just create one for it
+        *     on the fly.  The namespace response won't know about it. */
+       } else if (camel_imapx_mailbox_is_inbox (mailbox_name)) {
+               namespace = camel_imapx_namespace_new (
+                       CAMEL_IMAPX_NAMESPACE_PERSONAL, "", separator);
+               mailbox = camel_imapx_mailbox_new (response, namespace);
+               imapx_store_add_mailbox_unlocked (imapx_store, mailbox);
+               g_object_unref (namespace);
+
+       } else {
+               g_warning (
+                       "%s: No matching namespace for \"%c\" %s",
+                       G_STRFUNC, separator, mailbox_name);
+       }
+
+       g_object_unref (namespace_response);
+
+       return mailbox;
+}
+
+static CamelIMAPXMailbox *
+imapx_store_rename_mailbox_unlocked (CamelIMAPXStore *imapx_store,
+                                    const gchar *old_mailbox_name,
+                                    const gchar *new_mailbox_name)
+{
+       CamelIMAPXMailbox *old_mailbox;
+       CamelIMAPXMailbox *new_mailbox;
+       CamelIMAPXNamespace *namespace;
+       gsize old_mailbox_name_length;
+       GList *list, *link;
+       gchar separator;
+       gchar *pattern;
+
+       /* Acquire "mailboxes_lock" before calling. */
+
+       g_return_val_if_fail (old_mailbox_name != NULL, NULL);
+       g_return_val_if_fail (new_mailbox_name != NULL, NULL);
+
+       old_mailbox = imapx_store_ref_mailbox_unlocked (imapx_store, old_mailbox_name);
+       if (old_mailbox == NULL)
+               return NULL;
+
+       old_mailbox_name_length = strlen (old_mailbox_name);
+       namespace = camel_imapx_mailbox_get_namespace (old_mailbox);
+       separator = camel_imapx_mailbox_get_separator (old_mailbox);
+
+       new_mailbox = camel_imapx_mailbox_clone (old_mailbox, new_mailbox_name);
+
+       /* Add the new mailbox, remove the old mailbox.
+        * Note we still have a reference on the old mailbox. */
+       imapx_store_add_mailbox_unlocked (imapx_store, new_mailbox);
+       imapx_store_remove_mailbox_unlocked (imapx_store, old_mailbox);
+
+       /* Rename any child mailboxes. */
+
+       pattern = g_strdup_printf ("%s%c*", old_mailbox_name, separator);
+       list = imapx_store_list_mailboxes_unlocked (imapx_store, namespace, pattern);
+
+       for (link = list; link != NULL; link = g_list_next (link)) {
+               CamelIMAPXMailbox *old_child;
+               CamelIMAPXMailbox *new_child;
+               const gchar *old_child_name;
+               gchar *new_child_name;
+
+               old_child = CAMEL_IMAPX_MAILBOX (link->data);
+               old_child_name = camel_imapx_mailbox_get_name (old_child);
+
+               /* Sanity checks. */
+               g_warn_if_fail (
+                       old_child_name != NULL &&
+                       strlen (old_child_name) > old_mailbox_name_length &&
+                       old_child_name[old_mailbox_name_length] == separator);
+
+               new_child_name = g_strconcat (
+                       new_mailbox_name,
+                       old_child_name + old_mailbox_name_length, NULL);
+               new_child = camel_imapx_mailbox_clone (
+                       old_child, new_child_name);
+
+               /* Add the new mailbox, remove the old mailbox.
+                * Note we still have a reference on the old mailbox. */
+               imapx_store_add_mailbox_unlocked (imapx_store, new_child);
+               imapx_store_remove_mailbox_unlocked (imapx_store, old_child);
+
+               g_object_unref (new_child);
+               g_free (new_child_name);
+       }
+
+       g_list_free_full (list, (GDestroyNotify) g_object_unref);
+       g_free (pattern);
+
+       g_object_unref (old_mailbox);
+
+       return new_mailbox;
+}
+
+/**
+ * camel_imapx_store_ref_mailbox:
+ * @imapx_store: a #CamelIMAPXStore
+ * @mailbox_name: a mailbox name
+ *
+ * Looks up a #CamelMailbox by its name. If no match is found, the function
+ * returns %NULL.
+ *
+ * The returned #CamelIMAPXMailbox is referenced for thread-safety and
+ * should be unreferenced with g_object_unref() when finished with it.
+ *
+ * Returns: a #CamelIMAPXMailbox, or %NULL
+ *
+ * Since: 3.14
+ **/
 CamelIMAPXMailbox *
 camel_imapx_store_ref_mailbox (CamelIMAPXStore *imapx_store,
                               const gchar *mailbox_name)
 {
+       CamelIMAPXMailbox *mailbox;
+
        g_return_val_if_fail (CAMEL_IS_IMAPX_STORE (imapx_store), NULL);
        g_return_val_if_fail (mailbox_name != NULL, NULL);
 
-       return camel_imapx_conn_manager_ref_mailbox (imapx_store->priv->con_man, mailbox_name);
+       g_mutex_lock (&imapx_store->priv->mailboxes_lock);
+
+       mailbox = imapx_store_ref_mailbox_unlocked (imapx_store, mailbox_name);
+
+       g_mutex_unlock (&imapx_store->priv->mailboxes_lock);
+
+       return mailbox;
+}
+
+/**
+ * camel_imapx_store_list_mailboxes:
+ * @imapx_store: a #CamelIMAPXStore
+ * @namespace_: a #CamelIMAPXNamespace
+ * @pattern: mailbox name with possible wildcards, or %NULL
+ *
+ * Returns a list of #CamelIMAPXMailbox instances which match @namespace and
+ * @pattern. The @pattern may contain wildcard characters '*' and '%', which
+ * are interpreted similar to the IMAP LIST command. A %NULL @pattern lists
+ * all mailboxes in @namespace; equivalent to passing "*".
+ *
+ * The mailboxes returned in the list are referenced for thread-safety.
+ * They must each be unreferenced with g_object_unref() when finished with
+ * them. Free the returned list itself with g_list_free().
+ *
+ * An easy way to free the list properly in one step is as follows:
+ *
+ * |[
+ *   g_list_free_full (list, g_object_unref);
+ * ]|
+ *
+ * Returns: a list of #CamelIMAPXMailbox instances
+ *
+ * Since: 3.14
+ **/
+GList *
+camel_imapx_store_list_mailboxes (CamelIMAPXStore *imapx_store,
+                                 CamelIMAPXNamespace *namespace,
+                                 const gchar *pattern)
+{
+       GList *list;
+
+       g_return_val_if_fail (CAMEL_IS_IMAPX_STORE (imapx_store), NULL);
+       g_return_val_if_fail (CAMEL_IS_IMAPX_NAMESPACE (namespace), NULL);
+
+       g_mutex_lock (&imapx_store->priv->mailboxes_lock);
+
+       list = imapx_store_list_mailboxes_unlocked (imapx_store, namespace, pattern);
+
+       g_mutex_unlock (&imapx_store->priv->mailboxes_lock);
+
+       return list;
+}
+
+void
+camel_imapx_store_emit_mailbox_updated (CamelIMAPXStore *imapx_store,
+                                       CamelIMAPXMailbox *mailbox)
+{
+       g_return_if_fail (CAMEL_IS_IMAPX_STORE (imapx_store));
+       g_return_if_fail (CAMEL_IS_IMAPX_MAILBOX (mailbox));
+
+       g_signal_emit (imapx_store, signals[MAILBOX_UPDATED], 0, mailbox);
+}
+
+void
+camel_imapx_store_handle_mailbox_rename (CamelIMAPXStore *imapx_store,
+                                        CamelIMAPXMailbox *old_mailbox,
+                                        const gchar *new_mailbox_name)
+{
+       CamelIMAPXMailbox *new_mailbox;
+       const gchar *old_mailbox_name;
+
+       g_return_if_fail (CAMEL_IS_IMAPX_STORE (imapx_store));
+       g_return_if_fail (CAMEL_IS_IMAPX_MAILBOX (old_mailbox));
+       g_return_if_fail (new_mailbox_name != NULL);
+
+       old_mailbox_name = camel_imapx_mailbox_get_name (old_mailbox);
+
+       g_mutex_lock (&imapx_store->priv->mailboxes_lock);
+       new_mailbox = imapx_store_rename_mailbox_unlocked (
+               imapx_store, old_mailbox_name, new_mailbox_name);
+       g_mutex_unlock (&imapx_store->priv->mailboxes_lock);
+
+       g_warn_if_fail (new_mailbox != NULL);
+
+       g_signal_emit (
+               imapx_store, signals[MAILBOX_RENAMED], 0,
+               new_mailbox, old_mailbox_name);
+
+       g_clear_object (&new_mailbox);
+}
+
+void
+camel_imapx_store_handle_list_response (CamelIMAPXStore *imapx_store,
+                                       CamelIMAPXServer *imapx_server,
+                                       CamelIMAPXListResponse *response)
+{
+       CamelIMAPXMailbox *mailbox = NULL;
+       const gchar *mailbox_name;
+       const gchar *old_mailbox_name;
+       gboolean emit_mailbox_created = FALSE;
+       gboolean emit_mailbox_renamed = FALSE;
+       gboolean emit_mailbox_updated = FALSE;
+
+       g_return_if_fail (CAMEL_IS_IMAPX_STORE (imapx_store));
+       g_return_if_fail (CAMEL_IS_IMAPX_SERVER (imapx_server));
+       g_return_if_fail (CAMEL_IS_IMAPX_LIST_RESPONSE (response));
+
+       mailbox_name = camel_imapx_list_response_get_mailbox_name (response);
+       old_mailbox_name = camel_imapx_list_response_get_oldname (response);
+
+       /* Fabricate a CamelIMAPXNamespaceResponse if the server lacks the
+        * NAMESPACE capability and this is the first LIST / LSUB response. */
+       if (CAMEL_IMAPX_LACK_CAPABILITY (imapx_server->cinfo, NAMESPACE)) {
+               g_mutex_lock (&imapx_store->priv->namespaces_lock);
+               if (imapx_store->priv->namespaces == NULL) {
+                       imapx_store->priv->namespaces = camel_imapx_namespace_response_faux_new (response);
+               }
+               g_mutex_unlock (&imapx_store->priv->namespaces_lock);
+       }
+
+       /* Create, rename, or update a corresponding CamelIMAPXMailbox. */
+       g_mutex_lock (&imapx_store->priv->mailboxes_lock);
+       if (old_mailbox_name != NULL) {
+               mailbox = imapx_store_rename_mailbox_unlocked (
+                       imapx_store, old_mailbox_name, mailbox_name);
+               emit_mailbox_renamed = (mailbox != NULL);
+       }
+       if (mailbox == NULL) {
+               mailbox = imapx_store_ref_mailbox_unlocked (imapx_store, mailbox_name);
+               emit_mailbox_updated = (mailbox != NULL);
+       }
+       if (mailbox == NULL) {
+               mailbox = imapx_store_create_mailbox_unlocked (imapx_store, response);
+               emit_mailbox_created = (mailbox != NULL);
+       } else {
+               camel_imapx_mailbox_handle_list_response (mailbox, response);
+       }
+       g_mutex_unlock (&imapx_store->priv->mailboxes_lock);
+
+       if (emit_mailbox_created)
+               g_signal_emit (imapx_store, signals[MAILBOX_CREATED], 0, mailbox);
+
+       if (emit_mailbox_renamed)
+               g_signal_emit (
+                       imapx_store, signals[MAILBOX_RENAMED], 0,
+                       mailbox, old_mailbox_name);
+
+       if (emit_mailbox_updated)
+               g_signal_emit (imapx_store, signals[MAILBOX_UPDATED], 0, mailbox);
+
+       g_clear_object (&mailbox);
+}
+
+void
+camel_imapx_store_handle_lsub_response (CamelIMAPXStore *imapx_store,
+                                       CamelIMAPXServer *imapx_server,
+                                       CamelIMAPXListResponse *response)
+{
+       CamelIMAPXMailbox *mailbox;
+       const gchar *mailbox_name;
+       gboolean emit_mailbox_updated = FALSE;
+
+       g_return_if_fail (CAMEL_IS_IMAPX_STORE (imapx_store));
+       g_return_if_fail (CAMEL_IS_IMAPX_SERVER (imapx_server));
+       g_return_if_fail (CAMEL_IS_IMAPX_LIST_RESPONSE (response));
+
+       mailbox_name = camel_imapx_list_response_get_mailbox_name (response);
+
+       /* Fabricate a CamelIMAPXNamespaceResponse if the server lacks the
+        * NAMESPACE capability and this is the first LIST / LSUB response. */
+       if (CAMEL_IMAPX_LACK_CAPABILITY (imapx_server->cinfo, NAMESPACE)) {
+               g_mutex_lock (&imapx_store->priv->namespaces_lock);
+               if (imapx_store->priv->namespaces == NULL) {
+                       imapx_store->priv->namespaces = camel_imapx_namespace_response_faux_new (response);
+               }
+               g_mutex_unlock (&imapx_store->priv->namespaces_lock);
+       }
+
+       /* Update a corresponding CamelIMAPXMailbox.
+        *
+        * Note, don't create the CamelIMAPXMailbox like we do for a LIST
+        * response.  We always issue LIST before LSUB on a mailbox name,
+        * so if we don't already have a CamelIMAPXMailbox instance then
+        * this is a subscription on a non-existent mailbox.  Skip it. */
+       g_mutex_lock (&imapx_store->priv->mailboxes_lock);
+       mailbox = imapx_store_ref_mailbox_unlocked (imapx_store, mailbox_name);
+       if (mailbox != NULL) {
+               camel_imapx_mailbox_handle_lsub_response (mailbox, response);
+               emit_mailbox_updated = TRUE;
+       }
+       g_mutex_unlock (&imapx_store->priv->mailboxes_lock);
+
+       if (emit_mailbox_updated)
+               g_signal_emit (imapx_store, signals[MAILBOX_UPDATED], 0, mailbox);
+
+       g_clear_object (&mailbox);
 }
 
 CamelFolderQuotaInfo *
diff --git a/camel/providers/imapx/camel-imapx-store.h b/camel/providers/imapx/camel-imapx-store.h
index 1c8b43a..2d6f0f4 100644
--- a/camel/providers/imapx/camel-imapx-store.h
+++ b/camel/providers/imapx/camel-imapx-store.h
@@ -59,6 +59,15 @@ struct _CamelIMAPXStore {
 
 struct _CamelIMAPXStoreClass {
        CamelOfflineStoreClass parent_class;
+
+       /* Signals */
+       void            (*mailbox_created)      (CamelIMAPXStore *imapx_store,
+                                                CamelIMAPXMailbox *mailbox);
+       void            (*mailbox_renamed)      (CamelIMAPXStore *imapx_store,
+                                                CamelIMAPXMailbox *mailbox,
+                                                const gchar *oldname);
+       void            (*mailbox_updated)      (CamelIMAPXStore *imapx_store,
+                                                CamelIMAPXMailbox *mailbox);
 };
 
 GType          camel_imapx_store_get_type      (void);
@@ -78,10 +87,34 @@ void                camel_imapx_store_folder_op_done
                                                (CamelIMAPXStore *store,
                                                 CamelIMAPXServer *server,
                                                 const gchar *folder_name);
+CamelIMAPXNamespaceResponse *
+               camel_imapx_store_ref_namespaces
+                                               (CamelIMAPXStore *imapx_store);
+void           camel_imapx_store_set_namespaces
+                                               (CamelIMAPXStore *imapx_store,
+                                                CamelIMAPXNamespaceResponse *namespaces);
 CamelIMAPXMailbox *
                camel_imapx_store_ref_mailbox   (CamelIMAPXStore *imapx_store,
                                                 const gchar *mailbox_name);
-
+GList *                camel_imapx_store_list_mailboxes
+                                               (CamelIMAPXStore *imapx_store,
+                                                CamelIMAPXNamespace *namespace_,
+                                                const gchar *pattern);
+void           camel_imapx_store_emit_mailbox_updated
+                                               (CamelIMAPXStore *imapx_store,
+                                                CamelIMAPXMailbox *mailbox);
+void           camel_imapx_store_handle_mailbox_rename
+                                               (CamelIMAPXStore *imapx_store,
+                                                CamelIMAPXMailbox *old_mailbox,
+                                                const gchar *new_mailbox_name);
+void           camel_imapx_store_handle_list_response
+                                               (CamelIMAPXStore *imapx_store,
+                                                CamelIMAPXServer *imapx_server,
+                                                CamelIMAPXListResponse *response);
+void           camel_imapx_store_handle_lsub_response
+                                               (CamelIMAPXStore *imapx_store,
+                                                CamelIMAPXServer *imapx_server,
+                                                CamelIMAPXListResponse *response);
 CamelFolderQuotaInfo *
                camel_imapx_store_dup_quota_info
                                                (CamelIMAPXStore *store,


[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]