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