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