Bug #728973 - [IMAPX] Recover after store summary version mismatch
[platform/upstream/evolution-data-server.git] / camel / providers / imapx / camel-imapx-store.c
1 /* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
2 /* camel-imap-store.c : class for a imap store */
3 /*
4  * Authors: Michael Zucchi <notzed@ximian.com>
5  *
6  * Copyright (C) 1999-2008 Novell, Inc. (www.novell.com)
7  *
8  * This library is free software; you can redistribute it and/or modify it
9  * under the terms of the GNU Lesser General Public License as published by
10  * the Free Software Foundation.
11  *
12  * This library is distributed in the hope that it will be useful, but
13  * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
14  * or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
15  * for more details.
16  *
17  * You should have received a copy of the GNU Lesser General Public License
18  * along with this library; if not, see <http://www.gnu.org/licenses/>.
19  */
20
21 #ifdef HAVE_CONFIG_H
22 #include <config.h>
23 #endif
24
25 #include <sys/types.h>
26 #ifdef _WIN32
27 #include <winsock2.h>
28 #else
29 #include <sys/socket.h>
30 #include <netinet/in.h>
31 #endif
32 #include <errno.h>
33 #include <stdio.h>
34 #include <stdlib.h>
35 #include <string.h>
36 #include <unistd.h>
37 #include <glib/gstdio.h>
38 #include <glib/gi18n-lib.h>
39
40 #include "camel-imapx-conn-manager.h"
41 #include "camel-imapx-folder.h"
42 #include "camel-imapx-job.h"
43 #include "camel-imapx-server.h"
44 #include "camel-imapx-settings.h"
45 #include "camel-imapx-store.h"
46 #include "camel-imapx-summary.h"
47 #include "camel-imapx-utils.h"
48
49 /* Specified in RFC 2060 section 2.1 */
50 #define IMAP_PORT 143
51 #define IMAPS_PORT 993
52
53 #define FINFO_REFRESH_INTERVAL 60
54
55 #define CAMEL_IMAPX_STORE_GET_PRIVATE(obj) \
56         (G_TYPE_INSTANCE_GET_PRIVATE \
57         ((obj), CAMEL_TYPE_IMAPX_STORE, CamelIMAPXStorePrivate))
58
59 #define e(...) camel_imapx_debug(extra, __VA_ARGS__)
60
61 struct _CamelIMAPXStorePrivate {
62         CamelIMAPXConnManager *con_man;
63
64         CamelIMAPXServer *connecting_server;
65         gboolean is_concurrent_connection;
66
67         GMutex server_lock;
68
69         GHashTable *quota_info;
70         GMutex quota_info_lock;
71
72         GMutex settings_lock;
73         CamelSettings *settings;
74         gulong settings_notify_handler_id;
75
76         /* Used for synchronizing get_folder_info_sync(). */
77         GMutex get_finfo_lock;
78         time_t last_refresh_time;
79         volatile gint syncing_folders;
80
81         CamelIMAPXNamespaceResponse *namespaces;
82         GMutex namespaces_lock;
83
84         GHashTable *mailboxes;
85         GMutex mailboxes_lock;
86 };
87
88 enum {
89         PROP_0,
90         PROP_CONNECTABLE,
91         PROP_HOST_REACHABLE
92 };
93
94 enum {
95         MAILBOX_CREATED,
96         MAILBOX_RENAMED,
97         MAILBOX_UPDATED,
98         LAST_SIGNAL
99 };
100
101 static guint signals[LAST_SIGNAL];
102
103 static GInitableIface *parent_initable_interface;
104
105 /* Forward Declarations */
106 static void camel_imapx_store_initable_init (GInitableIface *iface);
107 static void camel_network_service_init (CamelNetworkServiceInterface *iface);
108 static void camel_subscribable_init (CamelSubscribableInterface *iface);
109
110 G_DEFINE_TYPE_WITH_CODE (
111         CamelIMAPXStore,
112         camel_imapx_store,
113         CAMEL_TYPE_OFFLINE_STORE,
114         G_IMPLEMENT_INTERFACE (
115                 G_TYPE_INITABLE,
116                 camel_imapx_store_initable_init)
117         G_IMPLEMENT_INTERFACE (
118                 CAMEL_TYPE_NETWORK_SERVICE,
119                 camel_network_service_init)
120         G_IMPLEMENT_INTERFACE (
121                 CAMEL_TYPE_SUBSCRIBABLE,
122                 camel_subscribable_init))
123
124 static guint
125 imapx_name_hash (gconstpointer key)
126 {
127         const gchar *mailbox = key;
128
129         if (camel_imapx_mailbox_is_inbox (mailbox))
130                 mailbox = "INBOX";
131
132         return g_str_hash (mailbox);
133 }
134
135 static gboolean
136 imapx_name_equal (gconstpointer a,
137                   gconstpointer b)
138 {
139         const gchar *mailbox_a = a;
140         const gchar *mailbox_b = b;
141
142         if (camel_imapx_mailbox_is_inbox (mailbox_a))
143                 mailbox_a = "INBOX";
144
145         if (camel_imapx_mailbox_is_inbox (mailbox_b))
146                 mailbox_b = "INBOX";
147
148         return g_str_equal (mailbox_a, mailbox_b);
149 }
150
151 static void
152 imapx_store_update_store_flags (CamelStore *store)
153 {
154         CamelService *service;
155         CamelSettings *settings;
156         CamelIMAPXSettings *imapx_settings;
157
158         service = CAMEL_SERVICE (store);
159         settings = camel_service_ref_settings (service);
160         imapx_settings = CAMEL_IMAPX_SETTINGS (settings);
161
162         if (camel_imapx_settings_get_use_real_junk_path (imapx_settings)) {
163                 store->flags &= ~CAMEL_STORE_VJUNK;
164                 store->flags |= CAMEL_STORE_REAL_JUNK_FOLDER;
165         } else {
166                 store->flags |= CAMEL_STORE_VJUNK;
167                 store->flags &= ~CAMEL_STORE_REAL_JUNK_FOLDER;
168         }
169
170         if (camel_imapx_settings_get_use_real_trash_path (imapx_settings))
171                 store->flags &= ~CAMEL_STORE_VTRASH;
172         else
173                 store->flags |= CAMEL_STORE_VTRASH;
174
175         g_object_unref (settings);
176 }
177
178 static void
179 imapx_store_settings_notify_cb (CamelSettings *settings,
180                                 GParamSpec *pspec,
181                                 CamelStore *store)
182 {
183         if (g_str_equal (pspec->name, "use-real-junk-path")) {
184                 imapx_store_update_store_flags (store);
185                 camel_store_folder_info_stale (store);
186         }
187
188         if (g_str_equal (pspec->name, "use-real-trash-path")) {
189                 imapx_store_update_store_flags (store);
190                 camel_store_folder_info_stale (store);
191         }
192
193         if (g_str_equal (pspec->name, "use-subscriptions")) {
194                 camel_store_folder_info_stale (store);
195         }
196 }
197
198 static CamelFolderInfo *
199 imapx_store_build_folder_info (CamelIMAPXStore *imapx_store,
200                                const gchar *folder_path,
201                                CamelFolderInfoFlags flags)
202 {
203         CamelStore *store = (CamelStore *) imapx_store;
204         CamelSettings *settings;
205         CamelFolderInfo *fi;
206         const gchar *name;
207
208         store = CAMEL_STORE (imapx_store);
209         settings = camel_service_ref_settings (CAMEL_SERVICE (store));
210
211         fi = camel_folder_info_new ();
212         fi->full_name = g_strdup (folder_path);
213         fi->flags = flags;
214         fi->unread = -1;
215         fi->total = -1;
216
217         name = strrchr (fi->full_name, '/');
218         if (name == NULL)
219                 name = fi->full_name;
220         else
221                 name++;
222
223         if (camel_imapx_mailbox_is_inbox (fi->full_name)) {
224                 fi->display_name = g_strdup (_("Inbox"));
225                 fi->flags |= CAMEL_FOLDER_SYSTEM;
226                 fi->flags |= CAMEL_FOLDER_TYPE_INBOX;
227         } else {
228                 fi->display_name = g_strdup (name);
229         }
230
231         if ((store->flags & CAMEL_STORE_VTRASH) == 0) {
232                 const gchar *trash_path;
233
234                 trash_path = camel_imapx_settings_get_real_trash_path (
235                         CAMEL_IMAPX_SETTINGS (settings));
236                 if (g_strcmp0 (trash_path, folder_path) == 0)
237                         fi->flags |= CAMEL_FOLDER_TYPE_TRASH;
238         }
239
240         if ((store->flags & CAMEL_STORE_REAL_JUNK_FOLDER) != 0) {
241                 const gchar *junk_path;
242
243                 junk_path = camel_imapx_settings_get_real_junk_path (
244                         CAMEL_IMAPX_SETTINGS (settings));
245                 if (g_strcmp0 (junk_path, folder_path) == 0)
246                         fi->flags |= CAMEL_FOLDER_TYPE_JUNK;
247         }
248
249         g_object_unref (settings);
250
251         return fi;
252 }
253
254 static void
255 imapx_store_rename_folder_info (CamelIMAPXStore *imapx_store,
256                                 const gchar *old_folder_path,
257                                 const gchar *new_folder_path)
258 {
259         GPtrArray *array;
260         gint olen = strlen (old_folder_path);
261         guint ii;
262
263         array = camel_store_summary_array (imapx_store->summary);
264
265         for (ii = 0; ii < array->len; ii++) {
266                 CamelStoreInfo *si;
267                 CamelIMAPXStoreInfo *imapx_si;
268                 const gchar *path;
269                 gchar *new_path;
270                 gchar *new_mailbox_name;
271
272                 si = g_ptr_array_index (array, ii);
273                 path = camel_store_info_path (imapx_store->summary, si);
274
275                 /* We need to adjust not only the entry for the renamed
276                  * folder, but also the entries for all the descendants
277                  * of the renamed folder. */
278
279                 if (!g_str_has_prefix (path, old_folder_path))
280                         continue;
281
282                 if (strlen (path) > olen)
283                         new_path = g_strdup_printf (
284                                 "%s/%s", new_folder_path, path + olen + 1);
285                 else
286                         new_path = g_strdup (new_folder_path);
287
288                 camel_store_info_set_string (
289                         imapx_store->summary, si,
290                         CAMEL_STORE_INFO_PATH, new_path);
291
292                 imapx_si = (CamelIMAPXStoreInfo *) si;
293                 g_warn_if_fail (imapx_si->separator != '\0');
294
295                 new_mailbox_name =
296                         camel_imapx_folder_path_to_mailbox (
297                         new_path, imapx_si->separator);
298
299                 /* Takes ownership of new_mailbox_name. */
300                 g_free (imapx_si->mailbox_name);
301                 imapx_si->mailbox_name = new_mailbox_name;
302
303                 camel_store_summary_touch (imapx_store->summary);
304
305                 g_free (new_path);
306         }
307
308         camel_store_summary_array_free (imapx_store->summary, array);
309 }
310
311 static void
312 imapx_store_rename_storage_path (CamelIMAPXStore *imapx_store,
313                                  const gchar *old_mailbox,
314                                  const gchar *new_mailbox)
315 {
316         CamelService *service;
317         const gchar *user_cache_dir;
318         gchar *root_storage_path;
319         gchar *old_storage_path;
320         gchar *new_storage_path;
321
322         service = CAMEL_SERVICE (imapx_store);
323         user_cache_dir = camel_service_get_user_cache_dir (service);
324         root_storage_path = g_build_filename (user_cache_dir, "folders", NULL);
325
326         old_storage_path =
327                 imapx_path_to_physical (root_storage_path, old_mailbox);
328         new_storage_path =
329                 imapx_path_to_physical (root_storage_path, new_mailbox);
330
331         if (g_rename (old_storage_path, new_storage_path) == -1) {
332                 g_warning (
333                         "Could not rename message cache "
334                         "'%s' to '%s: %s: cache reset",
335                         old_storage_path,
336                         new_storage_path,
337                         g_strerror (errno));
338         }
339
340         g_free (root_storage_path);
341         g_free (old_storage_path);
342         g_free (new_storage_path);
343 }
344
345 static void
346 imapx_store_add_mailbox_to_folder (CamelIMAPXStore *store,
347                                    CamelIMAPXMailbox *mailbox)
348 {
349         CamelIMAPXFolder *folder;
350         gchar *folder_path;
351
352         /* Add the CamelIMAPXMailbox to a cached CamelIMAPXFolder. */
353
354         folder_path = camel_imapx_mailbox_dup_folder_path (mailbox);
355
356         folder = camel_object_bag_get (
357                 CAMEL_STORE (store)->folders, folder_path);
358
359         if (folder != NULL) {
360                 camel_imapx_folder_set_mailbox (folder, mailbox);
361                 g_object_unref (folder);
362         }
363
364         g_free (folder_path);
365 }
366
367 static CamelStoreInfoFlags
368 imapx_store_mailbox_attributes_to_flags (CamelIMAPXMailbox *mailbox)
369 {
370         CamelStoreInfoFlags store_info_flags = 0;
371         const gchar *attribute;
372
373         attribute = CAMEL_IMAPX_LIST_ATTR_NOSELECT;
374         if (camel_imapx_mailbox_has_attribute (mailbox, attribute))
375                 store_info_flags |= CAMEL_STORE_INFO_FOLDER_NOSELECT;
376
377         attribute = CAMEL_IMAPX_LIST_ATTR_NOINFERIORS;
378         if (camel_imapx_mailbox_has_attribute (mailbox, attribute))
379                 store_info_flags |= CAMEL_STORE_INFO_FOLDER_NOINFERIORS;
380
381         attribute = CAMEL_IMAPX_LIST_ATTR_HASCHILDREN;
382         if (camel_imapx_mailbox_has_attribute (mailbox, attribute))
383                 store_info_flags |= CAMEL_STORE_INFO_FOLDER_CHILDREN;
384
385         attribute = CAMEL_IMAPX_LIST_ATTR_HASNOCHILDREN;
386         if (camel_imapx_mailbox_has_attribute (mailbox, attribute))
387                 store_info_flags |= CAMEL_STORE_INFO_FOLDER_NOCHILDREN;
388
389         attribute = CAMEL_IMAPX_LIST_ATTR_SUBSCRIBED;
390         if (camel_imapx_mailbox_has_attribute (mailbox, attribute))
391                 store_info_flags |= CAMEL_STORE_INFO_FOLDER_SUBSCRIBED;
392
393         /* XXX Does "\Marked" mean CAMEL_STORE_INFO_FOLDER_FLAGGED?
394          *     Who the heck knows; the enum value is undocumented. */
395
396         return store_info_flags;
397 }
398
399 static void
400 imapx_store_process_mailbox_attributes (CamelIMAPXStore *store,
401                                         CamelIMAPXMailbox *mailbox,
402                                         const gchar *oldname)
403 {
404         CamelFolderInfo *fi;
405         CamelIMAPXStoreInfo *si;
406         CamelStoreInfoFlags flags;
407         CamelSettings *settings;
408         gboolean use_subscriptions;
409         gboolean mailbox_is_subscribed;
410         gboolean mailbox_is_nonexistent;
411         gboolean mailbox_was_in_summary;
412         gboolean mailbox_was_subscribed;
413         gboolean emit_folder_created_subscribed = FALSE;
414         gboolean emit_folder_unsubscribed_deleted = FALSE;
415         gboolean emit_folder_renamed = FALSE;
416         const gchar *folder_path;
417         const gchar *mailbox_name;
418         gchar separator;
419
420         settings = camel_service_ref_settings (CAMEL_SERVICE (store));
421         use_subscriptions = camel_imapx_settings_get_use_subscriptions (
422                 CAMEL_IMAPX_SETTINGS (settings));
423         g_object_unref (settings);
424
425         mailbox_name = camel_imapx_mailbox_get_name (mailbox);
426         separator = camel_imapx_mailbox_get_separator (mailbox);
427
428         mailbox_is_subscribed =
429                 camel_imapx_mailbox_has_attribute (
430                 mailbox, CAMEL_IMAPX_LIST_ATTR_SUBSCRIBED) ||
431                 camel_imapx_mailbox_is_inbox (mailbox_name);
432
433         mailbox_is_nonexistent =
434                 camel_imapx_mailbox_has_attribute (
435                 mailbox, CAMEL_IMAPX_LIST_ATTR_NONEXISTENT);
436
437         /* XXX The flags type transforms from CamelStoreInfoFlags
438          *     to CamelFolderInfoFlags about half-way through this.
439          *     We should really eliminate the confusing redundancy. */
440         flags = imapx_store_mailbox_attributes_to_flags (mailbox);
441
442         /* Summary retains ownership of the returned CamelStoreInfo. */
443         si = camel_imapx_store_summary_mailbox (store->summary, mailbox_name);
444         if (si != NULL) {
445                 mailbox_was_in_summary = TRUE;
446                 if (si->info.flags & CAMEL_STORE_INFO_FOLDER_SUBSCRIBED)
447                         mailbox_was_subscribed = TRUE;
448                 else
449                         mailbox_was_subscribed = FALSE;
450         } else {
451                 /* XXX Shouldn't this take a GError if it can fail? */
452                 si = camel_imapx_store_summary_add_from_mailbox (
453                         store->summary, mailbox);
454                 g_return_if_fail (si != NULL);
455                 mailbox_was_in_summary = FALSE;
456                 mailbox_was_subscribed = FALSE;
457         }
458
459         /* Check whether the flags disagree. */
460         if (si->info.flags != flags) {
461                 si->info.flags = flags;
462                 camel_store_summary_touch (store->summary);
463         }
464
465         folder_path = camel_store_info_path (
466                 store->summary, (CamelStoreInfo *) si);
467         fi = imapx_store_build_folder_info (store, folder_path, flags);
468
469         /* Figure out which signals to emit, if any. */
470         if (use_subscriptions) {
471                 /* If we are honoring folder subscriptions, then
472                  * subscription changes are equivalent to folder
473                  * creation / deletion as far as we're concerned. */
474                 if (mailbox_is_subscribed && !mailbox_is_nonexistent) {
475                         if (oldname != NULL) {
476                                 emit_folder_renamed = TRUE;
477                         } else if (!mailbox_was_subscribed) {
478                                 emit_folder_created_subscribed = TRUE;
479                         }
480                 }
481                 if (!mailbox_is_subscribed && mailbox_was_subscribed)
482                         emit_folder_unsubscribed_deleted = TRUE;
483                 if (mailbox_is_nonexistent && mailbox_was_subscribed)
484                         emit_folder_unsubscribed_deleted = TRUE;
485         } else {
486                 if (!mailbox_is_nonexistent) {
487                         if (oldname != NULL) {
488                                 emit_folder_renamed = TRUE;
489                         } else if (!mailbox_was_in_summary) {
490                                 emit_folder_created_subscribed = TRUE;
491                         }
492                 }
493                 if (mailbox_is_nonexistent && mailbox_was_in_summary)
494                         emit_folder_unsubscribed_deleted = TRUE;
495         }
496
497         /* Suppress all signal emissions when synchronizing folders. */
498         if (g_atomic_int_get (&store->priv->syncing_folders) > 0) {
499                 emit_folder_created_subscribed = FALSE;
500                 emit_folder_unsubscribed_deleted = FALSE;
501                 emit_folder_renamed = FALSE;
502         }
503
504         /* At most one signal emission flag should be set. */
505         g_warn_if_fail (
506                 (emit_folder_created_subscribed ? 1 : 0) +
507                 (emit_folder_unsubscribed_deleted ? 1 : 0) +
508                 (emit_folder_renamed ? 1 : 0) <= 1);
509
510         if (emit_folder_created_subscribed) {
511                 camel_store_folder_created (
512                         CAMEL_STORE (store), fi);
513                 camel_subscribable_folder_subscribed (
514                         CAMEL_SUBSCRIBABLE (store), fi);
515         }
516
517         if (emit_folder_unsubscribed_deleted) {
518                 camel_subscribable_folder_unsubscribed (
519                         CAMEL_SUBSCRIBABLE (store), fi);
520                 camel_store_folder_deleted (
521                         CAMEL_STORE (store), fi);
522         }
523
524         if (emit_folder_renamed) {
525                 gchar *old_folder_path;
526                 gchar *new_folder_path;
527
528                 old_folder_path = camel_imapx_mailbox_to_folder_path (
529                         oldname, separator);
530                 new_folder_path = camel_imapx_mailbox_to_folder_path (
531                         mailbox_name, separator);
532
533                 imapx_store_rename_folder_info (
534                         store, old_folder_path, new_folder_path);
535                 imapx_store_rename_storage_path (
536                         store, old_folder_path, new_folder_path);
537
538                 camel_store_folder_renamed (
539                         CAMEL_STORE (store), old_folder_path, fi);
540
541                 g_free (old_folder_path);
542                 g_free (new_folder_path);
543         }
544
545         camel_folder_info_free (fi);
546 }
547
548 static void
549 imapx_store_process_mailbox_status (CamelIMAPXStore *imapx_store,
550                                     CamelIMAPXMailbox *mailbox)
551 {
552         CamelStore *store;
553         CamelFolder *folder;
554         gchar *folder_path;
555
556         folder_path = camel_imapx_mailbox_dup_folder_path (mailbox);
557         store = CAMEL_STORE (imapx_store);
558
559         /* Update only already opened folders */
560         folder = camel_object_bag_reserve (store->folders, folder_path);
561         if (folder != NULL) {
562                 CamelIMAPXFolder *imapx_folder;
563                 CamelIMAPXSummary *imapx_summary;
564                 guint32 uidvalidity;
565
566                 imapx_folder = CAMEL_IMAPX_FOLDER (folder);
567                 imapx_summary = CAMEL_IMAPX_SUMMARY (folder->summary);
568
569                 uidvalidity = camel_imapx_mailbox_get_uidvalidity (mailbox);
570
571                 if (uidvalidity > 0 && uidvalidity != imapx_summary->validity)
572                         camel_imapx_folder_invalidate_local_cache (
573                                 imapx_folder, uidvalidity);
574
575                 g_object_unref (folder);
576         } else {
577                 camel_object_bag_abort (store->folders, folder_path);
578         }
579
580         g_free (folder_path);
581 }
582
583 static void
584 imapx_store_connect_to_settings (CamelStore *store)
585 {
586         CamelIMAPXStorePrivate *priv;
587         CamelSettings *settings;
588         gulong handler_id;
589
590         /* XXX I considered calling camel_store_folder_info_stale()
591          *     here, but I suspect it would create unnecessary extra
592          *     work for applications during startup since the signal
593          *     is not emitted immediately.
594          *
595          *     Let's just say whomever replaces the settings object
596          *     in a CamelService is reponsible for deciding whether
597          *     camel_store_folder_info_stale() should be called. */
598
599         priv = CAMEL_IMAPX_STORE_GET_PRIVATE (store);
600
601         settings = camel_service_ref_settings (CAMEL_SERVICE (store));
602
603         g_mutex_lock (&priv->settings_lock);
604
605         if (priv->settings != NULL) {
606                 g_signal_handler_disconnect (
607                         priv->settings,
608                         priv->settings_notify_handler_id);
609                 priv->settings_notify_handler_id = 0;
610                 g_clear_object (&priv->settings);
611         }
612
613         priv->settings = g_object_ref (settings);
614
615         handler_id = g_signal_connect (
616                 settings, "notify",
617                 G_CALLBACK (imapx_store_settings_notify_cb), store);
618         priv->settings_notify_handler_id = handler_id;
619
620         g_mutex_unlock (&priv->settings_lock);
621
622         g_object_unref (settings);
623 }
624
625 static void
626 imapx_store_set_property (GObject *object,
627                           guint property_id,
628                           const GValue *value,
629                           GParamSpec *pspec)
630 {
631         switch (property_id) {
632                 case PROP_CONNECTABLE:
633                         camel_network_service_set_connectable (
634                                 CAMEL_NETWORK_SERVICE (object),
635                                 g_value_get_object (value));
636                         return;
637         }
638
639         G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
640 }
641
642 static void
643 imapx_store_get_property (GObject *object,
644                           guint property_id,
645                           GValue *value,
646                           GParamSpec *pspec)
647 {
648         switch (property_id) {
649                 case PROP_CONNECTABLE:
650                         g_value_take_object (
651                                 value,
652                                 camel_network_service_ref_connectable (
653                                 CAMEL_NETWORK_SERVICE (object)));
654                         return;
655
656                 case PROP_HOST_REACHABLE:
657                         g_value_set_boolean (
658                                 value,
659                                 camel_network_service_get_host_reachable (
660                                 CAMEL_NETWORK_SERVICE (object)));
661                         return;
662         }
663
664         G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
665 }
666
667 static void
668 imapx_store_dispose (GObject *object)
669 {
670         CamelIMAPXStore *imapx_store = CAMEL_IMAPX_STORE (object);
671
672         /* Force disconnect so we don't have it run later,
673          * after we've cleaned up some stuff. */
674         if (imapx_store->priv->con_man != NULL) {
675                 camel_service_disconnect_sync (CAMEL_SERVICE (imapx_store), FALSE, NULL, NULL);
676                 g_clear_object (&imapx_store->priv->con_man);
677         }
678
679         if (imapx_store->priv->settings_notify_handler_id > 0) {
680                 g_signal_handler_disconnect (
681                         imapx_store->priv->settings,
682                         imapx_store->priv->settings_notify_handler_id);
683                 imapx_store->priv->settings_notify_handler_id = 0;
684         }
685
686         g_clear_object (&imapx_store->summary);
687
688         g_clear_object (&imapx_store->priv->connecting_server);
689         g_clear_object (&imapx_store->priv->settings);
690         g_clear_object (&imapx_store->priv->namespaces);
691
692         g_hash_table_remove_all (imapx_store->priv->mailboxes);
693
694         /* Chain up to parent's dispose() method. */
695         G_OBJECT_CLASS (camel_imapx_store_parent_class)->dispose (object);
696 }
697
698 static void
699 imapx_store_finalize (GObject *object)
700 {
701         CamelIMAPXStorePrivate *priv;
702
703         priv = CAMEL_IMAPX_STORE_GET_PRIVATE (object);
704
705         g_mutex_clear (&priv->get_finfo_lock);
706
707         g_mutex_clear (&priv->server_lock);
708
709         g_hash_table_destroy (priv->quota_info);
710         g_mutex_clear (&priv->quota_info_lock);
711
712         g_mutex_clear (&priv->settings_lock);
713
714         g_mutex_clear (&priv->namespaces_lock);
715
716         g_hash_table_destroy (priv->mailboxes);
717         g_mutex_clear (&priv->mailboxes_lock);
718
719         /* Chain up to parent's finalize() method. */
720         G_OBJECT_CLASS (camel_imapx_store_parent_class)->finalize (object);
721 }
722
723 static void
724 imapx_store_notify (GObject *object,
725                     GParamSpec *pspec)
726 {
727         if (g_str_equal (pspec->name, "settings")) {
728                 imapx_store_connect_to_settings (CAMEL_STORE (object));
729                 imapx_store_update_store_flags (CAMEL_STORE (object));
730         }
731
732         /* Chain up to parent's notify() method. */
733         G_OBJECT_CLASS (camel_imapx_store_parent_class)->
734                 notify (object, pspec);
735 }
736
737 static gchar *
738 imapx_get_name (CamelService *service,
739                 gboolean brief)
740 {
741         CamelNetworkSettings *network_settings;
742         CamelSettings *settings;
743         gchar *host;
744         gchar *user;
745         gchar *name;
746
747         settings = camel_service_ref_settings (service);
748
749         network_settings = CAMEL_NETWORK_SETTINGS (settings);
750         host = camel_network_settings_dup_host (network_settings);
751         user = camel_network_settings_dup_user (network_settings);
752
753         g_object_unref (settings);
754
755         if (brief)
756                 name = g_strdup_printf (
757                         _("IMAP server %s"), host);
758         else
759                 name = g_strdup_printf (
760                         _("IMAP service for %s on %s"), user, host);
761
762         g_free (host);
763         g_free (user);
764
765         return name;
766 }
767
768 static gboolean
769 imapx_connect_sync (CamelService *service,
770                     GCancellable *cancellable,
771                     GError **error)
772 {
773         CamelIMAPXStore *imapx_store;
774         CamelIMAPXServer *imapx_server;
775         gboolean success;
776
777         imapx_store = CAMEL_IMAPX_STORE (service);
778
779         imapx_server = camel_imapx_store_ref_server (imapx_store, NULL, FALSE, cancellable, error);
780         success = imapx_server != NULL;
781
782         g_clear_object (&imapx_server);
783
784         return success;
785 }
786
787 static gboolean
788 imapx_disconnect_sync (CamelService *service,
789                        gboolean clean,
790                        GCancellable *cancellable,
791                        GError **error)
792 {
793         CamelIMAPXStorePrivate *priv;
794
795         priv = CAMEL_IMAPX_STORE_GET_PRIVATE (service);
796
797         if (priv->con_man != NULL)
798                 camel_imapx_conn_manager_close_connections (priv->con_man, NULL);
799
800         g_mutex_lock (&priv->server_lock);
801
802         g_clear_object (&priv->connecting_server);
803
804         g_mutex_unlock (&priv->server_lock);
805
806         return TRUE;
807 }
808
809 static CamelAuthenticationResult
810 imapx_authenticate_sync (CamelService *service,
811                          const gchar *mechanism,
812                          GCancellable *cancellable,
813                          GError **error)
814 {
815         CamelIMAPXStorePrivate *priv;
816         CamelIMAPXServer *imapx_server;
817         CamelAuthenticationResult result;
818
819         priv = CAMEL_IMAPX_STORE_GET_PRIVATE (service);
820
821         /* This should have been set for us by connect_sync(). */
822         g_mutex_lock (&priv->server_lock);
823         imapx_server = g_object_ref (priv->connecting_server);
824         g_mutex_unlock (&priv->server_lock);
825
826         result = camel_imapx_server_authenticate (
827                 imapx_server, mechanism, cancellable, error);
828
829         g_clear_object (&imapx_server);
830
831         return result;
832 }
833
834 CamelServiceAuthType camel_imapx_password_authtype = {
835         N_("Password"),
836
837         N_("This option will connect to the IMAP server using a "
838            "plaintext password."),
839
840         "",
841         TRUE
842 };
843
844 static GList *
845 imapx_query_auth_types_sync (CamelService *service,
846                              GCancellable *cancellable,
847                              GError **error)
848 {
849         CamelServiceAuthType *authtype;
850         GList *sasl_types = NULL;
851         GList *t, *next;
852         CamelIMAPXServer *server;
853
854         server = camel_imapx_server_new (CAMEL_IMAPX_STORE (service));
855
856         if (!imapx_connect_to_server (server, cancellable, error))
857                 goto exit;
858
859         sasl_types = camel_sasl_authtype_list (FALSE);
860         for (t = sasl_types; t; t = next) {
861                 authtype = t->data;
862                 next = t->next;
863
864                 if (!server->cinfo || !g_hash_table_lookup (server->cinfo->auth_types, authtype->authproto)) {
865                         sasl_types = g_list_remove_link (sasl_types, t);
866                         g_list_free_1 (t);
867                 }
868         }
869
870         sasl_types = g_list_prepend (
871                 sasl_types, &camel_imapx_password_authtype);
872
873 exit:
874         g_object_unref (server);
875
876         return sasl_types;
877 }
878
879 static CamelFolder *
880 get_folder_offline (CamelStore *store,
881                     const gchar *folder_name,
882                     CamelStoreGetFolderFlags flags,
883                     GError **error)
884 {
885         CamelIMAPXStore *imapx_store = CAMEL_IMAPX_STORE (store);
886         CamelFolder *new_folder = NULL;
887         CamelStoreInfo *si;
888         CamelService *service;
889         const gchar *user_cache_dir;
890
891         service = CAMEL_SERVICE (store);
892         user_cache_dir = camel_service_get_user_cache_dir (service);
893
894         si = camel_store_summary_path (imapx_store->summary, folder_name);
895
896         if (si != NULL) {
897                 gchar *base_dir;
898                 gchar *folder_dir;
899
900                 base_dir = g_build_filename (user_cache_dir, "folders", NULL);
901                 folder_dir = imapx_path_to_physical (base_dir, folder_name);
902                 new_folder = camel_imapx_folder_new (
903                         store, folder_dir, folder_name, error);
904                 g_free (folder_dir);
905                 g_free (base_dir);
906
907                 camel_store_summary_info_unref (imapx_store->summary, si);
908         } else {
909                 g_set_error (
910                         error, CAMEL_STORE_ERROR,
911                         CAMEL_STORE_ERROR_NO_FOLDER,
912                         _("No such folder %s"), folder_name);
913         }
914
915         return new_folder;
916 }
917
918 static void
919 fill_fi (CamelStore *store,
920          CamelFolderInfo *fi)
921 {
922         CamelFolder *folder;
923
924         folder = camel_object_bag_peek (store->folders, fi->full_name);
925         if (folder) {
926                 CamelIMAPXFolder *imapx_folder;
927                 CamelIMAPXSummary *ims;
928                 CamelIMAPXMailbox *mailbox;
929
930                 if (folder->summary)
931                         ims = (CamelIMAPXSummary *) folder->summary;
932                 else
933                         ims = (CamelIMAPXSummary *) camel_imapx_summary_new (folder);
934
935                 imapx_folder = CAMEL_IMAPX_FOLDER (folder);
936                 mailbox = camel_imapx_folder_ref_mailbox (imapx_folder);
937
938                 fi->unread = camel_folder_summary_get_unread_count ((CamelFolderSummary *) ims);
939                 fi->total = camel_folder_summary_get_saved_count ((CamelFolderSummary *) ims);
940
941                 g_clear_object (&mailbox);
942
943                 if (!folder->summary)
944                         g_object_unref (ims);
945                 g_object_unref (folder);
946         }
947 }
948
949 static void
950 imapx_delete_folder_from_cache (CamelIMAPXStore *imapx_store,
951                                 const gchar *folder_path)
952 {
953         gchar *state_file;
954         gchar *folder_dir, *storage_path;
955         CamelFolderInfo *fi;
956         CamelService *service;
957         const gchar *user_cache_dir;
958
959         service = CAMEL_SERVICE (imapx_store);
960         user_cache_dir = camel_service_get_user_cache_dir (service);
961
962         storage_path = g_build_filename (user_cache_dir, "folders", NULL);
963         folder_dir = imapx_path_to_physical (storage_path, folder_path);
964         g_free (storage_path);
965         if (g_access (folder_dir, F_OK) != 0) {
966                 g_free (folder_dir);
967                 goto event;
968         }
969
970         /* Delete summary and all the data */
971         state_file = g_build_filename (folder_dir, "cmeta", NULL);
972         g_unlink (state_file);
973         g_free (state_file);
974
975         camel_db_delete_folder (
976                 CAMEL_STORE (imapx_store)->cdb_w, folder_path, NULL);
977         g_rmdir (folder_dir);
978
979         state_file = g_build_filename (folder_dir, "subfolders", NULL);
980         g_rmdir (state_file);
981         g_free (state_file);
982
983         g_rmdir (folder_dir);
984         g_free (folder_dir);
985
986 event:
987         camel_store_summary_remove_path (imapx_store->summary, folder_path);
988         camel_store_summary_save (imapx_store->summary);
989
990         fi = imapx_store_build_folder_info (imapx_store, folder_path, 0);
991         camel_store_folder_deleted (CAMEL_STORE (imapx_store), fi);
992         camel_folder_info_free (fi);
993 }
994
995 static CamelFolderInfo *
996 get_folder_info_offline (CamelStore *store,
997                          const gchar *top,
998                          CamelStoreGetFolderInfoFlags flags,
999                          GError **error)
1000 {
1001         CamelIMAPXStore *imapx_store = CAMEL_IMAPX_STORE (store);
1002         CamelService *service;
1003         CamelSettings *settings;
1004         gboolean include_inbox = FALSE;
1005         CamelFolderInfo *fi;
1006         GPtrArray *folders;
1007         GPtrArray *array;
1008         gboolean use_subscriptions;
1009         guint ii;
1010
1011         service = CAMEL_SERVICE (store);
1012
1013         settings = camel_service_ref_settings (service);
1014
1015         use_subscriptions = camel_imapx_settings_get_use_subscriptions (
1016                 CAMEL_IMAPX_SETTINGS (settings));
1017
1018         g_object_unref (settings);
1019
1020         /* FIXME: obey other flags */
1021
1022         folders = g_ptr_array_new ();
1023
1024         if (top == NULL || top[0] == '\0') {
1025                 include_inbox = TRUE;
1026                 top = "";
1027         }
1028
1029         /* folder_info_build will insert parent nodes as necessary and mark
1030          * them as noselect, which is information we actually don't have at
1031          * the moment. So let it do the right thing by bailing out if it's
1032          * not a folder we're explicitly interested in. */
1033
1034         array = camel_store_summary_array (imapx_store->summary);
1035
1036         for (ii = 0; ii < array->len; ii++) {
1037                 CamelStoreInfo *si;
1038                 const gchar *folder_path;
1039                 gboolean si_is_inbox;
1040                 gboolean si_is_match;
1041
1042                 si = g_ptr_array_index (array, ii);
1043                 folder_path = camel_store_info_path (imapx_store->summary, si);
1044                 si_is_inbox = (g_ascii_strcasecmp (folder_path, "INBOX") == 0);
1045
1046                 /* Filter by folder path. */
1047                 si_is_match =
1048                         (include_inbox && si_is_inbox) ||
1049                         g_str_has_prefix (folder_path, top);
1050
1051                 if (!si_is_match)
1052                         continue;
1053
1054                 /* Filter by subscription flags.
1055                  *
1056                  * Skip the folder if:
1057                  *   The user only wants to see subscribed folders
1058                  *   AND the folder is not subscribed
1059                  *   AND the caller only wants SUBSCRIBED folder info
1060                  *   AND the caller does NOT want a SUBSCRIPTION_LIST
1061                  *
1062                  * Note that having both SUBSCRIBED and SUBSCRIPTION_LIST
1063                  * flags set is contradictory.  SUBSCRIPTION_LIST wins in
1064                  * that case.
1065                  */
1066                 si_is_match =
1067                         !use_subscriptions ||
1068                         (si->flags & CAMEL_STORE_INFO_FOLDER_SUBSCRIBED) ||
1069                         !(flags & CAMEL_STORE_FOLDER_INFO_SUBSCRIBED) ||
1070                         (flags & CAMEL_STORE_FOLDER_INFO_SUBSCRIPTION_LIST);
1071
1072                 if (!si_is_match)
1073                         continue;
1074
1075                 fi = imapx_store_build_folder_info (
1076                         imapx_store, folder_path, 0);
1077                 fi->unread = si->unread;
1078                 fi->total = si->total;
1079                 if ((fi->flags & CAMEL_FOLDER_TYPE_MASK) != 0)
1080                         fi->flags =
1081                                 (fi->flags & CAMEL_FOLDER_TYPE_MASK) |
1082                                 (si->flags & ~CAMEL_FOLDER_TYPE_MASK);
1083                 else
1084                         fi->flags = si->flags;
1085
1086                 /* blah, this gets lost somewhere, i can't be bothered finding out why */
1087                 if (si_is_inbox) {
1088                         fi->flags =
1089                                 (fi->flags & ~CAMEL_FOLDER_TYPE_MASK) |
1090                                 CAMEL_FOLDER_TYPE_INBOX;
1091                         fi->flags |= CAMEL_FOLDER_SYSTEM;
1092                 }
1093
1094                 if (!(si->flags & CAMEL_FOLDER_NOSELECT))
1095                         fill_fi ((CamelStore *) imapx_store, fi);
1096
1097                 if (!fi->child)
1098                         fi->flags |= CAMEL_FOLDER_NOCHILDREN;
1099
1100                 g_ptr_array_add (folders, fi);
1101         }
1102
1103         camel_store_summary_array_free (imapx_store->summary, array);
1104
1105         fi = camel_folder_info_build (folders, top, '/', TRUE);
1106
1107         g_ptr_array_free (folders, TRUE);
1108
1109         return fi;
1110 }
1111
1112 static void
1113 collect_folder_info_for_list (CamelIMAPXStore *imapx_store,
1114                               CamelIMAPXMailbox *mailbox,
1115                               GHashTable *folder_info_results)
1116 {
1117         CamelIMAPXStoreInfo *si;
1118         CamelFolderInfo *fi;
1119         const gchar *folder_path;
1120         const gchar *mailbox_name;
1121
1122         mailbox_name = camel_imapx_mailbox_get_name (mailbox);
1123
1124         si = camel_imapx_store_summary_mailbox (
1125                 imapx_store->summary, mailbox_name);
1126         g_return_if_fail (si != NULL);
1127
1128         folder_path = camel_store_info_path (
1129                 imapx_store->summary, (CamelStoreInfo *) si);
1130         fi = imapx_store_build_folder_info (imapx_store, folder_path, 0);
1131
1132         /* Takes ownership of the CamelFolderInfo. */
1133         g_hash_table_insert (folder_info_results, g_strdup (mailbox_name), fi);
1134 }
1135
1136 static gboolean
1137 fetch_folder_info_for_pattern (CamelIMAPXServer *server,
1138                                CamelIMAPXNamespace *namespace,
1139                                const gchar *pattern,
1140                                CamelStoreGetFolderInfoFlags flags,
1141                                GHashTable *folder_info_results,
1142                                GCancellable *cancellable,
1143                                GError **error)
1144 {
1145         CamelIMAPXStore *imapx_store;
1146         GList *list, *link;
1147         GError *local_error = NULL;
1148         gboolean success;
1149
1150         g_object_ref (server);
1151
1152         imapx_store = camel_imapx_server_ref_store (server);
1153
1154         success = camel_imapx_server_list (server, pattern, flags, cancellable, &local_error);
1155
1156         while (!success && g_error_matches (local_error, CAMEL_IMAPX_SERVER_ERROR, CAMEL_IMAPX_SERVER_ERROR_TRY_RECONNECT)) {
1157                 g_clear_error (&local_error);
1158                 g_clear_object (&server);
1159
1160                 server = camel_imapx_store_ref_server (imapx_store, NULL, FALSE, cancellable, &local_error);
1161                 if (server)
1162                         success = camel_imapx_server_list (server, pattern, flags, cancellable, &local_error);
1163         }
1164
1165         g_clear_object (&server);
1166
1167         if (!success) {
1168                 g_clear_object (&imapx_store);
1169
1170                 if (!g_error_matches (local_error, G_IO_ERROR, G_IO_ERROR_CANCELLED) &&
1171                     camel_imapx_namespace_get_category (namespace) != CAMEL_IMAPX_NAMESPACE_PERSONAL) {
1172                         /* Ignore errors for non-personal namespaces; one such error can be:
1173                            "NO LIST failed: wildcards not permitted in username" */
1174                         g_clear_error (&local_error);
1175                         return TRUE;
1176                 } else if (local_error) {
1177                         g_propagate_error (error, local_error);
1178                 }
1179
1180                 return FALSE;
1181         }
1182
1183         list = camel_imapx_store_list_mailboxes (imapx_store, namespace, pattern);
1184
1185         for (link = list; link != NULL; link = g_list_next (link)) {
1186                 CamelIMAPXMailbox *mailbox;
1187
1188                 mailbox = CAMEL_IMAPX_MAILBOX (link->data);
1189
1190                 collect_folder_info_for_list (
1191                         imapx_store, mailbox, folder_info_results);
1192         }
1193
1194         g_list_free_full (list, (GDestroyNotify) g_object_unref);
1195
1196         g_object_unref (imapx_store);
1197
1198         return TRUE;
1199 }
1200
1201 static gboolean
1202 fetch_folder_info_for_inbox (CamelIMAPXServer *server,
1203                              CamelStoreGetFolderInfoFlags flags,
1204                              GHashTable *folder_info_results,
1205                              GCancellable *cancellable,
1206                              GError **error)
1207 {
1208         CamelIMAPXStore *imapx_store;
1209         GError *local_error = NULL;
1210         gboolean success;
1211
1212         g_object_ref (server);
1213         imapx_store = camel_imapx_server_ref_store (server);
1214
1215         success = camel_imapx_server_list (server, "INBOX", flags, cancellable, &local_error);
1216
1217         while (!success && g_error_matches (local_error, CAMEL_IMAPX_SERVER_ERROR, CAMEL_IMAPX_SERVER_ERROR_TRY_RECONNECT)) {
1218                 g_clear_error (&local_error);
1219                 g_clear_object (&server);
1220
1221                 server = camel_imapx_store_ref_server (imapx_store, NULL, FALSE, cancellable, &local_error);
1222                 if (server)
1223                         success = camel_imapx_server_list (server, "INBOX", flags, cancellable, &local_error);
1224         }
1225
1226         g_clear_object (&server);
1227
1228         if (local_error)
1229                 g_propagate_error (error, local_error);
1230
1231         if (success) {
1232                 CamelIMAPXMailbox *mailbox;
1233
1234                 mailbox = camel_imapx_store_ref_mailbox (imapx_store, "INBOX");
1235                 g_return_val_if_fail (mailbox != NULL, FALSE);
1236
1237                 collect_folder_info_for_list (
1238                         imapx_store, mailbox, folder_info_results);
1239         }
1240
1241         g_object_unref (imapx_store);
1242
1243         return success;
1244 }
1245
1246 static gboolean
1247 fetch_folder_info_for_namespace_category (CamelIMAPXStore *imapx_store,
1248                                           CamelIMAPXServer *server,
1249                                           CamelIMAPXNamespaceCategory category,
1250                                           CamelStoreGetFolderInfoFlags flags,
1251                                           GHashTable *folder_info_results,
1252                                           GCancellable *cancellable,
1253                                           GError **error)
1254 {
1255         CamelIMAPXNamespaceResponse *namespace_response;
1256         GList *list, *link;
1257         gboolean success = TRUE;
1258
1259         namespace_response = camel_imapx_store_ref_namespaces (imapx_store);
1260         g_return_val_if_fail (namespace_response != NULL, FALSE);
1261
1262         list = camel_imapx_namespace_response_list (namespace_response);
1263
1264         for (link = list; link != NULL; link = g_list_next (link)) {
1265                 CamelIMAPXNamespace *namespace;
1266                 CamelIMAPXNamespaceCategory ns_category;
1267                 const gchar *ns_prefix;
1268                 gchar *pattern;
1269
1270                 namespace = CAMEL_IMAPX_NAMESPACE (link->data);
1271                 ns_category = camel_imapx_namespace_get_category (namespace);
1272                 ns_prefix = camel_imapx_namespace_get_prefix (namespace);
1273
1274                 if ((flags & (CAMEL_STORE_FOLDER_INFO_SUBSCRIPTION_LIST | CAMEL_STORE_FOLDER_INFO_SUBSCRIBED)) == 0 && ns_category != category)
1275                         continue;
1276
1277                 pattern = g_strdup_printf ("%s*", ns_prefix);
1278
1279                 success = fetch_folder_info_for_pattern (
1280                         server, namespace, pattern, flags,
1281                         folder_info_results, cancellable, error);
1282
1283                 g_free (pattern);
1284
1285                 if (!success)
1286                         break;
1287         }
1288
1289         g_list_free_full (list, (GDestroyNotify) g_object_unref);
1290
1291         g_object_unref (namespace_response);
1292
1293         return success;
1294 }
1295
1296 static gboolean
1297 fetch_folder_info_from_folder_path (CamelIMAPXStore *imapx_store,
1298                                     CamelIMAPXServer *server,
1299                                     const gchar *folder_path,
1300                                     CamelStoreGetFolderInfoFlags flags,
1301                                     GHashTable *folder_info_results,
1302                                     GCancellable *cancellable,
1303                                     GError **error)
1304 {
1305         CamelIMAPXNamespaceResponse *namespace_response;
1306         CamelIMAPXNamespace *namespace;
1307         gchar *mailbox_name;
1308         gchar *utf7_mailbox_name;
1309         gchar *pattern;
1310         gchar separator;
1311         gboolean success = FALSE;
1312
1313         namespace_response = camel_imapx_store_ref_namespaces (imapx_store);
1314         g_return_val_if_fail (namespace_response != NULL, FALSE);
1315
1316         /* Find a suitable IMAP namespace for the folder path. */
1317         namespace = camel_imapx_namespace_response_lookup_for_path (
1318                 namespace_response, folder_path);
1319         if (namespace == NULL) {
1320                 g_set_error (
1321                         error, CAMEL_STORE_ERROR,
1322                         CAMEL_STORE_ERROR_INVALID,
1323                         _("No IMAP namespace for folder path '%s'"),
1324                         folder_path);
1325                 goto exit;
1326         }
1327
1328         /* Convert the folder path to a mailbox name. */
1329         separator = camel_imapx_namespace_get_separator (namespace);
1330         mailbox_name = g_strdelimit (g_strdup (folder_path), "/", separator);
1331
1332         utf7_mailbox_name = camel_utf8_utf7 (mailbox_name);
1333         pattern = g_strdup_printf ("%s*", utf7_mailbox_name);
1334
1335         success = fetch_folder_info_for_pattern (
1336                 server, namespace, pattern, flags,
1337                 folder_info_results, cancellable, error);
1338
1339         g_free (pattern);
1340         g_free (utf7_mailbox_name);
1341         g_free (mailbox_name);
1342
1343 exit:
1344         g_clear_object (&namespace);
1345         g_clear_object (&namespace_response);
1346
1347         return success;
1348 }
1349
1350 static gboolean
1351 sync_folders (CamelIMAPXStore *imapx_store,
1352               const gchar *root_folder_path,
1353               CamelStoreGetFolderInfoFlags flags,
1354               GCancellable *cancellable,
1355               GError **error)
1356 {
1357         CamelIMAPXServer *server;
1358         GHashTable *folder_info_results;
1359         GPtrArray *array;
1360         guint ii;
1361         gboolean success;
1362
1363         server = camel_imapx_store_ref_server (imapx_store, NULL, FALSE, cancellable, error);
1364         if (server == NULL)
1365                 return FALSE;
1366
1367         /* mailbox name -> CamelFolderInfo */
1368         folder_info_results = g_hash_table_new_full (
1369                 (GHashFunc) imapx_name_hash,
1370                 (GEqualFunc) imapx_name_equal,
1371                 (GDestroyNotify) g_free,
1372                 (GDestroyNotify) camel_folder_info_free);
1373
1374         /* This suppresses CamelStore signal emissions
1375          * in imapx_store_process_mailbox_attributes(). */
1376         g_atomic_int_inc (&imapx_store->priv->syncing_folders);
1377
1378         if (root_folder_path != NULL && *root_folder_path != '\0') {
1379                 success = fetch_folder_info_from_folder_path (
1380                         imapx_store, server, root_folder_path, flags,
1381                         folder_info_results, cancellable, error);
1382         } else {
1383                 gboolean have_folder_info_for_inbox;
1384
1385                 /* XXX We only fetch personal mailboxes at this time. */
1386                 success = fetch_folder_info_for_namespace_category (
1387                         imapx_store, server, CAMEL_IMAPX_NAMESPACE_PERSONAL, flags,
1388                         folder_info_results, cancellable, error);
1389
1390                 have_folder_info_for_inbox =
1391                         g_hash_table_contains (folder_info_results, "INBOX");
1392
1393                 /* XXX Slight hack, mainly for Courier servers.  If INBOX
1394                  *     is not included in any defined personal namespaces,
1395                  *     then LIST it explicitly. */
1396                 if (success && !have_folder_info_for_inbox)
1397                         success = fetch_folder_info_for_inbox (
1398                                 server, flags, folder_info_results,
1399                                 cancellable, error);
1400         }
1401
1402         /* Don't need to test for zero, just decrement atomically. */
1403         g_atomic_int_dec_and_test (&imapx_store->priv->syncing_folders);
1404
1405         if (!success)
1406                 goto exit;
1407
1408         array = camel_store_summary_array (imapx_store->summary);
1409
1410         for (ii = 0; ii < array->len; ii++) {
1411                 CamelStoreInfo *si;
1412                 CamelFolderInfo *fi;
1413                 const gchar *mailbox_name;
1414                 const gchar *si_path;
1415                 gboolean pattern_match;
1416
1417                 si = g_ptr_array_index (array, ii);
1418                 si_path = camel_store_info_path (imapx_store->summary, si);
1419
1420                 mailbox_name = ((CamelIMAPXStoreInfo *) si)->mailbox_name;
1421                 if (mailbox_name == NULL || *mailbox_name == '\0')
1422                         continue;
1423
1424                 pattern_match =
1425                         (root_folder_path == NULL) ||
1426                         (*root_folder_path == '\0') ||
1427                         (g_str_has_prefix (si_path, root_folder_path));
1428                 if (!pattern_match)
1429                         continue;
1430
1431                 fi = g_hash_table_lookup (folder_info_results, mailbox_name);
1432
1433                 if (fi == NULL) {
1434                         gchar *dup_folder_path = g_strdup (si_path);
1435
1436                         if (dup_folder_path != NULL) {
1437                                 /* Do not unsubscribe from it, it influences UI for non-subscribable folders */
1438                                 imapx_delete_folder_from_cache (
1439                                         imapx_store, dup_folder_path);
1440                                 g_free (dup_folder_path);
1441                         } else {
1442                                 camel_store_summary_remove (
1443                                         imapx_store->summary, si);
1444                         }
1445                 }
1446         }
1447
1448         camel_store_summary_array_free (imapx_store->summary, array);
1449
1450 exit:
1451         g_hash_table_destroy (folder_info_results);
1452
1453         g_object_unref (server);
1454
1455         return success;
1456 }
1457
1458 static void
1459 imapx_refresh_finfo (CamelSession *session,
1460                      GCancellable *cancellable,
1461                      CamelIMAPXStore *store,
1462                      GError **error)
1463 {
1464         CamelService *service;
1465         const gchar *display_name;
1466
1467         service = CAMEL_SERVICE (store);
1468         display_name = camel_service_get_display_name (service);
1469
1470         camel_operation_push_message (
1471                 cancellable, _("Retrieving folder list for %s"),
1472                 display_name);
1473
1474         if (!camel_offline_store_get_online (CAMEL_OFFLINE_STORE (store)))
1475                 goto exit;
1476
1477         if (!camel_service_connect_sync (
1478                 CAMEL_SERVICE (store), cancellable, error))
1479                 goto exit;
1480
1481         sync_folders (store, NULL, 0, cancellable, error);
1482
1483         camel_store_summary_save (store->summary);
1484
1485 exit:
1486         camel_operation_pop_message (cancellable);
1487 }
1488
1489 static void
1490 discover_inbox (CamelIMAPXStore *imapx_store,
1491                 GCancellable *cancellable)
1492 {
1493         CamelIMAPXServer *imapx_server;
1494         CamelIMAPXMailbox *mailbox = NULL;
1495         const gchar *attribute;
1496
1497         imapx_server = camel_imapx_store_ref_server (imapx_store, NULL, FALSE, cancellable, NULL);
1498
1499         if (imapx_server == NULL)
1500                 return;
1501
1502         mailbox = camel_imapx_store_ref_mailbox (imapx_store, "INBOX");
1503
1504         if (mailbox == NULL)
1505                 goto exit;
1506
1507         attribute = CAMEL_IMAPX_LIST_ATTR_SUBSCRIBED;
1508         if (!camel_imapx_mailbox_has_attribute (mailbox, attribute)) {
1509                 GError *local_error = NULL;
1510                 gboolean success;
1511
1512                 success = camel_imapx_server_subscribe_mailbox (imapx_server, mailbox, cancellable, &local_error);
1513
1514                 while (!success && g_error_matches (local_error, CAMEL_IMAPX_SERVER_ERROR, CAMEL_IMAPX_SERVER_ERROR_TRY_RECONNECT)) {
1515                         g_clear_error (&local_error);
1516                         g_clear_object (&imapx_server);
1517
1518                         imapx_server = camel_imapx_store_ref_server (imapx_store, NULL, FALSE, cancellable, &local_error);
1519                         if (imapx_server)
1520                                 success = camel_imapx_server_subscribe_mailbox (imapx_server, mailbox, cancellable, &local_error);
1521                 }
1522
1523                 g_clear_error (&local_error);
1524         }
1525
1526 exit:
1527         g_clear_object (&mailbox);
1528         g_clear_object (&imapx_server);
1529 }
1530
1531 static gboolean
1532 imapx_can_refresh_folder (CamelStore *store,
1533                           CamelFolderInfo *info,
1534                           GError **error)
1535 {
1536         CamelService *service;
1537         CamelSettings *settings;
1538         CamelStoreClass *store_class;
1539         gboolean check_all;
1540         gboolean check_subscribed;
1541         gboolean subscribed;
1542         gboolean res;
1543         GError *local_error = NULL;
1544
1545         store_class = CAMEL_STORE_CLASS (camel_imapx_store_parent_class);
1546
1547         service = CAMEL_SERVICE (store);
1548
1549         settings = camel_service_ref_settings (service);
1550
1551         check_all = camel_imapx_settings_get_check_all (
1552                 CAMEL_IMAPX_SETTINGS (settings));
1553
1554         check_subscribed = camel_imapx_settings_get_check_subscribed (
1555                 CAMEL_IMAPX_SETTINGS (settings));
1556
1557         g_object_unref (settings);
1558
1559         subscribed = ((info->flags & CAMEL_FOLDER_SUBSCRIBED) != 0);
1560
1561         res = store_class->can_refresh_folder (store, info, &local_error) ||
1562                 check_all || (check_subscribed && subscribed);
1563
1564         if (local_error != NULL)
1565                 g_propagate_error (error, local_error);
1566
1567         return res;
1568 }
1569
1570 static CamelFolder *
1571 imapx_store_get_folder_sync (CamelStore *store,
1572                              const gchar *folder_name,
1573                              CamelStoreGetFolderFlags flags,
1574                              GCancellable *cancellable,
1575                              GError **error)
1576 {
1577         CamelFolder *folder;
1578         CamelSettings *settings;
1579         gboolean use_real_junk_path = FALSE;
1580         gboolean use_real_trash_path = FALSE;
1581
1582         /* XXX This should be taken care of before we get this far. */
1583         if (*folder_name == '/')
1584                 folder_name++;
1585
1586         folder = get_folder_offline (store, folder_name, flags, error);
1587
1588         /* Configure the folder flags according to IMAPX settings.
1589          *
1590          * XXX Since this is only done when the folder is first created,
1591          *     a restart is required to pick up changes to real Junk/Trash
1592          *     folder settings.  Need to think of a better way.
1593          *
1594          *     Perhaps have CamelStoreSettings grow junk and trash path
1595          *     string properties, and eliminate the CAMEL_FOLDER_IS_JUNK
1596          *     and CAMEL_FOLDER_IS_TRASH flags.  Then add functions like
1597          *     camel_folder_is_junk() and camel_folder_is_trash(), which
1598          *     compare their own full name against CamelStoreSettings.
1599          *
1600          *     Something to think about...
1601          */
1602
1603         settings = camel_service_ref_settings (CAMEL_SERVICE (store));
1604
1605         if (folder != NULL) {
1606                 use_real_junk_path =
1607                         camel_imapx_settings_get_use_real_junk_path (
1608                         CAMEL_IMAPX_SETTINGS (settings));
1609                 use_real_trash_path =
1610                         camel_imapx_settings_get_use_real_trash_path (
1611                         CAMEL_IMAPX_SETTINGS (settings));
1612         }
1613
1614         if (use_real_junk_path) {
1615                 gchar *real_junk_path;
1616
1617                 real_junk_path =
1618                         camel_imapx_settings_dup_real_junk_path (
1619                         CAMEL_IMAPX_SETTINGS (settings));
1620
1621                 /* So we can safely compare strings. */
1622                 if (real_junk_path == NULL)
1623                         real_junk_path = g_strdup ("");
1624
1625                 if (g_ascii_strcasecmp (real_junk_path, folder_name) == 0)
1626                         folder->folder_flags |= CAMEL_FOLDER_IS_JUNK;
1627
1628                 g_free (real_junk_path);
1629         }
1630
1631         if (use_real_trash_path) {
1632                 gchar *real_trash_path;
1633
1634                 real_trash_path =
1635                         camel_imapx_settings_dup_real_trash_path (
1636                         CAMEL_IMAPX_SETTINGS (settings));
1637
1638                 /* So we can safely compare strings. */
1639                 if (real_trash_path == NULL)
1640                         real_trash_path = g_strdup ("");
1641
1642                 if (g_ascii_strcasecmp (real_trash_path, folder_name) == 0)
1643                         folder->folder_flags |= CAMEL_FOLDER_IS_TRASH;
1644
1645                 g_free (real_trash_path);
1646         }
1647
1648         g_object_unref (settings);
1649
1650         return folder;
1651 }
1652
1653 static CamelFolderInfo *
1654 imapx_store_get_folder_info_sync (CamelStore *store,
1655                                   const gchar *top,
1656                                   CamelStoreGetFolderInfoFlags flags,
1657                                   GCancellable *cancellable,
1658                                   GError **error)
1659 {
1660         CamelIMAPXStore *imapx_store;
1661         CamelFolderInfo *fi = NULL;
1662         CamelService *service;
1663         CamelSettings *settings;
1664         gboolean initial_setup = FALSE;
1665         gboolean use_subscriptions;
1666
1667         service = CAMEL_SERVICE (store);
1668         imapx_store = CAMEL_IMAPX_STORE (store);
1669
1670         settings = camel_service_ref_settings (service);
1671
1672         use_subscriptions = camel_imapx_settings_get_use_subscriptions (
1673                 CAMEL_IMAPX_SETTINGS (settings));
1674
1675         g_object_unref (settings);
1676
1677         if (top == NULL)
1678                 top = "";
1679
1680         g_mutex_lock (&imapx_store->priv->get_finfo_lock);
1681
1682         if (!camel_offline_store_get_online (CAMEL_OFFLINE_STORE (store))) {
1683                 fi = get_folder_info_offline (store, top, flags, error);
1684                 goto exit;
1685         }
1686
1687         if (imapx_store->priv->last_refresh_time == 0) {
1688                 imapx_store->priv->last_refresh_time = time (NULL);
1689                 initial_setup = TRUE;
1690         }
1691
1692         /* XXX I don't know why the SUBSCRIBED flag matters here. */
1693         if (!initial_setup && flags & CAMEL_STORE_FOLDER_INFO_SUBSCRIBED) {
1694                 time_t time_since_last_refresh;
1695
1696                 time_since_last_refresh =
1697                         time (NULL) - imapx_store->priv->last_refresh_time;
1698
1699                 if (time_since_last_refresh > FINFO_REFRESH_INTERVAL) {
1700                         CamelSession *session;
1701
1702                         imapx_store->priv->last_refresh_time = time (NULL);
1703
1704                         session = camel_service_ref_session (service);
1705
1706                         camel_session_submit_job (
1707                                 session, (CamelSessionCallback)
1708                                 imapx_refresh_finfo,
1709                                 g_object_ref (store),
1710                                 (GDestroyNotify) g_object_unref);
1711
1712                         g_object_unref (session);
1713                 }
1714         }
1715
1716         /* Avoid server interaction if the FAST flag is set. */
1717         if (!initial_setup && flags & CAMEL_STORE_FOLDER_INFO_FAST) {
1718                 fi = get_folder_info_offline (store, top, flags, error);
1719                 goto exit;
1720         }
1721
1722         if (!sync_folders (imapx_store, top, flags, cancellable, error))
1723                 goto exit;
1724
1725         camel_store_summary_save (imapx_store->summary);
1726
1727         /* ensure the INBOX is subscribed if lsub was preferred*/
1728         if (initial_setup && use_subscriptions)
1729                 discover_inbox (imapx_store, cancellable);
1730
1731         fi = get_folder_info_offline (store, top, flags, error);
1732
1733 exit:
1734         g_mutex_unlock (&imapx_store->priv->get_finfo_lock);
1735
1736         return fi;
1737 }
1738
1739 static CamelFolder *
1740 imapx_store_get_junk_folder_sync (CamelStore *store,
1741                                   GCancellable *cancellable,
1742                                   GError **error)
1743 {
1744         CamelFolder *folder = NULL;
1745         CamelStoreClass *store_class;
1746         CamelSettings *settings;
1747
1748         settings = camel_service_ref_settings (CAMEL_SERVICE (store));
1749         if (camel_imapx_settings_get_use_real_junk_path (CAMEL_IMAPX_SETTINGS (settings))) {
1750                 gchar *real_junk_path;
1751
1752                 real_junk_path = camel_imapx_settings_dup_real_junk_path (CAMEL_IMAPX_SETTINGS (settings));
1753                 if (real_junk_path) {
1754                         folder = camel_store_get_folder_sync (store, real_junk_path, 0, cancellable, NULL);
1755                         g_free (real_junk_path);
1756                 }
1757         }
1758         g_object_unref (settings);
1759
1760         if (folder)
1761                 return folder;
1762
1763         store_class = CAMEL_STORE_CLASS (camel_imapx_store_parent_class);
1764         folder = store_class->get_junk_folder_sync (store, cancellable, error);
1765
1766         if (folder) {
1767                 CamelObject *object = CAMEL_OBJECT (folder);
1768                 CamelService *service;
1769                 const gchar *user_cache_dir;
1770                 gchar *state;
1771
1772                 service = CAMEL_SERVICE (store);
1773                 user_cache_dir = camel_service_get_user_cache_dir (service);
1774
1775                 state = g_build_filename (
1776                         user_cache_dir, "system", "Junk.cmeta", NULL);
1777
1778                 camel_object_set_state_filename (object, state);
1779                 g_free (state);
1780                 /* no defaults? */
1781                 camel_object_state_read (object);
1782         }
1783
1784         return folder;
1785 }
1786
1787 static CamelFolder *
1788 imapx_store_get_trash_folder_sync (CamelStore *store,
1789                                    GCancellable *cancellable,
1790                                    GError **error)
1791 {
1792         CamelFolder *folder = NULL;
1793         CamelStoreClass *store_class;
1794         CamelSettings *settings;
1795
1796         settings = camel_service_ref_settings (CAMEL_SERVICE (store));
1797         if (camel_imapx_settings_get_use_real_trash_path (CAMEL_IMAPX_SETTINGS (settings))) {
1798                 gchar *real_trash_path;
1799
1800                 real_trash_path = camel_imapx_settings_dup_real_trash_path (CAMEL_IMAPX_SETTINGS (settings));
1801                 if (real_trash_path) {
1802                         folder = camel_store_get_folder_sync (store, real_trash_path, 0, cancellable, NULL);
1803                         g_free (real_trash_path);
1804                 }
1805         }
1806         g_object_unref (settings);
1807
1808         if (folder)
1809                 return folder;
1810
1811         store_class = CAMEL_STORE_CLASS (camel_imapx_store_parent_class);
1812         folder = store_class->get_trash_folder_sync (store, cancellable, error);
1813
1814         if (folder) {
1815                 CamelObject *object = CAMEL_OBJECT (folder);
1816                 CamelService *service;
1817                 const gchar *user_cache_dir;
1818                 gchar *state;
1819
1820                 service = CAMEL_SERVICE (store);
1821                 user_cache_dir = camel_service_get_user_cache_dir (service);
1822
1823                 state = g_build_filename (
1824                         user_cache_dir, "system", "Trash.cmeta", NULL);
1825
1826                 camel_object_set_state_filename (object, state);
1827                 g_free (state);
1828                 /* no defaults? */
1829                 camel_object_state_read (object);
1830         }
1831
1832         return folder;
1833 }
1834
1835 static CamelFolderInfo *
1836 imapx_store_create_folder_sync (CamelStore *store,
1837                                 const gchar *parent_name,
1838                                 const gchar *folder_name,
1839                                 GCancellable *cancellable,
1840                                 GError **error)
1841 {
1842         CamelIMAPXNamespaceResponse *namespace_response;
1843         CamelIMAPXNamespace *namespace;
1844         CamelIMAPXStore *imapx_store;
1845         CamelIMAPXServer *imapx_server;
1846         CamelFolder *folder;
1847         CamelIMAPXMailbox *parent_mailbox = NULL;
1848         CamelFolderInfo *fi = NULL;
1849         GList *list;
1850         const gchar *namespace_prefix;
1851         const gchar *parent_mailbox_name;
1852         gchar *mailbox_name = NULL;
1853         gchar separator;
1854         gboolean success;
1855         GError *local_error = NULL;
1856
1857         imapx_store = CAMEL_IMAPX_STORE (store);
1858         imapx_server = camel_imapx_store_ref_server (imapx_store, NULL, FALSE, cancellable, error);
1859
1860         if (imapx_server == NULL)
1861                 return NULL;
1862
1863         if (parent_name == NULL || *parent_name == '\0')
1864                 goto check_namespace;
1865
1866         /* Obtain the separator from the parent CamelIMAPXMailbox. */
1867
1868         folder = camel_store_get_folder_sync (
1869                 store, parent_name, 0, cancellable, error);
1870
1871         if (folder != NULL) {
1872                 parent_mailbox = camel_imapx_folder_list_mailbox (
1873                         CAMEL_IMAPX_FOLDER (folder), cancellable, error);
1874                 g_object_unref (folder);
1875         }
1876
1877         if (parent_mailbox == NULL)
1878                 goto exit;
1879
1880         separator = camel_imapx_mailbox_get_separator (parent_mailbox);
1881         parent_mailbox_name = camel_imapx_mailbox_get_name (parent_mailbox);
1882
1883         mailbox_name = g_strdup_printf (
1884                 "%s%c%s", parent_mailbox_name, separator, folder_name);
1885
1886         g_object_unref (parent_mailbox);
1887
1888         goto check_separator;
1889
1890 check_namespace:
1891
1892         /* Obtain the separator from the first personal namespace.
1893          *
1894          * FIXME The CamelFolder API provides no way to specify a
1895          *       namespace prefix when creating a top-level mailbox,
1896          *       This needs fixed to properly support IMAP namespaces.
1897          */
1898
1899         namespace_response = camel_imapx_store_ref_namespaces (imapx_store);
1900         g_return_val_if_fail (namespace_response != NULL, NULL);
1901
1902         list = camel_imapx_namespace_response_list (namespace_response);
1903         g_return_val_if_fail (list != NULL, NULL);
1904
1905         /* The namespace list is in the order received in the NAMESPACE
1906          * response so the first element should be a personal namespace. */
1907         namespace = CAMEL_IMAPX_NAMESPACE (list->data);
1908
1909         separator = camel_imapx_namespace_get_separator (namespace);
1910         namespace_prefix = camel_imapx_namespace_get_prefix (namespace);
1911
1912         mailbox_name = g_strconcat (namespace_prefix, folder_name, NULL);
1913
1914         g_list_free_full (list, (GDestroyNotify) g_object_unref);
1915         g_object_unref (namespace_response);
1916
1917 check_separator:
1918
1919         if (strchr (folder_name, separator) != NULL) {
1920                 g_set_error (
1921                         error, CAMEL_FOLDER_ERROR,
1922                         CAMEL_FOLDER_ERROR_INVALID_PATH,
1923                         _("The folder name \"%s\" is invalid "
1924                         "because it contains the character \"%c\""),
1925                         folder_name, separator);
1926                 goto exit;
1927         }
1928
1929         /* This also LISTs the mailbox after creating it, which
1930          * triggers the CamelIMAPXStore::mailbox-created signal
1931          * and all the local processing that goes along with it. */
1932         success = camel_imapx_server_create_mailbox (imapx_server, mailbox_name, cancellable, &local_error);
1933
1934         while (!success && g_error_matches (local_error, CAMEL_IMAPX_SERVER_ERROR, CAMEL_IMAPX_SERVER_ERROR_TRY_RECONNECT)) {
1935                 g_clear_error (&local_error);
1936                 g_clear_object (&imapx_server);
1937
1938                 imapx_server = camel_imapx_store_ref_server (imapx_store, NULL, FALSE, cancellable, &local_error);
1939                 if (imapx_server)
1940                         success = camel_imapx_server_create_mailbox (imapx_server, mailbox_name, cancellable, &local_error);
1941         }
1942
1943         if (local_error)
1944                 g_propagate_error (error, local_error);
1945
1946         if (success) {
1947                 fi = imapx_store_build_folder_info (
1948                         imapx_store, folder_name,
1949                         CAMEL_FOLDER_NOCHILDREN);
1950         }
1951
1952 exit:
1953         g_free (mailbox_name);
1954
1955         g_clear_object (&imapx_server);
1956
1957         return fi;
1958 }
1959
1960 static gboolean
1961 imapx_store_delete_folder_sync (CamelStore *store,
1962                                 const gchar *folder_name,
1963                                 GCancellable *cancellable,
1964                                 GError **error)
1965 {
1966         CamelFolder *folder;
1967         CamelIMAPXStore *imapx_store;
1968         CamelIMAPXServer *imapx_server;
1969         CamelIMAPXMailbox *mailbox = NULL;
1970         gboolean success = FALSE;
1971         GError *local_error = NULL;
1972
1973         folder = camel_store_get_folder_sync (
1974                 store, folder_name, 0, cancellable, error);
1975
1976         if (folder == NULL)
1977                 return FALSE;
1978
1979         imapx_store = CAMEL_IMAPX_STORE (store);
1980         imapx_server = camel_imapx_store_ref_server (imapx_store, NULL, FALSE, cancellable, error);
1981
1982         if (imapx_server == NULL)
1983                 goto exit;
1984
1985         mailbox = camel_imapx_folder_list_mailbox (
1986                 CAMEL_IMAPX_FOLDER (folder), cancellable, error);
1987         if (mailbox == NULL)
1988                 goto exit;
1989
1990         success = camel_imapx_server_delete_mailbox (imapx_server, mailbox, cancellable, &local_error);
1991
1992         while (!success && g_error_matches (local_error, CAMEL_IMAPX_SERVER_ERROR, CAMEL_IMAPX_SERVER_ERROR_TRY_RECONNECT)) {
1993                 g_clear_error (&local_error);
1994                 g_clear_object (&imapx_server);
1995
1996                 imapx_server = camel_imapx_store_ref_server (imapx_store, NULL, FALSE, cancellable, &local_error);
1997                 if (imapx_server)
1998                         success = camel_imapx_server_delete_mailbox (imapx_server, mailbox, cancellable, &local_error);
1999         }
2000
2001         if (local_error)
2002                 g_propagate_error (error, local_error);
2003
2004         if (success)
2005                 imapx_delete_folder_from_cache (imapx_store, folder_name);
2006
2007 exit:
2008         g_clear_object (&folder);
2009         g_clear_object (&mailbox);
2010         g_clear_object (&imapx_server);
2011
2012         return success;
2013 }
2014
2015 static gboolean
2016 imapx_store_rename_folder_sync (CamelStore *store,
2017                                 const gchar *old,
2018                                 const gchar *new,
2019                                 GCancellable *cancellable,
2020                                 GError **error)
2021 {
2022         CamelFolder *folder;
2023         CamelService *service;
2024         CamelSettings *settings;
2025         CamelIMAPXStore *imapx_store;
2026         CamelIMAPXServer *imapx_server;
2027         CamelIMAPXMailbox *mailbox = NULL;
2028         CamelIMAPXMailbox *cloned_mailbox;
2029         gchar *new_mailbox_name = NULL;
2030         gchar separator;
2031         gboolean use_subscriptions;
2032         gboolean success = FALSE;
2033         GError *local_error = NULL;
2034
2035         service = CAMEL_SERVICE (store);
2036         imapx_store = CAMEL_IMAPX_STORE (store);
2037
2038         settings = camel_service_ref_settings (service);
2039
2040         use_subscriptions = camel_imapx_settings_get_use_subscriptions (
2041                 CAMEL_IMAPX_SETTINGS (settings));
2042
2043         g_object_unref (settings);
2044
2045         imapx_server = camel_imapx_store_ref_server (imapx_store, NULL, FALSE, cancellable, error);
2046
2047         if (imapx_server == NULL)
2048                 goto exit;
2049
2050         folder = camel_store_get_folder_sync (
2051                 store, old, 0, cancellable, error);
2052
2053         if (folder != NULL) {
2054                 mailbox = camel_imapx_folder_list_mailbox (
2055                         CAMEL_IMAPX_FOLDER (folder), cancellable, error);
2056                 g_object_unref (folder);
2057         }
2058
2059         if (mailbox == NULL)
2060                 goto exit;
2061
2062         /* Assume the renamed mailbox will remain in the same namespace,
2063          * and therefore use the same separator character.  XXX I'm not
2064          * sure if IMAP even allows inter-namespace mailbox renames. */
2065         separator = camel_imapx_mailbox_get_separator (mailbox);
2066         new_mailbox_name = camel_imapx_folder_path_to_mailbox (new, separator);
2067
2068         if (use_subscriptions) {
2069                 success = camel_imapx_server_unsubscribe_mailbox (imapx_server, mailbox, cancellable, &local_error);
2070
2071                 while (!success && g_error_matches (local_error, CAMEL_IMAPX_SERVER_ERROR, CAMEL_IMAPX_SERVER_ERROR_TRY_RECONNECT)) {
2072                         g_clear_error (&local_error);
2073                         g_clear_object (&imapx_server);
2074
2075                         imapx_server = camel_imapx_store_ref_server (imapx_store, NULL, FALSE, cancellable, &local_error);
2076                         if (imapx_server)
2077                                 success = camel_imapx_server_unsubscribe_mailbox (imapx_server, mailbox, cancellable, &local_error);
2078                 }
2079
2080                 g_clear_error (&local_error);
2081         }
2082
2083         success = camel_imapx_server_rename_mailbox (imapx_server, mailbox, new_mailbox_name, cancellable, &local_error);
2084
2085         while (!success && g_error_matches (local_error, CAMEL_IMAPX_SERVER_ERROR, CAMEL_IMAPX_SERVER_ERROR_TRY_RECONNECT)) {
2086                 g_clear_error (&local_error);
2087                 g_clear_object (&imapx_server);
2088
2089                 imapx_server = camel_imapx_store_ref_server (imapx_store, NULL, FALSE, cancellable, &local_error);
2090                 if (imapx_server)
2091                         success = camel_imapx_server_rename_mailbox (imapx_server, mailbox, new_mailbox_name, cancellable, &local_error);
2092         }
2093
2094         if (!success) {
2095                 if (local_error)
2096                         g_propagate_error (error, local_error);
2097                 local_error = NULL;
2098
2099                 if (use_subscriptions) {
2100                         gboolean success_2;
2101
2102                         success_2 = camel_imapx_server_subscribe_mailbox (imapx_server, mailbox, cancellable, &local_error);
2103
2104                         while (!success_2 && g_error_matches (local_error, CAMEL_IMAPX_SERVER_ERROR, CAMEL_IMAPX_SERVER_ERROR_TRY_RECONNECT)) {
2105                                 g_clear_error (&local_error);
2106                                 g_clear_object (&imapx_server);
2107
2108                                 imapx_server = camel_imapx_store_ref_server (imapx_store, NULL, FALSE, cancellable, &local_error);
2109                                 if (imapx_server)
2110                                         success_2 = camel_imapx_server_subscribe_mailbox (imapx_server, mailbox, cancellable, &local_error);
2111                         }
2112
2113                         g_clear_error (&local_error);
2114                 }
2115                 goto exit;
2116         }
2117
2118         /* Rename summary, and handle broken server. */
2119         imapx_store_rename_folder_info (imapx_store, old, new);
2120         imapx_store_rename_storage_path (imapx_store, old, new);
2121
2122         /* Create a cloned CamelIMAPXMailbox with the new mailbox name. */
2123         cloned_mailbox = camel_imapx_mailbox_clone (mailbox, new_mailbox_name);
2124
2125         camel_imapx_folder_set_mailbox (
2126                 CAMEL_IMAPX_FOLDER (folder), cloned_mailbox);
2127
2128         if (use_subscriptions) {
2129                 success = camel_imapx_server_subscribe_mailbox (imapx_server, cloned_mailbox, cancellable, &local_error);
2130
2131                 while (!success && g_error_matches (local_error, CAMEL_IMAPX_SERVER_ERROR, CAMEL_IMAPX_SERVER_ERROR_TRY_RECONNECT)) {
2132                         g_clear_error (&local_error);
2133                         g_clear_object (&imapx_server);
2134
2135                         imapx_server = camel_imapx_store_ref_server (imapx_store, NULL, FALSE, cancellable, &local_error);
2136                         if (imapx_server)
2137                                 success = camel_imapx_server_subscribe_mailbox (imapx_server, cloned_mailbox, cancellable, &local_error);
2138                 }
2139
2140                 if (local_error)
2141                         g_propagate_error (error, local_error);
2142         }
2143
2144         g_clear_object (&cloned_mailbox);
2145
2146 exit:
2147         g_free (new_mailbox_name);
2148
2149         g_clear_object (&mailbox);
2150         g_clear_object (&imapx_server);
2151
2152         return success;
2153 }
2154
2155 static void
2156 imapx_migrate_to_user_cache_dir (CamelService *service)
2157 {
2158         const gchar *user_data_dir, *user_cache_dir;
2159
2160         g_return_if_fail (service != NULL);
2161         g_return_if_fail (CAMEL_IS_SERVICE (service));
2162
2163         user_data_dir = camel_service_get_user_data_dir (service);
2164         user_cache_dir = camel_service_get_user_cache_dir (service);
2165
2166         g_return_if_fail (user_data_dir != NULL);
2167         g_return_if_fail (user_cache_dir != NULL);
2168
2169         /* migrate only if the source directory exists and the destination doesn't */
2170         if (g_file_test (user_data_dir, G_FILE_TEST_EXISTS | G_FILE_TEST_IS_DIR) &&
2171             !g_file_test (user_cache_dir, G_FILE_TEST_EXISTS | G_FILE_TEST_IS_DIR)) {
2172                 gchar *parent_dir;
2173
2174                 parent_dir = g_path_get_dirname (user_cache_dir);
2175                 g_mkdir_with_parents (parent_dir, S_IRWXU);
2176                 g_free (parent_dir);
2177
2178                 if (g_rename (user_data_dir, user_cache_dir) == -1)
2179                         g_debug ("%s: Failed to migrate '%s' to '%s': %s", G_STRFUNC, user_data_dir, user_cache_dir, g_strerror (errno));
2180         }
2181 }
2182
2183 static gboolean
2184 imapx_store_initable_init (GInitable *initable,
2185                            GCancellable *cancellable,
2186                            GError **error)
2187 {
2188         CamelIMAPXStore *imapx_store;
2189         CamelStore *store;
2190         CamelService *service;
2191         const gchar *user_cache_dir;
2192         gchar *summary;
2193
2194         imapx_store = CAMEL_IMAPX_STORE (initable);
2195         store = CAMEL_STORE (initable);
2196         service = CAMEL_SERVICE (initable);
2197
2198         store->flags |= CAMEL_STORE_USE_CACHE_DIR;
2199         imapx_migrate_to_user_cache_dir (service);
2200
2201         /* Chain up to parent interface's init() method. */
2202         if (!parent_initable_interface->init (initable, cancellable, error))
2203                 return FALSE;
2204
2205         service = CAMEL_SERVICE (initable);
2206         user_cache_dir = camel_service_get_user_cache_dir (service);
2207
2208         imapx_store->summary =
2209                 g_object_new (CAMEL_TYPE_IMAPX_STORE_SUMMARY, NULL);
2210
2211         summary = g_build_filename (user_cache_dir, ".ev-store-summary", NULL);
2212         camel_store_summary_set_filename (imapx_store->summary, summary);
2213         if (camel_store_summary_load (imapx_store->summary) == -1) {
2214                 camel_store_summary_touch (imapx_store->summary);
2215                 camel_store_summary_save (imapx_store->summary);
2216         }
2217
2218         g_free (summary);
2219
2220         return TRUE;
2221 }
2222
2223 static const gchar *
2224 imapx_store_get_service_name (CamelNetworkService *service,
2225                               CamelNetworkSecurityMethod method)
2226 {
2227         const gchar *service_name;
2228
2229         switch (method) {
2230                 case CAMEL_NETWORK_SECURITY_METHOD_SSL_ON_ALTERNATE_PORT:
2231                         service_name = "imaps";
2232                         break;
2233
2234                 default:
2235                         service_name = "imap";
2236                         break;
2237         }
2238
2239         return service_name;
2240 }
2241
2242 static guint16
2243 imapx_store_get_default_port (CamelNetworkService *service,
2244                               CamelNetworkSecurityMethod method)
2245 {
2246         guint16 default_port;
2247
2248         switch (method) {
2249                 case CAMEL_NETWORK_SECURITY_METHOD_SSL_ON_ALTERNATE_PORT:
2250                         default_port = IMAPS_PORT;
2251                         break;
2252
2253                 default:
2254                         default_port = IMAP_PORT;
2255                         break;
2256         }
2257
2258         return default_port;
2259 }
2260
2261 static gboolean
2262 imapx_store_folder_is_subscribed (CamelSubscribable *subscribable,
2263                                   const gchar *folder_name)
2264 {
2265         CamelIMAPXStore *imapx_store;
2266         CamelStoreInfo *si;
2267         gint is_subscribed = FALSE;
2268
2269         imapx_store = CAMEL_IMAPX_STORE (subscribable);
2270
2271         if (folder_name && *folder_name == '/')
2272                 folder_name++;
2273
2274         si = camel_store_summary_path (imapx_store->summary, folder_name);
2275         if (si != NULL) {
2276                 if (si->flags & CAMEL_STORE_INFO_FOLDER_SUBSCRIBED)
2277                         is_subscribed = TRUE;
2278                 camel_store_summary_info_unref (imapx_store->summary, si);
2279         }
2280
2281         return is_subscribed;
2282 }
2283
2284 static void
2285 imapx_ensure_parents_subscribed (CamelIMAPXStore *imapx_store,
2286                                  const gchar *folder_name)
2287 {
2288         GSList *parents = NULL, *iter;
2289         CamelSubscribable *subscribable;
2290         CamelFolderInfo *fi;
2291         gchar *parent, *sep;
2292
2293         g_return_if_fail (CAMEL_IS_IMAPX_STORE (imapx_store));
2294         g_return_if_fail (folder_name != NULL);
2295
2296         subscribable = CAMEL_SUBSCRIBABLE (imapx_store);
2297
2298         if (folder_name && *folder_name == '/')
2299                 folder_name++;
2300
2301         parent = g_strdup (folder_name);
2302         while (sep = strrchr (parent, '/'), sep) {
2303                 *sep = '\0';
2304
2305                 fi = camel_folder_info_new ();
2306
2307                 fi->display_name = strrchr (parent, '/');
2308                 if (fi->display_name != NULL)
2309                         fi->display_name = g_strdup (fi->display_name + 1);
2310                 else
2311                         fi->display_name = g_strdup (parent);
2312
2313                 fi->full_name = g_strdup (parent);
2314
2315                 /* Since this is a "fake" folder node, it is not selectable. */
2316                 fi->flags |= CAMEL_FOLDER_NOSELECT;
2317
2318                 parents = g_slist_prepend (parents, fi);
2319         }
2320
2321         for (iter = parents; iter; iter = g_slist_next (iter)) {
2322                 fi = iter->data;
2323
2324                 camel_subscribable_folder_subscribed (subscribable, fi);
2325                 camel_folder_info_free (fi);
2326         }
2327 }
2328
2329 static gboolean
2330 imapx_store_subscribe_folder_sync (CamelSubscribable *subscribable,
2331                                    const gchar *folder_name,
2332                                    GCancellable *cancellable,
2333                                    GError **error)
2334 {
2335         CamelFolder *folder;
2336         CamelIMAPXStore *imapx_store;
2337         CamelIMAPXServer *imapx_server;
2338         CamelIMAPXMailbox *mailbox = NULL;
2339         gboolean success = FALSE;
2340         GError *local_error = NULL;
2341
2342         imapx_store = CAMEL_IMAPX_STORE (subscribable);
2343         imapx_server = camel_imapx_store_ref_server (imapx_store, NULL, FALSE, cancellable, error);
2344
2345         if (imapx_server == NULL)
2346                 goto exit;
2347
2348         folder = camel_store_get_folder_sync (
2349                 CAMEL_STORE (subscribable),
2350                 folder_name, 0, cancellable, error);
2351
2352         if (folder != NULL) {
2353                 mailbox = camel_imapx_folder_list_mailbox (
2354                         CAMEL_IMAPX_FOLDER (folder), cancellable, error);
2355                 g_object_unref (folder);
2356         }
2357
2358         if (mailbox == NULL)
2359                 goto exit;
2360
2361         success = camel_imapx_server_subscribe_mailbox (imapx_server, mailbox, cancellable, &local_error);
2362
2363         while (!success && g_error_matches (local_error, CAMEL_IMAPX_SERVER_ERROR, CAMEL_IMAPX_SERVER_ERROR_TRY_RECONNECT)) {
2364                 g_clear_error (&local_error);
2365                 g_clear_object (&imapx_server);
2366
2367                 imapx_server = camel_imapx_store_ref_server (imapx_store, NULL, FALSE, cancellable, &local_error);
2368                 if (imapx_server)
2369                         success = camel_imapx_server_subscribe_mailbox (imapx_server, mailbox, cancellable, &local_error);
2370         }
2371
2372         if (local_error)
2373                 g_propagate_error (error, local_error);
2374
2375         if (success) {
2376                 CamelFolderInfo *fi;
2377
2378                 /* without this the folder is not visible if parents are not subscribed */
2379                 imapx_ensure_parents_subscribed (imapx_store, folder_name);
2380
2381                 fi = imapx_store_build_folder_info (
2382                         CAMEL_IMAPX_STORE (subscribable), folder_name, 0);
2383                 camel_subscribable_folder_subscribed (subscribable, fi);
2384                 camel_folder_info_free (fi);
2385         }
2386
2387 exit:
2388         g_clear_object (&mailbox);
2389         g_clear_object (&imapx_server);
2390
2391         return success;
2392 }
2393
2394 static gboolean
2395 imapx_store_unsubscribe_folder_sync (CamelSubscribable *subscribable,
2396                                      const gchar *folder_name,
2397                                      GCancellable *cancellable,
2398                                      GError **error)
2399 {
2400         CamelFolder *folder;
2401         CamelIMAPXStore *imapx_store;
2402         CamelIMAPXServer *imapx_server;
2403         CamelIMAPXMailbox *mailbox = NULL;
2404         gboolean success = FALSE;
2405         GError *local_error = NULL;
2406
2407         imapx_store = CAMEL_IMAPX_STORE (subscribable);
2408         imapx_server = camel_imapx_store_ref_server (imapx_store, NULL, FALSE, cancellable, error);
2409
2410         if (imapx_server == NULL)
2411                 goto exit;
2412
2413         folder = camel_store_get_folder_sync (
2414                 CAMEL_STORE (subscribable),
2415                 folder_name, 0, cancellable, error);
2416
2417         if (folder != NULL) {
2418                 mailbox = camel_imapx_folder_list_mailbox (
2419                         CAMEL_IMAPX_FOLDER (folder), cancellable, error);
2420                 g_object_unref (folder);
2421         }
2422
2423         if (mailbox == NULL)
2424                 goto exit;
2425
2426         success = camel_imapx_server_unsubscribe_mailbox (imapx_server, mailbox, cancellable, &local_error);
2427
2428         while (!success && g_error_matches (local_error, CAMEL_IMAPX_SERVER_ERROR, CAMEL_IMAPX_SERVER_ERROR_TRY_RECONNECT)) {
2429                 g_clear_error (&local_error);
2430                 g_clear_object (&imapx_server);
2431
2432                 imapx_server = camel_imapx_store_ref_server (imapx_store, NULL, FALSE, cancellable, &local_error);
2433                 if (imapx_server)
2434                         success = camel_imapx_server_unsubscribe_mailbox (imapx_server, mailbox, cancellable, &local_error);
2435         }
2436
2437         if (local_error)
2438                 g_propagate_error (error, local_error);
2439
2440         if (success) {
2441                 CamelFolderInfo *fi;
2442
2443                 fi = imapx_store_build_folder_info (
2444                         CAMEL_IMAPX_STORE (subscribable), folder_name, 0);
2445                 camel_subscribable_folder_unsubscribed (subscribable, fi);
2446                 camel_folder_info_free (fi);
2447         }
2448
2449 exit:
2450         g_clear_object (&mailbox);
2451         g_clear_object (&imapx_server);
2452
2453         return success;
2454 }
2455
2456 static void
2457 imapx_store_mailbox_created (CamelIMAPXStore *imapx_store,
2458                              CamelIMAPXMailbox *mailbox)
2459 {
2460         e (
2461                 '*',
2462                 "%s::mailbox-created (\"%s\")\n",
2463                 G_OBJECT_TYPE_NAME (imapx_store),
2464                 camel_imapx_mailbox_get_name (mailbox));
2465
2466         imapx_store_add_mailbox_to_folder (imapx_store, mailbox);
2467         imapx_store_process_mailbox_attributes (imapx_store, mailbox, NULL);
2468 }
2469
2470 static void
2471 imapx_store_mailbox_renamed (CamelIMAPXStore *imapx_store,
2472                              CamelIMAPXMailbox *mailbox,
2473                              const gchar *oldname)
2474 {
2475         e (
2476                 '*',
2477                 "%s::mailbox-renamed (\"%s\" -> \"%s\")\n",
2478                 G_OBJECT_TYPE_NAME (imapx_store), oldname,
2479                 camel_imapx_mailbox_get_name (mailbox));
2480
2481         imapx_store_process_mailbox_attributes (imapx_store, mailbox, oldname);
2482         imapx_store_process_mailbox_status (imapx_store, mailbox);
2483 }
2484
2485 static void
2486 imapx_store_mailbox_updated (CamelIMAPXStore *imapx_store,
2487                              CamelIMAPXMailbox *mailbox)
2488 {
2489         e (
2490                 '*',
2491                 "%s::mailbox-updated (\"%s\")\n",
2492                 G_OBJECT_TYPE_NAME (imapx_store),
2493                 camel_imapx_mailbox_get_name (mailbox));
2494
2495         imapx_store_process_mailbox_attributes (imapx_store, mailbox, NULL);
2496         imapx_store_process_mailbox_status (imapx_store, mailbox);
2497 }
2498
2499 static void
2500 camel_imapx_store_class_init (CamelIMAPXStoreClass *class)
2501 {
2502         GObjectClass *object_class;
2503         CamelServiceClass *service_class;
2504         CamelStoreClass *store_class;
2505
2506         g_type_class_add_private (class, sizeof (CamelIMAPXStorePrivate));
2507
2508         object_class = G_OBJECT_CLASS (class);
2509         object_class->set_property = imapx_store_set_property;
2510         object_class->get_property = imapx_store_get_property;
2511         object_class->dispose = imapx_store_dispose;
2512         object_class->finalize = imapx_store_finalize;
2513         object_class->notify = imapx_store_notify;
2514
2515         service_class = CAMEL_SERVICE_CLASS (class);
2516         service_class->settings_type = CAMEL_TYPE_IMAPX_SETTINGS;
2517         service_class->get_name = imapx_get_name;
2518         service_class->connect_sync = imapx_connect_sync;
2519         service_class->disconnect_sync = imapx_disconnect_sync;
2520         service_class->authenticate_sync = imapx_authenticate_sync;
2521         service_class->query_auth_types_sync = imapx_query_auth_types_sync;
2522
2523         store_class = CAMEL_STORE_CLASS (class);
2524         store_class->hash_folder_name = imapx_name_hash;
2525         store_class->equal_folder_name = imapx_name_equal;
2526         store_class->can_refresh_folder = imapx_can_refresh_folder;
2527         store_class->get_folder_sync = imapx_store_get_folder_sync;
2528         store_class->get_folder_info_sync = imapx_store_get_folder_info_sync;
2529         store_class->get_junk_folder_sync = imapx_store_get_junk_folder_sync;
2530         store_class->get_trash_folder_sync = imapx_store_get_trash_folder_sync;
2531         store_class->create_folder_sync = imapx_store_create_folder_sync;
2532         store_class->delete_folder_sync = imapx_store_delete_folder_sync;
2533         store_class->rename_folder_sync = imapx_store_rename_folder_sync;
2534
2535         class->mailbox_created = imapx_store_mailbox_created;
2536         class->mailbox_renamed = imapx_store_mailbox_renamed;
2537         class->mailbox_updated = imapx_store_mailbox_updated;
2538
2539         /* Inherited from CamelNetworkService. */
2540         g_object_class_override_property (
2541                 object_class,
2542                 PROP_CONNECTABLE,
2543                 "connectable");
2544
2545         /* Inherited from CamelNetworkService. */
2546         g_object_class_override_property (
2547                 object_class,
2548                 PROP_HOST_REACHABLE,
2549                 "host-reachable");
2550
2551         signals[MAILBOX_CREATED] = g_signal_new (
2552                 "mailbox-created",
2553                 G_OBJECT_CLASS_TYPE (class),
2554                 G_SIGNAL_RUN_FIRST,
2555                 G_STRUCT_OFFSET (CamelIMAPXStoreClass, mailbox_created),
2556                 NULL, NULL, NULL,
2557                 G_TYPE_NONE, 1,
2558                 CAMEL_TYPE_IMAPX_MAILBOX);
2559
2560         signals[MAILBOX_RENAMED] = g_signal_new (
2561                 "mailbox-renamed",
2562                 G_OBJECT_CLASS_TYPE (class),
2563                 G_SIGNAL_RUN_FIRST,
2564                 G_STRUCT_OFFSET (CamelIMAPXStoreClass, mailbox_renamed),
2565                 NULL, NULL, NULL,
2566                 G_TYPE_NONE, 2,
2567                 CAMEL_TYPE_IMAPX_MAILBOX,
2568                 G_TYPE_STRING);
2569
2570         signals[MAILBOX_UPDATED] = g_signal_new (
2571                 "mailbox-updated",
2572                 G_OBJECT_CLASS_TYPE (class),
2573                 G_SIGNAL_RUN_FIRST,
2574                 G_STRUCT_OFFSET (CamelIMAPXStoreClass, mailbox_updated),
2575                 NULL, NULL, NULL,
2576                 G_TYPE_NONE, 1,
2577                 CAMEL_TYPE_IMAPX_MAILBOX);
2578 }
2579
2580 static void
2581 camel_imapx_store_initable_init (GInitableIface *iface)
2582 {
2583         parent_initable_interface = g_type_interface_peek_parent (iface);
2584
2585         iface->init = imapx_store_initable_init;
2586 }
2587
2588 static void
2589 camel_network_service_init (CamelNetworkServiceInterface *iface)
2590 {
2591         iface->get_service_name = imapx_store_get_service_name;
2592         iface->get_default_port = imapx_store_get_default_port;
2593 }
2594
2595 static void
2596 camel_subscribable_init (CamelSubscribableInterface *iface)
2597 {
2598         iface->folder_is_subscribed = imapx_store_folder_is_subscribed;
2599         iface->subscribe_folder_sync = imapx_store_subscribe_folder_sync;
2600         iface->unsubscribe_folder_sync = imapx_store_unsubscribe_folder_sync;
2601 }
2602
2603 static void
2604 camel_imapx_store_init (CamelIMAPXStore *store)
2605 {
2606         store->priv = CAMEL_IMAPX_STORE_GET_PRIVATE (store);
2607
2608         store->priv->con_man = camel_imapx_conn_manager_new (CAMEL_STORE (store));
2609
2610         g_mutex_init (&store->priv->get_finfo_lock);
2611
2612         g_mutex_init (&store->priv->namespaces_lock);
2613         g_mutex_init (&store->priv->mailboxes_lock);
2614         /* Hash table key is owned by the CamelIMAPXMailbox. */
2615         store->priv->mailboxes = g_hash_table_new_full (g_str_hash, g_str_equal, NULL, g_object_unref);
2616
2617         /* Initialize to zero to ensure we always obtain fresh folder
2618          * info on startup.  See imapx_store_get_folder_info_sync(). */
2619         store->priv->last_refresh_time = 0;
2620
2621         g_mutex_init (&store->priv->server_lock);
2622
2623         store->priv->quota_info = g_hash_table_new_full (
2624                 (GHashFunc) g_str_hash,
2625                 (GEqualFunc) g_str_equal,
2626                 (GDestroyNotify) g_free,
2627                 (GDestroyNotify) camel_folder_quota_info_free);
2628         g_mutex_init (&store->priv->quota_info_lock);
2629
2630         g_mutex_init (&store->priv->settings_lock);
2631
2632         imapx_utils_init ();
2633
2634         g_signal_connect (
2635                 store, "notify::settings",
2636                 G_CALLBACK (imapx_store_update_store_flags), NULL);
2637 }
2638
2639 /**
2640  * camel_imapx_store_ref_server:
2641  * @store: a #CamelIMAPXStore
2642  * @folder_name: name of a folder, for which it'll be used; can be %NULL
2643  * @cancellable: a #GCancellable to use ofr possible new connection creation, or %NULL
2644  * @error: return location for a #GError, or %NULL
2645  *
2646  * Returns the #CamelIMAPXServer for @store, if available.
2647  *
2648  * As a convenience, if the @store is not currently connected to an IMAP
2649  * server, the function sets @error to %CAMEL_SERVER_ERROR_UNAVAILABLE and
2650  * returns %NULL.  If an operation can possibly be executed while offline,
2651  * pass %NULL for @error.
2652  *
2653  * The returned #CamelIMAPXServer is referenced for thread-safety and must
2654  * be unreferenced with g_object_unref() when finished with it.
2655  *
2656  * Returns: a #CamelIMAPXServer, or %NULL
2657  *
2658  * Since: 3.10
2659  **/
2660 CamelIMAPXServer *
2661 camel_imapx_store_ref_server (CamelIMAPXStore *store,
2662                               const gchar *folder_name,
2663                               gboolean for_expensive_job,
2664                               GCancellable *cancellable,
2665                               GError **error)
2666 {
2667         CamelIMAPXServer *server = NULL;
2668
2669         g_return_val_if_fail (CAMEL_IS_IMAPX_STORE (store), NULL);
2670
2671         server = camel_imapx_conn_manager_get_connection (
2672                 store->priv->con_man, folder_name, for_expensive_job, cancellable, error);
2673
2674         if (!server && error && !*error) {
2675                 g_set_error (
2676                         error, CAMEL_SERVICE_ERROR,
2677                         CAMEL_SERVICE_ERROR_UNAVAILABLE,
2678                         _("You must be working online "
2679                         "to complete this operation"));
2680         }
2681
2682         return server;
2683 }
2684
2685 /* The caller should hold the store->priv->server_lock already, when calling this */
2686 void
2687 camel_imapx_store_set_connecting_server (CamelIMAPXStore *store,
2688                                          CamelIMAPXServer *server,
2689                                          gboolean is_concurrent_connection)
2690 {
2691         g_return_if_fail (CAMEL_IS_IMAPX_STORE (store));
2692
2693         if (server)
2694                 g_return_if_fail (CAMEL_IS_IMAPX_SERVER (server));
2695
2696         g_mutex_lock (&store->priv->server_lock);
2697
2698         if (store->priv->connecting_server != server) {
2699                 g_clear_object (&store->priv->connecting_server);
2700                 if (server)
2701                         store->priv->connecting_server = g_object_ref (server);
2702         }
2703
2704         store->priv->is_concurrent_connection = is_concurrent_connection;
2705
2706         g_mutex_unlock (&store->priv->server_lock);
2707 }
2708
2709 gboolean
2710 camel_imapx_store_is_connecting_concurrent_connection (CamelIMAPXStore *imapx_store)
2711 {
2712         gboolean res;
2713
2714         g_return_val_if_fail (CAMEL_IS_IMAPX_STORE (imapx_store), FALSE);
2715
2716         g_mutex_lock (&imapx_store->priv->server_lock);
2717         res = imapx_store->priv->is_concurrent_connection;
2718         g_mutex_unlock (&imapx_store->priv->server_lock);
2719
2720         return res;
2721 }
2722
2723 void
2724 camel_imapx_store_folder_op_done (CamelIMAPXStore *store,
2725                                   CamelIMAPXServer *server,
2726                                   const gchar *folder_name)
2727 {
2728         g_return_if_fail (CAMEL_IS_IMAPX_STORE (store));
2729         g_return_if_fail (CAMEL_IS_IMAPX_SERVER (server));
2730         g_return_if_fail (folder_name != NULL);
2731
2732         camel_imapx_conn_manager_update_con_info (
2733                 store->priv->con_man, server, folder_name);
2734 }
2735
2736 /**
2737  * camel_imapx_store_ref_namespaces:
2738  * @imapx_store: a #CamelIMAPXStore
2739  *
2740  * Returns the #CamelIMAPXNamespaceResponse for @is. This is obtained
2741  * during the connection phase if the IMAP server lists the "NAMESPACE"
2742  * keyword in its CAPABILITY response, or else is fabricated from the
2743  * first LIST response.
2744  *
2745  * The returned #CamelIMAPXNamespaceResponse is reference for thread-safety
2746  * and must be unreferenced with g_object_unref() when finished with it.
2747  *
2748  * Returns: a #CamelIMAPXNamespaceResponse
2749  *
2750  * Since: 3.12.2
2751  **/
2752 CamelIMAPXNamespaceResponse *
2753 camel_imapx_store_ref_namespaces (CamelIMAPXStore *imapx_store)
2754 {
2755         CamelIMAPXNamespaceResponse *namespaces = NULL;
2756
2757         g_return_val_if_fail (CAMEL_IS_IMAPX_STORE (imapx_store), NULL);
2758
2759         g_mutex_lock (&imapx_store->priv->namespaces_lock);
2760
2761         if (imapx_store->priv->namespaces != NULL)
2762                 namespaces = g_object_ref (imapx_store->priv->namespaces);
2763
2764         g_mutex_unlock (&imapx_store->priv->namespaces_lock);
2765
2766         return namespaces;
2767 }
2768
2769 void
2770 camel_imapx_store_set_namespaces (CamelIMAPXStore *imapx_store,
2771                                   CamelIMAPXNamespaceResponse *namespaces)
2772 {
2773         g_return_if_fail (CAMEL_IS_IMAPX_STORE (imapx_store));
2774         if (namespaces)
2775                 g_return_if_fail (CAMEL_IS_IMAPX_NAMESPACE_RESPONSE (namespaces));
2776
2777         if (namespaces)
2778                 g_object_ref (namespaces);
2779
2780         g_mutex_lock (&imapx_store->priv->namespaces_lock);
2781
2782         g_clear_object (&imapx_store->priv->namespaces);
2783         imapx_store->priv->namespaces = namespaces;
2784
2785         g_mutex_unlock (&imapx_store->priv->namespaces_lock);
2786 }
2787
2788 static void
2789 imapx_store_add_mailbox_unlocked (CamelIMAPXStore *imapx_store,
2790                                   CamelIMAPXMailbox *mailbox)
2791 {
2792         const gchar *mailbox_name;
2793
2794         /* Acquire "mailboxes_lock" before calling. */
2795
2796         mailbox_name = camel_imapx_mailbox_get_name (mailbox);
2797         g_return_if_fail (mailbox_name != NULL);
2798
2799         /* Use g_hash_table_replace() here instead of g_hash_table_insert().
2800          * The hash table key is owned by the hash table value, so if we're
2801          * replacing an existing table item we want to replace both the key
2802          * and value to avoid data corruption. */
2803         g_hash_table_replace (
2804                 imapx_store->priv->mailboxes,
2805                 (gpointer) mailbox_name,
2806                 g_object_ref (mailbox));
2807 }
2808
2809 static gboolean
2810 imapx_store_remove_mailbox_unlocked (CamelIMAPXStore *imapx_store,
2811                                      CamelIMAPXMailbox *mailbox)
2812 {
2813         const gchar *mailbox_name;
2814
2815         /* Acquire "mailboxes_lock" before calling. */
2816
2817         mailbox_name = camel_imapx_mailbox_get_name (mailbox);
2818         g_return_val_if_fail (mailbox_name != NULL, FALSE);
2819
2820         return g_hash_table_remove (imapx_store->priv->mailboxes, mailbox_name);
2821 }
2822
2823 static CamelIMAPXMailbox *
2824 imapx_store_ref_mailbox_unlocked (CamelIMAPXStore *imapx_store,
2825                                   const gchar *mailbox_name)
2826 {
2827         CamelIMAPXMailbox *mailbox;
2828
2829         /* Acquire "mailboxes_lock" before calling. */
2830
2831         g_return_val_if_fail (mailbox_name != NULL, NULL);
2832
2833         /* The INBOX mailbox is case-insensitive. */
2834         if (g_ascii_strcasecmp (mailbox_name, "INBOX") == 0)
2835                 mailbox_name = "INBOX";
2836
2837         mailbox = g_hash_table_lookup (imapx_store->priv->mailboxes, mailbox_name);
2838
2839         /* Remove non-existent mailboxes as we find them. */
2840         if (mailbox != NULL && !camel_imapx_mailbox_exists (mailbox)) {
2841                 imapx_store_remove_mailbox_unlocked (imapx_store, mailbox);
2842                 mailbox = NULL;
2843         }
2844
2845         if (mailbox != NULL)
2846                 g_object_ref (mailbox);
2847
2848         return mailbox;
2849 }
2850
2851 static GList *
2852 imapx_store_list_mailboxes_unlocked (CamelIMAPXStore *imapx_store,
2853                                      CamelIMAPXNamespace *namespace,
2854                                      const gchar *pattern)
2855 {
2856         GHashTableIter iter;
2857         GList *list = NULL;
2858         gpointer value;
2859
2860         /* Acquire "mailboxes_lock" before calling. */
2861
2862         if (pattern == NULL)
2863                 pattern = "*";
2864
2865         g_hash_table_iter_init (&iter, imapx_store->priv->mailboxes);
2866
2867         while (g_hash_table_iter_next (&iter, NULL, &value)) {
2868                 CamelIMAPXMailbox *mailbox;
2869                 CamelIMAPXNamespace *mailbox_ns;
2870
2871                 mailbox = CAMEL_IMAPX_MAILBOX (value);
2872                 mailbox_ns = camel_imapx_mailbox_get_namespace (mailbox);
2873
2874                 if (!camel_imapx_mailbox_exists (mailbox))
2875                         continue;
2876
2877                 if (!camel_imapx_namespace_equal (namespace, mailbox_ns))
2878                         continue;
2879
2880                 if (!camel_imapx_mailbox_matches (mailbox, pattern))
2881                         continue;
2882
2883                 list = g_list_prepend (list, g_object_ref (mailbox));
2884         }
2885
2886         /* Sort the list by mailbox name. */
2887         return g_list_sort (list, (GCompareFunc) camel_imapx_mailbox_compare);
2888 }
2889
2890 static CamelIMAPXMailbox *
2891 imapx_store_create_mailbox_unlocked (CamelIMAPXStore *imapx_store,
2892                                      CamelIMAPXListResponse *response)
2893 {
2894         CamelIMAPXNamespaceResponse *namespace_response;
2895         CamelIMAPXNamespace *namespace;
2896         CamelIMAPXMailbox *mailbox = NULL;
2897         const gchar *mailbox_name;
2898         gchar separator;
2899
2900         /* Acquire "mailboxes_lock" before calling. */
2901
2902         namespace_response = camel_imapx_store_ref_namespaces (imapx_store);
2903         g_return_val_if_fail (namespace_response != NULL, FALSE);
2904
2905         mailbox_name = camel_imapx_list_response_get_mailbox_name (response);
2906         separator = camel_imapx_list_response_get_separator (response);
2907
2908         namespace = camel_imapx_namespace_response_lookup (
2909                 namespace_response, mailbox_name, separator);
2910
2911         if (namespace != NULL) {
2912                 mailbox = camel_imapx_mailbox_new (response, namespace);
2913                 imapx_store_add_mailbox_unlocked (imapx_store, mailbox);
2914                 g_object_unref (namespace);
2915
2916         /* XXX Slight hack, mainly for Courier servers.  If INBOX does
2917          *     not match any defined namespace, just create one for it
2918          *     on the fly.  The namespace response won't know about it. */
2919         } else if (camel_imapx_mailbox_is_inbox (mailbox_name)) {
2920                 namespace = camel_imapx_namespace_new (
2921                         CAMEL_IMAPX_NAMESPACE_PERSONAL, "", separator);
2922                 mailbox = camel_imapx_mailbox_new (response, namespace);
2923                 imapx_store_add_mailbox_unlocked (imapx_store, mailbox);
2924                 g_object_unref (namespace);
2925
2926         } else {
2927                 g_warning (
2928                         "%s: No matching namespace for \"%c\" %s",
2929                         G_STRFUNC, separator, mailbox_name);
2930         }
2931
2932         g_object_unref (namespace_response);
2933
2934         return mailbox;
2935 }
2936
2937 static CamelIMAPXMailbox *
2938 imapx_store_rename_mailbox_unlocked (CamelIMAPXStore *imapx_store,
2939                                      const gchar *old_mailbox_name,
2940                                      const gchar *new_mailbox_name)
2941 {
2942         CamelIMAPXMailbox *old_mailbox;
2943         CamelIMAPXMailbox *new_mailbox;
2944         CamelIMAPXNamespace *namespace;
2945         gsize old_mailbox_name_length;
2946         GList *list, *link;
2947         gchar separator;
2948         gchar *pattern;
2949
2950         /* Acquire "mailboxes_lock" before calling. */
2951
2952         g_return_val_if_fail (old_mailbox_name != NULL, NULL);
2953         g_return_val_if_fail (new_mailbox_name != NULL, NULL);
2954
2955         old_mailbox = imapx_store_ref_mailbox_unlocked (imapx_store, old_mailbox_name);
2956         if (old_mailbox == NULL)
2957                 return NULL;
2958
2959         old_mailbox_name_length = strlen (old_mailbox_name);
2960         namespace = camel_imapx_mailbox_get_namespace (old_mailbox);
2961         separator = camel_imapx_mailbox_get_separator (old_mailbox);
2962
2963         new_mailbox = camel_imapx_mailbox_clone (old_mailbox, new_mailbox_name);
2964
2965         /* Add the new mailbox, remove the old mailbox.
2966          * Note we still have a reference on the old mailbox. */
2967         imapx_store_add_mailbox_unlocked (imapx_store, new_mailbox);
2968         imapx_store_remove_mailbox_unlocked (imapx_store, old_mailbox);
2969
2970         /* Rename any child mailboxes. */
2971
2972         pattern = g_strdup_printf ("%s%c*", old_mailbox_name, separator);
2973         list = imapx_store_list_mailboxes_unlocked (imapx_store, namespace, pattern);
2974
2975         for (link = list; link != NULL; link = g_list_next (link)) {
2976                 CamelIMAPXMailbox *old_child;
2977                 CamelIMAPXMailbox *new_child;
2978                 const gchar *old_child_name;
2979                 gchar *new_child_name;
2980
2981                 old_child = CAMEL_IMAPX_MAILBOX (link->data);
2982                 old_child_name = camel_imapx_mailbox_get_name (old_child);
2983
2984                 /* Sanity checks. */
2985                 g_warn_if_fail (
2986                         old_child_name != NULL &&
2987                         strlen (old_child_name) > old_mailbox_name_length &&
2988                         old_child_name[old_mailbox_name_length] == separator);
2989
2990                 new_child_name = g_strconcat (
2991                         new_mailbox_name,
2992                         old_child_name + old_mailbox_name_length, NULL);
2993                 new_child = camel_imapx_mailbox_clone (
2994                         old_child, new_child_name);
2995
2996                 /* Add the new mailbox, remove the old mailbox.
2997                  * Note we still have a reference on the old mailbox. */
2998                 imapx_store_add_mailbox_unlocked (imapx_store, new_child);
2999                 imapx_store_remove_mailbox_unlocked (imapx_store, old_child);
3000
3001                 g_object_unref (new_child);
3002                 g_free (new_child_name);
3003         }
3004
3005         g_list_free_full (list, (GDestroyNotify) g_object_unref);
3006         g_free (pattern);
3007
3008         g_object_unref (old_mailbox);
3009
3010         return new_mailbox;
3011 }
3012
3013 /**
3014  * camel_imapx_store_ref_mailbox:
3015  * @imapx_store: a #CamelIMAPXStore
3016  * @mailbox_name: a mailbox name
3017  *
3018  * Looks up a #CamelMailbox by its name. If no match is found, the function
3019  * returns %NULL.
3020  *
3021  * The returned #CamelIMAPXMailbox is referenced for thread-safety and
3022  * should be unreferenced with g_object_unref() when finished with it.
3023  *
3024  * Returns: a #CamelIMAPXMailbox, or %NULL
3025  *
3026  * Since: 3.12.2
3027  **/
3028 CamelIMAPXMailbox *
3029 camel_imapx_store_ref_mailbox (CamelIMAPXStore *imapx_store,
3030                                const gchar *mailbox_name)
3031 {
3032         CamelIMAPXMailbox *mailbox;
3033
3034         g_return_val_if_fail (CAMEL_IS_IMAPX_STORE (imapx_store), NULL);
3035         g_return_val_if_fail (mailbox_name != NULL, NULL);
3036
3037         g_mutex_lock (&imapx_store->priv->mailboxes_lock);
3038
3039         mailbox = imapx_store_ref_mailbox_unlocked (imapx_store, mailbox_name);
3040
3041         g_mutex_unlock (&imapx_store->priv->mailboxes_lock);
3042
3043         return mailbox;
3044 }
3045
3046 /**
3047  * camel_imapx_store_list_mailboxes:
3048  * @imapx_store: a #CamelIMAPXStore
3049  * @namespace_: a #CamelIMAPXNamespace
3050  * @pattern: mailbox name with possible wildcards, or %NULL
3051  *
3052  * Returns a list of #CamelIMAPXMailbox instances which match @namespace and
3053  * @pattern. The @pattern may contain wildcard characters '*' and '%', which
3054  * are interpreted similar to the IMAP LIST command. A %NULL @pattern lists
3055  * all mailboxes in @namespace; equivalent to passing "*".
3056  *
3057  * The mailboxes returned in the list are referenced for thread-safety.
3058  * They must each be unreferenced with g_object_unref() when finished with
3059  * them. Free the returned list itself with g_list_free().
3060  *
3061  * An easy way to free the list properly in one step is as follows:
3062  *
3063  * |[
3064  *   g_list_free_full (list, g_object_unref);
3065  * ]|
3066  *
3067  * Returns: a list of #CamelIMAPXMailbox instances
3068  *
3069  * Since: 3.12.2
3070  **/
3071 GList *
3072 camel_imapx_store_list_mailboxes (CamelIMAPXStore *imapx_store,
3073                                   CamelIMAPXNamespace *namespace,
3074                                   const gchar *pattern)
3075 {
3076         GList *list;
3077
3078         g_return_val_if_fail (CAMEL_IS_IMAPX_STORE (imapx_store), NULL);
3079         g_return_val_if_fail (CAMEL_IS_IMAPX_NAMESPACE (namespace), NULL);
3080
3081         g_mutex_lock (&imapx_store->priv->mailboxes_lock);
3082
3083         list = imapx_store_list_mailboxes_unlocked (imapx_store, namespace, pattern);
3084
3085         g_mutex_unlock (&imapx_store->priv->mailboxes_lock);
3086
3087         return list;
3088 }
3089
3090 void
3091 camel_imapx_store_emit_mailbox_updated (CamelIMAPXStore *imapx_store,
3092                                         CamelIMAPXMailbox *mailbox)
3093 {
3094         g_return_if_fail (CAMEL_IS_IMAPX_STORE (imapx_store));
3095         g_return_if_fail (CAMEL_IS_IMAPX_MAILBOX (mailbox));
3096
3097         g_signal_emit (imapx_store, signals[MAILBOX_UPDATED], 0, mailbox);
3098 }
3099
3100 void
3101 camel_imapx_store_handle_mailbox_rename (CamelIMAPXStore *imapx_store,
3102                                          CamelIMAPXMailbox *old_mailbox,
3103                                          const gchar *new_mailbox_name)
3104 {
3105         CamelIMAPXMailbox *new_mailbox;
3106         const gchar *old_mailbox_name;
3107
3108         g_return_if_fail (CAMEL_IS_IMAPX_STORE (imapx_store));
3109         g_return_if_fail (CAMEL_IS_IMAPX_MAILBOX (old_mailbox));
3110         g_return_if_fail (new_mailbox_name != NULL);
3111
3112         old_mailbox_name = camel_imapx_mailbox_get_name (old_mailbox);
3113
3114         g_mutex_lock (&imapx_store->priv->mailboxes_lock);
3115         new_mailbox = imapx_store_rename_mailbox_unlocked (
3116                 imapx_store, old_mailbox_name, new_mailbox_name);
3117         g_mutex_unlock (&imapx_store->priv->mailboxes_lock);
3118
3119         g_warn_if_fail (new_mailbox != NULL);
3120
3121         g_signal_emit (
3122                 imapx_store, signals[MAILBOX_RENAMED], 0,
3123                 new_mailbox, old_mailbox_name);
3124
3125         g_clear_object (&new_mailbox);
3126 }
3127
3128 void
3129 camel_imapx_store_handle_list_response (CamelIMAPXStore *imapx_store,
3130                                         CamelIMAPXServer *imapx_server,
3131                                         CamelIMAPXListResponse *response)
3132 {
3133         CamelIMAPXMailbox *mailbox = NULL;
3134         const gchar *mailbox_name;
3135         const gchar *old_mailbox_name;
3136         gboolean emit_mailbox_created = FALSE;
3137         gboolean emit_mailbox_renamed = FALSE;
3138         gboolean emit_mailbox_updated = FALSE;
3139
3140         g_return_if_fail (CAMEL_IS_IMAPX_STORE (imapx_store));
3141         g_return_if_fail (CAMEL_IS_IMAPX_SERVER (imapx_server));
3142         g_return_if_fail (CAMEL_IS_IMAPX_LIST_RESPONSE (response));
3143
3144         mailbox_name = camel_imapx_list_response_get_mailbox_name (response);
3145         old_mailbox_name = camel_imapx_list_response_get_oldname (response);
3146
3147         /* Fabricate a CamelIMAPXNamespaceResponse if the server lacks the
3148          * NAMESPACE capability and this is the first LIST / LSUB response. */
3149         if (CAMEL_IMAPX_LACK_CAPABILITY (imapx_server->cinfo, NAMESPACE)) {
3150                 g_mutex_lock (&imapx_store->priv->namespaces_lock);
3151                 if (imapx_store->priv->namespaces == NULL) {
3152                         imapx_store->priv->namespaces = camel_imapx_namespace_response_faux_new (response);
3153                 }
3154                 g_mutex_unlock (&imapx_store->priv->namespaces_lock);
3155         }
3156
3157         /* Create, rename, or update a corresponding CamelIMAPXMailbox. */
3158         g_mutex_lock (&imapx_store->priv->mailboxes_lock);
3159         if (old_mailbox_name != NULL) {
3160                 mailbox = imapx_store_rename_mailbox_unlocked (
3161                         imapx_store, old_mailbox_name, mailbox_name);
3162                 emit_mailbox_renamed = (mailbox != NULL);
3163         }
3164         if (mailbox == NULL) {
3165                 mailbox = imapx_store_ref_mailbox_unlocked (imapx_store, mailbox_name);
3166                 emit_mailbox_updated = (mailbox != NULL);
3167         }
3168         if (mailbox == NULL) {
3169                 mailbox = imapx_store_create_mailbox_unlocked (imapx_store, response);
3170                 emit_mailbox_created = (mailbox != NULL);
3171         } else {
3172                 camel_imapx_mailbox_handle_list_response (mailbox, response);
3173         }
3174         g_mutex_unlock (&imapx_store->priv->mailboxes_lock);
3175
3176         if (emit_mailbox_created)
3177                 g_signal_emit (imapx_store, signals[MAILBOX_CREATED], 0, mailbox);
3178
3179         if (emit_mailbox_renamed)
3180                 g_signal_emit (
3181                         imapx_store, signals[MAILBOX_RENAMED], 0,
3182                         mailbox, old_mailbox_name);
3183
3184         if (emit_mailbox_updated)
3185                 g_signal_emit (imapx_store, signals[MAILBOX_UPDATED], 0, mailbox);
3186
3187         g_clear_object (&mailbox);
3188 }
3189
3190 void
3191 camel_imapx_store_handle_lsub_response (CamelIMAPXStore *imapx_store,
3192                                         CamelIMAPXServer *imapx_server,
3193                                         CamelIMAPXListResponse *response)
3194 {
3195         CamelIMAPXMailbox *mailbox;
3196         const gchar *mailbox_name;
3197         gboolean emit_mailbox_updated = FALSE;
3198
3199         g_return_if_fail (CAMEL_IS_IMAPX_STORE (imapx_store));
3200         g_return_if_fail (CAMEL_IS_IMAPX_SERVER (imapx_server));
3201         g_return_if_fail (CAMEL_IS_IMAPX_LIST_RESPONSE (response));
3202
3203         mailbox_name = camel_imapx_list_response_get_mailbox_name (response);
3204
3205         /* Fabricate a CamelIMAPXNamespaceResponse if the server lacks the
3206          * NAMESPACE capability and this is the first LIST / LSUB response. */
3207         if (CAMEL_IMAPX_LACK_CAPABILITY (imapx_server->cinfo, NAMESPACE)) {
3208                 g_mutex_lock (&imapx_store->priv->namespaces_lock);
3209                 if (imapx_store->priv->namespaces == NULL) {
3210                         imapx_store->priv->namespaces = camel_imapx_namespace_response_faux_new (response);
3211                 }
3212                 g_mutex_unlock (&imapx_store->priv->namespaces_lock);
3213         }
3214
3215         /* Update a corresponding CamelIMAPXMailbox.
3216          *
3217          * Note, don't create the CamelIMAPXMailbox like we do for a LIST
3218          * response.  We always issue LIST before LSUB on a mailbox name,
3219          * so if we don't already have a CamelIMAPXMailbox instance then
3220          * this is a subscription on a non-existent mailbox.  Skip it. */
3221         g_mutex_lock (&imapx_store->priv->mailboxes_lock);
3222         mailbox = imapx_store_ref_mailbox_unlocked (imapx_store, mailbox_name);
3223         if (mailbox != NULL) {
3224                 camel_imapx_mailbox_handle_lsub_response (mailbox, response);
3225                 emit_mailbox_updated = TRUE;
3226         }
3227         g_mutex_unlock (&imapx_store->priv->mailboxes_lock);
3228
3229         if (emit_mailbox_updated)
3230                 g_signal_emit (imapx_store, signals[MAILBOX_UPDATED], 0, mailbox);
3231
3232         g_clear_object (&mailbox);
3233 }
3234
3235 CamelFolderQuotaInfo *
3236 camel_imapx_store_dup_quota_info (CamelIMAPXStore *store,
3237                                   const gchar *quota_root_name)
3238 {
3239         CamelFolderQuotaInfo *info;
3240
3241         g_return_val_if_fail (CAMEL_IS_IMAPX_STORE (store), NULL);
3242         g_return_val_if_fail (quota_root_name != NULL, NULL);
3243
3244         g_mutex_lock (&store->priv->quota_info_lock);
3245
3246         info = g_hash_table_lookup (
3247                 store->priv->quota_info, quota_root_name);
3248
3249         /* camel_folder_quota_info_clone() handles NULL gracefully. */
3250         info = camel_folder_quota_info_clone (info);
3251
3252         g_mutex_unlock (&store->priv->quota_info_lock);
3253
3254         return info;
3255 }
3256
3257 void
3258 camel_imapx_store_set_quota_info (CamelIMAPXStore *store,
3259                                   const gchar *quota_root_name,
3260                                   const CamelFolderQuotaInfo *info)
3261 {
3262         g_return_if_fail (CAMEL_IS_IMAPX_STORE (store));
3263         g_return_if_fail (quota_root_name != NULL);
3264
3265         g_mutex_lock (&store->priv->quota_info_lock);
3266
3267         /* camel_folder_quota_info_clone() handles NULL gracefully. */
3268         g_hash_table_insert (
3269                 store->priv->quota_info,
3270                 g_strdup (quota_root_name),
3271                 camel_folder_quota_info_clone (info));
3272
3273         g_mutex_unlock (&store->priv->quota_info_lock);
3274 }
3275
3276 /* Tries to find matching job among all active connections.
3277    See camel_imapx_server_ref_job() for more information on parameters
3278    and return values.
3279 */
3280 CamelIMAPXJob *
3281 camel_imapx_store_ref_job (CamelIMAPXStore *imapx_store,
3282                            CamelIMAPXMailbox *mailbox,
3283                            guint32 job_type,
3284                            const gchar *uid)
3285 {
3286         GList *servers, *siter;
3287         CamelIMAPXJob *job = NULL;
3288
3289         g_return_val_if_fail (CAMEL_IS_IMAPX_STORE (imapx_store), NULL);
3290
3291         servers = camel_imapx_conn_manager_get_connections (imapx_store->priv->con_man);
3292
3293         for (siter = servers; siter; siter = g_list_next (siter)) {
3294                 CamelIMAPXServer *imapx_server = siter->data;
3295
3296                 job = camel_imapx_server_ref_job (imapx_server, mailbox, job_type, uid);
3297                 if (job)
3298                         break;
3299         }
3300
3301         g_list_free_full (servers, g_object_unref);
3302
3303         return job;
3304 }