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