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