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