1a2a7fa74924d76f6746218b1545e6477c7b1ce0
[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 message %s%c[%s] to %s ...\n",
1033                             src->path, G_DIR_SEPARATOR,
1034                             seq_set, destdir);
1035
1036                 ok = imap_cmd_copy(session, seq_set, destdir, uid_mapping);
1037                 if (ok != IMAP_SUCCESS) {
1038                         g_relation_destroy(uid_mapping);
1039                         imap_lep_set_free(seq_list);
1040                         return -1;
1041                 }
1042         }
1043
1044         for (cur = msglist; cur != NULL; cur = g_slist_next(cur)) {
1045                 MsgInfo *msginfo = (MsgInfo *)cur->data;
1046                 GTuples *tuples;
1047
1048                 tuples = g_relation_select(uid_mapping, 
1049                                            GINT_TO_POINTER(msginfo->msgnum),
1050                                            0);
1051                 if (tuples->len > 0) {
1052                         gint num = GPOINTER_TO_INT(g_tuples_index(tuples, 0, 1));
1053                         g_relation_insert(relation, msginfo,
1054                                           GPOINTER_TO_INT(num));
1055                         if (num > last_num)
1056                                 last_num = num;
1057                 } else
1058                         g_relation_insert(relation, msginfo,
1059                                           GPOINTER_TO_INT(0));
1060                 g_tuples_destroy(tuples);
1061         }
1062
1063         g_relation_destroy(uid_mapping);
1064         imap_lep_set_free(seq_list);
1065
1066         g_free(destdir);
1067         
1068         IMAP_FOLDER_ITEM(dest)->lastuid = 0;
1069         IMAP_FOLDER_ITEM(dest)->uid_next = 0;
1070         g_slist_free(IMAP_FOLDER_ITEM(dest)->uid_list);
1071         IMAP_FOLDER_ITEM(dest)->uid_list = NULL;
1072
1073         MUTEX_UNLOCK();
1074
1075         if (ok == IMAP_SUCCESS)
1076                 return last_num;
1077         else
1078                 return -1;
1079 }
1080
1081 static gint imap_copy_msg(Folder *folder, FolderItem *dest, MsgInfo *msginfo)
1082 {
1083         GSList msglist;
1084
1085         g_return_val_if_fail(msginfo != NULL, -1);
1086
1087         msglist.data = msginfo;
1088         msglist.next = NULL;
1089
1090         return imap_copy_msgs(folder, dest, &msglist, NULL);
1091 }
1092
1093 static gint imap_copy_msgs(Folder *folder, FolderItem *dest, 
1094                     MsgInfoList *msglist, GRelation *relation)
1095 {
1096         MsgInfo *msginfo;
1097         GSList *file_list;
1098         gint ret;
1099
1100         g_return_val_if_fail(folder != NULL, -1);
1101         g_return_val_if_fail(dest != NULL, -1);
1102         g_return_val_if_fail(msglist != NULL, -1);
1103
1104         msginfo = (MsgInfo *)msglist->data;
1105         g_return_val_if_fail(msginfo->folder != NULL, -1);
1106
1107         if (folder == msginfo->folder->folder) {
1108                 ret = imap_do_copy_msgs(folder, dest, msglist, relation);
1109                 return ret;
1110         }
1111
1112         file_list = procmsg_get_message_file_list(msglist);
1113         g_return_val_if_fail(file_list != NULL, -1);
1114
1115         ret = imap_add_msgs(folder, dest, file_list, relation);
1116
1117         procmsg_message_file_list_free(file_list);
1118
1119         return ret;
1120 }
1121
1122
1123 static gint imap_do_remove_msgs(Folder *folder, FolderItem *dest, 
1124                                 MsgInfoList *msglist, GRelation *relation)
1125 {
1126         gchar *destdir;
1127         GSList *seq_list = NULL, *cur;
1128         MsgInfo *msginfo;
1129         IMAPSession *session;
1130         gint ok = IMAP_SUCCESS;
1131         GRelation *uid_mapping;
1132         
1133         g_return_val_if_fail(folder != NULL, -1);
1134         g_return_val_if_fail(dest != NULL, -1);
1135         g_return_val_if_fail(msglist != NULL, -1);
1136
1137         MUTEX_TRYLOCK_OR_RETURN_VAL(-1);
1138
1139         session = imap_session_get(folder);
1140         if (!session) {
1141                 MUTEX_UNLOCK();
1142                 return -1;
1143         }
1144         msginfo = (MsgInfo *)msglist->data;
1145
1146         ok = imap_select(session, IMAP_FOLDER(folder), msginfo->folder->path,
1147                          NULL, NULL, NULL, NULL, FALSE);
1148         if (ok != IMAP_SUCCESS) {
1149                 MUTEX_UNLOCK();
1150                 return ok;
1151         }
1152
1153         destdir = imap_get_real_path(IMAP_FOLDER(folder), dest->path);
1154         for (cur = msglist; cur; cur = cur->next) {
1155                 msginfo = (MsgInfo *)cur->data;
1156                 seq_list = g_slist_append(seq_list, GINT_TO_POINTER(msginfo->msgnum));
1157         }
1158
1159         uid_mapping = g_relation_new(2);
1160         g_relation_index(uid_mapping, 0, g_direct_hash, g_direct_equal);
1161
1162         ok = imap_set_message_flags
1163                 (IMAP_SESSION(REMOTE_FOLDER(folder)->session),
1164                 seq_list, IMAP_FLAG_DELETED, TRUE);
1165         if (ok != IMAP_SUCCESS) {
1166                 log_warning(_("can't set deleted flags\n"));
1167                 MUTEX_UNLOCK();
1168                 return ok;
1169         }
1170         ok = imap_cmd_expunge(session);
1171         if (ok != IMAP_SUCCESS) {
1172                 log_warning(_("can't expunge\n"));
1173                 MUTEX_UNLOCK();
1174                 return ok;
1175         }
1176         
1177         g_relation_destroy(uid_mapping);
1178         g_slist_free(seq_list);
1179
1180         g_free(destdir);
1181
1182         MUTEX_UNLOCK();
1183         if (ok == IMAP_SUCCESS)
1184                 return 0;
1185         else
1186                 return -1;
1187 }
1188
1189 static gint imap_remove_msgs(Folder *folder, FolderItem *dest, 
1190                     MsgInfoList *msglist, GRelation *relation)
1191 {
1192         MsgInfo *msginfo;
1193
1194         g_return_val_if_fail(folder != NULL, -1);
1195         g_return_val_if_fail(dest != NULL, -1);
1196         if (msglist == NULL)
1197                 return 0;
1198
1199         msginfo = (MsgInfo *)msglist->data;
1200         g_return_val_if_fail(msginfo->folder != NULL, -1);
1201
1202         return imap_do_remove_msgs(folder, dest, msglist, relation);
1203 }
1204
1205 static gint imap_remove_all_msg(Folder *folder, FolderItem *item)
1206 {
1207         GSList *list = folder_item_get_msg_list(item);
1208         gint res = imap_remove_msgs(folder, item, list, NULL);
1209         g_slist_free(list);
1210         return res;
1211 }
1212
1213 static gboolean imap_is_msg_changed(Folder *folder, FolderItem *item,
1214                                     MsgInfo *msginfo)
1215 {
1216         /* TODO: properly implement this method */
1217         return FALSE;
1218 }
1219
1220 static gint imap_close(Folder *folder, FolderItem *item)
1221 {
1222         return 0;
1223 }
1224
1225 static gint imap_scan_tree(Folder *folder)
1226 {
1227         FolderItem *item = NULL;
1228         IMAPSession *session;
1229         gchar *root_folder = NULL;
1230
1231         g_return_val_if_fail(folder != NULL, -1);
1232         g_return_val_if_fail(folder->account != NULL, -1);
1233
1234         MUTEX_TRYLOCK_OR_RETURN_VAL(-1);
1235
1236         session = imap_session_get(folder);
1237         if (!session) {
1238                 if (!folder->node) {
1239                         folder_tree_destroy(folder);
1240                         item = folder_item_new(folder, folder->name, NULL);
1241                         item->folder = folder;
1242                         folder->node = item->node = g_node_new(item);
1243                 }
1244                 MUTEX_UNLOCK();
1245                 return -1;
1246         }
1247
1248         if (folder->account->imap_dir && *folder->account->imap_dir) {
1249                 gchar *real_path;
1250                 int r;
1251                 clist * lep_list;
1252
1253                 Xstrdup_a(root_folder, folder->account->imap_dir, {MUTEX_UNLOCK();return -1;});
1254                 extract_quote(root_folder, '"');
1255                 subst_char(root_folder,
1256                            imap_get_path_separator(IMAP_FOLDER(folder),
1257                                                    root_folder),
1258                            '/');
1259                 strtailchomp(root_folder, '/');
1260                 real_path = imap_get_real_path
1261                         (IMAP_FOLDER(folder), root_folder);
1262                 debug_print("IMAP root directory: %s\n", real_path);
1263
1264                 /* check if root directory exist */
1265
1266                 r = imap_threaded_list(session->folder, "", real_path,
1267                                        &lep_list);
1268                 if ((r != MAILIMAP_NO_ERROR) || (clist_count(lep_list) == 0)) {
1269                         if (!folder->node) {
1270                                 item = folder_item_new(folder, folder->name, NULL);
1271                                 item->folder = folder;
1272                                 folder->node = item->node = g_node_new(item);
1273                         }
1274                         MUTEX_UNLOCK();
1275                         return -1;
1276                 }
1277                 
1278                 mailimap_list_result_free(lep_list);
1279                 
1280                 g_free(real_path);
1281         }
1282
1283         if (folder->node)
1284                 item = FOLDER_ITEM(folder->node->data);
1285         if (!item || ((item->path || root_folder) &&
1286                       strcmp2(item->path, root_folder) != 0)) {
1287                 folder_tree_destroy(folder);
1288                 item = folder_item_new(folder, folder->name, root_folder);
1289                 item->folder = folder;
1290                 folder->node = item->node = g_node_new(item);
1291         }
1292
1293         imap_scan_tree_recursive(session, FOLDER_ITEM(folder->node->data));
1294         imap_create_missing_folders(folder);
1295
1296         MUTEX_UNLOCK();
1297         return 0;
1298 }
1299
1300 static gint imap_scan_tree_recursive(IMAPSession *session, FolderItem *item)
1301 {
1302         Folder *folder;
1303         IMAPFolder *imapfolder;
1304         FolderItem *new_item;
1305         GSList *item_list, *cur;
1306         GNode *node;
1307         gchar *real_path;
1308         gchar *wildcard_path;
1309         gchar separator;
1310         gchar wildcard[3];
1311         clist * lep_list;
1312         int r;
1313         
1314         g_return_val_if_fail(item != NULL, -1);
1315         g_return_val_if_fail(item->folder != NULL, -1);
1316         g_return_val_if_fail(item->no_sub == FALSE, -1);
1317
1318         folder = item->folder;
1319         imapfolder = IMAP_FOLDER(folder);
1320
1321         separator = imap_get_path_separator(imapfolder, item->path);
1322
1323         if (folder->ui_func)
1324                 folder->ui_func(folder, item, folder->ui_func_data);
1325
1326         if (item->path) {
1327                 wildcard[0] = separator;
1328                 wildcard[1] = '%';
1329                 wildcard[2] = '\0';
1330                 real_path = imap_get_real_path(imapfolder, item->path);
1331         } else {
1332                 wildcard[0] = '%';
1333                 wildcard[1] = '\0';
1334                 real_path = g_strdup("");
1335         }
1336
1337         Xstrcat_a(wildcard_path, real_path, wildcard,
1338                   {g_free(real_path); return IMAP_ERROR;});
1339         lep_list = NULL;
1340         r = imap_threaded_list(folder, "", wildcard_path, &lep_list);
1341         if (r != MAILIMAP_NO_ERROR) {
1342                 item_list = NULL;
1343         }
1344         else {
1345                 item_list = imap_list_from_lep(imapfolder,
1346                                                lep_list, real_path);
1347                 mailimap_list_result_free(lep_list);
1348         }
1349         
1350         g_free(real_path);
1351
1352         node = item->node->children;
1353         while (node != NULL) {
1354                 FolderItem *old_item = FOLDER_ITEM(node->data);
1355                 GNode *next = node->next;
1356
1357                 new_item = NULL;
1358                 for (cur = item_list; cur != NULL; cur = cur->next) {
1359                         FolderItem *cur_item = FOLDER_ITEM(cur->data);
1360                         if (!strcmp2(old_item->path, cur_item->path)) {
1361                                 new_item = cur_item;
1362                                 break;
1363                         }
1364                 }
1365                 if (!new_item) {
1366                         debug_print("folder '%s' not found. removing...\n",
1367                                     old_item->path);
1368                         folder_item_remove(old_item);
1369                 } else {
1370                         old_item->no_sub = new_item->no_sub;
1371                         old_item->no_select = new_item->no_select;
1372                         if (old_item->no_sub == TRUE && node->children) {
1373                                 debug_print("folder '%s' doesn't have "
1374                                             "subfolders. removing...\n",
1375                                             old_item->path);
1376                                 folder_item_remove_children(old_item);
1377                         }
1378                 }
1379
1380                 node = next;
1381         }
1382
1383         for (cur = item_list; cur != NULL; cur = cur->next) {
1384                 FolderItem *cur_item = FOLDER_ITEM(cur->data);
1385                 new_item = NULL;
1386                 for (node = item->node->children; node != NULL;
1387                      node = node->next) {
1388                         if (!strcmp2(FOLDER_ITEM(node->data)->path,
1389                                      cur_item->path)) {
1390                                 new_item = FOLDER_ITEM(node->data);
1391                                 folder_item_destroy(cur_item);
1392                                 cur_item = NULL;
1393                                 break;
1394                         }
1395                 }
1396                 if (!new_item) {
1397                         new_item = cur_item;
1398                         debug_print("new folder '%s' found.\n", new_item->path);
1399                         folder_item_append(item, new_item);
1400                 }
1401
1402                 if (!strcmp(new_item->path, "INBOX")) {
1403                         new_item->stype = F_INBOX;
1404                         folder->inbox = new_item;
1405                 } else if (!folder_item_parent(item) || item->stype == F_INBOX) {
1406                         gchar *base;
1407
1408                         base = g_path_get_basename(new_item->path);
1409
1410                         if (!folder->outbox && !g_ascii_strcasecmp(base, "Sent")) {
1411                                 new_item->stype = F_OUTBOX;
1412                                 folder->outbox = new_item;
1413                         } else if (!folder->draft && !g_ascii_strcasecmp(base, "Drafts")) {
1414                                 new_item->stype = F_DRAFT;
1415                                 folder->draft = new_item;
1416                         } else if (!folder->queue && !g_ascii_strcasecmp(base, "Queue")) {
1417                                 new_item->stype = F_QUEUE;
1418                                 folder->queue = new_item;
1419                         } else if (!folder->trash && !g_ascii_strcasecmp(base, "Trash")) {
1420                                 new_item->stype = F_TRASH;
1421                                 folder->trash = new_item;
1422                         }
1423                         g_free(base);
1424                 }
1425
1426                 if (new_item->no_sub == FALSE)
1427                         imap_scan_tree_recursive(session, new_item);
1428         }
1429
1430         g_slist_free(item_list);
1431
1432         return IMAP_SUCCESS;
1433 }
1434
1435 static gint imap_create_tree(Folder *folder)
1436 {
1437         g_return_val_if_fail(folder != NULL, -1);
1438         g_return_val_if_fail(folder->node != NULL, -1);
1439         g_return_val_if_fail(folder->node->data != NULL, -1);
1440         g_return_val_if_fail(folder->account != NULL, -1);
1441
1442         imap_scan_tree(folder);
1443         imap_create_missing_folders(folder);
1444
1445         return 0;
1446 }
1447
1448 static void imap_create_missing_folders(Folder *folder)
1449 {
1450         g_return_if_fail(folder != NULL);
1451
1452         if (!folder->inbox)
1453                 folder->inbox = imap_create_special_folder
1454                         (folder, F_INBOX, "INBOX");
1455         if (!folder->trash)
1456                 folder->trash = imap_create_special_folder
1457                         (folder, F_TRASH, "Trash");
1458 }
1459
1460 static FolderItem *imap_create_special_folder(Folder *folder,
1461                                               SpecialFolderItemType stype,
1462                                               const gchar *name)
1463 {
1464         FolderItem *item;
1465         FolderItem *new_item;
1466
1467         g_return_val_if_fail(folder != NULL, NULL);
1468         g_return_val_if_fail(folder->node != NULL, NULL);
1469         g_return_val_if_fail(folder->node->data != NULL, NULL);
1470         g_return_val_if_fail(folder->account != NULL, NULL);
1471         g_return_val_if_fail(name != NULL, NULL);
1472
1473         item = FOLDER_ITEM(folder->node->data);
1474         new_item = imap_create_folder(folder, item, name);
1475
1476         if (!new_item) {
1477                 g_warning("Can't create '%s'\n", name);
1478                 if (!folder->inbox) return NULL;
1479
1480                 new_item = imap_create_folder(folder, folder->inbox, name);
1481                 if (!new_item)
1482                         g_warning("Can't create '%s' under INBOX\n", name);
1483                 else
1484                         new_item->stype = stype;
1485         } else
1486                 new_item->stype = stype;
1487
1488         return new_item;
1489 }
1490
1491 static gchar *imap_folder_get_path(Folder *folder)
1492 {
1493         gchar *folder_path;
1494
1495         g_return_val_if_fail(folder != NULL, NULL);
1496         g_return_val_if_fail(folder->account != NULL, NULL);
1497
1498         folder_path = g_strconcat(get_imap_cache_dir(),
1499                                   G_DIR_SEPARATOR_S,
1500                                   folder->account->recv_server,
1501                                   G_DIR_SEPARATOR_S,
1502                                   folder->account->userid,
1503                                   NULL);
1504
1505         return folder_path;
1506 }
1507
1508 static gchar *imap_item_get_path(Folder *folder, FolderItem *item)
1509 {
1510         gchar *folder_path, *path;
1511
1512         g_return_val_if_fail(folder != NULL, NULL);
1513         g_return_val_if_fail(item != NULL, NULL);
1514         folder_path = imap_folder_get_path(folder);
1515
1516         g_return_val_if_fail(folder_path != NULL, NULL);
1517         if (folder_path[0] == G_DIR_SEPARATOR) {
1518                 if (item->path)
1519                         path = g_strconcat(folder_path, G_DIR_SEPARATOR_S,
1520                                            item->path, NULL);
1521                 else
1522                         path = g_strdup(folder_path);
1523         } else {
1524                 if (item->path)
1525                         path = g_strconcat(get_home_dir(), G_DIR_SEPARATOR_S,
1526                                            folder_path, G_DIR_SEPARATOR_S,
1527                                            item->path, NULL);
1528                 else
1529                         path = g_strconcat(get_home_dir(), G_DIR_SEPARATOR_S,
1530                                            folder_path, NULL);
1531         }
1532         g_free(folder_path);
1533
1534         return path;
1535 }
1536
1537 static FolderItem *imap_create_folder(Folder *folder, FolderItem *parent,
1538                                const gchar *name)
1539 {
1540         gchar *dirpath, *imap_path;
1541         IMAPSession *session;
1542         FolderItem *new_item;
1543         gchar separator;
1544         gchar *new_name;
1545         const gchar *p;
1546         gint ok;
1547
1548         g_return_val_if_fail(folder != NULL, NULL);
1549         g_return_val_if_fail(folder->account != NULL, NULL);
1550         g_return_val_if_fail(parent != NULL, NULL);
1551         g_return_val_if_fail(name != NULL, NULL);
1552
1553         MUTEX_TRYLOCK_OR_RETURN_VAL(NULL);
1554
1555         session = imap_session_get(folder);
1556         if (!session) {
1557                 MUTEX_UNLOCK();
1558                 return NULL;
1559         }
1560
1561         if (!folder_item_parent(parent) && strcmp(name, "INBOX") == 0)
1562                 dirpath = g_strdup(name);
1563         else if (parent->path)
1564                 dirpath = g_strconcat(parent->path, "/", name, NULL);
1565         else if ((p = strchr(name, '/')) != NULL && *(p + 1) != '\0')
1566                 dirpath = g_strdup(name);
1567         else if (folder->account->imap_dir && *folder->account->imap_dir) {
1568                 gchar *imap_dir;
1569
1570                 Xstrdup_a(imap_dir, folder->account->imap_dir, {MUTEX_UNLOCK();return NULL;});
1571                 strtailchomp(imap_dir, '/');
1572                 dirpath = g_strconcat(imap_dir, "/", name, NULL);
1573         } else
1574                 dirpath = g_strdup(name);
1575
1576         /* keep trailing directory separator to create a folder that contains
1577            sub folder */
1578         imap_path = imap_utf8_to_modified_utf7(dirpath);
1579         strtailchomp(dirpath, '/');
1580         Xstrdup_a(new_name, name, {
1581                 g_free(dirpath);                
1582                 MUTEX_UNLOCK();
1583                 return NULL;});
1584
1585         strtailchomp(new_name, '/');
1586         separator = imap_get_path_separator(IMAP_FOLDER(folder), imap_path);
1587         imap_path_separator_subst(imap_path, separator);
1588         subst_char(new_name, '/', separator);
1589
1590         if (strcmp(name, "INBOX") != 0) {
1591                 GPtrArray *argbuf;
1592                 gboolean exist = FALSE;
1593                 int r;
1594                 clist * lep_list;
1595                 
1596                 argbuf = g_ptr_array_new();
1597                 r = imap_threaded_list(folder, "", imap_path, &lep_list);
1598                 if (r != MAILIMAP_NO_ERROR) {
1599                         log_warning(_("can't create mailbox: LIST failed\n"));
1600                         g_free(imap_path);
1601                         g_free(dirpath);
1602                         ptr_array_free_strings(argbuf);
1603                         g_ptr_array_free(argbuf, TRUE);
1604                         MUTEX_UNLOCK();
1605                         return NULL;
1606                 }
1607                 
1608                 if (clist_count(lep_list) > 0)
1609                         exist = TRUE;
1610                 
1611                 if (!exist) {
1612                         ok = imap_cmd_create(session, imap_path);
1613                         if (ok != IMAP_SUCCESS) {
1614                                 log_warning(_("can't create mailbox\n"));
1615                                 g_free(imap_path);
1616                                 g_free(dirpath);
1617                                 MUTEX_UNLOCK();
1618                                 return NULL;
1619                         }
1620                 }
1621         }
1622
1623         new_item = folder_item_new(folder, new_name, dirpath);
1624         folder_item_append(parent, new_item);
1625         g_free(imap_path);
1626         g_free(dirpath);
1627
1628         dirpath = folder_item_get_path(new_item);
1629         if (!is_dir_exist(dirpath))
1630                 make_dir_hier(dirpath);
1631         g_free(dirpath);
1632
1633         MUTEX_UNLOCK();
1634         return new_item;
1635 }
1636
1637 static gint imap_rename_folder(Folder *folder, FolderItem *item,
1638                                const gchar *name)
1639 {
1640         gchar *dirpath;
1641         gchar *newpath;
1642         gchar *real_oldpath;
1643         gchar *real_newpath;
1644         gchar *paths[2];
1645         gchar *old_cache_dir;
1646         gchar *new_cache_dir;
1647         IMAPSession *session;
1648         gchar separator;
1649         gint ok;
1650         gint exists, recent, unseen;
1651         guint32 uid_validity;
1652
1653         g_return_val_if_fail(folder != NULL, -1);
1654         g_return_val_if_fail(item != NULL, -1);
1655         g_return_val_if_fail(item->path != NULL, -1);
1656         g_return_val_if_fail(name != NULL, -1);
1657
1658         MUTEX_TRYLOCK_OR_RETURN_VAL(-1);
1659
1660         if (strchr(name, imap_get_path_separator(IMAP_FOLDER(folder), item->path)) != NULL) {
1661                 g_warning(_("New folder name must not contain the namespace "
1662                             "path separator"));
1663                 MUTEX_UNLOCK();
1664                 return -1;
1665         }
1666
1667         session = imap_session_get(folder);
1668         if (!session) {
1669                 MUTEX_UNLOCK();
1670                 return -1;
1671         }
1672         real_oldpath = imap_get_real_path(IMAP_FOLDER(folder), item->path);
1673
1674         g_free(session->mbox);
1675         session->mbox = NULL;
1676         ok = imap_cmd_examine(session, "INBOX",
1677                               &exists, &recent, &unseen, &uid_validity, FALSE);
1678         if (ok != IMAP_SUCCESS) {
1679                 g_free(real_oldpath);
1680                 MUTEX_UNLOCK();
1681                 return -1;
1682         }
1683
1684         separator = imap_get_path_separator(IMAP_FOLDER(folder), item->path);
1685         if (strchr(item->path, G_DIR_SEPARATOR)) {
1686                 dirpath = g_path_get_dirname(item->path);
1687                 newpath = g_strconcat(dirpath, G_DIR_SEPARATOR_S, name, NULL);
1688                 g_free(dirpath);
1689         } else
1690                 newpath = g_strdup(name);
1691
1692         real_newpath = imap_utf8_to_modified_utf7(newpath);
1693         imap_path_separator_subst(real_newpath, separator);
1694
1695         ok = imap_cmd_rename(session, real_oldpath, real_newpath);
1696         if (ok != IMAP_SUCCESS) {
1697                 log_warning(_("can't rename mailbox: %s to %s\n"),
1698                             real_oldpath, real_newpath);
1699                 g_free(real_oldpath);
1700                 g_free(newpath);
1701                 g_free(real_newpath);
1702                 MUTEX_UNLOCK();
1703                 return -1;
1704         }
1705
1706         g_free(item->name);
1707         item->name = g_strdup(name);
1708
1709         old_cache_dir = folder_item_get_path(item);
1710
1711         paths[0] = g_strdup(item->path);
1712         paths[1] = newpath;
1713         g_node_traverse(item->node, G_PRE_ORDER, G_TRAVERSE_ALL, -1,
1714                         imap_rename_folder_func, paths);
1715
1716         if (is_dir_exist(old_cache_dir)) {
1717                 new_cache_dir = folder_item_get_path(item);
1718                 if (rename(old_cache_dir, new_cache_dir) < 0) {
1719                         FILE_OP_ERROR(old_cache_dir, "rename");
1720                 }
1721                 g_free(new_cache_dir);
1722         }
1723
1724         g_free(old_cache_dir);
1725         g_free(paths[0]);
1726         g_free(newpath);
1727         g_free(real_oldpath);
1728         g_free(real_newpath);
1729
1730         MUTEX_UNLOCK();
1731         return 0;
1732 }
1733
1734 static gint imap_remove_folder_real(Folder *folder, FolderItem *item)
1735 {
1736         gint ok;
1737         IMAPSession *session;
1738         gchar *path;
1739         gchar *cache_dir;
1740         gint exists, recent, unseen;
1741         guint32 uid_validity;
1742
1743         g_return_val_if_fail(folder != NULL, -1);
1744         g_return_val_if_fail(item != NULL, -1);
1745         g_return_val_if_fail(item->path != NULL, -1);
1746
1747         MUTEX_TRYLOCK_OR_RETURN_VAL(-1);
1748
1749         session = imap_session_get(folder);
1750         if (!session) {
1751                 MUTEX_UNLOCK();
1752                 return -1;
1753         }
1754         path = imap_get_real_path(IMAP_FOLDER(folder), item->path);
1755
1756         ok = imap_cmd_examine(session, "INBOX",
1757                               &exists, &recent, &unseen, &uid_validity, FALSE);
1758         if (ok != IMAP_SUCCESS) {
1759                 g_free(path);
1760                 MUTEX_UNLOCK();
1761                 return -1;
1762         }
1763
1764         ok = imap_cmd_delete(session, path);
1765         if (ok != IMAP_SUCCESS) {
1766                 log_warning(_("can't delete mailbox\n"));
1767                 g_free(path);
1768                 MUTEX_UNLOCK();
1769                 return -1;
1770         }
1771
1772         g_free(path);
1773         cache_dir = folder_item_get_path(item);
1774         if (is_dir_exist(cache_dir) && remove_dir_recursive(cache_dir) < 0)
1775                 g_warning("can't remove directory '%s'\n", cache_dir);
1776         g_free(cache_dir);
1777         folder_item_remove(item);
1778
1779         MUTEX_UNLOCK();
1780         return 0;
1781 }
1782
1783 static gint imap_remove_folder(Folder *folder, FolderItem *item)
1784 {
1785         GNode *node, *next;
1786
1787         g_return_val_if_fail(item != NULL, -1);
1788         g_return_val_if_fail(item->folder != NULL, -1);
1789         g_return_val_if_fail(item->node != NULL, -1);
1790
1791         node = item->node->children;
1792         while (node != NULL) {
1793                 next = node->next;
1794                 if (imap_remove_folder(folder, FOLDER_ITEM(node->data)) < 0)
1795                         return -1;
1796                 node = next;
1797         }
1798         debug_print("IMAP removing %s\n", item->path);
1799
1800         if (imap_remove_all_msg(folder, item) < 0)
1801                 return -1;
1802         return imap_remove_folder_real(folder, item);
1803 }
1804
1805 typedef struct _uncached_data {
1806         IMAPSession *session;
1807         FolderItem *item;
1808         MsgNumberList *numlist;
1809         guint cur;
1810         guint total;
1811         gboolean done;
1812 } uncached_data;
1813
1814 static void *imap_get_uncached_messages_thread(void *data)
1815 {
1816         uncached_data *stuff = (uncached_data *)data;
1817         IMAPSession *session = stuff->session;
1818         FolderItem *item = stuff->item;
1819         MsgNumberList *numlist = stuff->numlist;
1820         
1821         GSList *newlist = NULL;
1822         GSList *llast = NULL;
1823         GSList *seq_list, *cur;
1824
1825         debug_print("uncached_messages\n");
1826         
1827         if (session == NULL || item == NULL || item->folder == NULL
1828             || FOLDER_CLASS(item->folder) != &imap_class) {
1829                 stuff->done = TRUE;
1830                 return NULL;
1831         }
1832         
1833         seq_list = imap_get_lep_set_from_numlist(numlist);
1834         debug_print("get msgs info\n");
1835         for (cur = seq_list; cur != NULL; cur = g_slist_next(cur)) {
1836                 struct mailimap_set * imapset;
1837                 unsigned int i;
1838                 int r;
1839                 carray * env_list;
1840                 int count;
1841                 
1842                 imapset = cur->data;
1843                 
1844                 r = imap_threaded_fetch_env(session->folder,
1845                                             imapset, &env_list);
1846                 if (r != MAILIMAP_NO_ERROR)
1847                         continue;
1848                 
1849                 count = 0;
1850                 for(i = 0 ; i < carray_count(env_list) ; i ++) {
1851                         struct imap_fetch_env_info * info;
1852                         MsgInfo * msginfo;
1853                         
1854                         info = carray_get(env_list, i);
1855                         msginfo = imap_envelope_from_lep(info, item);
1856                         msginfo->folder = item;
1857                         if (!newlist)
1858                                 llast = newlist = g_slist_append(newlist, msginfo);
1859                         else {
1860                                 llast = g_slist_append(llast, msginfo);
1861                                 llast = llast->next;
1862                         }
1863                         count ++;
1864                 }
1865                 
1866                 imap_fetch_env_free(env_list);
1867         }
1868         
1869         session_set_access_time(SESSION(session));
1870         stuff->done = TRUE;
1871
1872         return newlist;
1873 }
1874
1875 #define MAX_MSG_NUM 50
1876
1877 static GSList *imap_get_uncached_messages(IMAPSession *session,
1878                                         FolderItem *item,
1879                                         MsgNumberList *numlist)
1880 {
1881         GSList *result = NULL;
1882         GSList * cur;
1883         uncached_data *data = g_new0(uncached_data, 1);
1884         int finished;
1885         
1886         finished = 0;
1887         cur = numlist;
1888         data->total = g_slist_length(numlist);
1889         debug_print("messages list : %i\n", data->total);
1890         while (cur != NULL) {
1891                 GSList * partial_result;
1892                 int count;
1893                 GSList * newlist;
1894                 GSList * llast;
1895                 
1896                 llast = NULL;
1897                 count = 0;
1898                 newlist = NULL;
1899                 while (count < MAX_MSG_NUM) {
1900                         void * p;
1901                         
1902                         p = cur->data;
1903                         
1904                         if (newlist == NULL)
1905                                 llast = newlist = g_slist_append(newlist, p);
1906                         else {
1907                                 llast = g_slist_append(llast, p);
1908                                 llast = llast->next;
1909                         }
1910                         count ++;
1911                         
1912                         cur = cur->next;
1913                         if (cur == NULL)
1914                                 break;
1915                 }
1916                 
1917                 data->done = FALSE;
1918                 data->session = session;
1919                 data->item = item;
1920                 data->numlist = newlist;
1921                 data->cur += count;
1922                 
1923                 if (prefs_common.work_offline && !imap_gtk_should_override()) {
1924                         g_free(data);
1925                         return NULL;
1926                 }
1927                 
1928                 partial_result =
1929                         (GSList *)imap_get_uncached_messages_thread(data);
1930                 
1931                 {
1932                         gchar buf[32];
1933                         g_snprintf(buf, sizeof(buf), "%d / %d",
1934                                    data->cur, data->total);
1935                         gtk_progress_bar_set_text
1936                                 (GTK_PROGRESS_BAR(mainwindow_get_mainwindow()->progressbar), buf);
1937                         gtk_progress_bar_set_fraction
1938                                 (GTK_PROGRESS_BAR(mainwindow_get_mainwindow()->progressbar),
1939                                  (gfloat)data->cur / (gfloat)data->total);
1940                         debug_print("update progress %g\n",
1941                                 (gfloat)data->cur / (gfloat)data->total);
1942                 }
1943                 
1944                 g_slist_free(newlist);
1945                 
1946                 result = g_slist_concat(result, partial_result);
1947         }
1948         g_free(data);
1949         
1950         gtk_progress_bar_set_fraction
1951                 (GTK_PROGRESS_BAR(mainwindow_get_mainwindow()->progressbar), 0);
1952         gtk_progress_bar_set_text
1953                 (GTK_PROGRESS_BAR(mainwindow_get_mainwindow()->progressbar), "");
1954         statusbar_pop_all();
1955         
1956         return result;
1957 }
1958
1959 static void imap_delete_all_cached_messages(FolderItem *item)
1960 {
1961         gchar *dir;
1962
1963         g_return_if_fail(item != NULL);
1964         g_return_if_fail(item->folder != NULL);
1965         g_return_if_fail(FOLDER_CLASS(item->folder) == &imap_class);
1966
1967         debug_print("Deleting all cached messages...\n");
1968
1969         dir = folder_item_get_path(item);
1970         if (is_dir_exist(dir))
1971                 remove_all_numbered_files(dir);
1972         g_free(dir);
1973
1974         debug_print("done.\n");
1975 }
1976
1977 static IMAPNameSpace *imap_find_namespace_from_list(GList *ns_list,
1978                                                     const gchar *path)
1979 {
1980         IMAPNameSpace *namespace = NULL;
1981         gchar *tmp_path, *name;
1982
1983         if (!path) path = "";
1984
1985         for (; ns_list != NULL; ns_list = ns_list->next) {
1986                 IMAPNameSpace *tmp_ns = ns_list->data;
1987
1988                 Xstrcat_a(tmp_path, path, "/", return namespace);
1989                 Xstrdup_a(name, tmp_ns->name, return namespace);
1990                 if (tmp_ns->separator && tmp_ns->separator != '/') {
1991                         subst_char(tmp_path, tmp_ns->separator, '/');
1992                         subst_char(name, tmp_ns->separator, '/');
1993                 }
1994                 if (strncmp(tmp_path, name, strlen(name)) == 0)
1995                         namespace = tmp_ns;
1996         }
1997
1998         return namespace;
1999 }
2000
2001 static IMAPNameSpace *imap_find_namespace(IMAPFolder *folder,
2002                                           const gchar *path)
2003 {
2004         IMAPNameSpace *namespace;
2005
2006         g_return_val_if_fail(folder != NULL, NULL);
2007
2008         namespace = imap_find_namespace_from_list(folder->ns_personal, path);
2009         if (namespace) return namespace;
2010         namespace = imap_find_namespace_from_list(folder->ns_others, path);
2011         if (namespace) return namespace;
2012         namespace = imap_find_namespace_from_list(folder->ns_shared, path);
2013         if (namespace) return namespace;
2014
2015         return NULL;
2016 }
2017
2018 static gchar imap_get_path_separator(IMAPFolder *folder, const gchar *path)
2019 {
2020         IMAPNameSpace *namespace;
2021         gchar separator = '/';
2022
2023         namespace = imap_find_namespace(folder, path);
2024         if (namespace && namespace->separator)
2025                 separator = namespace->separator;
2026
2027         return separator;
2028 }
2029
2030 static gchar *imap_get_real_path(IMAPFolder *folder, const gchar *path)
2031 {
2032         gchar *real_path;
2033         gchar separator;
2034
2035         g_return_val_if_fail(folder != NULL, NULL);
2036         g_return_val_if_fail(path != NULL, NULL);
2037
2038         real_path = imap_utf8_to_modified_utf7(path);
2039         separator = imap_get_path_separator(folder, path);
2040         imap_path_separator_subst(real_path, separator);
2041
2042         return real_path;
2043 }
2044
2045 static gint imap_set_message_flags(IMAPSession *session,
2046                                    MsgNumberList *numlist,
2047                                    IMAPFlags flags,
2048                                    gboolean is_set)
2049 {
2050         gint ok = 0;
2051         GSList *seq_list;
2052         GSList * cur;
2053
2054         seq_list = imap_get_lep_set_from_numlist(numlist);
2055         
2056         for(cur = seq_list ; cur != NULL ; cur = g_slist_next(cur)) {
2057                 struct mailimap_set * imapset;
2058                 
2059                 imapset = cur->data;
2060                 
2061                 ok = imap_cmd_store(session, imapset,
2062                                     flags, is_set);
2063         }
2064         
2065         imap_lep_set_free(seq_list);
2066         
2067         return IMAP_SUCCESS;
2068 }
2069
2070 typedef struct _select_data {
2071         IMAPSession *session;
2072         gchar *real_path;
2073         gint *exists;
2074         gint *recent;
2075         gint *unseen;
2076         guint32 *uid_validity;
2077         gboolean done;
2078 } select_data;
2079
2080 static gint imap_select(IMAPSession *session, IMAPFolder *folder,
2081                         const gchar *path,
2082                         gint *exists, gint *recent, gint *unseen,
2083                         guint32 *uid_validity, gboolean block)
2084 {
2085         gchar *real_path;
2086         gint ok;
2087         gint exists_, recent_, unseen_;
2088         guint32 uid_validity_;
2089         
2090         if (!exists || !recent || !unseen || !uid_validity) {
2091                 if (session->mbox && strcmp(session->mbox, path) == 0)
2092                         return IMAP_SUCCESS;
2093                 exists = &exists_;
2094                 recent = &recent_;
2095                 unseen = &unseen_;
2096                 uid_validity = &uid_validity_;
2097         }
2098
2099         g_free(session->mbox);
2100         session->mbox = NULL;
2101
2102         real_path = imap_get_real_path(folder, path);
2103
2104         ok = imap_cmd_select(session, real_path,
2105                              exists, recent, unseen, uid_validity, block);
2106         if (ok != IMAP_SUCCESS)
2107                 log_warning(_("can't select folder: %s\n"), real_path);
2108         else {
2109                 session->mbox = g_strdup(path);
2110                 session->folder_content_changed = FALSE;
2111         }
2112         g_free(real_path);
2113
2114         return ok;
2115 }
2116
2117 static gint imap_status(IMAPSession *session, IMAPFolder *folder,
2118                         const gchar *path,
2119                         gint *messages, gint *recent,
2120                         guint32 *uid_next, guint32 *uid_validity,
2121                         gint *unseen, gboolean block)
2122 {
2123         int r;
2124         clistiter * iter;
2125         struct mailimap_mailbox_data_status * data_status;
2126         int got_values;
2127         
2128         r = imap_threaded_status(FOLDER(folder), path, &data_status);
2129         if (r != MAILIMAP_NO_ERROR)
2130                 return IMAP_ERROR;
2131         
2132         if (data_status->st_info_list == NULL) {
2133                 mailimap_mailbox_data_status_free(data_status);
2134                 return IMAP_ERROR;
2135         }
2136         
2137         got_values = 0;
2138         for(iter = clist_begin(data_status->st_info_list) ; iter != NULL ;
2139             iter = clist_next(iter)) {
2140                 struct mailimap_status_info * info;             
2141                 
2142                 info = clist_content(iter);
2143                 switch (info->st_att) {
2144                 case MAILIMAP_STATUS_ATT_MESSAGES:
2145                         * messages = info->st_value;
2146                         got_values |= 1 << 0;
2147                         break;
2148                         
2149                 case MAILIMAP_STATUS_ATT_RECENT:
2150                         * recent = info->st_value;
2151                         got_values |= 1 << 1;
2152                         break;
2153                         
2154                 case MAILIMAP_STATUS_ATT_UIDNEXT:
2155                         * uid_next = info->st_value;
2156                         got_values |= 1 << 2;
2157                         break;
2158                         
2159                 case MAILIMAP_STATUS_ATT_UIDVALIDITY:
2160                         * uid_validity = info->st_value;
2161                         got_values |= 1 << 3;
2162                         break;
2163                         
2164                 case MAILIMAP_STATUS_ATT_UNSEEN:
2165                         * unseen = info->st_value;
2166                         got_values |= 1 << 4;
2167                         break;
2168                 }
2169         }
2170         mailimap_mailbox_data_status_free(data_status);
2171         
2172         if (got_values != ((1 << 4) + (1 << 3) +
2173                            (1 << 2) + (1 << 1) + (1 << 0)))
2174                 return IMAP_ERROR;
2175         
2176         return IMAP_SUCCESS;
2177 }
2178
2179 static void imap_free_capabilities(IMAPSession *session)
2180 {
2181         g_strfreev(session->capability);
2182         session->capability = NULL;
2183 }
2184
2185 /* low-level IMAP4rev1 commands */
2186
2187 #if 0
2188 static gint imap_cmd_authenticate(IMAPSession *session, const gchar *user,
2189                                   const gchar *pass, IMAPAuthType type)
2190 {
2191         gchar *auth_type;
2192         gint ok;
2193         gchar *buf = NULL;
2194         gchar *challenge;
2195         gint challenge_len;
2196         gchar hexdigest[33];
2197         gchar *response;
2198         gchar *response64;
2199
2200         auth_type = "CRAM-MD5";
2201
2202         imap_gen_send(session, "AUTHENTICATE %s", auth_type);
2203         ok = imap_gen_recv(session, &buf);
2204         if (ok != IMAP_SUCCESS || buf[0] != '+' || buf[1] != ' ') {
2205                 g_free(buf);
2206                 return IMAP_ERROR;
2207         }
2208
2209         challenge = g_malloc(strlen(buf + 2) + 1);
2210         challenge_len = base64_decode(challenge, buf + 2, -1);
2211         challenge[challenge_len] = '\0';
2212         g_free(buf);
2213         log_print("IMAP< [Decoded: %s]\n", challenge);
2214
2215         md5_hex_hmac(hexdigest, challenge, challenge_len, pass, strlen(pass));
2216         g_free(challenge);
2217
2218         response = g_strdup_printf("%s %s", user, hexdigest);
2219         log_print("IMAP> [Encoded: %s]\n", response);
2220         response64 = g_malloc((strlen(response) + 3) * 2 + 1);
2221         base64_encode(response64, response, strlen(response));
2222         g_free(response);
2223
2224         log_print("IMAP> %s\n", response64);
2225         sock_puts(SESSION(session)->sock, response64);
2226         ok = imap_cmd_ok(session, NULL);
2227         if (ok != IMAP_SUCCESS)
2228                 log_warning(_("IMAP4 authentication failed.\n"));
2229
2230         return ok;
2231 }
2232 #endif
2233
2234 static gint imap_cmd_login(IMAPSession *session,
2235                            const gchar *user, const gchar *pass)
2236 {
2237         int r;
2238         gint ok;
2239
2240         r = imap_threaded_login(session->folder, user, pass);
2241         if (r != MAILIMAP_NO_ERROR)
2242                 ok = IMAP_ERROR;
2243         else
2244                 ok = IMAP_SUCCESS;
2245         
2246         return ok;
2247 }
2248
2249 static gint imap_cmd_logout(IMAPSession *session)
2250 {
2251         imap_threaded_disconnect(session->folder);
2252         
2253         return IMAP_SUCCESS;
2254 }
2255
2256 static gint imap_cmd_noop(IMAPSession *session)
2257 {
2258         int r;
2259         unsigned int exists;
2260         
2261         r = imap_threaded_noop(session->folder, &exists);
2262         if (r != MAILIMAP_NO_ERROR)
2263                 return IMAP_ERROR;
2264         
2265         session->exists = exists;
2266         
2267         return IMAP_SUCCESS;
2268 }
2269
2270 #if USE_OPENSSL
2271 static gint imap_cmd_starttls(IMAPSession *session)
2272 {
2273         int r;
2274
2275         r = imap_threaded_starttls(session->folder);
2276         if (r != MAILIMAP_NO_ERROR)
2277                 return IMAP_ERROR;
2278         
2279         return IMAP_SUCCESS;
2280 }
2281 #endif
2282
2283 static gint imap_cmd_select(IMAPSession *session, const gchar *folder,
2284                             gint *exists, gint *recent, gint *unseen,
2285                             guint32 *uid_validity, gboolean block)
2286 {
2287         int r;
2288
2289         r = imap_threaded_select(session->folder, folder,
2290                                  exists, recent, unseen, uid_validity);
2291         if (r != MAILIMAP_NO_ERROR) {
2292                 return IMAP_ERROR;
2293         }
2294         
2295         return IMAP_SUCCESS;
2296 }
2297
2298 static gint imap_cmd_examine(IMAPSession *session, const gchar *folder,
2299                              gint *exists, gint *recent, gint *unseen,
2300                              guint32 *uid_validity, gboolean block)
2301 {
2302         int r;
2303
2304         r = imap_threaded_examine(session->folder, folder,
2305                                   exists, recent, unseen, uid_validity);
2306         if (r != MAILIMAP_NO_ERROR) {
2307                 return IMAP_ERROR;
2308         }
2309         
2310         return IMAP_SUCCESS;
2311 }
2312
2313 static gint imap_cmd_create(IMAPSession *session, const gchar *folder)
2314 {
2315         int r;
2316         
2317         r = imap_threaded_create(session->folder, folder);
2318         if (r != MAILIMAP_NO_ERROR)
2319                 return IMAP_ERROR;
2320         
2321         return IMAP_SUCCESS;
2322 }
2323
2324 static gint imap_cmd_rename(IMAPSession *session, const gchar *old_folder,
2325                             const gchar *new_folder)
2326 {
2327         int r;
2328         
2329         r = imap_threaded_rename(session->folder, old_folder,
2330                                  new_folder);
2331         if (r != MAILIMAP_NO_ERROR)
2332                 return IMAP_ERROR;
2333         
2334         return IMAP_SUCCESS;
2335 }
2336
2337 static gint imap_cmd_delete(IMAPSession *session, const gchar *folder)
2338 {
2339         int r;
2340         
2341         r = imap_threaded_delete(session->folder, folder);
2342         if (r != MAILIMAP_NO_ERROR)
2343                 return IMAP_ERROR;
2344         
2345         return IMAP_SUCCESS;
2346 }
2347
2348 typedef struct _fetch_data {
2349         IMAPSession *session;
2350         guint32 uid;
2351         const gchar *filename;
2352         gboolean headers;
2353         gboolean body;
2354         gboolean done;
2355 } fetch_data;
2356
2357 static void *imap_cmd_fetch_thread(void *data)
2358 {
2359         fetch_data *stuff = (fetch_data *)data;
2360         IMAPSession *session = stuff->session;
2361         guint32 uid = stuff->uid;
2362         const gchar *filename = stuff->filename;
2363         int r;
2364         
2365         if (stuff->body) {
2366                 r = imap_threaded_fetch_content(session->folder,
2367                                                uid, 1, filename);
2368         }
2369         else {
2370                 r = imap_threaded_fetch_content(session->folder,
2371                                                 uid, 0, filename);
2372         }
2373         if (r != MAILIMAP_NO_ERROR)
2374                 return GINT_TO_POINTER(IMAP_ERROR);
2375         
2376         return GINT_TO_POINTER(IMAP_SUCCESS);
2377 }
2378
2379 static gint imap_cmd_fetch(IMAPSession *session, guint32 uid,
2380                                 const gchar *filename, gboolean headers,
2381                                 gboolean body)
2382 {
2383         fetch_data *data = g_new0(fetch_data, 1);
2384         int result = 0;
2385         data->done = FALSE;
2386         data->session = session;
2387         data->uid = uid;
2388         data->filename = filename;
2389         data->headers = headers;
2390         data->body = body;
2391
2392         if (prefs_common.work_offline && !imap_gtk_should_override()) {
2393                 g_free(data);
2394                 return -1;
2395         }
2396
2397         result = GPOINTER_TO_INT(imap_cmd_fetch_thread(data));
2398         g_free(data);
2399         return result;
2400 }
2401
2402
2403 static gint imap_cmd_append(IMAPSession *session, const gchar *destfolder,
2404                             const gchar *file, IMAPFlags flags, 
2405                             guint32 *new_uid)
2406 {
2407         struct mailimap_flag_list * flag_list;
2408         int r;
2409         
2410         g_return_val_if_fail(file != NULL, IMAP_ERROR);
2411
2412         flag_list = imap_flag_to_lep(flags);
2413         r = imap_threaded_append(session->folder, destfolder,
2414                          file, flag_list);
2415         
2416         if (new_uid != NULL)
2417                 *new_uid = 0;
2418
2419         if (r != MAILIMAP_NO_ERROR)
2420                 return IMAP_ERROR;
2421         
2422         return IMAP_SUCCESS;
2423 }
2424
2425 static gint imap_cmd_copy(IMAPSession *session, struct mailimap_set * set,
2426                           const gchar *destfolder, GRelation *uid_mapping)
2427 {
2428         int r;
2429         
2430         g_return_val_if_fail(session != NULL, IMAP_ERROR);
2431         g_return_val_if_fail(set != NULL, IMAP_ERROR);
2432         g_return_val_if_fail(destfolder != NULL, IMAP_ERROR);
2433
2434         r = imap_threaded_copy(session->folder, set, destfolder);
2435         if (r != MAILIMAP_NO_ERROR)
2436                 return IMAP_ERROR;
2437         
2438         return IMAP_SUCCESS;
2439 }
2440
2441 static gint imap_cmd_store(IMAPSession *session, struct mailimap_set * set,
2442                            IMAPFlags flags, int do_add)
2443 {
2444         int r;
2445         struct mailimap_flag_list * flag_list;
2446         struct mailimap_store_att_flags * store_att_flags;
2447         
2448         flag_list = imap_flag_to_lep(flags);
2449         
2450         if (do_add)
2451                 store_att_flags =
2452                         mailimap_store_att_flags_new_add_flags_silent(flag_list);
2453         else
2454                 store_att_flags =
2455                         mailimap_store_att_flags_new_remove_flags_silent(flag_list);
2456         
2457         r = imap_threaded_store(session->folder, set, store_att_flags);
2458         if (r != MAILIMAP_NO_ERROR)
2459                 return IMAP_ERROR;
2460         
2461         return IMAP_SUCCESS;
2462 }
2463
2464 static gint imap_cmd_expunge(IMAPSession *session)
2465 {
2466         int r;
2467         
2468         if (prefs_common.work_offline && !imap_gtk_should_override()) {
2469                 return -1;
2470         }
2471         
2472         r = imap_threaded_expunge(session->folder);
2473         if (r != MAILIMAP_NO_ERROR)
2474                 return IMAP_ERROR;
2475         
2476         return IMAP_SUCCESS;
2477 }
2478
2479 static void imap_path_separator_subst(gchar *str, gchar separator)
2480 {
2481         gchar *p;
2482         gboolean in_escape = FALSE;
2483
2484         if (!separator || separator == '/') return;
2485
2486         for (p = str; *p != '\0'; p++) {
2487                 if (*p == '/' && !in_escape)
2488                         *p = separator;
2489                 else if (*p == '&' && *(p + 1) != '-' && !in_escape)
2490                         in_escape = TRUE;
2491                 else if (*p == '-' && in_escape)
2492                         in_escape = FALSE;
2493         }
2494 }
2495
2496 static gchar *imap_modified_utf7_to_utf8(const gchar *mutf7_str)
2497 {
2498         static iconv_t cd = (iconv_t)-1;
2499         static gboolean iconv_ok = TRUE;
2500         GString *norm_utf7;
2501         gchar *norm_utf7_p;
2502         size_t norm_utf7_len;
2503         const gchar *p;
2504         gchar *to_str, *to_p;
2505         size_t to_len;
2506         gboolean in_escape = FALSE;
2507
2508         if (!iconv_ok) return g_strdup(mutf7_str);
2509
2510         if (cd == (iconv_t)-1) {
2511                 cd = iconv_open(CS_INTERNAL, CS_UTF_7);
2512                 if (cd == (iconv_t)-1) {
2513                         g_warning("iconv cannot convert UTF-7 to %s\n",
2514                                   CS_INTERNAL);
2515                         iconv_ok = FALSE;
2516                         return g_strdup(mutf7_str);
2517                 }
2518         }
2519
2520         /* modified UTF-7 to normal UTF-7 conversion */
2521         norm_utf7 = g_string_new(NULL);
2522
2523         for (p = mutf7_str; *p != '\0'; p++) {
2524                 /* replace: '&'  -> '+',
2525                             "&-" -> '&',
2526                             escaped ','  -> '/' */
2527                 if (!in_escape && *p == '&') {
2528                         if (*(p + 1) != '-') {
2529                                 g_string_append_c(norm_utf7, '+');
2530                                 in_escape = TRUE;
2531                         } else {
2532                                 g_string_append_c(norm_utf7, '&');
2533                                 p++;
2534                         }
2535                 } else if (in_escape && *p == ',') {
2536                         g_string_append_c(norm_utf7, '/');
2537                 } else if (in_escape && *p == '-') {
2538                         g_string_append_c(norm_utf7, '-');
2539                         in_escape = FALSE;
2540                 } else {
2541                         g_string_append_c(norm_utf7, *p);
2542                 }
2543         }
2544
2545         norm_utf7_p = norm_utf7->str;
2546         norm_utf7_len = norm_utf7->len;
2547         to_len = strlen(mutf7_str) * 5;
2548         to_p = to_str = g_malloc(to_len + 1);
2549
2550         if (iconv(cd, (ICONV_CONST gchar **)&norm_utf7_p, &norm_utf7_len,
2551                   &to_p, &to_len) == -1) {
2552                 g_warning(_("iconv cannot convert UTF-7 to %s\n"),
2553                           conv_get_locale_charset_str());
2554                 g_string_free(norm_utf7, TRUE);
2555                 g_free(to_str);
2556                 return g_strdup(mutf7_str);
2557         }
2558
2559         /* second iconv() call for flushing */
2560         iconv(cd, NULL, NULL, &to_p, &to_len);
2561         g_string_free(norm_utf7, TRUE);
2562         *to_p = '\0';
2563
2564         return to_str;
2565 }
2566
2567 static gchar *imap_utf8_to_modified_utf7(const gchar *from)
2568 {
2569         static iconv_t cd = (iconv_t)-1;
2570         static gboolean iconv_ok = TRUE;
2571         gchar *norm_utf7, *norm_utf7_p;
2572         size_t from_len, norm_utf7_len;
2573         GString *to_str;
2574         gchar *from_tmp, *to, *p;
2575         gboolean in_escape = FALSE;
2576
2577         if (!iconv_ok) return g_strdup(from);
2578
2579         if (cd == (iconv_t)-1) {
2580                 cd = iconv_open(CS_UTF_7, CS_INTERNAL);
2581                 if (cd == (iconv_t)-1) {
2582                         g_warning(_("iconv cannot convert %s to UTF-7\n"),
2583                                   CS_INTERNAL);
2584                         iconv_ok = FALSE;
2585                         return g_strdup(from);
2586                 }
2587         }
2588
2589         /* UTF-8 to normal UTF-7 conversion */
2590         Xstrdup_a(from_tmp, from, return g_strdup(from));
2591         from_len = strlen(from);
2592         norm_utf7_len = from_len * 5;
2593         Xalloca(norm_utf7, norm_utf7_len + 1, return g_strdup(from));
2594         norm_utf7_p = norm_utf7;
2595
2596 #define IS_PRINT(ch) (isprint(ch) && IS_ASCII(ch))
2597
2598         while (from_len > 0) {
2599                 if (*from_tmp == '+') {
2600                         *norm_utf7_p++ = '+';
2601                         *norm_utf7_p++ = '-';
2602                         norm_utf7_len -= 2;
2603                         from_tmp++;
2604                         from_len--;
2605                 } else if (IS_PRINT(*(guchar *)from_tmp)) {
2606                         /* printable ascii char */
2607                         *norm_utf7_p = *from_tmp;
2608                         norm_utf7_p++;
2609                         norm_utf7_len--;
2610                         from_tmp++;
2611                         from_len--;
2612                 } else {
2613                         size_t conv_len = 0;
2614
2615                         /* unprintable char: convert to UTF-7 */
2616                         p = from_tmp;
2617                         while (!IS_PRINT(*(guchar *)p) && conv_len < from_len) {
2618                                 conv_len += g_utf8_skip[*(guchar *)p];
2619                                 p += g_utf8_skip[*(guchar *)p];
2620                         }
2621
2622                         from_len -= conv_len;
2623                         if (iconv(cd, (ICONV_CONST gchar **)&from_tmp,
2624                                   &conv_len,
2625                                   &norm_utf7_p, &norm_utf7_len) == -1) {
2626                                 g_warning(_("iconv cannot convert UTF-8 to UTF-7\n"));
2627                                 return g_strdup(from);
2628                         }
2629
2630                         /* second iconv() call for flushing */
2631                         iconv(cd, NULL, NULL, &norm_utf7_p, &norm_utf7_len);
2632                 }
2633         }
2634
2635 #undef IS_PRINT
2636
2637         *norm_utf7_p = '\0';
2638         to_str = g_string_new(NULL);
2639         for (p = norm_utf7; p < norm_utf7_p; p++) {
2640                 /* replace: '&' -> "&-",
2641                             '+' -> '&',
2642                             "+-" -> '+',
2643                             BASE64 '/' -> ',' */
2644                 if (!in_escape && *p == '&') {
2645                         g_string_append(to_str, "&-");
2646                 } else if (!in_escape && *p == '+') {
2647                         if (*(p + 1) == '-') {
2648                                 g_string_append_c(to_str, '+');
2649                                 p++;
2650                         } else {
2651                                 g_string_append_c(to_str, '&');
2652                                 in_escape = TRUE;
2653                         }
2654                 } else if (in_escape && *p == '/') {
2655                         g_string_append_c(to_str, ',');
2656                 } else if (in_escape && *p == '-') {
2657                         g_string_append_c(to_str, '-');
2658                         in_escape = FALSE;
2659                 } else {
2660                         g_string_append_c(to_str, *p);
2661                 }
2662         }
2663
2664         if (in_escape) {
2665                 in_escape = FALSE;
2666                 g_string_append_c(to_str, '-');
2667         }
2668
2669         to = to_str->str;
2670         g_string_free(to_str, FALSE);
2671
2672         return to;
2673 }
2674
2675 static gboolean imap_rename_folder_func(GNode *node, gpointer data)
2676 {
2677         FolderItem *item = node->data;
2678         gchar **paths = data;
2679         const gchar *oldpath = paths[0];
2680         const gchar *newpath = paths[1];
2681         gchar *base;
2682         gchar *new_itempath;
2683         gint oldpathlen;
2684
2685         oldpathlen = strlen(oldpath);
2686         if (strncmp(oldpath, item->path, oldpathlen) != 0) {
2687                 g_warning("path doesn't match: %s, %s\n", oldpath, item->path);
2688                 return TRUE;
2689         }
2690
2691         base = item->path + oldpathlen;
2692         while (*base == G_DIR_SEPARATOR) base++;
2693         if (*base == '\0')
2694                 new_itempath = g_strdup(newpath);
2695         else
2696                 new_itempath = g_strconcat(newpath, G_DIR_SEPARATOR_S, base,
2697                                            NULL);
2698         g_free(item->path);
2699         item->path = new_itempath;
2700
2701         return FALSE;
2702 }
2703
2704 typedef struct _get_list_uid_data {
2705         Folder *folder;
2706         IMAPFolderItem *item;
2707         GSList **msgnum_list;
2708         gboolean done;
2709 } get_list_uid_data;
2710
2711 static void *get_list_of_uids_thread(void *data)
2712 {
2713         get_list_uid_data *stuff = (get_list_uid_data *)data;
2714         Folder *folder = stuff->folder;
2715         IMAPFolderItem *item = stuff->item;
2716         GSList **msgnum_list = stuff->msgnum_list;
2717         gint ok, nummsgs = 0, lastuid_old;
2718         IMAPSession *session;
2719         GSList *uidlist, *elem;
2720         struct mailimap_set * set;
2721         clist * lep_uidlist;
2722         int r;
2723         
2724         session = imap_session_get(folder);
2725         if (session == NULL) {
2726                 stuff->done = TRUE;
2727                 return GINT_TO_POINTER(-1);
2728         }
2729
2730         ok = imap_select(session, IMAP_FOLDER(folder), item->item.path,
2731                          NULL, NULL, NULL, NULL, TRUE);
2732         if (ok != IMAP_SUCCESS) {
2733                 stuff->done = TRUE;
2734                 return GINT_TO_POINTER(-1);
2735         }
2736
2737         uidlist = NULL;
2738         
2739         set = mailimap_set_new_interval(item->lastuid + 1, 0);
2740         r = imap_threaded_search(folder, IMAP_SEARCH_TYPE_SIMPLE, set,
2741                                  &lep_uidlist);
2742         if (r == MAILIMAP_NO_ERROR) {
2743                 GSList * fetchuid_list;
2744                 
2745                 fetchuid_list =
2746                         imap_uid_list_from_lep(lep_uidlist);
2747                 uidlist = g_slist_concat(fetchuid_list, uidlist);
2748         }
2749         else {
2750                 GSList * fetchuid_list;
2751                 carray * lep_uidtab;
2752                 
2753                 r = imap_threaded_fetch_uid(folder, item->lastuid + 1,
2754                                             &lep_uidtab);
2755                 if (r == MAILIMAP_NO_ERROR) {
2756                         fetchuid_list =
2757                                 imap_uid_list_from_lep_tab(lep_uidtab);
2758                         uidlist = g_slist_concat(fetchuid_list, uidlist);
2759                 }
2760         }
2761         
2762         lastuid_old = item->lastuid;
2763         *msgnum_list = g_slist_copy(item->uid_list);
2764         nummsgs = g_slist_length(*msgnum_list);
2765         debug_print("Got %d uids from cache\n", g_slist_length(item->uid_list));
2766
2767         for (elem = uidlist; elem != NULL; elem = g_slist_next(elem)) {
2768                 guint msgnum;
2769
2770                 msgnum = GPOINTER_TO_INT(elem->data);
2771                 if (msgnum > lastuid_old) {
2772                         *msgnum_list = g_slist_prepend(*msgnum_list, GINT_TO_POINTER(msgnum));
2773                         item->uid_list = g_slist_prepend(item->uid_list, GINT_TO_POINTER(msgnum));
2774                         nummsgs++;
2775
2776                         if(msgnum > item->lastuid)
2777                                 item->lastuid = msgnum;
2778                 }
2779         }
2780         g_slist_free(uidlist);
2781
2782         stuff->done = TRUE;
2783         return GINT_TO_POINTER(nummsgs);
2784 }
2785
2786 static gint get_list_of_uids(Folder *folder, IMAPFolderItem *item, GSList **msgnum_list)
2787 {
2788         gint result;
2789         get_list_uid_data *data = g_new0(get_list_uid_data, 1);
2790         data->done = FALSE;
2791         data->folder = folder;
2792         data->item = item;
2793         data->msgnum_list = msgnum_list;
2794
2795         if (prefs_common.work_offline && !imap_gtk_should_override()) {
2796                 g_free(data);
2797                 return -1;
2798         }
2799
2800         result = GPOINTER_TO_INT(get_list_of_uids_thread(data));
2801         g_free(data);
2802         return result;
2803
2804 }
2805
2806 gint imap_get_num_list(Folder *folder, FolderItem *_item, GSList **msgnum_list, gboolean *old_uids_valid)
2807 {
2808         IMAPFolderItem *item = (IMAPFolderItem *)_item;
2809         IMAPSession *session;
2810         gint ok, nummsgs = 0, exists, recent, uid_val, uid_next, unseen;
2811         GSList *uidlist = NULL;
2812         gchar *dir;
2813         gboolean selected_folder;
2814         
2815         debug_print("get_num_list\n");
2816         
2817         g_return_val_if_fail(folder != NULL, -1);
2818         g_return_val_if_fail(item != NULL, -1);
2819         g_return_val_if_fail(item->item.path != NULL, -1);
2820         g_return_val_if_fail(FOLDER_CLASS(folder) == &imap_class, -1);
2821         g_return_val_if_fail(folder->account != NULL, -1);
2822
2823         MUTEX_TRYLOCK_OR_RETURN_VAL(-1);
2824
2825         session = imap_session_get(folder);
2826         g_return_val_if_fail(session != NULL, -1);
2827
2828         selected_folder = (session->mbox != NULL) &&
2829                           (!strcmp(session->mbox, item->item.path));
2830         if (selected_folder) {
2831                 ok = imap_cmd_noop(session);
2832                 if (ok != IMAP_SUCCESS) {
2833                         MUTEX_UNLOCK();
2834                         return -1;
2835                 }
2836                 exists = session->exists;
2837
2838                 *old_uids_valid = TRUE;
2839         } else {
2840                 ok = imap_status(session, IMAP_FOLDER(folder), item->item.path,
2841                                  &exists, &recent, &uid_next, &uid_val, &unseen, FALSE);
2842                 if (ok != IMAP_SUCCESS) {
2843                         MUTEX_UNLOCK();
2844                         return -1;
2845                 }
2846                 if(item->item.mtime == uid_val)
2847                         *old_uids_valid = TRUE;
2848                 else {
2849                         *old_uids_valid = FALSE;
2850
2851                         debug_print("Freeing imap uid cache\n");
2852                         item->lastuid = 0;
2853                         g_slist_free(item->uid_list);
2854                         item->uid_list = NULL;
2855                 
2856                         item->item.mtime = uid_val;
2857
2858                         imap_delete_all_cached_messages((FolderItem *)item);
2859                 }
2860         }
2861
2862         if (!selected_folder)
2863                 item->uid_next = uid_next;
2864
2865         /* If old uid_next matches new uid_next we can be sure no message
2866            was added to the folder */
2867         if (( selected_folder && !session->folder_content_changed) ||
2868             (!selected_folder && uid_next == item->uid_next)) {
2869                 nummsgs = g_slist_length(item->uid_list);
2870
2871                 /* If number of messages is still the same we
2872                    know our caches message numbers are still valid,
2873                    otherwise if the number of messages has decrease
2874                    we discard our cache to start a new scan to find
2875                    out which numbers have been removed */
2876                 if (exists == nummsgs) {
2877                         *msgnum_list = g_slist_copy(item->uid_list);
2878                         MUTEX_UNLOCK();
2879                         return nummsgs;
2880                 } else if (exists < nummsgs) {
2881                         debug_print("Freeing imap uid cache");
2882                         item->lastuid = 0;
2883                         g_slist_free(item->uid_list);
2884                         item->uid_list = NULL;
2885                 }
2886         }
2887
2888         if (exists == 0) {
2889                 *msgnum_list = NULL;
2890                 MUTEX_UNLOCK();
2891                 return 0;
2892         }
2893
2894         nummsgs = get_list_of_uids(folder, item, &uidlist);
2895
2896         if (nummsgs < 0) {
2897                 MUTEX_UNLOCK();
2898                 return -1;
2899         }
2900
2901         if (nummsgs != exists) {
2902                 /* Cache contains more messages then folder, we have cached
2903                    an old UID of a message that was removed and new messages
2904                    have been added too, otherwise the uid_next check would
2905                    not have failed */
2906                 debug_print("Freeing imap uid cache");
2907                 item->lastuid = 0;
2908                 g_slist_free(item->uid_list);
2909                 item->uid_list = NULL;
2910
2911                 g_slist_free(*msgnum_list);
2912
2913                 nummsgs = get_list_of_uids(folder, item, &uidlist);
2914         }
2915
2916         *msgnum_list = uidlist;
2917
2918         dir = folder_item_get_path((FolderItem *)item);
2919         debug_print("removing old messages from %s\n", dir);
2920         remove_numbered_files_not_in_list(dir, *msgnum_list);
2921         g_free(dir);
2922         MUTEX_UNLOCK();
2923         
2924         debug_print("get_num_list - ok - %i\n", nummsgs);
2925         
2926         return nummsgs;
2927 }
2928
2929 static MsgInfo *imap_parse_msg(const gchar *file, FolderItem *item)
2930 {
2931         MsgInfo *msginfo;
2932         MsgFlags flags;
2933
2934         flags.perm_flags = MSG_NEW|MSG_UNREAD;
2935         flags.tmp_flags = 0;
2936
2937         g_return_val_if_fail(item != NULL, NULL);
2938         g_return_val_if_fail(file != NULL, NULL);
2939
2940         if (item->stype == F_QUEUE) {
2941                 MSG_SET_TMP_FLAGS(flags, MSG_QUEUED);
2942         } else if (item->stype == F_DRAFT) {
2943                 MSG_SET_TMP_FLAGS(flags, MSG_DRAFT);
2944         }
2945
2946         msginfo = procheader_parse_file(file, flags, FALSE, FALSE);
2947         if (!msginfo) return NULL;
2948         
2949         msginfo->plaintext_file = g_strdup(file);
2950         msginfo->folder = item;
2951
2952         return msginfo;
2953 }
2954
2955 GSList *imap_get_msginfos(Folder *folder, FolderItem *item,
2956                           GSList *msgnum_list)
2957 {
2958         IMAPSession *session;
2959         MsgInfoList *ret = NULL;
2960         gint ok;
2961         
2962         debug_print("get_msginfos\n");
2963         
2964         g_return_val_if_fail(folder != NULL, NULL);
2965         g_return_val_if_fail(item != NULL, NULL);
2966         g_return_val_if_fail(msgnum_list != NULL, NULL);
2967
2968         session = imap_session_get(folder);
2969         g_return_val_if_fail(session != NULL, NULL);
2970
2971         debug_print("IMAP getting msginfos\n");
2972         ok = imap_select(session, IMAP_FOLDER(folder), item->path,
2973                          NULL, NULL, NULL, NULL, FALSE);
2974         if (ok != IMAP_SUCCESS)
2975                 return NULL;
2976
2977         if (!(item->stype == F_QUEUE || item->stype == F_DRAFT)) {
2978                 ret = g_slist_concat(ret,
2979                         imap_get_uncached_messages(session, item,
2980                                                    msgnum_list));
2981         } else {
2982                 MsgNumberList *sorted_list, *elem;
2983                 gint startnum, lastnum;
2984
2985                 sorted_list = g_slist_sort(g_slist_copy(msgnum_list), g_int_compare);
2986
2987                 startnum = lastnum = GPOINTER_TO_INT(sorted_list->data);
2988
2989                 for (elem = sorted_list;; elem = g_slist_next(elem)) {
2990                         guint num = 0;
2991
2992                         if (elem)
2993                                 num = GPOINTER_TO_INT(elem->data);
2994
2995                         if (num > lastnum + 1 || elem == NULL) {
2996                                 int i;
2997                                 for (i = startnum; i <= lastnum; ++i) {
2998                                         gchar *file;
2999                         
3000                                         file = imap_fetch_msg(folder, item, i);
3001                                         if (file != NULL) {
3002                                                 MsgInfo *msginfo = imap_parse_msg(file, item);
3003                                                 if (msginfo != NULL) {
3004                                                         msginfo->msgnum = i;
3005                                                         ret = g_slist_append(ret, msginfo);
3006                                                 }
3007                                                 g_free(file);
3008                                         }
3009                                 }
3010
3011                                 if (elem == NULL)
3012                                         break;
3013
3014                                 startnum = num;
3015                         }
3016                         lastnum = num;
3017                 }
3018
3019                 g_slist_free(sorted_list);
3020         }
3021
3022         return ret;
3023 }
3024
3025 MsgInfo *imap_get_msginfo(Folder *folder, FolderItem *item, gint uid)
3026 {
3027         MsgInfo *msginfo = NULL;
3028         MsgInfoList *msginfolist;
3029         MsgNumberList numlist;
3030
3031         numlist.next = NULL;
3032         numlist.data = GINT_TO_POINTER(uid);
3033
3034         msginfolist = imap_get_msginfos(folder, item, &numlist);
3035         if (msginfolist != NULL) {
3036                 msginfo = msginfolist->data;
3037                 g_slist_free(msginfolist);
3038         }
3039
3040         return msginfo;
3041 }
3042
3043 gboolean imap_scan_required(Folder *folder, FolderItem *_item)
3044 {
3045         IMAPSession *session;
3046         IMAPFolderItem *item = (IMAPFolderItem *)_item;
3047         gint ok, exists = 0, recent = 0, unseen = 0;
3048         guint32 uid_next, uid_val = 0;
3049         gboolean selected_folder;
3050         
3051         g_return_val_if_fail(folder != NULL, FALSE);
3052         g_return_val_if_fail(item != NULL, FALSE);
3053         g_return_val_if_fail(item->item.folder != NULL, FALSE);
3054         g_return_val_if_fail(FOLDER_CLASS(item->item.folder) == &imap_class, FALSE);
3055
3056         if (item->item.path == NULL)
3057                 return FALSE;
3058
3059         session = imap_session_get(folder);
3060         g_return_val_if_fail(session != NULL, FALSE);
3061
3062         selected_folder = (session->mbox != NULL) &&
3063                           (!strcmp(session->mbox, item->item.path));
3064         if (selected_folder) {
3065                 ok = imap_cmd_noop(session);
3066                 if (ok != IMAP_SUCCESS)
3067                         return FALSE;
3068
3069                 if (session->folder_content_changed)
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 }