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