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