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