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