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