794939f195316f9468134d1b810fcfe9e0412045
[platform/upstream/evolution-data-server.git] / camel / providers / imap / camel-imap-store.c
1 /* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
2 /* camel-imap-store.c : class for an imap store */
3
4 /*
5  *  Authors:
6  *    Dan Winship <danw@ximian.com>
7  *    Jeffrey Stedfast <fejj@ximian.com>
8  *
9  *  Copyright (C) 1999-2008 Novell, Inc. (www.novell.com)
10  *
11  * This program is free software; you can redistribute it and/or
12  * modify it under the terms of version 2 of the GNU Lesser General Public
13  * License as published by the Free Software Foundation.
14  *
15  * This program is distributed in the hope that it will be useful,
16  * but WITHOUT ANY WARRANTY; without even the implied warranty of
17  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
18  * General Public License for more details.
19  *
20  * You should have received a copy of the GNU Lesser General Public
21  * License along with this program; if not, write to the
22  * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
23  * Boston, MA 02110-1301, USA.
24  *
25  */
26
27 #include <config.h>
28
29 #include <errno.h>
30 #include <stdio.h>
31 #include <stdlib.h>
32 #include <string.h>
33 #include <unistd.h>
34
35 #include <glib/gi18n-lib.h>
36 #include <glib/gstdio.h>
37
38 #include "camel-imap-command.h"
39 #include "camel-imap-folder.h"
40 #include "camel-imap-message-cache.h"
41 #include "camel-imap-settings.h"
42 #include "camel-imap-store-summary.h"
43 #include "camel-imap-store.h"
44 #include "camel-imap-summary.h"
45 #include "camel-imap-utils.h"
46
47 #ifdef G_OS_WIN32
48 #include <winsock2.h>
49 #include <ws2tcpip.h>
50 #endif
51
52 #define d(x)
53
54 /* Specified in RFC 2060 */
55 #define IMAP_PORT  143
56 #define IMAPS_PORT 993
57
58 #ifdef G_OS_WIN32
59 /* The strtok() in Microsoft's C library is MT-safe (but still uses
60  * only one buffer pointer per thread, but for the use of strtok_r()
61  * here that's enough).
62  */
63 #define strtok_r(s,sep,lasts) (*(lasts)=strtok((s),(sep)))
64 #endif
65
66 extern gint camel_verbose_debug;
67
68 static gchar imap_tag_prefix = 'A';
69
70 static gboolean imap_store_noop_sync (CamelStore *store, GCancellable *cancellable, GError **error);
71 static CamelFolder *imap_store_get_junk_folder_sync (CamelStore *store, GCancellable *cancellable, GError **error);
72 static CamelFolder *imap_store_get_trash_folder_sync (CamelStore *store, GCancellable *cancellable, GError **error);
73 static guint hash_folder_name (gconstpointer key);
74 static gint compare_folder_name (gconstpointer a, gconstpointer b);
75
76 static CamelFolderInfo *imap_store_create_folder_sync (CamelStore *store, const gchar *parent_name, const gchar *folder_name, GCancellable *cancellable, GError **error);
77 static gboolean imap_store_delete_folder_sync (CamelStore *store, const gchar *folder_name, GCancellable *cancellable, GError **error);
78 static gboolean imap_store_rename_folder_sync (CamelStore *store, const gchar *old_name, const gchar *new_name, GCancellable *cancellable, GError **error);
79 static gboolean imap_store_folder_is_subscribed (CamelSubscribable *subscribable, const gchar *folder_name);
80 static gboolean imap_store_subscribe_folder_sync (CamelSubscribable *subscribable, const gchar *folder_name, GCancellable *cancellable, GError **error);
81 static gboolean imap_store_unsubscribe_folder_sync (CamelSubscribable *subscribable, const gchar *folder_name, GCancellable *cancellable, GError **error);
82
83 static gboolean get_folders_sync (CamelImapStore *imap_store, const gchar *pattern, GCancellable *cancellable, GError **error);
84
85 static gboolean imap_folder_effectively_unsubscribed (CamelImapStore *imap_store, const gchar *folder_name, GError **error);
86 static gboolean imap_check_folder_still_extant (CamelImapStore *imap_store, const gchar *full_name,  GError **error);
87 static void imap_forget_folder (CamelImapStore *imap_store, const gchar *folder_name, GError **error);
88 static void imap_set_server_level (CamelImapStore *store);
89
90 static gboolean imap_can_refresh_folder (CamelStore *store, CamelFolderInfo *info, GError **error);
91 static CamelFolder * imap_store_get_folder_sync (CamelStore *store, const gchar *folder_name, CamelStoreGetFolderFlags flags, GCancellable *cancellable, GError **error);
92 static CamelFolderInfo * imap_store_get_folder_info_sync (CamelStore *store, const gchar *top, CamelStoreGetFolderInfoFlags flags, GCancellable *cancellable, GError **error);
93 static CamelFolder * get_folder_offline (CamelStore *store, const gchar *folder_name, guint32 flags, GError **error);
94 static CamelFolderInfo * get_folder_info_offline (CamelStore *store, const gchar *top, guint32 flags, GError **error);
95
96 static struct {
97         const gchar *name;
98         guint32 flag;
99 } capabilities[] = {
100         { "IMAP4",              IMAP_CAPABILITY_IMAP4 },
101         { "IMAP4REV1",          IMAP_CAPABILITY_IMAP4REV1 },
102         { "STATUS",             IMAP_CAPABILITY_STATUS },
103         { "NAMESPACE",          IMAP_CAPABILITY_NAMESPACE },
104         { "UIDPLUS",            IMAP_CAPABILITY_UIDPLUS },
105         { "LITERAL+",           IMAP_CAPABILITY_LITERALPLUS },
106         { "STARTTLS",           IMAP_CAPABILITY_STARTTLS },
107         { "XGWEXTENSIONS",      IMAP_CAPABILITY_XGWEXTENSIONS },
108         { "XGWMOVE",            IMAP_CAPABILITY_XGWMOVE },
109         { "LOGINDISABLED",      IMAP_CAPABILITY_LOGINDISABLED },
110         { "QUOTA",              IMAP_CAPABILITY_QUOTA },
111         { NULL, 0 }
112 };
113
114 extern CamelServiceAuthType camel_imap_password_authtype;
115
116 static GInitableIface *parent_initable_interface;
117
118 /* Forward Declarations */
119 static void camel_imap_store_initable_init (GInitableIface *interface);
120 static void camel_network_service_init (CamelNetworkServiceInterface *interface);
121 static void camel_subscribable_init (CamelSubscribableInterface *interface);
122
123 G_DEFINE_TYPE_WITH_CODE (
124         CamelImapStore,
125         camel_imap_store,
126         CAMEL_TYPE_OFFLINE_STORE,
127         G_IMPLEMENT_INTERFACE (
128                 G_TYPE_INITABLE,
129                 camel_imap_store_initable_init)
130         G_IMPLEMENT_INTERFACE (
131                 CAMEL_TYPE_NETWORK_SERVICE,
132                 camel_network_service_init)
133         G_IMPLEMENT_INTERFACE (
134                 CAMEL_TYPE_SUBSCRIBABLE,
135                 camel_subscribable_init))
136
137 static void
138 imap_store_update_store_flags (CamelStore *store)
139 {
140         CamelService *service;
141         CamelSettings *settings;
142         CamelImapSettings *imap_settings;
143         gboolean use_real_path;
144         gchar *real_path;
145
146         /* XXX This only responds to the service's entire settings object
147          *     being replaced, not when individual settings change.  When
148          *     individual settings change, a restart is required for them
149          *     to take effect. */
150
151         service = CAMEL_SERVICE (store);
152         settings = camel_service_get_settings (service);
153         imap_settings = CAMEL_IMAP_SETTINGS (settings);
154
155         real_path = camel_imap_settings_dup_real_junk_path (imap_settings);
156         use_real_path = camel_imap_settings_get_use_real_junk_path (imap_settings);
157
158         if (use_real_path && real_path != NULL) {
159                 store->flags &= ~CAMEL_STORE_VJUNK;
160                 store->flags |= CAMEL_STORE_REAL_JUNK_FOLDER;
161         } else {
162                 store->flags &= ~CAMEL_STORE_REAL_JUNK_FOLDER;
163                 store->flags |= CAMEL_STORE_VJUNK;
164         }
165
166         g_free (real_path);
167
168         real_path = camel_imap_settings_dup_real_trash_path (imap_settings);
169         use_real_path = camel_imap_settings_get_use_real_trash_path (imap_settings);
170
171         if (use_real_path && real_path != NULL)
172                 store->flags &= ~CAMEL_STORE_VTRASH;
173         else
174                 store->flags |= CAMEL_STORE_VTRASH;
175
176         g_free (real_path);
177 }
178
179 static void
180 parse_capability (CamelImapStore *store,
181                   gchar *capa)
182 {
183         gchar *lasts = NULL;
184         gint i;
185
186         for (capa = strtok_r (capa, " ", &lasts); capa; capa = strtok_r (NULL, " ", &lasts)) {
187                 if (!strncmp (capa, "AUTH=", 5)) {
188                         g_hash_table_insert (store->authtypes,
189                                              g_strdup (capa + 5),
190                                              GINT_TO_POINTER (1));
191                         continue;
192                 }
193                 for (i = 0; capabilities[i].name; i++) {
194                         if (g_ascii_strcasecmp (capa, capabilities[i].name) == 0) {
195                                 store->capabilities |= capabilities[i].flag;
196                                 break;
197                         }
198                 }
199         }
200 }
201
202 static gboolean free_key (gpointer key, gpointer value, gpointer user_data);
203
204 static gboolean
205 imap_get_capability (CamelService *service,
206                      GCancellable *cancellable,
207                      GError **error)
208 {
209         CamelImapStore *store = CAMEL_IMAP_STORE (service);
210         CamelImapResponse *response;
211         gchar *result;
212
213         /* Find out the IMAP capabilities */
214         /* We assume we have utf8 capable search until a failed search tells us otherwise */
215         store->capabilities = IMAP_CAPABILITY_utf8_search;
216         if (store->authtypes) {
217                 g_hash_table_foreach_remove (store->authtypes, free_key, NULL);
218                 g_hash_table_destroy (store->authtypes);
219         }
220         store->authtypes = g_hash_table_new (g_str_hash, g_str_equal);
221         response = camel_imap_command (store, NULL, cancellable, error, "CAPABILITY");
222         if (!response)
223                 return FALSE;
224         result = camel_imap_response_extract (store, response, "CAPABILITY ", error);
225         if (!result)
226                 return FALSE;
227
228         /* Skip over "* CAPABILITY ". */
229         parse_capability (store, result + 13);
230         g_free (result);
231
232         /* dunno why the groupwise guys didn't just list this in capabilities */
233         if (store->capabilities & IMAP_CAPABILITY_XGWEXTENSIONS) {
234                 /* not critical if this fails */
235                 response = camel_imap_command (store, NULL, cancellable, NULL, "XGWEXTENSIONS");
236                 if (response && (result = camel_imap_response_extract (store, response, "XGWEXTENSIONS ", NULL))) {
237                         parse_capability (store, result + 16);
238                         g_free (result);
239                 }
240         }
241
242         imap_set_server_level (store);
243
244         if (store->summary->capabilities != store->capabilities) {
245                 store->summary->capabilities = store->capabilities;
246                 camel_store_summary_touch ((CamelStoreSummary *) store->summary);
247                 camel_store_summary_save ((CamelStoreSummary *) store->summary);
248         }
249
250         return TRUE;
251 }
252
253 /* folder_name is path name */
254 static CamelFolderInfo *
255 imap_build_folder_info (CamelImapStore *imap_store,
256                         const gchar *folder_name)
257 {
258         const gchar *name;
259         CamelFolderInfo *fi;
260
261         fi = camel_folder_info_new ();
262         fi->full_name = g_strdup (folder_name);
263         fi->unread = -1;
264         fi->total = -1;
265
266         name = strrchr (fi->full_name, '/');
267         if (name == NULL)
268                 name = fi->full_name;
269         else
270                 name++;
271         if (!g_ascii_strcasecmp (fi->full_name, "INBOX"))
272                 fi->display_name = g_strdup (_("Inbox"));
273         else
274                 fi->display_name = g_strdup (name);
275
276         return fi;
277 }
278
279 static gboolean
280 connect_to_server (CamelService *service,
281                    GCancellable *cancellable,
282                    GError **error)
283 {
284         CamelImapStore *store = (CamelImapStore *) service;
285         CamelNetworkSettings *network_settings;
286         CamelSettings *settings;
287         CamelImapResponse *response;
288         CamelStream *tcp_stream;
289         CamelSockOptData sockopt;
290         CamelNetworkSecurityMethod method;
291         gboolean force_imap4 = FALSE;
292         gboolean clean_quit = TRUE;
293         gboolean success = TRUE;
294         gchar *host;
295         gchar *buf;
296
297         tcp_stream = camel_network_service_connect_sync (
298                 CAMEL_NETWORK_SERVICE (service), cancellable, error);
299
300         if (tcp_stream == NULL)
301                 return FALSE;
302
303         settings = camel_service_get_settings (service);
304
305         network_settings = CAMEL_NETWORK_SETTINGS (settings);
306         host = camel_network_settings_dup_host (network_settings);
307         method = camel_network_settings_get_security_method (network_settings);
308
309         store->ostream = tcp_stream;
310         store->istream = camel_stream_buffer_new (tcp_stream, CAMEL_STREAM_BUFFER_READ);
311
312         store->connected = TRUE;
313         store->preauthed = FALSE;
314         store->command = 0;
315
316         /* Disable Nagle - we send a lot of small requests which nagle slows down */
317         sockopt.option = CAMEL_SOCKOPT_NODELAY;
318         sockopt.value.no_delay = TRUE;
319         camel_tcp_stream_setsockopt ((CamelTcpStream *) tcp_stream, &sockopt);
320
321         /* Set keepalive - needed for some hosts/router configurations, we're idle a lot */
322         sockopt.option = CAMEL_SOCKOPT_KEEPALIVE;
323         sockopt.value.keep_alive = TRUE;
324         camel_tcp_stream_setsockopt ((CamelTcpStream *) tcp_stream, &sockopt);
325
326         /* Read the greeting, if any, and deal with PREAUTH */
327         if (camel_imap_store_readline (store, &buf, cancellable, error) < 0) {
328                 if (store->istream) {
329                         g_object_unref (store->istream);
330                         store->istream = NULL;
331                 }
332
333                 if (store->ostream) {
334                         g_object_unref (store->ostream);
335                         store->ostream = NULL;
336                 }
337
338                 store->connected = FALSE;
339                 success = FALSE;
340
341                 goto exit;
342         }
343
344         if (!strncmp(buf, "* PREAUTH", 9))
345                 store->preauthed = TRUE;
346
347         if (strstr (buf, "Courier-IMAP") || getenv("CAMEL_IMAP_BRAINDAMAGED")) {
348                 /* Courier-IMAP is braindamaged. So far this flag only
349                  * works around the fact that Courier-IMAP is known to
350                  * give invalid BODY responses seemingly because its
351                  * MIME parser sucks. In any event, we can't rely on
352                  * them so we always have to request the full messages
353                  * rather than getting individual parts. */
354                 store->braindamaged = TRUE;
355         } else if (strstr (buf, "WEB.DE") || strstr (buf, "Mail2World")) {
356                 /* This is a workaround for servers which advertise
357                  * IMAP4rev1 but which can sometimes subtly break in
358                  * various ways if we try to use IMAP4rev1 queries.
359                  *
360                  * WEB.DE: when querying for HEADER.FIELDS.NOT, it
361                  * returns an empty literal for the headers. Many
362                  * complaints about empty message-list fields on the
363                  * mailing lists and probably a few bugzilla bugs as
364                  * well.
365                  *
366                  * Mail2World (aka NamePlanet): When requesting
367                  * message info's, it ignores the fact that we
368                  * requested BODY.PEEK[HEADER.FIELDS.NOT (RECEIVED)]
369                  * and so the responses are incomplete. See bug #58766
370                  * for details.
371                  **/
372                 force_imap4 = TRUE;
373         }
374
375         g_free (buf);
376
377         /* get the imap server capabilities */
378         if (!imap_get_capability (service, cancellable, error)) {
379                 if (store->istream) {
380                         g_object_unref (store->istream);
381                         store->istream = NULL;
382                 }
383
384                 if (store->ostream) {
385                         g_object_unref (store->ostream);
386                         store->ostream = NULL;
387                 }
388
389                 store->connected = FALSE;
390                 success = FALSE;
391
392                 goto exit;
393         }
394
395         if (force_imap4) {
396                 store->capabilities &= ~IMAP_CAPABILITY_IMAP4REV1;
397                 store->server_level = IMAP_LEVEL_IMAP4;
398         }
399
400         if (method != CAMEL_NETWORK_SECURITY_METHOD_STARTTLS_ON_STANDARD_PORT)
401                 goto exit;  /* we're done */
402
403         /* as soon as we send a STARTTLS command, all hope is lost of a clean QUIT if problems arise */
404         clean_quit = FALSE;
405
406         if (!(store->capabilities & IMAP_CAPABILITY_STARTTLS)) {
407                 g_set_error (
408                         error, CAMEL_ERROR, CAMEL_ERROR_GENERIC,
409                         _("Failed to connect to IMAP server %s in secure mode: %s"),
410                         host, _("STARTTLS not supported"));
411
412                 goto exception;
413         }
414
415         response = camel_imap_command (store, NULL, cancellable, error, "STARTTLS");
416         if (!response) {
417                 g_object_unref (store->istream);
418                 store->istream = NULL;
419
420                 g_object_unref (store->ostream);
421                 store->ostream = NULL;
422
423                 success = FALSE;
424
425                 goto exit;
426         }
427
428         camel_imap_response_free_without_processing (store, response);
429
430         /* Okay, now toggle SSL/TLS mode */
431         if (camel_tcp_stream_ssl_enable_ssl (
432                 CAMEL_TCP_STREAM_SSL (tcp_stream), cancellable, error) == -1) {
433                 g_prefix_error (
434                         error,
435                         _("Failed to connect to IMAP server %s in secure mode: "),
436                         host);
437                 goto exception;
438         }
439
440         /* rfc2595, section 4 states that after a successful STLS
441          * command, the client MUST discard prior CAPA responses */
442         if (!imap_get_capability (service, cancellable, error)) {
443                 if (store->istream) {
444                         g_object_unref (store->istream);
445                         store->istream = NULL;
446                 }
447
448                 if (store->ostream) {
449                         g_object_unref (store->ostream);
450                         store->ostream = NULL;
451                 }
452
453                 store->connected = FALSE;
454                 success = FALSE;
455
456                 goto exit;
457         }
458
459         if (store->capabilities & IMAP_CAPABILITY_LOGINDISABLED ) {
460                 clean_quit = TRUE;
461                 g_set_error (
462                         error, CAMEL_ERROR, CAMEL_ERROR_GENERIC,
463                         _("Failed to connect to IMAP server %s in secure mode: %s"),
464                         host, _("Unknown error"));
465                 goto exception;
466         }
467
468         goto exit;
469
470 exception:
471
472         if (clean_quit && store->connected) {
473                 /* try to disconnect cleanly */
474                 response = camel_imap_command (store, NULL, cancellable, error, "LOGOUT");
475                 if (response)
476                         camel_imap_response_free_without_processing (store, response);
477         }
478
479         if (store->istream) {
480                 g_object_unref (store->istream);
481                 store->istream = NULL;
482         }
483
484         if (store->ostream) {
485                 g_object_unref (store->ostream);
486                 store->ostream = NULL;
487         }
488
489         store->connected = FALSE;
490
491         success = FALSE;
492
493 exit:
494         g_free (host);
495
496         return success;
497 }
498
499 #ifndef G_OS_WIN32
500
501 /* Using custom commands to connect to IMAP servers is not supported on Win32 */
502
503 static gboolean
504 connect_to_server_process (CamelService *service,
505                            const gchar *cmd,
506                            GCancellable *cancellable,
507                            GError **error)
508 {
509         CamelImapStore *store = (CamelImapStore *) service;
510         CamelNetworkSettings *network_settings;
511         CamelProvider *provider;
512         CamelSettings *settings;
513         CamelStream *cmd_stream;
514         CamelURL url;
515         gint ret, i = 0;
516         gchar *buf;
517         gchar *cmd_copy;
518         gchar *full_cmd;
519         gchar *child_env[7];
520         const gchar *password;
521         gchar *host;
522         gchar *user;
523         guint16 port;
524
525         memset (&url, 0, sizeof (CamelURL));
526
527         password = camel_service_get_password (service);
528         provider = camel_service_get_provider (service);
529         settings = camel_service_get_settings (service);
530
531         network_settings = CAMEL_NETWORK_SETTINGS (settings);
532         host = camel_network_settings_dup_host (network_settings);
533         port = camel_network_settings_get_port (network_settings);
534         user = camel_network_settings_dup_user (network_settings);
535
536         camel_url_set_protocol (&url, provider->protocol);
537         camel_url_set_host (&url, host);
538         camel_url_set_port (&url, port);
539         camel_url_set_user (&url, user);
540
541         /* Put full details in the environment, in case the connection
542          * program needs them */
543         buf = camel_url_to_string (&url, 0);
544         child_env[i++] = g_strdup_printf("URL=%s", buf);
545         g_free (buf);
546
547         child_env[i++] = g_strdup_printf("URLHOST=%s", host);
548         if (port)
549                 child_env[i++] = g_strdup_printf("URLPORT=%d", port);
550         if (user)
551                 child_env[i++] = g_strdup_printf("URLUSER=%s", user);
552         if (password)
553                 child_env[i++] = g_strdup_printf("URLPASSWD=%s", password);
554         child_env[i] = NULL;
555
556         /* Now do %h, %u, etc. substitution in cmd */
557         buf = cmd_copy = g_strdup (cmd);
558
559         full_cmd = g_strdup("");
560
561         for (;;) {
562                 gchar *pc;
563                 gchar *tmp;
564                 const gchar *var;
565                 gint len;
566
567                 pc = strchr (buf, '%');
568         ignore:
569                 if (!pc) {
570                         tmp = g_strdup_printf("%s%s", full_cmd, buf);
571                         g_free (full_cmd);
572                         full_cmd = tmp;
573                         break;
574                 }
575
576                 len = pc - buf;
577
578                 var = NULL;
579
580                 switch (pc[1]) {
581                 case 'h':
582                         var = host;
583                         break;
584                 case 'u':
585                         var = user;
586                         break;
587                 }
588                 if (!var) {
589                         /* If there wasn't a valid %-code, with an actual
590                          * variable to insert, pretend we didn't see the % */
591                         pc = strchr (pc + 1, '%');
592                         goto ignore;
593                 }
594                 tmp = g_strdup_printf("%s%.*s%s", full_cmd, len, buf, var);
595                 g_free (full_cmd);
596                 full_cmd = tmp;
597                 buf = pc + 2;
598         }
599
600         g_free (cmd_copy);
601
602         g_free (host);
603         g_free (user);
604
605         cmd_stream = camel_stream_process_new ();
606
607         ret = camel_stream_process_connect (
608                 CAMEL_STREAM_PROCESS (cmd_stream),
609                 full_cmd, (const gchar **) child_env, error);
610
611         while (i)
612                 g_free (child_env[--i]);
613
614         if (ret == -1) {
615                 g_object_unref (cmd_stream);
616                 g_free (full_cmd);
617                 return FALSE;
618         }
619
620         g_free (full_cmd);
621
622         store->ostream = cmd_stream;
623         store->istream = camel_stream_buffer_new (cmd_stream, CAMEL_STREAM_BUFFER_READ);
624
625         store->connected = TRUE;
626         store->preauthed = FALSE;
627         store->command = 0;
628
629         /* Read the greeting, if any, and deal with PREAUTH */
630         if (camel_imap_store_readline (store, &buf, cancellable, error) < 0) {
631                 if (store->istream) {
632                         g_object_unref (store->istream);
633                         store->istream = NULL;
634                 }
635
636                 if (store->ostream) {
637                         g_object_unref (store->ostream);
638                         store->ostream = NULL;
639                 }
640
641                 store->connected = FALSE;
642                 return FALSE;
643         }
644
645         if (!strncmp(buf, "* PREAUTH", 9))
646                 store->preauthed = TRUE;
647         g_free (buf);
648
649         /* get the imap server capabilities */
650         if (!imap_get_capability (service, cancellable, error)) {
651                 if (store->istream) {
652                         g_object_unref (store->istream);
653                         store->istream = NULL;
654                 }
655
656                 if (store->ostream) {
657                         g_object_unref (store->ostream);
658                         store->ostream = NULL;
659                 }
660
661                 store->connected = FALSE;
662                 return FALSE;
663         }
664
665         return TRUE;
666
667 }
668
669 #endif
670
671 static gboolean
672 connect_to_server_wrapper (CamelService *service,
673                            GCancellable *cancellable,
674                            GError **error)
675 {
676         CamelSettings *settings;
677         gchar *shell_command;
678         gboolean use_shell_command;
679         gboolean success;
680
681         settings = camel_service_get_settings (service);
682         shell_command = camel_imap_settings_dup_shell_command (
683                 CAMEL_IMAP_SETTINGS (settings));
684         use_shell_command = camel_imap_settings_get_use_shell_command (
685                 CAMEL_IMAP_SETTINGS (settings));
686
687 #ifndef G_OS_WIN32
688         if (use_shell_command && shell_command != NULL)
689                 success = connect_to_server_process (
690                         service, shell_command, cancellable, error);
691         else
692                 success = connect_to_server (service, cancellable, error);
693 #else
694         success = connect_to_server (service, cancellable, error);
695 #endif
696
697         g_free (shell_command);
698
699         return success;
700 }
701
702 static gboolean
703 imap_auth_loop (CamelService *service,
704                 GCancellable *cancellable,
705                 GError **error)
706 {
707         CamelImapStore *store = CAMEL_IMAP_STORE (service);
708         CamelNetworkSettings *network_settings;
709         CamelSettings *settings;
710         CamelSession *session;
711         gchar *mechanism;
712         gchar *host;
713         gboolean success = TRUE;
714
715         session = camel_service_get_session (service);
716         settings = camel_service_get_settings (service);
717
718         network_settings = CAMEL_NETWORK_SETTINGS (settings);
719         host = camel_network_settings_dup_host (network_settings);
720         mechanism = camel_network_settings_dup_auth_mechanism (network_settings);
721
722         if (store->preauthed) {
723                 if (camel_verbose_debug)
724                         fprintf(stderr, "Server %s has preauthenticated us.\n",
725                                 host);
726                 goto exit;
727         }
728
729         if (mechanism != NULL) {
730                 if (!g_hash_table_lookup (store->authtypes, mechanism)) {
731                         g_set_error (
732                                 error, CAMEL_SERVICE_ERROR,
733                                 CAMEL_SERVICE_ERROR_CANT_AUTHENTICATE,
734                                 _("IMAP server %s does not support %s "
735                                   "authentication"), host, mechanism);
736                         success = FALSE;
737                         goto exit;
738                 }
739         }
740
741         success = camel_session_authenticate_sync (
742                 session, service, mechanism, cancellable, error);
743
744 exit:
745         g_free (host);
746         g_free (mechanism);
747
748         return success;
749 }
750
751 static gboolean
752 free_key (gpointer key,
753           gpointer value,
754           gpointer user_data)
755 {
756         g_free (key);
757         return TRUE;
758 }
759
760 static void
761 imap_store_dispose (GObject *object)
762 {
763         CamelImapStore *imap_store = CAMEL_IMAP_STORE (object);
764
765         if (imap_store->summary != NULL) {
766                 camel_store_summary_save (
767                         CAMEL_STORE_SUMMARY (imap_store->summary));
768                 g_object_unref (imap_store->summary);
769                 imap_store->summary = NULL;
770         }
771
772         /* Chain up to parent's dispose() method. */
773         G_OBJECT_CLASS (camel_imap_store_parent_class)->dispose (object);
774 }
775
776 static void
777 imap_store_finalize (GObject *object)
778 {
779         CamelImapStore *imap_store = CAMEL_IMAP_STORE (object);
780
781         /* This frees current_folder, folders, authtypes, streams, and namespace. */
782         camel_service_disconnect_sync (
783                 CAMEL_SERVICE (imap_store), TRUE, NULL, NULL);
784
785         g_static_rec_mutex_free (&imap_store->command_and_response_lock);
786         g_hash_table_destroy (imap_store->known_alerts);
787
788         /* Chain up to parent's finalize() method. */
789         G_OBJECT_CLASS (camel_imap_store_parent_class)->finalize (object);
790 }
791
792 static gchar *
793 imap_store_get_name (CamelService *service,
794                      gboolean brief)
795 {
796         CamelNetworkSettings *network_settings;
797         CamelSettings *settings;
798         gchar *host;
799         gchar *user;
800         gchar *name;
801
802         settings = camel_service_get_settings (service);
803
804         network_settings = CAMEL_NETWORK_SETTINGS (settings);
805         host = camel_network_settings_dup_host (network_settings);
806         user = camel_network_settings_dup_user (network_settings);
807
808         if (brief)
809                 name = g_strdup_printf (
810                         _("IMAP server %s"), host);
811         else
812                 name = g_strdup_printf (
813                         _("IMAP service for %s on %s"), user, host);
814
815         g_free (host);
816         g_free (user);
817
818         return name;
819 }
820
821 static gboolean
822 imap_store_connect_sync (CamelService *service,
823                          GCancellable *cancellable,
824                          GError **error)
825 {
826         CamelImapStore *store = CAMEL_IMAP_STORE (service);
827         CamelImapResponse *response;
828         CamelSettings *settings;
829         CamelImapSettings *imap_settings;
830         gchar *result, *name;
831         gsize len;
832         const gchar *namespace;
833         GError *local_error = NULL;
834
835         settings = camel_service_get_settings (service);
836         imap_settings = CAMEL_IMAP_SETTINGS (settings);
837
838         if (!camel_offline_store_get_online (CAMEL_OFFLINE_STORE (store)))
839                 return TRUE;
840
841         if (!connect_to_server_wrapper (service, cancellable, error) ||
842             !imap_auth_loop (service, cancellable, error)) {
843                 /* reset cancellable, in case it is cancelled,
844                  * thus the disconnect is run */
845                 if (cancellable)
846                         g_cancellable_reset (cancellable);
847                 camel_service_disconnect_sync (
848                         service, TRUE, cancellable, NULL);
849                 return FALSE;
850         }
851
852         /* Get namespace and hierarchy separator */
853         if (store->capabilities & IMAP_CAPABILITY_NAMESPACE) {
854                 struct _namespaces *namespaces;
855                 const gchar *namespace;
856
857                 response = camel_imap_command (store, NULL, cancellable, &local_error, "NAMESPACE");
858                 if (!response)
859                         goto done;
860
861                 result = camel_imap_response_extract (store, response, "NAMESPACE", &local_error);
862                 if (!result)
863                         goto done;
864
865                 namespaces = imap_parse_namespace_response (result);
866
867                 if (!camel_imap_settings_get_use_namespace (imap_settings))
868                         camel_imap_settings_set_namespace (imap_settings, NULL);
869
870                 namespace = camel_imap_settings_get_namespace (imap_settings);
871
872                 if (namespaces != NULL && namespace == NULL) {
873                         struct _namespace *np = NULL;
874
875                         if (namespaces->personal)
876                                 np = namespaces->personal;
877                         else if (namespaces->other)
878                                 np = namespaces->other;
879                         else if (namespaces->shared)
880                                 np = namespaces->shared;
881
882                         if (np != NULL)
883                                 camel_imap_settings_set_namespace (
884                                         imap_settings, np->prefix);
885                 }
886
887                 if (namespaces) {
888                         #define add_all(_ns)                                                                    \
889                                 if (_ns) {                                                                      \
890                                         struct _namespace *ns;                                                  \
891                                                                                                                 \
892                                         for (ns = _ns; ns; ns = ns->next) {                                     \
893                                                 if (ns->prefix)                                                 \
894                                                         camel_imap_store_summary_namespace_add_secondary        \
895                                                                 (store->summary, ns->prefix, ns->delim);        \
896                                         }                                                                       \
897                                 }
898
899                         add_all (namespaces->personal);
900                         add_all (namespaces->other);
901                         add_all (namespaces->shared);
902
903                         #undef add_all
904                 }
905
906                 imap_namespaces_destroy (namespaces);
907
908                 if (camel_imap_settings_get_namespace (imap_settings) == NULL) {
909                         /* fallback for a broken result */
910                         name = camel_strstrcase (result, "NAMESPACE ((");
911                         if (name) {
912                                 gchar *ns;
913                                 gchar *sep;
914
915                                 name += 12;
916                                 ns = imap_parse_string (
917                                         (const gchar **) &name, &len);
918                                 camel_imap_settings_set_namespace (
919                                         imap_settings, ns);
920                                 g_free (ns);
921
922                                 if (name && *name++ == ' ') {
923                                         sep = imap_parse_string ((const gchar **) &name, &len);
924                                         if (sep) {
925                                                 store->dir_sep = *sep;
926                                                 g_free (sep);
927                                         }
928                                 }
929                         }
930                 }
931                 g_free (result);
932         }
933
934         if (!store->dir_sep) {
935                 const gchar *use_namespace = NULL;
936
937                 if (store->summary->namespace != NULL)
938                         use_namespace = store->summary->namespace->full_name;
939
940                 if (use_namespace == NULL)
941                         use_namespace = camel_imap_settings_get_namespace (
942                                 imap_settings);
943
944                 if (use_namespace == NULL)
945                         use_namespace = "";
946
947                 if (store->server_level >= IMAP_LEVEL_IMAP4REV1) {
948                         /* This idiom means "tell me the hierarchy separator
949                          * for the given path, even if that path doesn't exist.
950                          */
951                         response = camel_imap_command (
952                                 store, NULL, cancellable, &local_error,
953                                 "LIST %G \"\"", use_namespace);
954                 } else {
955                         /* Plain IMAP4 doesn't have that idiom, so we fall back
956                          * to "tell me about this folder", which will fail if
957                          * the folder doesn't exist (eg, if namespace is "").
958                          */
959                         response = camel_imap_command (
960                                 store, NULL, cancellable, &local_error,
961                                 "LIST \"\" %G", use_namespace);
962                 }
963                 if (!response)
964                         goto done;
965
966                 result = camel_imap_response_extract (store, response, "LIST", NULL);
967                 if (result) {
968                         imap_parse_list_response (store, result, NULL, &store->dir_sep, NULL);
969                         g_free (result);
970                 }
971
972                 if (!store->dir_sep)
973                         store->dir_sep = '/';   /* Guess */
974
975         }
976
977         /* canonicalize the namespace to not end with dir_sep */
978         namespace = camel_imap_settings_get_namespace (imap_settings);
979         len = (namespace != NULL) ? strlen (namespace) : 0;
980         if (len && namespace[len - 1] == store->dir_sep) {
981                 gchar *tmp = g_strdup (namespace);
982                 tmp[len - 1] = '\0';
983                 camel_imap_settings_set_namespace (imap_settings, tmp);
984                 namespace = camel_imap_settings_get_namespace (imap_settings);
985                 g_free (tmp);
986         }
987
988         camel_imap_store_summary_namespace_set_main (
989                 store->summary, namespace, store->dir_sep);
990
991         if (camel_imap_settings_get_use_subscriptions (imap_settings) &&
992             camel_store_summary_count ((CamelStoreSummary *) store->summary) == 0) {
993                 CamelStoreInfo *si;
994
995                 /* look in all namespaces */
996                 if (!get_folders_sync (store, NULL, cancellable, &local_error))
997                         goto done;
998
999                 /* Make sure INBOX is present/subscribed */
1000                 si = camel_store_summary_path((CamelStoreSummary *)store->summary, "INBOX");
1001                 if (si == NULL || (si->flags & CAMEL_FOLDER_SUBSCRIBED) == 0) {
1002                         response = camel_imap_command (store, NULL, cancellable, &local_error, "SUBSCRIBE INBOX");
1003                         if (response != NULL) {
1004                                 camel_imap_response_free (store, response);
1005                         }
1006                         if (si)
1007                                 camel_store_summary_info_free ((CamelStoreSummary *) store->summary, si);
1008                         if (local_error != NULL)
1009                                 goto done;
1010                         get_folders_sync(store, "INBOX", cancellable, &local_error);
1011                 }
1012
1013                 store->refresh_stamp = time (NULL);
1014         }
1015
1016 done:
1017         /* save any changes we had */
1018         camel_store_summary_save ((CamelStoreSummary *) store->summary);
1019
1020         if (local_error != NULL) {
1021                 /* reset cancellable, in case it is cancelled,
1022                  * thus the disconnect is run */
1023                 if (cancellable)
1024                         g_cancellable_reset (cancellable);
1025                 camel_service_disconnect_sync (
1026                         service, TRUE, cancellable, NULL);
1027                 g_propagate_error (error, local_error);
1028                 return FALSE;
1029         }
1030
1031         return TRUE;
1032 }
1033
1034 static gboolean
1035 imap_store_disconnect_sync (CamelService *service,
1036                             gboolean clean,
1037                             GCancellable *cancellable,
1038                             GError **error)
1039 {
1040         CamelImapStore *store = CAMEL_IMAP_STORE (service);
1041         CamelSettings *settings;
1042         CamelImapSettings *imap_settings;
1043
1044         settings = camel_service_get_settings (service);
1045         imap_settings = CAMEL_IMAP_SETTINGS (settings);
1046
1047         if (camel_offline_store_get_online (CAMEL_OFFLINE_STORE (store)) && clean) {
1048                 CamelImapResponse *response;
1049
1050                 response = camel_imap_command (store, NULL, NULL, NULL, "LOGOUT");
1051                 camel_imap_response_free (store, response);
1052         }
1053
1054         if (store->istream) {
1055                 camel_stream_close (store->istream, cancellable, NULL);
1056                 g_object_unref (store->istream);
1057                 store->istream = NULL;
1058         }
1059
1060         if (store->ostream) {
1061                 camel_stream_close (store->ostream, cancellable, NULL);
1062                 g_object_unref (store->ostream);
1063                 store->ostream = NULL;
1064         }
1065
1066         store->connected = FALSE;
1067         if (store->current_folder) {
1068                 g_object_unref (store->current_folder);
1069                 store->current_folder = NULL;
1070         }
1071
1072         if (store->authtypes) {
1073                 g_hash_table_foreach_remove (store->authtypes,
1074                                              free_key, NULL);
1075                 g_hash_table_destroy (store->authtypes);
1076                 store->authtypes = NULL;
1077         }
1078
1079         g_hash_table_remove_all (store->known_alerts);
1080
1081         if (camel_imap_settings_get_use_namespace (imap_settings))
1082                 camel_imap_settings_set_namespace (imap_settings, NULL);
1083
1084         return TRUE;
1085 }
1086
1087 static CamelAuthenticationResult
1088 imap_store_authenticate_sync (CamelService *service,
1089                               const gchar *mechanism,
1090                               GCancellable *cancellable,
1091                               GError **error)
1092 {
1093         CamelImapStore *store = CAMEL_IMAP_STORE (service);
1094         CamelAuthenticationResult result;
1095         CamelImapResponse *response;
1096         CamelSasl *sasl = NULL;
1097         gchar *sasl_resp;
1098         gchar *resp;
1099         GError *local_error = NULL;
1100
1101         /* If not using SASL, do a simple LOGIN here. */
1102         if (mechanism == NULL) {
1103                 CamelNetworkSettings *network_settings;
1104                 CamelSettings *settings;
1105                 const gchar *password;
1106                 gchar *user;
1107
1108                 password = camel_service_get_password (service);
1109                 settings = camel_service_get_settings (service);
1110
1111                 network_settings = CAMEL_NETWORK_SETTINGS (settings);
1112                 user = camel_network_settings_dup_user (network_settings);
1113
1114                 if (user == NULL) {
1115                         g_set_error_literal (
1116                                 error, CAMEL_SERVICE_ERROR,
1117                                 CAMEL_SERVICE_ERROR_CANT_AUTHENTICATE,
1118                                 _("Cannot authenticate without a username"));
1119                         return CAMEL_AUTHENTICATION_ERROR;
1120                 }
1121
1122                 if (password == NULL) {
1123                         g_set_error_literal (
1124                                 error, CAMEL_SERVICE_ERROR,
1125                                 CAMEL_SERVICE_ERROR_CANT_AUTHENTICATE,
1126                                 _("Authentication password not available"));
1127                         g_free (user);
1128                         return CAMEL_AUTHENTICATION_ERROR;
1129                 }
1130
1131                 response = camel_imap_command (
1132                         store, NULL, cancellable, &local_error,
1133                         "LOGIN %S %S", user, password);
1134
1135                 if (response != NULL)
1136                         camel_imap_response_free (store, response);
1137
1138                 g_free (user);
1139
1140                 goto exit;
1141         }
1142
1143         /* Henceforth we're using SASL. */
1144
1145         sasl = camel_sasl_new ("imap", mechanism, service);
1146         if (sasl == NULL) {
1147                 g_set_error (
1148                         error, CAMEL_SERVICE_ERROR,
1149                         CAMEL_SERVICE_ERROR_CANT_AUTHENTICATE,
1150                         _("No support for %s authentication"), mechanism);
1151                 return CAMEL_AUTHENTICATION_ERROR;
1152         }
1153
1154         response = camel_imap_command (
1155                 store, NULL, cancellable, &local_error,
1156                 "AUTHENTICATE %s", mechanism);
1157         if (response == NULL)
1158                 goto exit;
1159
1160         while (!camel_sasl_get_authenticated (sasl)) {
1161                 resp = camel_imap_response_extract_continuation (
1162                         store, response, &local_error);
1163
1164                 if (resp == NULL)
1165                         goto exit;
1166
1167                 sasl_resp = camel_sasl_challenge_base64_sync (
1168                         sasl, imap_next_word (resp),
1169                         cancellable, &local_error);
1170
1171                 g_free (resp);
1172
1173                 if (sasl_resp == NULL)
1174                         goto break_and_exit;
1175
1176                 response = camel_imap_command_continuation (
1177                         store, NULL, sasl_resp, strlen (sasl_resp),
1178                         cancellable, &local_error);
1179
1180                 g_free (sasl_resp);
1181
1182                 if (response == NULL)
1183                         goto exit;
1184         }
1185
1186         resp = camel_imap_response_extract_continuation (store, response, NULL);
1187         if (resp != NULL) {
1188                 /* Oops. SASL claims we're done, but the IMAP server
1189                  * doesn't think so... */
1190                 g_free (resp);
1191                 goto exit;
1192         }
1193
1194         goto exit;
1195
1196 break_and_exit:
1197         /* Get the server out of "waiting for continuation data" mode. */
1198         response = camel_imap_command_continuation (
1199                 store, NULL, "*", 1, cancellable, NULL);
1200         if (response != NULL)
1201                 camel_imap_response_free (store, response);
1202
1203 exit:
1204         /* XXX Apparently the IMAP parser sets CAMEL_SERVICE_ERROR_INVALID
1205          *     for failed IMAP server responses, so I guess check for that
1206          *     to know if our authentication credentials were rejected. */
1207         if (local_error == NULL) {
1208                 result = CAMEL_AUTHENTICATION_ACCEPTED;
1209         } else if (g_error_matches (local_error, CAMEL_SERVICE_ERROR, CAMEL_SERVICE_ERROR_INVALID)) {
1210                 g_clear_error (&local_error);
1211                 result = CAMEL_AUTHENTICATION_REJECTED;
1212
1213                 /* Some servers (eg, courier) will disconnect
1214                  * on a bad password, so we reconnect here. */
1215                 if (!store->connected) {
1216                         if (!connect_to_server_wrapper (
1217                                 service, cancellable, error))
1218                                 result = CAMEL_AUTHENTICATION_ERROR;
1219                 }
1220
1221         } else {
1222                 g_propagate_error (error, local_error);
1223                 result = CAMEL_AUTHENTICATION_ERROR;
1224         }
1225
1226         if (sasl != NULL)
1227                 g_object_unref (sasl);
1228
1229         return result;
1230 }
1231
1232 static GList *
1233 imap_store_query_auth_types_sync (CamelService *service,
1234                                   GCancellable *cancellable,
1235                                   GError **error)
1236 {
1237         CamelImapStore *store = CAMEL_IMAP_STORE (service);
1238         CamelServiceAuthType *authtype;
1239         GList *sasl_types, *t, *next;
1240         gboolean connected;
1241
1242         if (!camel_offline_store_get_online (CAMEL_OFFLINE_STORE (store))) {
1243                 g_set_error (
1244                         error, CAMEL_SERVICE_ERROR,
1245                         CAMEL_SERVICE_ERROR_UNAVAILABLE,
1246                         _("You must be working online to complete this operation"));
1247                 return NULL;
1248         }
1249
1250         connected = store->istream != NULL && store->connected;
1251         if (!connected)
1252                 connected = connect_to_server_wrapper (
1253                         service, cancellable, error);
1254         if (!connected)
1255                 return NULL;
1256
1257         sasl_types = camel_sasl_authtype_list (FALSE);
1258         for (t = sasl_types; t; t = next) {
1259                 authtype = t->data;
1260                 next = t->next;
1261
1262                 if (!g_hash_table_lookup (store->authtypes, authtype->authproto)) {
1263                         sasl_types = g_list_remove_link (sasl_types, t);
1264                         g_list_free_1 (t);
1265                 }
1266         }
1267
1268         return g_list_prepend (sasl_types, &camel_imap_password_authtype);
1269 }
1270
1271 static void
1272 imap_migrate_to_user_cache_dir (CamelService *service)
1273 {
1274         const gchar *user_data_dir, *user_cache_dir;
1275
1276         g_return_if_fail (service != NULL);
1277         g_return_if_fail (CAMEL_IS_SERVICE (service));
1278
1279         user_data_dir = camel_service_get_user_data_dir (service);
1280         user_cache_dir = camel_service_get_user_cache_dir (service);
1281
1282         g_return_if_fail (user_data_dir != NULL);
1283         g_return_if_fail (user_cache_dir != NULL);
1284
1285         /* migrate only if the source directory exists and the destination doesn't */
1286         if (g_file_test (user_data_dir, G_FILE_TEST_EXISTS | G_FILE_TEST_IS_DIR) &&
1287             !g_file_test (user_cache_dir, G_FILE_TEST_EXISTS | G_FILE_TEST_IS_DIR)) {
1288                 gchar *parent_dir;
1289
1290                 parent_dir = g_path_get_dirname (user_cache_dir);
1291                 g_mkdir_with_parents (parent_dir, S_IRWXU);
1292                 g_free (parent_dir);
1293
1294                 if (g_rename (user_data_dir, user_cache_dir) == -1)
1295                         g_debug ("%s: Failed to migrate '%s' to '%s': %s", G_STRFUNC, user_data_dir, user_cache_dir, g_strerror (errno));
1296         }
1297 }
1298
1299 static gboolean
1300 imap_store_initable_init (GInitable *initable,
1301                           GCancellable *cancellable,
1302                           GError **error)
1303 {
1304         CamelImapStore *imap_store;
1305         CamelStore *store;
1306         CamelService *service;
1307         const gchar *user_cache_dir;
1308         gchar *tmp_path;
1309
1310         imap_store = CAMEL_IMAP_STORE (initable);
1311         store = CAMEL_STORE (initable);
1312         service = CAMEL_SERVICE (initable);
1313
1314         store->flags |= CAMEL_STORE_USE_CACHE_DIR;
1315         imap_migrate_to_user_cache_dir (service);
1316
1317         /* Chain up to parent interface's init() method. */
1318         if (!parent_initable_interface->init (initable, cancellable, error))
1319                 return FALSE;
1320
1321         service = CAMEL_SERVICE (initable);
1322         user_cache_dir = camel_service_get_user_cache_dir (service);
1323
1324         /* setup/load the store summary */
1325         tmp_path = g_build_filename (user_cache_dir, ".ev-store-summary", NULL);
1326         imap_store->summary = camel_imap_store_summary_new ();
1327         camel_store_summary_set_filename ((CamelStoreSummary *) imap_store->summary, tmp_path);
1328         g_free (tmp_path);
1329         if (camel_store_summary_load ((CamelStoreSummary *) imap_store->summary) == 0) {
1330                 CamelImapStoreSummary *is = imap_store->summary;
1331
1332                 /* XXX This won't work anymore.  The CamelSettings
1333                  *     object for this store is not yet configured. */
1334 #if 0
1335                 if (is->namespace) {
1336                         const gchar *namespace;
1337
1338                         namespace = camel_imap_settings_get_namespace (
1339                                 CAMEL_IMAP_SETTINGS (settings));
1340
1341                         /* if namespace has changed, clear folder list */
1342                         if (g_strcmp0 (namespace, is->namespace->full_name) != 0)
1343                                 camel_store_summary_clear ((CamelStoreSummary *) is);
1344                 }
1345 #endif
1346
1347                 imap_store->capabilities = is->capabilities;
1348                 imap_set_server_level (imap_store);
1349         }
1350
1351         return TRUE;
1352 }
1353
1354 static const gchar *
1355 imap_store_get_service_name (CamelNetworkService *service,
1356                              CamelNetworkSecurityMethod method)
1357 {
1358         const gchar *service_name;
1359
1360         switch (method) {
1361                 case CAMEL_NETWORK_SECURITY_METHOD_SSL_ON_ALTERNATE_PORT:
1362                         service_name = "imaps";
1363                         break;
1364
1365                 default:
1366                         service_name = "imap";
1367                         break;
1368         }
1369
1370         return service_name;
1371 }
1372
1373 static guint16
1374 imap_store_get_default_port (CamelNetworkService *service,
1375                              CamelNetworkSecurityMethod method)
1376 {
1377         guint16 default_port;
1378
1379         switch (method) {
1380                 case CAMEL_NETWORK_SECURITY_METHOD_SSL_ON_ALTERNATE_PORT:
1381                         default_port = IMAPS_PORT;
1382                         break;
1383
1384                 default:
1385                         default_port = IMAP_PORT;
1386                         break;
1387         }
1388
1389         return default_port;
1390 }
1391
1392 static gboolean
1393 imap_store_folder_is_subscribed (CamelSubscribable *subscribable,
1394                                  const gchar *folder_name)
1395 {
1396         CamelImapStore *imap_store = CAMEL_IMAP_STORE (subscribable);
1397         CamelStoreInfo *si;
1398         gint truth = FALSE;
1399
1400         si = camel_store_summary_path ((CamelStoreSummary *) imap_store->summary, folder_name);
1401         if (si) {
1402                 truth = (si->flags & CAMEL_STORE_INFO_FOLDER_SUBSCRIBED) != 0;
1403                 camel_store_summary_info_free ((CamelStoreSummary *) imap_store->summary, si);
1404         }
1405
1406         return truth;
1407 }
1408
1409 /* Note: folder_name must match a folder as listed with get_folder_info() -> full_name */
1410 static gboolean
1411 imap_store_subscribe_folder_sync (CamelSubscribable *subscribable,
1412                                   const gchar *folder_name,
1413                                   GCancellable *cancellable,
1414                                   GError **error)
1415 {
1416         CamelImapStore *imap_store;
1417         CamelImapResponse *response;
1418         CamelFolderInfo *fi;
1419         CamelStoreInfo *si;
1420
1421         imap_store = CAMEL_IMAP_STORE (subscribable);
1422
1423         if (!camel_imap_store_connected (imap_store, error))
1424                 return FALSE;
1425
1426         response = camel_imap_command (imap_store, NULL, cancellable, error,
1427                                        "SUBSCRIBE %F", folder_name);
1428         if (!response)
1429                 return FALSE;
1430
1431         camel_imap_response_free (imap_store, response);
1432
1433         si = camel_store_summary_path ((CamelStoreSummary *) imap_store->summary, folder_name);
1434         if (si) {
1435                 if ((si->flags & CAMEL_STORE_INFO_FOLDER_SUBSCRIBED) == 0) {
1436                         si->flags |= CAMEL_STORE_INFO_FOLDER_SUBSCRIBED;
1437                         camel_store_summary_touch ((CamelStoreSummary *) imap_store->summary);
1438                         camel_store_summary_save ((CamelStoreSummary *) imap_store->summary);
1439                 }
1440                 camel_store_summary_info_free ((CamelStoreSummary *) imap_store->summary, si);
1441         }
1442
1443         if (imap_store->renaming) {
1444                 /* we don't need to emit a "folder_subscribed" signal
1445                  * if we are in the process of renaming folders, so we
1446                  * are done here... */
1447                 return TRUE;
1448         }
1449
1450         fi = imap_build_folder_info (imap_store, folder_name);
1451         fi->flags |= CAMEL_FOLDER_NOCHILDREN;
1452
1453         camel_subscribable_folder_subscribed (subscribable, fi);
1454         camel_folder_info_free (fi);
1455
1456         return TRUE;
1457 }
1458
1459 static gboolean
1460 imap_store_unsubscribe_folder_sync (CamelSubscribable *subscribable,
1461                                     const gchar *folder_name,
1462                                     GCancellable *cancellable,
1463                                     GError **error)
1464 {
1465         CamelImapStore *imap_store;
1466         CamelImapResponse *response;
1467
1468         imap_store = CAMEL_IMAP_STORE (subscribable);
1469
1470         if (!camel_imap_store_connected (imap_store, error))
1471                 return FALSE;
1472
1473         response = camel_imap_command (imap_store, NULL, cancellable, error,
1474                                        "UNSUBSCRIBE %F", folder_name);
1475         if (!response)
1476                 return FALSE;
1477
1478         camel_imap_response_free (imap_store, response);
1479
1480         return imap_folder_effectively_unsubscribed (
1481                 imap_store, folder_name, error);
1482 }
1483
1484 static void
1485 camel_imap_store_class_init (CamelImapStoreClass *class)
1486 {
1487         GObjectClass *object_class;
1488         CamelServiceClass *service_class;
1489         CamelStoreClass *store_class;
1490
1491         object_class = G_OBJECT_CLASS (class);
1492         object_class->dispose = imap_store_dispose;
1493         object_class->finalize = imap_store_finalize;
1494
1495         service_class = CAMEL_SERVICE_CLASS (class);
1496         service_class->settings_type = CAMEL_TYPE_IMAP_SETTINGS;
1497         service_class->get_name = imap_store_get_name;
1498         service_class->connect_sync = imap_store_connect_sync;
1499         service_class->disconnect_sync = imap_store_disconnect_sync;
1500         service_class->authenticate_sync = imap_store_authenticate_sync;
1501         service_class->query_auth_types_sync = imap_store_query_auth_types_sync;
1502
1503         store_class = CAMEL_STORE_CLASS (class);
1504         store_class->hash_folder_name = hash_folder_name;
1505         store_class->compare_folder_name = compare_folder_name;
1506         store_class->can_refresh_folder = imap_can_refresh_folder;
1507         store_class->free_folder_info = camel_store_free_folder_info_full;
1508         store_class->get_folder_sync = imap_store_get_folder_sync;
1509         store_class->get_folder_info_sync = imap_store_get_folder_info_sync;
1510         store_class->get_junk_folder_sync = imap_store_get_junk_folder_sync;
1511         store_class->get_trash_folder_sync = imap_store_get_trash_folder_sync;
1512         store_class->create_folder_sync = imap_store_create_folder_sync;
1513         store_class->delete_folder_sync = imap_store_delete_folder_sync;
1514         store_class->rename_folder_sync = imap_store_rename_folder_sync;
1515         store_class->noop_sync = imap_store_noop_sync;
1516 }
1517
1518 static void
1519 camel_imap_store_initable_init (GInitableIface *interface)
1520 {
1521         parent_initable_interface = g_type_interface_peek_parent (interface);
1522
1523         interface->init = imap_store_initable_init;
1524 }
1525
1526 static void
1527 camel_network_service_init (CamelNetworkServiceInterface *interface)
1528 {
1529         interface->get_service_name = imap_store_get_service_name;
1530         interface->get_default_port = imap_store_get_default_port;
1531 }
1532
1533 static void
1534 camel_subscribable_init (CamelSubscribableInterface *interface)
1535 {
1536         interface->folder_is_subscribed = imap_store_folder_is_subscribed;
1537         interface->subscribe_folder_sync = imap_store_subscribe_folder_sync;
1538         interface->unsubscribe_folder_sync = imap_store_unsubscribe_folder_sync;
1539 }
1540
1541 static void
1542 camel_imap_store_init (CamelImapStore *imap_store)
1543 {
1544         g_static_rec_mutex_init (&imap_store->command_and_response_lock);
1545
1546         imap_store->istream = NULL;
1547         imap_store->ostream = NULL;
1548
1549         /* TODO: support dir_sep per namespace */
1550         imap_store->dir_sep = '\0';
1551         imap_store->current_folder = NULL;
1552         imap_store->connected = FALSE;
1553         imap_store->preauthed = FALSE;
1554
1555         imap_store->tag_prefix = imap_tag_prefix++;
1556         if (imap_tag_prefix > 'Z')
1557                 imap_tag_prefix = 'A';
1558
1559         imap_store->known_alerts = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, NULL);
1560
1561         g_signal_connect (
1562                 imap_store, "notify::settings",
1563                 G_CALLBACK (imap_store_update_store_flags), NULL);
1564 }
1565
1566 static void
1567 imap_set_server_level (CamelImapStore *store)
1568 {
1569         if (store->capabilities & IMAP_CAPABILITY_IMAP4REV1) {
1570                 store->server_level = IMAP_LEVEL_IMAP4REV1;
1571                 store->capabilities |= IMAP_CAPABILITY_STATUS;
1572         } else if (store->capabilities & IMAP_CAPABILITY_IMAP4)
1573                 store->server_level = IMAP_LEVEL_IMAP4;
1574         else
1575                 store->server_level = IMAP_LEVEL_UNKNOWN;
1576 }
1577
1578 static gboolean
1579 imap_folder_effectively_unsubscribed (CamelImapStore *imap_store,
1580                                       const gchar *folder_name,
1581                                       GError **error)
1582 {
1583         CamelFolderInfo *fi;
1584         CamelStoreInfo *si;
1585
1586         si = camel_store_summary_path ((CamelStoreSummary *) imap_store->summary, folder_name);
1587         if (si) {
1588                 if (si->flags & CAMEL_STORE_INFO_FOLDER_SUBSCRIBED) {
1589                         si->flags &= ~CAMEL_STORE_INFO_FOLDER_SUBSCRIBED;
1590                         camel_store_summary_touch ((CamelStoreSummary *) imap_store->summary);
1591                         camel_store_summary_save ((CamelStoreSummary *) imap_store->summary);
1592                 }
1593                 camel_store_summary_info_free ((CamelStoreSummary *) imap_store->summary, si);
1594         }
1595
1596         if (imap_store->renaming) {
1597                 /* we don't need to emit a "folder_unsubscribed" signal
1598                  * if we are in the process of renaming folders, so we
1599                  * are done here... */
1600                 return TRUE;
1601
1602         }
1603
1604         fi = imap_build_folder_info (imap_store, folder_name);
1605         camel_subscribable_folder_unsubscribed (
1606                 CAMEL_SUBSCRIBABLE (imap_store), fi);
1607         camel_folder_info_free (fi);
1608
1609         return TRUE;
1610 }
1611
1612 static void
1613 imap_forget_folder (CamelImapStore *imap_store,
1614                     const gchar *folder_name,
1615                     GError **error)
1616 {
1617         CamelService *service;
1618         const gchar *user_cache_dir;
1619         gchar *state_file;
1620         gchar *journal_file;
1621         gchar *folder_dir, *storage_path;
1622         CamelFolderInfo *fi;
1623         const gchar *name;
1624
1625         name = strrchr (folder_name, imap_store->dir_sep);
1626         if (name)
1627                 name++;
1628         else
1629                 name = folder_name;
1630
1631         service = CAMEL_SERVICE (imap_store);
1632         user_cache_dir = camel_service_get_user_cache_dir (service);
1633
1634         storage_path = g_build_filename (user_cache_dir, "folders", NULL);
1635         folder_dir = imap_path_to_physical (storage_path, folder_name);
1636         g_free (storage_path);
1637         if (g_access (folder_dir, F_OK) != 0) {
1638                 g_free (folder_dir);
1639                 goto event;
1640         }
1641
1642         /* Delete summary and all the data */
1643         journal_file = g_strdup_printf ("%s/journal", folder_dir);
1644         g_unlink (journal_file);
1645         g_free (journal_file);
1646
1647         state_file = g_strdup_printf ("%s/cmeta", folder_dir);
1648         g_unlink (state_file);
1649         g_free (state_file);
1650
1651         camel_db_delete_folder (((CamelStore *) imap_store)->cdb_w, folder_name, NULL);
1652         camel_imap_message_cache_delete (folder_dir, NULL);
1653
1654         state_file = g_strdup_printf("%s/subfolders", folder_dir);
1655         g_rmdir (state_file);
1656         g_free (state_file);
1657
1658         g_rmdir (folder_dir);
1659         g_free (folder_dir);
1660
1661  event:
1662
1663         camel_store_summary_remove_path ((CamelStoreSummary *) imap_store->summary, folder_name);
1664         camel_store_summary_save ((CamelStoreSummary *) imap_store->summary);
1665
1666         fi = imap_build_folder_info (imap_store, folder_name);
1667         camel_store_folder_deleted (CAMEL_STORE (imap_store), fi);
1668         camel_folder_info_free (fi);
1669 }
1670
1671 static gboolean
1672 imap_check_folder_still_extant (CamelImapStore *imap_store,
1673                                 const gchar *full_name,
1674                                 GError **error)
1675 {
1676         CamelImapResponse *response;
1677
1678         response = camel_imap_command (imap_store, NULL, NULL, NULL, "LIST \"\" %F",
1679                                        full_name);
1680
1681         if (response) {
1682                 gboolean stillthere = response->untagged->len != 0;
1683
1684                 camel_imap_response_free_without_processing (imap_store, response);
1685
1686                 return stillthere;
1687         }
1688
1689         /* if the command was rejected, there must be some other error,
1690          * assume it worked so we dont blow away the folder unecessarily */
1691         return TRUE;
1692 }
1693
1694 static gboolean
1695 imap_summary_is_dirty (CamelFolderSummary *summary)
1696 {
1697         CamelImapMessageInfo *info;
1698         gint i;
1699         gboolean found = FALSE;
1700         GPtrArray *known_uids;
1701
1702         known_uids = camel_folder_summary_get_array (summary);
1703         g_return_val_if_fail (known_uids != NULL, FALSE);
1704
1705         for (i = 0; i < known_uids->len && !found; i++) {
1706                 info = (CamelImapMessageInfo *) camel_folder_summary_get (summary, g_ptr_array_index (known_uids, i));
1707                 if (info) {
1708                         found = info->info.flags & CAMEL_MESSAGE_FOLDER_FLAGGED;
1709                         camel_message_info_free (info);
1710                 }
1711         }
1712
1713         camel_folder_summary_free_array (known_uids);
1714
1715         return found;
1716 }
1717
1718 static gboolean
1719 imap_store_noop_sync (CamelStore *store,
1720                       GCancellable *cancellable,
1721                       GError **error)
1722 {
1723         CamelImapStore *imap_store = (CamelImapStore *) store;
1724         CamelImapResponse *response;
1725         CamelFolder *current_folder;
1726         gboolean success = TRUE;
1727
1728         if (!camel_imap_store_connected (imap_store, error))
1729                 return FALSE;
1730
1731         current_folder = imap_store->current_folder;
1732         if (current_folder && imap_summary_is_dirty (current_folder->summary)) {
1733                 /* let's sync the flags instead.  NB: must avoid folder lock */
1734                 success = CAMEL_FOLDER_GET_CLASS (current_folder)->synchronize_sync (
1735                         current_folder, FALSE, cancellable, error);
1736         } else {
1737                 response = camel_imap_command (imap_store, NULL, cancellable, error, "NOOP");
1738                 if (response)
1739                         camel_imap_response_free (imap_store, response);
1740                 else
1741                         success = FALSE;
1742         }
1743
1744         return success;
1745 }
1746
1747 static CamelFolder *
1748 imap_store_get_trash_folder_sync (CamelStore *store,
1749                                   GCancellable *cancellable,
1750                                   GError **error)
1751 {
1752         CamelService *service;
1753         CamelSettings *settings;
1754         CamelFolder *folder = NULL;
1755         const gchar *user_cache_dir;
1756         gchar *trash_path;
1757
1758         service = CAMEL_SERVICE (store);
1759         settings = camel_service_get_settings (service);
1760         user_cache_dir = camel_service_get_user_cache_dir (service);
1761
1762         trash_path = camel_imap_settings_dup_real_trash_path (
1763                 CAMEL_IMAP_SETTINGS (settings));
1764         if (trash_path != NULL) {
1765                 folder = camel_store_get_folder_sync (
1766                         store, trash_path, 0, cancellable, NULL);
1767                 if (folder == NULL)
1768                         camel_imap_settings_set_real_trash_path (
1769                                 CAMEL_IMAP_SETTINGS (settings), NULL);
1770         }
1771         g_free (trash_path);
1772
1773         if (folder)
1774                 return folder;
1775
1776         folder = CAMEL_STORE_CLASS (camel_imap_store_parent_class)->
1777                 get_trash_folder_sync (store, cancellable, error);
1778
1779         if (folder) {
1780                 CamelObject *object = CAMEL_OBJECT (folder);
1781                 gchar *state;
1782
1783                 state = g_build_filename (
1784                         user_cache_dir, "system", "Trash.cmeta", NULL);
1785
1786                 camel_object_set_state_filename (object, state);
1787                 g_free (state);
1788                 /* no defaults? */
1789                 camel_object_state_read (object);
1790         }
1791
1792         return folder;
1793 }
1794
1795 static CamelFolder *
1796 imap_store_get_junk_folder_sync (CamelStore *store,
1797                                  GCancellable *cancellable,
1798                                  GError **error)
1799 {
1800         CamelService *service;
1801         CamelSettings *settings;
1802         CamelFolder *folder = NULL;
1803         const gchar *user_cache_dir;
1804         gchar *junk_path;
1805
1806         service = CAMEL_SERVICE (store);
1807         settings = camel_service_get_settings (service);
1808         user_cache_dir = camel_service_get_user_cache_dir (service);
1809
1810         junk_path = camel_imap_settings_dup_real_junk_path (
1811                 CAMEL_IMAP_SETTINGS (settings));
1812         if (junk_path != NULL) {
1813                 folder = camel_store_get_folder_sync (
1814                         store, junk_path, 0, cancellable, NULL);
1815                 if (folder == NULL)
1816                         camel_imap_settings_set_real_junk_path (
1817                                 CAMEL_IMAP_SETTINGS (settings), NULL);
1818         }
1819         g_free (junk_path);
1820
1821         if (folder)
1822                 return folder;
1823
1824         folder = CAMEL_STORE_CLASS (camel_imap_store_parent_class)->
1825                 get_junk_folder_sync (store, cancellable, error);
1826
1827         if (folder) {
1828                 CamelObject *object = CAMEL_OBJECT (folder);
1829                 gchar *state;
1830
1831                 state = g_build_filename (
1832                         user_cache_dir, "system", "Junk.cmeta", NULL);
1833
1834                 camel_object_set_state_filename (object, state);
1835                 g_free (state);
1836                 /* no defaults? */
1837                 camel_object_state_read (object);
1838         }
1839
1840         return folder;
1841 }
1842
1843 static guint
1844 hash_folder_name (gconstpointer key)
1845 {
1846         if (g_ascii_strcasecmp (key, "INBOX") == 0)
1847                 return g_str_hash ("INBOX");
1848         else
1849                 return g_str_hash (key);
1850 }
1851
1852 static gint
1853 compare_folder_name (gconstpointer a,
1854                      gconstpointer b)
1855 {
1856         gconstpointer aname = a, bname = b;
1857
1858         if (g_ascii_strcasecmp (a, "INBOX") == 0)
1859                 aname = "INBOX";
1860         if (g_ascii_strcasecmp (b, "INBOX") == 0)
1861                 bname = "INBOX";
1862         return g_str_equal (aname, bname);
1863 }
1864
1865 struct imap_status_item {
1866         struct imap_status_item *next;
1867         gchar *name;
1868         guint32 value;
1869 };
1870
1871 static void
1872 imap_status_item_free (struct imap_status_item *items)
1873 {
1874         struct imap_status_item *next;
1875
1876         while (items != NULL) {
1877                 next = items->next;
1878                 g_free (items->name);
1879                 g_free (items);
1880                 items = next;
1881         }
1882 }
1883
1884 static struct imap_status_item *
1885 get_folder_status (CamelImapStore *imap_store,
1886                    const gchar *folder_name,
1887                    const gchar *type)
1888 {
1889         struct imap_status_item *items, *item, *tail;
1890         CamelImapResponse *response;
1891         gchar *status, *name, *p;
1892
1893         /* FIXME: we assume the server is STATUS-capable */
1894
1895         response = camel_imap_command (imap_store, NULL, NULL, NULL,
1896                                        "STATUS %F (%s)",
1897                                        folder_name,
1898                                        type);
1899
1900         if (!response) {
1901                 if (imap_check_folder_still_extant (imap_store, folder_name, NULL) == FALSE) {
1902                         imap_folder_effectively_unsubscribed (imap_store, folder_name, NULL);
1903                         imap_forget_folder (imap_store, folder_name, NULL);
1904                 }
1905                 return NULL;
1906         }
1907
1908         if (!(status = camel_imap_response_extract (imap_store, response, "STATUS", NULL)))
1909                 return NULL;
1910
1911         p = status + strlen ("* STATUS ");
1912         while (*p == ' ')
1913                 p++;
1914
1915         /* skip past the mailbox string */
1916         if (*p == '"') {
1917                 p++;
1918                 while (*p != '\0') {
1919                         if (*p == '"' && p[-1] != '\\') {
1920                                 p++;
1921                                 break;
1922                         }
1923
1924                         p++;
1925                 }
1926         } else {
1927                 while (*p != ' ')
1928                         p++;
1929         }
1930
1931         while (*p == ' ')
1932                 p++;
1933
1934         if (*p++ != '(') {
1935                 g_free (status);
1936                 return NULL;
1937         }
1938
1939         while (*p == ' ')
1940                 p++;
1941
1942         if (*p == ')') {
1943                 g_free (status);
1944                 return NULL;
1945         }
1946
1947         items = NULL;
1948         tail = (struct imap_status_item *) &items;
1949
1950         do {
1951                 name = p;
1952                 while (*p != ' ')
1953                         p++;
1954
1955                 item = g_malloc (sizeof (struct imap_status_item));
1956                 item->next = NULL;
1957                 item->name = g_strndup (name, p - name);
1958                 item->value = strtoul (p, &p, 10);
1959
1960                 tail->next = item;
1961                 tail = item;
1962
1963                 while (*p == ' ')
1964                         p++;
1965         } while (*p != ')');
1966
1967         g_free (status);
1968
1969         return items;
1970 }
1971
1972 static CamelFolder *
1973 imap_store_get_folder_sync (CamelStore *store,
1974                             const gchar *folder_name,
1975                             CamelStoreGetFolderFlags flags,
1976                             GCancellable *cancellable,
1977                             GError **error)
1978 {
1979         CamelImapStore *imap_store = CAMEL_IMAP_STORE (store);
1980         CamelImapResponse *response;
1981         CamelFolder *new_folder;
1982         CamelService *service;
1983         const gchar *user_cache_dir;
1984         gchar *folder_dir, *storage_path;
1985         GError *local_error = NULL;
1986
1987         service = CAMEL_SERVICE (store);
1988         user_cache_dir = camel_service_get_user_cache_dir (service);
1989
1990         /* Try to get it locally first, if it is, then the client will
1991          * force a select when necessary */
1992         new_folder = get_folder_offline (store, folder_name, flags, &local_error);
1993         if (new_folder)
1994                 return new_folder;
1995
1996         g_clear_error (&local_error);
1997
1998         if (!camel_offline_store_get_online (CAMEL_OFFLINE_STORE (store))) {
1999                 g_set_error (
2000                         error, CAMEL_SERVICE_ERROR,
2001                         CAMEL_SERVICE_ERROR_UNAVAILABLE,
2002                         _("You must be working online to complete this operation"));
2003                 return NULL;
2004         }
2005
2006         if (!camel_imap_store_connected (imap_store, error))
2007                 return NULL;
2008
2009         if (!g_ascii_strcasecmp (folder_name, "INBOX"))
2010                 folder_name = "INBOX";
2011
2012         if (imap_store->current_folder) {
2013                 g_object_unref (imap_store->current_folder);
2014                 imap_store->current_folder = NULL;
2015         }
2016
2017         response = camel_imap_command (imap_store, NULL, cancellable, &local_error, "SELECT %F", folder_name);
2018         if (!response) {
2019                 gchar *folder_real, *parent_name, *parent_real;
2020                 const gchar *c;
2021
2022                 if (g_error_matches (local_error, G_IO_ERROR, G_IO_ERROR_CANCELLED)) {
2023                         g_propagate_error (error, local_error);
2024                         return NULL;
2025                 }
2026
2027                 g_clear_error (&local_error);
2028
2029                 if (!(flags & CAMEL_STORE_FOLDER_CREATE)) {
2030                         g_set_error (
2031                                 error, CAMEL_STORE_ERROR,
2032                                 CAMEL_STORE_ERROR_NO_FOLDER,
2033                                 _("No such folder %s"), folder_name);
2034                         return NULL;
2035                 }
2036
2037                 parent_name = strrchr (folder_name, '/');
2038                 c = parent_name ? parent_name + 1 : folder_name;
2039                 while (*c && *c != imap_store->dir_sep && !strchr ("#%*", *c))
2040                         c++;
2041
2042                 if (*c != '\0') {
2043                         g_set_error (
2044                                 error, CAMEL_FOLDER_ERROR,
2045                                 CAMEL_FOLDER_ERROR_INVALID_PATH,
2046                                 _("The folder name \"%s\" is invalid because it contains the character \"%c\""),
2047                                 folder_name, *c);
2048                         return NULL;
2049                 }
2050
2051                 if (parent_name) {
2052                         parent_name = g_strndup (folder_name, parent_name - folder_name);
2053                         parent_real = camel_imap_store_summary_path_to_full (imap_store->summary, parent_name, imap_store->dir_sep);
2054                 } else {
2055                         parent_real = NULL;
2056                 }
2057
2058                 if (parent_real != NULL) {
2059                         gboolean need_convert = FALSE;
2060                         gchar *resp, *thisone;
2061                         gint flags;
2062                         gint i;
2063
2064                         if (!(response = camel_imap_command (imap_store, NULL, cancellable, error, "LIST \"\" %G", parent_real))) {
2065                                 g_free (parent_name);
2066                                 g_free (parent_real);
2067                                 return NULL;
2068                         }
2069
2070                         /* FIXME: does not handle unexpected circumstances very well */
2071                         for (i = 0; i < response->untagged->len; i++) {
2072                                 resp = response->untagged->pdata[i];
2073
2074                                 if (!imap_parse_list_response (imap_store, resp, &flags, NULL, &thisone))
2075                                         continue;
2076
2077                                 if (!strcmp (parent_name, thisone)) {
2078                                         if (flags & CAMEL_FOLDER_NOINFERIORS)
2079                                                 need_convert = TRUE;
2080                                 }
2081
2082                                 g_free (thisone);
2083                         }
2084
2085                         camel_imap_response_free (imap_store, response);
2086
2087                         /* if not, check if we can delete it and recreate it */
2088                         if (need_convert) {
2089                                 struct imap_status_item *items, *item;
2090                                 guint32 messages = 0;
2091                                 gchar *name;
2092
2093                                 item = items = get_folder_status (imap_store, parent_name, "MESSAGES");
2094                                 while (item != NULL) {
2095                                         if (!g_ascii_strcasecmp (item->name, "MESSAGES")) {
2096                                                 messages = item->value;
2097                                                 break;
2098                                         }
2099
2100                                         item = item->next;
2101                                 }
2102
2103                                 imap_status_item_free (items);
2104
2105                                 if (messages > 0) {
2106                                         g_set_error (
2107                                                 error, CAMEL_FOLDER_ERROR,
2108                                                 CAMEL_FOLDER_ERROR_INVALID_STATE,
2109                                                 _("The parent folder is not allowed to contain subfolders"));
2110                                         g_free (parent_name);
2111                                         g_free (parent_real);
2112                                         return NULL;
2113                                 }
2114
2115                                 /* delete the old parent and recreate it */
2116                                 if (!imap_store_delete_folder_sync (
2117                                         store, parent_name, cancellable, error)) {
2118                                         g_free (parent_name);
2119                                         g_free (parent_real);
2120                                         return NULL;
2121                                 }
2122
2123                                 /* add the dirsep to the end of parent_name */
2124                                 name = g_strdup_printf ("%s%c", parent_real, imap_store->dir_sep);
2125                                 response = camel_imap_command (imap_store, NULL, cancellable, error, "CREATE %G",
2126                                                                name);
2127                                 g_free (name);
2128
2129                                 if (!response) {
2130                                         g_free (parent_name);
2131                                         g_free (parent_real);
2132                                         return NULL;
2133                                 } else
2134                                         camel_imap_response_free (imap_store, response);
2135                         }
2136
2137                         g_free (parent_real);
2138                 }
2139
2140                 g_free (parent_name);
2141
2142                 folder_real = camel_imap_store_summary_path_to_full (imap_store->summary, folder_name, imap_store->dir_sep);
2143                 response = camel_imap_command (imap_store, NULL, cancellable, error, "CREATE %G", folder_real);
2144                 if (response) {
2145                         camel_imap_store_summary_add_from_full (imap_store->summary, folder_real, imap_store->dir_sep);
2146
2147                         camel_imap_response_free (imap_store, response);
2148
2149                         response = camel_imap_command (imap_store, NULL, NULL, NULL, "SELECT %F", folder_name);
2150                 }
2151                 g_free (folder_real);
2152                 if (!response) {
2153                         return NULL;
2154                 }
2155         } else if (flags & CAMEL_STORE_FOLDER_EXCL) {
2156                 g_set_error (
2157                         error, CAMEL_ERROR, CAMEL_ERROR_GENERIC,
2158                         _("Cannot create folder '%s': folder exists."),
2159                         folder_name);
2160
2161                 camel_imap_response_free_without_processing (imap_store, response);
2162
2163                 return NULL;
2164         }
2165
2166         storage_path = g_build_filename (user_cache_dir, "folders", NULL);
2167         folder_dir = imap_path_to_physical (storage_path, folder_name);
2168         g_free (storage_path);
2169         new_folder = camel_imap_folder_new (store, folder_name, folder_dir, error);
2170         g_free (folder_dir);
2171         if (new_folder) {
2172                 imap_store->current_folder = g_object_ref (new_folder);
2173                 if (!camel_imap_folder_selected (
2174                         new_folder, response, cancellable, error)) {
2175
2176                         g_object_unref (imap_store->current_folder);
2177                         imap_store->current_folder = NULL;
2178                         g_object_unref (new_folder);
2179                         new_folder = NULL;
2180                 }
2181         }
2182         camel_imap_response_free_without_processing (imap_store, response);
2183
2184         return new_folder;
2185 }
2186
2187 static CamelFolder *
2188 get_folder_offline (CamelStore *store,
2189                     const gchar *folder_name,
2190                     guint32 flags,
2191                     GError **error)
2192 {
2193         CamelImapStore *imap_store = CAMEL_IMAP_STORE (store);
2194         CamelFolder *new_folder = NULL;
2195         CamelStoreInfo *si;
2196         CamelService *service;
2197         const gchar *user_cache_dir;
2198
2199         service = CAMEL_SERVICE (store);
2200         user_cache_dir = camel_service_get_user_cache_dir (service);
2201
2202         si = camel_store_summary_path ((CamelStoreSummary *) imap_store->summary, folder_name);
2203         if (si) {
2204                 gchar *folder_dir, *storage_path;
2205
2206                 /* Note: Although the INBOX is defined to be case-insensitive in the IMAP RFC
2207                  * it is still up to the server how to acutally name it in a LIST response. Since
2208                  * we stored the name as the server provided it us in the summary we take that name
2209                  * to look up the folder.
2210                  * But for the on-disk cache we do always capitalize the Inbox no matter what the
2211                  * server provided.
2212                  */
2213                 if (!g_ascii_strcasecmp (folder_name, "INBOX"))
2214                         folder_name = "INBOX";
2215
2216                 storage_path = g_build_filename (user_cache_dir, "folders", NULL);
2217                 folder_dir = imap_path_to_physical (storage_path, folder_name);
2218                 g_free (storage_path);
2219                 new_folder = camel_imap_folder_new (store, folder_name, folder_dir, error);
2220                 g_free (folder_dir);
2221
2222                 camel_store_summary_info_free ((CamelStoreSummary *) imap_store->summary, si);
2223         } else {
2224                 g_set_error (
2225                         error, CAMEL_STORE_ERROR,
2226                         CAMEL_STORE_ERROR_NO_FOLDER,
2227                         _("No such folder %s"), folder_name);
2228         }
2229
2230         return new_folder;
2231 }
2232
2233 static gboolean
2234 imap_store_delete_folder_sync (CamelStore *store,
2235                                const gchar *folder_name,
2236                                GCancellable *cancellable,
2237                                GError **error)
2238 {
2239         CamelImapStore *imap_store = CAMEL_IMAP_STORE (store);
2240         CamelImapResponse *response;
2241         gboolean success = TRUE;
2242
2243         if (!camel_imap_store_connected (imap_store, error))
2244                 return FALSE;
2245
2246         /* make sure this folder isn't currently SELECTed */
2247         response = camel_imap_command (imap_store, NULL, cancellable, error, "SELECT INBOX");
2248         if (!response)
2249                 return FALSE;
2250
2251         camel_imap_response_free_without_processing (imap_store, response);
2252         if (imap_store->current_folder)
2253                 g_object_unref (imap_store->current_folder);
2254         /* no need to actually create a CamelFolder for INBOX */
2255         imap_store->current_folder = NULL;
2256
2257         response = camel_imap_command(imap_store, NULL, cancellable, error, "DELETE %F", folder_name);
2258         if (response) {
2259                 camel_imap_response_free (imap_store, response);
2260                 imap_forget_folder (imap_store, folder_name, NULL);
2261         } else
2262                 success = FALSE;
2263
2264         return success;
2265 }
2266
2267 static void
2268 manage_subscriptions (CamelStore *store,
2269                       const gchar *old_name,
2270                       gboolean subscribe,
2271                       GCancellable *cancellable)
2272 {
2273         CamelImapStore *imap_store = CAMEL_IMAP_STORE (store);
2274         CamelStoreInfo *si;
2275         gint olen = strlen (old_name);
2276         const gchar *path;
2277         gint i, count;
2278
2279         count = camel_store_summary_count ((CamelStoreSummary *) imap_store->summary);
2280         for (i = 0; i < count; i++) {
2281                 si = camel_store_summary_index ((CamelStoreSummary *) imap_store->summary, i);
2282                 if (si) {
2283                         path = camel_store_info_path (imap_store->summary, si);
2284                         if (strncmp (path, old_name, olen) == 0) {
2285                                 if (subscribe)
2286                                         imap_store_subscribe_folder_sync (
2287                                                 CAMEL_SUBSCRIBABLE (store),
2288                                                 path, cancellable, NULL);
2289                                 else
2290                                         imap_store_unsubscribe_folder_sync (
2291                                                 CAMEL_SUBSCRIBABLE (store),
2292                                                 path, cancellable, NULL);
2293                         }
2294                         camel_store_summary_info_free ((CamelStoreSummary *) imap_store->summary, si);
2295                 }
2296         }
2297 }
2298
2299 static void
2300 rename_folder_info (CamelImapStore *imap_store,
2301                     const gchar *old_name,
2302                     const gchar *new_name)
2303 {
2304         gint i, count;
2305         CamelStoreInfo *si;
2306         gint olen = strlen (old_name);
2307         const gchar *path;
2308         gchar *npath, *nfull;
2309
2310         count = camel_store_summary_count ((CamelStoreSummary *) imap_store->summary);
2311         for (i = 0; i < count; i++) {
2312                 si = camel_store_summary_index ((CamelStoreSummary *) imap_store->summary, i);
2313                 if (si == NULL)
2314                         continue;
2315                 path = camel_store_info_path (imap_store->summary, si);
2316                 if (strncmp (path, old_name, olen) == 0) {
2317                         if (strlen (path) > olen)
2318                                 npath = g_strdup_printf("%s/%s", new_name, path+olen+1);
2319                         else
2320                                 npath = g_strdup (new_name);
2321                         nfull = camel_imap_store_summary_path_to_full (imap_store->summary, npath, imap_store->dir_sep);
2322
2323                         /* workaround for broken server (courier uses '.') that doesn't rename
2324                          * subordinate folders as required by rfc 2060 */
2325                         if (imap_store->dir_sep == '.') {
2326                                 CamelImapResponse *response;
2327
2328                                 response = camel_imap_command (imap_store, NULL, NULL, NULL, "RENAME %F %G", path, nfull);
2329                                 if (response)
2330                                         camel_imap_response_free (imap_store, response);
2331                         }
2332
2333                         camel_store_info_set_string ((CamelStoreSummary *) imap_store->summary, si, CAMEL_STORE_INFO_PATH, npath);
2334                         camel_store_info_set_string ((CamelStoreSummary *) imap_store->summary, si, CAMEL_IMAP_STORE_INFO_FULL_NAME, nfull);
2335
2336                         camel_store_summary_touch ((CamelStoreSummary *) imap_store->summary);
2337                         g_free (nfull);
2338                         g_free (npath);
2339                 }
2340                 camel_store_summary_info_free ((CamelStoreSummary *) imap_store->summary, si);
2341         }
2342 }
2343
2344 static gboolean
2345 imap_store_rename_folder_sync (CamelStore *store,
2346                                const gchar *old_name,
2347                                const gchar *new_name_in,
2348                                GCancellable *cancellable,
2349                                GError **error)
2350 {
2351         CamelImapStore *imap_store = CAMEL_IMAP_STORE (store);
2352         CamelImapResponse *response;
2353         CamelService *service;
2354         CamelSettings *settings;
2355         const gchar *user_cache_dir;
2356         gchar *oldpath, *newpath, *storage_path;
2357         gboolean use_subscriptions;
2358         gboolean success = TRUE;
2359
2360         service = CAMEL_SERVICE (store);
2361         settings = camel_service_get_settings (service);
2362         user_cache_dir = camel_service_get_user_cache_dir (service);
2363
2364         use_subscriptions = camel_imap_settings_get_use_subscriptions (
2365                 CAMEL_IMAP_SETTINGS (settings));
2366
2367         if (!camel_imap_store_connected (imap_store, error)) {
2368                 success = FALSE;
2369                 goto fail;
2370         }
2371
2372         /* make sure this folder isn't currently SELECTed - it's
2373          * actually possible to rename INBOX but if you do another
2374          * INBOX will immediately be created by the server */
2375         response = camel_imap_command (imap_store, NULL, cancellable, error, "SELECT INBOX");
2376         if (!response) {
2377                 success = FALSE;
2378                 goto fail;
2379         }
2380
2381         camel_imap_response_free_without_processing (imap_store, response);
2382         if (imap_store->current_folder)
2383                 g_object_unref (imap_store->current_folder);
2384         /* no need to actually create a CamelFolder for INBOX */
2385         imap_store->current_folder = NULL;
2386
2387         imap_store->renaming = TRUE;
2388         if (use_subscriptions)
2389                 manage_subscriptions (
2390                         store, old_name, FALSE, cancellable);
2391
2392         response = camel_imap_command (imap_store, NULL, cancellable, error, "RENAME %F %F", old_name, new_name_in);
2393         if (!response) {
2394                 if (use_subscriptions)
2395                         manage_subscriptions (
2396                                 store, old_name, TRUE, cancellable);
2397                 success = FALSE;
2398                 goto fail;
2399         }
2400
2401         camel_imap_response_free (imap_store, response);
2402
2403         /* rename summary, and handle broken server */
2404         rename_folder_info (imap_store, old_name, new_name_in);
2405
2406         if (use_subscriptions)
2407                 manage_subscriptions (
2408                         store, new_name_in, TRUE, cancellable);
2409
2410         storage_path = g_build_filename (user_cache_dir, "folders", NULL);
2411         oldpath = imap_path_to_physical (storage_path, old_name);
2412         newpath = imap_path_to_physical (storage_path, new_name_in);
2413
2414         /* So do we care if this didn't work?  Its just a cache? */
2415         if (g_rename (oldpath, newpath) == -1) {
2416                 g_warning ("Could not rename message cache '%s' to '%s': %s: cache reset",
2417                            oldpath, newpath, g_strerror (errno));
2418         }
2419
2420         if (CAMEL_STORE (imap_store)->folders) {
2421                 CamelFolder *folder;
2422
2423                 folder = camel_object_bag_get (CAMEL_STORE (imap_store)->folders, old_name);
2424                 if (folder) {
2425                         CamelImapFolder *imap_folder = CAMEL_IMAP_FOLDER (folder);
2426
2427                         if (imap_folder && imap_folder->journal) {
2428                                 gchar *folder_dir = imap_path_to_physical (storage_path, new_name_in);
2429                                 gchar *path = g_strdup_printf ("%s/journal", folder_dir);
2430
2431                                 camel_offline_journal_set_filename (imap_folder->journal, path);
2432
2433                                 g_free (path);
2434                                 g_free (folder_dir);
2435                         }
2436
2437                         g_object_unref (folder);
2438                 }
2439         }
2440
2441         g_free (storage_path);
2442         g_free (oldpath);
2443         g_free (newpath);
2444 fail:
2445         imap_store->renaming = FALSE;
2446
2447         return success;
2448 }
2449
2450 static CamelFolderInfo *
2451 imap_store_create_folder_sync (CamelStore *store,
2452                                const gchar *parent_name,
2453                                const gchar *folder_name,
2454                                GCancellable *cancellable,
2455                                GError **error)
2456 {
2457         CamelImapStore *imap_store = CAMEL_IMAP_STORE (store);
2458         gchar *full_name, *resp, *thisone, *parent_real, *real_name;
2459         CamelImapResponse *response;
2460         CamelFolderInfo *root = NULL;
2461         gboolean need_convert;
2462         gint i = 0, flags;
2463         const gchar *c;
2464
2465         if (!camel_offline_store_get_online (CAMEL_OFFLINE_STORE (store))) {
2466                 g_set_error (
2467                         error, CAMEL_SERVICE_ERROR,
2468                         CAMEL_SERVICE_ERROR_UNAVAILABLE,
2469                         _("You must be working online to complete this operation"));
2470                 return NULL;
2471         }
2472
2473         if (!parent_name)
2474                 parent_name = "";
2475
2476         c = folder_name;
2477         while (*c && *c != imap_store->dir_sep && !strchr ("#%*", *c))
2478                 c++;
2479
2480         if (*c != '\0') {
2481                 g_set_error (
2482                         error, CAMEL_FOLDER_ERROR,
2483                         CAMEL_FOLDER_ERROR_INVALID_PATH,
2484                         _("The folder name \"%s\" is invalid because it contains the character \"%c\""),
2485                         folder_name, *c);
2486                 return NULL;
2487         }
2488
2489         /* check if the parent allows inferiors */
2490
2491         /* FIXME: use storesummary directly */
2492         parent_real = camel_imap_store_summary_full_from_path (imap_store->summary, parent_name);
2493         if (parent_real == NULL) {
2494                 g_set_error (
2495                         error, CAMEL_FOLDER_ERROR,
2496                         CAMEL_FOLDER_ERROR_INVALID_STATE,
2497                         _("Unknown parent folder: %s"), parent_name);
2498                 return NULL;
2499         }
2500
2501         need_convert = FALSE;
2502         response = camel_imap_command (imap_store, NULL, cancellable, error, "LIST \"\" %G",
2503                                        parent_real);
2504         if (!response) /* whoa, this is bad */ {
2505                 g_free (parent_real);
2506                 return NULL;
2507         }
2508
2509         /* FIXME: does not handle unexpected circumstances very well */
2510         for (i = 0; i < response->untagged->len && !need_convert; i++) {
2511                 resp = response->untagged->pdata[i];
2512
2513                 if (!imap_parse_list_response (imap_store, resp, &flags, NULL, &thisone))
2514                         continue;
2515
2516                 if (strcmp (thisone, parent_name) == 0) {
2517                         if (flags & CAMEL_FOLDER_NOINFERIORS)
2518                                 need_convert = TRUE;
2519                 }
2520
2521                 g_free (thisone);
2522         }
2523
2524         camel_imap_response_free (imap_store, response);
2525
2526         /* if not, check if we can delete it and recreate it */
2527         if (need_convert) {
2528                 struct imap_status_item *items, *item;
2529                 guint32 messages = 0;
2530                 gchar *name;
2531
2532                 item = items = get_folder_status (imap_store, parent_name, "MESSAGES");
2533                 while (item != NULL) {
2534                         if (!g_ascii_strcasecmp (item->name, "MESSAGES")) {
2535                                 messages = item->value;
2536                                 break;
2537                         }
2538
2539                         item = item->next;
2540                 }
2541
2542                 imap_status_item_free (items);
2543
2544                 if (messages > 0) {
2545                         g_set_error (
2546                                 error, CAMEL_FOLDER_ERROR,
2547                                 CAMEL_FOLDER_ERROR_INVALID_STATE,
2548                                 _("The parent folder is not allowed to contain subfolders"));
2549                         g_free (parent_real);
2550                         return NULL;
2551                 }
2552
2553                 /* delete the old parent and recreate it */
2554                 if (!imap_store_delete_folder_sync (store, parent_name, cancellable, error))
2555                         return NULL;
2556
2557                 /* add the dirsep to the end of parent_name */
2558                 name = g_strdup_printf ("%s%c", parent_real, imap_store->dir_sep);
2559                 response = camel_imap_command (imap_store, NULL, cancellable, error, "CREATE %G",
2560                                                name);
2561                 g_free (name);
2562
2563                 if (!response) {
2564                         g_free (parent_real);
2565                         return NULL;
2566                 } else
2567                         camel_imap_response_free (imap_store, response);
2568
2569                 root = imap_build_folder_info (imap_store, parent_name);
2570         }
2571
2572         /* ok now we can create the folder */
2573         real_name = camel_imap_store_summary_path_to_full (imap_store->summary, folder_name, imap_store->dir_sep);
2574         full_name = imap_concat (imap_store, parent_real, real_name);
2575         g_free (real_name);
2576         response = camel_imap_command (imap_store, NULL, cancellable, error, "CREATE %G", full_name);
2577
2578         if (response) {
2579                 CamelImapStoreInfo *si;
2580                 CamelFolderInfo *fi;
2581
2582                 camel_imap_response_free (imap_store, response);
2583
2584                 si = camel_imap_store_summary_add_from_full (imap_store->summary, full_name, imap_store->dir_sep);
2585                 camel_store_summary_save ((CamelStoreSummary *) imap_store->summary);
2586                 fi = imap_build_folder_info (imap_store, camel_store_info_path (imap_store->summary, si));
2587                 fi->flags |= CAMEL_FOLDER_NOCHILDREN;
2588                 if (root) {
2589                         root->child = fi;
2590                         fi->parent = root;
2591                 } else {
2592                         root = fi;
2593                 }
2594                 camel_store_folder_created (store, root);
2595         } else if (root) {
2596                 /* need to re-recreate the folder we just deleted */
2597                 camel_store_folder_created (store, root);
2598                 camel_folder_info_free (root);
2599                 root = NULL;
2600         }
2601
2602         g_free (full_name);
2603         g_free (parent_real);
2604
2605         return root;
2606 }
2607
2608 static CamelFolderInfo *
2609 parse_list_response_as_folder_info (CamelImapStore *imap_store,
2610                                     const gchar *response)
2611 {
2612         CamelFolderInfo *fi;
2613         gint flags;
2614         gchar sep, *dir;
2615         CamelImapStoreInfo *si;
2616         guint32 newflags;
2617
2618         if (!imap_parse_list_response (imap_store, response, &flags, &sep, &dir))
2619                 return NULL;
2620
2621         /* FIXME: should use imap_build_folder_info, note the differences with param setting tho */
2622
2623         si = camel_imap_store_summary_add_from_full (imap_store->summary, dir, sep ? sep : '/');
2624         g_free (dir);
2625         if (si == NULL)
2626                 return NULL;
2627
2628         newflags = (si->info.flags & (CAMEL_STORE_INFO_FOLDER_SUBSCRIBED | CAMEL_STORE_INFO_FOLDER_CHECK_FOR_NEW)) | (flags & ~CAMEL_STORE_INFO_FOLDER_SUBSCRIBED);
2629         if (si->info.flags != newflags) {
2630                 si->info.flags = newflags;
2631                 camel_store_summary_touch ((CamelStoreSummary *) imap_store->summary);
2632         }
2633
2634         flags = (flags & ~CAMEL_FOLDER_SUBSCRIBED) | (si->info.flags & CAMEL_STORE_FOLDER_INFO_SUBSCRIBED);
2635
2636         fi = camel_folder_info_new ();
2637         fi->full_name = g_strdup (camel_store_info_path (imap_store->summary, si));
2638         if (!g_ascii_strcasecmp(fi->full_name, "inbox")) {
2639                 flags |= CAMEL_FOLDER_SYSTEM | CAMEL_FOLDER_TYPE_INBOX;
2640                 fi->display_name = g_strdup (_("Inbox"));
2641         } else
2642                 fi->display_name = g_strdup (camel_store_info_name (imap_store->summary, si));
2643
2644         /* HACK: some servers report noinferiors for all folders (uw-imapd)
2645          * We just translate this into nochildren, and let the imap layer enforce
2646          * it.  See create folder */
2647         if (flags & CAMEL_FOLDER_NOINFERIORS)
2648                 flags = (flags & ~CAMEL_FOLDER_NOINFERIORS) | CAMEL_FOLDER_NOCHILDREN;
2649         fi->flags = flags;
2650
2651         fi->total = -1;
2652         fi->unread = -1;
2653
2654         return fi;
2655 }
2656
2657 static gint imap_match_pattern (CamelImapStoreNamespace *ns, const gchar *pattern, const gchar *name)
2658 {
2659         gchar p, n, dir_sep;
2660
2661         if (!ns)
2662                 return TRUE;
2663
2664         dir_sep = ns->sep;
2665
2666         p = *pattern++;
2667         n = *name++;
2668         while (n && p) {
2669                 if (n == p) {
2670                         p = *pattern++;
2671                         n = *name++;
2672                 } else if (p == '%') {
2673                         if (n != dir_sep) {
2674                                 n = *name++;
2675                         } else {
2676                                 p = *pattern++;
2677                         }
2678                 } else if (p == '*') {
2679                         return TRUE;
2680                 } else
2681                         return FALSE;
2682         }
2683
2684         return n == 0 && (p == '%' || p == 0);
2685 }
2686
2687 /* imap needs to treat inbox case insensitive */
2688 /* we'll assume the names are normalized already */
2689 static guint folder_hash (gconstpointer ap)
2690 {
2691         const gchar *a = ap;
2692
2693         if (g_ascii_strcasecmp(a, "INBOX") == 0)
2694                 a = "INBOX";
2695
2696         return g_str_hash (a);
2697 }
2698
2699 static gint folder_eq (gconstpointer ap, gconstpointer bp)
2700 {
2701         const gchar *a = ap;
2702         const gchar *b = bp;
2703
2704         if (g_ascii_strcasecmp(a, "INBOX") == 0)
2705                 a = "INBOX";
2706         if (g_ascii_strcasecmp(b, "INBOX") == 0)
2707                 b = "INBOX";
2708
2709         return g_str_equal (a, b);
2710 }
2711
2712 static void
2713 get_folders_free (gpointer k,
2714                   gpointer v,
2715                   gpointer d)
2716 {
2717         camel_folder_info_free (v);
2718 }
2719
2720 static gboolean
2721 get_folders_sync (CamelImapStore *imap_store,
2722                   const gchar *ppattern,
2723                   GCancellable *cancellable,
2724                   GError **error)
2725 {
2726         CamelImapResponse *response;
2727         CamelFolderInfo *fi, *hfi;
2728         gchar *list;
2729         gint i, count, j, k;
2730         GHashTable *present;
2731         CamelStoreInfo *si;
2732         const gchar *pattern = ppattern;
2733         CamelImapStoreNamespace *ns;
2734         gboolean success = TRUE, first_namespace = TRUE;
2735
2736         /* We do a LIST followed by LSUB, and merge the results.  LSUB may not be a strict
2737          * subset of LIST for some servers, so we can't use either or separately */
2738         present = g_hash_table_new (folder_hash, folder_eq);
2739
2740         if (!pattern)
2741                 pattern = "";
2742
2743         for (ns = imap_store->summary->namespace; ns; ns = ns->next, first_namespace = FALSE) {
2744                 for (k = 0; k < 2; k++) {
2745                         gchar *tmp = NULL;
2746
2747                         if (!ppattern) {
2748                                 if (!ns->full_name || !*ns->full_name) {
2749                                         if (k == 1)
2750                                                 break;
2751                                         tmp = g_strdup ("*");
2752                                 } else if (k == 0)
2753                                         tmp = g_strdup_printf ("%s%c", ns->full_name, ns->sep);
2754                                 else
2755                                         tmp = g_strdup_printf ("%s%c*", ns->full_name, ns->sep);
2756                                 pattern = tmp;
2757                         }
2758
2759                         for (j = 0; j < 2; j++) {
2760                                 response = camel_imap_command (imap_store, NULL, cancellable, first_namespace ? error : NULL,
2761                                                                 "%s \"\" %G", j==1 ? "LSUB" : "LIST",
2762                                                                 pattern);
2763                                 if (!response) {
2764                                         success = FALSE;
2765                                         g_free (tmp);
2766                                         goto fail;
2767                                 }
2768
2769                                 for (i = 0; i < response->untagged->len; i++) {
2770                                         list = response->untagged->pdata[i];
2771                                         fi = parse_list_response_as_folder_info (imap_store, list);
2772                                         if (fi && *fi->full_name) {
2773                                                 hfi = g_hash_table_lookup (present, fi->full_name);
2774                                                 if (hfi == NULL) {
2775                                                         if (j == 1) {
2776                                                                 fi->flags |= CAMEL_STORE_INFO_FOLDER_SUBSCRIBED;
2777                                                                 if ((fi->flags & (CAMEL_IMAP_FOLDER_MARKED | CAMEL_IMAP_FOLDER_UNMARKED)))
2778                                                                         imap_store->capabilities |= IMAP_CAPABILITY_useful_lsub;
2779                                                         }
2780                                                         g_hash_table_insert (present, fi->full_name, fi);
2781                                                 } else {
2782                                                         if (j == 1)
2783                                                                 hfi->flags |= CAMEL_STORE_INFO_FOLDER_SUBSCRIBED;
2784                                                         camel_folder_info_free (fi);
2785                                                 }
2786                                         } else if (fi) {
2787                                                 camel_folder_info_free (fi);
2788                                         }
2789                                 }
2790
2791                                 camel_imap_response_free (imap_store, response);
2792                         }
2793
2794                         g_free (tmp);
2795
2796                         /* look for matching only, if ppattern was non-NULL */
2797                         if (ppattern)
2798                                 break;
2799                 }
2800         }
2801
2802         /* Sync summary to match */
2803
2804         /* FIXME: we need to emit folder_create/subscribed/etc events for any new folders */
2805         count = camel_store_summary_count ((CamelStoreSummary *) imap_store->summary);
2806
2807         for (i = 0; i < count; i++) {
2808                 const gchar *full_name;
2809
2810                 si = camel_store_summary_index ((CamelStoreSummary *) imap_store->summary, i);
2811                 if (si == NULL)
2812                         continue;
2813
2814                 full_name = camel_imap_store_info_full_name (imap_store->summary, si);
2815                 if (!full_name || !*full_name) {
2816                         camel_store_summary_info_free ((CamelStoreSummary *) imap_store->summary, si);
2817                         continue;
2818                 }
2819
2820                 if (!ppattern || imap_match_pattern (camel_imap_store_summary_namespace_find_full (imap_store->summary, full_name), pattern, full_name)) {
2821                         if ((fi = g_hash_table_lookup (present, camel_store_info_path (imap_store->summary, si))) != NULL) {
2822                                 if (((fi->flags ^ si->flags) & CAMEL_STORE_INFO_FOLDER_SUBSCRIBED)) {
2823                                         si->flags = (si->flags & ~CAMEL_FOLDER_SUBSCRIBED) | (fi->flags & CAMEL_FOLDER_SUBSCRIBED);
2824                                         camel_store_summary_touch ((CamelStoreSummary *) imap_store->summary);
2825
2826                                         camel_store_folder_created (CAMEL_STORE (imap_store), fi);
2827                                         camel_subscribable_folder_subscribed (CAMEL_SUBSCRIBABLE (imap_store), fi);
2828                                 }
2829                         } else {
2830                                 gchar *dup_folder_name = g_strdup (camel_store_info_path (imap_store->summary, si));
2831
2832                                 if (dup_folder_name) {
2833                                         imap_folder_effectively_unsubscribed (imap_store, dup_folder_name, NULL);
2834                                         imap_forget_folder (imap_store, dup_folder_name, NULL);
2835
2836                                         g_free (dup_folder_name);
2837                                 } else {
2838                                         camel_store_summary_remove ((CamelStoreSummary *) imap_store->summary, si);
2839                                 }
2840
2841                                 count--;
2842                                 i--;
2843                         }
2844                 }
2845                 camel_store_summary_info_free ((CamelStoreSummary *) imap_store->summary, si);
2846         }
2847 fail:
2848         g_hash_table_foreach (present, get_folders_free, NULL);
2849         g_hash_table_destroy (present);
2850
2851         return success;
2852 }
2853
2854 #if 0
2855 static void
2856 dumpfi (CamelFolderInfo *fi)
2857 {
2858         gint depth;
2859         CamelFolderInfo *n = fi;
2860
2861         if (fi == NULL)
2862                 return;
2863
2864         depth = 0;
2865         while (n->parent) {
2866                 depth++;
2867                 n = n->parent;
2868         }
2869
2870         while (fi) {
2871                 printf("%-25s %-25s %*s\n", fi->name, fi->full_name, (gint)(depth*2+strlen(fi->uri)), fi->uri);
2872                 if (fi->child)
2873                         dumpfi (fi->child);
2874                 fi = fi->next;
2875         }
2876 }
2877 #endif
2878
2879 static void
2880 fill_fi (CamelStore *store,
2881          CamelFolderInfo *fi,
2882          guint32 flags)
2883 {
2884         CamelFolder *folder;
2885
2886         folder = camel_object_bag_peek (store->folders, fi->full_name);
2887         if (folder) {
2888                 CamelImapSummary *ims;
2889
2890                 if (folder->summary)
2891                         ims = (CamelImapSummary *) folder->summary;
2892                 else
2893                         ims = (CamelImapSummary *) camel_imap_summary_new (folder);
2894
2895                 fi->unread = camel_folder_summary_get_unread_count ((CamelFolderSummary *) ims);
2896                 fi->total = camel_folder_summary_get_saved_count ((CamelFolderSummary *) ims);
2897
2898                 if (!folder->summary)
2899                         g_object_unref (ims);
2900                 g_object_unref (folder);
2901         }
2902 }
2903
2904 static void
2905 refresh_refresh (CamelSession *session,
2906                  GCancellable *cancellable,
2907                  CamelImapStore *store,
2908                  GError **error)
2909 {
2910         CamelService *service;
2911         CamelSettings *settings;
2912         gchar *namespace;
2913
2914         service = CAMEL_SERVICE (store);
2915         settings = camel_service_get_settings (service);
2916
2917         namespace = camel_imap_settings_dup_namespace (
2918                 CAMEL_IMAP_SETTINGS (settings));
2919
2920         camel_operation_push_message (cancellable,
2921                 _("Retrieving list of folders at '%s'"),
2922                 camel_service_get_display_name (service));
2923
2924         if (!camel_imap_store_connected (store, error))
2925                 goto done;
2926
2927         if (namespace != NULL) {
2928                 if (!get_folders_sync (store, "INBOX", cancellable, error))
2929                         goto done;
2930         } else {
2931                 if (!get_folders_sync (store, "*", cancellable, error))
2932                         goto done;
2933         }
2934
2935         /* look in all namespaces */
2936         get_folders_sync (store, NULL, cancellable, error);
2937
2938         camel_store_summary_save (CAMEL_STORE_SUMMARY (store->summary));
2939
2940 done:
2941         camel_operation_pop_message (cancellable);
2942
2943         g_free (namespace);
2944 }
2945
2946 static CamelFolderInfo *
2947 imap_store_get_folder_info_sync (CamelStore *store,
2948                                  const gchar *top,
2949                                  CamelStoreGetFolderInfoFlags flags,
2950                                  GCancellable *cancellable,
2951                                  GError **error)
2952 {
2953         CamelImapStore *imap_store = CAMEL_IMAP_STORE (store);
2954         CamelFolderInfo *tree = NULL;
2955         CamelService *service;
2956         CamelSession *session;
2957
2958         service = CAMEL_SERVICE (store);
2959         session = camel_service_get_session (service);
2960
2961         /* If we have a list of folders already, use that, but if we haven't
2962          * updated for a while, then trigger an asynchronous rescan.  Otherwise
2963          * we update the list first, and then build it from that */
2964
2965         if (top == NULL)
2966                 top = "";
2967
2968         if (camel_debug("imap:folder_info"))
2969                 printf("get folder info online\n");
2970
2971         if (!camel_offline_store_get_online (CAMEL_OFFLINE_STORE (store))) {
2972                 tree = get_folder_info_offline (store, top, flags, error);
2973                 return tree;
2974         }
2975
2976         if ((flags & CAMEL_STORE_FOLDER_INFO_SUBSCRIBED)
2977             && camel_store_summary_count ((CamelStoreSummary *) imap_store->summary) > 0) {
2978                 time_t now;
2979                 gint ref;
2980
2981                 now = time (NULL);
2982                 ref = now > imap_store->refresh_stamp + 60 * 60 * 1;
2983                 if (ref) {
2984                         ref = now > imap_store->refresh_stamp + 60 * 60 * 1;
2985                         if (ref) {
2986                                 imap_store->refresh_stamp = now;
2987
2988                                 camel_session_submit_job (
2989                                         session, (CamelSessionCallback)
2990                                         refresh_refresh,
2991                                         g_object_ref (store),
2992                                         (GDestroyNotify) g_object_unref);
2993                         }
2994                 }
2995         } else {
2996                 gchar *pattern;
2997                 gint i;
2998                 CamelImapStoreNamespace *ns;
2999
3000                 if (!camel_imap_store_connected ((CamelImapStore *) store, error))
3001                         return NULL;
3002
3003                 if (top[0] == 0) {
3004                         pattern = g_alloca (3);
3005                         pattern[0] = '*';
3006                         pattern[1] = 0;
3007                         i = 0;
3008                 } else {
3009                         gchar *name;
3010
3011                         name = camel_imap_store_summary_full_from_path (imap_store->summary, top);
3012                         if (name == NULL)
3013                                 name = camel_imap_store_summary_path_to_full (imap_store->summary, top, imap_store->dir_sep);
3014
3015                         i = strlen (name);
3016                         pattern = g_alloca (i + 5);
3017                         strcpy (pattern, name);
3018                         g_free (name);
3019                 }
3020
3021                 ns = camel_imap_store_summary_get_main_namespace (imap_store->summary);
3022                 if (!get_folders_sync (imap_store, pattern, cancellable, error))
3023                         return NULL;
3024                 if (pattern[0] != '*' && ns) {
3025                         pattern[i] = ns->sep;
3026                         pattern[i + 1] = (flags & CAMEL_STORE_FOLDER_INFO_RECURSIVE) ? '*':'%';
3027                         pattern[i + 2] = 0;
3028                         get_folders_sync (imap_store, pattern, cancellable, NULL);
3029                 }
3030                 camel_store_summary_save ((CamelStoreSummary *) imap_store->summary);
3031         }
3032
3033         tree = get_folder_info_offline (store, top, flags, error);
3034         return tree;
3035 }
3036
3037 static CamelFolderInfo *
3038 get_folder_info_offline (CamelStore *store,
3039                          const gchar *top,
3040                          guint32 flags,
3041                          GError **error)
3042 {
3043         CamelImapStore *imap_store = CAMEL_IMAP_STORE (store);
3044         gboolean include_inbox = FALSE;
3045         CamelFolderInfo *fi;
3046         CamelService *service;
3047         CamelSettings *settings;
3048         GPtrArray *folders;
3049         gchar *pattern, *name;
3050         gint i;
3051         CamelImapStoreNamespace *main_ns, *ns;
3052         gboolean use_subscriptions;
3053         gchar *junk_path;
3054         gchar *trash_path;
3055
3056         if (camel_debug("imap:folder_info"))
3057                 printf("get folder info offline\n");
3058
3059         service = CAMEL_SERVICE (store);
3060         settings = camel_service_get_settings (service);
3061
3062         use_subscriptions = camel_imap_settings_get_use_subscriptions (
3063                 CAMEL_IMAP_SETTINGS (settings));
3064
3065         junk_path = camel_imap_settings_dup_real_junk_path (
3066                 CAMEL_IMAP_SETTINGS (settings));
3067
3068         /* So we can safely compare strings. */
3069         if (junk_path == NULL)
3070                 junk_path = g_strdup ("");
3071
3072         trash_path = camel_imap_settings_dup_real_trash_path (
3073                 CAMEL_IMAP_SETTINGS (settings));
3074
3075         /* So we can safely compare strings. */
3076         if (trash_path == NULL)
3077                 trash_path = g_strdup ("");
3078
3079         /* FIXME: obey other flags */
3080
3081         folders = g_ptr_array_new ();
3082
3083         if (top == NULL || top[0] == '\0') {
3084                 include_inbox = TRUE;
3085                 top = "";
3086         }
3087
3088         /* get starting point */
3089         if (top[0] == 0) {
3090                 name = g_strdup("");
3091         } else {
3092                 name = camel_imap_store_summary_full_from_path (imap_store->summary, top);
3093                 if (name == NULL)
3094                         name = camel_imap_store_summary_path_to_full (imap_store->summary, top, imap_store->dir_sep);
3095         }
3096
3097         main_ns = camel_imap_store_summary_get_main_namespace (imap_store->summary);
3098         pattern = imap_concat(imap_store, name, "*");
3099
3100         /* folder_info_build will insert parent nodes as necessary and mark
3101          * them as noselect, which is information we actually don't have at
3102          * the moment. So let it do the right thing by bailing out if it's
3103          * not a folder we're explicitly interested in. */
3104
3105         for (i = 0; i < camel_store_summary_count ((CamelStoreSummary *) imap_store->summary); i++) {
3106                 CamelStoreInfo *si = camel_store_summary_index ((CamelStoreSummary *) imap_store->summary, i);
3107                 const gchar *full_name;
3108                 gboolean folder_is_junk;
3109                 gboolean folder_is_trash;
3110
3111                 if (si == NULL)
3112                         continue;
3113
3114                 full_name = camel_imap_store_info_full_name (imap_store->summary, si);
3115                 if (!full_name || !*full_name) {
3116                         camel_store_summary_info_free ((CamelStoreSummary *) imap_store->summary, si);
3117                         continue;
3118                 }
3119
3120                 ns = camel_imap_store_summary_namespace_find_full (imap_store->summary, full_name);
3121
3122                 if ((g_str_equal (name, full_name)
3123                      || imap_match_pattern (ns, pattern, full_name)
3124                      || (include_inbox && !g_ascii_strcasecmp (full_name, "INBOX")))
3125                     && ((ns == main_ns &&
3126                         (!use_subscriptions
3127                            || (flags & CAMEL_STORE_FOLDER_INFO_SUBSCRIBED) == 0))
3128                         || (si->flags & CAMEL_STORE_INFO_FOLDER_SUBSCRIBED)
3129                         || (flags & CAMEL_STORE_FOLDER_INFO_SUBSCRIPTION_LIST) != 0)) {
3130
3131                         fi = imap_build_folder_info (imap_store, camel_store_info_path ((CamelStoreSummary *) imap_store->summary, si));
3132                         fi->unread = si->unread;
3133                         fi->total = si->total;
3134                         fi->flags = si->flags;
3135                         /* HACK: some servers report noinferiors for all folders (uw-imapd)
3136                          * We just translate this into nochildren, and let the imap layer enforce
3137                          * it.  See create folder */
3138                         if (fi->flags & CAMEL_FOLDER_NOINFERIORS)
3139                                 fi->flags = (fi->flags & ~CAMEL_FOLDER_NOINFERIORS) | CAMEL_FOLDER_NOCHILDREN;
3140
3141                         /* blah, this gets lost somewhere, i can't be bothered finding out why */
3142                         if (!g_ascii_strcasecmp(fi->full_name, "inbox"))
3143                                 fi->flags = (fi->flags & ~CAMEL_FOLDER_TYPE_MASK) | CAMEL_FOLDER_TYPE_INBOX;
3144
3145                         folder_is_trash =
3146                                 (fi->flags & CAMEL_FOLDER_TYPE_MASK) == 0 &&
3147                                 g_ascii_strcasecmp (fi->full_name, trash_path) == 0;
3148                         if (folder_is_trash) {
3149                                 fi->flags &= ~CAMEL_FOLDER_TYPE_MASK;
3150                                 fi->flags |= CAMEL_FOLDER_TYPE_TRASH;
3151                         }
3152
3153                         folder_is_junk =
3154                                 (fi->flags & CAMEL_FOLDER_TYPE_MASK) == 0 &&
3155                                 g_ascii_strcasecmp (fi->full_name, junk_path) == 0;
3156
3157                         if (folder_is_junk) {
3158                                 fi->flags &= ~CAMEL_FOLDER_TYPE_MASK;
3159                                 fi->flags |= CAMEL_FOLDER_TYPE_JUNK;
3160                         }
3161
3162                         if (!(si->flags & CAMEL_FOLDER_NOSELECT))
3163                                 fill_fi ((CamelStore *) imap_store, fi, 0);
3164
3165                         if (!fi->child)
3166                                 fi->flags |= CAMEL_FOLDER_NOCHILDREN;
3167                         g_ptr_array_add (folders, fi);
3168                 }
3169                 camel_store_summary_info_free ((CamelStoreSummary *) imap_store->summary, si);
3170         }
3171         g_free (pattern);
3172
3173         fi = camel_folder_info_build (folders, top, '/', TRUE);
3174         g_ptr_array_free (folders, TRUE);
3175         g_free (name);
3176
3177         g_free (junk_path);
3178         g_free (trash_path);
3179
3180         return fi;
3181 }
3182
3183 /* Use this whenever you need to ensure you're both connected and
3184  * online. */
3185 gboolean
3186 camel_imap_store_connected (CamelImapStore *store,
3187                             GError **error)
3188 {
3189         CamelService *service;
3190         CamelOfflineStore *offline_store;
3191         gboolean success;
3192         GError *local_error = NULL;
3193
3194         /* This looks stupid ... because it is.
3195          *
3196          * camel-service-connect will return OK if we connect in 'offline mode',
3197          * which isn't what we want at all.  So we have to recheck we actually
3198          * did connect anyway ... */
3199
3200         if (store->istream != NULL)
3201                 return TRUE;
3202
3203         service = CAMEL_SERVICE (store);
3204         offline_store = CAMEL_OFFLINE_STORE (store);
3205
3206         success =
3207                 camel_offline_store_get_online (offline_store) &&
3208                 camel_service_connect_sync (service, NULL, &local_error);
3209
3210         if (success && store->istream != NULL)
3211                 return TRUE;
3212
3213         if (local_error != NULL)
3214                 g_propagate_error (error, local_error);
3215         else
3216                 g_set_error (
3217                         error, CAMEL_SERVICE_ERROR,
3218                         CAMEL_SERVICE_ERROR_UNAVAILABLE,
3219                         _("You must be working online "
3220                           "to complete this operation"));
3221
3222         return FALSE;
3223 }
3224
3225 gssize
3226 camel_imap_store_readline (CamelImapStore *store,
3227                            gchar **dest,
3228                            GCancellable *cancellable,
3229                            GError **error)
3230 {
3231         CamelStreamBuffer *stream;
3232         gchar linebuf[1024] = {0};
3233         GByteArray *ba;
3234         gssize nread;
3235
3236         g_return_val_if_fail (CAMEL_IS_IMAP_STORE (store), -1);
3237         g_return_val_if_fail (dest, -1);
3238
3239         *dest = NULL;
3240
3241         /* Check for connectedness. Failed (or cancelled) operations will
3242          * close the connection. We can't expect a read to have any
3243          * meaning if we reconnect, so always set an exception.
3244          */
3245
3246         if (!camel_imap_store_connected (store, error))
3247                 return -1;
3248
3249         stream = CAMEL_STREAM_BUFFER (store->istream);
3250
3251         ba = g_byte_array_new ();
3252         while ((nread = camel_stream_buffer_gets (stream, linebuf, sizeof (linebuf), cancellable, error)) > 0) {
3253                 g_byte_array_append (ba, (const guint8 *) linebuf, nread);
3254                 if (linebuf[nread - 1] == '\n')
3255                         break;
3256         }
3257
3258         if (nread <= 0) {
3259                 if (nread == 0)
3260                         g_set_error (
3261                                 error, CAMEL_SERVICE_ERROR,
3262                                 CAMEL_SERVICE_ERROR_UNAVAILABLE,
3263                                 _("Server unexpectedly disconnected"));
3264                 else
3265                         g_prefix_error (
3266                                 error, _("Server unexpectedly disconnected: "));
3267
3268                 /* do not pass cancellable, the connection is gone or
3269                  * the cancellable cancelled, thus there will be no I/O */
3270                 camel_service_disconnect_sync (
3271                         CAMEL_SERVICE (store), FALSE, NULL, NULL);
3272                 g_byte_array_free (ba, TRUE);
3273                 return -1;
3274         }
3275
3276         if (camel_verbose_debug) {
3277                 fprintf (stderr, "received: ");
3278                 fwrite (ba->data, 1, ba->len, stderr);
3279         }
3280
3281         /* camel-imap-command.c:imap_read_untagged expects the CRLFs
3282          * to be stripped off and be nul-terminated *sigh* */
3283         nread = ba->len - 1;
3284         ba->data[nread] = '\0';
3285         if (ba->data[nread - 1] == '\r') {
3286                 ba->data[nread - 1] = '\0';
3287                 nread--;
3288         }
3289
3290         *dest = (gchar *) ba->data;
3291         g_byte_array_free (ba, FALSE);
3292
3293         return nread;
3294 }
3295
3296 static gboolean
3297 imap_can_refresh_folder (CamelStore *store,
3298                          CamelFolderInfo *info,
3299                          GError **error)
3300 {
3301         CamelService *service;
3302         CamelSettings *settings;
3303         gboolean res;
3304         gboolean check_all;
3305         gboolean check_subscribed;
3306         gboolean subscribed;
3307         GError *local_error = NULL;
3308
3309         service = CAMEL_SERVICE (store);
3310         settings = camel_service_get_settings (service);
3311
3312         check_all = camel_imap_settings_get_check_all (
3313                 CAMEL_IMAP_SETTINGS (settings));
3314
3315         check_subscribed = camel_imap_settings_get_check_subscribed (
3316                 CAMEL_IMAP_SETTINGS (settings));
3317
3318         subscribed = ((info->flags & CAMEL_FOLDER_SUBSCRIBED) != 0);
3319
3320         res = CAMEL_STORE_CLASS (camel_imap_store_parent_class)->
3321                 can_refresh_folder (store, info, &local_error) ||
3322                 check_all || (check_subscribed && subscribed);
3323
3324         if (!res && local_error == NULL && CAMEL_IS_IMAP_STORE (store)) {
3325                 CamelStoreInfo *si;
3326                 CamelStoreSummary *sm = CAMEL_STORE_SUMMARY (((CamelImapStore *)(store))->summary);
3327
3328                 if (!sm)
3329                         return FALSE;
3330
3331                 si = camel_store_summary_path (sm, info->full_name);
3332                 if (si) {
3333                         res = (si->flags & CAMEL_STORE_INFO_FOLDER_CHECK_FOR_NEW) != 0 ? TRUE : FALSE;
3334
3335                         camel_store_summary_info_free (sm, si);
3336                 }
3337         }
3338
3339         if (local_error != NULL)
3340                 g_propagate_error (error, local_error);
3341
3342         return res;
3343 }