2005-10-09 [colin] 1.9.15cvs25
[claws.git] / src / imap.c
1 /*
2  * Sylpheed -- a GTK+ based, lightweight, and fast e-mail client
3  * Copyright (C) 1999-2005 Hiroyuki Yamamoto
4  *
5  * This program is free software; you can redistribute it and/or modify
6  * it under the terms of the GNU General Public License as published by
7  * the Free Software Foundation; either version 2 of the License, or
8  * (at your option) any later version.
9  *
10  * This program is distributed in the hope that it will be useful,
11  * but WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13  * GNU General Public License for more details.
14  *
15  * You should have received a copy of the GNU General Public License
16  * along with this program; if not, write to the Free Software
17  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
18  */
19
20 #ifdef HAVE_CONFIG_H
21 #  include "config.h"
22 #endif
23
24 #include "defs.h"
25
26 #include <glib.h>
27 #include <glib/gi18n.h>
28 #include <stdio.h>
29 #include <string.h>
30 #include "imap.h"
31 #include "imap_gtk.h"
32 #include "inc.h"
33
34 #ifdef HAVE_LIBETPAN
35
36 #include <stdlib.h>
37 #include <dirent.h>
38 #include <unistd.h>
39 #include <ctype.h>
40 #include <time.h>
41 #include <errno.h>
42 #if HAVE_ICONV
43 #  include <iconv.h>
44 #endif
45
46 #if USE_OPENSSL
47 #  include "ssl.h"
48 #endif
49
50 #include "folder.h"
51 #include "session.h"
52 #include "procmsg.h"
53 #include "socket.h"
54 #include "recv.h"
55 #include "procheader.h"
56 #include "prefs_account.h"
57 #include "codeconv.h"
58 #include "md5.h"
59 #include "base64.h"
60 #include "utils.h"
61 #include "prefs_common.h"
62 #include "inputdialog.h"
63 #include "log.h"
64 #include "remotefolder.h"
65 #include "alertpanel.h"
66 #include "sylpheed.h"
67 #include "statusbar.h"
68 #include "msgcache.h"
69 #include "imap-thread.h"
70
71 typedef struct _IMAPFolder      IMAPFolder;
72 typedef struct _IMAPSession     IMAPSession;
73 typedef struct _IMAPNameSpace   IMAPNameSpace;
74 typedef struct _IMAPFolderItem  IMAPFolderItem;
75
76 #include "prefs_account.h"
77
78 #define IMAP_FOLDER(obj)        ((IMAPFolder *)obj)
79 #define IMAP_FOLDER_ITEM(obj)   ((IMAPFolderItem *)obj)
80 #define IMAP_SESSION(obj)       ((IMAPSession *)obj)
81
82 struct _IMAPFolder
83 {
84         RemoteFolder rfolder;
85
86         /* list of IMAPNameSpace */
87         GList *ns_personal;
88         GList *ns_others;
89         GList *ns_shared;
90         gchar last_seen_separator;
91         guint refcnt;
92 };
93
94 struct _IMAPSession
95 {
96         Session session;
97
98         gboolean authenticated;
99
100         GSList *capability;
101         gboolean uidplus;
102
103         gchar *mbox;
104         guint cmd_count;
105
106         /* CLAWS */
107         gboolean folder_content_changed;
108         guint exists;
109         Folder * folder;
110 };
111
112 struct _IMAPNameSpace
113 {
114         gchar *name;
115         gchar separator;
116 };
117
118 #define IMAP_SUCCESS    0
119 #define IMAP_SOCKET     2
120 #define IMAP_AUTHFAIL   3
121 #define IMAP_PROTOCOL   4
122 #define IMAP_SYNTAX     5
123 #define IMAP_IOERR      6
124 #define IMAP_ERROR      7
125
126 #define IMAPBUFSIZE     8192
127
128 typedef enum
129 {
130         IMAP_FLAG_SEEN          = 1 << 0,
131         IMAP_FLAG_ANSWERED      = 1 << 1,
132         IMAP_FLAG_FLAGGED       = 1 << 2,
133         IMAP_FLAG_DELETED       = 1 << 3,
134         IMAP_FLAG_DRAFT         = 1 << 4
135 } IMAPFlags;
136
137 #define IMAP_IS_SEEN(flags)     ((flags & IMAP_FLAG_SEEN) != 0)
138 #define IMAP_IS_ANSWERED(flags) ((flags & IMAP_FLAG_ANSWERED) != 0)
139 #define IMAP_IS_FLAGGED(flags)  ((flags & IMAP_FLAG_FLAGGED) != 0)
140 #define IMAP_IS_DELETED(flags)  ((flags & IMAP_FLAG_DELETED) != 0)
141 #define IMAP_IS_DRAFT(flags)    ((flags & IMAP_FLAG_DRAFT) != 0)
142
143
144 #define IMAP4_PORT      143
145 #if USE_OPENSSL
146 #define IMAPS_PORT      993
147 #endif
148
149 #define IMAP_CMD_LIMIT  1000
150
151 struct _IMAPFolderItem
152 {
153         FolderItem item;
154
155         guint lastuid;
156         guint uid_next;
157         GSList *uid_list;
158         gboolean batching;
159
160         time_t use_cache;
161         gint c_messages;
162         guint32 c_uid_next;
163         guint32 c_uid_validity;
164         gint c_unseen;
165 };
166
167 static void imap_folder_init            (Folder         *folder,
168                                          const gchar    *name,
169                                          const gchar    *path);
170
171 static Folder   *imap_folder_new        (const gchar    *name,
172                                          const gchar    *path);
173 static void      imap_folder_destroy    (Folder         *folder);
174
175 static IMAPSession *imap_session_new    (Folder         *folder,
176                                          const PrefsAccount     *account);
177 static void     imap_session_authenticate(IMAPSession           *session,
178                                           const PrefsAccount    *account);
179 static void     imap_session_destroy    (Session        *session);
180
181 static gchar   *imap_fetch_msg          (Folder         *folder, 
182                                          FolderItem     *item, 
183                                          gint            uid);
184 static gchar   *imap_fetch_msg_full     (Folder         *folder, 
185                                          FolderItem     *item, 
186                                          gint            uid,
187                                          gboolean        headers,
188                                          gboolean        body);
189 static gint     imap_add_msg            (Folder         *folder,
190                                          FolderItem     *dest,
191                                          const gchar    *file, 
192                                          MsgFlags       *flags);
193 static gint     imap_add_msgs           (Folder         *folder, 
194                                          FolderItem     *dest,
195                                          GSList         *file_list,
196                                          GRelation      *relation);
197
198 static gint     imap_copy_msg           (Folder         *folder,
199                                          FolderItem     *dest, 
200                                          MsgInfo        *msginfo);
201 static gint     imap_copy_msgs          (Folder         *folder, 
202                                          FolderItem     *dest, 
203                                          MsgInfoList    *msglist, 
204                                          GRelation      *relation);
205
206 static gint     imap_remove_msg         (Folder         *folder, 
207                                          FolderItem     *item, 
208                                          gint            uid);
209 static gint     imap_remove_msgs        (Folder         *folder, 
210                                          FolderItem     *dest, 
211                                          MsgInfoList    *msglist, 
212                                          GRelation      *relation);
213 static gint     imap_remove_all_msg     (Folder         *folder, 
214                                          FolderItem     *item);
215
216 static gboolean imap_is_msg_changed     (Folder         *folder,
217                                          FolderItem     *item, 
218                                          MsgInfo        *msginfo);
219
220 static gint     imap_close              (Folder         *folder, 
221                                          FolderItem     *item);
222
223 static gint     imap_scan_tree          (Folder         *folder);
224
225 static gint     imap_create_tree        (Folder         *folder);
226
227 static FolderItem *imap_create_folder   (Folder         *folder,
228                                          FolderItem     *parent,
229                                          const gchar    *name);
230 static gint     imap_rename_folder      (Folder         *folder,
231                                          FolderItem     *item, 
232                                          const gchar    *name);
233 static gint     imap_remove_folder      (Folder         *folder, 
234                                          FolderItem     *item);
235
236 static FolderItem *imap_folder_item_new (Folder         *folder);
237 static void imap_folder_item_destroy    (Folder         *folder,
238                                          FolderItem     *item);
239
240 static IMAPSession *imap_session_get    (Folder         *folder);
241
242 static gint imap_auth                   (IMAPSession    *session,
243                                          const gchar    *user,
244                                          const gchar    *pass,
245                                          IMAPAuthType    type);
246
247 static gint imap_scan_tree_recursive    (IMAPSession    *session,
248                                          FolderItem     *item);
249
250 static void imap_create_missing_folders (Folder         *folder);
251 static FolderItem *imap_create_special_folder
252                                         (Folder                 *folder,
253                                          SpecialFolderItemType   stype,
254                                          const gchar            *name);
255
256 static gint imap_do_copy_msgs           (Folder         *folder,
257                                          FolderItem     *dest,
258                                          MsgInfoList    *msglist,
259                                          GRelation      *relation);
260
261 static void imap_delete_all_cached_messages     (FolderItem     *item);
262 static void imap_set_batch              (Folder         *folder,
263                                          FolderItem     *item,
264                                          gboolean        batch);
265 static gint imap_set_message_flags      (IMAPSession    *session,
266                                          MsgNumberList  *numlist,
267                                          IMAPFlags       flags,
268                                          gboolean        is_set);
269 static gint imap_select                 (IMAPSession    *session,
270                                          IMAPFolder     *folder,
271                                          const gchar    *path,
272                                          gint           *exists,
273                                          gint           *recent,
274                                          gint           *unseen,
275                                          guint32        *uid_validity,
276                                          gboolean        block);
277 static gint imap_status                 (IMAPSession    *session,
278                                          IMAPFolder     *folder,
279                                          const gchar    *path,
280                                          IMAPFolderItem *item,
281                                          gint           *messages,
282                                          guint32        *uid_next,
283                                          guint32        *uid_validity,
284                                          gint           *unseen,
285                                          gboolean        block);
286
287 static IMAPNameSpace *imap_find_namespace       (IMAPFolder     *folder,
288                                                  const gchar    *path);
289 static gchar imap_get_path_separator            (IMAPFolder     *folder,
290                                                  const gchar    *path);
291 static gchar *imap_get_real_path                (IMAPFolder     *folder,
292                                                  const gchar    *path);
293 static void imap_synchronise            (FolderItem     *item);
294
295 static void imap_free_capabilities      (IMAPSession    *session);
296
297 /* low-level IMAP4rev1 commands */
298 static gint imap_cmd_login      (IMAPSession    *session,
299                                  const gchar    *user,
300                                  const gchar    *pass,
301                                  const gchar    *type);
302 static gint imap_cmd_logout     (IMAPSession    *session);
303 static gint imap_cmd_noop       (IMAPSession    *session);
304 #if USE_OPENSSL
305 static gint imap_cmd_starttls   (IMAPSession    *session);
306 #endif
307 static gint imap_cmd_select     (IMAPSession    *session,
308                                  const gchar    *folder,
309                                  gint           *exists,
310                                  gint           *recent,
311                                  gint           *unseen,
312                                  guint32        *uid_validity,
313                                  gboolean        block);
314 static gint imap_cmd_examine    (IMAPSession    *session,
315                                  const gchar    *folder,
316                                  gint           *exists,
317                                  gint           *recent,
318                                  gint           *unseen,
319                                  guint32        *uid_validity,
320                                  gboolean        block);
321 static gint imap_cmd_create     (IMAPSession    *sock,
322                                  const gchar    *folder);
323 static gint imap_cmd_rename     (IMAPSession    *sock,
324                                  const gchar    *oldfolder,
325                                  const gchar    *newfolder);
326 static gint imap_cmd_delete     (IMAPSession    *session,
327                                  const gchar    *folder);
328 static gint imap_cmd_fetch      (IMAPSession    *sock,
329                                  guint32         uid,
330                                  const gchar    *filename,
331                                  gboolean        headers,
332                                  gboolean        body);
333 static gint imap_cmd_append     (IMAPSession    *session,
334                                  const gchar    *destfolder,
335                                  const gchar    *file,
336                                  IMAPFlags       flags,
337                                  guint32        *new_uid);
338 static gint imap_cmd_copy       (IMAPSession *session,
339                                  struct mailimap_set * set,
340                                  const gchar *destfolder,
341                                  GRelation *uid_mapping);
342 static gint imap_cmd_store      (IMAPSession    *session,
343                                  struct mailimap_set * set,
344                                  IMAPFlags flags,
345                                  int do_add);
346 static gint imap_cmd_expunge    (IMAPSession    *session);
347
348 static void imap_path_separator_subst           (gchar          *str,
349                                                  gchar           separator);
350
351 static gchar *imap_utf8_to_modified_utf7        (const gchar    *from);
352 static gchar *imap_modified_utf7_to_utf8        (const gchar    *mutf7_str);
353
354 static gboolean imap_rename_folder_func         (GNode          *node,
355                                                  gpointer        data);
356 static gint imap_get_num_list                   (Folder         *folder,
357                                                  FolderItem     *item,
358                                                  GSList        **list,
359                                                  gboolean       *old_uids_valid);
360 static GSList *imap_get_msginfos                (Folder         *folder,
361                                                  FolderItem     *item,
362                                                  GSList         *msgnum_list);
363 static MsgInfo *imap_get_msginfo                (Folder         *folder,
364                                                  FolderItem     *item,
365                                                  gint            num);
366 static gboolean imap_scan_required              (Folder         *folder,
367                                                  FolderItem     *item);
368 static void imap_change_flags                   (Folder         *folder,
369                                                  FolderItem     *item,
370                                                  MsgInfo        *msginfo,
371                                                  MsgPermFlags    newflags);
372 static gint imap_get_flags                      (Folder         *folder,
373                                                  FolderItem     *item,
374                                                  MsgInfoList    *msglist,
375                                                  GRelation      *msgflags);
376 static gchar *imap_folder_get_path              (Folder         *folder);
377 static gchar *imap_item_get_path                (Folder         *folder,
378                                                  FolderItem     *item);
379 static MsgInfo *imap_parse_msg(const gchar *file, FolderItem *item);
380
381
382 /* data types conversion libetpan <-> sylpheed */
383 static GSList * imap_list_from_lep(IMAPFolder * folder,
384                                    clist * list, const gchar * real_path);
385 static GSList * imap_get_lep_set_from_numlist(MsgNumberList *numlist);
386 static GSList * imap_get_lep_set_from_msglist(MsgInfoList *msglist);
387 static GSList * imap_uid_list_from_lep(clist * list);
388 static GSList * imap_uid_list_from_lep_tab(carray * list);
389 static MsgInfo *imap_envelope_from_lep(struct imap_fetch_env_info * info,
390                                        FolderItem *item);
391 static void imap_lep_set_free(GSList *seq_list);
392 static struct mailimap_flag_list * imap_flag_to_lep(IMAPFlags flags);
393
394
395 static GHashTable *flags_set_table = NULL;
396 static GHashTable *flags_unset_table = NULL;
397 typedef struct _hashtable_data {
398         IMAPSession *session;
399         GSList *msglist;
400 } hashtable_data;
401
402 static FolderClass imap_class;
403
404 typedef struct _thread_data {
405         gchar *server;
406         gushort port;
407         gboolean done;
408         SockInfo *sock;
409 #ifdef USE_OPENSSL
410         SSLType ssl_type;
411 #endif
412 } thread_data;
413
414 FolderClass *imap_get_class(void)
415 {
416         if (imap_class.idstr == NULL) {
417                 imap_class.type = F_IMAP;
418                 imap_class.idstr = "imap";
419                 imap_class.uistr = "IMAP4";
420
421                 /* Folder functions */
422                 imap_class.new_folder = imap_folder_new;
423                 imap_class.destroy_folder = imap_folder_destroy;
424                 imap_class.scan_tree = imap_scan_tree;
425                 imap_class.create_tree = imap_create_tree;
426
427                 /* FolderItem functions */
428                 imap_class.item_new = imap_folder_item_new;
429                 imap_class.item_destroy = imap_folder_item_destroy;
430                 imap_class.item_get_path = imap_item_get_path;
431                 imap_class.create_folder = imap_create_folder;
432                 imap_class.rename_folder = imap_rename_folder;
433                 imap_class.remove_folder = imap_remove_folder;
434                 imap_class.close = imap_close;
435                 imap_class.get_num_list = imap_get_num_list;
436                 imap_class.scan_required = imap_scan_required;
437
438                 /* Message functions */
439                 imap_class.get_msginfo = imap_get_msginfo;
440                 imap_class.get_msginfos = imap_get_msginfos;
441                 imap_class.fetch_msg = imap_fetch_msg;
442                 imap_class.fetch_msg_full = imap_fetch_msg_full;
443                 imap_class.add_msg = imap_add_msg;
444                 imap_class.add_msgs = imap_add_msgs;
445                 imap_class.copy_msg = imap_copy_msg;
446                 imap_class.copy_msgs = imap_copy_msgs;
447                 imap_class.remove_msg = imap_remove_msg;
448                 imap_class.remove_msgs = imap_remove_msgs;
449                 imap_class.remove_all_msg = imap_remove_all_msg;
450                 imap_class.is_msg_changed = imap_is_msg_changed;
451                 imap_class.change_flags = imap_change_flags;
452                 imap_class.get_flags = imap_get_flags;
453                 imap_class.set_batch = imap_set_batch;
454                 imap_class.synchronise = imap_synchronise;
455 #ifdef USE_PTREAD
456                 pthread_mutex_init(&imap_mutex, NULL);
457 #endif
458         }
459         
460         return &imap_class;
461 }
462
463 static Folder *imap_folder_new(const gchar *name, const gchar *path)
464 {
465         Folder *folder;
466
467         folder = (Folder *)g_new0(IMAPFolder, 1);
468         folder->klass = &imap_class;
469         imap_folder_init(folder, name, path);
470
471         return folder;
472 }
473
474 static void imap_folder_destroy(Folder *folder)
475 {
476         gchar *dir;
477
478         while (imap_folder_get_refcnt(folder) > 0)
479                 gtk_main_iteration();
480         
481         dir = imap_folder_get_path(folder);
482         if (is_dir_exist(dir))
483                 remove_dir_recursive(dir);
484         g_free(dir);
485
486         folder_remote_folder_destroy(REMOTE_FOLDER(folder));
487         imap_done(folder);
488 }
489
490 static void imap_folder_init(Folder *folder, const gchar *name,
491                              const gchar *path)
492 {
493         folder_remote_folder_init((Folder *)folder, name, path);
494 }
495
496 static FolderItem *imap_folder_item_new(Folder *folder)
497 {
498         IMAPFolderItem *item;
499         
500         item = g_new0(IMAPFolderItem, 1);
501         item->lastuid = 0;
502         item->uid_next = 0;
503         item->uid_list = NULL;
504
505         return (FolderItem *)item;
506 }
507
508 static void imap_folder_item_destroy(Folder *folder, FolderItem *_item)
509 {
510         IMAPFolderItem *item = (IMAPFolderItem *)_item;
511
512         g_return_if_fail(item != NULL);
513         g_slist_free(item->uid_list);
514
515         g_free(_item);
516 }
517
518 static gboolean imap_reset_uid_lists_func(GNode *node, gpointer data)
519 {
520         IMAPFolderItem *item = (IMAPFolderItem *)node->data;
521         
522         item->lastuid = 0;
523         item->uid_next = 0;
524         g_slist_free(item->uid_list);
525         item->uid_list = NULL;
526         
527         return FALSE;
528 }
529
530 static void imap_reset_uid_lists(Folder *folder)
531 {
532         if(folder->node == NULL)
533                 return;
534         
535         /* Destroy all uid lists and rest last uid */
536         g_node_traverse(folder->node, G_IN_ORDER, G_TRAVERSE_ALL, -1, imap_reset_uid_lists_func, NULL); 
537 }
538
539 void imap_get_capabilities(IMAPSession *session)
540 {
541         struct mailimap_capability_data *capabilities = NULL;
542         clistiter *cur;
543
544         if (session->capability != NULL)
545                 return;
546
547         capabilities = imap_threaded_capability(session->folder);
548
549         if (capabilities == NULL)
550                 return;
551
552         for(cur = clist_begin(capabilities->cap_list) ; cur != NULL ;
553             cur = clist_next(cur)) {
554                 struct mailimap_capability * cap = 
555                         clist_content(cur);
556                 if (!cap || cap->cap_data.cap_name == NULL)
557                         continue;
558                 session->capability = g_slist_append
559                                 (session->capability,
560                                  g_strdup(cap->cap_data.cap_name));
561                 debug_print("got capa %s\n", cap->cap_data.cap_name);
562         }
563         mailimap_capability_data_free(capabilities);
564 }
565
566 gboolean imap_has_capability(IMAPSession *session, const gchar *cap) 
567 {
568         GSList *cur;
569         for (cur = session->capability; cur; cur = cur->next) {
570                 if (!g_ascii_strcasecmp(cur->data, cap))
571                         return TRUE;
572         }
573         return FALSE;
574 }
575
576 static gint imap_auth(IMAPSession *session, const gchar *user, const gchar *pass,
577                       IMAPAuthType type)
578 {
579         gint ok = IMAP_ERROR;
580         static time_t last_login_err = 0;
581         
582         imap_get_capabilities(session);
583
584         switch(type) {
585         case IMAP_AUTH_CRAM_MD5:
586                 ok = imap_cmd_login(session, user, pass, "CRAM-MD5");
587                 break;
588         case IMAP_AUTH_LOGIN:
589                 ok = imap_cmd_login(session, user, pass, "LOGIN");
590                 break;
591         default:
592                 debug_print("capabilities:\n"
593                                 "\t CRAM-MD5 %d\n"
594                                 "\t LOGIN %d\n", 
595                         imap_has_capability(session, "CRAM-MD5"),
596                         imap_has_capability(session, "LOGIN"));
597                 if (imap_has_capability(session, "CRAM-MD5"))
598                         ok = imap_cmd_login(session, user, pass, "CRAM-MD5");
599                 if (ok == IMAP_ERROR) /* we always try LOGIN before giving up */
600                         ok = imap_cmd_login(session, user, pass, "LOGIN");
601         }
602         if (ok == IMAP_SUCCESS)
603                 session->authenticated = TRUE;
604         else {
605                 gchar *ext_info = NULL;
606                 
607                 if (type == IMAP_AUTH_CRAM_MD5) {
608                         ext_info = _("\n\nCRAM-MD5 logins only work if libetpan has been "
609                                      "compiled with SASL support and the "
610                                      "CRAM-MD5 SASL plugin is installed.");
611                 } else {
612                         ext_info = "";
613                 }
614                 
615                 if (time(NULL) - last_login_err > 10) {
616                         if (!prefs_common.no_recv_err_panel) {
617                                 alertpanel_error(_("Connection to %s failed: "
618                                         "login refused.%s"),
619                                         SESSION(session)->server, ext_info);
620                         } else {
621                                 log_error(_("Connection to %s failed: "
622                                         "login refused.%s\n"),
623                                         SESSION(session)->server, ext_info);
624                         }
625                 }
626                 last_login_err = time(NULL);
627         }
628         return ok;
629 }
630
631 static IMAPSession *imap_reconnect_if_possible(Folder *folder, IMAPSession *session)
632 {
633         RemoteFolder *rfolder = REMOTE_FOLDER(folder);
634         /* Check if this is the first try to establish a
635            connection, if yes we don't try to reconnect */
636         debug_print("reconnecting\n");
637         if (rfolder->session == NULL) {
638                 log_warning(_("Connecting to %s failed"),
639                             folder->account->recv_server);
640                 session_destroy(SESSION(session));
641                 session = NULL;
642         } else {
643                 log_warning(_("IMAP4 connection to %s has been"
644                             " disconnected. Reconnecting...\n"),
645                             folder->account->recv_server);
646                 statusbar_print_all(_("IMAP4 connection to %s has been"
647                             " disconnected. Reconnecting...\n"),
648                             folder->account->recv_server);
649                 SESSION(session)->state = SESSION_DISCONNECTED;
650                 session_destroy(SESSION(session));
651                 /* Clear folders session to make imap_session_get create
652                    a new session, because of rfolder->session == NULL
653                    it will not try to reconnect again and so avoid an
654                    endless loop */
655                 rfolder->session = NULL;
656                 session = imap_session_get(folder);
657                 rfolder->session = SESSION(session);
658                 statusbar_pop_all();
659         }
660         return session;
661 }
662
663 static IMAPSession *imap_session_get(Folder *folder)
664 {
665         RemoteFolder *rfolder = REMOTE_FOLDER(folder);
666         IMAPSession *session = NULL;
667
668         g_return_val_if_fail(folder != NULL, NULL);
669         g_return_val_if_fail(FOLDER_CLASS(folder) == &imap_class, NULL);
670         g_return_val_if_fail(folder->account != NULL, NULL);
671         
672         if (prefs_common.work_offline && !inc_offline_should_override()) {
673                 return NULL;
674         }
675
676         /* Make sure we have a session */
677         if (rfolder->session != NULL) {
678                 session = IMAP_SESSION(rfolder->session);
679         } else {
680                 imap_reset_uid_lists(folder);
681                 session = imap_session_new(folder, folder->account);
682         }
683         if(session == NULL)
684                 return NULL;
685
686         /* Make sure session is authenticated */
687         if (!IMAP_SESSION(session)->authenticated)
688                 imap_session_authenticate(IMAP_SESSION(session), folder->account);
689         
690         if (!IMAP_SESSION(session)->authenticated) {
691                 session_destroy(SESSION(session));
692                 rfolder->session = NULL;
693                 return NULL;
694         }
695
696 #if 0
697         /* Make sure we have parsed the IMAP namespace */
698         imap_parse_namespace(IMAP_SESSION(session),
699                              IMAP_FOLDER(folder));
700 #endif
701
702         /* I think the point of this code is to avoid sending a
703          * keepalive if we've used the session recently and therefore
704          * think it's still alive.  Unfortunately, most of the code
705          * does not yet check for errors on the socket, and so if the
706          * connection drops we don't notice until the timeout expires.
707          * A better solution than sending a NOOP every time would be
708          * for every command to be prepared to retry until it is
709          * successfully sent. -- mbp */
710         if (time(NULL) - SESSION(session)->last_access_time > SESSION_TIMEOUT_INTERVAL) {
711                 /* verify that the session is still alive */
712                 if (imap_cmd_noop(session) != IMAP_SUCCESS) {
713                         debug_print("disconnected!\n");
714                         session = imap_reconnect_if_possible(folder, session);
715                 }
716         }
717
718         rfolder->session = SESSION(session);
719         
720         return IMAP_SESSION(session);
721 }
722
723 static IMAPSession *imap_session_new(Folder * folder,
724                                      const PrefsAccount *account)
725 {
726         IMAPSession *session;
727         gushort port;
728         int r;
729         int authenticated;
730         
731 #ifdef USE_OPENSSL
732         /* FIXME: IMAP over SSL only... */ 
733         SSLType ssl_type;
734
735         port = account->set_imapport ? account->imapport
736                 : account->ssl_imap == SSL_TUNNEL ? IMAPS_PORT : IMAP4_PORT;
737         ssl_type = account->ssl_imap;   
738 #else
739         if (account->ssl_imap != SSL_NONE) {
740                 if (alertpanel_full(_("Insecure connection"),
741                         _("This connection is configured to be secured "
742                           "using SSL, but SSL is not available in this "
743                           "build of Sylpheed-Claws. \n\n"
744                           "Do you want to continue connecting to this "
745                           "server? The communication would not be "
746                           "secure."),
747                           _("Con_tinue connecting"), 
748                           GTK_STOCK_CANCEL, NULL,
749                           FALSE, NULL, ALERT_WARNING,
750                           G_ALERTALTERNATE) != G_ALERTDEFAULT)
751                         return NULL;
752         }
753         port = account->set_imapport ? account->imapport
754                 : IMAP4_PORT;
755 #endif
756
757         imap_init(folder);
758         statusbar_print_all(_("Connecting to IMAP4 server: %s..."), folder->account->recv_server);
759         if (account->set_tunnelcmd) {
760                 r = imap_threaded_connect_cmd(folder,
761                                               account->tunnelcmd,
762                                               account->recv_server,
763                                               port);
764         }
765         else {
766 #ifdef USE_OPENSSL
767                 if (ssl_type == SSL_TUNNEL) {
768                         r = imap_threaded_connect_ssl(folder,
769                                                       account->recv_server,
770                                                       port);
771                 }
772                 else 
773 #endif
774                 {
775                         r = imap_threaded_connect(folder,
776                                                   account->recv_server,
777                                                   port);
778                 }
779         }
780         
781         statusbar_pop_all();
782         if (r == MAILIMAP_NO_ERROR_AUTHENTICATED) {
783                 authenticated = TRUE;
784         }
785         else if (r == MAILIMAP_NO_ERROR_NON_AUTHENTICATED) {
786                 authenticated = FALSE;
787         }
788         else {
789                 if(!prefs_common.no_recv_err_panel) {
790                         alertpanel_error(_("Can't connect to IMAP4 server: %s:%d"),
791                                          account->recv_server, port);
792                 } else {
793                         log_error(_("Can't connect to IMAP4 server: %s:%d\n"),
794                                          account->recv_server, port);
795                 } 
796                 
797                 return NULL;
798         }
799         
800         session = g_new0(IMAPSession, 1);
801         session_init(SESSION(session));
802         SESSION(session)->type             = SESSION_IMAP;
803         SESSION(session)->server           = g_strdup(account->recv_server);
804         SESSION(session)->sock             = NULL;
805         
806         SESSION(session)->destroy          = imap_session_destroy;
807
808         session->capability = NULL;
809         
810         session->authenticated = authenticated;
811         session->mbox = NULL;
812         session->cmd_count = 0;
813         session->folder = folder;
814         IMAP_FOLDER(session->folder)->last_seen_separator = 0;
815
816 #if USE_OPENSSL
817         if (account->ssl_imap == SSL_STARTTLS) {
818                 gint ok;
819
820                 ok = imap_cmd_starttls(session);
821                 if (ok != IMAP_SUCCESS) {
822                         log_warning(_("Can't start TLS session.\n"));
823                         session_destroy(SESSION(session));
824                         return NULL;
825                 }
826
827                 imap_free_capabilities(session);
828                 session->authenticated = FALSE;
829                 session->uidplus = FALSE;
830                 session->cmd_count = 1;
831         }
832 #endif
833         log_message("IMAP connection is %s-authenticated\n",
834                     (session->authenticated) ? "pre" : "un");
835         
836         return session;
837 }
838
839 static void imap_session_authenticate(IMAPSession *session, 
840                                       const PrefsAccount *account)
841 {
842         gchar *pass;
843
844         g_return_if_fail(account->userid != NULL);
845
846         pass = account->passwd;
847         if (!pass) {
848                 gchar *tmp_pass;
849                 tmp_pass = input_dialog_query_password(account->recv_server, account->userid);
850                 if (!tmp_pass)
851                         tmp_pass = g_strdup(""); /* allow empty password */
852                 Xstrdup_a(pass, tmp_pass, {g_free(tmp_pass); return;});
853                 g_free(tmp_pass);
854         }
855         statusbar_print_all(_("Connecting to IMAP4 server %s...\n"),
856                                 account->recv_server);
857         if (imap_auth(session, account->userid, pass, account->imap_auth_type) != IMAP_SUCCESS) {
858                 imap_threaded_disconnect(session->folder);
859                 imap_cmd_logout(session);
860                 statusbar_pop_all();
861                 
862                 return;
863         }
864         statusbar_pop_all();
865         session->authenticated = TRUE;
866 }
867
868 static void imap_session_destroy(Session *session)
869 {
870         if (session->state != SESSION_DISCONNECTED)
871                 imap_threaded_disconnect(IMAP_SESSION(session)->folder);
872         
873         imap_free_capabilities(IMAP_SESSION(session));
874         g_free(IMAP_SESSION(session)->mbox);
875         sock_close(session->sock);
876         session->sock = NULL;
877 }
878
879 static gchar *imap_fetch_msg(Folder *folder, FolderItem *item, gint uid)
880 {
881         return imap_fetch_msg_full(folder, item, uid, TRUE, TRUE);
882 }
883
884 static guint get_size_with_crs(MsgInfo *info) 
885 {
886         FILE *fp = NULL;
887         guint cnt = 0;
888         gchar buf[4096];
889         
890         if (info == NULL)
891                 return -1;
892         
893         fp = procmsg_open_message(info);
894         if (!fp)
895                 return -1;
896         
897         while (fgets(buf, sizeof (buf), fp) != NULL) {
898                 cnt += strlen(buf);
899                 if (!strstr(buf, "\r") && strstr(buf, "\n"))
900                         cnt++;
901         }
902         
903         fclose(fp);
904         return cnt;
905 }
906
907 static gchar *imap_fetch_msg_full(Folder *folder, FolderItem *item, gint uid,
908                                   gboolean headers, gboolean body)
909 {
910         gchar *path, *filename;
911         IMAPSession *session;
912         gint ok;
913
914         g_return_val_if_fail(folder != NULL, NULL);
915         g_return_val_if_fail(item != NULL, NULL);
916
917         if (uid == 0)
918                 return NULL;
919
920         path = folder_item_get_path(item);
921         if (!is_dir_exist(path))
922                 make_dir_hier(path);
923         filename = g_strconcat(path, G_DIR_SEPARATOR_S, itos(uid), NULL);
924         g_free(path);
925
926         if (is_file_exist(filename)) {
927                 /* see whether the local file represents the whole message
928                  * or not. As the IMAP server reports size with \r chars,
929                  * we have to update the local file (UNIX \n only) size */
930                 MsgInfo *msginfo = imap_parse_msg(filename, item);
931                 MsgInfo *cached = msgcache_get_msg(item->cache,uid);
932                 guint have_size = get_size_with_crs(msginfo);
933
934                 if (cached)
935                         debug_print("message %d has been already %scached (%d/%d).\n", uid,
936                                 have_size == cached->size ? "fully ":"",
937                                 have_size, (int)cached->size);
938                 
939                 if (cached && (cached->size == have_size || !body)) {
940                         procmsg_msginfo_free(cached);
941                         procmsg_msginfo_free(msginfo);
942                         file_strip_crs(filename);
943                         return filename;
944                 } else {
945                         procmsg_msginfo_free(cached);
946                         procmsg_msginfo_free(msginfo);
947                 }
948         }
949
950         session = imap_session_get(folder);
951         if (!session) {
952                 g_free(filename);
953                 return NULL;
954         }
955
956         debug_print("IMAP fetching messages\n");
957         ok = imap_select(session, IMAP_FOLDER(folder), item->path,
958                          NULL, NULL, NULL, NULL, FALSE);
959         if (ok != IMAP_SUCCESS) {
960                 g_warning("can't select mailbox %s\n", item->path);
961                 g_free(filename);
962                 return NULL;
963         }
964
965         debug_print("getting message %d...\n", uid);
966         ok = imap_cmd_fetch(session, (guint32)uid, filename, headers, body);
967
968         if (ok != IMAP_SUCCESS) {
969                 g_warning("can't fetch message %d\n", uid);
970                 g_free(filename);
971                 return NULL;
972         }
973
974         file_strip_crs(filename);
975         return filename;
976 }
977
978 static gint imap_add_msg(Folder *folder, FolderItem *dest, 
979                          const gchar *file, MsgFlags *flags)
980 {
981         gint ret;
982         GSList file_list;
983         MsgFileInfo fileinfo;
984
985         g_return_val_if_fail(file != NULL, -1);
986
987         fileinfo.msginfo = NULL;
988         fileinfo.file = (gchar *)file;
989         fileinfo.flags = flags;
990         file_list.data = &fileinfo;
991         file_list.next = NULL;
992
993         ret = imap_add_msgs(folder, dest, &file_list, NULL);
994         return ret;
995 }
996
997 static gint imap_add_msgs(Folder *folder, FolderItem *dest, GSList *file_list,
998                    GRelation *relation)
999 {
1000         gchar *destdir;
1001         IMAPSession *session;
1002         guint32 last_uid = 0;
1003         GSList *cur;
1004         MsgFileInfo *fileinfo;
1005         gint ok;
1006         gint curnum = 0, total = 0;
1007
1008
1009         g_return_val_if_fail(folder != NULL, -1);
1010         g_return_val_if_fail(dest != NULL, -1);
1011         g_return_val_if_fail(file_list != NULL, -1);
1012         
1013         session = imap_session_get(folder);
1014         if (!session) {
1015                 return -1;
1016         }
1017         destdir = imap_get_real_path(IMAP_FOLDER(folder), dest->path);
1018
1019         statusbar_print_all(_("Adding messages..."));
1020         total = g_slist_length(file_list);
1021         for (cur = file_list; cur != NULL; cur = cur->next) {
1022                 IMAPFlags iflags = 0;
1023                 guint32 new_uid = 0;
1024                 gchar *real_file = NULL;
1025                 gboolean file_is_tmp = FALSE;
1026                 fileinfo = (MsgFileInfo *)cur->data;
1027
1028                 statusbar_progress_all(curnum, total, 1);
1029                 curnum++;
1030
1031                 if (fileinfo->flags) {
1032                         if (MSG_IS_MARKED(*fileinfo->flags))
1033                                 iflags |= IMAP_FLAG_FLAGGED;
1034                         if (MSG_IS_REPLIED(*fileinfo->flags))
1035                                 iflags |= IMAP_FLAG_ANSWERED;
1036                         if (!MSG_IS_UNREAD(*fileinfo->flags))
1037                                 iflags |= IMAP_FLAG_SEEN;
1038                 }
1039                 
1040                 if (fileinfo->flags) {
1041                         if ((MSG_IS_QUEUED(*fileinfo->flags) 
1042                              || MSG_IS_DRAFT(*fileinfo->flags))
1043                         && !folder_has_parent_of_type(dest, F_QUEUE)
1044                         && !folder_has_parent_of_type(dest, F_DRAFT)) {
1045                                 real_file = get_tmp_file();
1046                                 file_is_tmp = TRUE;
1047                                 if (procmsg_remove_special_headers(
1048                                                 fileinfo->file, 
1049                                                 real_file) !=0) {
1050                                         g_free(real_file);
1051                                         g_free(destdir);
1052                                         return -1;
1053                                 }
1054                         } 
1055                 }
1056                 if (real_file == NULL)
1057                         real_file = g_strdup(fileinfo->file);
1058                 
1059                 if (folder_has_parent_of_type(dest, F_QUEUE) ||
1060                     folder_has_parent_of_type(dest, F_OUTBOX) ||
1061                     folder_has_parent_of_type(dest, F_DRAFT) ||
1062                     folder_has_parent_of_type(dest, F_TRASH))
1063                         iflags |= IMAP_FLAG_SEEN;
1064
1065                 ok = imap_cmd_append(session, destdir, real_file, iflags, 
1066                                      &new_uid);
1067
1068                 if (ok != IMAP_SUCCESS) {
1069                         g_warning("can't append message %s\n", real_file);
1070                         if (file_is_tmp)
1071                                 g_unlink(real_file);
1072                         g_free(real_file);
1073                         g_free(destdir);
1074                         return -1;
1075                 }
1076
1077                 if (relation != NULL)
1078                         g_relation_insert(relation, fileinfo->msginfo != NULL ? 
1079                                           (gpointer) fileinfo->msginfo : (gpointer) fileinfo,
1080                                           GINT_TO_POINTER(dest->last_num + 1));
1081                 if (last_uid < new_uid)
1082                         last_uid = new_uid;
1083                 if (file_is_tmp)
1084                         g_unlink(real_file);
1085         }
1086         statusbar_progress_all(0,0,0);
1087         statusbar_pop_all();
1088
1089         g_free(destdir);
1090
1091         return last_uid;
1092 }
1093
1094 static gint imap_do_copy_msgs(Folder *folder, FolderItem *dest, 
1095                               MsgInfoList *msglist, GRelation *relation)
1096 {
1097         FolderItem *src;
1098         gchar *destdir;
1099         GSList *seq_list, *cur;
1100         MsgInfo *msginfo;
1101         IMAPSession *session;
1102         gint ok = IMAP_SUCCESS;
1103         GRelation *uid_mapping;
1104         gint last_num = 0;
1105
1106         g_return_val_if_fail(folder != NULL, -1);
1107         g_return_val_if_fail(dest != NULL, -1);
1108         g_return_val_if_fail(msglist != NULL, -1);
1109         
1110         session = imap_session_get(folder);
1111         
1112         if (!session) {
1113                 return -1;
1114         }
1115         msginfo = (MsgInfo *)msglist->data;
1116
1117         src = msginfo->folder;
1118         if (src == dest) {
1119                 g_warning("the src folder is identical to the dest.\n");
1120                 return -1;
1121         }
1122
1123         ok = imap_select(session, IMAP_FOLDER(folder), msginfo->folder->path,
1124                          NULL, NULL, NULL, NULL, FALSE);
1125         if (ok != IMAP_SUCCESS) {
1126                 return ok;
1127         }
1128
1129         destdir = imap_get_real_path(IMAP_FOLDER(folder), dest->path);
1130         seq_list = imap_get_lep_set_from_msglist(msglist);
1131         uid_mapping = g_relation_new(2);
1132         g_relation_index(uid_mapping, 0, g_direct_hash, g_direct_equal);
1133         
1134         statusbar_print_all(_("Copying messages..."));
1135         for (cur = seq_list; cur != NULL; cur = g_slist_next(cur)) {
1136                 struct mailimap_set * seq_set;
1137                 seq_set = cur->data;
1138
1139                 debug_print("Copying messages from %s to %s ...\n",
1140                             src->path, destdir);
1141
1142                 ok = imap_cmd_copy(session, seq_set, destdir, uid_mapping);
1143                 if (ok != IMAP_SUCCESS) {
1144                         g_relation_destroy(uid_mapping);
1145                         imap_lep_set_free(seq_list);
1146                         return -1;
1147                 }
1148         }
1149
1150         for (cur = msglist; cur != NULL; cur = g_slist_next(cur)) {
1151                 MsgInfo *msginfo = (MsgInfo *)cur->data;
1152                 GTuples *tuples;
1153
1154                 tuples = g_relation_select(uid_mapping, 
1155                                            GINT_TO_POINTER(msginfo->msgnum),
1156                                            0);
1157                 if (tuples->len > 0) {
1158                         gint num = GPOINTER_TO_INT(g_tuples_index(tuples, 0, 1));
1159                         g_relation_insert(relation, msginfo,
1160                                           GPOINTER_TO_INT(num));
1161                         if (num > last_num)
1162                                 last_num = num;
1163                 } else
1164                         g_relation_insert(relation, msginfo,
1165                                           GPOINTER_TO_INT(0));
1166                 g_tuples_destroy(tuples);
1167         }
1168         statusbar_pop_all();
1169
1170         g_relation_destroy(uid_mapping);
1171         imap_lep_set_free(seq_list);
1172
1173         g_free(destdir);
1174         
1175         IMAP_FOLDER_ITEM(dest)->lastuid = 0;
1176         IMAP_FOLDER_ITEM(dest)->uid_next = 0;
1177         g_slist_free(IMAP_FOLDER_ITEM(dest)->uid_list);
1178         IMAP_FOLDER_ITEM(dest)->uid_list = NULL;
1179
1180         if (ok == IMAP_SUCCESS)
1181                 return last_num;
1182         else
1183                 return -1;
1184 }
1185
1186 static gint imap_copy_msg(Folder *folder, FolderItem *dest, MsgInfo *msginfo)
1187 {
1188         GSList msglist;
1189
1190         g_return_val_if_fail(msginfo != NULL, -1);
1191
1192         msglist.data = msginfo;
1193         msglist.next = NULL;
1194
1195         return imap_copy_msgs(folder, dest, &msglist, NULL);
1196 }
1197
1198 static gint imap_copy_msgs(Folder *folder, FolderItem *dest, 
1199                     MsgInfoList *msglist, GRelation *relation)
1200 {
1201         MsgInfo *msginfo;
1202         GSList *file_list;
1203         gint ret;
1204
1205         g_return_val_if_fail(folder != NULL, -1);
1206         g_return_val_if_fail(dest != NULL, -1);
1207         g_return_val_if_fail(msglist != NULL, -1);
1208
1209         msginfo = (MsgInfo *)msglist->data;
1210         g_return_val_if_fail(msginfo->folder != NULL, -1);
1211
1212         if (folder == msginfo->folder->folder &&
1213             !folder_has_parent_of_type(msginfo->folder, F_DRAFT) &&
1214             !folder_has_parent_of_type(msginfo->folder, F_QUEUE)) {
1215                 ret = imap_do_copy_msgs(folder, dest, msglist, relation);
1216                 return ret;
1217         }
1218
1219         file_list = procmsg_get_message_file_list(msglist);
1220         g_return_val_if_fail(file_list != NULL, -1);
1221
1222         ret = imap_add_msgs(folder, dest, file_list, relation);
1223
1224         procmsg_message_file_list_free(file_list);
1225
1226         return ret;
1227 }
1228
1229
1230 static gint imap_do_remove_msgs(Folder *folder, FolderItem *dest, 
1231                                 MsgInfoList *msglist, GRelation *relation)
1232 {
1233         gchar *destdir;
1234         GSList *numlist = NULL, *cur;
1235         MsgInfo *msginfo;
1236         IMAPSession *session;
1237         gint ok = IMAP_SUCCESS;
1238         GRelation *uid_mapping;
1239         
1240         g_return_val_if_fail(folder != NULL, -1);
1241         g_return_val_if_fail(dest != NULL, -1);
1242         g_return_val_if_fail(msglist != NULL, -1);
1243
1244         session = imap_session_get(folder);
1245         if (!session) {
1246                 return -1;
1247         }
1248         msginfo = (MsgInfo *)msglist->data;
1249
1250         ok = imap_select(session, IMAP_FOLDER(folder), msginfo->folder->path,
1251                          NULL, NULL, NULL, NULL, FALSE);
1252         if (ok != IMAP_SUCCESS) {
1253                 return ok;
1254         }
1255
1256         destdir = imap_get_real_path(IMAP_FOLDER(folder), dest->path);
1257         for (cur = msglist; cur; cur = cur->next) {
1258                 msginfo = (MsgInfo *)cur->data;
1259                 if (!MSG_IS_DELETED(msginfo->flags))
1260                         numlist = g_slist_append(numlist, GINT_TO_POINTER(msginfo->msgnum));
1261         }
1262
1263         uid_mapping = g_relation_new(2);
1264         g_relation_index(uid_mapping, 0, g_direct_hash, g_direct_equal);
1265
1266         ok = imap_set_message_flags
1267                 (IMAP_SESSION(REMOTE_FOLDER(folder)->session),
1268                 numlist, IMAP_FLAG_DELETED, TRUE);
1269         if (ok != IMAP_SUCCESS) {
1270                 log_warning(_("can't set deleted flags\n"));
1271                 return ok;
1272         }
1273         ok = imap_cmd_expunge(session);
1274         if (ok != IMAP_SUCCESS) {
1275                 log_warning(_("can't expunge\n"));
1276                 return ok;
1277         }
1278         
1279         g_relation_destroy(uid_mapping);
1280         g_slist_free(numlist);
1281
1282         g_free(destdir);
1283
1284         if (ok == IMAP_SUCCESS)
1285                 return 0;
1286         else
1287                 return -1;
1288 }
1289
1290 static gint imap_remove_msgs(Folder *folder, FolderItem *dest, 
1291                     MsgInfoList *msglist, GRelation *relation)
1292 {
1293         MsgInfo *msginfo;
1294
1295         g_return_val_if_fail(folder != NULL, -1);
1296         g_return_val_if_fail(dest != NULL, -1);
1297         if (msglist == NULL)
1298                 return 0;
1299
1300         msginfo = (MsgInfo *)msglist->data;
1301         g_return_val_if_fail(msginfo->folder != NULL, -1);
1302
1303         return imap_do_remove_msgs(folder, dest, msglist, relation);
1304 }
1305
1306 static gint imap_remove_all_msg(Folder *folder, FolderItem *item)
1307 {
1308         GSList *list = folder_item_get_msg_list(item);
1309         gint res = imap_remove_msgs(folder, item, list, NULL);
1310         procmsg_msg_list_free(list);
1311         return res;
1312 }
1313
1314 static gboolean imap_is_msg_changed(Folder *folder, FolderItem *item,
1315                                     MsgInfo *msginfo)
1316 {
1317         /* TODO: properly implement this method */
1318         return FALSE;
1319 }
1320
1321 static gint imap_close(Folder *folder, FolderItem *item)
1322 {
1323         return 0;
1324 }
1325
1326 static gint imap_scan_tree(Folder *folder)
1327 {
1328         FolderItem *item = NULL;
1329         IMAPSession *session;
1330         gchar *root_folder = NULL;
1331
1332         g_return_val_if_fail(folder != NULL, -1);
1333         g_return_val_if_fail(folder->account != NULL, -1);
1334
1335         session = imap_session_get(folder);
1336         if (!session) {
1337                 if (!folder->node) {
1338                         folder_tree_destroy(folder);
1339                         item = folder_item_new(folder, folder->name, NULL);
1340                         item->folder = folder;
1341                         folder->node = item->node = g_node_new(item);
1342                 }
1343                 return -1;
1344         }
1345
1346         if (folder->account->imap_dir && *folder->account->imap_dir) {
1347                 gchar *real_path;
1348                 int r;
1349                 clist * lep_list;
1350
1351                 Xstrdup_a(root_folder, folder->account->imap_dir, {return -1;});
1352                 extract_quote(root_folder, '"');
1353                 subst_char(root_folder,
1354                            imap_get_path_separator(IMAP_FOLDER(folder),
1355                                                    root_folder),
1356                            '/');
1357                 strtailchomp(root_folder, '/');
1358                 real_path = imap_get_real_path
1359                         (IMAP_FOLDER(folder), root_folder);
1360                 debug_print("IMAP root directory: %s\n", real_path);
1361
1362                 /* check if root directory exist */
1363
1364                 r = imap_threaded_list(session->folder, "", real_path,
1365                                        &lep_list);
1366                 if ((r != MAILIMAP_NO_ERROR) || (clist_count(lep_list) == 0)) {
1367                         if (!folder->node) {
1368                                 item = folder_item_new(folder, folder->name, NULL);
1369                                 item->folder = folder;
1370                                 folder->node = item->node = g_node_new(item);
1371                         }
1372                         return -1;
1373                 }
1374                 
1375                 mailimap_list_result_free(lep_list);
1376                 
1377                 g_free(real_path);
1378         }
1379
1380         if (folder->node)
1381                 item = FOLDER_ITEM(folder->node->data);
1382         if (!item || ((item->path || root_folder) &&
1383                       strcmp2(item->path, root_folder) != 0)) {
1384                 folder_tree_destroy(folder);
1385                 item = folder_item_new(folder, folder->name, root_folder);
1386                 item->folder = folder;
1387                 folder->node = item->node = g_node_new(item);
1388         }
1389
1390         imap_scan_tree_recursive(session, FOLDER_ITEM(folder->node->data));
1391         imap_create_missing_folders(folder);
1392
1393         return 0;
1394 }
1395
1396 static gint imap_scan_tree_recursive(IMAPSession *session, FolderItem *item)
1397 {
1398         Folder *folder;
1399         IMAPFolder *imapfolder;
1400         FolderItem *new_item;
1401         GSList *item_list, *cur;
1402         GNode *node;
1403         gchar *real_path;
1404         gchar *wildcard_path;
1405         gchar separator;
1406         gchar wildcard[3];
1407         clist * lep_list;
1408         int r;
1409         
1410         g_return_val_if_fail(item != NULL, -1);
1411         g_return_val_if_fail(item->folder != NULL, -1);
1412         g_return_val_if_fail(item->no_sub == FALSE, -1);
1413
1414         folder = item->folder;
1415         imapfolder = IMAP_FOLDER(folder);
1416
1417         separator = imap_get_path_separator(imapfolder, item->path);
1418
1419         if (folder->ui_func)
1420                 folder->ui_func(folder, item, folder->ui_func_data);
1421
1422         if (item->path) {
1423                 wildcard[0] = separator;
1424                 wildcard[1] = '%';
1425                 wildcard[2] = '\0';
1426                 real_path = imap_get_real_path(imapfolder, item->path);
1427         } else {
1428                 wildcard[0] = '%';
1429                 wildcard[1] = '\0';
1430                 real_path = g_strdup("");
1431         }
1432
1433         Xstrcat_a(wildcard_path, real_path, wildcard,
1434                   {g_free(real_path); return IMAP_ERROR;});
1435         lep_list = NULL;
1436         r = imap_threaded_list(folder, "", wildcard_path, &lep_list);
1437         if (r != MAILIMAP_NO_ERROR) {
1438                 item_list = NULL;
1439         }
1440         else {
1441                 item_list = imap_list_from_lep(imapfolder,
1442                                                lep_list, real_path);
1443                 mailimap_list_result_free(lep_list);
1444         }
1445         
1446         g_free(real_path);
1447
1448         node = item->node->children;
1449         while (node != NULL) {
1450                 FolderItem *old_item = FOLDER_ITEM(node->data);
1451                 GNode *next = node->next;
1452
1453                 new_item = NULL;
1454                 for (cur = item_list; cur != NULL; cur = cur->next) {
1455                         FolderItem *cur_item = FOLDER_ITEM(cur->data);
1456                         if (!strcmp2(old_item->path, cur_item->path)) {
1457                                 new_item = cur_item;
1458                                 break;
1459                         }
1460                 }
1461                 if (!new_item) {
1462                         debug_print("folder '%s' not found. removing...\n",
1463                                     old_item->path);
1464                         folder_item_remove(old_item);
1465                 } else {
1466                         old_item->no_sub = new_item->no_sub;
1467                         old_item->no_select = new_item->no_select;
1468                         if (old_item->no_sub == TRUE && node->children) {
1469                                 debug_print("folder '%s' doesn't have "
1470                                             "subfolders. removing...\n",
1471                                             old_item->path);
1472                                 folder_item_remove_children(old_item);
1473                         }
1474                 }
1475
1476                 node = next;
1477         }
1478
1479         for (cur = item_list; cur != NULL; cur = cur->next) {
1480                 FolderItem *cur_item = FOLDER_ITEM(cur->data);
1481                 new_item = NULL;
1482                 for (node = item->node->children; node != NULL;
1483                      node = node->next) {
1484                         if (!strcmp2(FOLDER_ITEM(node->data)->path,
1485                                      cur_item->path)) {
1486                                 new_item = FOLDER_ITEM(node->data);
1487                                 folder_item_destroy(cur_item);
1488                                 cur_item = NULL;
1489                                 break;
1490                         }
1491                 }
1492                 if (!new_item) {
1493                         new_item = cur_item;
1494                         debug_print("new folder '%s' found.\n", new_item->path);
1495                         folder_item_append(item, new_item);
1496                 }
1497
1498                 if (!strcmp(new_item->path, "INBOX")) {
1499                         new_item->stype = F_INBOX;
1500                         folder->inbox = new_item;
1501                 } else if (!folder_item_parent(item) || item->stype == F_INBOX) {
1502                         gchar *base;
1503
1504                         base = g_path_get_basename(new_item->path);
1505
1506                         if (!folder->outbox && !g_ascii_strcasecmp(base, "Sent")) {
1507                                 new_item->stype = F_OUTBOX;
1508                                 folder->outbox = new_item;
1509                         } else if (!folder->draft && !g_ascii_strcasecmp(base, "Drafts")) {
1510                                 new_item->stype = F_DRAFT;
1511                                 folder->draft = new_item;
1512                         } else if (!folder->queue && !g_ascii_strcasecmp(base, "Queue")) {
1513                                 new_item->stype = F_QUEUE;
1514                                 folder->queue = new_item;
1515                         } else if (!folder->trash && !g_ascii_strcasecmp(base, "Trash")) {
1516                                 new_item->stype = F_TRASH;
1517                                 folder->trash = new_item;
1518                         }
1519                         g_free(base);
1520                 }
1521
1522                 if (new_item->no_sub == FALSE)
1523                         imap_scan_tree_recursive(session, new_item);
1524         }
1525
1526         g_slist_free(item_list);
1527
1528         return IMAP_SUCCESS;
1529 }
1530
1531 static gint imap_create_tree(Folder *folder)
1532 {
1533         g_return_val_if_fail(folder != NULL, -1);
1534         g_return_val_if_fail(folder->node != NULL, -1);
1535         g_return_val_if_fail(folder->node->data != NULL, -1);
1536         g_return_val_if_fail(folder->account != NULL, -1);
1537
1538         imap_scan_tree(folder);
1539         imap_create_missing_folders(folder);
1540
1541         return 0;
1542 }
1543
1544 static void imap_create_missing_folders(Folder *folder)
1545 {
1546         g_return_if_fail(folder != NULL);
1547
1548         if (!folder->inbox)
1549                 folder->inbox = imap_create_special_folder
1550                         (folder, F_INBOX, "INBOX");
1551         if (!folder->trash)
1552                 folder->trash = imap_create_special_folder
1553                         (folder, F_TRASH, "Trash");
1554         if (!folder->queue)
1555                 folder->queue = imap_create_special_folder
1556                         (folder, F_QUEUE, "Queue");
1557         if (!folder->outbox)
1558                 folder->outbox = imap_create_special_folder
1559                         (folder, F_OUTBOX, "Sent");
1560         if (!folder->draft)
1561                 folder->draft = imap_create_special_folder
1562                         (folder, F_DRAFT, "Drafts");
1563 }
1564
1565 static FolderItem *imap_create_special_folder(Folder *folder,
1566                                               SpecialFolderItemType stype,
1567                                               const gchar *name)
1568 {
1569         FolderItem *item;
1570         FolderItem *new_item;
1571
1572         g_return_val_if_fail(folder != NULL, NULL);
1573         g_return_val_if_fail(folder->node != NULL, NULL);
1574         g_return_val_if_fail(folder->node->data != NULL, NULL);
1575         g_return_val_if_fail(folder->account != NULL, NULL);
1576         g_return_val_if_fail(name != NULL, NULL);
1577
1578         item = FOLDER_ITEM(folder->node->data);
1579         new_item = imap_create_folder(folder, item, name);
1580
1581         if (!new_item) {
1582                 g_warning("Can't create '%s'\n", name);
1583                 if (!folder->inbox) return NULL;
1584
1585                 new_item = imap_create_folder(folder, folder->inbox, name);
1586                 if (!new_item)
1587                         g_warning("Can't create '%s' under INBOX\n", name);
1588                 else
1589                         new_item->stype = stype;
1590         } else
1591                 new_item->stype = stype;
1592
1593         return new_item;
1594 }
1595
1596 static gchar *imap_folder_get_path(Folder *folder)
1597 {
1598         gchar *folder_path;
1599
1600         g_return_val_if_fail(folder != NULL, NULL);
1601         g_return_val_if_fail(folder->account != NULL, NULL);
1602
1603         folder_path = g_strconcat(get_imap_cache_dir(),
1604                                   G_DIR_SEPARATOR_S,
1605                                   folder->account->recv_server,
1606                                   G_DIR_SEPARATOR_S,
1607                                   folder->account->userid,
1608                                   NULL);
1609
1610         return folder_path;
1611 }
1612
1613 static gchar *imap_item_get_path(Folder *folder, FolderItem *item)
1614 {
1615         gchar *folder_path, *path;
1616
1617         g_return_val_if_fail(folder != NULL, NULL);
1618         g_return_val_if_fail(item != NULL, NULL);
1619         folder_path = imap_folder_get_path(folder);
1620
1621         g_return_val_if_fail(folder_path != NULL, NULL);
1622         if (folder_path[0] == G_DIR_SEPARATOR) {
1623                 if (item->path)
1624                         path = g_strconcat(folder_path, G_DIR_SEPARATOR_S,
1625                                            item->path, NULL);
1626                 else
1627                         path = g_strdup(folder_path);
1628         } else {
1629                 if (item->path)
1630                         path = g_strconcat(get_home_dir(), G_DIR_SEPARATOR_S,
1631                                            folder_path, G_DIR_SEPARATOR_S,
1632                                            item->path, NULL);
1633                 else
1634                         path = g_strconcat(get_home_dir(), G_DIR_SEPARATOR_S,
1635                                            folder_path, NULL);
1636         }
1637         g_free(folder_path);
1638
1639         return path;
1640 }
1641
1642 static FolderItem *imap_create_folder(Folder *folder, FolderItem *parent,
1643                                const gchar *name)
1644 {
1645         gchar *dirpath, *imap_path;
1646         IMAPSession *session;
1647         FolderItem *new_item;
1648         gchar separator;
1649         gchar *new_name;
1650         const gchar *p;
1651         gint ok;
1652
1653         g_return_val_if_fail(folder != NULL, NULL);
1654         g_return_val_if_fail(folder->account != NULL, NULL);
1655         g_return_val_if_fail(parent != NULL, NULL);
1656         g_return_val_if_fail(name != NULL, NULL);
1657
1658         session = imap_session_get(folder);
1659         if (!session) {
1660                 return NULL;
1661         }
1662
1663         if (!folder_item_parent(parent) && strcmp(name, "INBOX") == 0)
1664                 dirpath = g_strdup(name);
1665         else if (parent->path)
1666                 dirpath = g_strconcat(parent->path, "/", name, NULL);
1667         else if ((p = strchr(name, '/')) != NULL && *(p + 1) != '\0')
1668                 dirpath = g_strdup(name);
1669         else if (folder->account->imap_dir && *folder->account->imap_dir) {
1670                 gchar *imap_dir;
1671
1672                 Xstrdup_a(imap_dir, folder->account->imap_dir, {return NULL;});
1673                 strtailchomp(imap_dir, '/');
1674                 dirpath = g_strconcat(imap_dir, "/", name, NULL);
1675         } else
1676                 dirpath = g_strdup(name);
1677
1678         /* keep trailing directory separator to create a folder that contains
1679            sub folder */
1680         imap_path = imap_utf8_to_modified_utf7(dirpath);
1681         strtailchomp(dirpath, '/');
1682         Xstrdup_a(new_name, name, {
1683                 g_free(dirpath);                
1684                 return NULL;});
1685
1686         strtailchomp(new_name, '/');
1687         separator = imap_get_path_separator(IMAP_FOLDER(folder), imap_path);
1688         imap_path_separator_subst(imap_path, separator);
1689         subst_char(new_name, '/', separator);
1690
1691         if (strcmp(name, "INBOX") != 0) {
1692                 GPtrArray *argbuf;
1693                 gboolean exist = FALSE;
1694                 int r;
1695                 clist * lep_list;
1696                 
1697                 argbuf = g_ptr_array_new();
1698                 r = imap_threaded_list(folder, "", imap_path, &lep_list);
1699                 if (r != MAILIMAP_NO_ERROR) {
1700                         log_warning(_("can't create mailbox: LIST failed\n"));
1701                         g_free(imap_path);
1702                         g_free(dirpath);
1703                         ptr_array_free_strings(argbuf);
1704                         g_ptr_array_free(argbuf, TRUE);
1705                         return NULL;
1706                 }
1707                 
1708                 if (clist_count(lep_list) > 0)
1709                         exist = TRUE;
1710                 
1711                 if (!exist) {
1712                         ok = imap_cmd_create(session, imap_path);
1713                         if (ok != IMAP_SUCCESS) {
1714                                 log_warning(_("can't create mailbox\n"));
1715                                 g_free(imap_path);
1716                                 g_free(dirpath);
1717                                 return NULL;
1718                         }
1719                 }
1720         }
1721
1722         new_item = folder_item_new(folder, new_name, dirpath);
1723         folder_item_append(parent, new_item);
1724         g_free(imap_path);
1725         g_free(dirpath);
1726
1727         dirpath = folder_item_get_path(new_item);
1728         if (!is_dir_exist(dirpath))
1729                 make_dir_hier(dirpath);
1730         g_free(dirpath);
1731
1732         return new_item;
1733 }
1734
1735 static gint imap_rename_folder(Folder *folder, FolderItem *item,
1736                                const gchar *name)
1737 {
1738         gchar *dirpath;
1739         gchar *newpath;
1740         gchar *real_oldpath;
1741         gchar *real_newpath;
1742         gchar *paths[2];
1743         gchar *old_cache_dir;
1744         gchar *new_cache_dir;
1745         IMAPSession *session;
1746         gchar separator;
1747         gint ok;
1748         gint exists, recent, unseen;
1749         guint32 uid_validity;
1750
1751         g_return_val_if_fail(folder != NULL, -1);
1752         g_return_val_if_fail(item != NULL, -1);
1753         g_return_val_if_fail(item->path != NULL, -1);
1754         g_return_val_if_fail(name != NULL, -1);
1755
1756         if (strchr(name, imap_get_path_separator(IMAP_FOLDER(folder), item->path)) != NULL) {
1757                 g_warning(_("New folder name must not contain the namespace "
1758                             "path separator"));
1759                 return -1;
1760         }
1761
1762         session = imap_session_get(folder);
1763         if (!session) {
1764                 return -1;
1765         }
1766         real_oldpath = imap_get_real_path(IMAP_FOLDER(folder), item->path);
1767
1768         g_free(session->mbox);
1769         session->mbox = NULL;
1770         ok = imap_cmd_examine(session, "INBOX",
1771                               &exists, &recent, &unseen, &uid_validity, FALSE);
1772         if (ok != IMAP_SUCCESS) {
1773                 g_free(real_oldpath);
1774                 return -1;
1775         }
1776
1777         separator = imap_get_path_separator(IMAP_FOLDER(folder), item->path);
1778         if (strchr(item->path, G_DIR_SEPARATOR)) {
1779                 dirpath = g_path_get_dirname(item->path);
1780                 newpath = g_strconcat(dirpath, G_DIR_SEPARATOR_S, name, NULL);
1781                 g_free(dirpath);
1782         } else
1783                 newpath = g_strdup(name);
1784
1785         real_newpath = imap_utf8_to_modified_utf7(newpath);
1786         imap_path_separator_subst(real_newpath, separator);
1787
1788         ok = imap_cmd_rename(session, real_oldpath, real_newpath);
1789         if (ok != IMAP_SUCCESS) {
1790                 log_warning(_("can't rename mailbox: %s to %s\n"),
1791                             real_oldpath, real_newpath);
1792                 g_free(real_oldpath);
1793                 g_free(newpath);
1794                 g_free(real_newpath);
1795                 return -1;
1796         }
1797
1798         g_free(item->name);
1799         item->name = g_strdup(name);
1800
1801         old_cache_dir = folder_item_get_path(item);
1802
1803         paths[0] = g_strdup(item->path);
1804         paths[1] = newpath;
1805         g_node_traverse(item->node, G_PRE_ORDER, G_TRAVERSE_ALL, -1,
1806                         imap_rename_folder_func, paths);
1807
1808         if (is_dir_exist(old_cache_dir)) {
1809                 new_cache_dir = folder_item_get_path(item);
1810                 if (rename(old_cache_dir, new_cache_dir) < 0) {
1811                         FILE_OP_ERROR(old_cache_dir, "rename");
1812                 }
1813                 g_free(new_cache_dir);
1814         }
1815
1816         g_free(old_cache_dir);
1817         g_free(paths[0]);
1818         g_free(newpath);
1819         g_free(real_oldpath);
1820         g_free(real_newpath);
1821
1822         return 0;
1823 }
1824
1825 static gint imap_remove_folder_real(Folder *folder, FolderItem *item)
1826 {
1827         gint ok;
1828         IMAPSession *session;
1829         gchar *path;
1830         gchar *cache_dir;
1831         gint exists, recent, unseen;
1832         guint32 uid_validity;
1833
1834         g_return_val_if_fail(folder != NULL, -1);
1835         g_return_val_if_fail(item != NULL, -1);
1836         g_return_val_if_fail(item->path != NULL, -1);
1837
1838         session = imap_session_get(folder);
1839         if (!session) {
1840                 return -1;
1841         }
1842         path = imap_get_real_path(IMAP_FOLDER(folder), item->path);
1843
1844         ok = imap_cmd_examine(session, "INBOX",
1845                               &exists, &recent, &unseen, &uid_validity, FALSE);
1846         if (ok != IMAP_SUCCESS) {
1847                 g_free(path);
1848                 return -1;
1849         }
1850
1851         ok = imap_cmd_delete(session, path);
1852         if (ok != IMAP_SUCCESS) {
1853                 log_warning(_("can't delete mailbox\n"));
1854                 g_free(path);
1855                 return -1;
1856         }
1857
1858         g_free(path);
1859         cache_dir = folder_item_get_path(item);
1860         if (is_dir_exist(cache_dir) && remove_dir_recursive(cache_dir) < 0)
1861                 g_warning("can't remove directory '%s'\n", cache_dir);
1862         g_free(cache_dir);
1863         folder_item_remove(item);
1864
1865         return 0;
1866 }
1867
1868 static gint imap_remove_folder(Folder *folder, FolderItem *item)
1869 {
1870         GNode *node, *next;
1871
1872         g_return_val_if_fail(item != NULL, -1);
1873         g_return_val_if_fail(item->folder != NULL, -1);
1874         g_return_val_if_fail(item->node != NULL, -1);
1875
1876         node = item->node->children;
1877         while (node != NULL) {
1878                 next = node->next;
1879                 if (imap_remove_folder(folder, FOLDER_ITEM(node->data)) < 0)
1880                         return -1;
1881                 node = next;
1882         }
1883         debug_print("IMAP removing %s\n", item->path);
1884
1885         if (imap_remove_all_msg(folder, item) < 0)
1886                 return -1;
1887         return imap_remove_folder_real(folder, item);
1888 }
1889
1890 typedef struct _uncached_data {
1891         IMAPSession *session;
1892         FolderItem *item;
1893         MsgNumberList *numlist;
1894         guint cur;
1895         guint total;
1896         gboolean done;
1897 } uncached_data;
1898
1899 static void *imap_get_uncached_messages_thread(void *data)
1900 {
1901         uncached_data *stuff = (uncached_data *)data;
1902         IMAPSession *session = stuff->session;
1903         FolderItem *item = stuff->item;
1904         MsgNumberList *numlist = stuff->numlist;
1905         
1906         GSList *newlist = NULL;
1907         GSList *llast = NULL;
1908         GSList *seq_list, *cur;
1909
1910         debug_print("uncached_messages\n");
1911         
1912         if (session == NULL || item == NULL || item->folder == NULL
1913             || FOLDER_CLASS(item->folder) != &imap_class) {
1914                 stuff->done = TRUE;
1915                 return NULL;
1916         }
1917         
1918         seq_list = imap_get_lep_set_from_numlist(numlist);
1919         debug_print("get msgs info\n");
1920         for (cur = seq_list; cur != NULL; cur = g_slist_next(cur)) {
1921                 struct mailimap_set * imapset;
1922                 unsigned int i;
1923                 int r;
1924                 carray * env_list;
1925                 int count;
1926                 
1927                 imapset = cur->data;
1928                 
1929                 r = imap_threaded_fetch_env(session->folder,
1930                                             imapset, &env_list);
1931                 if (r != MAILIMAP_NO_ERROR)
1932                         continue;
1933                 
1934                 count = 0;
1935                 for(i = 0 ; i < carray_count(env_list) ; i ++) {
1936                         struct imap_fetch_env_info * info;
1937                         MsgInfo * msginfo;
1938                         
1939                         info = carray_get(env_list, i);
1940                         msginfo = imap_envelope_from_lep(info, item);
1941                         msginfo->folder = item;
1942                         if (!newlist)
1943                                 llast = newlist = g_slist_append(newlist, msginfo);
1944                         else {
1945                                 llast = g_slist_append(llast, msginfo);
1946                                 llast = llast->next;
1947                         }
1948                         count ++;
1949                 }
1950                 
1951                 imap_fetch_env_free(env_list);
1952         }
1953         
1954         session_set_access_time(SESSION(session));
1955         stuff->done = TRUE;
1956         return newlist;
1957 }
1958
1959 #define MAX_MSG_NUM 50
1960
1961 static GSList *imap_get_uncached_messages(IMAPSession *session,
1962                                         FolderItem *item,
1963                                         MsgNumberList *numlist)
1964 {
1965         GSList *result = NULL;
1966         GSList * cur;
1967         uncached_data *data = g_new0(uncached_data, 1);
1968         int finished;
1969         
1970         finished = 0;
1971         cur = numlist;
1972         data->total = g_slist_length(numlist);
1973         debug_print("messages list : %i\n", data->total);
1974
1975         while (cur != NULL) {
1976                 GSList * partial_result;
1977                 int count;
1978                 GSList * newlist;
1979                 GSList * llast;
1980                 
1981                 llast = NULL;
1982                 count = 0;
1983                 newlist = NULL;
1984                 while (count < MAX_MSG_NUM) {
1985                         void * p;
1986                         
1987                         p = cur->data;
1988                         
1989                         if (newlist == NULL)
1990                                 llast = newlist = g_slist_append(newlist, p);
1991                         else {
1992                                 llast = g_slist_append(llast, p);
1993                                 llast = llast->next;
1994                         }
1995                         count ++;
1996                         
1997                         cur = cur->next;
1998                         if (cur == NULL)
1999                                 break;
2000                 }
2001                 
2002                 data->done = FALSE;
2003                 data->session = session;
2004                 data->item = item;
2005                 data->numlist = newlist;
2006                 data->cur += count;
2007                 
2008                 if (prefs_common.work_offline && !inc_offline_should_override()) {
2009                         g_free(data);
2010                         return NULL;
2011                 }
2012                 
2013                 partial_result =
2014                         (GSList *)imap_get_uncached_messages_thread(data);
2015                 
2016                 statusbar_progress_all(data->cur,data->total, 1);
2017                 
2018                 g_slist_free(newlist);
2019                 
2020                 result = g_slist_concat(result, partial_result);
2021         }
2022         g_free(data);
2023         
2024         statusbar_progress_all(0,0,0);
2025         statusbar_pop_all();
2026         
2027         return result;
2028 }
2029
2030 static void imap_delete_all_cached_messages(FolderItem *item)
2031 {
2032         gchar *dir;
2033
2034         g_return_if_fail(item != NULL);
2035         g_return_if_fail(item->folder != NULL);
2036         g_return_if_fail(FOLDER_CLASS(item->folder) == &imap_class);
2037
2038         debug_print("Deleting all cached messages...\n");
2039
2040         dir = folder_item_get_path(item);
2041         if (is_dir_exist(dir))
2042                 remove_all_numbered_files(dir);
2043         g_free(dir);
2044
2045         debug_print("done.\n");
2046 }
2047
2048 static IMAPNameSpace *imap_find_namespace_from_list(GList *ns_list,
2049                                                     const gchar *path)
2050 {
2051         IMAPNameSpace *namespace = NULL;
2052         gchar *tmp_path, *name;
2053
2054         if (!path) path = "";
2055
2056         for (; ns_list != NULL; ns_list = ns_list->next) {
2057                 IMAPNameSpace *tmp_ns = ns_list->data;
2058
2059                 Xstrcat_a(tmp_path, path, "/", return namespace);
2060                 Xstrdup_a(name, tmp_ns->name, return namespace);
2061                 if (tmp_ns->separator && tmp_ns->separator != '/') {
2062                         subst_char(tmp_path, tmp_ns->separator, '/');
2063                         subst_char(name, tmp_ns->separator, '/');
2064                 }
2065                 if (strncmp(tmp_path, name, strlen(name)) == 0)
2066                         namespace = tmp_ns;
2067         }
2068
2069         return namespace;
2070 }
2071
2072 static IMAPNameSpace *imap_find_namespace(IMAPFolder *folder,
2073                                           const gchar *path)
2074 {
2075         IMAPNameSpace *namespace;
2076
2077         g_return_val_if_fail(folder != NULL, NULL);
2078
2079         namespace = imap_find_namespace_from_list(folder->ns_personal, path);
2080         if (namespace) return namespace;
2081         namespace = imap_find_namespace_from_list(folder->ns_others, path);
2082         if (namespace) return namespace;
2083         namespace = imap_find_namespace_from_list(folder->ns_shared, path);
2084         if (namespace) return namespace;
2085
2086         return NULL;
2087 }
2088
2089
2090 static gchar imap_get_path_separator(IMAPFolder *folder, const gchar *path)
2091 {
2092         IMAPNameSpace *namespace;
2093         gchar separator = '/';
2094
2095         if (folder->last_seen_separator == 0) {
2096                 clist * lep_list;
2097                 int r = imap_threaded_list((Folder *)folder, "", "", &lep_list);
2098                 if (r != MAILIMAP_NO_ERROR) {
2099                         log_warning(_("LIST failed\n"));
2100                         return '/';
2101                 }
2102                 
2103                 if (clist_count(lep_list) > 0) {
2104                         clistiter * iter = clist_begin(lep_list); 
2105                         struct mailimap_mailbox_list * mb;
2106                         mb = clist_content(iter);
2107                 
2108                         folder->last_seen_separator = mb->mb_delimiter;
2109                         debug_print("got separator: %c\n", folder->last_seen_separator);
2110                 }
2111                 mailimap_list_result_free(lep_list);
2112         }
2113
2114         if (folder->last_seen_separator != 0) {
2115                 debug_print("using separator: %c\n", folder->last_seen_separator);
2116                 return folder->last_seen_separator;
2117         }
2118
2119         namespace = imap_find_namespace(folder, path);
2120         if (namespace && namespace->separator)
2121                 separator = namespace->separator;
2122
2123         return separator;
2124 }
2125
2126 static gchar *imap_get_real_path(IMAPFolder *folder, const gchar *path)
2127 {
2128         gchar *real_path;
2129         gchar separator;
2130
2131         g_return_val_if_fail(folder != NULL, NULL);
2132         g_return_val_if_fail(path != NULL, NULL);
2133
2134         real_path = imap_utf8_to_modified_utf7(path);
2135         separator = imap_get_path_separator(folder, path);
2136         imap_path_separator_subst(real_path, separator);
2137
2138         return real_path;
2139 }
2140
2141 static gint imap_set_message_flags(IMAPSession *session,
2142                                    MsgNumberList *numlist,
2143                                    IMAPFlags flags,
2144                                    gboolean is_set)
2145 {
2146         gint ok = 0;
2147         GSList *seq_list;
2148         GSList * cur;
2149
2150         seq_list = imap_get_lep_set_from_numlist(numlist);
2151         
2152         for(cur = seq_list ; cur != NULL ; cur = g_slist_next(cur)) {
2153                 struct mailimap_set * imapset;
2154                 
2155                 imapset = cur->data;
2156                 
2157                 ok = imap_cmd_store(session, imapset,
2158                                     flags, is_set);
2159         }
2160         
2161         imap_lep_set_free(seq_list);
2162         
2163         return IMAP_SUCCESS;
2164 }
2165
2166 typedef struct _select_data {
2167         IMAPSession *session;
2168         gchar *real_path;
2169         gint *exists;
2170         gint *recent;
2171         gint *unseen;
2172         guint32 *uid_validity;
2173         gboolean done;
2174 } select_data;
2175
2176 static gint imap_select(IMAPSession *session, IMAPFolder *folder,
2177                         const gchar *path,
2178                         gint *exists, gint *recent, gint *unseen,
2179                         guint32 *uid_validity, gboolean block)
2180 {
2181         gchar *real_path;
2182         gint ok;
2183         gint exists_, recent_, unseen_;
2184         guint32 uid_validity_;
2185         
2186         if (!exists || !recent || !unseen || !uid_validity) {
2187                 if (session->mbox && strcmp(session->mbox, path) == 0)
2188                         return IMAP_SUCCESS;
2189                 exists = &exists_;
2190                 recent = &recent_;
2191                 unseen = &unseen_;
2192                 uid_validity = &uid_validity_;
2193         }
2194
2195         g_free(session->mbox);
2196         session->mbox = NULL;
2197
2198         real_path = imap_get_real_path(folder, path);
2199
2200         ok = imap_cmd_select(session, real_path,
2201                              exists, recent, unseen, uid_validity, block);
2202         if (ok != IMAP_SUCCESS)
2203                 log_warning(_("can't select folder: %s\n"), real_path);
2204         else {
2205                 session->mbox = g_strdup(path);
2206                 session->folder_content_changed = FALSE;
2207         }
2208         g_free(real_path);
2209
2210         return ok;
2211 }
2212
2213 static gint imap_status(IMAPSession *session, IMAPFolder *folder,
2214                         const gchar *path, IMAPFolderItem *item,
2215                         gint *messages,
2216                         guint32 *uid_next, guint32 *uid_validity,
2217                         gint *unseen, gboolean block)
2218 {
2219         int r;
2220         clistiter * iter;
2221         struct mailimap_mailbox_data_status * data_status;
2222         int got_values;
2223         gchar *real_path;
2224         guint mask = 0;
2225         
2226         real_path = imap_get_real_path(folder, path);
2227
2228 #if 0
2229         if (time(NULL) - item->last_update >= 5 && item->last_update != 1) {
2230                 /* do the full stuff */
2231                 item->last_update = 1; /* force update */
2232                 debug_print("updating everything\n");
2233                 r = imap_status(session, folder, path, item,
2234                 &item->c_messages, &item->c_uid_next,
2235                 &item->c_uid_validity, &item->c_unseen, block);
2236                 if (r != MAILIMAP_NO_ERROR) {
2237                         debug_print("status err %d\n", r);
2238                         return IMAP_ERROR;
2239                 }
2240                 item->last_update = time(NULL);
2241                 if (messages) 
2242                         *messages = item->c_messages;
2243                 if (uid_next)
2244                         *uid_next = item->c_uid_next;
2245                 if (uid_validity)
2246                         *uid_validity = item->c_uid_validity;
2247                 if (unseen)
2248                         *unseen = item->c_unseen;
2249                 return 0;
2250         } else if (time(NULL) - item->last_update < 5) {
2251                 /* return cached stuff */
2252                 debug_print("using cache\n");
2253                 if (messages) 
2254                         *messages = item->c_messages;
2255                 if (uid_next)
2256                         *uid_next = item->c_uid_next;
2257                 if (uid_validity)
2258                         *uid_validity = item->c_uid_validity;
2259                 if (unseen)
2260                         *unseen = item->c_unseen;
2261                 return 0;
2262         }
2263 #endif
2264
2265         /* if we get there, we're updating cache */
2266
2267         if (messages) {
2268                 mask |= 1 << 0;
2269         }
2270         if (uid_next) {
2271                 mask |= 1 << 2;
2272         }
2273         if (uid_validity) {
2274                 mask |= 1 << 3;
2275         }
2276         if (unseen) {
2277                 mask |= 1 << 4;
2278         }
2279         r = imap_threaded_status(FOLDER(folder), real_path, 
2280                 &data_status, mask);
2281
2282         g_free(real_path);
2283         if (r != MAILIMAP_NO_ERROR) {
2284                 debug_print("status err %d\n", r);
2285                 return IMAP_ERROR;
2286         }
2287         
2288         if (data_status->st_info_list == NULL) {
2289                 mailimap_mailbox_data_status_free(data_status);
2290                 debug_print("status->st_info_list == NULL\n");
2291                 return IMAP_ERROR;
2292         }
2293         
2294         got_values = 0;
2295         for(iter = clist_begin(data_status->st_info_list) ; iter != NULL ;
2296             iter = clist_next(iter)) {
2297                 struct mailimap_status_info * info;             
2298                 
2299                 info = clist_content(iter);
2300                 switch (info->st_att) {
2301                 case MAILIMAP_STATUS_ATT_MESSAGES:
2302                         * messages = info->st_value;
2303                         got_values |= 1 << 0;
2304                         break;
2305                         
2306                 case MAILIMAP_STATUS_ATT_UIDNEXT:
2307                         * uid_next = info->st_value;
2308                         got_values |= 1 << 2;
2309                         break;
2310                         
2311                 case MAILIMAP_STATUS_ATT_UIDVALIDITY:
2312                         * uid_validity = info->st_value;
2313                         got_values |= 1 << 3;
2314                         break;
2315                         
2316                 case MAILIMAP_STATUS_ATT_UNSEEN:
2317                         * unseen = info->st_value;
2318                         got_values |= 1 << 4;
2319                         break;
2320                 }
2321         }
2322         mailimap_mailbox_data_status_free(data_status);
2323         
2324         if (got_values != mask) {
2325                 debug_print("status: incomplete values received (%d)\n", got_values);
2326                 return IMAP_ERROR;
2327         }
2328         return IMAP_SUCCESS;
2329 }
2330
2331 static void imap_free_capabilities(IMAPSession *session)
2332 {
2333         slist_free_strings(session->capability);
2334         g_slist_free(session->capability);
2335         session->capability = NULL;
2336 }
2337
2338 /* low-level IMAP4rev1 commands */
2339
2340 #if 0
2341 static gint imap_cmd_authenticate(IMAPSession *session, const gchar *user,
2342                                   const gchar *pass, IMAPAuthType type)
2343 {
2344         gchar *auth_type;
2345         gint ok;
2346         gchar *buf = NULL;
2347         gchar *challenge;
2348         gint challenge_len;
2349         gchar hexdigest[33];
2350         gchar *response;
2351         gchar *response64;
2352
2353         auth_type = "CRAM-MD5";
2354
2355         imap_gen_send(session, "AUTHENTICATE %s", auth_type);
2356         ok = imap_gen_recv(session, &buf);
2357         if (ok != IMAP_SUCCESS || buf[0] != '+' || buf[1] != ' ') {
2358                 g_free(buf);
2359                 return IMAP_ERROR;
2360         }
2361
2362         challenge = g_malloc(strlen(buf + 2) + 1);
2363         challenge_len = base64_decode(challenge, buf + 2, -1);
2364         challenge[challenge_len] = '\0';
2365         g_free(buf);
2366
2367         md5_hex_hmac(hexdigest, challenge, challenge_len, pass, strlen(pass));
2368         g_free(challenge);
2369
2370         response = g_strdup_printf("%s %s", user, hexdigest);
2371         response64 = g_malloc((strlen(response) + 3) * 2 + 1);
2372         base64_encode(response64, response, strlen(response));
2373         g_free(response);
2374
2375         sock_puts(SESSION(session)->sock, response64);
2376         ok = imap_cmd_ok(session, NULL);
2377         if (ok != IMAP_SUCCESS)
2378                 log_warning(_("IMAP4 authentication failed.\n"));
2379
2380         return ok;
2381 }
2382 #endif
2383
2384 static gint imap_cmd_login(IMAPSession *session,
2385                            const gchar *user, const gchar *pass,
2386                            const gchar *type)
2387 {
2388         int r;
2389         gint ok;
2390
2391         log_print("IMAP4> Logging %s to %s using %s\n", 
2392                         user,
2393                         SESSION(session)->server,
2394                         type);
2395         r = imap_threaded_login(session->folder, user, pass, type);
2396         if (r != MAILIMAP_NO_ERROR) {
2397                 log_error("IMAP4< Error logging in to %s\n",
2398                                 SESSION(session)->server);
2399                 ok = IMAP_ERROR;
2400         } else {
2401                 ok = IMAP_SUCCESS;
2402         }
2403         return ok;
2404 }
2405
2406 static gint imap_cmd_logout(IMAPSession *session)
2407 {
2408         imap_threaded_disconnect(session->folder);
2409
2410         return IMAP_SUCCESS;
2411 }
2412
2413 static gint imap_cmd_noop(IMAPSession *session)
2414 {
2415         int r;
2416         unsigned int exists;
2417         
2418         r = imap_threaded_noop(session->folder, &exists);
2419         if (r != MAILIMAP_NO_ERROR) {
2420                 debug_print("noop err %d\n", r);
2421                 return IMAP_ERROR;
2422         }
2423         session->exists = exists;
2424         session_set_access_time(SESSION(session));
2425
2426         return IMAP_SUCCESS;
2427 }
2428
2429 #if USE_OPENSSL
2430 static gint imap_cmd_starttls(IMAPSession *session)
2431 {
2432         int r;
2433         
2434         r = imap_threaded_starttls(session->folder);
2435         if (r != MAILIMAP_NO_ERROR) {
2436                 debug_print("starttls err %d\n", r);
2437                 return IMAP_ERROR;
2438         }
2439         return IMAP_SUCCESS;
2440 }
2441 #endif
2442
2443 static gint imap_cmd_select(IMAPSession *session, const gchar *folder,
2444                             gint *exists, gint *recent, gint *unseen,
2445                             guint32 *uid_validity, gboolean block)
2446 {
2447         int r;
2448
2449         r = imap_threaded_select(session->folder, folder,
2450                                  exists, recent, unseen, uid_validity);
2451         if (r != MAILIMAP_NO_ERROR) {
2452                 debug_print("select err %d\n", r);
2453                 return IMAP_ERROR;
2454         }
2455         return IMAP_SUCCESS;
2456 }
2457
2458 static gint imap_cmd_examine(IMAPSession *session, const gchar *folder,
2459                              gint *exists, gint *recent, gint *unseen,
2460                              guint32 *uid_validity, gboolean block)
2461 {
2462         int r;
2463
2464         r = imap_threaded_examine(session->folder, folder,
2465                                   exists, recent, unseen, uid_validity);
2466         if (r != MAILIMAP_NO_ERROR) {
2467                 debug_print("examine err %d\n", r);
2468                 
2469                 return IMAP_ERROR;
2470         }
2471         return IMAP_SUCCESS;
2472 }
2473
2474 static gint imap_cmd_create(IMAPSession *session, const gchar *folder)
2475 {
2476         int r;
2477
2478         r = imap_threaded_create(session->folder, folder);
2479         if (r != MAILIMAP_NO_ERROR) {
2480                 
2481                 return IMAP_ERROR;
2482         }
2483
2484         return IMAP_SUCCESS;
2485 }
2486
2487 static gint imap_cmd_rename(IMAPSession *session, const gchar *old_folder,
2488                             const gchar *new_folder)
2489 {
2490         int r;
2491
2492         r = imap_threaded_rename(session->folder, old_folder,
2493                                  new_folder);
2494         if (r != MAILIMAP_NO_ERROR) {
2495                 
2496                 return IMAP_ERROR;
2497         }
2498
2499         return IMAP_SUCCESS;
2500 }
2501
2502 static gint imap_cmd_delete(IMAPSession *session, const gchar *folder)
2503 {
2504         int r;
2505         
2506
2507         r = imap_threaded_delete(session->folder, folder);
2508         if (r != MAILIMAP_NO_ERROR) {
2509                 
2510                 return IMAP_ERROR;
2511         }
2512
2513         return IMAP_SUCCESS;
2514 }
2515
2516 typedef struct _fetch_data {
2517         IMAPSession *session;
2518         guint32 uid;
2519         const gchar *filename;
2520         gboolean headers;
2521         gboolean body;
2522         gboolean done;
2523 } fetch_data;
2524
2525 static void *imap_cmd_fetch_thread(void *data)
2526 {
2527         fetch_data *stuff = (fetch_data *)data;
2528         IMAPSession *session = stuff->session;
2529         guint32 uid = stuff->uid;
2530         const gchar *filename = stuff->filename;
2531         int r;
2532         
2533         if (stuff->body) {
2534                 r = imap_threaded_fetch_content(session->folder,
2535                                                uid, 1, filename);
2536         }
2537         else {
2538                 r = imap_threaded_fetch_content(session->folder,
2539                                                 uid, 0, filename);
2540         }
2541         if (r != MAILIMAP_NO_ERROR) {
2542                 debug_print("fetch err %d\n", r);
2543                 return GINT_TO_POINTER(IMAP_ERROR);
2544         }
2545         return GINT_TO_POINTER(IMAP_SUCCESS);
2546 }
2547
2548 static gint imap_cmd_fetch(IMAPSession *session, guint32 uid,
2549                                 const gchar *filename, gboolean headers,
2550                                 gboolean body)
2551 {
2552         fetch_data *data = g_new0(fetch_data, 1);
2553         int result = 0;
2554         data->done = FALSE;
2555         data->session = session;
2556         data->uid = uid;
2557         data->filename = filename;
2558         data->headers = headers;
2559         data->body = body;
2560
2561         if (prefs_common.work_offline && !inc_offline_should_override()) {
2562                 g_free(data);
2563                 return -1;
2564         }
2565         statusbar_print_all(_("Fetching message..."));
2566         result = GPOINTER_TO_INT(imap_cmd_fetch_thread(data));
2567         statusbar_pop_all();
2568         g_free(data);
2569         return result;
2570 }
2571
2572
2573 static gint imap_cmd_append(IMAPSession *session, const gchar *destfolder,
2574                             const gchar *file, IMAPFlags flags, 
2575                             guint32 *new_uid)
2576 {
2577         struct mailimap_flag_list * flag_list;
2578         int r;
2579         
2580         g_return_val_if_fail(file != NULL, IMAP_ERROR);
2581
2582         flag_list = imap_flag_to_lep(flags);
2583         r = imap_threaded_append(session->folder, destfolder,
2584                          file, flag_list);
2585         if (new_uid != NULL)
2586                 *new_uid = 0;
2587
2588         if (r != MAILIMAP_NO_ERROR) {
2589                 debug_print("append err %d\n", r);
2590                 return IMAP_ERROR;
2591         }
2592         return IMAP_SUCCESS;
2593 }
2594
2595 static gint imap_cmd_copy(IMAPSession *session, struct mailimap_set * set,
2596                           const gchar *destfolder, GRelation *uid_mapping)
2597 {
2598         int r;
2599         
2600         g_return_val_if_fail(session != NULL, IMAP_ERROR);
2601         g_return_val_if_fail(set != NULL, IMAP_ERROR);
2602         g_return_val_if_fail(destfolder != NULL, IMAP_ERROR);
2603
2604         r = imap_threaded_copy(session->folder, set, destfolder);
2605         if (r != MAILIMAP_NO_ERROR) {
2606                 
2607                 return IMAP_ERROR;
2608         }
2609
2610         return IMAP_SUCCESS;
2611 }
2612
2613 static gint imap_cmd_store(IMAPSession *session, struct mailimap_set * set,
2614                            IMAPFlags flags, int do_add)
2615 {
2616         int r;
2617         struct mailimap_flag_list * flag_list;
2618         struct mailimap_store_att_flags * store_att_flags;
2619         
2620         flag_list = imap_flag_to_lep(flags);
2621         
2622         if (do_add)
2623                 store_att_flags =
2624                         mailimap_store_att_flags_new_add_flags_silent(flag_list);
2625         else
2626                 store_att_flags =
2627                         mailimap_store_att_flags_new_remove_flags_silent(flag_list);
2628         
2629         r = imap_threaded_store(session->folder, set, store_att_flags);
2630         if (r != MAILIMAP_NO_ERROR) {
2631                 
2632                 return IMAP_ERROR;
2633         }
2634         
2635         return IMAP_SUCCESS;
2636 }
2637
2638 static gint imap_cmd_expunge(IMAPSession *session)
2639 {
2640         int r;
2641         
2642         if (prefs_common.work_offline && !inc_offline_should_override()) {
2643                 return -1;
2644         }
2645
2646         r = imap_threaded_expunge(session->folder);
2647         if (r != MAILIMAP_NO_ERROR) {
2648                 
2649                 return IMAP_ERROR;
2650         }
2651
2652         return IMAP_SUCCESS;
2653 }
2654
2655 static void imap_path_separator_subst(gchar *str, gchar separator)
2656 {
2657         gchar *p;
2658         gboolean in_escape = FALSE;
2659
2660         if (!separator || separator == '/') return;
2661
2662         for (p = str; *p != '\0'; p++) {
2663                 if (*p == '/' && !in_escape)
2664                         *p = separator;
2665                 else if (*p == '&' && *(p + 1) != '-' && !in_escape)
2666                         in_escape = TRUE;
2667                 else if (*p == '-' && in_escape)
2668                         in_escape = FALSE;
2669         }
2670 }
2671
2672 static gchar *imap_modified_utf7_to_utf8(const gchar *mutf7_str)
2673 {
2674         static iconv_t cd = (iconv_t)-1;
2675         static gboolean iconv_ok = TRUE;
2676         GString *norm_utf7;
2677         gchar *norm_utf7_p;
2678         size_t norm_utf7_len;
2679         const gchar *p;
2680         gchar *to_str, *to_p;
2681         size_t to_len;
2682         gboolean in_escape = FALSE;
2683
2684         if (!iconv_ok) return g_strdup(mutf7_str);
2685
2686         if (cd == (iconv_t)-1) {
2687                 cd = iconv_open(CS_INTERNAL, CS_UTF_7);
2688                 if (cd == (iconv_t)-1) {
2689                         g_warning("iconv cannot convert UTF-7 to %s\n",
2690                                   CS_INTERNAL);
2691                         iconv_ok = FALSE;
2692                         return g_strdup(mutf7_str);
2693                 }
2694         }
2695
2696         /* modified UTF-7 to normal UTF-7 conversion */
2697         norm_utf7 = g_string_new(NULL);
2698
2699         for (p = mutf7_str; *p != '\0'; p++) {
2700                 /* replace: '&'  -> '+',
2701                             "&-" -> '&',
2702                             escaped ','  -> '/' */
2703                 if (!in_escape && *p == '&') {
2704                         if (*(p + 1) != '-') {
2705                                 g_string_append_c(norm_utf7, '+');
2706                                 in_escape = TRUE;
2707                         } else {
2708                                 g_string_append_c(norm_utf7, '&');
2709                                 p++;
2710                         }
2711                 } else if (in_escape && *p == ',') {
2712                         g_string_append_c(norm_utf7, '/');
2713                 } else if (in_escape && *p == '-') {
2714                         g_string_append_c(norm_utf7, '-');
2715                         in_escape = FALSE;
2716                 } else {
2717                         g_string_append_c(norm_utf7, *p);
2718                 }
2719         }
2720
2721         norm_utf7_p = norm_utf7->str;
2722         norm_utf7_len = norm_utf7->len;
2723         to_len = strlen(mutf7_str) * 5;
2724         to_p = to_str = g_malloc(to_len + 1);
2725
2726         if (iconv(cd, (ICONV_CONST gchar **)&norm_utf7_p, &norm_utf7_len,
2727                   &to_p, &to_len) == -1) {
2728                 g_warning(_("iconv cannot convert UTF-7 to %s\n"),
2729                           conv_get_locale_charset_str());
2730                 g_string_free(norm_utf7, TRUE);
2731                 g_free(to_str);
2732                 return g_strdup(mutf7_str);
2733         }
2734
2735         /* second iconv() call for flushing */
2736         iconv(cd, NULL, NULL, &to_p, &to_len);
2737         g_string_free(norm_utf7, TRUE);
2738         *to_p = '\0';
2739
2740         return to_str;
2741 }
2742
2743 static gchar *imap_utf8_to_modified_utf7(const gchar *from)
2744 {
2745         static iconv_t cd = (iconv_t)-1;
2746         static gboolean iconv_ok = TRUE;
2747         gchar *norm_utf7, *norm_utf7_p;
2748         size_t from_len, norm_utf7_len;
2749         GString *to_str;
2750         gchar *from_tmp, *to, *p;
2751         gboolean in_escape = FALSE;
2752
2753         if (!iconv_ok) return g_strdup(from);
2754
2755         if (cd == (iconv_t)-1) {
2756                 cd = iconv_open(CS_UTF_7, CS_INTERNAL);
2757                 if (cd == (iconv_t)-1) {
2758                         g_warning(_("iconv cannot convert %s to UTF-7\n"),
2759                                   CS_INTERNAL);
2760                         iconv_ok = FALSE;
2761                         return g_strdup(from);
2762                 }
2763         }
2764
2765         /* UTF-8 to normal UTF-7 conversion */
2766         Xstrdup_a(from_tmp, from, return g_strdup(from));
2767         from_len = strlen(from);
2768         norm_utf7_len = from_len * 5;
2769         Xalloca(norm_utf7, norm_utf7_len + 1, return g_strdup(from));
2770         norm_utf7_p = norm_utf7;
2771
2772 #define IS_PRINT(ch) (isprint(ch) && IS_ASCII(ch))
2773
2774         while (from_len > 0) {
2775                 if (*from_tmp == '+') {
2776                         *norm_utf7_p++ = '+';
2777                         *norm_utf7_p++ = '-';
2778                         norm_utf7_len -= 2;
2779                         from_tmp++;
2780                         from_len--;
2781                 } else if (IS_PRINT(*(guchar *)from_tmp)) {
2782                         /* printable ascii char */
2783                         *norm_utf7_p = *from_tmp;
2784                         norm_utf7_p++;
2785                         norm_utf7_len--;
2786                         from_tmp++;
2787                         from_len--;
2788                 } else {
2789                         size_t conv_len = 0;
2790
2791                         /* unprintable char: convert to UTF-7 */
2792                         p = from_tmp;
2793                         while (!IS_PRINT(*(guchar *)p) && conv_len < from_len) {
2794                                 conv_len += g_utf8_skip[*(guchar *)p];
2795                                 p += g_utf8_skip[*(guchar *)p];
2796                         }
2797
2798                         from_len -= conv_len;
2799                         if (iconv(cd, (ICONV_CONST gchar **)&from_tmp,
2800                                   &conv_len,
2801                                   &norm_utf7_p, &norm_utf7_len) == -1) {
2802                                 g_warning(_("iconv cannot convert UTF-8 to UTF-7\n"));
2803                                 return g_strdup(from);
2804                         }
2805
2806                         /* second iconv() call for flushing */
2807                         iconv(cd, NULL, NULL, &norm_utf7_p, &norm_utf7_len);
2808                 }
2809         }
2810
2811 #undef IS_PRINT
2812
2813         *norm_utf7_p = '\0';
2814         to_str = g_string_new(NULL);
2815         for (p = norm_utf7; p < norm_utf7_p; p++) {
2816                 /* replace: '&' -> "&-",
2817                             '+' -> '&',
2818                             "+-" -> '+',
2819                             BASE64 '/' -> ',' */
2820                 if (!in_escape && *p == '&') {
2821                         g_string_append(to_str, "&-");
2822                 } else if (!in_escape && *p == '+') {
2823                         if (*(p + 1) == '-') {
2824                                 g_string_append_c(to_str, '+');
2825                                 p++;
2826                         } else {
2827                                 g_string_append_c(to_str, '&');
2828                                 in_escape = TRUE;
2829                         }
2830                 } else if (in_escape && *p == '/') {
2831                         g_string_append_c(to_str, ',');
2832                 } else if (in_escape && *p == '-') {
2833                         g_string_append_c(to_str, '-');
2834                         in_escape = FALSE;
2835                 } else {
2836                         g_string_append_c(to_str, *p);
2837                 }
2838         }
2839
2840         if (in_escape) {
2841                 in_escape = FALSE;
2842                 g_string_append_c(to_str, '-');
2843         }
2844
2845         to = to_str->str;
2846         g_string_free(to_str, FALSE);
2847
2848         return to;
2849 }
2850
2851 static gboolean imap_rename_folder_func(GNode *node, gpointer data)
2852 {
2853         FolderItem *item = node->data;
2854         gchar **paths = data;
2855         const gchar *oldpath = paths[0];
2856         const gchar *newpath = paths[1];
2857         gchar *base;
2858         gchar *new_itempath;
2859         gint oldpathlen;
2860
2861         oldpathlen = strlen(oldpath);
2862         if (strncmp(oldpath, item->path, oldpathlen) != 0) {
2863                 g_warning("path doesn't match: %s, %s\n", oldpath, item->path);
2864                 return TRUE;
2865         }
2866
2867         base = item->path + oldpathlen;
2868         while (*base == G_DIR_SEPARATOR) base++;
2869         if (*base == '\0')
2870                 new_itempath = g_strdup(newpath);
2871         else
2872                 new_itempath = g_strconcat(newpath, G_DIR_SEPARATOR_S, base,
2873                                            NULL);
2874         g_free(item->path);
2875         item->path = new_itempath;
2876
2877         return FALSE;
2878 }
2879
2880 typedef struct _get_list_uid_data {
2881         Folder *folder;
2882         IMAPFolderItem *item;
2883         GSList **msgnum_list;
2884         gboolean done;
2885 } get_list_uid_data;
2886
2887 static void *get_list_of_uids_thread(void *data)
2888 {
2889         get_list_uid_data *stuff = (get_list_uid_data *)data;
2890         Folder *folder = stuff->folder;
2891         IMAPFolderItem *item = stuff->item;
2892         GSList **msgnum_list = stuff->msgnum_list;
2893         gint ok, nummsgs = 0, lastuid_old;
2894         IMAPSession *session;
2895         GSList *uidlist, *elem;
2896         struct mailimap_set * set;
2897         clist * lep_uidlist;
2898         int r;
2899
2900         session = imap_session_get(folder);
2901         if (session == NULL) {
2902                 stuff->done = TRUE;
2903                 return GINT_TO_POINTER(-1);
2904         }
2905
2906         ok = imap_select(session, IMAP_FOLDER(folder), item->item.path,
2907                          NULL, NULL, NULL, NULL, TRUE);
2908         if (ok != IMAP_SUCCESS) {
2909                 stuff->done = TRUE;
2910                 return GINT_TO_POINTER(-1);
2911         }
2912
2913         uidlist = NULL;
2914         
2915         set = mailimap_set_new_interval(item->lastuid + 1, 0);
2916
2917         r = imap_threaded_search(folder, IMAP_SEARCH_TYPE_SIMPLE, set,
2918                                  &lep_uidlist);
2919         if (r == MAILIMAP_NO_ERROR) {
2920                 GSList * fetchuid_list;
2921                 
2922                 fetchuid_list =
2923                         imap_uid_list_from_lep(lep_uidlist);
2924                 uidlist = g_slist_concat(fetchuid_list, uidlist);
2925         }
2926         else {
2927                 GSList * fetchuid_list;
2928                 carray * lep_uidtab;
2929                 
2930                 r = imap_threaded_fetch_uid(folder, item->lastuid + 1,
2931                                             &lep_uidtab);
2932                 if (r == MAILIMAP_NO_ERROR) {
2933                         fetchuid_list =
2934                                 imap_uid_list_from_lep_tab(lep_uidtab);
2935                         uidlist = g_slist_concat(fetchuid_list, uidlist);
2936                 }
2937         }
2938         
2939         lastuid_old = item->lastuid;
2940         *msgnum_list = g_slist_copy(item->uid_list);
2941         nummsgs = g_slist_length(*msgnum_list);
2942         debug_print("Got %d uids from cache\n", g_slist_length(item->uid_list));
2943
2944         for (elem = uidlist; elem != NULL; elem = g_slist_next(elem)) {
2945                 guint msgnum;
2946
2947                 msgnum = GPOINTER_TO_INT(elem->data);
2948                 if (msgnum > lastuid_old) {
2949                         *msgnum_list = g_slist_prepend(*msgnum_list, GINT_TO_POINTER(msgnum));
2950                         item->uid_list = g_slist_prepend(item->uid_list, GINT_TO_POINTER(msgnum));
2951                         nummsgs++;
2952
2953                         if(msgnum > item->lastuid)
2954                                 item->lastuid = msgnum;
2955                 }
2956         }
2957         g_slist_free(uidlist);
2958
2959         stuff->done = TRUE;
2960         return GINT_TO_POINTER(nummsgs);
2961 }
2962
2963 static gint get_list_of_uids(Folder *folder, IMAPFolderItem *item, GSList **msgnum_list)
2964 {
2965         gint result;
2966         get_list_uid_data *data = g_new0(get_list_uid_data, 1);
2967         data->done = FALSE;
2968         data->folder = folder;
2969         data->item = item;
2970         data->msgnum_list = msgnum_list;
2971
2972         if (prefs_common.work_offline && !inc_offline_should_override()) {
2973                 g_free(data);
2974                 return -1;
2975         }
2976
2977         result = GPOINTER_TO_INT(get_list_of_uids_thread(data));
2978         g_free(data);
2979         return result;
2980
2981 }
2982
2983 gint imap_get_num_list(Folder *folder, FolderItem *_item, GSList **msgnum_list, gboolean *old_uids_valid)
2984 {
2985         IMAPFolderItem *item = (IMAPFolderItem *)_item;
2986         IMAPSession *session;
2987         gint ok, nummsgs = 0, exists, uid_val, uid_next;
2988         GSList *uidlist = NULL;
2989         gchar *dir;
2990         gboolean selected_folder;
2991         
2992         debug_print("get_num_list\n");
2993         
2994         g_return_val_if_fail(folder != NULL, -1);
2995         g_return_val_if_fail(item != NULL, -1);
2996         g_return_val_if_fail(item->item.path != NULL, -1);
2997         g_return_val_if_fail(FOLDER_CLASS(folder) == &imap_class, -1);
2998         g_return_val_if_fail(folder->account != NULL, -1);
2999
3000         session = imap_session_get(folder);
3001         g_return_val_if_fail(session != NULL, -1);
3002
3003         statusbar_print_all("Scanning %s...\n", FOLDER_ITEM(item)->path 
3004                                 ? FOLDER_ITEM(item)->path:"");
3005
3006         selected_folder = (session->mbox != NULL) &&
3007                           (!strcmp(session->mbox, item->item.path));
3008         if (selected_folder) {
3009                 ok = imap_cmd_noop(session);
3010                 if (ok != IMAP_SUCCESS) {
3011                         debug_print("disconnected!\n");
3012                         session = imap_reconnect_if_possible(folder, session);
3013                         if (session == NULL) {
3014                                 statusbar_pop_all();
3015                                 return -1;
3016                         }
3017                 }
3018                 exists = session->exists;
3019
3020                 *old_uids_valid = TRUE;
3021         } else {
3022                 if (item->use_cache && time(NULL) - item->use_cache < 2) {
3023                         exists = item->c_messages;
3024                         uid_next = item->c_uid_next;
3025                         uid_val = item->c_uid_validity;
3026                         ok = IMAP_SUCCESS;
3027                         debug_print("using cache %d %d %d\n", exists, uid_next, uid_val);
3028                 } else {
3029                         ok = imap_status(session, IMAP_FOLDER(folder), item->item.path, item,
3030                                  &exists, &uid_next, &uid_val, NULL, FALSE);
3031                 }
3032                 item->use_cache = (time_t)0;
3033                 if (ok != IMAP_SUCCESS) {
3034                         statusbar_pop_all();
3035                         return -1;
3036                 }
3037                 if(item->item.mtime == uid_val)
3038                         *old_uids_valid = TRUE;
3039                 else {
3040                         *old_uids_valid = FALSE;
3041
3042                         debug_print("Freeing imap uid cache\n");
3043                         item->lastuid = 0;
3044                         g_slist_free(item->uid_list);
3045                         item->uid_list = NULL;
3046                 
3047                         item->item.mtime = uid_val;
3048
3049                         imap_delete_all_cached_messages((FolderItem *)item);
3050                 }
3051         }
3052
3053         if (!selected_folder)
3054                 item->uid_next = uid_next;
3055
3056         /* If old uid_next matches new uid_next we can be sure no message
3057            was added to the folder */
3058         if (( selected_folder && !session->folder_content_changed) ||
3059             (!selected_folder && uid_next == item->uid_next)) {
3060                 nummsgs = g_slist_length(item->uid_list);
3061
3062                 /* If number of messages is still the same we
3063                    know our caches message numbers are still valid,
3064                    otherwise if the number of messages has decrease
3065                    we discard our cache to start a new scan to find
3066                    out which numbers have been removed */
3067                 if (exists == nummsgs) {
3068                         *msgnum_list = g_slist_copy(item->uid_list);
3069                         statusbar_pop_all();
3070                         return nummsgs;
3071                 } else if (exists < nummsgs) {
3072                         debug_print("Freeing imap uid cache");
3073                         item->lastuid = 0;
3074                         g_slist_free(item->uid_list);
3075                         item->uid_list = NULL;
3076                 }
3077         }
3078
3079         if (exists == 0) {
3080                 *msgnum_list = NULL;
3081                 statusbar_pop_all();
3082                 return 0;
3083         }
3084
3085         nummsgs = get_list_of_uids(folder, item, &uidlist);
3086
3087         if (nummsgs < 0) {
3088                 statusbar_pop_all();
3089                 return -1;
3090         }
3091
3092         if (nummsgs != exists) {
3093                 /* Cache contains more messages then folder, we have cached
3094                    an old UID of a message that was removed and new messages
3095                    have been added too, otherwise the uid_next check would
3096                    not have failed */
3097                 debug_print("Freeing imap uid cache");
3098                 item->lastuid = 0;
3099                 g_slist_free(item->uid_list);
3100                 item->uid_list = NULL;
3101
3102                 g_slist_free(*msgnum_list);
3103
3104                 nummsgs = get_list_of_uids(folder, item, &uidlist);
3105         }
3106
3107         *msgnum_list = uidlist;
3108
3109         dir = folder_item_get_path((FolderItem *)item);
3110         debug_print("removing old messages from %s\n", dir);
3111         remove_numbered_files_not_in_list(dir, *msgnum_list);
3112         g_free(dir);
3113         
3114         debug_print("get_num_list - ok - %i\n", nummsgs);
3115         statusbar_pop_all();
3116         return nummsgs;
3117 }
3118
3119 static MsgInfo *imap_parse_msg(const gchar *file, FolderItem *item)
3120 {
3121         MsgInfo *msginfo;
3122         MsgFlags flags;
3123
3124         flags.perm_flags = MSG_NEW|MSG_UNREAD;
3125         flags.tmp_flags = 0;
3126
3127         g_return_val_if_fail(item != NULL, NULL);
3128         g_return_val_if_fail(file != NULL, NULL);
3129
3130         if (folder_has_parent_of_type(item, F_QUEUE)) {
3131                 MSG_SET_TMP_FLAGS(flags, MSG_QUEUED);
3132         } else if (folder_has_parent_of_type(item, F_DRAFT)) {
3133                 MSG_SET_TMP_FLAGS(flags, MSG_DRAFT);
3134         }
3135
3136         msginfo = procheader_parse_file(file, flags, FALSE, FALSE);
3137         if (!msginfo) return NULL;
3138         
3139         msginfo->plaintext_file = g_strdup(file);
3140         msginfo->folder = item;
3141
3142         return msginfo;
3143 }
3144
3145 GSList *imap_get_msginfos(Folder *folder, FolderItem *item,
3146                           GSList *msgnum_list)
3147 {
3148         IMAPSession *session;
3149         MsgInfoList *ret = NULL;
3150         gint ok;
3151         
3152         debug_print("get_msginfos\n");
3153         
3154         g_return_val_if_fail(folder != NULL, NULL);
3155         g_return_val_if_fail(item != NULL, NULL);
3156         g_return_val_if_fail(msgnum_list != NULL, NULL);
3157
3158         session = imap_session_get(folder);
3159         g_return_val_if_fail(session != NULL, NULL);
3160
3161         debug_print("IMAP getting msginfos\n");
3162         ok = imap_select(session, IMAP_FOLDER(folder), item->path,
3163                          NULL, NULL, NULL, NULL, FALSE);
3164         if (ok != IMAP_SUCCESS)
3165                 return NULL;
3166
3167         if (!(folder_has_parent_of_type(item, F_DRAFT) || 
3168               folder_has_parent_of_type(item, F_QUEUE))) {
3169                 ret = g_slist_concat(ret,
3170                         imap_get_uncached_messages(session, item,
3171                                                    msgnum_list));
3172         } else {
3173                 MsgNumberList *sorted_list, *elem;
3174                 gint startnum, lastnum;
3175
3176                 sorted_list = g_slist_sort(g_slist_copy(msgnum_list), g_int_compare);
3177
3178                 startnum = lastnum = GPOINTER_TO_INT(sorted_list->data);
3179
3180                 for (elem = sorted_list;; elem = g_slist_next(elem)) {
3181                         guint num = 0;
3182
3183                         if (elem)
3184                                 num = GPOINTER_TO_INT(elem->data);
3185
3186                         if (num > lastnum + 1 || elem == NULL) {
3187                                 int i;
3188                                 for (i = startnum; i <= lastnum; ++i) {
3189                                         gchar *file;
3190                         
3191                                         file = imap_fetch_msg(folder, item, i);
3192                                         if (file != NULL) {
3193                                                 MsgInfo *msginfo = imap_parse_msg(file, item);
3194                                                 if (msginfo != NULL) {
3195                                                         msginfo->msgnum = i;
3196                                                         ret = g_slist_append(ret, msginfo);
3197                                                 }
3198                                                 g_free(file);
3199                                         }
3200                                 }
3201
3202                                 if (elem == NULL)
3203                                         break;
3204
3205                                 startnum = num;
3206                         }
3207                         lastnum = num;
3208                 }
3209
3210                 g_slist_free(sorted_list);
3211         }
3212
3213         return ret;
3214 }
3215
3216 MsgInfo *imap_get_msginfo(Folder *folder, FolderItem *item, gint uid)
3217 {
3218         MsgInfo *msginfo = NULL;
3219         MsgInfoList *msginfolist;
3220         MsgNumberList numlist;
3221
3222         numlist.next = NULL;
3223         numlist.data = GINT_TO_POINTER(uid);
3224
3225         msginfolist = imap_get_msginfos(folder, item, &numlist);
3226         if (msginfolist != NULL) {
3227                 msginfo = msginfolist->data;
3228                 g_slist_free(msginfolist);
3229         }
3230
3231         return msginfo;
3232 }
3233
3234 gboolean imap_scan_required(Folder *folder, FolderItem *_item)
3235 {
3236         IMAPSession *session;
3237         IMAPFolderItem *item = (IMAPFolderItem *)_item;
3238         gint ok, exists = 0, unseen = 0;
3239         guint32 uid_next, uid_val;
3240         gboolean selected_folder;
3241         
3242         g_return_val_if_fail(folder != NULL, FALSE);
3243         g_return_val_if_fail(item != NULL, FALSE);
3244         g_return_val_if_fail(item->item.folder != NULL, FALSE);
3245         g_return_val_if_fail(FOLDER_CLASS(item->item.folder) == &imap_class, FALSE);
3246
3247         if (item->item.path == NULL)
3248                 return FALSE;
3249
3250         session = imap_session_get(folder);
3251         g_return_val_if_fail(session != NULL, FALSE);
3252
3253         selected_folder = (session->mbox != NULL) &&
3254                           (!strcmp(session->mbox, item->item.path));
3255         if (selected_folder) {
3256                 ok = imap_cmd_noop(session);
3257                 if (ok != IMAP_SUCCESS) {
3258                         debug_print("disconnected!\n");
3259                         session = imap_reconnect_if_possible(folder, session);
3260                         if (session == NULL)
3261                                 return FALSE;
3262                 }
3263
3264                 if (session->folder_content_changed
3265                 ||  session->exists != item->item.total_msgs)
3266                         return TRUE;
3267         } else {
3268                 ok = imap_status(session, IMAP_FOLDER(folder), item->item.path, IMAP_FOLDER_ITEM(item),
3269                                  &exists, &uid_next, &uid_val, &unseen, FALSE);
3270                 if (ok != IMAP_SUCCESS)
3271                         return FALSE;
3272
3273                 item->use_cache = time(NULL);
3274                 item->c_messages = exists;
3275                 item->c_uid_next = uid_next;
3276                 item->c_uid_validity = uid_val;
3277                 item->c_unseen = unseen;
3278
3279                 if ((uid_next != item->uid_next) || (exists != item->item.total_msgs))
3280                         return TRUE;
3281         }
3282
3283         return FALSE;
3284 }
3285
3286 void imap_change_flags(Folder *folder, FolderItem *item, MsgInfo *msginfo, MsgPermFlags newflags)
3287 {
3288         IMAPSession *session;
3289         IMAPFlags flags_set = 0, flags_unset = 0;
3290         gint ok = IMAP_SUCCESS;
3291         MsgNumberList numlist;
3292         hashtable_data *ht_data = NULL;
3293
3294         g_return_if_fail(folder != NULL);
3295         g_return_if_fail(folder->klass == &imap_class);
3296         g_return_if_fail(item != NULL);
3297         g_return_if_fail(item->folder == folder);
3298         g_return_if_fail(msginfo != NULL);
3299         g_return_if_fail(msginfo->folder == item);
3300
3301         session = imap_session_get(folder);
3302         if (!session) {
3303                 return;
3304         }
3305         if ((ok = imap_select(session, IMAP_FOLDER(folder), msginfo->folder->path,
3306             NULL, NULL, NULL, NULL, FALSE)) != IMAP_SUCCESS) {
3307                 return;
3308         }
3309
3310         if (!MSG_IS_MARKED(msginfo->flags) &&  (newflags & MSG_MARKED))
3311                 flags_set |= IMAP_FLAG_FLAGGED;
3312         if ( MSG_IS_MARKED(msginfo->flags) && !(newflags & MSG_MARKED))
3313                 flags_unset |= IMAP_FLAG_FLAGGED;
3314
3315         if (!MSG_IS_UNREAD(msginfo->flags) &&  (newflags & MSG_UNREAD))
3316                 flags_unset |= IMAP_FLAG_SEEN;
3317         if ( MSG_IS_UNREAD(msginfo->flags) && !(newflags & MSG_UNREAD))
3318                 flags_set |= IMAP_FLAG_SEEN;
3319
3320         if (!MSG_IS_REPLIED(msginfo->flags) &&  (newflags & MSG_REPLIED))
3321                 flags_set |= IMAP_FLAG_ANSWERED;
3322         if ( MSG_IS_REPLIED(msginfo->flags) && !(newflags & MSG_REPLIED))
3323                 flags_unset |= IMAP_FLAG_ANSWERED;
3324
3325         if (!MSG_IS_DELETED(msginfo->flags) &&  (newflags & MSG_DELETED))
3326                 flags_set |= IMAP_FLAG_DELETED;
3327         if ( MSG_IS_DELETED(msginfo->flags) && !(newflags & MSG_DELETED))
3328                 flags_unset |= IMAP_FLAG_DELETED;
3329
3330         numlist.next = NULL;
3331         numlist.data = GINT_TO_POINTER(msginfo->msgnum);
3332
3333         if (IMAP_FOLDER_ITEM(item)->batching) {
3334                 /* instead of performing an UID STORE command for each message change,
3335                  * as a lot of them can change "together", we just fill in hashtables
3336                  * and defer the treatment so that we're able to send only one
3337                  * command.
3338                  */
3339                 debug_print("IMAP batch mode on, deferring flags change\n");
3340                 if (flags_set) {
3341                         ht_data = g_hash_table_lookup(flags_set_table, GINT_TO_POINTER(flags_set));
3342                         if (ht_data == NULL) {
3343                                 ht_data = g_new0(hashtable_data, 1);
3344                                 ht_data->session = session;
3345                                 g_hash_table_insert(flags_set_table, GINT_TO_POINTER(flags_set), ht_data);
3346                         }
3347                         if (!g_slist_find(ht_data->msglist, GINT_TO_POINTER(msginfo->msgnum)))
3348                                 ht_data->msglist = g_slist_prepend(ht_data->msglist, GINT_TO_POINTER(msginfo->msgnum));
3349                 } 
3350                 if (flags_unset) {
3351                         ht_data = g_hash_table_lookup(flags_unset_table, GINT_TO_POINTER(flags_unset));
3352                         if (ht_data == NULL) {
3353                                 ht_data = g_new0(hashtable_data, 1);
3354                                 ht_data->session = session;
3355                                 g_hash_table_insert(flags_unset_table, GINT_TO_POINTER(flags_unset), ht_data);
3356                         }
3357                         if (!g_slist_find(ht_data->msglist, GINT_TO_POINTER(msginfo->msgnum)))
3358                                 ht_data->msglist = g_slist_prepend(ht_data->msglist, GINT_TO_POINTER(msginfo->msgnum));         
3359                 }
3360         } else {
3361                 debug_print("IMAP changing flags\n");
3362                 if (flags_set) {
3363                         ok = imap_set_message_flags(session, &numlist, flags_set, TRUE);
3364                         if (ok != IMAP_SUCCESS) {
3365                                 return;
3366                         }
3367                 }
3368
3369                 if (flags_unset) {
3370                         ok = imap_set_message_flags(session, &numlist, flags_unset, FALSE);
3371                         if (ok != IMAP_SUCCESS) {
3372                                 return;
3373                         }
3374                 }
3375         }
3376         msginfo->flags.perm_flags = newflags;
3377         
3378         return;
3379 }
3380
3381 static gint imap_remove_msg(Folder *folder, FolderItem *item, gint uid)
3382 {
3383         gint ok;
3384         IMAPSession *session;
3385         gchar *dir;
3386         MsgNumberList numlist;
3387         
3388         g_return_val_if_fail(folder != NULL, -1);
3389         g_return_val_if_fail(FOLDER_CLASS(folder) == &imap_class, -1);
3390         g_return_val_if_fail(item != NULL, -1);
3391
3392         session = imap_session_get(folder);
3393         if (!session) return -1;
3394
3395         ok = imap_select(session, IMAP_FOLDER(folder), item->path,
3396                          NULL, NULL, NULL, NULL, FALSE);
3397         if (ok != IMAP_SUCCESS)
3398                 return ok;
3399
3400         numlist.next = NULL;
3401         numlist.data = GINT_TO_POINTER(uid);
3402         
3403         ok = imap_set_message_flags
3404                 (IMAP_SESSION(REMOTE_FOLDER(folder)->session),
3405                 &numlist, IMAP_FLAG_DELETED, TRUE);
3406         if (ok != IMAP_SUCCESS) {
3407                 log_warning(_("can't set deleted flags: %d\n"), uid);
3408                 return ok;
3409         }
3410
3411         if (!session->uidplus) {
3412                 ok = imap_cmd_expunge(session);
3413         } else {
3414                 gchar *uidstr;
3415
3416                 uidstr = g_strdup_printf("%u", uid);
3417                 ok = imap_cmd_expunge(session);
3418                 g_free(uidstr);
3419         }
3420         if (ok != IMAP_SUCCESS) {
3421                 log_warning(_("can't expunge\n"));
3422                 return ok;
3423         }
3424
3425         IMAP_FOLDER_ITEM(item)->uid_list = g_slist_remove(
3426             IMAP_FOLDER_ITEM(item)->uid_list, numlist.data);
3427         dir = folder_item_get_path(item);
3428         if (is_dir_exist(dir))
3429                 remove_numbered_files(dir, uid, uid);
3430         g_free(dir);
3431
3432         return IMAP_SUCCESS;
3433 }
3434
3435 static gint compare_msginfo(gconstpointer a, gconstpointer b)
3436 {
3437         return ((MsgInfo *)a)->msgnum - ((MsgInfo *)b)->msgnum;
3438 }
3439
3440 static guint gslist_find_next_num(MsgNumberList **list, guint num)
3441 {
3442         GSList *elem;
3443
3444         g_return_val_if_fail(list != NULL, -1);
3445
3446         for (elem = *list; elem != NULL; elem = g_slist_next(elem))
3447                 if (GPOINTER_TO_INT(elem->data) >= num)
3448                         break;
3449         *list = elem;
3450         return elem != NULL ? GPOINTER_TO_INT(elem->data) : (gint)-1;
3451 }
3452
3453 /*
3454  * NEW and DELETED flags are not syncronized
3455  * - The NEW/RECENT flags in IMAP folders can not really be directly
3456  *   modified by Sylpheed
3457  * - The DELETE/DELETED flag in IMAP and Sylpheed don't have the same
3458  *   meaning, in IMAP it always removes the messages from the FolderItem
3459  *   in Sylpheed it can mean to move the message to trash
3460  */
3461
3462 typedef struct _get_flags_data {
3463         Folder *folder;
3464         FolderItem *item;
3465         MsgInfoList *msginfo_list;
3466         GRelation *msgflags;
3467         gboolean full_search;
3468         gboolean done;
3469 } get_flags_data;
3470
3471 static /*gint*/ void *imap_get_flags_thread(void *data)
3472 {
3473         get_flags_data *stuff = (get_flags_data *)data;
3474         Folder *folder = stuff->folder;
3475         FolderItem *item = stuff->item;
3476         MsgInfoList *msginfo_list = stuff->msginfo_list;
3477         GRelation *msgflags = stuff->msgflags;
3478         gboolean full_search = stuff->full_search;
3479         IMAPSession *session;
3480         GSList *sorted_list = NULL;
3481         GSList *unseen = NULL, *answered = NULL, *flagged = NULL, *deleted = NULL;
3482         GSList *p_unseen, *p_answered, *p_flagged, *p_deleted;
3483         GSList *elem;
3484         GSList *seq_list, *cur;
3485         gboolean reverse_seen = FALSE;
3486         GString *cmd_buf;
3487         gint ok;
3488         gint exists_cnt, unseen_cnt;
3489         gboolean selected_folder;
3490         
3491         if (folder == NULL || item == NULL) {
3492                 stuff->done = TRUE;
3493                 return GINT_TO_POINTER(-1);
3494         }
3495
3496         session = imap_session_get(folder);
3497         if (session == NULL) {
3498                 stuff->done = TRUE;
3499                 return GINT_TO_POINTER(-1);
3500         }
3501
3502         selected_folder = (session->mbox != NULL) &&
3503                           (!strcmp(session->mbox, item->path));
3504
3505         if (!selected_folder) {
3506                 ok = imap_status(session, IMAP_FOLDER(folder), item->path, IMAP_FOLDER_ITEM(item),
3507                          &exists_cnt, NULL, NULL, &unseen_cnt, TRUE);
3508                 ok = imap_select(session, IMAP_FOLDER(folder), item->path,
3509                         NULL, NULL, NULL, NULL, TRUE);
3510                 if (ok != IMAP_SUCCESS) {
3511                         stuff->done = TRUE;
3512                         return GINT_TO_POINTER(-1);
3513                 }
3514
3515                 if (unseen_cnt > exists_cnt / 2)
3516                         reverse_seen = TRUE;
3517         } 
3518         else {
3519                 if (item->unread_msgs > item->total_msgs / 2)
3520                         reverse_seen = TRUE;
3521         }
3522
3523         cmd_buf = g_string_new(NULL);
3524
3525         sorted_list = g_slist_sort(g_slist_copy(msginfo_list), compare_msginfo);
3526         if (!full_search) {
3527                 seq_list = imap_get_lep_set_from_msglist(msginfo_list);
3528         } else {
3529                 struct mailimap_set * set;
3530                 set = mailimap_set_new_interval(1, 0);
3531                 seq_list = g_slist_append(NULL, set);
3532         }
3533
3534         for (cur = seq_list; cur != NULL; cur = g_slist_next(cur)) {
3535                 struct mailimap_set * imapset;
3536                 clist * lep_uidlist;
3537                 int r;
3538                 
3539                 imapset = cur->data;
3540                 if (reverse_seen) {
3541                         r = imap_threaded_search(folder, IMAP_SEARCH_TYPE_SEEN,
3542                                                  imapset, &lep_uidlist);
3543                 }
3544                 else {
3545                         r = imap_threaded_search(folder,
3546                                                  IMAP_SEARCH_TYPE_UNSEEN,
3547                                                  imapset, &lep_uidlist);
3548                 }
3549                 if (r == MAILIMAP_NO_ERROR) {
3550                         GSList * uidlist;
3551                         
3552                         uidlist = imap_uid_list_from_lep(lep_uidlist);
3553                         mailimap_search_result_free(lep_uidlist);
3554                         
3555                         unseen = g_slist_concat(unseen, uidlist);
3556                 }
3557                 
3558                 r = imap_threaded_search(folder, IMAP_SEARCH_TYPE_ANSWERED,
3559                                          imapset, &lep_uidlist);
3560                 if (r == MAILIMAP_NO_ERROR) {
3561                         GSList * uidlist;
3562                         
3563                         uidlist = imap_uid_list_from_lep(lep_uidlist);
3564                         mailimap_search_result_free(lep_uidlist);
3565                         
3566                         answered = g_slist_concat(answered, uidlist);
3567                 }
3568
3569                 r = imap_threaded_search(folder, IMAP_SEARCH_TYPE_FLAGGED,
3570                                          imapset, &lep_uidlist);
3571                 if (r == MAILIMAP_NO_ERROR) {
3572                         GSList * uidlist;
3573                         
3574                         uidlist = imap_uid_list_from_lep(lep_uidlist);
3575                         mailimap_search_result_free(lep_uidlist);
3576                         
3577                         flagged = g_slist_concat(flagged, uidlist);
3578                 }
3579                 
3580                 r = imap_threaded_search(folder, IMAP_SEARCH_TYPE_DELETED,
3581                                          imapset, &lep_uidlist);
3582                 if (r == MAILIMAP_NO_ERROR) {
3583                         GSList * uidlist;
3584                         
3585                         uidlist = imap_uid_list_from_lep(lep_uidlist);
3586                         mailimap_search_result_free(lep_uidlist);
3587                         
3588                         deleted = g_slist_concat(deleted, uidlist);
3589                 }
3590         }
3591
3592         p_unseen = unseen;
3593         p_answered = answered;
3594         p_flagged = flagged;
3595         p_deleted = deleted;
3596
3597         for (elem = sorted_list; elem != NULL; elem = g_slist_next(elem)) {
3598                 MsgInfo *msginfo;
3599                 MsgPermFlags flags;
3600                 gboolean wasnew;
3601                 
3602                 msginfo = (MsgInfo *) elem->data;
3603                 flags = msginfo->flags.perm_flags;
3604                 wasnew = (flags & MSG_NEW);
3605                 flags &= ~((reverse_seen ? 0 : MSG_UNREAD | MSG_NEW) | MSG_REPLIED | MSG_MARKED);
3606                 if (reverse_seen)
3607                         flags |= MSG_UNREAD | (wasnew ? MSG_NEW : 0);
3608                 if (gslist_find_next_num(&p_unseen, msginfo->msgnum) == msginfo->msgnum) {
3609                         if (!reverse_seen) {
3610                                 flags |= MSG_UNREAD | (wasnew ? MSG_NEW : 0);
3611                         } else {
3612                                 flags &= ~(MSG_UNREAD | MSG_NEW);
3613                         }
3614                 }
3615                 if (gslist_find_next_num(&p_answered, msginfo->msgnum) == msginfo->msgnum)
3616                         flags |= MSG_REPLIED;
3617                 else
3618                         flags &= ~MSG_REPLIED;
3619                 if (gslist_find_next_num(&p_flagged, msginfo->msgnum) == msginfo->msgnum)
3620                         flags |= MSG_MARKED;
3621                 else
3622                         flags &= ~MSG_MARKED;
3623                 if (gslist_find_next_num(&p_deleted, msginfo->msgnum) == msginfo->msgnum)
3624                         flags |= MSG_DELETED;
3625                 else
3626                         flags &= ~MSG_DELETED;
3627                 g_relation_insert(msgflags, msginfo, GINT_TO_POINTER(flags));
3628         }
3629
3630         imap_lep_set_free(seq_list);
3631         g_slist_free(flagged);
3632         g_slist_free(deleted);
3633         g_slist_free(answered);
3634         g_slist_free(unseen);
3635         g_slist_free(sorted_list);
3636         g_string_free(cmd_buf, TRUE);
3637
3638         stuff->done = TRUE;
3639         return GINT_TO_POINTER(0);
3640 }
3641
3642 static gint imap_get_flags(Folder *folder, FolderItem *item,
3643                            MsgInfoList *msginfo_list, GRelation *msgflags)
3644 {
3645         gint result;
3646         get_flags_data *data = g_new0(get_flags_data, 1);
3647         data->done = FALSE;
3648         data->folder = folder;
3649         data->item = item;
3650         data->msginfo_list = msginfo_list;
3651         data->msgflags = msgflags;
3652         data->full_search = FALSE;
3653
3654         GSList *tmp = NULL, *cur;
3655         
3656         if (prefs_common.work_offline && !inc_offline_should_override()) {
3657                 g_free(data);
3658                 return -1;
3659         }
3660
3661         tmp = folder_item_get_msg_list(item);
3662
3663         if (g_slist_length(tmp) == g_slist_length(msginfo_list))
3664                 data->full_search = TRUE;
3665         
3666         for (cur = tmp; cur; cur = cur->next)
3667                 procmsg_msginfo_free((MsgInfo *)cur->data);
3668         
3669         g_slist_free(tmp);
3670
3671         result = GPOINTER_TO_INT(imap_get_flags_thread(data));
3672         
3673         g_free(data);
3674         return result;
3675
3676 }
3677
3678 static gboolean process_flags(gpointer key, gpointer value, gpointer user_data)
3679 {
3680         gboolean flags_set = GPOINTER_TO_INT(user_data);
3681         gint flags_value = GPOINTER_TO_INT(key);
3682         hashtable_data *data = (hashtable_data *)value;
3683         
3684         data->msglist = g_slist_reverse(data->msglist);
3685         
3686         debug_print("IMAP %ssetting flags to %d for %d messages\n",
3687                 flags_set?"":"un",
3688                 flags_value,
3689                 g_slist_length(data->msglist));
3690         imap_set_message_flags(data->session, data->msglist, flags_value, flags_set);
3691         
3692         g_slist_free(data->msglist);    
3693         g_free(data);
3694         return TRUE;
3695 }
3696
3697 static void process_hashtable(void)
3698 {
3699         if (flags_set_table) {
3700                 g_hash_table_foreach_remove(flags_set_table, process_flags, GINT_TO_POINTER(TRUE));
3701                 g_free(flags_set_table);
3702                 flags_set_table = NULL;
3703         }
3704         if (flags_unset_table) {
3705                 g_hash_table_foreach_remove(flags_unset_table, process_flags, GINT_TO_POINTER(FALSE));
3706                 g_free(flags_unset_table);
3707                 flags_unset_table = NULL;
3708         }
3709 }
3710
3711 static IMAPFolderItem *batching_item = NULL;
3712
3713 static void imap_set_batch (Folder *folder, FolderItem *_item, gboolean batch)
3714 {
3715         IMAPFolderItem *item = (IMAPFolderItem *)_item;
3716
3717         g_return_if_fail(item != NULL);
3718         
3719         if (batch && batching_item != NULL) {
3720                 g_warning("already batching on %s\n", batching_item->item.path);
3721                 return;
3722         }
3723         
3724         if (item->batching == batch)
3725                 return;
3726         
3727         item->batching = batch;
3728         
3729         batching_item = batch?item:NULL;
3730         
3731         if (batch) {
3732                 debug_print("IMAP switching to batch mode\n");
3733                 if (flags_set_table) {
3734                         g_warning("flags_set_table non-null but we just entered batch mode!\n");
3735                         flags_set_table = NULL;
3736                 }
3737                 if (flags_unset_table) {
3738                         g_warning("flags_unset_table non-null but we just entered batch mode!\n");
3739                         flags_unset_table = NULL;
3740                 }
3741                 flags_set_table = g_hash_table_new(NULL, g_direct_equal);
3742                 flags_unset_table = g_hash_table_new(NULL, g_direct_equal);
3743         } else {
3744                 debug_print("IMAP switching away from batch mode\n");
3745                 /* process stuff */
3746                 process_hashtable();
3747         }
3748 }
3749
3750
3751
3752 /* data types conversion libetpan <-> sylpheed */
3753
3754
3755
3756 #define ETPAN_IMAP_MB_MARKED      1
3757 #define ETPAN_IMAP_MB_UNMARKED    2
3758 #define ETPAN_IMAP_MB_NOSELECT    4
3759 #define ETPAN_IMAP_MB_NOINFERIORS 8
3760
3761 static int imap_flags_to_flags(struct mailimap_mbx_list_flags * imap_flags)
3762 {
3763   int flags;
3764   clistiter * cur;
3765   
3766   flags = 0;
3767   if (imap_flags->mbf_type == MAILIMAP_MBX_LIST_FLAGS_SFLAG) {
3768     switch (imap_flags->mbf_sflag) {
3769     case MAILIMAP_MBX_LIST_SFLAG_MARKED:
3770       flags |= ETPAN_IMAP_MB_MARKED;
3771       break;
3772     case MAILIMAP_MBX_LIST_SFLAG_NOSELECT:
3773       flags |= ETPAN_IMAP_MB_NOSELECT;
3774       break;
3775     case MAILIMAP_MBX_LIST_SFLAG_UNMARKED:
3776       flags |= ETPAN_IMAP_MB_UNMARKED;
3777       break;
3778     }
3779   }
3780   
3781   for(cur = clist_begin(imap_flags->mbf_oflags) ; cur != NULL ;
3782       cur = clist_next(cur)) {
3783     struct mailimap_mbx_list_oflag * oflag;
3784     
3785     oflag = clist_content(cur);
3786     
3787     switch (oflag->of_type) {
3788     case MAILIMAP_MBX_LIST_OFLAG_NOINFERIORS:
3789       flags |= ETPAN_IMAP_MB_NOINFERIORS;
3790       break;
3791     }
3792   }
3793   
3794   return flags;
3795 }
3796
3797 static GSList * imap_list_from_lep(IMAPFolder * folder,
3798                                    clist * list, const gchar * real_path)
3799 {
3800         clistiter * iter;
3801         GSList * item_list;
3802         
3803         item_list = NULL;
3804         
3805         for(iter = clist_begin(list) ; iter != NULL ;
3806             iter = clist_next(iter)) {
3807                 struct mailimap_mailbox_list * mb;
3808                 int flags;
3809                 char delimiter;
3810                 char * name;
3811                 char * dup_name;
3812                 gchar * base;
3813                 gchar * loc_name;
3814                 gchar * loc_path;
3815                 FolderItem *new_item;
3816                 
3817                 mb = clist_content(iter);
3818                 
3819                 flags = 0;
3820                 if (mb->mb_flag != NULL)
3821                         flags = imap_flags_to_flags(mb->mb_flag);
3822                 
3823                 delimiter = mb->mb_delimiter;
3824                 name = mb->mb_name;
3825                 
3826                 dup_name = strdup(name);                
3827                 if (delimiter != '\0')
3828                         subst_char(dup_name, delimiter, '/');
3829                 
3830                 base = g_path_get_basename(dup_name);
3831                 if (base[0] == '.') {
3832                         g_free(base);
3833                         free(dup_name);
3834                         continue;
3835                 }
3836                 
3837                 if (strcmp(dup_name, real_path) == 0) {
3838                         g_free(base);
3839                         free(dup_name);
3840                         continue;
3841                 }
3842
3843                 if (dup_name[strlen(dup_name)-1] == '/') {
3844                         g_free(base);
3845                         free(dup_name);
3846                         continue;
3847                 }
3848                 
3849                 loc_name = imap_modified_utf7_to_utf8(base);
3850                 loc_path = imap_modified_utf7_to_utf8(dup_name);
3851                 
3852                 new_item = folder_item_new(FOLDER(folder), loc_name, loc_path);
3853                 if ((flags & ETPAN_IMAP_MB_NOINFERIORS) != 0)
3854                         new_item->no_sub = TRUE;
3855                 if (strcmp(dup_name, "INBOX") != 0 &&
3856                     ((flags & ETPAN_IMAP_MB_NOSELECT) != 0))
3857                         new_item->no_select = TRUE;
3858                 
3859                 item_list = g_slist_append(item_list, new_item);
3860                 
3861                 debug_print("folder '%s' found.\n", loc_path);
3862                 g_free(base);
3863                 g_free(loc_path);
3864                 g_free(loc_name);
3865                 
3866                 free(dup_name);
3867         }
3868         
3869         return item_list;
3870 }
3871
3872 static GSList * imap_get_lep_set_from_numlist(MsgNumberList *numlist)
3873 {
3874         GSList *sorted_list, *cur;
3875         guint first, last, next;
3876         GSList *ret_list = NULL;
3877         unsigned int count;
3878         struct mailimap_set * current_set;
3879         unsigned int item_count;
3880         
3881         if (numlist == NULL)
3882                 return NULL;
3883         
3884         count = 0;
3885         current_set = mailimap_set_new_empty();
3886         
3887         sorted_list = g_slist_copy(numlist);
3888         sorted_list = g_slist_sort(sorted_list, g_int_compare);
3889
3890         first = GPOINTER_TO_INT(sorted_list->data);
3891         
3892         item_count = 0;
3893         for (cur = sorted_list; cur != NULL; cur = g_slist_next(cur)) {
3894                 if (GPOINTER_TO_INT(cur->data) == 0)
3895                         continue;
3896                 
3897                 item_count ++;
3898
3899                 last = GPOINTER_TO_INT(cur->data);
3900                 if (cur->next)
3901                         next = GPOINTER_TO_INT(cur->next->data);
3902                 else
3903                         next = 0;
3904
3905                 if (last + 1 != next || next == 0) {
3906
3907                         struct mailimap_set_item * item;
3908                         item = mailimap_set_item_new(first, last);
3909                         mailimap_set_add(current_set, item);
3910                         count ++;
3911                         
3912                         first = next;
3913                         
3914                         if (count >= IMAP_SET_MAX_COUNT) {
3915                                 ret_list = g_slist_append(ret_list,
3916                                                           current_set);
3917                                 current_set = mailimap_set_new_empty();
3918                                 count = 0;
3919                                 item_count = 0;
3920                         }
3921                 }
3922         }
3923         
3924         if (clist_count(current_set->set_list) > 0) {
3925                 ret_list = g_slist_append(ret_list,
3926                                           current_set);
3927         }
3928         
3929         g_slist_free(sorted_list);
3930
3931         return ret_list;
3932 }
3933
3934 static GSList * imap_get_lep_set_from_msglist(MsgInfoList *msglist)
3935 {
3936         MsgNumberList *numlist = NULL;
3937         MsgInfoList *cur;
3938         GSList *seq_list;
3939
3940         for (cur = msglist; cur != NULL; cur = g_slist_next(cur)) {
3941                 MsgInfo *msginfo = (MsgInfo *) cur->data;
3942
3943                 numlist = g_slist_append(numlist, GINT_TO_POINTER(msginfo->msgnum));
3944         }
3945         seq_list = imap_get_lep_set_from_numlist(numlist);
3946         g_slist_free(numlist);
3947
3948         return seq_list;
3949 }
3950
3951 static GSList * imap_uid_list_from_lep(clist * list)
3952 {
3953         clistiter * iter;
3954         GSList * result;
3955         
3956         result = NULL;
3957         
3958         for(iter = clist_begin(list) ; iter != NULL ;
3959             iter = clist_next(iter)) {
3960                 uint32_t * puid;
3961                 
3962                 puid = clist_content(iter);
3963                 result = g_slist_append(result, GINT_TO_POINTER(* puid));
3964         }
3965         
3966         return result;
3967 }
3968
3969 static GSList * imap_uid_list_from_lep_tab(carray * list)
3970 {
3971         unsigned int i;
3972         GSList * result;
3973         
3974         result = NULL;
3975         
3976         for(i = 0 ; i < carray_count(list) ; i ++) {
3977                 uint32_t * puid;
3978                 
3979                 puid = carray_get(list, i);
3980                 result = g_slist_append(result, GINT_TO_POINTER(* puid));
3981         }
3982         
3983         return result;
3984 }
3985
3986 static MsgInfo *imap_envelope_from_lep(struct imap_fetch_env_info * info,
3987                                        FolderItem *item)
3988 {
3989         MsgInfo *msginfo = NULL;
3990         guint32 uid = 0;
3991         size_t size = 0;
3992         MsgFlags flags = {0, 0};
3993         
3994         MSG_SET_TMP_FLAGS(flags, MSG_IMAP);
3995         if (folder_has_parent_of_type(item, F_QUEUE)) {
3996                 MSG_SET_TMP_FLAGS(flags, MSG_QUEUED);
3997         } else if (folder_has_parent_of_type(item, F_DRAFT)) {
3998                 MSG_SET_TMP_FLAGS(flags, MSG_DRAFT);
3999         }
4000         flags.perm_flags = info->flags;
4001         
4002         uid = info->uid;
4003         size = info->size;
4004         msginfo = procheader_parse_str(info->headers, flags, FALSE, FALSE);
4005         
4006         if (msginfo) {
4007                 msginfo->msgnum = uid;
4008                 msginfo->size = size;
4009         }
4010
4011         return msginfo;
4012 }
4013
4014 static void imap_lep_set_free(GSList *seq_list)
4015 {
4016         GSList * cur;
4017         
4018         for(cur = seq_list ; cur != NULL ; cur = g_slist_next(cur)) {
4019                 struct mailimap_set * imapset;
4020                 
4021                 imapset = cur->data;
4022                 mailimap_set_free(imapset);
4023         }
4024         g_slist_free(seq_list);
4025 }
4026
4027 static struct mailimap_flag_list * imap_flag_to_lep(IMAPFlags flags)
4028 {
4029         struct mailimap_flag_list * flag_list;
4030         
4031         flag_list = mailimap_flag_list_new_empty();
4032         
4033         if (IMAP_IS_SEEN(flags))
4034                 mailimap_flag_list_add(flag_list,
4035                                        mailimap_flag_new_seen());
4036         if (IMAP_IS_ANSWERED(flags))
4037                 mailimap_flag_list_add(flag_list,
4038                                        mailimap_flag_new_answered());
4039         if (IMAP_IS_FLAGGED(flags))
4040                 mailimap_flag_list_add(flag_list,
4041                                        mailimap_flag_new_flagged());
4042         if (IMAP_IS_DELETED(flags))
4043                 mailimap_flag_list_add(flag_list,
4044                                        mailimap_flag_new_deleted());
4045         if (IMAP_IS_DRAFT(flags))
4046                 mailimap_flag_list_add(flag_list,
4047                                        mailimap_flag_new_draft());
4048         
4049         return flag_list;
4050 }
4051
4052 guint imap_folder_get_refcnt(Folder *folder)
4053 {
4054         return ((IMAPFolder *)folder)->refcnt;
4055 }
4056
4057 void imap_folder_ref(Folder *folder)
4058 {
4059         ((IMAPFolder *)folder)->refcnt++;
4060 }
4061
4062 void imap_folder_unref(Folder *folder)
4063 {
4064         if (((IMAPFolder *)folder)->refcnt > 0)
4065                 ((IMAPFolder *)folder)->refcnt--;
4066 }
4067
4068 #else /* HAVE_LIBETPAN */
4069
4070 static FolderClass imap_class;
4071
4072 static Folder   *imap_folder_new        (const gchar    *name,
4073                                          const gchar    *path)
4074 {
4075         return NULL;
4076 }
4077 static gint     imap_create_tree        (Folder         *folder)
4078 {
4079         return -1;
4080 }
4081 static FolderItem *imap_create_folder   (Folder         *folder,
4082                                          FolderItem     *parent,
4083                                          const gchar    *name)
4084 {
4085         return NULL;
4086 }
4087 static gint     imap_rename_folder      (Folder         *folder,
4088                                          FolderItem     *item, 
4089                                          const gchar    *name)
4090 {
4091         return -1;
4092 }
4093
4094 FolderClass *imap_get_class(void)
4095 {
4096         if (imap_class.idstr == NULL) {
4097                 imap_class.type = F_IMAP;
4098                 imap_class.idstr = "imap";
4099                 imap_class.uistr = "IMAP4";
4100
4101                 imap_class.new_folder = imap_folder_new;
4102                 imap_class.create_tree = imap_create_tree;
4103                 imap_class.create_folder = imap_create_folder;
4104                 imap_class.rename_folder = imap_rename_folder;
4105                 /* nothing implemented */
4106         }
4107
4108         return &imap_class;
4109 }
4110 #endif
4111
4112 void imap_synchronise(FolderItem *item) 
4113 {
4114         imap_gtk_synchronise(item);
4115 }