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