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