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