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