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