Merge miscellaneous cleanups from camel-gobject.
[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 /*
5  * Authors: Michael Zucchi <notzed@ximian.com>
6  *
7  * Copyright (C) 2000-2002 Ximian, Inc. (www.ximian.com)
8  *
9  * This program is free software; you can redistribute it and/or
10  * modify it under the terms of version 2 of the GNU General Public
11  * License as published by the Free Software Foundation.
12  *
13  * This program is distributed in the hope that it will be useful,
14  * but WITHOUT ANY WARRANTY; without even the implied warranty of
15  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16  * GNU General Public License for more details.
17  *
18  * You should have received a copy of the GNU General Public License
19  * along with this program; if not, write to the Free Software
20  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
21  * USA
22  */
23
24 #ifdef HAVE_CONFIG_H
25 #include <config.h>
26 #endif
27
28 #include <sys/types.h>
29 #ifdef _WIN32
30 #include <winsock2.h>
31 #else
32 #include <sys/socket.h>
33 #include <netinet/in.h>
34 #endif
35 #include <errno.h>
36 #include <stdio.h>
37 #include <stdlib.h>
38 #include <string.h>
39 #include <unistd.h>
40 #include <errno.h>
41 #include <glib/gstdio.h>
42 #include <glib/gi18n-lib.h>
43
44 #include <camel/camel-private.h>
45
46 #include "camel-imapx-store.h"
47 #include "camel-imapx-folder.h"
48 #include "camel-imapx-exception.h"
49 #include "camel-imapx-utils.h"
50 #include "camel-imapx-server.h"
51 #include "camel-imapx-summary.h"
52
53 /* Specified in RFC 2060 section 2.1 */
54 #define IMAP_PORT 143
55
56 #define FINFO_REFRESH_INTERVAL 60
57
58 static CamelOfflineStoreClass *parent_class = NULL;
59
60 static guint
61 imapx_name_hash(gconstpointer key)
62 {
63         if (g_ascii_strcasecmp(key, "INBOX") == 0)
64                 return g_str_hash("INBOX");
65         else
66                 return g_str_hash(key);
67 }
68
69 static gint
70 imapx_name_equal(gconstpointer a, gconstpointer b)
71 {
72         gconstpointer aname = a, bname = b;
73
74         if (g_ascii_strcasecmp(a, "INBOX") == 0)
75                 aname = "INBOX";
76         if (g_ascii_strcasecmp(b, "INBOX") == 0)
77                 bname = "INBOX";
78         return g_str_equal(aname, bname);
79 }
80
81 static void
82 imapx_parse_receiving_options (CamelIMAPXStore *istore, CamelURL *url)
83 {
84         if (camel_url_get_param (url, "use_lsub"))
85                 istore->rec_options |= IMAPX_SUBSCRIPTIONS;
86
87         if (camel_url_get_param (url, "override_namespace") && camel_url_get_param (url, "namespace")) {
88                 istore->rec_options |= IMAPX_OVERRIDE_NAMESPACE;
89                 g_free(istore->namespace);
90                 istore->namespace = g_strdup (camel_url_get_param (url, "namespace"));
91         }
92
93         if (camel_url_get_param (url, "check_all"))
94                 istore->rec_options |= IMAPX_CHECK_ALL;
95
96         if (camel_url_get_param (url, "check_lsub"))
97                 istore->rec_options |= IMAPX_CHECK_LSUB;
98
99         if (camel_url_get_param (url, "filter")) {
100                 istore->rec_options |= IMAPX_FILTER_INBOX;
101                 ((CamelStore *) istore)->flags |= CAMEL_STORE_FILTER_INBOX;
102         }
103
104         if (camel_url_get_param (url, "filter_junk"))
105                 istore->rec_options |= IMAPX_FILTER_JUNK;
106
107         if (camel_url_get_param (url, "filter_junk_inbox"))
108                 istore->rec_options |= IMAPX_FILTER_JUNK_INBOX;
109
110         if (camel_url_get_param (url, "use_idle"))
111                 istore->rec_options |= IMAPX_USE_IDLE;
112 }
113
114 static void
115 imapx_construct(CamelService *service, CamelSession *session, CamelProvider *provider, CamelURL *url, CamelException *ex)
116 {
117         gchar *summary;
118         CamelIMAPXStore *store = (CamelIMAPXStore *)service;
119
120         CAMEL_SERVICE_CLASS (parent_class)->construct (service, session, provider, url, ex);
121         if (camel_exception_is_set(ex))
122                 return;
123
124         store->base_url = camel_url_to_string (service->url, (CAMEL_URL_HIDE_PASSWORD |
125                                                                    CAMEL_URL_HIDE_PARAMS |
126                                                                    CAMEL_URL_HIDE_AUTH));
127         imapx_parse_receiving_options (store, service->url);
128
129         store->summary = camel_imapx_store_summary_new();
130         store->storage_path = camel_session_get_storage_path(session, service, ex);
131         if (store->storage_path) {
132                 summary = g_build_filename(store->storage_path, ".ev-store-summary", NULL);
133                 camel_store_summary_set_filename((CamelStoreSummary *)store->summary, summary);
134                 /* FIXME: need to remove params, passwords, etc */
135                 camel_store_summary_set_uri_base((CamelStoreSummary *)store->summary, service->url);
136                 camel_store_summary_load((CamelStoreSummary *)store->summary);
137         }
138 }
139
140 extern CamelServiceAuthType camel_imapx_password_authtype;
141
142 static GList *
143 imapx_query_auth_types (CamelService *service, CamelException *ex)
144 {
145         CamelIMAPXStore *istore = CAMEL_IMAPX_STORE (service);
146         CamelServiceAuthType *authtype;
147         GList *sasl_types, *t, *next;
148         gboolean connected;
149         
150         if (CAMEL_OFFLINE_STORE (istore)->state == CAMEL_OFFLINE_STORE_NETWORK_UNAVAIL) {
151                 camel_exception_set (ex, CAMEL_EXCEPTION_SERVICE_UNAVAILABLE,
152                                      _("You must be working online to complete this operation"));
153                 return NULL;
154         }
155
156         CAMEL_SERVICE_REC_LOCK (istore, connect_lock);
157         
158         if (istore->server == NULL)
159                 istore->server = camel_imapx_server_new((CamelStore *)istore, service->url);
160         
161         connected = istore->server->stream != NULL;
162         if (!connected)
163                 connected = imapx_connect_to_server (istore->server, ex);
164         CAMEL_SERVICE_REC_UNLOCK (istore, connect_lock);
165         if (!connected)
166                 return NULL;
167
168         sasl_types = camel_sasl_authtype_list (FALSE);
169         for (t = sasl_types; t; t = next) {
170                 authtype = t->data;
171                 next = t->next;
172
173                 if (!g_hash_table_lookup (istore->server->cinfo->auth_types, authtype->authproto)) {
174                         sasl_types = g_list_remove_link (sasl_types, t);
175                         g_list_free_1 (t);
176                 }
177         }
178
179         return g_list_prepend (sasl_types, &camel_imapx_password_authtype);
180 }
181
182 static gchar *
183 imapx_get_name (CamelService *service, gboolean brief)
184 {
185         if (brief)
186                 return g_strdup_printf (_("IMAP server %s"), service->url->host);
187         else
188                 return g_strdup_printf (_("IMAP service for %s on %s"),
189                                         service->url->user, service->url->host);
190 }
191
192 static gboolean
193 imapx_connect (CamelService *service, CamelException *ex)
194 {
195         CamelIMAPXStore *istore = (CamelIMAPXStore *)service;
196
197         /* We never really are 'connected' or 'disconnected' */
198         if (istore->server == NULL)
199                 istore->server = camel_imapx_server_new((CamelStore *)istore, service->url);
200
201         return camel_imapx_server_connect (istore->server, TRUE, ex);
202 }
203
204 static gboolean
205 imapx_disconnect (CamelService *service, gboolean clean, CamelException *ex)
206 {
207         CamelIMAPXStore *istore = CAMEL_IMAPX_STORE (service);
208
209         CAMEL_SERVICE_CLASS (parent_class)->disconnect (service, clean, ex);
210
211         if (istore->server)
212                 camel_imapx_server_connect(istore->server, FALSE, ex);
213
214         return TRUE;
215 }
216
217 static CamelFolder *
218 imapx_get_junk(CamelStore *store, CamelException *ex)
219 {
220         CamelFolder *folder = CAMEL_STORE_CLASS(parent_class)->get_junk(store, ex);
221
222         if (folder) {
223                 gchar *state = g_build_filename(((CamelIMAPXStore *)store)->storage_path, "system", "Junk.cmeta", NULL);
224
225                 camel_object_set(folder, NULL, CAMEL_OBJECT_STATE_FILE, state, NULL);
226                 g_free(state);
227                 /* no defaults? */
228                 camel_object_state_read(folder);
229         }
230
231         return folder;
232 }
233
234 static CamelFolder *
235 imapx_get_trash (CamelStore *store, CamelException *ex)
236 {
237         CamelFolder *folder = CAMEL_STORE_CLASS(parent_class)->get_trash(store, ex);
238
239         if (folder) {
240                 gchar *state = g_build_filename(((CamelIMAPXStore *)store)->storage_path, "system", "Trash.cmeta", NULL);
241
242                 camel_object_set(folder, NULL, CAMEL_OBJECT_STATE_FILE, state, NULL);
243                 g_free(state);
244                 /* no defaults? */
245                 camel_object_state_read(folder);
246         }
247
248         return folder;
249 }
250
251 static void
252 imapx_noop (CamelStore *store, CamelException *ex)
253 {
254         CamelIMAPXStore *istore = (CamelIMAPXStore *) store;
255
256         if (CAMEL_OFFLINE_STORE(store)->state == CAMEL_OFFLINE_STORE_NETWORK_UNAVAIL)
257                 return;
258
259         if (istore->server && camel_imapx_server_connect (istore->server, TRUE, ex))
260                 camel_imapx_server_noop (istore->server, NULL, ex);
261 }
262
263 static guint
264 imapx_hash_folder_name (gconstpointer key)
265 {
266         if (g_ascii_strcasecmp (key, "INBOX") == 0)
267                 return g_str_hash ("INBOX");
268         else
269                 return g_str_hash (key);
270 }
271
272 static gint
273 imapx_compare_folder_name (gconstpointer a, gconstpointer b)
274 {
275         gconstpointer aname = a, bname = b;
276
277         if (g_ascii_strcasecmp (a, "INBOX") == 0)
278                 aname = "INBOX";
279         if (g_ascii_strcasecmp (b, "INBOX") == 0)
280                 bname = "INBOX";
281         return g_str_equal (aname, bname);
282 }
283
284 static CamelFolder *
285 get_folder_offline (CamelStore *store, const gchar *folder_name,
286                     guint32 flags, CamelException *ex)
287 {
288         CamelIMAPXStore *imapx_store = CAMEL_IMAPX_STORE (store);
289         CamelFolder *new_folder = NULL;
290         CamelStoreInfo *si;
291
292         si = camel_store_summary_path((CamelStoreSummary *)imapx_store->summary, folder_name);
293         if (si) {
294                 gchar *folder_dir, *storage_path;
295
296                 /* Note: Although the INBOX is defined to be case-insensitive in the IMAP RFC
297                  * it is still up to the server how to acutally name it in a LIST response. Since
298                  * we stored the name as the server provided it us in the summary we take that name
299                  * to look up the folder.
300                  * But for the on-disk cache we do always capitalize the Inbox no matter what the
301                  * server provided.
302                  */
303                 if (!g_ascii_strcasecmp (folder_name, "INBOX"))
304                         folder_name = "INBOX";
305
306                 storage_path = g_strdup_printf("%s/folders", imapx_store->storage_path);
307                 folder_dir = imapx_path_to_physical (storage_path, folder_name);
308                 g_free(storage_path);
309                 /* FIXME */
310                 new_folder = camel_imapx_folder_new (store, folder_dir, folder_name, ex);
311
312                 g_free(folder_dir);
313                 camel_store_summary_info_free((CamelStoreSummary *)imapx_store->summary, si);
314         } else {
315                 camel_exception_setv (ex, CAMEL_EXCEPTION_STORE_NO_FOLDER,
316                                       _("No such folder %s"), folder_name);
317         }
318
319         return new_folder;
320 }
321
322 static CamelFolder *
323 imapx_get_folder (CamelStore *store, const gchar *folder_name, guint32 flags, CamelException *ex)
324 {
325         CamelFolder *folder;
326
327         folder = get_folder_offline(store, folder_name, flags, ex);
328         if (folder == NULL) {
329                 camel_exception_setv(ex, 2, "No such folder: %s", folder_name);
330                 return NULL;
331         }
332
333         return folder;
334 }
335
336 static CamelFolder *
337 imapx_get_inbox(CamelStore *store, CamelException *ex)
338 {
339         camel_exception_setv(ex, 1, "get_inbox::unimplemented");
340
341         return NULL;
342 }
343
344 /* folder_name is path name */
345 static CamelFolderInfo *
346 imapx_build_folder_info (CamelIMAPXStore *imapx_store, const gchar *folder_name)
347 {
348         CamelURL *url;
349         const gchar *name;
350         CamelFolderInfo *fi;
351
352         fi = camel_folder_info_new ();
353         fi->full_name = g_strdup(folder_name);
354         fi->unread = -1;
355         fi->total = -1;
356
357         url = camel_url_new (imapx_store->base_url, NULL);
358         g_free (url->path);
359         url->path = g_strdup_printf ("/%s", folder_name);
360         fi->uri = camel_url_to_string (url, CAMEL_URL_HIDE_ALL);
361         camel_url_free(url);
362         name = strrchr (fi->full_name, '/');
363         if (name == NULL)
364                 name = fi->full_name;
365         else
366                 name++;
367         if (!g_ascii_strcasecmp (fi->full_name, "INBOX"))
368                 fi->name = g_strdup (_("Inbox"));
369         /* Do not localize the rest, these are from a server, thus shouldn't be localized */
370         /*else if (!g_ascii_strcasecmp (fi->full_name, "Drafts"))
371                 fi->name = g_strdup (_("Drafts"));
372         else if (!g_ascii_strcasecmp (fi->full_name, "Sent"))
373                 fi->name = g_strdup (_("Sent"));
374         else if (!g_ascii_strcasecmp (fi->full_name, "Templates"))
375                 fi->name = g_strdup (_("Templates"));
376         else if (!g_ascii_strcasecmp (fi->full_name, "Trash"))
377                 fi->name = g_strdup (_("Trash"));*/
378         else
379                 fi->name = g_strdup (name);
380
381         return fi;
382 }
383
384 static void
385 fill_fi(CamelStore *store, CamelFolderInfo *fi, guint32 flags)
386 {
387         CamelFolder *folder;
388
389         folder = camel_object_bag_peek(store->folders, fi->full_name);
390         if (folder) {
391                 CamelIMAPXSummary *ims;
392
393                 if (folder->summary)
394                         ims = (CamelIMAPXSummary *) folder->summary;
395                 else
396                         ims = (CamelIMAPXSummary *) camel_imapx_summary_new (folder, NULL);
397
398                 fi->unread = ((CamelFolderSummary *)ims)->unread_count;
399                 fi->total = ((CamelFolderSummary *)ims)->saved_count;
400
401                 if (!folder->summary)
402                         camel_object_unref (ims);
403                 camel_object_unref(folder);
404         }
405 }
406
407 /* imap needs to treat inbox case insensitive */
408 /* we'll assume the names are normalized already */
409 static guint
410 folder_hash(gconstpointer ap)
411 {
412         const gchar *a = ap;
413
414         if (g_ascii_strcasecmp(a, "INBOX") == 0)
415                 a = "INBOX";
416
417         return g_str_hash(a);
418 }
419
420 static gint
421 folder_eq(gconstpointer ap, gconstpointer bp)
422 {
423         const gchar *a = ap;
424         const gchar *b = bp;
425
426         if (g_ascii_strcasecmp(a, "INBOX") == 0)
427                 a = "INBOX";
428         if (g_ascii_strcasecmp(b, "INBOX") == 0)
429                 b = "INBOX";
430
431         return g_str_equal(a, b);
432 }
433
434 static gboolean
435 imapx_match_pattern(CamelIMAPXStoreNamespace *ns, const gchar *pattern, const gchar *name)
436 {
437         gchar p, n, dir_sep;
438
439         if (!ns)
440                 return TRUE;
441
442         dir_sep = ns->sep;
443         p = *pattern++;
444         n = *name++;
445         while (n && p) {
446                 if (n == p) {
447                         p = *pattern++;
448                         n = *name++;
449                 } else if (p == '%') {
450                         if (n != dir_sep) {
451                                 n = *name++;
452                         } else {
453                                 p = *pattern++;
454                         }
455                 } else if (p == '*') {
456                         return TRUE;
457                 } else
458                         return FALSE;
459         }
460
461         return n == 0 && (p == '%' || p == 0);
462 }
463
464 static void
465 imapx_unmark_folder_subscribed (CamelIMAPXStore *istore, const gchar *folder_name, gboolean emit_signal, CamelException *ex)
466 {
467         CamelStoreInfo *si;
468
469         si = camel_store_summary_path((CamelStoreSummary *)istore->summary, folder_name);
470         if (si) {
471                 if (si->flags & CAMEL_STORE_INFO_FOLDER_SUBSCRIBED) {
472                         si->flags &= ~CAMEL_STORE_INFO_FOLDER_SUBSCRIBED;
473                         camel_store_summary_touch((CamelStoreSummary *)istore->summary);
474                         camel_store_summary_save((CamelStoreSummary *)istore->summary);
475                 }
476                 camel_store_summary_info_free((CamelStoreSummary *)istore->summary, si);
477         }
478
479         if (emit_signal) {
480                 CamelFolderInfo *fi;
481
482                 fi = imapx_build_folder_info(istore, folder_name);
483                 camel_object_trigger_event (CAMEL_OBJECT (istore), "folder_unsubscribed", fi);
484                 camel_folder_info_free (fi);
485         }
486 }
487
488 static void
489 imapx_mark_folder_subscribed (CamelIMAPXStore *istore, const gchar *folder_name, gboolean emit_signal, CamelException *ex)
490 {
491         CamelStoreInfo *si;
492
493         si = camel_store_summary_path((CamelStoreSummary *)istore->summary, folder_name);
494         if (si) {
495                 if ((si->flags & CAMEL_STORE_INFO_FOLDER_SUBSCRIBED) == 0) {
496                         si->flags |= CAMEL_STORE_INFO_FOLDER_SUBSCRIBED;
497                         camel_store_summary_touch((CamelStoreSummary *)istore->summary);
498                         camel_store_summary_save((CamelStoreSummary *)istore->summary);
499                 }
500                 camel_store_summary_info_free((CamelStoreSummary *)istore->summary, si);
501         }
502         
503         if (emit_signal) {
504                 CamelFolderInfo *fi;
505
506                 fi = imapx_build_folder_info(istore, folder_name);
507                 camel_object_trigger_event (CAMEL_OBJECT (istore), "folder_subscribed", fi);
508                 camel_folder_info_free (fi);
509         }
510 }
511
512 static void
513 imapx_subscribe_folder (CamelStore *store, const gchar *folder_name, gboolean emit_signal, CamelException *ex)
514 {
515         CamelIMAPXStore *istore = (CamelIMAPXStore *) store;
516
517         if (CAMEL_OFFLINE_STORE(store)->state == CAMEL_OFFLINE_STORE_NETWORK_UNAVAIL)
518                 return;
519
520         if (istore->server && camel_imapx_server_connect (istore->server, TRUE, ex))
521                 camel_imapx_server_manage_subscription (istore->server, folder_name, TRUE, ex);
522
523         if (!camel_exception_is_set (ex))
524                 imapx_mark_folder_subscribed (istore, folder_name, emit_signal, ex);
525 }
526
527 static void
528 imapx_unsubscribe_folder (CamelStore *store, const gchar *folder_name, gboolean emit_signal, CamelException *ex)
529 {
530         CamelIMAPXStore *istore = (CamelIMAPXStore *) store;
531
532         if (CAMEL_OFFLINE_STORE(store)->state == CAMEL_OFFLINE_STORE_NETWORK_UNAVAIL)
533                 return;
534
535         if (istore->server && camel_imapx_server_connect (istore->server, TRUE, ex))
536                 camel_imapx_server_manage_subscription (istore->server, folder_name, FALSE, ex);
537         
538         if (!camel_exception_is_set (ex))
539                 imapx_unmark_folder_subscribed (istore, folder_name, emit_signal, ex);
540 }
541
542 static void
543 imapx_store_subscribe_folder (CamelStore *store, const gchar *folder_name, CamelException *ex)
544 {
545         imapx_subscribe_folder (store, folder_name, TRUE, ex);
546 }
547
548 static void
549 imapx_store_unsubscribe_folder (CamelStore *store, const gchar *folder_name, CamelException *ex)
550 {
551         CamelException eex = CAMEL_EXCEPTION_INITIALISER;
552         
553         if (!ex)
554                 ex = &eex;
555
556         imapx_unsubscribe_folder (store, folder_name, TRUE, ex);
557 }
558
559 static void
560 imapx_delete_folder_from_cache (CamelIMAPXStore *istore, const gchar *folder_name, CamelException *ex)
561 {
562         gchar *state_file;
563         gchar *folder_dir, *storage_path;
564         CamelFolderInfo *fi;
565
566         storage_path = g_strdup_printf ("%s/folders", istore->storage_path);
567         folder_dir = imapx_path_to_physical (storage_path, folder_name);
568         g_free (storage_path);
569         if (g_access (folder_dir, F_OK) != 0) {
570                 g_free (folder_dir);
571                 goto event;
572         }
573
574         /* Delete summary and all the data */
575         state_file = g_strdup_printf ("%s/cmeta", folder_dir);
576         g_unlink (state_file);
577         g_free (state_file);
578
579         camel_db_delete_folder (((CamelStore *)istore)->cdb_w, folder_name, ex);
580         g_rmdir (folder_dir);
581
582         state_file = g_strdup_printf("%s/subfolders", folder_dir);
583         g_rmdir(state_file);
584         g_free(state_file);
585
586         g_rmdir (folder_dir);
587         g_free (folder_dir);
588
589  event:
590         camel_store_summary_remove_path((CamelStoreSummary *)istore->summary, folder_name);
591         camel_store_summary_save((CamelStoreSummary *)istore->summary);
592
593         fi = imapx_build_folder_info(istore, folder_name);
594         camel_object_trigger_event (CAMEL_OBJECT (istore), "folder_deleted", fi);
595         camel_folder_info_free (fi);
596 }
597
598 static void
599 imapx_delete_folder (CamelStore *store, const gchar *folder_name, CamelException *ex)
600 {
601         CamelIMAPXStore *istore = (CamelIMAPXStore *) store;
602
603         if (CAMEL_OFFLINE_STORE (store)->state == CAMEL_OFFLINE_STORE_NETWORK_UNAVAIL) {
604                 camel_exception_set (ex, CAMEL_EXCEPTION_SERVICE_UNAVAILABLE,
605                                      _("You must be working online to complete this operation"));
606                 return;
607         }
608
609         if (istore->server && camel_imapx_server_connect (istore->server, TRUE, ex))
610                 camel_imapx_server_delete_folder (istore->server, folder_name, ex);
611
612         if (!camel_exception_is_set (ex))
613                 imapx_delete_folder_from_cache (istore, folder_name, ex);
614 }
615
616
617 static void
618 rename_folder_info (CamelIMAPXStore *istore, const gchar *old_name, const gchar *new_name, CamelException *ex)
619 {
620         gint i, count;
621         CamelStoreInfo *si;
622         gint olen = strlen(old_name);
623         const gchar *path;
624         gchar *npath, *nfull;
625
626         count = camel_store_summary_count((CamelStoreSummary *)istore->summary);
627         for (i=0;i<count;i++) {
628                 si = camel_store_summary_index((CamelStoreSummary *)istore->summary, i);
629                 if (si == NULL)
630                         continue;
631                 path = camel_store_info_path(istore->summary, si);
632                 if (strncmp(path, old_name, olen) == 0) {
633                         if (strlen(path) > olen)
634                                 npath = g_strdup_printf("%s/%s", new_name, path+olen+1);
635                         else
636                                 npath = g_strdup(new_name);
637                         nfull = camel_imapx_store_summary_path_to_full(istore->summary, npath, istore->dir_sep);
638
639                         /* workaround for broken server (courier uses '.') that doesn't rename
640                            subordinate folders as required by rfc 2060 */
641                         if (istore->dir_sep == '.') {
642                                 camel_imapx_server_rename_folder (istore->server, path, nfull, ex);
643                         }
644
645                         camel_store_info_set_string((CamelStoreSummary *)istore->summary, si, CAMEL_STORE_INFO_PATH, npath);
646                         camel_store_info_set_string((CamelStoreSummary *)istore->summary, si, CAMEL_IMAPX_STORE_INFO_FULL_NAME, nfull);
647
648                         camel_store_summary_touch((CamelStoreSummary *)istore->summary);
649                         g_free(nfull);
650                         g_free(npath);
651                 }
652                 camel_store_summary_info_free((CamelStoreSummary *)istore->summary, si);
653         }
654 }
655
656 static void
657 imapx_rename_folder (CamelStore *store, const gchar *old, const gchar *new, CamelException *ex)
658 {
659         CamelIMAPXStore *istore = (CamelIMAPXStore *) store;
660         gchar *oldpath, *newpath, *storage_path;
661
662
663         if (CAMEL_OFFLINE_STORE (store)->state == CAMEL_OFFLINE_STORE_NETWORK_UNAVAIL) {
664                 camel_exception_set (ex, CAMEL_EXCEPTION_SERVICE_UNAVAILABLE,
665                                      _("You must be working online to complete this operation"));
666                 return;
667         }
668
669         if (istore->rec_options & IMAPX_SUBSCRIPTIONS)
670                 imapx_unsubscribe_folder (store, old, FALSE, ex);
671
672         if (istore->server && camel_imapx_server_connect (istore->server, TRUE, ex))
673                 camel_imapx_server_rename_folder (istore->server, old, new, ex);
674
675         if (camel_exception_is_set (ex)) {
676                 imapx_subscribe_folder (store, old, FALSE, ex);
677                 return;
678         }
679
680         /* rename summary, and handle broken server */
681         rename_folder_info(istore, old, new, ex);
682
683         if (istore->rec_options & IMAPX_SUBSCRIPTIONS)
684                 imapx_subscribe_folder (store, new, FALSE, ex);
685
686         storage_path = g_strdup_printf("%s/folders", istore->storage_path);
687         oldpath = imapx_path_to_physical (storage_path, old);
688         newpath = imapx_path_to_physical (storage_path, new);
689         g_free(storage_path);
690
691         /* So do we care if this didn't work?  Its just a cache? */
692         if (g_rename (oldpath, newpath) == -1) {
693                 g_warning ("Could not rename message cache '%s' to '%s': %s: cache reset",
694                            oldpath, newpath, g_strerror (errno));
695         }
696
697         g_free (oldpath);
698         g_free (newpath);
699 }
700
701 static CamelFolderInfo *
702 imapx_create_folder (CamelStore *store, const gchar *parent_name, const gchar *folder_name, CamelException *ex)
703 {       
704         const gchar *c;
705         CamelStoreInfo *si;
706         CamelIMAPXStoreNamespace *ns;
707         CamelIMAPXStore *istore = (CamelIMAPXStore *) store;
708         gchar *real_name, *full_name, *parent_real;
709         CamelFolderInfo *fi = NULL;
710         gchar dir_sep;
711         
712         if (CAMEL_OFFLINE_STORE (store)->state == CAMEL_OFFLINE_STORE_NETWORK_UNAVAIL) {
713                 camel_exception_set (ex, CAMEL_EXCEPTION_SERVICE_UNAVAILABLE,
714                                      _("You must be working online to complete this operation"));
715                 return NULL;
716         }
717         
718         if (!(istore->server && camel_imapx_server_connect (istore->server, TRUE, ex)))
719                 return NULL;
720
721         if (!parent_name)
722                 parent_name = "";
723
724         ns = camel_imapx_store_summary_namespace_find_path (istore->summary, parent_name);
725         if (ns)
726                 dir_sep = ns->sep;
727         else
728                 dir_sep = '/';
729
730         c = folder_name;
731         while (*c && *c != dir_sep && !strchr ("#%*", *c))
732                 c++;
733
734         if (*c != '\0') {
735                 camel_exception_setv (ex, CAMEL_EXCEPTION_FOLDER_INVALID_PATH,
736                                       _("The folder name \"%s\" is invalid because it contains the character \"%c\""),
737                                       folder_name, *c);
738                 return NULL;
739         }
740         
741         parent_real = camel_imapx_store_summary_full_from_path(istore->summary, parent_name);
742         if (parent_real == NULL) {
743                 camel_exception_setv(ex, CAMEL_EXCEPTION_FOLDER_INVALID_STATE,
744                                      _("Unknown parent folder: %s"), parent_name);
745                 return NULL;
746         }
747
748         si = camel_store_summary_path ((CamelStoreSummary *)istore->summary, parent_name);
749         if (si && si->flags & CAMEL_STORE_INFO_FOLDER_NOINFERIORS) {
750                 camel_exception_set (ex, CAMEL_EXCEPTION_FOLDER_INVALID_STATE,
751                                 _("The parent folder is not allowed to contain subfolders"));
752                 return NULL;
753         }
754
755         if (si)
756                 camel_store_summary_info_free ((CamelStoreSummary *) istore->summary, si);
757
758         real_name = camel_imapx_store_summary_path_to_full (istore->summary, folder_name, dir_sep);
759         full_name = imapx_concat (istore, parent_real, real_name);
760         g_free(real_name);
761
762         camel_imapx_server_create_folder (istore->server, full_name, ex);
763
764         if (!camel_exception_is_set (ex)) {
765                 CamelIMAPXStoreInfo *si;
766
767                 si = camel_imapx_store_summary_add_from_full(istore->summary, full_name, dir_sep);
768                 camel_store_summary_save((CamelStoreSummary *)istore->summary);
769                 fi = imapx_build_folder_info(istore, camel_store_info_path(istore->summary, si));
770                 fi->flags |= CAMEL_FOLDER_NOCHILDREN;
771                 camel_object_trigger_event (CAMEL_OBJECT (store), "folder_created", fi);
772         }
773
774
775         g_free (full_name);
776         g_free(parent_real);
777
778         return fi;
779 }
780
781
782 static CamelFolderInfo *
783 get_folder_info_offline (CamelStore *store, const gchar *top,
784                          guint32 flags, CamelException *ex)
785 {
786         CamelIMAPXStore *imapx_store = CAMEL_IMAPX_STORE (store);
787         gboolean include_inbox = FALSE;
788         CamelFolderInfo *fi;
789         GPtrArray *folders;
790         gchar *pattern, *name;
791         gint i;
792
793         /* FIXME: obey other flags */
794
795         folders = g_ptr_array_new ();
796
797         if (top == NULL || top[0] == '\0') {
798                 include_inbox = TRUE;
799                 top = "";
800         }
801
802         /* get starting point */
803         if (top[0] == 0) {
804                 if (imapx_store->namespace && imapx_store->namespace[0]) {
805                         name = g_strdup(imapx_store->summary->namespaces->personal->full_name);
806                         top = imapx_store->summary->namespaces->personal->path;
807                 } else
808                         name = g_strdup("");
809         } else {
810                 name = camel_imapx_store_summary_full_from_path(imapx_store->summary, top);
811                 if (name == NULL)
812                         name = camel_imapx_store_summary_path_to_full(imapx_store->summary, top, imapx_store->dir_sep);
813         }
814
815         pattern = imapx_concat(imapx_store, name, "*");
816
817         /* folder_info_build will insert parent nodes as necessary and mark
818          * them as noselect, which is information we actually don't have at
819          * the moment. So let it do the right thing by bailing out if it's
820          * not a folder we're explicitly interested in. */
821
822         for (i=0;i<camel_store_summary_count((CamelStoreSummary *)imapx_store->summary);i++) {
823                 CamelStoreInfo *si = camel_store_summary_index((CamelStoreSummary *)imapx_store->summary, i);
824                 const gchar *full_name;
825                 CamelIMAPXStoreNamespace *ns;
826
827                 if (si == NULL)
828                         continue;
829
830                 full_name = camel_imapx_store_info_full_name (imapx_store->summary, si);
831                 if (!full_name || !*full_name) {
832                         camel_store_summary_info_free ((CamelStoreSummary *)imapx_store->summary, si);
833                         continue;
834                 }
835
836                 ns = camel_imapx_store_summary_namespace_find_full (imapx_store->summary, full_name);
837
838                 /* Modify the checks to see match the namespaces from preferences */
839                 if ((g_str_equal (name, full_name)
840                      || imapx_match_pattern (ns, pattern, full_name)
841                      || (include_inbox && !g_ascii_strcasecmp (full_name, "INBOX")))
842                     && ( ((imapx_store->rec_options & IMAPX_SUBSCRIPTIONS) == 0
843                             || (flags & CAMEL_STORE_FOLDER_INFO_SUBSCRIBED) == 0)
844                         || (si->flags & CAMEL_STORE_INFO_FOLDER_SUBSCRIBED)
845                         || (flags & CAMEL_STORE_FOLDER_INFO_SUBSCRIPTION_LIST) != 0)) {
846
847                         fi = imapx_build_folder_info(imapx_store, camel_store_info_path((CamelStoreSummary *)imapx_store->summary, si));
848                         fi->unread = si->unread;
849                         fi->total = si->total;
850                         fi->flags = si->flags;
851                         /* HACK: some servers report noinferiors for all folders (uw-imapd)
852                            We just translate this into nochildren, and let the imap layer enforce
853                            it.  See create folder */
854                         if (fi->flags & CAMEL_FOLDER_NOINFERIORS)
855                                 fi->flags = (fi->flags & ~CAMEL_FOLDER_NOINFERIORS) | CAMEL_FOLDER_NOCHILDREN;
856
857                         /* blah, this gets lost somewhere, i can't be bothered finding out why */
858                         if (!g_ascii_strcasecmp(fi->full_name, "inbox")) {
859                                 fi->flags = (fi->flags & ~CAMEL_FOLDER_TYPE_MASK) | CAMEL_FOLDER_TYPE_INBOX;
860                                 fi->flags |= CAMEL_FOLDER_SYSTEM;
861                         }
862
863                         if (si->flags & CAMEL_FOLDER_NOSELECT) {
864                                 CamelURL *url = camel_url_new(fi->uri, NULL);
865
866                                 camel_url_set_param (url, "noselect", "yes");
867                                 g_free(fi->uri);
868                                 fi->uri = camel_url_to_string (url, 0);
869                                 camel_url_free (url);
870                         } else {
871                                 fill_fi((CamelStore *)imapx_store, fi, 0);
872                         }
873                         if (!fi->child)
874                                 fi->flags |= CAMEL_FOLDER_NOCHILDREN;
875                         g_ptr_array_add (folders, fi);
876                 }
877                 camel_store_summary_info_free((CamelStoreSummary *)imapx_store->summary, si);
878         }
879         g_free(pattern);
880
881         fi = camel_folder_info_build (folders, top, '/', TRUE);
882         g_ptr_array_free (folders, TRUE);
883         g_free(name);
884
885         return fi;
886 }
887
888 static void
889 add_folders_to_summary (CamelIMAPXStore *istore, GPtrArray *folders, GHashTable *table, gboolean subcribed)
890 {
891         gint i = 0;
892
893         for (i = 0; i < folders->len; i++) {
894                 struct _list_info *li = folders->pdata[i];
895                 CamelIMAPXStoreInfo *si;
896                 guint32 new_flags;
897                 CamelFolderInfo *fi, *hfi;
898                 gchar *path;
899                 CamelURL *url;
900
901                 si = camel_imapx_store_summary_add_from_full (istore->summary, li->name, li->separator);
902                 if (!si)
903                         continue;
904
905                 new_flags = (si->info.flags & (CAMEL_STORE_INFO_FOLDER_SUBSCRIBED | CAMEL_STORE_INFO_FOLDER_CHECK_FOR_NEW)) |
906                                                 (li->flags & ~CAMEL_STORE_INFO_FOLDER_SUBSCRIBED);
907
908                 if (!(istore->server->cinfo->capa & IMAPX_CAPABILITY_NAMESPACE))
909                         istore->dir_sep = li->separator;
910
911                 if (si->info.flags != new_flags) {
912                         si->info.flags = new_flags;
913                         camel_store_summary_touch ((CamelStoreSummary *) istore->summary);
914                 }
915
916                 fi = camel_folder_info_new ();
917                 fi->full_name = g_strdup(camel_store_info_path(istore->summary, si));
918                 if (!g_ascii_strcasecmp(fi->full_name, "inbox")) {
919                         li->flags |= CAMEL_FOLDER_SYSTEM|CAMEL_FOLDER_TYPE_INBOX;
920                         fi->name = g_strdup (_("Inbox"));
921                 } else
922                         fi->name = g_strdup(camel_store_info_name(istore->summary, si));
923
924                 hfi = g_hash_table_lookup (table, fi->full_name);
925                 if (hfi) {
926                         if (subcribed)
927                                 hfi->flags |= CAMEL_STORE_INFO_FOLDER_SUBSCRIBED;
928
929                         camel_folder_info_free (fi);
930                         continue;
931                 }
932
933                 /* HACK: some servers report noinferiors for all folders (uw-imapd)
934                    We just translate this into nochildren, and let the imap layer enforce
935                    it.  See create folder */
936                 if (li->flags & CAMEL_FOLDER_NOINFERIORS)
937                         li->flags = (li->flags & ~CAMEL_FOLDER_NOINFERIORS) | CAMEL_FOLDER_NOCHILDREN;
938                 fi->flags = li->flags;
939
940                 if (subcribed)
941                         fi->flags |= CAMEL_STORE_INFO_FOLDER_SUBSCRIBED;
942
943                 url = camel_url_new (istore->base_url, NULL);
944                 path = alloca(strlen(fi->full_name)+2);
945                 sprintf(path, "/%s", fi->full_name);
946                 camel_url_set_path(url, path);
947
948                 if (li->flags & CAMEL_FOLDER_NOSELECT || fi->name[0] == 0)
949                         camel_url_set_param (url, "noselect", "yes");
950                 fi->uri = camel_url_to_string (url, 0);
951                 camel_url_free (url);
952
953                 fi->total = -1;
954                 fi->unread = -1;
955
956                 g_hash_table_insert (table, fi->full_name, fi);
957         }
958 }
959
960 static void
961 free_list (gpointer data, gpointer user_data)
962 {
963         struct _list_info *li = data;
964         imapx_free_list (li);
965 }
966
967 static void
968 imapx_get_folders_free(gpointer k, gpointer v, gpointer d)
969 {
970         camel_folder_info_free(v);
971 }
972
973 static void
974 fetch_folders_for_pattern (CamelIMAPXStore *istore, const gchar *pattern, guint32 flags, GHashTable *table, CamelException *ex)
975 {
976         GPtrArray *folders = NULL;
977
978         folders = camel_imapx_server_list (istore->server, pattern, flags, ex);
979         if (camel_exception_is_set (ex))
980                 return;
981
982         add_folders_to_summary (istore, folders, table, (flags & CAMEL_STORE_FOLDER_INFO_SUBSCRIBED));
983
984         g_ptr_array_foreach (folders, free_list, folders);
985         g_ptr_array_free (folders, TRUE);
986 }
987
988 static GSList *
989 get_namespaces (CamelIMAPXStore *istore)
990 {
991         GSList *namespaces = NULL;
992         CamelIMAPXNamespaceList *nsl = NULL;
993
994         /* Add code to return the namespaces from preference else all of them */
995         nsl = istore->summary->namespaces;
996         if (nsl->personal)
997                 namespaces = g_slist_append (namespaces, nsl->personal);
998         if (nsl->other)
999                 namespaces = g_slist_append (namespaces, nsl->other);
1000         if (nsl->shared)
1001                 namespaces = g_slist_append (namespaces, nsl->shared);
1002
1003         return namespaces;
1004 }
1005
1006 static GHashTable *
1007 fetch_folders_for_namespaces (CamelIMAPXStore *istore, const gchar *pattern, gboolean sync, CamelException *ex)
1008 {
1009         GHashTable *folders = NULL;
1010         GSList *namespaces = NULL, *l;
1011
1012         folders = g_hash_table_new (folder_hash, folder_eq);
1013         namespaces = get_namespaces (istore);
1014
1015         for (l = namespaces; l != NULL; l = g_slist_next (l))
1016         {
1017                 CamelIMAPXStoreNamespace *ns = l->data;
1018
1019                 while (ns) {
1020                         guint32 flags = 0;
1021                         gchar *pat = NULL;
1022
1023                         if (!pattern) {
1024                                 if (!*ns->path)
1025                                         pat = g_strdup ("");
1026                                 else
1027                                         pat = g_strdup_printf ("%s%c", ns->path, ns->sep);
1028                         } else
1029                                 pat = g_strdup (pattern);
1030
1031                         if (sync)
1032                                 flags |= CAMEL_STORE_FOLDER_INFO_SUBSCRIPTION_LIST;
1033
1034                         flags |= CAMEL_STORE_FOLDER_INFO_RECURSIVE;
1035                         fetch_folders_for_pattern (istore, pat, flags, folders, ex);
1036                         if (camel_exception_is_set (ex)) {
1037                                 g_free (pat);
1038                                 goto exception;
1039                         }
1040
1041                         flags |= CAMEL_STORE_FOLDER_INFO_SUBSCRIBED;
1042                         fetch_folders_for_pattern (istore, pat, flags, folders, ex);
1043                         if (camel_exception_is_set (ex)) {
1044                                 g_free (pat);
1045                                 goto exception;
1046                         }
1047
1048                         g_free (pat);
1049
1050                         if (pattern)
1051                                 return folders;
1052
1053                         ns = ns->next;
1054                 }
1055         }
1056
1057         return folders;
1058
1059 exception:
1060         g_hash_table_destroy (folders);
1061         return NULL;
1062 }
1063
1064 static void
1065 sync_folders (CamelIMAPXStore *istore, const gchar *pattern, gboolean sync, CamelException *ex)
1066 {
1067         GHashTable *folders_from_server;
1068         gint i, total;
1069
1070         folders_from_server = fetch_folders_for_namespaces (istore, pattern, sync, ex);
1071         if (camel_exception_is_set (ex))
1072                 return;
1073
1074         total = camel_store_summary_count ((CamelStoreSummary *) istore->summary);
1075         for (i = 0; i < total; i++) {
1076                 CamelStoreInfo *si;
1077                 const gchar *full_name;
1078                 CamelFolderInfo *fi;
1079
1080                 si = camel_store_summary_index ((CamelStoreSummary *) istore->summary, i);
1081                 if (!si)
1082                         continue;
1083
1084                 full_name = camel_imapx_store_info_full_name (istore->summary, si);
1085                 if (!full_name || !*full_name) {
1086                         camel_store_summary_info_free ((CamelStoreSummary *)istore->summary, si);
1087                         continue;
1088                 }
1089
1090                 if (!pattern || !*pattern || imapx_match_pattern (camel_imapx_store_summary_namespace_find_full (istore->summary, full_name), pattern, full_name)) {
1091                         if ((fi = g_hash_table_lookup(folders_from_server, camel_store_info_path(istore->summary, si))) != NULL) {
1092                                 if (((fi->flags ^ si->flags) & CAMEL_STORE_INFO_FOLDER_SUBSCRIBED)) {
1093                                         si->flags = (si->flags & ~CAMEL_FOLDER_SUBSCRIBED) | (fi->flags & CAMEL_FOLDER_SUBSCRIBED);
1094                                         camel_store_summary_touch((CamelStoreSummary *)istore->summary);
1095
1096                                         camel_object_trigger_event (CAMEL_OBJECT (istore), "folder_created", fi);
1097                                         camel_object_trigger_event (CAMEL_OBJECT (istore), "folder_subscribed", fi);
1098                                 }
1099                         } else {
1100                                 gchar *dup_folder_name = g_strdup (camel_store_info_path (istore->summary, si));
1101
1102                                 if (dup_folder_name) {
1103                                         CamelException eex;
1104
1105                                         camel_exception_init (&eex);
1106                                         imapx_unmark_folder_subscribed (istore,dup_folder_name, TRUE, &eex);
1107                                         imapx_delete_folder_from_cache (istore, dup_folder_name, &eex);
1108                                         
1109                                         g_free (dup_folder_name);
1110                                         camel_exception_clear (&eex);
1111                                 } else {
1112                                         camel_store_summary_remove ((CamelStoreSummary *)istore->summary, si);
1113                                 }
1114
1115                                 total--;
1116                                 i--;
1117                         }
1118                 }
1119                 camel_store_summary_info_free((CamelStoreSummary *)istore->summary, si);
1120         }
1121
1122         g_hash_table_foreach (folders_from_server, imapx_get_folders_free, NULL);
1123         g_hash_table_destroy (folders_from_server);
1124 }
1125
1126 struct _imapx_refresh_msg {
1127         CamelSessionThreadMsg msg;
1128
1129         CamelStore *store;
1130         CamelException ex;
1131 };
1132
1133 static void
1134 imapx_refresh_finfo (CamelSession *session, CamelSessionThreadMsg *msg)
1135 {
1136         struct _imapx_refresh_msg *m = (struct _imapx_refresh_msg *)msg;
1137         CamelIMAPXStore *istore = (CamelIMAPXStore *)m->store;
1138
1139         if (CAMEL_OFFLINE_STORE(istore)->state == CAMEL_OFFLINE_STORE_NETWORK_UNAVAIL)
1140                 return;
1141
1142         if (!camel_service_connect((CamelService *)istore, &m->ex))
1143                 return;
1144
1145         /* look in all namespaces */
1146         sync_folders (istore, "", FALSE, &m->ex);
1147         camel_store_summary_save ((CamelStoreSummary *)istore->summary);
1148 }
1149
1150 static void
1151 imapx_refresh_free(CamelSession *session, CamelSessionThreadMsg *msg)
1152 {
1153         struct _imapx_refresh_msg *m = (struct _imapx_refresh_msg *)msg;
1154
1155         camel_object_unref(m->store);
1156         camel_exception_clear(&m->ex);
1157 }
1158
1159 static CamelSessionThreadOps imapx_refresh_ops = {
1160         imapx_refresh_finfo,
1161         imapx_refresh_free,
1162 };
1163
1164 static void
1165 discover_inbox (CamelStore *store, CamelException *ex)
1166 {
1167         CamelStoreInfo *si;
1168         CamelIMAPXStore *istore = (CamelIMAPXStore *)store;
1169         
1170         si = camel_store_summary_path((CamelStoreSummary *) istore->summary, "INBOX");
1171         if (si == NULL || (si->flags & CAMEL_FOLDER_SUBSCRIBED) == 0) {
1172                 imapx_subscribe_folder (store, "INBOX", FALSE, ex);
1173                 
1174                 if (!camel_exception_is_set(ex) && !si)
1175                         sync_folders (istore, "INBOX", TRUE, ex);
1176         
1177                 if (si)
1178                         camel_store_summary_info_free((CamelStoreSummary *) istore->summary, si);
1179         }
1180 }
1181
1182 static CamelFolderInfo *
1183 imapx_get_folder_info(CamelStore *store, const gchar *top, guint32 flags, CamelException *ex)
1184 {
1185         CamelIMAPXStore *istore = (CamelIMAPXStore *)store;
1186         CamelFolderInfo * fi= NULL;
1187         gboolean initial_setup = FALSE;
1188         gchar *pattern;
1189
1190         if (top == NULL)
1191                 top = "";
1192
1193         g_mutex_lock (istore->get_finfo_lock);
1194
1195         if (CAMEL_OFFLINE_STORE(store)->state == CAMEL_OFFLINE_STORE_NETWORK_UNAVAIL) {
1196                 fi = get_folder_info_offline (store, top, flags, ex);
1197         
1198                 g_mutex_unlock (istore->get_finfo_lock);
1199                 return fi;
1200         }
1201
1202         if (camel_store_summary_count ((CamelStoreSummary *) istore->summary) == 0)
1203                 initial_setup = TRUE;
1204
1205         if (!initial_setup && flags & CAMEL_STORE_FOLDER_INFO_SUBSCRIBED) {
1206                 time_t now = time (NULL);
1207
1208                 if (now - istore->last_refresh_time > FINFO_REFRESH_INTERVAL) {
1209                         struct _imapx_refresh_msg *m;
1210                         
1211                         istore->last_refresh_time = time (NULL);
1212                         m = camel_session_thread_msg_new(((CamelService *)store)->session, &imapx_refresh_ops, sizeof(*m));
1213                         m->store = store;
1214                         camel_object_ref(store);
1215                         camel_exception_init(&m->ex);
1216                         camel_session_thread_queue(((CamelService *)store)->session, &m->msg, 0);
1217                 }
1218                 
1219                 fi = get_folder_info_offline (store, top, flags, ex);
1220                 g_mutex_unlock (istore->get_finfo_lock);
1221                 return fi;
1222         }
1223         
1224         if (!camel_service_connect((CamelService *)store, ex)) {
1225                 g_mutex_unlock (istore->get_finfo_lock);
1226                 return NULL;
1227         }
1228
1229         if (*top && flags & CAMEL_STORE_FOLDER_INFO_SUBSCRIPTION_LIST) {
1230                 fi = get_folder_info_offline (store, top, flags, ex);
1231                 g_mutex_unlock (istore->get_finfo_lock);
1232                 return fi;
1233         }
1234
1235         if (*top) {
1236                 gchar *name;
1237                 gint i;
1238
1239                 name = camel_imapx_store_summary_full_from_path(istore->summary, top);
1240                 if (name == NULL)
1241                         name = camel_imapx_store_summary_path_to_full(istore->summary, top, istore->dir_sep);
1242
1243                 i = strlen(name);
1244                 pattern = g_alloca(i+5);
1245                 strcpy(pattern, name);
1246                 g_free(name);
1247         } else {
1248                 pattern = g_alloca (1);
1249                 pattern[0] = '\0';
1250         }
1251
1252         sync_folders (istore, pattern, TRUE, ex);
1253         if (camel_exception_is_set (ex)) {
1254                 g_mutex_unlock (istore->get_finfo_lock);
1255                 return NULL;
1256         }
1257
1258         camel_store_summary_save((CamelStoreSummary *) istore->summary);
1259
1260         /* ensure the INBOX is subscribed if lsub was preferred*/
1261         if (initial_setup && istore->rec_options & IMAPX_SUBSCRIPTIONS)
1262                 discover_inbox (store, ex);
1263
1264         fi = get_folder_info_offline (store, top, flags, ex);
1265         g_mutex_unlock (istore->get_finfo_lock);
1266         return fi;
1267 }
1268
1269 static gboolean
1270 imapx_can_refresh_folder (CamelStore *store, CamelFolderInfo *info, CamelException *ex)
1271 {
1272         gboolean res;
1273
1274         res = CAMEL_STORE_CLASS(parent_class)->can_refresh_folder (store, info, ex) ||
1275               (camel_url_get_param (((CamelService *)store)->url, "check_all") != NULL) ||
1276               (camel_url_get_param (((CamelService *)store)->url, "check_lsub") != NULL && (info->flags & CAMEL_FOLDER_SUBSCRIBED) != 0);
1277
1278         if (!res && !camel_exception_is_set (ex) && CAMEL_IS_IMAP_STORE (store)) {
1279                 CamelStoreInfo *si;
1280                 CamelStoreSummary *sm = CAMEL_STORE_SUMMARY (((CamelIMAPXStore *)(store))->summary);
1281
1282                 if (!sm)
1283                         return FALSE;
1284
1285                 si = camel_store_summary_path (sm, info->full_name);
1286                 if (si) {
1287                         res = (si->flags & CAMEL_STORE_INFO_FOLDER_CHECK_FOR_NEW) != 0 ? TRUE : FALSE;
1288
1289                         camel_store_summary_info_free (sm, si);
1290                 }
1291         }
1292
1293         return res;
1294 }
1295
1296 static gboolean
1297 imapx_folder_subscribed (CamelStore *store, const gchar *folder_name)
1298 {
1299         CamelIMAPXStore *istore = CAMEL_IMAPX_STORE (store);
1300         CamelStoreInfo *si;
1301         gint is_subscribed = FALSE;
1302
1303         si = camel_store_summary_path((CamelStoreSummary *)istore->summary, folder_name);
1304         if (si) {
1305                 is_subscribed = (si->flags & CAMEL_STORE_INFO_FOLDER_SUBSCRIBED) != 0;
1306                 camel_store_summary_info_free((CamelStoreSummary *)istore->summary, si);
1307         }
1308
1309         return is_subscribed;
1310 }
1311
1312 static void
1313 camel_imapx_store_class_init(CamelIMAPXStoreClass *klass)
1314 {
1315         CamelServiceClass *camel_service_class = CAMEL_SERVICE_CLASS(klass);
1316         CamelStoreClass *camel_store_class = CAMEL_STORE_CLASS(klass);
1317
1318         parent_class = CAMEL_OFFLINE_STORE_CLASS (camel_type_get_global_classfuncs (camel_offline_store_get_type ()));
1319
1320         camel_service_class->construct = imapx_construct;
1321         camel_service_class->query_auth_types = imapx_query_auth_types;
1322         camel_service_class->get_name = imapx_get_name;
1323         camel_service_class->connect = imapx_connect;
1324         camel_service_class->disconnect = imapx_disconnect;
1325
1326         camel_store_class->get_trash = imapx_get_trash;
1327         camel_store_class->get_junk = imapx_get_junk;
1328         camel_store_class->noop = imapx_noop;
1329         camel_store_class->get_folder = imapx_get_folder;
1330         camel_store_class->get_inbox = imapx_get_inbox;
1331         camel_store_class->hash_folder_name = imapx_hash_folder_name;
1332         camel_store_class->compare_folder_name = imapx_compare_folder_name;
1333
1334         camel_store_class->can_refresh_folder = imapx_can_refresh_folder;
1335         camel_store_class->create_folder = imapx_create_folder;
1336         camel_store_class->rename_folder = imapx_rename_folder;
1337         camel_store_class->delete_folder = imapx_delete_folder;
1338         camel_store_class->subscribe_folder = imapx_store_subscribe_folder;
1339         camel_store_class->unsubscribe_folder = imapx_store_unsubscribe_folder;
1340         camel_store_class->get_folder_info = imapx_get_folder_info;
1341         camel_store_class->folder_subscribed = imapx_folder_subscribed;
1342         camel_store_class->free_folder_info = camel_store_free_folder_info_full;
1343
1344         ((CamelStoreClass *)klass)->hash_folder_name = imapx_name_hash;
1345         ((CamelStoreClass *)klass)->compare_folder_name = imapx_name_equal;
1346 }
1347
1348 static void
1349 camel_imapx_store_init (gpointer object, gpointer klass)
1350 {
1351         CamelStore *store = (CamelStore *) object;
1352         CamelIMAPXStore *istore = CAMEL_IMAPX_STORE (object);
1353
1354         store->flags |= CAMEL_STORE_ASYNC | CAMEL_STORE_SUBSCRIPTIONS;
1355         istore->get_finfo_lock = g_mutex_new ();
1356         istore->last_refresh_time = time (NULL) - (FINFO_REFRESH_INTERVAL + 10);
1357         istore->dir_sep = '/';
1358 }
1359
1360 static void
1361 imapx_store_finalize(CamelObject *object)
1362 {
1363         CamelIMAPXStore *imapx_store = CAMEL_IMAPX_STORE (object);
1364
1365         /* force disconnect so we dont have it run later, after we've cleaned up some stuff */
1366         /* SIGH */
1367
1368         camel_service_disconnect((CamelService *)imapx_store, TRUE, NULL);
1369         g_mutex_free (imapx_store->get_finfo_lock);
1370
1371         if (imapx_store->base_url)
1372                 g_free (imapx_store->base_url);
1373 }
1374
1375 CamelType
1376 camel_imapx_store_get_type (void)
1377 {
1378         static CamelType camel_imapx_store_type = CAMEL_INVALID_TYPE;
1379
1380         if (!camel_imapx_store_type) {
1381                 camel_imapx_store_type = camel_type_register(camel_offline_store_get_type (),
1382                                                             "CamelIMAPXStore",
1383                                                             sizeof (CamelIMAPXStore),
1384                                                             sizeof (CamelIMAPXStoreClass),
1385                                                             (CamelObjectClassInitFunc) camel_imapx_store_class_init,
1386                                                             NULL,
1387                                                             (CamelObjectInitFunc) camel_imapx_store_init,
1388                                                              imapx_store_finalize);
1389         }
1390
1391         return camel_imapx_store_type;
1392 }