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