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