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