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