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