66016785343865e65e6dc796e4dfa8825cfff580
[platform/upstream/evolution-data-server.git] / camel / 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
9  * modify it under the terms of version 2 of the GNU Lesser General Public
10  * License as published by the Free Software Foundation.
11  *
12  * This program is distributed in the hope that it will be useful,
13  * but WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
15  * General Public License for more details.
16  *
17  * You should have received a copy of the GNU Lesser General Public
18  * License along with this library; if not, write to the
19  * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
20  * Boston, MA 02110-1301, USA.
21  */
22
23 #ifdef HAVE_CONFIG_H
24 #include <config.h>
25 #endif
26
27 #include <sys/types.h>
28 #ifdef _WIN32
29 #include <winsock2.h>
30 #else
31 #include <sys/socket.h>
32 #include <netinet/in.h>
33 #endif
34 #include <errno.h>
35 #include <stdio.h>
36 #include <stdlib.h>
37 #include <string.h>
38 #include <unistd.h>
39 #include <glib/gstdio.h>
40 #include <glib/gi18n-lib.h>
41
42 #include "camel-imapx-folder.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 static GInitableIface *parent_initable_interface;
56
57 /* Forward Declarations */
58 static void camel_imapx_store_initable_init (GInitableIface *interface);
59 static void camel_network_service_init (CamelNetworkServiceInterface *interface);
60 static void camel_subscribable_init (CamelSubscribableInterface *interface);
61
62 G_DEFINE_TYPE_WITH_CODE (
63         CamelIMAPXStore,
64         camel_imapx_store,
65         CAMEL_TYPE_OFFLINE_STORE,
66         G_IMPLEMENT_INTERFACE (
67                 G_TYPE_INITABLE,
68                 camel_imapx_store_initable_init)
69         G_IMPLEMENT_INTERFACE (
70                 CAMEL_TYPE_NETWORK_SERVICE,
71                 camel_network_service_init)
72         G_IMPLEMENT_INTERFACE (
73                 CAMEL_TYPE_SUBSCRIBABLE,
74                 camel_subscribable_init))
75
76 static guint
77 imapx_name_hash (gconstpointer key)
78 {
79         if (g_ascii_strcasecmp(key, "INBOX") == 0)
80                 return g_str_hash("INBOX");
81         else
82                 return g_str_hash (key);
83 }
84
85 static gint
86 imapx_name_equal (gconstpointer a,
87                   gconstpointer b)
88 {
89         gconstpointer aname = a, bname = b;
90
91         if (g_ascii_strcasecmp(a, "INBOX") == 0)
92                 aname = "INBOX";
93         if (g_ascii_strcasecmp(b, "INBOX") == 0)
94                 bname = "INBOX";
95         return g_str_equal (aname, bname);
96 }
97
98 static void
99 imapx_store_dispose (GObject *object)
100 {
101         CamelIMAPXStore *imapx_store = CAMEL_IMAPX_STORE (object);
102
103         /* Force disconnect so we dont have it run later,
104          * after we've cleaned up some stuff. */
105         if (imapx_store->con_man != NULL) {
106                 camel_service_disconnect_sync (
107                         CAMEL_SERVICE (imapx_store), TRUE, NULL, NULL);
108                 g_object_unref (imapx_store->con_man);
109                 imapx_store->con_man = NULL;
110         }
111
112         if (imapx_store->authenticating_server != NULL) {
113                 g_object_unref (imapx_store->authenticating_server);
114                 imapx_store->authenticating_server = NULL;
115         }
116
117         if (imapx_store->summary != NULL) {
118                 g_object_unref (imapx_store->summary);
119                 imapx_store->summary = NULL;
120         }
121
122         /* Chain up to parent's dispose() method. */
123         G_OBJECT_CLASS (camel_imapx_store_parent_class)->dispose (object);
124 }
125
126 static void
127 imapx_store_finalize (GObject *object)
128 {
129         CamelIMAPXStore *imapx_store = CAMEL_IMAPX_STORE (object);
130
131         g_mutex_free (imapx_store->get_finfo_lock);
132
133         /* Chain up to parent's finalize() method. */
134         G_OBJECT_CLASS (camel_imapx_store_parent_class)->finalize (object);
135 }
136
137 static gchar *
138 imapx_get_name (CamelService *service,
139                 gboolean brief)
140 {
141         CamelNetworkSettings *network_settings;
142         CamelSettings *settings;
143         gchar *host;
144         gchar *user;
145         gchar *name;
146
147         settings = camel_service_get_settings (service);
148
149         network_settings = CAMEL_NETWORK_SETTINGS (settings);
150         host = camel_network_settings_dup_host (network_settings);
151         user = camel_network_settings_dup_user (network_settings);
152
153         if (brief)
154                 name = g_strdup_printf (
155                         _("IMAP server %s"), host);
156         else
157                 name = g_strdup_printf (
158                         _("IMAP service for %s on %s"), user, host);
159
160         g_free (host);
161         g_free (user);
162
163         return name;
164 }
165
166 CamelIMAPXServer *
167 camel_imapx_store_get_server (CamelIMAPXStore *istore,
168                               const gchar *folder_name,
169                               GCancellable *cancellable,
170                               GError **error)
171 {
172         return camel_imapx_conn_manager_get_connection (
173                 istore->con_man, folder_name, cancellable, error);
174 }
175
176 void
177 camel_imapx_store_op_done (CamelIMAPXStore *istore,
178                            CamelIMAPXServer *server,
179                            const gchar *folder_name)
180 {
181         g_return_if_fail (server != NULL);
182
183         camel_imapx_conn_manager_update_con_info (
184                 istore->con_man, server, folder_name);
185 }
186
187 static gboolean
188 imapx_connect_sync (CamelService *service,
189                     GCancellable *cancellable,
190                     GError **error)
191 {
192         CamelIMAPXStore *istore = (CamelIMAPXStore *) service;
193         CamelIMAPXServer *server;
194
195         server = camel_imapx_store_get_server (istore, NULL, cancellable, error);
196         if (server) {
197                 g_object_unref (server);
198                 return TRUE;
199         }
200
201         return FALSE;
202 }
203
204 static gboolean
205 imapx_disconnect_sync (CamelService *service,
206                        gboolean clean,
207                        GCancellable *cancellable,
208                        GError **error)
209 {
210         CamelIMAPXStore *istore = CAMEL_IMAPX_STORE (service);
211         CamelServiceClass *service_class;
212
213         service_class = CAMEL_SERVICE_CLASS (camel_imapx_store_parent_class);
214         if (!service_class->disconnect_sync (service, clean, cancellable, error))
215                 return FALSE;
216
217         if (istore->con_man != NULL)
218                 camel_imapx_conn_manager_close_connections (istore->con_man);
219
220         return TRUE;
221 }
222
223 static CamelAuthenticationResult
224 imapx_authenticate_sync (CamelService *service,
225                          const gchar *mechanism,
226                          GCancellable *cancellable,
227                          GError **error)
228 {
229         CamelIMAPXStore *istore = CAMEL_IMAPX_STORE (service);
230         CamelIMAPXServer *server;
231
232         /* CamelIMAPXConnManager sets this before calling
233          * camel_imapx_server_connect()(), and then clears it
234          * immediately after, all while holding the recursive
235          * connection lock (CAMEL_SERVICE_REC_CONNECT_LOCK).
236          * Otherwise we'd have no way of knowing which server
237          * is trying to authenticate. */
238         server = istore->authenticating_server;
239
240         g_return_val_if_fail (
241                 CAMEL_IS_IMAPX_SERVER (server),
242                 CAMEL_AUTHENTICATION_REJECTED);
243
244         return camel_imapx_server_authenticate (
245                 server, mechanism, cancellable, error);
246 }
247
248 CamelServiceAuthType camel_imapx_password_authtype = {
249         N_("Password"),
250
251         N_("This option will connect to the IMAP server using a "
252            "plaintext password."),
253
254         "",
255         TRUE
256 };
257
258 static GList *
259 imapx_query_auth_types_sync (CamelService *service,
260                              GCancellable *cancellable,
261                              GError **error)
262 {
263         CamelIMAPXStore *istore = CAMEL_IMAPX_STORE (service);
264         CamelServiceAuthType *authtype;
265         GList *sasl_types, *t, *next;
266         gboolean connected;
267         CamelIMAPXServer *server;
268
269         if (!camel_offline_store_get_online (CAMEL_OFFLINE_STORE (istore))) {
270                 g_set_error (
271                         error, CAMEL_SERVICE_ERROR,
272                         CAMEL_SERVICE_ERROR_UNAVAILABLE,
273                         _("You must be working online to complete this operation"));
274                 return NULL;
275         }
276
277         server = camel_imapx_server_new (CAMEL_STORE (istore));
278
279         connected = server->stream != NULL;
280         if (!connected)
281                 connected = imapx_connect_to_server (server, cancellable, error);
282         if (!connected)
283                 return NULL;
284
285         sasl_types = camel_sasl_authtype_list (FALSE);
286         for (t = sasl_types; t; t = next) {
287                 authtype = t->data;
288                 next = t->next;
289
290                 if (!g_hash_table_lookup (server->cinfo->auth_types, authtype->authproto)) {
291                         sasl_types = g_list_remove_link (sasl_types, t);
292                         g_list_free_1 (t);
293                 }
294         }
295
296         g_object_unref (server);
297
298         return g_list_prepend (sasl_types, &camel_imapx_password_authtype);
299 }
300
301 static CamelFolder *
302 get_folder_offline (CamelStore *store,
303                     const gchar *folder_name,
304                     guint32 flags,
305                     GError **error)
306 {
307         CamelIMAPXStore *imapx_store = CAMEL_IMAPX_STORE (store);
308         CamelFolder *new_folder = NULL;
309         CamelStoreInfo *si;
310         CamelService *service;
311         const gchar *user_cache_dir;
312
313         service = CAMEL_SERVICE (store);
314         user_cache_dir = camel_service_get_user_cache_dir (service);
315
316         si = camel_store_summary_path ((CamelStoreSummary *) imapx_store->summary, folder_name);
317         if (si) {
318                 gchar *folder_dir, *storage_path;
319
320                 /* Note: Although the INBOX is defined to be case-insensitive in the IMAP RFC
321                  * it is still up to the server how to acutally name it in a LIST response. Since
322                  * we stored the name as the server provided it us in the summary we take that name
323                  * to look up the folder.
324                  * But for the on-disk cache we do always capitalize the Inbox no matter what the
325                  * server provided.
326                  */
327                 if (!g_ascii_strcasecmp (folder_name, "INBOX"))
328                         folder_name = "INBOX";
329
330                 storage_path = g_build_filename (user_cache_dir, "folders", NULL);
331                 folder_dir = imapx_path_to_physical (storage_path, folder_name);
332                 g_free (storage_path);
333                 /* FIXME */
334                 new_folder = camel_imapx_folder_new (store, folder_dir, folder_name, error);
335
336                 g_free (folder_dir);
337                 camel_store_summary_info_free ((CamelStoreSummary *) imapx_store->summary, si);
338         } else {
339                 g_set_error (
340                         error, CAMEL_STORE_ERROR,
341                         CAMEL_STORE_ERROR_NO_FOLDER,
342                         _("No such folder %s"), folder_name);
343         }
344
345         return new_folder;
346 }
347
348 /* folder_name is path name */
349 static CamelFolderInfo *
350 imapx_build_folder_info (CamelIMAPXStore *imapx_store,
351                          const gchar *folder_name)
352 {
353         CamelFolderInfo *fi;
354         const gchar *name;
355
356         fi = camel_folder_info_new ();
357         fi->full_name = g_strdup (folder_name);
358         fi->unread = -1;
359         fi->total = -1;
360
361         name = strrchr (fi->full_name, '/');
362         if (name == NULL)
363                 name = fi->full_name;
364         else
365                 name++;
366         if (!g_ascii_strcasecmp (fi->full_name, "INBOX"))
367                 fi->display_name = g_strdup (_("Inbox"));
368         /* Do not localize the rest, these are from a server, thus shouldn't be localized */
369         /*else if (!g_ascii_strcasecmp (fi->full_name, "Drafts"))
370                 fi->display_name = g_strdup (_("Drafts"));
371         else if (!g_ascii_strcasecmp (fi->full_name, "Sent"))
372                 fi->display_name = g_strdup (_("Sent"));
373         else if (!g_ascii_strcasecmp (fi->full_name, "Templates"))
374                 fi->display_name = g_strdup (_("Templates"));
375         else if (!g_ascii_strcasecmp (fi->full_name, "Trash"))
376                 fi->display_name = g_strdup (_("Trash"));*/
377         else
378                 fi->display_name = g_strdup (name);
379
380         return fi;
381 }
382
383 static void
384 fill_fi (CamelStore *store,
385          CamelFolderInfo *fi,
386          guint32 flags)
387 {
388         CamelFolder *folder;
389         CamelService *service = (CamelService *) store;
390         CamelSettings *settings;
391         gboolean mobile_mode;
392
393         settings = camel_service_get_settings (service);
394         mobile_mode = camel_imapx_settings_get_mobile_mode (
395                 CAMEL_IMAPX_SETTINGS (settings));
396
397         folder = camel_object_bag_peek (store->folders, fi->full_name);
398         if (folder) {
399                 CamelIMAPXSummary *ims;
400
401                 if (folder->summary)
402                         ims = (CamelIMAPXSummary *) folder->summary;
403                 else
404                         ims = (CamelIMAPXSummary *) camel_imapx_summary_new (folder);
405
406                 /* Mobile clients would still love to see the total unread of actual mails
407                  * than what they just have downloaded. So we override that information by giving 
408                  * what the server has instead of what we have downloaded. */
409                 if (mobile_mode)
410                         fi->unread = ((CamelIMAPXFolder *) folder)->unread_on_server;
411                 else
412                         fi->unread = camel_folder_summary_get_unread_count ((CamelFolderSummary *) ims);
413                 fi->total = camel_folder_summary_get_saved_count ((CamelFolderSummary *) ims);
414
415                 if (!folder->summary)
416                         g_object_unref (ims);
417                 g_object_unref (folder);
418         }
419 }
420
421 /* imap needs to treat inbox case insensitive */
422 /* we'll assume the names are normalized already */
423 static guint
424 folder_hash (gconstpointer ap)
425 {
426         const gchar *a = ap;
427
428         if (g_ascii_strcasecmp(a, "INBOX") == 0)
429                 a = "INBOX";
430
431         return g_str_hash (a);
432 }
433
434 static gint
435 folder_eq (gconstpointer ap,
436            gconstpointer bp)
437 {
438         const gchar *a = ap;
439         const gchar *b = bp;
440
441         if (g_ascii_strcasecmp(a, "INBOX") == 0)
442                 a = "INBOX";
443         if (g_ascii_strcasecmp(b, "INBOX") == 0)
444                 b = "INBOX";
445
446         return g_str_equal (a, b);
447 }
448
449 static gboolean
450 imapx_match_pattern (CamelIMAPXStoreNamespace *ns,
451                      const gchar *pattern,
452                      const gchar *name)
453 {
454         gchar p, n, dir_sep;
455
456         if (!ns)
457                 return TRUE;
458
459         dir_sep = ns->sep;
460         p = *pattern++;
461         n = *name++;
462         while (n && p) {
463                 if (n == p) {
464                         p = *pattern++;
465                         n = *name++;
466                 } else if (p == '%') {
467                         if (n != dir_sep) {
468                                 n = *name++;
469                         } else {
470                                 p = *pattern++;
471                         }
472                 } else if (p == '*') {
473                         return TRUE;
474                 } else
475                         return FALSE;
476         }
477
478         return n == 0 && (p == '%' || p == 0);
479 }
480
481 static void
482 imapx_unmark_folder_subscribed (CamelIMAPXStore *istore,
483                                 const gchar *folder_name,
484                                 gboolean emit_signal)
485 {
486         CamelStoreInfo *si;
487
488         si = camel_store_summary_path ((CamelStoreSummary *) istore->summary, folder_name);
489         if (si) {
490                 if (si->flags & CAMEL_STORE_INFO_FOLDER_SUBSCRIBED) {
491                         si->flags &= ~CAMEL_STORE_INFO_FOLDER_SUBSCRIBED;
492                         camel_store_summary_touch ((CamelStoreSummary *) istore->summary);
493                         camel_store_summary_save ((CamelStoreSummary *) istore->summary);
494                 }
495                 camel_store_summary_info_free ((CamelStoreSummary *) istore->summary, si);
496         }
497
498         if (emit_signal) {
499                 CamelFolderInfo *fi;
500
501                 fi = imapx_build_folder_info (istore, folder_name);
502                 camel_subscribable_folder_unsubscribed (
503                         CAMEL_SUBSCRIBABLE (istore), fi);
504                 camel_folder_info_free (fi);
505         }
506 }
507
508 static void
509 imapx_mark_folder_subscribed (CamelIMAPXStore *istore,
510                               const gchar *folder_name,
511                               gboolean emit_signal)
512 {
513         CamelStoreInfo *si;
514
515         si = camel_store_summary_path ((CamelStoreSummary *) istore->summary, folder_name);
516         if (si) {
517                 if ((si->flags & CAMEL_STORE_INFO_FOLDER_SUBSCRIBED) == 0) {
518                         si->flags |= CAMEL_STORE_INFO_FOLDER_SUBSCRIBED;
519                         camel_store_summary_touch ((CamelStoreSummary *) istore->summary);
520                         camel_store_summary_save ((CamelStoreSummary *) istore->summary);
521                 }
522                 camel_store_summary_info_free ((CamelStoreSummary *) istore->summary, si);
523         }
524
525         if (emit_signal) {
526                 CamelFolderInfo *fi;
527
528                 fi = imapx_build_folder_info (istore, folder_name);
529                 camel_subscribable_folder_subscribed (
530                         CAMEL_SUBSCRIBABLE (istore), fi);
531                 camel_folder_info_free (fi);
532         }
533 }
534
535 static gboolean
536 imapx_subscribe_folder (CamelStore *store,
537                         const gchar *folder_name,
538                         gboolean emit_signal,
539                         GCancellable *cancellable,
540                         GError **error)
541 {
542         CamelIMAPXStore *istore = (CamelIMAPXStore *) store;
543         CamelIMAPXServer *server;
544         gboolean success;
545
546         if (!camel_offline_store_get_online (CAMEL_OFFLINE_STORE (store)))
547                 return TRUE;
548
549         server = camel_imapx_store_get_server (istore, NULL, cancellable, error);
550         if (!server)
551                 return FALSE;
552
553         success = camel_imapx_server_manage_subscription (
554                 server, folder_name, TRUE, cancellable, error);
555         g_object_unref (server);
556
557         if (success)
558                 imapx_mark_folder_subscribed (istore, folder_name, emit_signal);
559
560         return success;
561 }
562
563 static gboolean
564 imapx_unsubscribe_folder (CamelStore *store,
565                           const gchar *folder_name,
566                           gboolean emit_signal,
567                           GCancellable *cancellable,
568                           GError **error)
569 {
570         CamelIMAPXStore *istore = (CamelIMAPXStore *) store;
571         CamelIMAPXServer *server;
572         gboolean success;
573
574         if (!camel_offline_store_get_online (CAMEL_OFFLINE_STORE (store)))
575                 return TRUE;
576
577         server = camel_imapx_store_get_server (istore, NULL, cancellable, error);
578         if (!server)
579                 return FALSE;
580
581         success = camel_imapx_server_manage_subscription (
582                 server, folder_name, FALSE, cancellable, error);
583         g_object_unref (server);
584
585         if (success)
586                 imapx_unmark_folder_subscribed (istore, folder_name, emit_signal);
587
588         return success;
589 }
590
591 static void
592 imapx_delete_folder_from_cache (CamelIMAPXStore *istore,
593                                 const gchar *folder_name)
594 {
595         gchar *state_file;
596         gchar *folder_dir, *storage_path;
597         CamelFolderInfo *fi;
598         CamelService *service;
599         const gchar *user_cache_dir;
600
601         service = CAMEL_SERVICE (istore);
602         user_cache_dir = camel_service_get_user_cache_dir (service);
603
604         storage_path = g_build_filename (user_cache_dir, "folders", NULL);
605         folder_dir = imapx_path_to_physical (storage_path, folder_name);
606         g_free (storage_path);
607         if (g_access (folder_dir, F_OK) != 0) {
608                 g_free (folder_dir);
609                 goto event;
610         }
611
612         /* Delete summary and all the data */
613         state_file = g_build_filename (folder_dir, "cmeta", NULL);
614         g_unlink (state_file);
615         g_free (state_file);
616
617         camel_db_delete_folder (((CamelStore *) istore)->cdb_w, folder_name, NULL);
618         g_rmdir (folder_dir);
619
620         state_file = g_build_filename (folder_dir, "subfolders", NULL);
621         g_rmdir (state_file);
622         g_free (state_file);
623
624         g_rmdir (folder_dir);
625         g_free (folder_dir);
626
627  event:
628         camel_store_summary_remove_path ((CamelStoreSummary *) istore->summary, folder_name);
629         camel_store_summary_save ((CamelStoreSummary *) istore->summary);
630
631         fi = imapx_build_folder_info (istore, folder_name);
632         camel_store_folder_deleted (CAMEL_STORE (istore), fi);
633         camel_folder_info_free (fi);
634 }
635
636 static void
637 rename_folder_info (CamelIMAPXStore *istore,
638                     const gchar *old_name,
639                     const gchar *new_name)
640 {
641         gint i, count;
642         CamelStoreInfo *si;
643         gint olen = strlen (old_name);
644         const gchar *path;
645         gchar *npath, *nfull;
646
647         count = camel_store_summary_count ((CamelStoreSummary *) istore->summary);
648         for (i = 0; i < count; i++) {
649                 si = camel_store_summary_index ((CamelStoreSummary *) istore->summary, i);
650                 if (si == NULL)
651                         continue;
652                 path = camel_store_info_path (istore->summary, si);
653                 if (strncmp (path, old_name, olen) == 0) {
654                         if (strlen (path) > olen)
655                                 npath = g_strdup_printf("%s/%s", new_name, path+olen+1);
656                         else
657                                 npath = g_strdup (new_name);
658                         nfull = camel_imapx_store_summary_path_to_full (istore->summary, npath, istore->dir_sep);
659
660                         camel_store_info_set_string ((CamelStoreSummary *) istore->summary, si, CAMEL_STORE_INFO_PATH, npath);
661                         camel_store_info_set_string ((CamelStoreSummary *) istore->summary, si, CAMEL_IMAPX_STORE_INFO_FULL_NAME, nfull);
662
663                         camel_store_summary_touch ((CamelStoreSummary *) istore->summary);
664                         g_free (nfull);
665                         g_free (npath);
666                 }
667                 camel_store_summary_info_free ((CamelStoreSummary *) istore->summary, si);
668         }
669 }
670
671 static CamelFolderInfo *
672 get_folder_info_offline (CamelStore *store,
673                          const gchar *top,
674                          guint32 flags,
675                          GError **error)
676 {
677         CamelIMAPXStore *imapx_store = CAMEL_IMAPX_STORE (store);
678         CamelService *service;
679         CamelSettings *settings;
680         gboolean include_inbox = FALSE;
681         CamelFolderInfo *fi;
682         GPtrArray *folders;
683         gchar *pattern, *name;
684         gboolean use_namespace;
685         gboolean use_subscriptions;
686         gint i;
687
688         service = CAMEL_SERVICE (store);
689         settings = camel_service_get_settings (service);
690
691         use_namespace = camel_imapx_settings_get_use_namespace (
692                 CAMEL_IMAPX_SETTINGS (settings));
693
694         use_subscriptions = camel_imapx_settings_get_use_subscriptions (
695                 CAMEL_IMAPX_SETTINGS (settings));
696
697         /* FIXME: obey other flags */
698
699         folders = g_ptr_array_new ();
700
701         if (top == NULL || top[0] == '\0') {
702                 include_inbox = TRUE;
703                 top = "";
704         }
705
706         /* get starting point */
707         if (top[0] == 0) {
708                 gchar *namespace = NULL;
709
710                 if (use_namespace)
711                         namespace = camel_imapx_settings_dup_namespace (
712                                 CAMEL_IMAPX_SETTINGS (settings));
713
714                 if (namespace != NULL) {
715                         name = g_strdup (imapx_store->summary->namespaces->personal->full_name);
716                         top = imapx_store->summary->namespaces->personal->path;
717                 } else
718                         name = g_strdup("");
719
720                 g_free (namespace);
721         } else {
722                 name = camel_imapx_store_summary_full_from_path (imapx_store->summary, top);
723                 if (name == NULL)
724                         name = camel_imapx_store_summary_path_to_full (imapx_store->summary, top, imapx_store->dir_sep);
725         }
726
727         pattern = imapx_concat(imapx_store, name, "*");
728
729         /* folder_info_build will insert parent nodes as necessary and mark
730          * them as noselect, which is information we actually don't have at
731          * the moment. So let it do the right thing by bailing out if it's
732          * not a folder we're explicitly interested in. */
733
734         for (i = 0; i < camel_store_summary_count ((CamelStoreSummary *) imapx_store->summary); i++) {
735                 CamelStoreInfo *si = camel_store_summary_index ((CamelStoreSummary *) imapx_store->summary, i);
736                 const gchar *full_name;
737                 CamelIMAPXStoreNamespace *ns;
738
739                 if (si == NULL)
740                         continue;
741
742                 full_name = camel_imapx_store_info_full_name (imapx_store->summary, si);
743                 if (!full_name || !*full_name) {
744                         camel_store_summary_info_free ((CamelStoreSummary *) imapx_store->summary, si);
745                         continue;
746                 }
747
748                 ns = camel_imapx_store_summary_namespace_find_full (imapx_store->summary, full_name);
749
750                 /* Modify the checks to see match the namespaces from preferences */
751                 if ((g_str_equal (name, full_name)
752                      || imapx_match_pattern (ns, pattern, full_name)
753                      || (include_inbox && !g_ascii_strcasecmp (full_name, "INBOX")))
754                     && ( (!use_subscriptions
755                             || (flags & CAMEL_STORE_FOLDER_INFO_SUBSCRIBED) == 0)
756                         || (si->flags & CAMEL_STORE_INFO_FOLDER_SUBSCRIBED)
757                         || (flags & CAMEL_STORE_FOLDER_INFO_SUBSCRIPTION_LIST) != 0)) {
758
759                         fi = imapx_build_folder_info (imapx_store, camel_store_info_path ((CamelStoreSummary *) imapx_store->summary, si));
760                         fi->unread = si->unread;
761                         fi->total = si->total;
762                         fi->flags = si->flags;
763                         /* HACK: some servers report noinferiors for all folders (uw-imapd)
764                          * We just translate this into nochildren, and let the imap layer enforce
765                          * it.  See create folder */
766                         if (fi->flags & CAMEL_FOLDER_NOINFERIORS)
767                                 fi->flags = (fi->flags & ~CAMEL_FOLDER_NOINFERIORS) | CAMEL_FOLDER_NOCHILDREN;
768
769                         /* blah, this gets lost somewhere, i can't be bothered finding out why */
770                         if (!g_ascii_strcasecmp(fi->full_name, "inbox")) {
771                                 fi->flags = (fi->flags & ~CAMEL_FOLDER_TYPE_MASK) | CAMEL_FOLDER_TYPE_INBOX;
772                                 fi->flags |= CAMEL_FOLDER_SYSTEM;
773                         }
774
775                         if (!(si->flags & CAMEL_FOLDER_NOSELECT))
776                                 fill_fi ((CamelStore *) imapx_store, fi, 0);
777
778                         if (!fi->child)
779                                 fi->flags |= CAMEL_FOLDER_NOCHILDREN;
780                         g_ptr_array_add (folders, fi);
781                 }
782                 camel_store_summary_info_free ((CamelStoreSummary *) imapx_store->summary, si);
783         }
784         g_free (pattern);
785
786         fi = camel_folder_info_build (folders, top, '/', TRUE);
787         g_ptr_array_free (folders, TRUE);
788         g_free (name);
789
790         return fi;
791 }
792
793 static void
794 add_folders_to_summary (CamelIMAPXStore *istore,
795                         CamelIMAPXServer *server,
796                         GPtrArray *folders,
797                         GHashTable *table,
798                         gboolean subscribed)
799 {
800         gint i = 0;
801
802         for (i = 0; i < folders->len; i++) {
803                 struct _list_info *li = folders->pdata[i];
804                 CamelIMAPXStoreInfo *si;
805                 guint32 new_flags;
806                 CamelFolderInfo *fi, *sfi;
807                 gchar *path;
808
809                 if (subscribed) {
810                         path = camel_imapx_store_summary_path_to_full (istore->summary, li->name, li->separator);
811                         sfi = g_hash_table_lookup (table, path);
812                         if (sfi)
813                                 sfi->flags |= CAMEL_STORE_INFO_FOLDER_SUBSCRIBED;
814
815                         g_free (path);
816                         continue;
817                 }
818
819                 si = camel_imapx_store_summary_add_from_full (istore->summary, li->name, li->separator);
820                 if (!si)
821                         continue;
822
823                 new_flags = (si->info.flags & (CAMEL_STORE_INFO_FOLDER_SUBSCRIBED | CAMEL_STORE_INFO_FOLDER_CHECK_FOR_NEW)) |
824                                                 (li->flags & ~CAMEL_STORE_INFO_FOLDER_SUBSCRIBED);
825
826                 if (!(server->cinfo->capa & IMAPX_CAPABILITY_NAMESPACE))
827                         istore->dir_sep = li->separator;
828
829                 if (si->info.flags != new_flags) {
830                         si->info.flags = new_flags;
831                         camel_store_summary_touch ((CamelStoreSummary *) istore->summary);
832                 }
833
834                 fi = camel_folder_info_new ();
835                 fi->full_name = g_strdup (camel_store_info_path (istore->summary, si));
836                 if (!g_ascii_strcasecmp(fi->full_name, "inbox")) {
837                         li->flags |= CAMEL_FOLDER_SYSTEM | CAMEL_FOLDER_TYPE_INBOX;
838                         fi->display_name = g_strdup (_("Inbox"));
839                 } else
840                         fi->display_name = g_strdup (camel_store_info_name (istore->summary, si));
841
842                 /* HACK: some servers report noinferiors for all folders (uw-imapd)
843                  * We just translate this into nochildren, and let the imap layer enforce
844                  * it.  See create folder */
845                 if (li->flags & CAMEL_FOLDER_NOINFERIORS)
846                         li->flags = (li->flags & ~CAMEL_FOLDER_NOINFERIORS) | CAMEL_FOLDER_NOCHILDREN;
847                 fi->flags = li->flags;
848
849                 fi->total = -1;
850                 fi->unread = -1;
851
852                 g_hash_table_insert (table, fi->full_name, fi);
853         }
854 }
855
856 static void
857 free_list (gpointer data,
858            gpointer user_data)
859 {
860         struct _list_info *li = data;
861         imapx_free_list (li);
862 }
863
864 static void
865 imapx_get_folders_free (gpointer k,
866                         gpointer v,
867                         gpointer d)
868 {
869         camel_folder_info_free (v);
870 }
871
872 static gboolean
873 fetch_folders_for_pattern (CamelIMAPXStore *istore,
874                            CamelIMAPXServer *server,
875                            const gchar *pattern,
876                            guint32 flags,
877                            const gchar *ext,
878                            GHashTable *table,
879                            GCancellable *cancellable,
880                            GError **error)
881 {
882         GPtrArray *folders;
883
884         folders = camel_imapx_server_list (
885                 server, pattern, flags, ext, cancellable, error);
886         if (folders == NULL)
887                 return FALSE;
888
889         add_folders_to_summary (istore, server, folders, table, (flags & CAMEL_STORE_FOLDER_INFO_SUBSCRIBED));
890
891         g_ptr_array_foreach (folders, free_list, folders);
892         g_ptr_array_free (folders, TRUE);
893
894         return TRUE;
895 }
896
897 static GList *
898 get_namespaces (CamelIMAPXStore *istore)
899 {
900         GList *namespaces = NULL;
901         CamelIMAPXNamespaceList *nsl = NULL;
902
903         /* Add code to return the namespaces from preference else all of them */
904         nsl = istore->summary->namespaces;
905         if (nsl->personal)
906                 namespaces = g_list_append (namespaces, nsl->personal);
907         if (nsl->other)
908                 namespaces = g_list_append (namespaces, nsl->other);
909         if (nsl->shared)
910                 namespaces = g_list_append (namespaces, nsl->shared);
911
912         return namespaces;
913 }
914
915 static GHashTable *
916 fetch_folders_for_namespaces (CamelIMAPXStore *istore,
917                               const gchar *pattern,
918                               gboolean sync,
919                               GCancellable *cancellable,
920                               GError **error)
921 {
922         CamelIMAPXServer *server;
923         GHashTable *folders = NULL;
924         GList *namespaces = NULL, *l;
925
926         server = camel_imapx_store_get_server (istore, NULL, cancellable, error);
927         if (!server)
928                 return NULL;
929
930         folders = g_hash_table_new (folder_hash, folder_eq);
931         namespaces = get_namespaces (istore);
932
933         for (l = namespaces; l != NULL; l = g_list_next (l))
934         {
935                 CamelIMAPXStoreNamespace *ns = l->data;
936
937                 while (ns) {
938                         guint32 flags = 0;
939                         gchar *pat = NULL;
940                         const gchar *list_ext = NULL;
941
942                         if (!pattern) {
943                                 if (!*ns->path)
944                                         pat = g_strdup ("");
945                                 else
946                                         pat = g_strdup_printf ("%s%c", ns->path, ns->sep);
947                         } else
948                                 pat = g_strdup (pattern);
949
950                         if (sync)
951                                 flags |= CAMEL_STORE_FOLDER_INFO_SUBSCRIPTION_LIST;
952
953                         if (server->cinfo->capa & IMAPX_CAPABILITY_LIST_EXTENDED)
954                                 list_ext = "RETURN (SUBSCRIBED)";
955
956                         flags |= CAMEL_STORE_FOLDER_INFO_RECURSIVE;
957                         if (!fetch_folders_for_pattern (
958                                 istore, server, pat, flags, list_ext,
959                                 folders, cancellable, error)) {
960                                 g_free (pat);
961                                 goto exception;
962                         }
963                         if (!list_ext) {
964                                 /* If the server doesn't support LIST-EXTENDED then we have to
965                                  * issue LSUB to list the subscribed folders separately */
966                                 flags |= CAMEL_STORE_FOLDER_INFO_SUBSCRIBED;
967                                 if (!fetch_folders_for_pattern (
968                                         istore, server, pat, flags, NULL,
969                                         folders, cancellable, error)) {
970                                         g_free (pat);
971                                         goto exception;
972                                 }
973                         }
974                         g_free (pat);
975
976                         if (pattern)
977                                 goto out;
978
979                         ns = ns->next;
980                 }
981         }
982         g_list_free (namespaces);
983  out:
984         g_object_unref (server);
985         return folders;
986
987 exception:
988         g_object_unref (server);
989         g_hash_table_destroy (folders);
990         return NULL;
991 }
992
993 static gboolean
994 sync_folders (CamelIMAPXStore *istore,
995               const gchar *pattern,
996               gboolean sync,
997               GCancellable *cancellable,
998               GError **error)
999 {
1000         GHashTable *folders_from_server;
1001         gint i, total;
1002
1003         folders_from_server = fetch_folders_for_namespaces (
1004                 istore, pattern, sync, cancellable, error);
1005         if (folders_from_server == NULL)
1006                 return FALSE;
1007
1008         total = camel_store_summary_count ((CamelStoreSummary *) istore->summary);
1009         for (i = 0; i < total; i++) {
1010                 CamelStoreInfo *si;
1011                 const gchar *full_name;
1012                 CamelFolderInfo *fi;
1013
1014                 si = camel_store_summary_index ((CamelStoreSummary *) istore->summary, i);
1015                 if (!si)
1016                         continue;
1017
1018                 full_name = camel_imapx_store_info_full_name (istore->summary, si);
1019                 if (!full_name || !*full_name) {
1020                         camel_store_summary_info_free ((CamelStoreSummary *) istore->summary, si);
1021                         continue;
1022                 }
1023
1024                 if (!pattern || !*pattern || imapx_match_pattern (camel_imapx_store_summary_namespace_find_full (istore->summary, full_name), pattern, full_name)) {
1025                         if ((fi = g_hash_table_lookup (folders_from_server, camel_store_info_path (istore->summary, si))) != NULL) {
1026                                 if (((fi->flags ^ si->flags) & CAMEL_STORE_INFO_FOLDER_SUBSCRIBED)) {
1027                                         si->flags = (si->flags & ~CAMEL_FOLDER_SUBSCRIBED) | (fi->flags & CAMEL_FOLDER_SUBSCRIBED);
1028                                         camel_store_summary_touch ((CamelStoreSummary *) istore->summary);
1029
1030                                         camel_store_folder_created (CAMEL_STORE (istore), fi);
1031                                         camel_subscribable_folder_subscribed (CAMEL_SUBSCRIBABLE (istore), fi);
1032                                 }
1033                         } else {
1034                                 gchar *dup_folder_name = g_strdup (camel_store_info_path (istore->summary, si));
1035
1036                                 if (dup_folder_name) {
1037                                         imapx_unmark_folder_subscribed (istore,dup_folder_name, TRUE);
1038                                         imapx_delete_folder_from_cache (istore, dup_folder_name);
1039                                         g_free (dup_folder_name);
1040                                 } else {
1041                                         camel_store_summary_remove ((CamelStoreSummary *) istore->summary, si);
1042                                 }
1043
1044                                 total--;
1045                                 i--;
1046                         }
1047                 }
1048                 camel_store_summary_info_free ((CamelStoreSummary *) istore->summary, si);
1049         }
1050
1051         g_hash_table_foreach (folders_from_server, imapx_get_folders_free, NULL);
1052         g_hash_table_destroy (folders_from_server);
1053
1054         return TRUE;
1055 }
1056
1057 static void
1058 imapx_refresh_finfo (CamelSession *session,
1059                      GCancellable *cancellable,
1060                      CamelIMAPXStore *store,
1061                      GError **error)
1062 {
1063         CamelService *service;
1064         const gchar *display_name;
1065
1066         service = CAMEL_SERVICE (store);
1067         display_name = camel_service_get_display_name (service);
1068
1069         camel_operation_push_message (
1070                 cancellable, _("Retrieving folder list for %s"),
1071                 display_name);
1072
1073         if (!camel_offline_store_get_online (CAMEL_OFFLINE_STORE (store)))
1074                 goto exit;
1075
1076         if (!camel_service_connect_sync (
1077                 CAMEL_SERVICE (store), cancellable, error))
1078                 goto exit;
1079
1080         /* look in all namespaces */
1081         sync_folders (store, "", FALSE, cancellable, error);
1082
1083         camel_store_summary_save (CAMEL_STORE_SUMMARY (store->summary));
1084
1085 exit:
1086         camel_operation_pop_message (cancellable);
1087 }
1088
1089 static void
1090 discover_inbox (CamelStore *store,
1091                 GCancellable *cancellable)
1092 {
1093         CamelStoreInfo *si;
1094         CamelIMAPXStore *istore = (CamelIMAPXStore *) store;
1095
1096         si = camel_store_summary_path((CamelStoreSummary *) istore->summary, "INBOX");
1097         if (si == NULL || (si->flags & CAMEL_FOLDER_SUBSCRIBED) == 0) {
1098                 if (imapx_subscribe_folder (store, "INBOX", FALSE, cancellable, NULL) && !si)
1099                         sync_folders (istore, "INBOX", TRUE, cancellable, NULL);
1100
1101                 if (si)
1102                         camel_store_summary_info_free ((CamelStoreSummary *) istore->summary, si);
1103         }
1104 }
1105
1106 static gboolean
1107 imapx_can_refresh_folder (CamelStore *store,
1108                           CamelFolderInfo *info,
1109                           GError **error)
1110 {
1111         CamelService *service;
1112         CamelSettings *settings;
1113         CamelStoreClass *store_class;
1114         gboolean check_all;
1115         gboolean check_subscribed;
1116         gboolean subscribed;
1117         gboolean res;
1118         GError *local_error = NULL;
1119
1120         store_class = CAMEL_STORE_CLASS (camel_imapx_store_parent_class);
1121
1122         service = CAMEL_SERVICE (store);
1123         settings = camel_service_get_settings (service);
1124
1125         check_all = camel_imapx_settings_get_check_all (
1126                 CAMEL_IMAPX_SETTINGS (settings));
1127
1128         check_subscribed = camel_imapx_settings_get_check_subscribed (
1129                 CAMEL_IMAPX_SETTINGS (settings));
1130
1131         subscribed = ((info->flags & CAMEL_FOLDER_SUBSCRIBED) != 0);
1132
1133         res = store_class->can_refresh_folder (store, info, &local_error) ||
1134                 check_all || (check_subscribed && subscribed);
1135
1136         if (!res && local_error == NULL && CAMEL_IS_IMAPX_STORE (store)) {
1137                 CamelStoreInfo *si;
1138                 CamelStoreSummary *sm = CAMEL_STORE_SUMMARY (((CamelIMAPXStore *)(store))->summary);
1139
1140                 if (!sm)
1141                         return FALSE;
1142
1143                 si = camel_store_summary_path (sm, info->full_name);
1144                 if (si) {
1145                         res = (si->flags & CAMEL_STORE_INFO_FOLDER_CHECK_FOR_NEW) != 0 ? TRUE : FALSE;
1146
1147                         camel_store_summary_info_free (sm, si);
1148                 }
1149         }
1150
1151         if (local_error != NULL)
1152                 g_propagate_error (error, local_error);
1153
1154         return res;
1155 }
1156
1157 static CamelFolder *
1158 imapx_store_get_folder_sync (CamelStore *store,
1159                              const gchar *folder_name,
1160                              CamelStoreGetFolderFlags flags,
1161                              GCancellable *cancellable,
1162                              GError **error)
1163 {
1164         CamelFolder *folder;
1165
1166         folder = get_folder_offline (store, folder_name, flags, NULL);
1167         if (folder == NULL) {
1168                 g_set_error (
1169                         error, CAMEL_STORE_ERROR,
1170                         CAMEL_STORE_ERROR_NO_FOLDER,
1171                         _("No such folder: %s"), folder_name);
1172                 return NULL;
1173         }
1174
1175         return folder;
1176 }
1177
1178 static CamelFolderInfo *
1179 imapx_store_get_folder_info_sync (CamelStore *store,
1180                                   const gchar *top,
1181                                   CamelStoreGetFolderInfoFlags flags,
1182                                   GCancellable *cancellable,
1183                                   GError **error)
1184 {
1185         CamelIMAPXStore *istore = (CamelIMAPXStore *) store;
1186         CamelFolderInfo * fi= NULL;
1187         CamelService *service;
1188         CamelSession *session;
1189         CamelSettings *settings;
1190         gboolean initial_setup = FALSE;
1191         gboolean use_subscriptions;
1192         gchar *pattern;
1193
1194         service = CAMEL_SERVICE (store);
1195         session = camel_service_get_session (service);
1196         settings = camel_service_get_settings (service);
1197
1198         use_subscriptions = camel_imapx_settings_get_use_subscriptions (
1199                 CAMEL_IMAPX_SETTINGS (settings));
1200
1201         if (top == NULL)
1202                 top = "";
1203
1204         g_mutex_lock (istore->get_finfo_lock);
1205
1206         if (!camel_offline_store_get_online (CAMEL_OFFLINE_STORE (store))) {
1207                 fi = get_folder_info_offline (store, top, flags, error);
1208
1209                 g_mutex_unlock (istore->get_finfo_lock);
1210                 return fi;
1211         }
1212
1213         if (camel_store_summary_count ((CamelStoreSummary *) istore->summary) == 0)
1214                 initial_setup = TRUE;
1215
1216         if (!initial_setup && flags & CAMEL_STORE_FOLDER_INFO_SUBSCRIBED) {
1217                 time_t now = time (NULL);
1218
1219                 if (now - istore->last_refresh_time > FINFO_REFRESH_INTERVAL) {
1220                         istore->last_refresh_time = time (NULL);
1221
1222                         camel_session_submit_job (
1223                                 session, (CamelSessionCallback)
1224                                 imapx_refresh_finfo,
1225                                 g_object_ref (store),
1226                                 (GDestroyNotify) g_object_unref);
1227                 }
1228
1229                 fi = get_folder_info_offline (store, top, flags, error);
1230                 g_mutex_unlock (istore->get_finfo_lock);
1231                 return fi;
1232         }
1233
1234         if (!camel_service_connect_sync (
1235                 CAMEL_SERVICE (store), cancellable, error)) {
1236                 g_mutex_unlock (istore->get_finfo_lock);
1237                 return NULL;
1238         }
1239
1240         if (*top && flags & CAMEL_STORE_FOLDER_INFO_SUBSCRIPTION_LIST) {
1241                 fi = get_folder_info_offline (store, top, flags, error);
1242                 g_mutex_unlock (istore->get_finfo_lock);
1243                 return fi;
1244         }
1245
1246         if (*top) {
1247                 gchar *name;
1248                 gint i;
1249
1250                 name = camel_imapx_store_summary_full_from_path (istore->summary, top);
1251                 if (name == NULL)
1252                         name = camel_imapx_store_summary_path_to_full (istore->summary, top, istore->dir_sep);
1253
1254                 i = strlen (name);
1255                 pattern = g_alloca (i + 5);
1256                 strcpy (pattern, name);
1257                 g_free (name);
1258         } else {
1259                 pattern = g_alloca (1);
1260                 pattern[0] = '\0';
1261         }
1262
1263         if (!sync_folders (istore, pattern, TRUE, cancellable, error)) {
1264                 g_mutex_unlock (istore->get_finfo_lock);
1265                 return NULL;
1266         }
1267
1268         camel_store_summary_save ((CamelStoreSummary *) istore->summary);
1269
1270         /* ensure the INBOX is subscribed if lsub was preferred*/
1271         if (initial_setup && use_subscriptions)
1272                 discover_inbox (store, cancellable);
1273
1274         fi = get_folder_info_offline (store, top, flags, error);
1275         g_mutex_unlock (istore->get_finfo_lock);
1276         return fi;
1277 }
1278
1279 static CamelFolder *
1280 imapx_store_get_junk_folder_sync (CamelStore *store,
1281                                   GCancellable *cancellable,
1282                                   GError **error)
1283 {
1284         CamelFolder *folder;
1285         CamelStoreClass *store_class;
1286
1287         store_class = CAMEL_STORE_CLASS (camel_imapx_store_parent_class);
1288         folder = store_class->get_junk_folder_sync (store, cancellable, error);
1289
1290         if (folder) {
1291                 CamelObject *object = CAMEL_OBJECT (folder);
1292                 CamelService *service;
1293                 const gchar *user_cache_dir;
1294                 gchar *state;
1295
1296                 service = CAMEL_SERVICE (store);
1297                 user_cache_dir = camel_service_get_user_cache_dir (service);
1298
1299                 state = g_build_filename (
1300                         user_cache_dir, "system", "Junk.cmeta", NULL);
1301
1302                 camel_object_set_state_filename (object, state);
1303                 g_free (state);
1304                 /* no defaults? */
1305                 camel_object_state_read (object);
1306         }
1307
1308         return folder;
1309 }
1310
1311 static CamelFolder *
1312 imapx_store_get_trash_folder_sync (CamelStore *store,
1313                                    GCancellable *cancellable,
1314                                    GError **error)
1315 {
1316         CamelFolder *folder;
1317         CamelStoreClass *store_class;
1318
1319         store_class = CAMEL_STORE_CLASS (camel_imapx_store_parent_class);
1320         folder = store_class->get_trash_folder_sync (store, cancellable, error);
1321
1322         if (folder) {
1323                 CamelObject *object = CAMEL_OBJECT (folder);
1324                 CamelService *service;
1325                 const gchar *user_cache_dir;
1326                 gchar *state;
1327
1328                 service = CAMEL_SERVICE (store);
1329                 user_cache_dir = camel_service_get_user_cache_dir (service);
1330
1331                 state = g_build_filename (
1332                         user_cache_dir, "system", "Trash.cmeta", NULL);
1333
1334                 camel_object_set_state_filename (object, state);
1335                 g_free (state);
1336                 /* no defaults? */
1337                 camel_object_state_read (object);
1338         }
1339
1340         return folder;
1341 }
1342
1343 static CamelFolderInfo *
1344 imapx_store_create_folder_sync (CamelStore *store,
1345                                 const gchar *parent_name,
1346                                 const gchar *folder_name,
1347                                 GCancellable *cancellable,
1348                                 GError **error)
1349 {
1350         CamelStoreInfo *si;
1351         CamelIMAPXStoreNamespace *ns;
1352         CamelIMAPXStore *istore = (CamelIMAPXStore *) store;
1353         CamelIMAPXServer *server;
1354         gchar *real_name, *full_name, *parent_real;
1355         CamelFolderInfo *fi = NULL;
1356         gchar dir_sep;
1357         gboolean success;
1358
1359         if (!camel_offline_store_get_online (CAMEL_OFFLINE_STORE (store))) {
1360                 g_set_error (
1361                         error, CAMEL_SERVICE_ERROR,
1362                         CAMEL_SERVICE_ERROR_UNAVAILABLE,
1363                         _("You must be working online to complete this operation"));
1364                 return NULL;
1365         }
1366
1367         server = camel_imapx_store_get_server (istore, NULL, cancellable, error);
1368         if (!server)
1369                 return NULL;
1370
1371         if (!parent_name)
1372                 parent_name = "";
1373
1374         ns = camel_imapx_store_summary_namespace_find_path (istore->summary, parent_name);
1375         if (ns)
1376                 dir_sep = ns->sep;
1377         else
1378                 dir_sep = '/';
1379
1380         if (strchr (folder_name, dir_sep)) {
1381                 g_set_error (
1382                         error, CAMEL_FOLDER_ERROR,
1383                         CAMEL_FOLDER_ERROR_INVALID_PATH,
1384                         _("The folder name \"%s\" is invalid because it contains the character \"%c\""),
1385                         folder_name, dir_sep);
1386                 g_object_unref (server);
1387                 return NULL;
1388         }
1389
1390         parent_real = camel_imapx_store_summary_full_from_path (istore->summary, parent_name);
1391         if (parent_real == NULL) {
1392                 g_set_error (
1393                         error, CAMEL_FOLDER_ERROR,
1394                         CAMEL_FOLDER_ERROR_INVALID_STATE,
1395                         _("Unknown parent folder: %s"), parent_name);
1396                 g_object_unref (server);
1397                 return NULL;
1398         }
1399
1400         si = camel_store_summary_path ((CamelStoreSummary *) istore->summary, parent_name);
1401         if (si && si->flags & CAMEL_STORE_INFO_FOLDER_NOINFERIORS) {
1402                 g_set_error (
1403                         error, CAMEL_FOLDER_ERROR,
1404                         CAMEL_FOLDER_ERROR_INVALID_STATE,
1405                         _("The parent folder is not allowed to contain subfolders"));
1406                 g_object_unref (server);
1407                 return NULL;
1408         }
1409
1410         if (si)
1411                 camel_store_summary_info_free ((CamelStoreSummary *) istore->summary, si);
1412
1413         real_name = camel_imapx_store_summary_path_to_full (istore->summary, folder_name, dir_sep);
1414         full_name = imapx_concat (istore, parent_real, real_name);
1415         g_free (real_name);
1416
1417         success = camel_imapx_server_create_folder (
1418                 server, full_name, cancellable, error);
1419         g_object_unref (server);
1420
1421         if (success) {
1422                 CamelIMAPXStoreInfo *si;
1423
1424                 si = camel_imapx_store_summary_add_from_full (istore->summary, full_name, dir_sep);
1425                 camel_store_summary_save ((CamelStoreSummary *) istore->summary);
1426                 fi = imapx_build_folder_info (istore, camel_store_info_path (istore->summary, si));
1427                 fi->flags |= CAMEL_FOLDER_NOCHILDREN;
1428                 camel_store_folder_created (store, fi);
1429         }
1430
1431         g_free (full_name);
1432         g_free (parent_real);
1433
1434         return fi;
1435 }
1436
1437 static gboolean
1438 imapx_store_delete_folder_sync (CamelStore *store,
1439                                 const gchar *folder_name,
1440                                 GCancellable *cancellable,
1441                                 GError **error)
1442 {
1443         CamelIMAPXStore *istore = (CamelIMAPXStore *) store;
1444         CamelIMAPXServer *server;
1445         gboolean success;
1446
1447         if (!camel_offline_store_get_online (CAMEL_OFFLINE_STORE (store))) {
1448                 g_set_error (
1449                         error, CAMEL_SERVICE_ERROR,
1450                         CAMEL_SERVICE_ERROR_UNAVAILABLE,
1451                         _("You must be working online to complete this operation"));
1452                 return FALSE;
1453         }
1454         /* Use INBOX connection as the implementation would try to select inbox to ensure
1455          * we are not selected on the folder being deleted */
1456         server = camel_imapx_store_get_server (istore, "INBOX", cancellable, error);
1457         if (!server)
1458                 return FALSE;
1459
1460         success = camel_imapx_server_delete_folder (
1461                 server, folder_name, cancellable, error);
1462         g_object_unref (server);
1463
1464         if (success)
1465                 imapx_delete_folder_from_cache (istore, folder_name);
1466
1467         return success;
1468 }
1469
1470 static gboolean
1471 imapx_store_rename_folder_sync (CamelStore *store,
1472                                 const gchar *old,
1473                                 const gchar *new,
1474                                 GCancellable *cancellable,
1475                                 GError **error)
1476 {
1477         CamelIMAPXStore *istore = (CamelIMAPXStore *) store;
1478         CamelIMAPXServer *server;
1479         CamelService *service;
1480         CamelSettings *settings;
1481         const gchar *user_cache_dir;
1482         gchar *oldpath, *newpath, *storage_path;
1483         gboolean use_subscriptions;
1484         gboolean success = FALSE;
1485
1486         service = CAMEL_SERVICE (store);
1487         settings = camel_service_get_settings (service);
1488         user_cache_dir = camel_service_get_user_cache_dir (service);
1489
1490         use_subscriptions = camel_imapx_settings_get_use_subscriptions (
1491                 CAMEL_IMAPX_SETTINGS (settings));
1492
1493         if (!camel_offline_store_get_online (CAMEL_OFFLINE_STORE (store))) {
1494                 g_set_error (
1495                         error, CAMEL_SERVICE_ERROR,
1496                         CAMEL_SERVICE_ERROR_UNAVAILABLE,
1497                         _("You must be working online to complete this operation"));
1498                 return FALSE;
1499         }
1500
1501         if (use_subscriptions)
1502                 imapx_unsubscribe_folder (store, old, FALSE, cancellable, NULL);
1503
1504         /* Use INBOX connection as the implementation would try to select inbox to ensure
1505          * we are not selected on the folder being renamed */
1506         server = camel_imapx_store_get_server(istore, "INBOX", cancellable, error);
1507         if (server) {
1508                 success = camel_imapx_server_rename_folder (
1509                         server, old, new, cancellable, error);
1510                 g_object_unref (server);
1511         }
1512
1513         if (!success) {
1514                 imapx_subscribe_folder (store, old, FALSE, cancellable, NULL);
1515                 return FALSE;
1516         }
1517
1518         /* rename summary, and handle broken server */
1519         rename_folder_info (istore, old, new);
1520
1521         if (use_subscriptions)
1522                 success = imapx_subscribe_folder (
1523                         store, new, FALSE, cancellable, error);
1524
1525         storage_path = g_build_filename (user_cache_dir, "folders", NULL);
1526         oldpath = imapx_path_to_physical (storage_path, old);
1527         newpath = imapx_path_to_physical (storage_path, new);
1528         g_free (storage_path);
1529
1530         /* So do we care if this didn't work?  Its just a cache? */
1531         if (g_rename (oldpath, newpath) == -1) {
1532                 g_warning ("Could not rename message cache '%s' to '%s': %s: cache reset",
1533                            oldpath, newpath, g_strerror (errno));
1534         }
1535
1536         g_free (oldpath);
1537         g_free (newpath);
1538
1539         return success;
1540 }
1541
1542 static gboolean
1543 imapx_store_noop_sync (CamelStore *store,
1544                        GCancellable *cancellable,
1545                        GError **error)
1546 {
1547         CamelIMAPXStore *istore = (CamelIMAPXStore *) store;
1548         GList *list, *link;
1549         gboolean success = TRUE;
1550
1551         if (!camel_offline_store_get_online (CAMEL_OFFLINE_STORE (store)))
1552                 return TRUE;
1553
1554         list = camel_imapx_conn_manager_get_connections (istore->con_man);
1555
1556         for (link = list; link != NULL; link = g_list_next (link)) {
1557                 CamelIMAPXServer *server = CAMEL_IMAPX_SERVER (link->data);
1558
1559                 /* we just return last noops value, technically not correct though */
1560                 success = camel_imapx_server_noop (server, NULL, cancellable, error);
1561                 if (!success)
1562                         break;
1563         }
1564
1565         g_list_free_full (list, (GDestroyNotify) g_object_unref);
1566
1567         return success;
1568 }
1569
1570 static void
1571 imapx_migrate_to_user_cache_dir (CamelService *service)
1572 {
1573         const gchar *user_data_dir, *user_cache_dir;
1574
1575         g_return_if_fail (service != NULL);
1576         g_return_if_fail (CAMEL_IS_SERVICE (service));
1577
1578         user_data_dir = camel_service_get_user_data_dir (service);
1579         user_cache_dir = camel_service_get_user_cache_dir (service);
1580
1581         g_return_if_fail (user_data_dir != NULL);
1582         g_return_if_fail (user_cache_dir != NULL);
1583
1584         /* migrate only if the source directory exists and the destination doesn't */
1585         if (g_file_test (user_data_dir, G_FILE_TEST_EXISTS | G_FILE_TEST_IS_DIR) &&
1586             !g_file_test (user_cache_dir, G_FILE_TEST_EXISTS | G_FILE_TEST_IS_DIR)) {
1587                 gchar *parent_dir;
1588
1589                 parent_dir = g_path_get_dirname (user_cache_dir);
1590                 g_mkdir_with_parents (parent_dir, S_IRWXU);
1591                 g_free (parent_dir);
1592
1593                 if (g_rename (user_data_dir, user_cache_dir) == -1)
1594                         g_debug ("%s: Failed to migrate '%s' to '%s': %s", G_STRFUNC, user_data_dir, user_cache_dir, g_strerror (errno));
1595         }
1596 }
1597
1598 static gboolean
1599 imapx_store_initable_init (GInitable *initable,
1600                            GCancellable *cancellable,
1601                            GError **error)
1602 {
1603         CamelIMAPXStore *imapx_store;
1604         CamelStore *store;
1605         CamelService *service;
1606         const gchar *user_cache_dir;
1607         gchar *summary;
1608
1609         imapx_store = CAMEL_IMAPX_STORE (initable);
1610         store = CAMEL_STORE (initable);
1611         service = CAMEL_SERVICE (initable);
1612
1613         store->flags |= CAMEL_STORE_USE_CACHE_DIR;
1614         imapx_migrate_to_user_cache_dir (service);
1615
1616         /* Chain up to parent interface's init() method. */
1617         if (!parent_initable_interface->init (initable, cancellable, error))
1618                 return FALSE;
1619
1620         service = CAMEL_SERVICE (initable);
1621         user_cache_dir = camel_service_get_user_cache_dir (service);
1622
1623         imapx_store->summary = camel_imapx_store_summary_new ();
1624
1625         summary = g_build_filename (user_cache_dir, ".ev-store-summary", NULL);
1626         camel_store_summary_set_filename ((CamelStoreSummary *) imapx_store->summary, summary);
1627         camel_store_summary_load ((CamelStoreSummary *) imapx_store->summary);
1628
1629         g_free (summary);
1630
1631         return TRUE;
1632 }
1633
1634 static const gchar *
1635 imapx_store_get_service_name (CamelNetworkService *service,
1636                               CamelNetworkSecurityMethod method)
1637 {
1638         const gchar *service_name;
1639
1640         switch (method) {
1641                 case CAMEL_NETWORK_SECURITY_METHOD_SSL_ON_ALTERNATE_PORT:
1642                         service_name = "imaps";
1643                         break;
1644
1645                 default:
1646                         service_name = "imap";
1647                         break;
1648         }
1649
1650         return service_name;
1651 }
1652
1653 static guint16
1654 imapx_store_get_default_port (CamelNetworkService *service,
1655                               CamelNetworkSecurityMethod method)
1656 {
1657         guint16 default_port;
1658
1659         switch (method) {
1660                 case CAMEL_NETWORK_SECURITY_METHOD_SSL_ON_ALTERNATE_PORT:
1661                         default_port = IMAPS_PORT;
1662                         break;
1663
1664                 default:
1665                         default_port = IMAP_PORT;
1666                         break;
1667         }
1668
1669         return default_port;
1670 }
1671
1672 static gboolean
1673 imapx_store_folder_is_subscribed (CamelSubscribable *subscribable,
1674                                   const gchar *folder_name)
1675 {
1676         CamelIMAPXStore *istore = CAMEL_IMAPX_STORE (subscribable);
1677         CamelStoreInfo *si;
1678         gint is_subscribed = FALSE;
1679
1680         si = camel_store_summary_path ((CamelStoreSummary *) istore->summary, folder_name);
1681         if (si) {
1682                 is_subscribed = (si->flags & CAMEL_STORE_INFO_FOLDER_SUBSCRIBED) != 0;
1683                 camel_store_summary_info_free ((CamelStoreSummary *) istore->summary, si);
1684         }
1685
1686         return is_subscribed;
1687 }
1688
1689 static gboolean
1690 imapx_store_subscribe_folder_sync (CamelSubscribable *subscribable,
1691                                    const gchar *folder_name,
1692                                    GCancellable *cancellable,
1693                                    GError **error)
1694 {
1695         return imapx_subscribe_folder (
1696                 CAMEL_STORE (subscribable),
1697                 folder_name, TRUE, cancellable, error);
1698 }
1699
1700 static gboolean
1701 imapx_store_unsubscribe_folder_sync (CamelSubscribable *subscribable,
1702                                      const gchar *folder_name,
1703                                      GCancellable *cancellable,
1704                                      GError **error)
1705 {
1706         return imapx_unsubscribe_folder (
1707                 CAMEL_STORE (subscribable),
1708                 folder_name, TRUE, cancellable, error);
1709 }
1710
1711 static void
1712 camel_imapx_store_class_init (CamelIMAPXStoreClass *class)
1713 {
1714         GObjectClass *object_class;
1715         CamelServiceClass *service_class;
1716         CamelStoreClass *store_class;
1717
1718         object_class = G_OBJECT_CLASS (class);
1719         object_class->dispose = imapx_store_dispose;
1720         object_class->finalize = imapx_store_finalize;
1721
1722         service_class = CAMEL_SERVICE_CLASS (class);
1723         service_class->settings_type = CAMEL_TYPE_IMAPX_SETTINGS;
1724         service_class->get_name = imapx_get_name;
1725         service_class->connect_sync = imapx_connect_sync;
1726         service_class->disconnect_sync = imapx_disconnect_sync;
1727         service_class->authenticate_sync = imapx_authenticate_sync;
1728         service_class->query_auth_types_sync = imapx_query_auth_types_sync;
1729
1730         store_class = CAMEL_STORE_CLASS (class);
1731         store_class->hash_folder_name = imapx_name_hash;
1732         store_class->compare_folder_name = imapx_name_equal;
1733         store_class->can_refresh_folder = imapx_can_refresh_folder;
1734         store_class->free_folder_info = camel_store_free_folder_info_full;
1735         store_class->get_folder_sync = imapx_store_get_folder_sync;
1736         store_class->get_folder_info_sync = imapx_store_get_folder_info_sync;
1737         store_class->get_junk_folder_sync = imapx_store_get_junk_folder_sync;
1738         store_class->get_trash_folder_sync = imapx_store_get_trash_folder_sync;
1739         store_class->create_folder_sync = imapx_store_create_folder_sync;
1740         store_class->delete_folder_sync = imapx_store_delete_folder_sync;
1741         store_class->rename_folder_sync = imapx_store_rename_folder_sync;
1742         store_class->noop_sync = imapx_store_noop_sync;
1743 }
1744
1745 static void
1746 camel_imapx_store_initable_init (GInitableIface *interface)
1747 {
1748         parent_initable_interface = g_type_interface_peek_parent (interface);
1749
1750         interface->init = imapx_store_initable_init;
1751 }
1752
1753 static void
1754 camel_network_service_init (CamelNetworkServiceInterface *interface)
1755 {
1756         interface->get_service_name = imapx_store_get_service_name;
1757         interface->get_default_port = imapx_store_get_default_port;
1758 }
1759
1760 static void
1761 camel_subscribable_init (CamelSubscribableInterface *interface)
1762 {
1763         interface->folder_is_subscribed = imapx_store_folder_is_subscribed;
1764         interface->subscribe_folder_sync = imapx_store_subscribe_folder_sync;
1765         interface->unsubscribe_folder_sync = imapx_store_unsubscribe_folder_sync;
1766 }
1767
1768 static void
1769 camel_imapx_store_init (CamelIMAPXStore *istore)
1770 {
1771         istore->get_finfo_lock = g_mutex_new ();
1772         istore->last_refresh_time = time (NULL) - (FINFO_REFRESH_INTERVAL + 10);
1773         istore->dir_sep = '/';
1774         istore->con_man = camel_imapx_conn_manager_new (CAMEL_STORE (istore));
1775
1776         imapx_utils_init ();
1777 }
1778