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