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