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