2007-01-05 [paul] 2.6.1cvs110
[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                         g_warning("         SESSION WAS LOCKED !!      "); \
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                       strcmp2(item->path, root_folder) != 0)) {
1554                 folder_tree_destroy(folder);
1555                 item = folder_item_new(folder, folder->name, root_folder);
1556                 item->folder = folder;
1557                 folder->node = item->node = g_node_new(item);
1558         }
1559
1560         imap_scan_tree_recursive(session, FOLDER_ITEM(folder->node->data));
1561         imap_create_missing_folders(folder);
1562         unlock_session();
1563
1564         return 0;
1565 }
1566
1567 static gint imap_scan_tree_recursive(IMAPSession *session, FolderItem *item)
1568 {
1569         Folder *folder;
1570         IMAPFolder *imapfolder;
1571         FolderItem *new_item;
1572         GSList *item_list, *cur;
1573         GNode *node;
1574         gchar *real_path;
1575         gchar *wildcard_path;
1576         gchar separator;
1577         gchar wildcard[3];
1578         clist * lep_list;
1579         int r;
1580         
1581         g_return_val_if_fail(item != NULL, -1);
1582         g_return_val_if_fail(item->folder != NULL, -1);
1583         g_return_val_if_fail(item->no_sub == FALSE, -1);
1584
1585         folder = item->folder;
1586         imapfolder = IMAP_FOLDER(folder);
1587
1588         separator = imap_get_path_separator(session, imapfolder, item->path);
1589
1590         if (folder->ui_func)
1591                 folder->ui_func(folder, item, folder->ui_func_data);
1592
1593         if (item->path) {
1594                 wildcard[0] = separator;
1595                 wildcard[1] = '%';
1596                 wildcard[2] = '\0';
1597                 real_path = imap_get_real_path(session, imapfolder, item->path);
1598         } else {
1599                 wildcard[0] = '%';
1600                 wildcard[1] = '\0';
1601                 real_path = g_strdup("");
1602         }
1603
1604         Xstrcat_a(wildcard_path, real_path, wildcard,
1605                   {g_free(real_path); return IMAP_ERROR;});
1606         lep_list = NULL;
1607         r = imap_threaded_list(folder, "", wildcard_path, &lep_list);
1608         if (r != MAILIMAP_NO_ERROR) {
1609                 item_list = NULL;
1610         }
1611         else {
1612                 item_list = imap_list_from_lep(imapfolder,
1613                                                lep_list, real_path, FALSE);
1614                 mailimap_list_result_free(lep_list);
1615         }
1616         
1617         g_free(real_path);
1618
1619         node = item->node->children;
1620         while (node != NULL) {
1621                 FolderItem *old_item = FOLDER_ITEM(node->data);
1622                 GNode *next = node->next;
1623
1624                 new_item = NULL;
1625                 for (cur = item_list; cur != NULL; cur = cur->next) {
1626                         FolderItem *cur_item = FOLDER_ITEM(cur->data);
1627                         if (!strcmp2(old_item->path, cur_item->path)) {
1628                                 new_item = cur_item;
1629                                 break;
1630                         }
1631                 }
1632                 if (!new_item) {
1633                         debug_print("folder '%s' not found. removing...\n",
1634                                     old_item->path);
1635                         folder_item_remove(old_item);
1636                 } else {
1637                         old_item->no_sub = new_item->no_sub;
1638                         old_item->no_select = new_item->no_select;
1639                         if (old_item->no_sub == TRUE && node->children) {
1640                                 debug_print("folder '%s' doesn't have "
1641                                             "subfolders. removing...\n",
1642                                             old_item->path);
1643                                 folder_item_remove_children(old_item);
1644                         }
1645                 }
1646
1647                 node = next;
1648         }
1649
1650         for (cur = item_list; cur != NULL; cur = cur->next) {
1651                 FolderItem *cur_item = FOLDER_ITEM(cur->data);
1652                 new_item = NULL;
1653
1654                 for (node = item->node->children; node != NULL;
1655                      node = node->next) {
1656                         if (!strcmp2(FOLDER_ITEM(node->data)->path,
1657                                      cur_item->path)) {
1658                                 new_item = FOLDER_ITEM(node->data);
1659                                 folder_item_destroy(cur_item);
1660                                 cur_item = NULL;
1661                                 break;
1662                         }
1663                 }
1664                 if (!new_item) {
1665                         new_item = cur_item;
1666                         debug_print("new folder '%s' found.\n", new_item->path);
1667                         folder_item_append(item, new_item);
1668                 }
1669
1670                 if (!strcmp(new_item->path, "INBOX")) {
1671                         new_item->stype = F_INBOX;
1672                         folder->inbox = new_item;
1673                 } else if (!folder_item_parent(item) || item->stype == F_INBOX) {
1674                         gchar *base;
1675
1676                         base = g_path_get_basename(new_item->path);
1677
1678                         if (!folder->outbox && !g_ascii_strcasecmp(base, "Sent")) {
1679                                 new_item->stype = F_OUTBOX;
1680                                 folder->outbox = new_item;
1681                         } else if (!folder->draft && !g_ascii_strcasecmp(base, "Drafts")) {
1682                                 new_item->stype = F_DRAFT;
1683                                 folder->draft = new_item;
1684                         } else if (!folder->queue && !g_ascii_strcasecmp(base, "Queue")) {
1685                                 new_item->stype = F_QUEUE;
1686                                 folder->queue = new_item;
1687                         } else if (!folder->trash && !g_ascii_strcasecmp(base, "Trash")) {
1688                                 new_item->stype = F_TRASH;
1689                                 folder->trash = new_item;
1690                         }
1691                         g_free(base);
1692                 }
1693
1694                 if (new_item->no_sub == FALSE)
1695                         imap_scan_tree_recursive(session, new_item);
1696         }
1697
1698         g_slist_free(item_list);
1699
1700         return IMAP_SUCCESS;
1701 }
1702
1703 static gint imap_create_tree(Folder *folder)
1704 {
1705         g_return_val_if_fail(folder != NULL, -1);
1706         g_return_val_if_fail(folder->node != NULL, -1);
1707         g_return_val_if_fail(folder->node->data != NULL, -1);
1708         g_return_val_if_fail(folder->account != NULL, -1);
1709
1710         imap_scan_tree(folder);
1711         imap_create_missing_folders(folder);
1712
1713         return 0;
1714 }
1715
1716 static void imap_create_missing_folders(Folder *folder)
1717 {
1718         g_return_if_fail(folder != NULL);
1719
1720         if (!folder->inbox)
1721                 folder->inbox = imap_create_special_folder
1722                         (folder, F_INBOX, "INBOX");
1723         if (!folder->trash)
1724                 folder->trash = imap_create_special_folder
1725                         (folder, F_TRASH, "Trash");
1726         if (!folder->queue)
1727                 folder->queue = imap_create_special_folder
1728                         (folder, F_QUEUE, "Queue");
1729         if (!folder->outbox)
1730                 folder->outbox = imap_create_special_folder
1731                         (folder, F_OUTBOX, "Sent");
1732         if (!folder->draft)
1733                 folder->draft = imap_create_special_folder
1734                         (folder, F_DRAFT, "Drafts");
1735 }
1736
1737 static FolderItem *imap_create_special_folder(Folder *folder,
1738                                               SpecialFolderItemType stype,
1739                                               const gchar *name)
1740 {
1741         FolderItem *item;
1742         FolderItem *new_item;
1743
1744         g_return_val_if_fail(folder != NULL, NULL);
1745         g_return_val_if_fail(folder->node != NULL, NULL);
1746         g_return_val_if_fail(folder->node->data != NULL, NULL);
1747         g_return_val_if_fail(folder->account != NULL, NULL);
1748         g_return_val_if_fail(name != NULL, NULL);
1749
1750         item = FOLDER_ITEM(folder->node->data);
1751         new_item = imap_create_folder(folder, item, name);
1752
1753         if (!new_item) {
1754                 g_warning("Can't create '%s'\n", name);
1755                 if (!folder->inbox) return NULL;
1756
1757                 new_item = imap_create_folder(folder, folder->inbox, name);
1758                 if (!new_item)
1759                         g_warning("Can't create '%s' under INBOX\n", name);
1760                 else
1761                         new_item->stype = stype;
1762         } else
1763                 new_item->stype = stype;
1764
1765         return new_item;
1766 }
1767
1768 static gchar *imap_folder_get_path(Folder *folder)
1769 {
1770         gchar *folder_path;
1771
1772         g_return_val_if_fail(folder != NULL, NULL);
1773         g_return_val_if_fail(folder->account != NULL, NULL);
1774
1775         folder_path = g_strconcat(get_imap_cache_dir(),
1776                                   G_DIR_SEPARATOR_S,
1777                                   folder->account->recv_server,
1778                                   G_DIR_SEPARATOR_S,
1779                                   folder->account->userid,
1780                                   NULL);
1781
1782         return folder_path;
1783 }
1784
1785 static gchar *imap_item_get_path(Folder *folder, FolderItem *item)
1786 {
1787         gchar *folder_path, *path;
1788
1789         g_return_val_if_fail(folder != NULL, NULL);
1790         g_return_val_if_fail(item != NULL, NULL);
1791         folder_path = imap_folder_get_path(folder);
1792
1793         g_return_val_if_fail(folder_path != NULL, NULL);
1794         if (folder_path[0] == G_DIR_SEPARATOR) {
1795                 if (item->path)
1796                         path = g_strconcat(folder_path, G_DIR_SEPARATOR_S,
1797                                            item->path, NULL);
1798                 else
1799                         path = g_strdup(folder_path);
1800         } else {
1801                 if (item->path)
1802                         path = g_strconcat(get_home_dir(), G_DIR_SEPARATOR_S,
1803                                            folder_path, G_DIR_SEPARATOR_S,
1804                                            item->path, NULL);
1805                 else
1806                         path = g_strconcat(get_home_dir(), G_DIR_SEPARATOR_S,
1807                                            folder_path, NULL);
1808         }
1809         g_free(folder_path);
1810
1811         return path;
1812 }
1813
1814 static FolderItem *imap_create_folder(Folder *folder, FolderItem *parent,
1815                                const gchar *name)
1816 {
1817         gchar *dirpath, *imap_path;
1818         IMAPSession *session;
1819         FolderItem *new_item;
1820         gchar separator;
1821         gchar *new_name;
1822         const gchar *p;
1823         gint ok;
1824         gboolean no_select = FALSE, no_sub = FALSE;
1825         
1826         g_return_val_if_fail(folder != NULL, NULL);
1827         g_return_val_if_fail(folder->account != NULL, NULL);
1828         g_return_val_if_fail(parent != NULL, NULL);
1829         g_return_val_if_fail(name != NULL, NULL);
1830
1831         debug_print("getting session...\n");
1832         session = imap_session_get(folder);
1833         if (!session) {
1834                 return NULL;
1835         }
1836
1837         if (!folder_item_parent(parent) && strcmp(name, "INBOX") == 0) {
1838                 dirpath = g_strdup(name);
1839         }else if (parent->path)
1840                 dirpath = g_strconcat(parent->path, "/", name, NULL);
1841         else if ((p = strchr(name, '/')) != NULL && *(p + 1) != '\0')
1842                 dirpath = g_strdup(name);
1843         else if (folder->account->imap_dir && *folder->account->imap_dir) {
1844                 gchar *imap_dir;
1845
1846                 Xstrdup_a(imap_dir, folder->account->imap_dir, {unlock_session();return NULL;});
1847                 strtailchomp(imap_dir, '/');
1848                 dirpath = g_strconcat(imap_dir, "/", name, NULL);
1849         } else
1850                 dirpath = g_strdup(name);
1851                 
1852         
1853
1854         /* keep trailing directory separator to create a folder that contains
1855            sub folder */
1856         imap_path = imap_utf8_to_modified_utf7(dirpath);
1857
1858         strtailchomp(dirpath, '/');
1859         Xstrdup_a(new_name, name, {
1860                 g_free(dirpath); 
1861                 unlock_session();               
1862                 return NULL;});
1863
1864         separator = imap_get_path_separator(session, IMAP_FOLDER(folder), imap_path);
1865         imap_path_separator_subst(imap_path, separator);
1866         /* remove trailing / for display */
1867         strtailchomp(new_name, '/');
1868
1869         if (strcmp(dirpath, "INBOX") != 0) {
1870                 GPtrArray *argbuf;
1871                 gboolean exist = FALSE;
1872                 int r;
1873                 clist * lep_list;
1874                 
1875                 argbuf = g_ptr_array_new();
1876                 r = imap_threaded_list(folder, "", imap_path, &lep_list);
1877                 if (r != MAILIMAP_NO_ERROR) {
1878                         log_warning(_("can't create mailbox: LIST failed\n"));
1879                         g_free(imap_path);
1880                         g_free(dirpath);
1881                         ptr_array_free_strings(argbuf);
1882                         g_ptr_array_free(argbuf, TRUE);
1883                         unlock_session();
1884                         return NULL;
1885                 }
1886                 
1887                 if (clist_count(lep_list) > 0)
1888                         exist = TRUE;
1889                 mailimap_list_result_free(lep_list);
1890                 lep_list = NULL;
1891                 if (!exist) {
1892                         ok = imap_cmd_create(session, imap_path);
1893                         if (ok != IMAP_SUCCESS) {
1894                                 log_warning(_("can't create mailbox\n"));
1895                                 g_free(imap_path);
1896                                 g_free(dirpath);
1897                                 unlock_session();
1898                                 return NULL;
1899                         }
1900                         r = imap_threaded_list(folder, "", imap_path, &lep_list);
1901                         if (r == MAILIMAP_NO_ERROR) {
1902                                 GSList *item_list = imap_list_from_lep(IMAP_FOLDER(folder),
1903                                                lep_list, dirpath, TRUE);
1904                                 if (item_list) {
1905                                         FolderItem *cur_item = FOLDER_ITEM(item_list->data);
1906                                         no_select = cur_item->no_select;
1907                                         no_sub = cur_item->no_sub;
1908                                         g_slist_free(item_list);
1909                                 } 
1910                                 mailimap_list_result_free(lep_list);
1911                         }
1912
1913                 }
1914         } else {
1915                 clist *lep_list;
1916                 int r;
1917                 /* just get flags */
1918                 r = imap_threaded_list(folder, "", "INBOX", &lep_list);
1919                 if (r == MAILIMAP_NO_ERROR) {
1920                         GSList *item_list = imap_list_from_lep(IMAP_FOLDER(folder),
1921                                        lep_list, dirpath, TRUE);
1922                         if (item_list) {
1923                                 FolderItem *cur_item = FOLDER_ITEM(item_list->data);
1924                                 no_select = cur_item->no_select;
1925                                 no_sub = cur_item->no_sub;
1926                                 g_slist_free(item_list);
1927                         } 
1928                         mailimap_list_result_free(lep_list);
1929                 }
1930         }
1931
1932         new_item = folder_item_new(folder, new_name, dirpath);
1933         new_item->no_select = no_select;
1934         new_item->no_sub = no_sub;
1935         folder_item_append(parent, new_item);
1936         g_free(imap_path);
1937         g_free(dirpath);
1938
1939         dirpath = folder_item_get_path(new_item);
1940         if (!is_dir_exist(dirpath))
1941                 make_dir_hier(dirpath);
1942         g_free(dirpath);
1943         unlock_session();
1944         return new_item;
1945 }
1946
1947 static gint imap_rename_folder(Folder *folder, FolderItem *item,
1948                                const gchar *name)
1949 {
1950         gchar *dirpath;
1951         gchar *newpath;
1952         gchar *real_oldpath;
1953         gchar *real_newpath;
1954         gchar *paths[2];
1955         gchar *old_cache_dir;
1956         gchar *new_cache_dir;
1957         IMAPSession *session;
1958         gchar separator;
1959         gint ok;
1960         gint exists, recent, unseen;
1961         guint32 uid_validity;
1962
1963         g_return_val_if_fail(folder != NULL, -1);
1964         g_return_val_if_fail(item != NULL, -1);
1965         g_return_val_if_fail(item->path != NULL, -1);
1966         g_return_val_if_fail(name != NULL, -1);
1967
1968         debug_print("getting session...\n");
1969         session = imap_session_get(folder);
1970         if (!session) {
1971                 return -1;
1972         }
1973
1974         if (strchr(name, imap_get_path_separator(session, IMAP_FOLDER(folder), item->path)) != NULL) {
1975                 g_warning(_("New folder name must not contain the namespace "
1976                             "path separator"));
1977                 unlock_session();
1978                 return -1;
1979         }
1980
1981         real_oldpath = imap_get_real_path(session, IMAP_FOLDER(folder), item->path);
1982
1983         g_free(session->mbox);
1984         session->mbox = NULL;
1985         ok = imap_cmd_examine(session, "INBOX",
1986                               &exists, &recent, &unseen, &uid_validity, FALSE);
1987         if (ok != IMAP_SUCCESS) {
1988                 g_free(real_oldpath);
1989                 unlock_session();
1990                 return -1;
1991         }
1992
1993         separator = imap_get_path_separator(session, IMAP_FOLDER(folder), item->path);
1994         if (strchr(item->path, G_DIR_SEPARATOR)) {
1995                 dirpath = g_path_get_dirname(item->path);
1996                 newpath = g_strconcat(dirpath, G_DIR_SEPARATOR_S, name, NULL);
1997                 g_free(dirpath);
1998         } else
1999                 newpath = g_strdup(name);
2000
2001         real_newpath = imap_utf8_to_modified_utf7(newpath);
2002         imap_path_separator_subst(real_newpath, separator);
2003
2004         ok = imap_cmd_rename(session, real_oldpath, real_newpath);
2005         if (ok != IMAP_SUCCESS) {
2006                 log_warning(_("can't rename mailbox: %s to %s\n"),
2007                             real_oldpath, real_newpath);
2008                 g_free(real_oldpath);
2009                 g_free(newpath);
2010                 g_free(real_newpath);
2011                 unlock_session();
2012                 return -1;
2013         }
2014
2015         g_free(item->name);
2016         item->name = g_strdup(name);
2017
2018         old_cache_dir = folder_item_get_path(item);
2019
2020         paths[0] = g_strdup(item->path);
2021         paths[1] = newpath;
2022         g_node_traverse(item->node, G_PRE_ORDER, G_TRAVERSE_ALL, -1,
2023                         imap_rename_folder_func, paths);
2024
2025         if (is_dir_exist(old_cache_dir)) {
2026                 new_cache_dir = folder_item_get_path(item);
2027                 if (rename(old_cache_dir, new_cache_dir) < 0) {
2028                         FILE_OP_ERROR(old_cache_dir, "rename");
2029                 }
2030                 g_free(new_cache_dir);
2031         }
2032
2033         g_free(old_cache_dir);
2034         g_free(paths[0]);
2035         g_free(newpath);
2036         g_free(real_oldpath);
2037         g_free(real_newpath);
2038         unlock_session();
2039         return 0;
2040 }
2041
2042 static gint imap_remove_folder_real(Folder *folder, FolderItem *item)
2043 {
2044         gint ok;
2045         IMAPSession *session;
2046         gchar *path;
2047         gchar *cache_dir;
2048
2049         g_return_val_if_fail(folder != NULL, -1);
2050         g_return_val_if_fail(item != NULL, -1);
2051         g_return_val_if_fail(item->path != NULL, -1);
2052
2053         debug_print("getting session...\n");
2054         session = imap_session_get(folder);
2055         if (!session) {
2056                 return -1;
2057         }
2058         path = imap_get_real_path(session, IMAP_FOLDER(folder), item->path);
2059
2060         ok = imap_cmd_delete(session, path);
2061         if (ok != IMAP_SUCCESS) {
2062                 gchar *tmp = g_strdup_printf("%s%c", path, 
2063                                 imap_get_path_separator(session, IMAP_FOLDER(folder), path));
2064                 g_free(path);
2065                 path = tmp;
2066                 ok = imap_cmd_delete(session, path);
2067         }
2068
2069         if (ok != IMAP_SUCCESS) {
2070                 log_warning(_("can't delete mailbox\n"));
2071                 g_free(path);
2072                 unlock_session();
2073                 return -1;
2074         }
2075
2076         g_free(path);
2077         cache_dir = folder_item_get_path(item);
2078         if (is_dir_exist(cache_dir) && remove_dir_recursive(cache_dir) < 0)
2079                 g_warning("can't remove directory '%s'\n", cache_dir);
2080         g_free(cache_dir);
2081         folder_item_remove(item);
2082         unlock_session();
2083         return 0;
2084 }
2085
2086 static gint imap_remove_folder(Folder *folder, FolderItem *item)
2087 {
2088         GNode *node, *next;
2089
2090         g_return_val_if_fail(item != NULL, -1);
2091         g_return_val_if_fail(item->folder != NULL, -1);
2092         g_return_val_if_fail(item->node != NULL, -1);
2093
2094         node = item->node->children;
2095         while (node != NULL) {
2096                 next = node->next;
2097                 if (imap_remove_folder(folder, FOLDER_ITEM(node->data)) < 0)
2098                         return -1;
2099                 node = next;
2100         }
2101         debug_print("IMAP removing %s\n", item->path);
2102
2103         if (imap_remove_all_msg(folder, item) < 0)
2104                 return -1;
2105         return imap_remove_folder_real(folder, item);
2106 }
2107
2108 typedef struct _uncached_data {
2109         IMAPSession *session;
2110         FolderItem *item;
2111         MsgNumberList *numlist;
2112         guint cur;
2113         guint total;
2114         gboolean done;
2115 } uncached_data;
2116
2117 static void *imap_get_uncached_messages_thread(void *data)
2118 {
2119         uncached_data *stuff = (uncached_data *)data;
2120         IMAPSession *session = stuff->session;
2121         FolderItem *item = stuff->item;
2122         MsgNumberList *numlist = stuff->numlist;
2123         
2124         GSList *newlist = NULL;
2125         GSList *llast = NULL;
2126         GSList *seq_list, *cur;
2127
2128         debug_print("uncached_messages\n");
2129         
2130         if (session == NULL || item == NULL || item->folder == NULL
2131             || FOLDER_CLASS(item->folder) != &imap_class) {
2132                 stuff->done = TRUE;
2133                 return NULL;
2134         }
2135         
2136         seq_list = imap_get_lep_set_from_numlist(numlist);
2137         debug_print("get msgs info\n");
2138         for (cur = seq_list; cur != NULL; cur = g_slist_next(cur)) {
2139                 struct mailimap_set * imapset;
2140                 unsigned int i;
2141                 int r;
2142                 carray * env_list;
2143                 int count;
2144                 
2145                 imapset = cur->data;
2146                 
2147                 r = imap_threaded_fetch_env(session->folder,
2148                                             imapset, &env_list);
2149                 if (r != MAILIMAP_NO_ERROR)
2150                         continue;
2151                 
2152                 session_set_access_time(SESSION(session));
2153
2154                 count = 0;
2155                 for(i = 0 ; i < carray_count(env_list) ; i ++) {
2156                         struct imap_fetch_env_info * info;
2157                         MsgInfo * msginfo;
2158                         
2159                         info = carray_get(env_list, i);
2160                         msginfo = imap_envelope_from_lep(info, item);
2161                         if (msginfo == NULL)
2162                                 continue;
2163                         msginfo->folder = item;
2164                         if (!newlist)
2165                                 llast = newlist = g_slist_append(newlist, msginfo);
2166                         else {
2167                                 llast = g_slist_append(llast, msginfo);
2168                                 llast = llast->next;
2169                         }
2170                         count ++;
2171                 }
2172                 
2173                 imap_fetch_env_free(env_list);
2174         }
2175         
2176         for (cur = seq_list; cur != NULL; cur = g_slist_next(cur)) {
2177                 struct mailimap_set * imapset;
2178                 
2179                 imapset = cur->data;
2180                 mailimap_set_free(imapset);
2181         }
2182         
2183         session_set_access_time(SESSION(session));
2184         stuff->done = TRUE;
2185         return newlist;
2186 }
2187
2188 #define MAX_MSG_NUM 50
2189
2190 static GSList *imap_get_uncached_messages(IMAPSession *session,
2191                                         FolderItem *item,
2192                                         MsgNumberList *numlist)
2193 {
2194         GSList *result = NULL;
2195         GSList * cur;
2196         uncached_data *data = g_new0(uncached_data, 1);
2197         int finished;
2198         
2199         finished = 0;
2200         cur = numlist;
2201         data->total = g_slist_length(numlist);
2202         debug_print("messages list : %i\n", data->total);
2203
2204         while (cur != NULL) {
2205                 GSList * partial_result;
2206                 int count;
2207                 GSList * newlist;
2208                 GSList * llast;
2209                 
2210                 llast = NULL;
2211                 count = 0;
2212                 newlist = NULL;
2213                 while (count < MAX_MSG_NUM) {
2214                         void * p;
2215                         
2216                         p = cur->data;
2217                         
2218                         if (newlist == NULL)
2219                                 llast = newlist = g_slist_append(newlist, p);
2220                         else {
2221                                 llast = g_slist_append(llast, p);
2222                                 llast = llast->next;
2223                         }
2224                         count ++;
2225                         
2226                         cur = cur->next;
2227                         if (cur == NULL)
2228                                 break;
2229                 }
2230                 
2231                 data->done = FALSE;
2232                 data->session = session;
2233                 data->item = item;
2234                 data->numlist = newlist;
2235                 data->cur += count;
2236                 
2237                 if (prefs_common.work_offline && 
2238                     !inc_offline_should_override(
2239                         _("Claws Mail needs network access in order "
2240                           "to access the IMAP server."))) {
2241                         g_free(data);
2242                         return NULL;
2243                 }
2244                 
2245                 partial_result =
2246                         (GSList *)imap_get_uncached_messages_thread(data);
2247                 
2248                 statusbar_progress_all(data->cur,data->total, 1);
2249                 
2250                 g_slist_free(newlist);
2251                 
2252                 result = g_slist_concat(result, partial_result);
2253         }
2254         g_free(data);
2255         
2256         statusbar_progress_all(0,0,0);
2257         statusbar_pop_all();
2258         
2259         return result;
2260 }
2261
2262 static void imap_delete_all_cached_messages(FolderItem *item)
2263 {
2264         gchar *dir;
2265
2266         g_return_if_fail(item != NULL);
2267         g_return_if_fail(item->folder != NULL);
2268         g_return_if_fail(FOLDER_CLASS(item->folder) == &imap_class);
2269
2270         debug_print("Deleting all cached messages...\n");
2271
2272         dir = folder_item_get_path(item);
2273         if (is_dir_exist(dir))
2274                 remove_all_numbered_files(dir);
2275         g_free(dir);
2276
2277         debug_print("done.\n");
2278 }
2279
2280 gchar imap_get_path_separator_for_item(FolderItem *item)
2281 {
2282         Folder *folder = NULL;
2283         IMAPFolder *imap_folder = NULL;
2284         IMAPSession *session = NULL;
2285         gchar result = '/';
2286         
2287         if (!item)
2288                 return '/';
2289         folder = item->folder;
2290         
2291         if (!folder)
2292                 return '/';
2293         
2294         imap_folder = IMAP_FOLDER(folder);
2295         
2296         if (!imap_folder)
2297                 return '/';
2298         
2299         debug_print("getting session...");
2300         session = imap_session_get(FOLDER(folder));
2301         result = imap_get_path_separator(session, imap_folder, item->path);
2302         unlock_session();
2303         return result;
2304 }
2305
2306 static gchar imap_refresh_path_separator(IMAPSession *session, IMAPFolder *folder, const gchar *subfolder)
2307 {
2308         clist * lep_list;
2309         int r;
2310         gchar separator = '\0';
2311         
2312         g_return_val_if_fail(session != NULL, '/');
2313         r = imap_threaded_list((Folder *)folder, "", subfolder, &lep_list);
2314         
2315         if (r != MAILIMAP_NO_ERROR) {
2316                 log_warning(_("LIST failed\n"));
2317                 return '\0';
2318         }
2319
2320         if (clist_count(lep_list) > 0) {
2321                 clistiter * iter = clist_begin(lep_list); 
2322                 struct mailimap_mailbox_list * mb;
2323                 mb = clist_content(iter);
2324
2325                 separator = mb->mb_delimiter;
2326                 debug_print("got separator: %c\n", folder->last_seen_separator);
2327         }
2328         mailimap_list_result_free(lep_list);
2329         return separator;
2330 }
2331
2332 static gchar imap_get_path_separator(IMAPSession *session, IMAPFolder *folder, const gchar *path)
2333 {
2334         gchar separator = '/';
2335
2336         if (folder->last_seen_separator == 0) {
2337                 folder->last_seen_separator = imap_refresh_path_separator(session, folder, "");
2338         }
2339
2340         if (folder->last_seen_separator == 0) {
2341                 folder->last_seen_separator = imap_refresh_path_separator(session, folder, "INBOX");
2342         }
2343
2344         if (folder->last_seen_separator != 0) {
2345                 debug_print("using separator: %c\n", folder->last_seen_separator);
2346                 return folder->last_seen_separator;
2347         }
2348
2349         return separator;
2350 }
2351
2352 static gchar *imap_get_real_path(IMAPSession *session, IMAPFolder *folder, const gchar *path)
2353 {
2354         gchar *real_path;
2355         gchar separator;
2356
2357         g_return_val_if_fail(folder != NULL, NULL);
2358         g_return_val_if_fail(path != NULL, NULL);
2359
2360         real_path = imap_utf8_to_modified_utf7(path);
2361         separator = imap_get_path_separator(session, folder, path);
2362         imap_path_separator_subst(real_path, separator);
2363
2364         return real_path;
2365 }
2366
2367 static gint imap_set_message_flags(IMAPSession *session,
2368                                    MsgNumberList *numlist,
2369                                    IMAPFlags flags,
2370                                    gboolean is_set)
2371 {
2372         gint ok = 0;
2373         GSList *seq_list;
2374         GSList * cur;
2375
2376         seq_list = imap_get_lep_set_from_numlist(numlist);
2377         
2378         for(cur = seq_list ; cur != NULL ; cur = g_slist_next(cur)) {
2379                 struct mailimap_set * imapset;
2380                 
2381                 imapset = cur->data;
2382                 
2383                 ok = imap_cmd_store(session, imapset,
2384                                     flags, is_set);
2385         }
2386         
2387         imap_lep_set_free(seq_list);
2388         
2389         return IMAP_SUCCESS;
2390 }
2391
2392 typedef struct _select_data {
2393         IMAPSession *session;
2394         gchar *real_path;
2395         gint *exists;
2396         gint *recent;
2397         gint *unseen;
2398         guint32 *uid_validity;
2399         gboolean done;
2400 } select_data;
2401
2402 static gint imap_select(IMAPSession *session, IMAPFolder *folder,
2403                         const gchar *path,
2404                         gint *exists, gint *recent, gint *unseen,
2405                         guint32 *uid_validity, gboolean block)
2406 {
2407         gchar *real_path;
2408         gint ok;
2409         gint exists_, recent_, unseen_;
2410         guint32 uid_validity_;
2411         
2412         if (!exists && !recent && !unseen && !uid_validity) {
2413                 if (session->mbox && strcmp(session->mbox, path) == 0)
2414                         return IMAP_SUCCESS;
2415         }
2416         if (!exists)
2417                 exists = &exists_;
2418         if (!recent)
2419                 recent = &recent_;
2420         if (!unseen)
2421                 unseen = &unseen_;
2422         if (!uid_validity)
2423                 uid_validity = &uid_validity_;
2424
2425         g_free(session->mbox);
2426         session->mbox = NULL;
2427
2428         real_path = imap_get_real_path(session, folder, path);
2429
2430         ok = imap_cmd_select(session, real_path,
2431                              exists, recent, unseen, uid_validity, block);
2432         if (ok != IMAP_SUCCESS)
2433                 log_warning(_("can't select folder: %s\n"), real_path);
2434         else {
2435                 session->mbox = g_strdup(path);
2436                 session->folder_content_changed = FALSE;
2437         }
2438         g_free(real_path);
2439
2440         return ok;
2441 }
2442
2443 static gint imap_status(IMAPSession *session, IMAPFolder *folder,
2444                         const gchar *path, IMAPFolderItem *item,
2445                         gint *messages,
2446                         guint32 *uid_next, guint32 *uid_validity,
2447                         gint *unseen, gboolean block)
2448 {
2449         int r;
2450         clistiter * iter;
2451         struct mailimap_mailbox_data_status * data_status;
2452         int got_values;
2453         gchar *real_path;
2454         guint mask = 0;
2455         
2456         real_path = imap_get_real_path(session, folder, path);
2457
2458         if (messages) {
2459                 mask |= 1 << 0;
2460         }
2461         if (uid_next) {
2462                 mask |= 1 << 2;
2463         }
2464         if (uid_validity) {
2465                 mask |= 1 << 3;
2466         }
2467         if (unseen) {
2468                 mask |= 1 << 4;
2469         }
2470         r = imap_threaded_status(FOLDER(folder), real_path, 
2471                 &data_status, mask);
2472
2473         g_free(real_path);
2474         if (r != MAILIMAP_NO_ERROR) {
2475                 debug_print("status err %d\n", r);
2476                 return IMAP_ERROR;
2477         }
2478         
2479         if (data_status->st_info_list == NULL) {
2480                 mailimap_mailbox_data_status_free(data_status);
2481                 debug_print("status->st_info_list == NULL\n");
2482                 return IMAP_ERROR;
2483         }
2484         
2485         got_values = 0;
2486         for(iter = clist_begin(data_status->st_info_list) ; iter != NULL ;
2487             iter = clist_next(iter)) {
2488                 struct mailimap_status_info * info;             
2489                 
2490                 info = clist_content(iter);
2491                 switch (info->st_att) {
2492                 case MAILIMAP_STATUS_ATT_MESSAGES:
2493                         * messages = info->st_value;
2494                         got_values |= 1 << 0;
2495                         break;
2496                         
2497                 case MAILIMAP_STATUS_ATT_UIDNEXT:
2498                         * uid_next = info->st_value;
2499                         got_values |= 1 << 2;
2500                         break;
2501                         
2502                 case MAILIMAP_STATUS_ATT_UIDVALIDITY:
2503                         * uid_validity = info->st_value;
2504                         got_values |= 1 << 3;
2505                         break;
2506                         
2507                 case MAILIMAP_STATUS_ATT_UNSEEN:
2508                         * unseen = info->st_value;
2509                         got_values |= 1 << 4;
2510                         break;
2511                 }
2512         }
2513         mailimap_mailbox_data_status_free(data_status);
2514         
2515         if (got_values != mask) {
2516                 debug_print("status: incomplete values received (%d)\n", got_values);
2517                 return IMAP_ERROR;
2518         }
2519         return IMAP_SUCCESS;
2520 }
2521
2522 static void imap_free_capabilities(IMAPSession *session)
2523 {
2524         slist_free_strings(session->capability);
2525         g_slist_free(session->capability);
2526         session->capability = NULL;
2527 }
2528
2529 /* low-level IMAP4rev1 commands */
2530
2531 static gint imap_cmd_login(IMAPSession *session,
2532                            const gchar *user, const gchar *pass,
2533                            const gchar *type)
2534 {
2535         int r;
2536         gint ok;
2537
2538         if (!strcmp(type, "LOGIN") && imap_has_capability(session, "LOGINDISABLED")) {
2539                 gint ok = IMAP_ERROR;
2540                 if (imap_has_capability(session, "STARTTLS")) {
2541 #if USE_OPENSSL
2542                         log_warning(_("Server requires TLS to log in.\n"));
2543                         ok = imap_cmd_starttls(session);
2544                         if (ok != IMAP_SUCCESS) {
2545                                 log_warning(_("Can't start TLS session.\n"));
2546                                 return IMAP_ERROR;
2547                         } else {
2548                                 /* refresh capas */
2549                                 imap_free_capabilities(session);
2550                                 if (imap_get_capabilities(session) != MAILIMAP_NO_ERROR) {
2551                                         log_warning(_("Can't refresh capabilities.\n"));
2552                                         return IMAP_ERROR;
2553                                 }
2554                         }
2555 #else           
2556                         log_error(_("Connection to %s failed: "
2557                                         "server requires TLS, but Claws Mail "
2558                                         "has been compiled without OpenSSL "
2559                                         "support.\n"),
2560                                         SESSION(session)->server);
2561                         return IMAP_ERROR;
2562 #endif
2563                 } else {
2564                         log_error(_("Server logins are disabled.\n"));
2565                         return IMAP_ERROR;
2566                 }
2567         }
2568
2569         log_print("IMAP4> Logging %s to %s using %s\n", 
2570                         user,
2571                         SESSION(session)->server,
2572                         type);
2573         r = imap_threaded_login(session->folder, user, pass, type);
2574         if (r != MAILIMAP_NO_ERROR) {
2575                 log_print("IMAP4< Error logging in to %s\n",
2576                                 SESSION(session)->server);
2577                 ok = IMAP_ERROR;
2578         } else {
2579                 log_print("IMAP4< Login to %s successful\n",
2580                                 SESSION(session)->server);
2581                 ok = IMAP_SUCCESS;
2582         }
2583         return ok;
2584 }
2585
2586 static gint imap_cmd_noop(IMAPSession *session)
2587 {
2588         int r;
2589         unsigned int exists;
2590         
2591         r = imap_threaded_noop(session->folder, &exists);
2592         if (r != MAILIMAP_NO_ERROR) {
2593                 debug_print("noop err %d\n", r);
2594                 return IMAP_ERROR;
2595         }
2596         session->exists = exists;
2597         session_set_access_time(SESSION(session));
2598
2599         return IMAP_SUCCESS;
2600 }
2601
2602 #if USE_OPENSSL
2603 static gint imap_cmd_starttls(IMAPSession *session)
2604 {
2605         int r;
2606         
2607         r = imap_threaded_starttls(session->folder, 
2608                 SESSION(session)->server, SESSION(session)->port);
2609         if (r != MAILIMAP_NO_ERROR) {
2610                 debug_print("starttls err %d\n", r);
2611                 return IMAP_ERROR;
2612         }
2613         return IMAP_SUCCESS;
2614 }
2615 #endif
2616
2617 static gint imap_cmd_select(IMAPSession *session, const gchar *folder,
2618                             gint *exists, gint *recent, gint *unseen,
2619                             guint32 *uid_validity, gboolean block)
2620 {
2621         int r;
2622
2623         r = imap_threaded_select(session->folder, folder,
2624                                  exists, recent, unseen, uid_validity);
2625         if (r != MAILIMAP_NO_ERROR) {
2626                 debug_print("select err %d\n", r);
2627                 return IMAP_ERROR;
2628         }
2629         return IMAP_SUCCESS;
2630 }
2631
2632 static gint imap_cmd_examine(IMAPSession *session, const gchar *folder,
2633                              gint *exists, gint *recent, gint *unseen,
2634                              guint32 *uid_validity, gboolean block)
2635 {
2636         int r;
2637
2638         r = imap_threaded_examine(session->folder, folder,
2639                                   exists, recent, unseen, uid_validity);
2640         if (r != MAILIMAP_NO_ERROR) {
2641                 debug_print("examine err %d\n", r);
2642                 
2643                 return IMAP_ERROR;
2644         }
2645         return IMAP_SUCCESS;
2646 }
2647
2648 static gint imap_cmd_create(IMAPSession *session, const gchar *folder)
2649 {
2650         int r;
2651
2652         r = imap_threaded_create(session->folder, folder);
2653         if (r != MAILIMAP_NO_ERROR) {
2654                 
2655                 return IMAP_ERROR;
2656         }
2657
2658         return IMAP_SUCCESS;
2659 }
2660
2661 static gint imap_cmd_rename(IMAPSession *session, const gchar *old_folder,
2662                             const gchar *new_folder)
2663 {
2664         int r;
2665
2666         r = imap_threaded_rename(session->folder, old_folder,
2667                                  new_folder);
2668         if (r != MAILIMAP_NO_ERROR) {
2669                 
2670                 return IMAP_ERROR;
2671         }
2672
2673         return IMAP_SUCCESS;
2674 }
2675
2676 static gint imap_cmd_delete(IMAPSession *session, const gchar *folder)
2677 {
2678         int r;
2679         
2680
2681         r = imap_threaded_delete(session->folder, folder);
2682         if (r != MAILIMAP_NO_ERROR) {
2683                 
2684                 return IMAP_ERROR;
2685         }
2686
2687         return IMAP_SUCCESS;
2688 }
2689
2690 typedef struct _fetch_data {
2691         IMAPSession *session;
2692         guint32 uid;
2693         const gchar *filename;
2694         gboolean headers;
2695         gboolean body;
2696         gboolean done;
2697 } fetch_data;
2698
2699 static void *imap_cmd_fetch_thread(void *data)
2700 {
2701         fetch_data *stuff = (fetch_data *)data;
2702         IMAPSession *session = stuff->session;
2703         guint32 uid = stuff->uid;
2704         const gchar *filename = stuff->filename;
2705         int r;
2706         
2707         if (stuff->body) {
2708                 r = imap_threaded_fetch_content(session->folder,
2709                                                uid, 1, filename);
2710         }
2711         else {
2712                 r = imap_threaded_fetch_content(session->folder,
2713                                                 uid, 0, filename);
2714         }
2715         if (r != MAILIMAP_NO_ERROR) {
2716                 debug_print("fetch err %d\n", r);
2717                 return GINT_TO_POINTER(IMAP_ERROR);
2718         }
2719         return GINT_TO_POINTER(IMAP_SUCCESS);
2720 }
2721
2722 static gint imap_cmd_fetch(IMAPSession *session, guint32 uid,
2723                                 const gchar *filename, gboolean headers,
2724                                 gboolean body)
2725 {
2726         fetch_data *data = g_new0(fetch_data, 1);
2727         int result = 0;
2728         data->done = FALSE;
2729         data->session = session;
2730         data->uid = uid;
2731         data->filename = filename;
2732         data->headers = headers;
2733         data->body = body;
2734
2735         if (prefs_common.work_offline && 
2736             !inc_offline_should_override(
2737                 _("Claws Mail needs network access in order "
2738                   "to access the IMAP server."))) {
2739                 g_free(data);
2740                 return -1;
2741         }
2742         statusbar_print_all(_("Fetching message..."));
2743         result = GPOINTER_TO_INT(imap_cmd_fetch_thread(data));
2744         statusbar_pop_all();
2745         g_free(data);
2746         return result;
2747 }
2748
2749
2750 static gint imap_cmd_append(IMAPSession *session, const gchar *destfolder,
2751                             const gchar *file, IMAPFlags flags, 
2752                             guint32 *new_uid)
2753 {
2754         struct mailimap_flag_list * flag_list;
2755         int r;
2756         
2757         g_return_val_if_fail(file != NULL, IMAP_ERROR);
2758
2759         flag_list = imap_flag_to_lep(flags);
2760         r = imap_threaded_append(session->folder, destfolder,
2761                          file, flag_list, (int *)new_uid);
2762         mailimap_flag_list_free(flag_list);
2763
2764         if (r != MAILIMAP_NO_ERROR) {
2765                 debug_print("append err %d\n", r);
2766                 return IMAP_ERROR;
2767         }
2768         return IMAP_SUCCESS;
2769 }
2770
2771 static gint imap_cmd_copy(IMAPSession *session, struct mailimap_set * set,
2772                           const gchar *destfolder, GRelation *uid_mapping,
2773                           struct mailimap_set **source, struct mailimap_set **dest)
2774 {
2775         int r;
2776         
2777         g_return_val_if_fail(session != NULL, IMAP_ERROR);
2778         g_return_val_if_fail(set != NULL, IMAP_ERROR);
2779         g_return_val_if_fail(destfolder != NULL, IMAP_ERROR);
2780
2781         r = imap_threaded_copy(session->folder, set, destfolder, source, dest);
2782         if (r != MAILIMAP_NO_ERROR) {
2783                 
2784                 return IMAP_ERROR;
2785         }
2786
2787         return IMAP_SUCCESS;
2788 }
2789
2790 static gint imap_cmd_store(IMAPSession *session, struct mailimap_set * set,
2791                            IMAPFlags flags, int do_add)
2792 {
2793         int r;
2794         struct mailimap_flag_list * flag_list;
2795         struct mailimap_store_att_flags * store_att_flags;
2796         
2797         flag_list = imap_flag_to_lep(flags);
2798         
2799         if (do_add)
2800                 store_att_flags =
2801                         mailimap_store_att_flags_new_add_flags_silent(flag_list);
2802         else
2803                 store_att_flags =
2804                         mailimap_store_att_flags_new_remove_flags_silent(flag_list);
2805         
2806         r = imap_threaded_store(session->folder, set, store_att_flags);
2807         mailimap_store_att_flags_free(store_att_flags);
2808         if (r != MAILIMAP_NO_ERROR) {
2809                 
2810                 return IMAP_ERROR;
2811         }
2812         
2813         return IMAP_SUCCESS;
2814 }
2815
2816 static gint imap_cmd_expunge(IMAPSession *session)
2817 {
2818         int r;
2819         
2820         if (prefs_common.work_offline && 
2821             !inc_offline_should_override(
2822                 _("Claws Mail needs network access in order "
2823                   "to access the IMAP server."))) {
2824                 return -1;
2825         }
2826
2827         r = imap_threaded_expunge(session->folder);
2828         if (r != MAILIMAP_NO_ERROR) {
2829                 
2830                 return IMAP_ERROR;
2831         }
2832
2833         return IMAP_SUCCESS;
2834 }
2835
2836 static void imap_path_separator_subst(gchar *str, gchar separator)
2837 {
2838         gchar *p;
2839         gboolean in_escape = FALSE;
2840
2841         if (!separator || separator == '/') return;
2842
2843         for (p = str; *p != '\0'; p++) {
2844                 if (*p == '/' && !in_escape)
2845                         *p = separator;
2846                 else if (*p == '&' && *(p + 1) != '-' && !in_escape)
2847                         in_escape = TRUE;
2848                 else if (*p == '-' && in_escape)
2849                         in_escape = FALSE;
2850         }
2851 }
2852
2853 static gchar *imap_modified_utf7_to_utf8(const gchar *mutf7_str)
2854 {
2855         static iconv_t cd = (iconv_t)-1;
2856         static gboolean iconv_ok = TRUE;
2857         GString *norm_utf7;
2858         gchar *norm_utf7_p;
2859         size_t norm_utf7_len;
2860         const gchar *p;
2861         gchar *to_str, *to_p;
2862         size_t to_len;
2863         gboolean in_escape = FALSE;
2864
2865         if (!iconv_ok) return g_strdup(mutf7_str);
2866
2867         if (cd == (iconv_t)-1) {
2868                 cd = iconv_open(CS_INTERNAL, CS_UTF_7);
2869                 if (cd == (iconv_t)-1) {
2870                         g_warning("iconv cannot convert UTF-7 to %s\n",
2871                                   CS_INTERNAL);
2872                         iconv_ok = FALSE;
2873                         return g_strdup(mutf7_str);
2874                 }
2875         }
2876
2877         /* modified UTF-7 to normal UTF-7 conversion */
2878         norm_utf7 = g_string_new(NULL);
2879
2880         for (p = mutf7_str; *p != '\0'; p++) {
2881                 /* replace: '&'  -> '+',
2882                             "&-" -> '&',
2883                             escaped ','  -> '/' */
2884                 if (!in_escape && *p == '&') {
2885                         if (*(p + 1) != '-') {
2886                                 g_string_append_c(norm_utf7, '+');
2887                                 in_escape = TRUE;
2888                         } else {
2889                                 g_string_append_c(norm_utf7, '&');
2890                                 p++;
2891                         }
2892                 } else if (in_escape && *p == ',') {
2893                         g_string_append_c(norm_utf7, '/');
2894                 } else if (in_escape && *p == '-') {
2895                         g_string_append_c(norm_utf7, '-');
2896                         in_escape = FALSE;
2897                 } else {
2898                         g_string_append_c(norm_utf7, *p);
2899                 }
2900         }
2901
2902         norm_utf7_p = norm_utf7->str;
2903         norm_utf7_len = norm_utf7->len;
2904         to_len = strlen(mutf7_str) * 5;
2905         to_p = to_str = g_malloc(to_len + 1);
2906
2907         if (iconv(cd, (ICONV_CONST gchar **)&norm_utf7_p, &norm_utf7_len,
2908                   &to_p, &to_len) == -1) {
2909                 g_warning(_("iconv cannot convert UTF-7 to %s\n"),
2910                           conv_get_locale_charset_str());
2911                 g_string_free(norm_utf7, TRUE);
2912                 g_free(to_str);
2913                 return g_strdup(mutf7_str);
2914         }
2915
2916         /* second iconv() call for flushing */
2917         iconv(cd, NULL, NULL, &to_p, &to_len);
2918         g_string_free(norm_utf7, TRUE);
2919         *to_p = '\0';
2920
2921         return to_str;
2922 }
2923
2924 static gchar *imap_utf8_to_modified_utf7(const gchar *from)
2925 {
2926         static iconv_t cd = (iconv_t)-1;
2927         static gboolean iconv_ok = TRUE;
2928         gchar *norm_utf7, *norm_utf7_p;
2929         size_t from_len, norm_utf7_len;
2930         GString *to_str;
2931         gchar *from_tmp, *to, *p;
2932         gboolean in_escape = FALSE;
2933
2934         if (!iconv_ok) return g_strdup(from);
2935
2936         if (cd == (iconv_t)-1) {
2937                 cd = iconv_open(CS_UTF_7, CS_INTERNAL);
2938                 if (cd == (iconv_t)-1) {
2939                         g_warning(_("iconv cannot convert %s to UTF-7\n"),
2940                                   CS_INTERNAL);
2941                         iconv_ok = FALSE;
2942                         return g_strdup(from);
2943                 }
2944         }
2945
2946         /* UTF-8 to normal UTF-7 conversion */
2947         Xstrdup_a(from_tmp, from, return g_strdup(from));
2948         from_len = strlen(from);
2949         norm_utf7_len = from_len * 5;
2950         Xalloca(norm_utf7, norm_utf7_len + 1, return g_strdup(from));
2951         norm_utf7_p = norm_utf7;
2952
2953 #define IS_PRINT(ch) (isprint(ch) && IS_ASCII(ch))
2954
2955         while (from_len > 0) {
2956                 if (*from_tmp == '+') {
2957                         *norm_utf7_p++ = '+';
2958                         *norm_utf7_p++ = '-';
2959                         norm_utf7_len -= 2;
2960                         from_tmp++;
2961                         from_len--;
2962                 } else if (IS_PRINT(*(guchar *)from_tmp)) {
2963                         /* printable ascii char */
2964                         *norm_utf7_p = *from_tmp;
2965                         norm_utf7_p++;
2966                         norm_utf7_len--;
2967                         from_tmp++;
2968                         from_len--;
2969                 } else {
2970                         size_t conv_len = 0;
2971
2972                         /* unprintable char: convert to UTF-7 */
2973                         p = from_tmp;
2974                         while (!IS_PRINT(*(guchar *)p) && conv_len < from_len) {
2975                                 conv_len += g_utf8_skip[*(guchar *)p];
2976                                 p += g_utf8_skip[*(guchar *)p];
2977                         }
2978
2979                         from_len -= conv_len;
2980                         if (iconv(cd, (ICONV_CONST gchar **)&from_tmp,
2981                                   &conv_len,
2982                                   &norm_utf7_p, &norm_utf7_len) == -1) {
2983                                 g_warning(_("iconv cannot convert UTF-8 to UTF-7\n"));
2984                                 return g_strdup(from);
2985                         }
2986
2987                         /* second iconv() call for flushing */
2988                         iconv(cd, NULL, NULL, &norm_utf7_p, &norm_utf7_len);
2989                 }
2990         }
2991
2992 #undef IS_PRINT
2993
2994         *norm_utf7_p = '\0';
2995         to_str = g_string_new(NULL);
2996         for (p = norm_utf7; p < norm_utf7_p; p++) {
2997                 /* replace: '&' -> "&-",
2998                             '+' -> '&',
2999                             "+-" -> '+',
3000                             BASE64 '/' -> ',' */
3001                 if (!in_escape && *p == '&') {
3002                         g_string_append(to_str, "&-");
3003                 } else if (!in_escape && *p == '+') {
3004                         if (*(p + 1) == '-') {
3005                                 g_string_append_c(to_str, '+');
3006                                 p++;
3007                         } else {
3008                                 g_string_append_c(to_str, '&');
3009                                 in_escape = TRUE;
3010                         }
3011                 } else if (in_escape && *p == '/') {
3012                         g_string_append_c(to_str, ',');
3013                 } else if (in_escape && *p == '-') {
3014                         g_string_append_c(to_str, '-');
3015                         in_escape = FALSE;
3016                 } else {
3017                         g_string_append_c(to_str, *p);
3018                 }
3019         }
3020
3021         if (in_escape) {
3022                 in_escape = FALSE;
3023                 g_string_append_c(to_str, '-');
3024         }
3025
3026         to = to_str->str;
3027         g_string_free(to_str, FALSE);
3028
3029         return to;
3030 }
3031
3032 static gboolean imap_rename_folder_func(GNode *node, gpointer data)
3033 {
3034         FolderItem *item = node->data;
3035         gchar **paths = data;
3036         const gchar *oldpath = paths[0];
3037         const gchar *newpath = paths[1];
3038         gchar *base;
3039         gchar *new_itempath;
3040         gint oldpathlen;
3041
3042         oldpathlen = strlen(oldpath);
3043         if (strncmp(oldpath, item->path, oldpathlen) != 0) {
3044                 g_warning("path doesn't match: %s, %s\n", oldpath, item->path);
3045                 return TRUE;
3046         }
3047
3048         base = item->path + oldpathlen;
3049         while (*base == G_DIR_SEPARATOR) base++;
3050         if (*base == '\0')
3051                 new_itempath = g_strdup(newpath);
3052         else
3053                 new_itempath = g_strconcat(newpath, G_DIR_SEPARATOR_S, base,
3054                                            NULL);
3055         g_free(item->path);
3056         item->path = new_itempath;
3057
3058         return FALSE;
3059 }
3060
3061 typedef struct _get_list_uid_data {
3062         Folder *folder;
3063         IMAPSession *session;
3064         IMAPFolderItem *item;
3065         GSList **msgnum_list;
3066         gboolean done;
3067 } get_list_uid_data;
3068
3069 static void *get_list_of_uids_thread(void *data)
3070 {
3071         get_list_uid_data *stuff = (get_list_uid_data *)data;
3072         Folder *folder = stuff->folder;
3073         IMAPFolderItem *item = stuff->item;
3074         GSList **msgnum_list = stuff->msgnum_list;
3075         gint ok, nummsgs = 0, lastuid_old;
3076         IMAPSession *session;
3077         GSList *uidlist, *elem;
3078         clist * lep_uidlist;
3079         int r;
3080
3081         session = stuff->session;
3082         if (session == NULL) {
3083                 stuff->done = TRUE;
3084                 return GINT_TO_POINTER(-1);
3085         }
3086         /* no session locking here, it's already locked by caller */
3087         ok = imap_select(session, IMAP_FOLDER(folder), item->item.path,
3088                          NULL, NULL, NULL, NULL, TRUE);
3089         if (ok != IMAP_SUCCESS) {
3090                 stuff->done = TRUE;
3091                 return GINT_TO_POINTER(-1);
3092         }
3093
3094         uidlist = NULL;
3095         
3096         r = imap_threaded_search(folder, IMAP_SEARCH_TYPE_SIMPLE, NULL,
3097                                  &lep_uidlist);
3098         
3099         if (r == MAILIMAP_NO_ERROR) {
3100                 GSList * fetchuid_list;
3101                 
3102                 fetchuid_list =
3103                         imap_uid_list_from_lep(lep_uidlist);
3104                 mailimap_search_result_free(lep_uidlist);
3105                 
3106                 uidlist = g_slist_concat(fetchuid_list, uidlist);
3107         }
3108         else {
3109                 GSList * fetchuid_list;
3110                 carray * lep_uidtab;
3111                 
3112                 r = imap_threaded_fetch_uid(folder, item->lastuid + 1,
3113                                             &lep_uidtab);
3114                 if (r == MAILIMAP_NO_ERROR) {
3115                         fetchuid_list =
3116                                 imap_uid_list_from_lep_tab(lep_uidtab);
3117                         imap_fetch_uid_list_free(lep_uidtab);
3118                         uidlist = g_slist_concat(fetchuid_list, uidlist);
3119                 }
3120         }
3121         
3122         lastuid_old = item->lastuid;
3123         *msgnum_list = g_slist_copy(item->uid_list);
3124         nummsgs = g_slist_length(*msgnum_list);
3125         debug_print("Got %d uids from cache\n", g_slist_length(item->uid_list));
3126
3127         for (elem = uidlist; elem != NULL; elem = g_slist_next(elem)) {
3128                 guint msgnum;
3129
3130                 msgnum = GPOINTER_TO_INT(elem->data);
3131                 if (msgnum > lastuid_old) {
3132                         *msgnum_list = g_slist_prepend(*msgnum_list, GINT_TO_POINTER(msgnum));
3133                         item->uid_list = g_slist_prepend(item->uid_list, GINT_TO_POINTER(msgnum));
3134                         nummsgs++;
3135
3136                         if(msgnum > item->lastuid)
3137                                 item->lastuid = msgnum;
3138                 }
3139         }
3140         g_slist_free(uidlist);
3141         stuff->done = TRUE;
3142         return GINT_TO_POINTER(nummsgs);
3143 }
3144
3145 static gint get_list_of_uids(IMAPSession *session, Folder *folder, IMAPFolderItem *item, GSList **msgnum_list)
3146 {
3147         gint result;
3148         get_list_uid_data *data = g_new0(get_list_uid_data, 1);
3149         data->done = FALSE;
3150         data->folder = folder;
3151         data->item = item;
3152         data->msgnum_list = msgnum_list;
3153         data->session = session;
3154         if (prefs_common.work_offline && 
3155             !inc_offline_should_override(
3156                 _("Claws Mail needs network access in order "
3157                   "to access the IMAP server."))) {
3158                 g_free(data);
3159                 return -1;
3160         }
3161
3162         result = GPOINTER_TO_INT(get_list_of_uids_thread(data));
3163         g_free(data);
3164         return result;
3165
3166 }
3167
3168 gint imap_get_num_list(Folder *folder, FolderItem *_item, GSList **msgnum_list, gboolean *old_uids_valid)
3169 {
3170         IMAPFolderItem *item = (IMAPFolderItem *)_item;
3171         IMAPSession *session;
3172         gint ok, nummsgs = 0, exists;
3173         guint32 uid_next = 0, uid_val = 0;
3174         GSList *uidlist = NULL;
3175         gchar *dir;
3176         gboolean selected_folder;
3177         debug_print("get_num_list\n");
3178         
3179         g_return_val_if_fail(folder != NULL, -1);
3180         g_return_val_if_fail(item != NULL, -1);
3181         g_return_val_if_fail(item->item.path != NULL, -1);
3182         g_return_val_if_fail(FOLDER_CLASS(folder) == &imap_class, -1);
3183         g_return_val_if_fail(folder->account != NULL, -1);
3184
3185         debug_print("getting session...\n");
3186         session = imap_session_get(folder);
3187         g_return_val_if_fail(session != NULL, -1);
3188
3189         if (FOLDER_ITEM(item)->path) 
3190                 statusbar_print_all(_("Scanning folder %s%c%s ..."),
3191                                       FOLDER_ITEM(item)->folder->name, 
3192                                       G_DIR_SEPARATOR,
3193                                       FOLDER_ITEM(item)->path);
3194         else
3195                 statusbar_print_all(_("Scanning folder %s ..."),
3196                                       FOLDER_ITEM(item)->folder->name);
3197
3198         selected_folder = (session->mbox != NULL) &&
3199                           (!strcmp(session->mbox, item->item.path));
3200         if (selected_folder && time(NULL) - item->use_cache < 2) {
3201                 ok = imap_cmd_noop(session);
3202                 if (ok != IMAP_SUCCESS) {
3203                         debug_print("disconnected!\n");
3204                         session = imap_reconnect_if_possible(folder, session);
3205                         if (session == NULL) {
3206                                 statusbar_pop_all();
3207                                 unlock_session();
3208                                 return -1;
3209                         }
3210                 }
3211                 exists = session->exists;
3212
3213                 uid_next = item->c_uid_next;
3214                 uid_val = item->c_uid_validity;
3215                 *old_uids_valid = TRUE;
3216         } else {
3217                 if (item->use_cache && time(NULL) - item->use_cache < 2) {
3218                         exists = item->c_messages;
3219                         uid_next = item->c_uid_next;
3220                         uid_val = item->c_uid_validity;
3221                         ok = IMAP_SUCCESS;
3222                         debug_print("using cache %d %d %d\n", exists, uid_next, uid_val);
3223                 } else {
3224                         ok = imap_status(session, IMAP_FOLDER(folder), item->item.path, item,
3225                                  &exists, &uid_next, &uid_val, NULL, FALSE);
3226                 }
3227                 item->item.last_num = uid_next - 1;
3228                 
3229                 item->use_cache = (time_t)0;
3230                 if (ok != IMAP_SUCCESS) {
3231                         statusbar_pop_all();
3232                         unlock_session();
3233                         return -1;
3234                 }
3235                 if(item->item.mtime == uid_val)
3236                         *old_uids_valid = TRUE;
3237                 else {
3238                         *old_uids_valid = FALSE;
3239
3240                         debug_print("Freeing imap uid cache (%d != %d)\n",
3241                                         (int)item->item.mtime, uid_val);
3242                         item->lastuid = 0;
3243                         g_slist_free(item->uid_list);
3244                         item->uid_list = NULL;
3245                 
3246                         item->item.mtime = uid_val;
3247
3248                         imap_delete_all_cached_messages((FolderItem *)item);
3249                 }
3250         }
3251
3252         /* If old uid_next matches new uid_next we can be sure no message
3253            was added to the folder */
3254         debug_print("uid_next is %d and item->uid_next %d \n", 
3255                 uid_next, item->uid_next);
3256         if (uid_next == item->uid_next) {
3257                 nummsgs = g_slist_length(item->uid_list);
3258
3259                 /* If number of messages is still the same we
3260                    know our caches message numbers are still valid,
3261                    otherwise if the number of messages has decrease
3262                    we discard our cache to start a new scan to find
3263                    out which numbers have been removed */
3264                 if (exists == nummsgs) {
3265                         debug_print("exists == nummsgs\n");
3266                         *msgnum_list = g_slist_copy(item->uid_list);
3267                         statusbar_pop_all();
3268                         unlock_session();
3269                         return nummsgs;
3270                 } else if (exists < nummsgs) {
3271                         debug_print("Freeing imap uid cache");
3272                         item->lastuid = 0;
3273                         g_slist_free(item->uid_list);
3274                         item->uid_list = NULL;
3275                 }
3276         }
3277
3278         if (exists == 0) {
3279                 *msgnum_list = NULL;
3280                 statusbar_pop_all();
3281                 unlock_session();
3282                 return 0;
3283         }
3284
3285         nummsgs = get_list_of_uids(session, folder, item, &uidlist);
3286
3287         if (nummsgs < 0) {
3288                 statusbar_pop_all();
3289                 unlock_session();
3290                 return -1;
3291         }
3292
3293         if (nummsgs != exists) {
3294                 /* Cache contains more messages then folder, we have cached
3295                    an old UID of a message that was removed and new messages
3296                    have been added too, otherwise the uid_next check would
3297                    not have failed */
3298                 debug_print("Freeing imap uid cache");
3299                 item->lastuid = 0;
3300                 g_slist_free(item->uid_list);
3301                 item->uid_list = NULL;
3302
3303                 g_slist_free(*msgnum_list);
3304
3305                 nummsgs = get_list_of_uids(session, folder, item, &uidlist);
3306         }
3307
3308         *msgnum_list = uidlist;
3309
3310         dir = folder_item_get_path((FolderItem *)item);
3311         debug_print("removing old messages from %s\n", dir);
3312         remove_numbered_files_not_in_list(dir, *msgnum_list);
3313         g_free(dir);
3314         
3315         item->uid_next = uid_next;
3316         
3317         debug_print("get_num_list - ok - %i\n", nummsgs);
3318         statusbar_pop_all();
3319         unlock_session();
3320         return nummsgs;
3321 }
3322
3323 static MsgInfo *imap_parse_msg(const gchar *file, FolderItem *item)
3324 {
3325         MsgInfo *msginfo;
3326         MsgFlags flags;
3327
3328         flags.perm_flags = MSG_NEW|MSG_UNREAD;
3329         flags.tmp_flags = 0;
3330
3331         g_return_val_if_fail(item != NULL, NULL);
3332         g_return_val_if_fail(file != NULL, NULL);
3333
3334         if (folder_has_parent_of_type(item, F_QUEUE)) {
3335                 MSG_SET_TMP_FLAGS(flags, MSG_QUEUED);
3336         } else if (folder_has_parent_of_type(item, F_DRAFT)) {
3337                 MSG_SET_TMP_FLAGS(flags, MSG_DRAFT);
3338         }
3339
3340         msginfo = procheader_parse_file(file, flags, FALSE, FALSE);
3341         if (!msginfo) return NULL;
3342         
3343         msginfo->plaintext_file = g_strdup(file);
3344         msginfo->folder = item;
3345
3346         return msginfo;
3347 }
3348
3349 GSList *imap_get_msginfos(Folder *folder, FolderItem *item,
3350                           GSList *msgnum_list)
3351 {
3352         IMAPSession *session;
3353         MsgInfoList *ret = NULL;
3354         gint ok;
3355         
3356         debug_print("get_msginfos\n");
3357         
3358         g_return_val_if_fail(folder != NULL, NULL);
3359         g_return_val_if_fail(item != NULL, NULL);
3360         g_return_val_if_fail(msgnum_list != NULL, NULL);
3361
3362         debug_print("getting session...\n");
3363         session = imap_session_get(folder);
3364         g_return_val_if_fail(session != NULL, NULL);
3365
3366         debug_print("IMAP getting msginfos\n");
3367         ok = imap_select(session, IMAP_FOLDER(folder), item->path,
3368                          NULL, NULL, NULL, NULL, FALSE);
3369         if (ok != IMAP_SUCCESS) {
3370                 unlock_session();
3371                 return NULL;
3372         }
3373         if (!(folder_has_parent_of_type(item, F_DRAFT) || 
3374               folder_has_parent_of_type(item, F_QUEUE))) {
3375                 ret = g_slist_concat(ret,
3376                         imap_get_uncached_messages(session, item,
3377                                                    msgnum_list));
3378         } else {
3379                 MsgNumberList *sorted_list, *elem, *llast = NULL;
3380                 gint startnum, lastnum;
3381
3382                 sorted_list = g_slist_sort(g_slist_copy(msgnum_list), g_int_compare);
3383
3384                 startnum = lastnum = GPOINTER_TO_INT(sorted_list->data);
3385
3386                 llast = g_slist_last(ret);
3387                 for (elem = sorted_list;; elem = g_slist_next(elem)) {
3388                         guint num = 0;
3389
3390                         if (elem)
3391                                 num = GPOINTER_TO_INT(elem->data);
3392
3393                         if (num > lastnum + 1 || elem == NULL) {
3394                                 int i;
3395                                 for (i = startnum; i <= lastnum; ++i) {
3396                                         gchar *file;
3397                         
3398                                         file = imap_fetch_msg(folder, item, i);
3399                                         if (file != NULL) {
3400                                                 MsgInfo *msginfo = imap_parse_msg(file, item);
3401                                                 if (msginfo != NULL) {
3402                                                         msginfo->msgnum = i;
3403                                                         if (llast == NULL)
3404                                                                 llast = ret = g_slist_append(ret, msginfo);
3405                                                         else {
3406                                                                 llast = g_slist_append(llast, msginfo);
3407                                                                 llast = llast->next;
3408                                                         }
3409                                                 }
3410                                                 g_free(file);
3411                                         }
3412                                         session_set_access_time(SESSION(session));
3413                                 }
3414
3415                                 if (elem == NULL)
3416                                         break;
3417
3418                                 startnum = num;
3419                         }
3420                         lastnum = num;
3421                 }
3422
3423                 g_slist_free(sorted_list);
3424         }
3425         unlock_session();
3426         return ret;
3427 }
3428
3429 MsgInfo *imap_get_msginfo(Folder *folder, FolderItem *item, gint uid)
3430 {
3431         MsgInfo *msginfo = NULL;
3432         MsgInfoList *msginfolist;
3433         MsgNumberList numlist;
3434
3435         numlist.next = NULL;
3436         numlist.data = GINT_TO_POINTER(uid);
3437
3438         msginfolist = imap_get_msginfos(folder, item, &numlist);
3439         if (msginfolist != NULL) {
3440                 msginfo = msginfolist->data;
3441                 g_slist_free(msginfolist);
3442         }
3443
3444         return msginfo;
3445 }
3446
3447 gboolean imap_scan_required(Folder *folder, FolderItem *_item)
3448 {
3449         IMAPSession *session;
3450         IMAPFolderItem *item = (IMAPFolderItem *)_item;
3451         gint ok, exists = 0, unseen = 0;
3452         guint32 uid_next = 0, uid_val = 0;
3453         gboolean selected_folder;
3454         
3455         g_return_val_if_fail(folder != NULL, FALSE);
3456         g_return_val_if_fail(item != NULL, FALSE);
3457         g_return_val_if_fail(item->item.folder != NULL, FALSE);
3458         g_return_val_if_fail(FOLDER_CLASS(item->item.folder) == &imap_class, FALSE);
3459
3460         if (item->item.path == NULL)
3461                 return FALSE;
3462
3463         debug_print("getting session...\n");
3464         session = imap_session_get(folder);
3465         g_return_val_if_fail(session != NULL, FALSE);
3466
3467         selected_folder = (session->mbox != NULL) &&
3468                           (!strcmp(session->mbox, item->item.path));
3469         if (selected_folder && time(NULL) - item->use_cache < 2) {
3470                 ok = imap_cmd_noop(session);
3471                 if (ok != IMAP_SUCCESS) {
3472                         debug_print("disconnected!\n");
3473                         session = imap_reconnect_if_possible(folder, session);
3474                         if (session == NULL)
3475                                 return FALSE;
3476                 }
3477
3478                 if (session->folder_content_changed
3479                 ||  session->exists != item->item.total_msgs) {
3480                         unlock_session();
3481                         return TRUE;
3482                 }
3483         } else {
3484                 ok = imap_status(session, IMAP_FOLDER(folder), item->item.path, IMAP_FOLDER_ITEM(item),
3485                                  &exists, &uid_next, &uid_val, &unseen, FALSE);
3486                 if (ok != IMAP_SUCCESS) {
3487                         unlock_session();
3488                         return FALSE;
3489                 }
3490
3491                 item->use_cache = time(NULL);
3492                 item->c_messages = exists;
3493                 item->c_uid_next = uid_next;
3494                 item->c_uid_validity = uid_val;
3495                 item->c_unseen = unseen;
3496                 item->item.last_num = uid_next - 1;
3497                 debug_print("uidnext %d, item->uid_next %d, exists %d, item->item.total_msgs %d\n", 
3498                         uid_next, item->uid_next, exists, item->item.total_msgs);
3499                 if ((uid_next != item->uid_next) || (exists != item->item.total_msgs)
3500                     || unseen != item->item.unread_msgs || uid_val != item->item.mtime) {
3501                         unlock_session();
3502                         return TRUE;
3503                 }
3504         }
3505         unlock_session();
3506         return FALSE;
3507 }
3508
3509 void imap_change_flags(Folder *folder, FolderItem *item, MsgInfo *msginfo, MsgPermFlags newflags)
3510 {
3511         IMAPSession *session;
3512         IMAPFlags flags_set = 0, flags_unset = 0;
3513         gint ok = IMAP_SUCCESS;
3514         MsgNumberList numlist;
3515         hashtable_data *ht_data = NULL;
3516
3517         g_return_if_fail(folder != NULL);
3518         g_return_if_fail(folder->klass == &imap_class);
3519         g_return_if_fail(item != NULL);
3520         g_return_if_fail(item->folder == folder);
3521         g_return_if_fail(msginfo != NULL);
3522         g_return_if_fail(msginfo->folder == item);
3523
3524         if (!MSG_IS_MARKED(msginfo->flags) &&  (newflags & MSG_MARKED))
3525                 flags_set |= IMAP_FLAG_FLAGGED;
3526         if ( MSG_IS_MARKED(msginfo->flags) && !(newflags & MSG_MARKED))
3527                 flags_unset |= IMAP_FLAG_FLAGGED;
3528
3529         if (!MSG_IS_UNREAD(msginfo->flags) &&  (newflags & MSG_UNREAD))
3530                 flags_unset |= IMAP_FLAG_SEEN;
3531         if ( MSG_IS_UNREAD(msginfo->flags) && !(newflags & MSG_UNREAD))
3532                 flags_set |= IMAP_FLAG_SEEN;
3533
3534         if (!MSG_IS_REPLIED(msginfo->flags) &&  (newflags & MSG_REPLIED))
3535                 flags_set |= IMAP_FLAG_ANSWERED;
3536         if ( MSG_IS_REPLIED(msginfo->flags) && !(newflags & MSG_REPLIED))
3537                 flags_unset |= IMAP_FLAG_ANSWERED;
3538
3539         if (!MSG_IS_DELETED(msginfo->flags) &&  (newflags & MSG_DELETED))
3540                 flags_set |= IMAP_FLAG_DELETED;
3541         if ( MSG_IS_DELETED(msginfo->flags) && !(newflags & MSG_DELETED))
3542                 flags_unset |= IMAP_FLAG_DELETED;
3543
3544         if (!flags_set && !flags_unset) {
3545                 /* the changed flags were not translatable to IMAP-speak.
3546                  * like MSG_POSTFILTERED, so just apply. */
3547                 msginfo->flags.perm_flags = newflags;
3548                 return;
3549         }
3550
3551         debug_print("getting session...\n");
3552         session = imap_session_get(folder);
3553         if (!session) {
3554                 return;
3555         }
3556
3557         if ((ok = imap_select(session, IMAP_FOLDER(folder), msginfo->folder->path,
3558             NULL, NULL, NULL, NULL, FALSE)) != IMAP_SUCCESS) {
3559                 unlock_session();
3560                 return;
3561         }
3562         numlist.next = NULL;
3563         numlist.data = GINT_TO_POINTER(msginfo->msgnum);
3564
3565         if (IMAP_FOLDER_ITEM(item)->batching) {
3566                 /* instead of performing an UID STORE command for each message change,
3567                  * as a lot of them can change "together", we just fill in hashtables
3568                  * and defer the treatment so that we're able to send only one
3569                  * command.
3570                  */
3571                 debug_print("IMAP batch mode on, deferring flags change\n");
3572                 if (flags_set) {
3573                         ht_data = g_hash_table_lookup(IMAP_FOLDER_ITEM(item)->flags_set_table, 
3574                                 GINT_TO_POINTER(flags_set));
3575                         if (ht_data == NULL) {
3576                                 ht_data = g_new0(hashtable_data, 1);
3577                                 ht_data->session = session;
3578                                 ht_data->item = IMAP_FOLDER_ITEM(item);
3579                                 g_hash_table_insert(IMAP_FOLDER_ITEM(item)->flags_set_table, 
3580                                         GINT_TO_POINTER(flags_set), ht_data);
3581                         }
3582                         if (!g_slist_find(ht_data->msglist, GINT_TO_POINTER(msginfo->msgnum)))
3583                                 ht_data->msglist = g_slist_prepend(ht_data->msglist, GINT_TO_POINTER(msginfo->msgnum));
3584                 } 
3585                 if (flags_unset) {
3586                         ht_data = g_hash_table_lookup(IMAP_FOLDER_ITEM(item)->flags_unset_table, 
3587                                 GINT_TO_POINTER(flags_unset));
3588                         if (ht_data == NULL) {
3589                                 ht_data = g_new0(hashtable_data, 1);
3590                                 ht_data->session = session;
3591                                 ht_data->item = IMAP_FOLDER_ITEM(item);
3592                                 g_hash_table_insert(IMAP_FOLDER_ITEM(item)->flags_unset_table, 
3593                                         GINT_TO_POINTER(flags_unset), ht_data);
3594                         }
3595                         if (!g_slist_find(ht_data->msglist, GINT_TO_POINTER(msginfo->msgnum)))
3596                                 ht_data->msglist = g_slist_prepend(ht_data->msglist, 
3597                                         GINT_TO_POINTER(msginfo->msgnum));              
3598                 }
3599         } else {
3600                 debug_print("IMAP changing flags\n");
3601                 if (flags_set) {
3602                         ok = imap_set_message_flags(session, &numlist, flags_set, TRUE);
3603                         if (ok != IMAP_SUCCESS) {
3604                                 unlock_session();
3605                                 return;
3606                         }
3607                 }
3608
3609                 if (flags_unset) {
3610                         ok = imap_set_message_flags(session, &numlist, flags_unset, FALSE);
3611                         if (ok != IMAP_SUCCESS) {
3612                                 unlock_session();
3613                                 return;
3614                         }
3615                 }
3616         }
3617         msginfo->flags.perm_flags = newflags;
3618         unlock_session();
3619         return;
3620 }
3621
3622 static gint imap_remove_msg(Folder *folder, FolderItem *item, gint uid)
3623 {
3624         gint ok;
3625         IMAPSession *session;
3626         gchar *dir;
3627         MsgNumberList numlist;
3628         
3629         g_return_val_if_fail(folder != NULL, -1);
3630         g_return_val_if_fail(FOLDER_CLASS(folder) == &imap_class, -1);
3631         g_return_val_if_fail(item != NULL, -1);
3632
3633         debug_print("getting session...\n");
3634         session = imap_session_get(folder);
3635         if (!session) return -1;
3636
3637         ok = imap_select(session, IMAP_FOLDER(folder), item->path,
3638                          NULL, NULL, NULL, NULL, FALSE);
3639         if (ok != IMAP_SUCCESS) {
3640                 unlock_session();
3641                 return ok;
3642         }
3643         numlist.next = NULL;
3644         numlist.data = GINT_TO_POINTER(uid);
3645         
3646         ok = imap_set_message_flags
3647                 (IMAP_SESSION(REMOTE_FOLDER(folder)->session),
3648                 &numlist, IMAP_FLAG_DELETED, TRUE);
3649         if (ok != IMAP_SUCCESS) {
3650                 log_warning(_("can't set deleted flags: %d\n"), uid);
3651                 unlock_session();
3652                 return ok;
3653         }
3654
3655         if (!session->uidplus) {
3656                 ok = imap_cmd_expunge(session);
3657         } else {
3658                 gchar *uidstr;
3659
3660                 uidstr = g_strdup_printf("%u", uid);
3661                 ok = imap_cmd_expunge(session);
3662                 g_free(uidstr);
3663         }
3664         if (ok != IMAP_SUCCESS) {
3665                 log_warning(_("can't expunge\n"));
3666                 unlock_session();
3667                 return ok;
3668         }
3669
3670         IMAP_FOLDER_ITEM(item)->uid_list = g_slist_remove(
3671             IMAP_FOLDER_ITEM(item)->uid_list, numlist.data);
3672         dir = folder_item_get_path(item);
3673         if (is_dir_exist(dir))
3674                 remove_numbered_files(dir, uid, uid);
3675         g_free(dir);
3676         unlock_session();
3677         return IMAP_SUCCESS;
3678 }
3679
3680 static gint compare_msginfo(gconstpointer a, gconstpointer b)
3681 {
3682         return ((MsgInfo *)a)->msgnum - ((MsgInfo *)b)->msgnum;
3683 }
3684
3685 static guint gslist_find_next_num(MsgNumberList **list, guint num)
3686 {
3687         GSList *elem;
3688
3689         g_return_val_if_fail(list != NULL, -1);
3690
3691         for (elem = *list; elem != NULL; elem = g_slist_next(elem))
3692                 if (GPOINTER_TO_INT(elem->data) >= num)
3693                         break;
3694         *list = elem;
3695         return elem != NULL ? GPOINTER_TO_INT(elem->data) : (gint)-1;
3696 }
3697
3698 /*
3699  * NEW and DELETED flags are not syncronized
3700  * - The NEW/RECENT flags in IMAP folders can not really be directly
3701  *   modified by Sylpheed
3702  * - The DELETE/DELETED flag in IMAP and Sylpheed don't have the same
3703  *   meaning, in IMAP it always removes the messages from the FolderItem
3704  *   in Sylpheed it can mean to move the message to trash
3705  */
3706
3707 typedef struct _get_flags_data {
3708         Folder *folder;
3709         FolderItem *item;
3710         MsgInfoList *msginfo_list;
3711         GRelation *msgflags;
3712         gboolean full_search;
3713         gboolean done;
3714 } get_flags_data;
3715
3716 static /*gint*/ void *imap_get_flags_thread(void *data)
3717 {
3718         get_flags_data *stuff = (get_flags_data *)data;
3719         Folder *folder = stuff->folder;
3720         FolderItem *item = stuff->item;
3721         MsgInfoList *msginfo_list = stuff->msginfo_list;
3722         GRelation *msgflags = stuff->msgflags;
3723         gboolean full_search = stuff->full_search;
3724         IMAPSession *session;
3725         GSList *sorted_list = NULL;
3726         GSList *unseen = NULL, *answered = NULL, *flagged = NULL, *deleted = NULL;
3727         GSList *p_unseen, *p_answered, *p_flagged, *p_deleted;
3728         GSList *elem;
3729         GSList *seq_list, *cur;
3730         gboolean reverse_seen = FALSE;
3731         GString *cmd_buf;
3732         gint ok;
3733         gint exists_cnt, unseen_cnt;
3734         gboolean selected_folder;
3735         
3736         if (folder == NULL || item == NULL) {
3737                 stuff->done = TRUE;
3738                 return GINT_TO_POINTER(-1);
3739         }
3740
3741         debug_print("getting session...\n");
3742         session = imap_session_get(folder);
3743         if (session == NULL) {
3744                 stuff->done = TRUE;
3745                 return GINT_TO_POINTER(-1);
3746         }
3747
3748         selected_folder = (session->mbox != NULL) &&
3749                           (!strcmp(session->mbox, item->path));
3750
3751         if (!selected_folder) {
3752                 ok = imap_select(session, IMAP_FOLDER(folder), item->path,
3753                         &exists_cnt, NULL, &unseen_cnt, NULL, TRUE);
3754                 if (ok != IMAP_SUCCESS) {
3755                         stuff->done = TRUE;
3756                         unlock_session();
3757                         return GINT_TO_POINTER(-1);
3758                 }
3759
3760                 if (unseen_cnt > exists_cnt / 2)
3761                         reverse_seen = TRUE;
3762         } 
3763         else {
3764                 if (item->unread_msgs > item->total_msgs / 2)
3765                         reverse_seen = TRUE;
3766         }
3767
3768         cmd_buf = g_string_new(NULL);
3769
3770         sorted_list = g_slist_sort(g_slist_copy(msginfo_list), compare_msginfo);
3771         if (!full_search) {
3772                 seq_list = imap_get_lep_set_from_msglist(msginfo_list);
3773         } else {
3774                 struct mailimap_set * set;
3775                 set = mailimap_set_new_interval(1, 0);
3776                 seq_list = g_slist_append(NULL, set);
3777         }
3778
3779         for (cur = seq_list; cur != NULL; cur = g_slist_next(cur)) {
3780                 struct mailimap_set * imapset;
3781                 clist * lep_uidlist;
3782                 int r;
3783                 
3784                 imapset = cur->data;
3785                 if (reverse_seen) {
3786                         r = imap_threaded_search(folder, IMAP_SEARCH_TYPE_SEEN,
3787                                                  full_search ? NULL:imapset, &lep_uidlist);
3788                 }
3789                 else {
3790                         r = imap_threaded_search(folder,
3791                                                  IMAP_SEARCH_TYPE_UNSEEN,
3792                                                  full_search ? NULL:imapset, &lep_uidlist);
3793                 }
3794                 if (r == MAILIMAP_NO_ERROR) {
3795                         GSList * uidlist;
3796                         
3797                         uidlist = imap_uid_list_from_lep(lep_uidlist);
3798                         mailimap_search_result_free(lep_uidlist);
3799                         
3800                         unseen = g_slist_concat(unseen, uidlist);
3801                 }
3802                 
3803                 r = imap_threaded_search(folder, IMAP_SEARCH_TYPE_FLAGGED,
3804                                          full_search ? NULL:imapset, &lep_uidlist);
3805                 if (r == MAILIMAP_NO_ERROR) {
3806                         GSList * uidlist;
3807
3808                         uidlist = imap_uid_list_from_lep(lep_uidlist);
3809                         mailimap_search_result_free(lep_uidlist);
3810
3811                         flagged = g_slist_concat(flagged, uidlist);
3812                 }
3813
3814                 if (item->opened || item->processing_pending || item == folder->inbox) {
3815                         r = imap_threaded_search(folder, IMAP_SEARCH_TYPE_ANSWERED,
3816                                                  full_search ? NULL:imapset, &lep_uidlist);
3817                         if (r == MAILIMAP_NO_ERROR) {
3818                                 GSList * uidlist;
3819
3820                                 uidlist = imap_uid_list_from_lep(lep_uidlist);
3821                                 mailimap_search_result_free(lep_uidlist);
3822
3823                                 answered = g_slist_concat(answered, uidlist);
3824                         }
3825
3826                         r = imap_threaded_search(folder, IMAP_SEARCH_TYPE_DELETED,
3827                                                  full_search ? NULL:imapset, &lep_uidlist);
3828                         if (r == MAILIMAP_NO_ERROR) {
3829                                 GSList * uidlist;
3830
3831                                 uidlist = imap_uid_list_from_lep(lep_uidlist);
3832                                 mailimap_search_result_free(lep_uidlist);
3833
3834                                 deleted = g_slist_concat(deleted, uidlist);
3835                         }
3836                 }
3837         }
3838
3839         p_unseen = unseen;
3840         p_answered = answered;
3841         p_flagged = flagged;
3842         p_deleted = deleted;
3843
3844         for (elem = sorted_list; elem != NULL; elem = g_slist_next(elem)) {
3845                 MsgInfo *msginfo;
3846                 MsgPermFlags flags;
3847                 gboolean wasnew;
3848                 
3849                 msginfo = (MsgInfo *) elem->data;
3850                 flags = msginfo->flags.perm_flags;
3851                 wasnew = (flags & MSG_NEW);
3852                 if (item->opened || item->processing_pending || item == folder->inbox) {
3853                         flags &= ~((reverse_seen ? 0 : MSG_UNREAD | MSG_NEW) | MSG_REPLIED | MSG_MARKED);
3854                 } else {
3855                         flags &= ~((reverse_seen ? 0 : MSG_UNREAD | MSG_NEW | MSG_MARKED));
3856                 }
3857                 if (reverse_seen)
3858                         flags |= MSG_UNREAD | (wasnew ? MSG_NEW : 0);
3859                 if (gslist_find_next_num(&p_unseen, msginfo->msgnum) == msginfo->msgnum) {
3860                         if (!reverse_seen) {
3861                                 flags |= MSG_UNREAD | (wasnew ? MSG_NEW : 0);
3862                         } else {
3863                                 flags &= ~(MSG_UNREAD | MSG_NEW);
3864                         }
3865                 }
3866                 
3867                 if (gslist_find_next_num(&p_flagged, msginfo->msgnum) == msginfo->msgnum)
3868                         flags |= MSG_MARKED;
3869                 else
3870                         flags &= ~MSG_MARKED;
3871
3872                 if (item->opened || item->processing_pending || item == folder->inbox) {
3873                         if (gslist_find_next_num(&p_answered, msginfo->msgnum) == msginfo->msgnum)
3874                                 flags |= MSG_REPLIED;
3875                         else
3876                                 flags &= ~MSG_REPLIED;
3877                         if (gslist_find_next_num(&p_deleted, msginfo->msgnum) == msginfo->msgnum)
3878                                 flags |= MSG_DELETED;
3879                         else
3880                                 flags &= ~MSG_DELETED;
3881                 }
3882                 g_relation_insert(msgflags, msginfo, GINT_TO_POINTER(flags));
3883         }
3884
3885         imap_lep_set_free(seq_list);
3886         g_slist_free(flagged);
3887         g_slist_free(deleted);
3888         g_slist_free(answered);
3889         g_slist_free(unseen);
3890         g_slist_free(sorted_list);
3891         g_string_free(cmd_buf, TRUE);
3892
3893         stuff->done = TRUE;
3894         unlock_session();
3895         return GINT_TO_POINTER(0);
3896 }
3897
3898 static gint imap_get_flags(Folder *folder, FolderItem *item,
3899                            MsgInfoList *msginfo_list, GRelation *msgflags)
3900 {
3901         gint result;
3902         get_flags_data *data = g_new0(get_flags_data, 1);
3903         data->done = FALSE;
3904         data->folder = folder;
3905         data->item = item;
3906         data->msginfo_list = msginfo_list;
3907         data->msgflags = msgflags;
3908         data->full_search = FALSE;
3909
3910         GSList *tmp = NULL, *cur;
3911         
3912         if (prefs_common.work_offline && 
3913             !inc_offline_should_override(
3914                 _("Claws Mail needs network access in order "
3915                   "to access the IMAP server."))) {
3916                 g_free(data);
3917                 return -1;
3918         }
3919
3920         tmp = folder_item_get_msg_list(item);
3921
3922         if (g_slist_length(tmp) <= g_slist_length(msginfo_list))
3923                 data->full_search = TRUE;
3924         
3925         for (cur = tmp; cur; cur = cur->next)
3926                 procmsg_msginfo_free((MsgInfo *)cur->data);
3927         
3928         g_slist_free(tmp);
3929
3930         result = GPOINTER_TO_INT(imap_get_flags_thread(data));
3931         
3932         g_free(data);
3933         return result;
3934
3935 }
3936
3937 static gboolean process_flags(gpointer key, gpointer value, gpointer user_data)
3938 {
3939         gboolean flags_set = GPOINTER_TO_INT(user_data);
3940         gint flags_value = GPOINTER_TO_INT(key);
3941         hashtable_data *data = (hashtable_data *)value;
3942         IMAPFolderItem *_item = data->item;
3943         FolderItem *item = (FolderItem *)_item;
3944         gint ok = IMAP_ERROR;
3945         IMAPSession *session = NULL;
3946         
3947         debug_print("getting session...\n");
3948         session = imap_session_get(item->folder);
3949
3950         data->msglist = g_slist_reverse(data->msglist);
3951         
3952         debug_print("IMAP %ssetting flags to %d for %d messages\n",
3953                 flags_set?"":"un",
3954                 flags_value,
3955                 g_slist_length(data->msglist));
3956         
3957         if (session) {
3958                 ok = imap_select(session, IMAP_FOLDER(item->folder), item->path,
3959                          NULL, NULL, NULL, NULL, FALSE);
3960         }
3961         if (ok == IMAP_SUCCESS) {
3962                 imap_set_message_flags(data->session, data->msglist, flags_value, flags_set);
3963         } else {
3964                 g_warning("can't select mailbox %s\n", item->path);
3965         }
3966
3967         unlock_session();
3968         g_slist_free(data->msglist);    
3969         g_free(data);
3970         return TRUE;
3971 }
3972
3973 static void process_hashtable(IMAPFolderItem *item)
3974 {
3975         if (item->flags_set_table) {
3976                 g_hash_table_foreach_remove(item->flags_set_table, process_flags, GINT_TO_POINTER(TRUE));
3977                 g_hash_table_destroy(item->flags_set_table);
3978                 item->flags_set_table = NULL;
3979         }
3980         if (item->flags_unset_table) {
3981                 g_hash_table_foreach_remove(item->flags_unset_table, process_flags, GINT_TO_POINTER(FALSE));
3982                 g_hash_table_destroy(item->flags_unset_table);
3983                 item->flags_unset_table = NULL;
3984         }
3985 }
3986
3987 static void imap_set_batch (Folder *folder, FolderItem *_item, gboolean batch)
3988 {
3989         IMAPFolderItem *item = (IMAPFolderItem *)_item;
3990
3991         g_return_if_fail(item != NULL);
3992         
3993         if (item->batching == batch)
3994                 return;
3995         
3996         if (batch) {
3997                 item->batching = TRUE;
3998                 debug_print("IMAP switching to batch mode\n");
3999                 if (!item->flags_set_table) {
4000                         item->flags_set_table = g_hash_table_new(NULL, g_direct_equal);
4001                 }
4002                 if (!item->flags_unset_table) {
4003                         item->flags_unset_table = g_hash_table_new(NULL, g_direct_equal);
4004                 }
4005         } else {
4006                 debug_print("IMAP switching away from batch mode\n");
4007                 /* process stuff */
4008                 process_hashtable(item);
4009                 item->batching = FALSE;
4010         }
4011 }
4012
4013
4014
4015 /* data types conversion libetpan <-> claws */
4016
4017
4018
4019 #define ETPAN_IMAP_MB_MARKED      1
4020 #define ETPAN_IMAP_MB_UNMARKED    2
4021 #define ETPAN_IMAP_MB_NOSELECT    4
4022 #define ETPAN_IMAP_MB_NOINFERIORS 8
4023
4024 static int imap_flags_to_flags(struct mailimap_mbx_list_flags * imap_flags)
4025 {
4026   int flags;
4027   clistiter * cur;
4028   
4029   flags = 0;
4030   if (imap_flags->mbf_type == MAILIMAP_MBX_LIST_FLAGS_SFLAG) {
4031     switch (imap_flags->mbf_sflag) {
4032     case MAILIMAP_MBX_LIST_SFLAG_MARKED:
4033       flags |= ETPAN_IMAP_MB_MARKED;
4034       break;
4035     case MAILIMAP_MBX_LIST_SFLAG_NOSELECT:
4036       flags |= ETPAN_IMAP_MB_NOSELECT;
4037       break;
4038     case MAILIMAP_MBX_LIST_SFLAG_UNMARKED:
4039       flags |= ETPAN_IMAP_MB_UNMARKED;
4040       break;
4041     }
4042   }
4043   
4044   for(cur = clist_begin(imap_flags->mbf_oflags) ; cur != NULL ;
4045       cur = clist_next(cur)) {
4046     struct mailimap_mbx_list_oflag * oflag;
4047     
4048     oflag = clist_content(cur);
4049     
4050     switch (oflag->of_type) {
4051     case MAILIMAP_MBX_LIST_OFLAG_NOINFERIORS:
4052       flags |= ETPAN_IMAP_MB_NOINFERIORS;
4053       break;
4054     }
4055   }
4056   
4057   return flags;
4058 }
4059
4060 static GSList * imap_list_from_lep(IMAPFolder * folder,
4061                                    clist * list, const gchar * real_path, gboolean all)
4062 {
4063         clistiter * iter;
4064         GSList * item_list = NULL, *llast = NULL;
4065         
4066         for(iter = clist_begin(list) ; iter != NULL ;
4067             iter = clist_next(iter)) {
4068                 struct mailimap_mailbox_list * mb;
4069                 int flags;
4070                 char delimiter;
4071                 char * name;
4072                 char * dup_name;
4073                 gchar * base;
4074                 gchar * loc_name;
4075                 gchar * loc_path;
4076                 FolderItem *new_item;
4077                 
4078                 mb = clist_content(iter);
4079
4080                 if (mb == NULL)
4081                         continue;
4082
4083                 flags = 0;
4084                 if (mb->mb_flag != NULL)
4085                         flags = imap_flags_to_flags(mb->mb_flag);
4086                 
4087                 delimiter = mb->mb_delimiter;
4088                 name = mb->mb_name;
4089                 
4090                 dup_name = strdup(name);                
4091                 if (delimiter != '\0')
4092                         subst_char(dup_name, delimiter, '/');
4093                 
4094                 base = g_path_get_basename(dup_name);
4095                 if (base[0] == '.') {
4096                         g_free(base);
4097                         free(dup_name);
4098                         continue;
4099                 }
4100                 
4101                 if (!all && strcmp(dup_name, real_path) == 0) {
4102                         g_free(base);
4103                         free(dup_name);
4104                         continue;
4105                 }
4106
4107                 if (!all && dup_name[strlen(dup_name)-1] == '/') {
4108                         dup_name[strlen(dup_name)-1] = '\0';
4109                 }
4110                 
4111                 loc_name = imap_modified_utf7_to_utf8(base);
4112                 loc_path = imap_modified_utf7_to_utf8(dup_name);
4113                 
4114                 new_item = folder_item_new(FOLDER(folder), loc_name, loc_path);
4115                 if ((flags & ETPAN_IMAP_MB_NOINFERIORS) != 0)
4116                         new_item->no_sub = TRUE;
4117                 if (strcmp(dup_name, "INBOX") != 0 &&
4118                     ((flags & ETPAN_IMAP_MB_NOSELECT) != 0))
4119                         new_item->no_select = TRUE;
4120                 
4121                 if (item_list == NULL)
4122                         llast = item_list = g_slist_append(item_list, new_item);
4123                 else {
4124                         llast = g_slist_append(llast, new_item);
4125                         llast = llast->next;
4126                 }
4127                 debug_print("folder '%s' found.\n", loc_path);
4128                 g_free(base);
4129                 g_free(loc_path);
4130                 g_free(loc_name);
4131                 
4132                 free(dup_name);
4133         }
4134         
4135         return item_list;
4136 }
4137
4138 static GSList * imap_get_lep_set_from_numlist(MsgNumberList *numlist)
4139 {
4140         GSList *sorted_list, *cur;
4141         guint first, last, next;
4142         GSList *ret_list = NULL, *llast = NULL;
4143         unsigned int count;
4144         struct mailimap_set * current_set;
4145         unsigned int item_count;
4146         
4147         if (numlist == NULL)
4148                 return NULL;
4149         
4150         count = 0;
4151         current_set = mailimap_set_new_empty();
4152         
4153         sorted_list = g_slist_copy(numlist);
4154         sorted_list = g_slist_sort(sorted_list, g_int_compare);
4155
4156         first = GPOINTER_TO_INT(sorted_list->data);
4157         
4158         item_count = 0;
4159         for (cur = sorted_list; cur != NULL; cur = g_slist_next(cur)) {
4160                 if (GPOINTER_TO_INT(cur->data) == 0)
4161                         continue;
4162                 
4163                 item_count ++;
4164
4165                 last = GPOINTER_TO_INT(cur->data);
4166                 if (cur->next)
4167                         next = GPOINTER_TO_INT(cur->next->data);
4168                 else
4169                         next = 0;
4170
4171                 if (last + 1 != next || next == 0) {
4172
4173                         struct mailimap_set_item * item;
4174                         item = mailimap_set_item_new(first, last);
4175                         mailimap_set_add(current_set, item);
4176                         count ++;
4177                         
4178                         first = next;
4179                         
4180                         if (count >= IMAP_SET_MAX_COUNT) {
4181                                 if (ret_list == NULL)
4182                                         llast = ret_list = g_slist_append(ret_list,
4183                                                           current_set);
4184                                 else {
4185                                         llast = g_slist_append(llast, current_set);
4186                                         llast = llast->next;
4187                                 }
4188                                 current_set = mailimap_set_new_empty();
4189                                 count = 0;
4190                                 item_count = 0;
4191                         }
4192                 }
4193         }
4194         
4195         if (clist_count(current_set->set_list) > 0) {
4196                 ret_list = g_slist_append(ret_list,
4197                                           current_set);
4198         }
4199         
4200         g_slist_free(sorted_list);
4201
4202         return ret_list;
4203 }
4204
4205 static GSList * imap_get_lep_set_from_msglist(MsgInfoList *msglist)
4206 {
4207         MsgNumberList *numlist = NULL;
4208         MsgInfoList *cur;
4209         GSList *seq_list;
4210
4211         for (cur = msglist; cur != NULL; cur = g_slist_next(cur)) {
4212                 MsgInfo *msginfo = (MsgInfo *) cur->data;
4213
4214                 numlist = g_slist_prepend(numlist, GINT_TO_POINTER(msginfo->msgnum));
4215         }
4216         numlist = g_slist_reverse(numlist);
4217         seq_list = imap_get_lep_set_from_numlist(numlist);
4218         g_slist_free(numlist);
4219
4220         return seq_list;
4221 }
4222
4223 static GSList * imap_uid_list_from_lep(clist * list)
4224 {
4225         clistiter * iter;
4226         GSList * result;
4227         
4228         result = NULL;
4229         
4230         for(iter = clist_begin(list) ; iter != NULL ;
4231             iter = clist_next(iter)) {
4232                 uint32_t * puid;
4233                 
4234                 puid = clist_content(iter);
4235                 result = g_slist_prepend(result, GINT_TO_POINTER(* puid));
4236         }
4237         
4238         result = g_slist_reverse(result);
4239         return result;
4240 }
4241
4242 static GSList * imap_uid_list_from_lep_tab(carray * list)
4243 {
4244         unsigned int i;
4245         GSList * result;
4246         
4247         result = NULL;
4248         
4249         for(i = 0 ; i < carray_count(list) ; i ++) {
4250                 uint32_t * puid;
4251                 
4252                 puid = carray_get(list, i);
4253                 result = g_slist_prepend(result, GINT_TO_POINTER(* puid));
4254         }
4255         result = g_slist_reverse(result);
4256         return result;
4257 }
4258
4259 static MsgInfo *imap_envelope_from_lep(struct imap_fetch_env_info * info,
4260                                        FolderItem *item)
4261 {
4262         MsgInfo *msginfo = NULL;
4263         guint32 uid = 0;
4264         size_t size = 0;
4265         MsgFlags flags = {0, 0};
4266         
4267         if (info->headers == NULL)
4268                 return NULL;
4269
4270         MSG_SET_TMP_FLAGS(flags, MSG_IMAP);
4271         if (folder_has_parent_of_type(item, F_QUEUE)) {
4272                 MSG_SET_TMP_FLAGS(flags, MSG_QUEUED);
4273         } else if (folder_has_parent_of_type(item, F_DRAFT)) {
4274                 MSG_SET_TMP_FLAGS(flags, MSG_DRAFT);
4275         }
4276         flags.perm_flags = info->flags;
4277         
4278         uid = info->uid;
4279         size = info->size;
4280         msginfo = procheader_parse_str(info->headers, flags, FALSE, FALSE);
4281         
4282         if (msginfo) {
4283                 msginfo->msgnum = uid;
4284                 msginfo->size = size;
4285         }
4286
4287         return msginfo;
4288 }
4289
4290 static void imap_lep_set_free(GSList *seq_list)
4291 {
4292         GSList * cur;
4293         
4294         for(cur = seq_list ; cur != NULL ; cur = g_slist_next(cur)) {
4295                 struct mailimap_set * imapset;
4296                 
4297                 imapset = cur->data;
4298                 mailimap_set_free(imapset);
4299         }
4300         g_slist_free(seq_list);
4301 }
4302
4303 static struct mailimap_flag_list * imap_flag_to_lep(IMAPFlags flags)
4304 {
4305         struct mailimap_flag_list * flag_list;
4306         
4307         flag_list = mailimap_flag_list_new_empty();
4308         
4309         if (IMAP_IS_SEEN(flags))
4310                 mailimap_flag_list_add(flag_list,
4311                                        mailimap_flag_new_seen());
4312         if (IMAP_IS_ANSWERED(flags))
4313                 mailimap_flag_list_add(flag_list,
4314                                        mailimap_flag_new_answered());
4315         if (IMAP_IS_FLAGGED(flags))
4316                 mailimap_flag_list_add(flag_list,
4317                                        mailimap_flag_new_flagged());
4318         if (IMAP_IS_DELETED(flags))
4319                 mailimap_flag_list_add(flag_list,
4320                                        mailimap_flag_new_deleted());
4321         if (IMAP_IS_DRAFT(flags))
4322                 mailimap_flag_list_add(flag_list,
4323                                        mailimap_flag_new_draft());
4324         
4325         return flag_list;
4326 }
4327
4328 guint imap_folder_get_refcnt(Folder *folder)
4329 {
4330         return ((IMAPFolder *)folder)->refcnt;
4331 }
4332
4333 void imap_folder_ref(Folder *folder)
4334 {
4335         ((IMAPFolder *)folder)->refcnt++;
4336 }
4337
4338 void imap_disconnect_all(void)
4339 {
4340         GList *list;
4341         for (list = account_get_list(); list != NULL; list = list->next) {
4342                 PrefsAccount *account = list->data;
4343                 if (account->protocol == A_IMAP4) {
4344                         RemoteFolder *folder = (RemoteFolder *)account->folder;
4345                         if (folder && folder->session) {
4346                                 IMAPSession *session = (IMAPSession *)folder->session;
4347                                 imap_threaded_disconnect(FOLDER(folder));
4348                                 SESSION(session)->state = SESSION_DISCONNECTED;
4349                                 session_destroy(SESSION(session));
4350                                 folder->session = NULL;
4351                         }
4352                 }
4353         }
4354 }
4355
4356 void imap_folder_unref(Folder *folder)
4357 {
4358         if (((IMAPFolder *)folder)->refcnt > 0)
4359                 ((IMAPFolder *)folder)->refcnt--;
4360 }
4361
4362 #else /* HAVE_LIBETPAN */
4363
4364 static FolderClass imap_class;
4365
4366 static XMLTag *imap_item_get_xml(Folder *folder, FolderItem *item);
4367 static void imap_item_set_xml(Folder *folder, FolderItem *item, XMLTag *tag);
4368
4369 static Folder   *imap_folder_new        (const gchar    *name,
4370                                          const gchar    *path)
4371 {
4372         static gboolean missing_imap_warning = TRUE;
4373         if (missing_imap_warning) {
4374                 missing_imap_warning = FALSE;
4375                 alertpanel_error(
4376                         _("You have one or more IMAP accounts "
4377                           "defined. However this version of "
4378                           "Claws Mail has been built without "
4379                           "IMAP support; your IMAP account(s) are "
4380                           "disabled.\n\n"
4381                           "You probably need to "
4382                           "install libetpan and recompile "
4383                           "Claws Mail."));
4384         }
4385         return NULL;
4386 }
4387 static gint     imap_create_tree        (Folder         *folder)
4388 {
4389         return -1;
4390 }
4391 static FolderItem *imap_create_folder   (Folder         *folder,
4392                                          FolderItem     *parent,
4393                                          const gchar    *name)
4394 {
4395         return NULL;
4396 }
4397 static gint     imap_rename_folder      (Folder         *folder,
4398                                          FolderItem     *item, 
4399                                          const gchar    *name)
4400 {
4401         return -1;
4402 }
4403
4404 gchar imap_get_path_separator_for_item(FolderItem *item)
4405 {
4406         return '/';
4407 }
4408
4409 FolderClass *imap_get_class(void)
4410 {
4411         if (imap_class.idstr == NULL) {
4412                 imap_class.type = F_IMAP;
4413                 imap_class.idstr = "imap";
4414                 imap_class.uistr = "IMAP4";
4415
4416                 imap_class.new_folder = imap_folder_new;
4417                 imap_class.create_tree = imap_create_tree;
4418                 imap_class.create_folder = imap_create_folder;
4419                 imap_class.rename_folder = imap_rename_folder;
4420
4421                 imap_class.set_xml = folder_set_xml;
4422                 imap_class.get_xml = folder_get_xml;
4423                 imap_class.item_set_xml = imap_item_set_xml;
4424                 imap_class.item_get_xml = imap_item_get_xml;
4425                 /* nothing implemented */
4426         }
4427
4428         return &imap_class;
4429 }
4430
4431 void imap_disconnect_all(void)
4432 {
4433 }
4434
4435 #endif
4436
4437 void imap_synchronise(FolderItem *item) 
4438 {
4439         imap_gtk_synchronise(item);
4440 }
4441
4442 static void imap_item_set_xml(Folder *folder, FolderItem *item, XMLTag *tag)
4443 {
4444 #ifdef HAVE_LIBETPAN
4445         GList *cur;
4446 #endif
4447         folder_item_set_xml(folder, item, tag);
4448         
4449 #ifdef HAVE_LIBETPAN
4450         for (cur = tag->attr; cur != NULL; cur = g_list_next(cur)) {
4451                 XMLAttr *attr = (XMLAttr *) cur->data;
4452
4453                 if (!attr || !attr->name || !attr->value) continue;
4454                 if (!strcmp(attr->name, "uidnext"))
4455                         IMAP_FOLDER_ITEM(item)->uid_next = atoi(attr->value);
4456         }
4457 #endif
4458 }
4459
4460 static XMLTag *imap_item_get_xml(Folder *folder, FolderItem *item)
4461 {
4462         XMLTag *tag;
4463
4464         tag = folder_item_get_xml(folder, item);
4465
4466 #ifdef HAVE_LIBETPAN
4467         xml_tag_add_attr(tag, xml_attr_new_int("uidnext", 
4468                         IMAP_FOLDER_ITEM(item)->uid_next));
4469
4470 #endif
4471         return tag;
4472 }