2005-06-14 [colin] 1.9.11cvs65
[claws.git] / src / imap.c
1 /*
2  * Sylpheed -- a GTK+ based, lightweight, and fast e-mail client
3  * Copyright (C) 1999-2005 Hiroyuki Yamamoto
4  *
5  * This program is free software; you can redistribute it and/or modify
6  * it under the terms of the GNU General Public License as published by
7  * the Free Software Foundation; either version 2 of the License, or
8  * (at your option) any later version.
9  *
10  * This program is distributed in the hope that it will be useful,
11  * but WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13  * GNU General Public License for more details.
14  *
15  * You should have received a copy of the GNU General Public License
16  * along with this program; if not, write to the Free Software
17  * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
18  */
19
20 #ifdef HAVE_CONFIG_H
21 #  include "config.h"
22 #endif
23
24 #include "defs.h"
25
26 #include <glib.h>
27 #include <glib/gi18n.h>
28 #include <stdio.h>
29 #include <string.h>
30 #include <stdlib.h>
31 #include <dirent.h>
32 #include <unistd.h>
33 #include <ctype.h>
34 #include <time.h>
35 #if HAVE_ICONV
36 #  include <iconv.h>
37 #endif
38
39 #if USE_OPENSSL
40 #  include "ssl.h"
41 #endif
42 #include "folder.h"
43 #include "session.h"
44 #include "procmsg.h"
45 #include "imap.h"
46 #include "imap_gtk.h"
47 #include "socket.h"
48 #include "ssl.h"
49 #include "recv.h"
50 #include "procheader.h"
51 #include "prefs_account.h"
52 #include "codeconv.h"
53 #include "md5.h"
54 #include "base64.h"
55 #include "utils.h"
56 #include "prefs_common.h"
57 #include "inputdialog.h"
58 #include "log.h"
59 #include "remotefolder.h"
60 #include "alertpanel.h"
61 #include "sylpheed.h"
62 #include "statusbar.h"
63 #include "msgcache.h"
64
65 #ifdef USE_PTHREAD
66 #include <pthread.h>
67 #endif
68
69 typedef struct _IMAPFolder      IMAPFolder;
70 typedef struct _IMAPSession     IMAPSession;
71 typedef struct _IMAPNameSpace   IMAPNameSpace;
72 typedef struct _IMAPFolderItem  IMAPFolderItem;
73
74 #include "prefs_account.h"
75
76 #define IMAP_FOLDER(obj)        ((IMAPFolder *)obj)
77 #define IMAP_FOLDER_ITEM(obj)   ((IMAPFolderItem *)obj)
78 #define IMAP_SESSION(obj)       ((IMAPSession *)obj)
79
80 struct _IMAPFolder
81 {
82         RemoteFolder rfolder;
83
84         /* list of IMAPNameSpace */
85         GList *ns_personal;
86         GList *ns_others;
87         GList *ns_shared;
88 };
89
90 struct _IMAPSession
91 {
92         Session session;
93
94         gboolean authenticated;
95
96         gchar **capability;
97         gboolean uidplus;
98
99         gchar *mbox;
100         guint cmd_count;
101
102         /* CLAWS */
103         gboolean folder_content_changed;
104         guint exists;
105 };
106
107 struct _IMAPNameSpace
108 {
109         gchar *name;
110         gchar separator;
111 };
112
113 #define IMAP_SUCCESS    0
114 #define IMAP_SOCKET     2
115 #define IMAP_AUTHFAIL   3
116 #define IMAP_PROTOCOL   4
117 #define IMAP_SYNTAX     5
118 #define IMAP_IOERR      6
119 #define IMAP_ERROR      7
120
121 #define IMAPBUFSIZE     8192
122
123 typedef enum
124 {
125         IMAP_FLAG_SEEN          = 1 << 0,
126         IMAP_FLAG_ANSWERED      = 1 << 1,
127         IMAP_FLAG_FLAGGED       = 1 << 2,
128         IMAP_FLAG_DELETED       = 1 << 3,
129         IMAP_FLAG_DRAFT         = 1 << 4
130 } IMAPFlags;
131
132 #define IMAP_IS_SEEN(flags)     ((flags & IMAP_FLAG_SEEN) != 0)
133 #define IMAP_IS_ANSWERED(flags) ((flags & IMAP_FLAG_ANSWERED) != 0)
134 #define IMAP_IS_FLAGGED(flags)  ((flags & IMAP_FLAG_FLAGGED) != 0)
135 #define IMAP_IS_DELETED(flags)  ((flags & IMAP_FLAG_DELETED) != 0)
136 #define IMAP_IS_DRAFT(flags)    ((flags & IMAP_FLAG_DRAFT) != 0)
137
138
139 #define IMAP4_PORT      143
140 #if USE_OPENSSL
141 #define IMAPS_PORT      993
142 #endif
143
144 #define IMAP_CMD_LIMIT  1000
145
146 #define QUOTE_IF_REQUIRED(out, str)                             \
147 {                                                               \
148         if (*str != '"' && strpbrk(str, " \t(){}[]%*\\") != NULL) {     \
149                 gchar *__tmp;                                   \
150                 gint len;                                       \
151                                                                 \
152                 len = strlen(str) + 3;                          \
153                 Xalloca(__tmp, len, return IMAP_ERROR);         \
154                 g_snprintf(__tmp, len, "\"%s\"", str);          \
155                 out = __tmp;                                    \
156         } else {                                                \
157                 Xstrdup_a(out, str, return IMAP_ERROR);         \
158         }                                                       \
159 }
160
161 typedef gchar * IMAPSet;
162
163 struct _IMAPFolderItem
164 {
165         FolderItem item;
166
167         guint lastuid;
168         guint uid_next;
169         GSList *uid_list;
170         gboolean batching;
171 };
172
173 static void imap_folder_init            (Folder         *folder,
174                                          const gchar    *name,
175                                          const gchar    *path);
176
177 static Folder   *imap_folder_new        (const gchar    *name,
178                                          const gchar    *path);
179 static void      imap_folder_destroy    (Folder         *folder);
180
181 static IMAPSession *imap_session_new    (const PrefsAccount     *account);
182 static void     imap_session_authenticate(IMAPSession           *session,
183                                           const PrefsAccount    *account);
184 static void     imap_session_destroy    (Session        *session);
185
186 static gchar   *imap_fetch_msg          (Folder         *folder, 
187                                          FolderItem     *item, 
188                                          gint            uid);
189 static gchar   *imap_fetch_msg_full     (Folder         *folder, 
190                                          FolderItem     *item, 
191                                          gint            uid,
192                                          gboolean        headers,
193                                          gboolean        body);
194 static gint     imap_add_msg            (Folder         *folder,
195                                          FolderItem     *dest,
196                                          const gchar    *file, 
197                                          MsgFlags       *flags);
198 static gint     imap_add_msgs           (Folder         *folder, 
199                                          FolderItem     *dest,
200                                          GSList         *file_list,
201                                          GRelation      *relation);
202
203 static gint     imap_copy_msg           (Folder         *folder,
204                                          FolderItem     *dest, 
205                                          MsgInfo        *msginfo);
206 static gint     imap_copy_msgs          (Folder         *folder, 
207                                          FolderItem     *dest, 
208                                          MsgInfoList    *msglist, 
209                                          GRelation      *relation);
210
211 static gint     imap_remove_msg         (Folder         *folder, 
212                                          FolderItem     *item, 
213                                          gint            uid);
214 static gint     imap_remove_msgs        (Folder         *folder, 
215                                          FolderItem     *dest, 
216                                          MsgInfoList    *msglist, 
217                                          GRelation      *relation);
218 static gint     imap_remove_all_msg     (Folder         *folder, 
219                                          FolderItem     *item);
220
221 static gboolean imap_is_msg_changed     (Folder         *folder,
222                                          FolderItem     *item, 
223                                          MsgInfo        *msginfo);
224
225 static gint     imap_close              (Folder         *folder, 
226                                          FolderItem     *item);
227
228 static gint     imap_scan_tree          (Folder         *folder);
229
230 static gint     imap_create_tree        (Folder         *folder);
231
232 static FolderItem *imap_create_folder   (Folder         *folder,
233                                          FolderItem     *parent,
234                                          const gchar    *name);
235 static gint     imap_rename_folder      (Folder         *folder,
236                                          FolderItem     *item, 
237                                          const gchar    *name);
238 static gint     imap_remove_folder      (Folder         *folder, 
239                                          FolderItem     *item);
240
241 static FolderItem *imap_folder_item_new (Folder         *folder);
242 static void imap_folder_item_destroy    (Folder         *folder,
243                                          FolderItem     *item);
244
245 static IMAPSession *imap_session_get    (Folder         *folder);
246
247 static gint imap_greeting               (IMAPSession    *session);
248 static gint imap_auth                   (IMAPSession    *session,
249                                          const gchar    *user,
250                                          const gchar    *pass,
251                                          IMAPAuthType    type);
252
253 static gint imap_scan_tree_recursive    (IMAPSession    *session,
254                                          FolderItem     *item);
255 static GSList *imap_parse_list          (IMAPFolder     *folder,
256                                          IMAPSession    *session,
257                                          const gchar    *real_path,
258                                          gchar          *separator);
259
260 static void imap_create_missing_folders (Folder         *folder);
261 static FolderItem *imap_create_special_folder
262                                         (Folder                 *folder,
263                                          SpecialFolderItemType   stype,
264                                          const gchar            *name);
265
266 static gint imap_do_copy_msgs           (Folder         *folder,
267                                          FolderItem     *dest,
268                                          MsgInfoList    *msglist,
269                                          GRelation      *relation);
270
271 static void imap_delete_all_cached_messages     (FolderItem     *item);
272 static void imap_set_batch              (Folder         *folder,
273                                          FolderItem     *item,
274                                          gboolean        batch);
275 #if USE_OPENSSL
276 static SockInfo *imap_open              (const gchar    *server,
277                                          gushort         port,
278                                          SSLType         ssl_type);
279 #else
280 static SockInfo *imap_open              (const gchar    *server,
281                                          gushort         port);
282 #endif
283
284 #if USE_OPENSSL
285 static SockInfo *imap_open_tunnel(const gchar *server,
286                                   const gchar *tunnelcmd,
287                                   SSLType ssl_type);
288 #else
289 static SockInfo *imap_open_tunnel(const gchar *server,
290                                   const gchar *tunnelcmd);
291 #endif
292
293 #if USE_OPENSSL
294 static SockInfo *imap_init_sock(SockInfo *sock, SSLType ssl_type);
295 #else
296 static SockInfo *imap_init_sock(SockInfo *sock);
297 #endif
298
299 static gchar *imap_get_flag_str         (IMAPFlags       flags);
300 static gint imap_set_message_flags      (IMAPSession    *session,
301                                          MsgNumberList  *numlist,
302                                          IMAPFlags       flags,
303                                          gboolean        is_set);
304 static gint imap_select                 (IMAPSession    *session,
305                                          IMAPFolder     *folder,
306                                          const gchar    *path,
307                                          gint           *exists,
308                                          gint           *recent,
309                                          gint           *unseen,
310                                          guint32        *uid_validity,
311                                          gboolean        block);
312 static gint imap_status                 (IMAPSession    *session,
313                                          IMAPFolder     *folder,
314                                          const gchar    *path,
315                                          gint           *messages,
316                                          gint           *recent,
317                                          guint32        *uid_next,
318                                          guint32        *uid_validity,
319                                          gint           *unseen,
320                                          gboolean        block);
321
322 static void imap_parse_namespace                (IMAPSession    *session,
323                                                  IMAPFolder     *folder);
324 static void imap_get_namespace_by_list          (IMAPSession    *session,
325                                                  IMAPFolder     *folder);
326 static IMAPNameSpace *imap_find_namespace       (IMAPFolder     *folder,
327                                                  const gchar    *path);
328 static gchar imap_get_path_separator            (IMAPFolder     *folder,
329                                                  const gchar    *path);
330 static gchar *imap_get_real_path                (IMAPFolder     *folder,
331                                                  const gchar    *path);
332
333 static gchar *imap_parse_atom           (SockInfo       *sock,
334                                          gchar          *src,
335                                          gchar          *dest,
336                                          gint            dest_len,
337                                          GString        *str);
338 static MsgFlags imap_parse_flags        (const gchar    *flag_str);
339 static MsgInfo *imap_parse_envelope     (SockInfo       *sock,
340                                          FolderItem     *item,
341                                          GString        *line_str);
342
343 static gboolean imap_has_capability     (IMAPSession    *session,
344                                          const gchar    *cap);
345 static void imap_free_capabilities      (IMAPSession    *session);
346
347 /* low-level IMAP4rev1 commands */
348 static gint imap_cmd_authenticate
349                                 (IMAPSession    *session,
350                                  const gchar    *user,
351                                  const gchar    *pass,
352                                  IMAPAuthType    type);
353 static gint imap_cmd_login      (IMAPSession    *session,
354                                  const gchar    *user,
355                                  const gchar    *pass);
356 static gint imap_cmd_logout     (IMAPSession    *session);
357 static gint imap_cmd_noop       (IMAPSession    *session);
358 #if USE_OPENSSL
359 static gint imap_cmd_starttls   (IMAPSession    *session);
360 #endif
361 static gint imap_cmd_namespace  (IMAPSession    *session,
362                                  gchar         **ns_str);
363 static gint imap_cmd_list       (IMAPSession    *session,
364                                  const gchar    *ref,
365                                  const gchar    *mailbox,
366                                  GPtrArray      *argbuf);
367 static gint imap_cmd_do_select  (IMAPSession    *session,
368                                  const gchar    *folder,
369                                  gboolean        examine,
370                                  gint           *exists,
371                                  gint           *recent,
372                                  gint           *unseen,
373                                  guint32        *uid_validity,
374                                  gboolean        block);
375 static gint imap_cmd_select     (IMAPSession    *session,
376                                  const gchar    *folder,
377                                  gint           *exists,
378                                  gint           *recent,
379                                  gint           *unseen,
380                                  guint32        *uid_validity,
381                                  gboolean        block);
382 static gint imap_cmd_examine    (IMAPSession    *session,
383                                  const gchar    *folder,
384                                  gint           *exists,
385                                  gint           *recent,
386                                  gint           *unseen,
387                                  guint32        *uid_validity,
388                                  gboolean        block);
389 static gint imap_cmd_create     (IMAPSession    *sock,
390                                  const gchar    *folder);
391 static gint imap_cmd_rename     (IMAPSession    *sock,
392                                  const gchar    *oldfolder,
393                                  const gchar    *newfolder);
394 static gint imap_cmd_delete     (IMAPSession    *session,
395                                  const gchar    *folder);
396 static gint imap_cmd_envelope   (IMAPSession    *session,
397                                  IMAPSet         set);
398 static gint imap_cmd_fetch      (IMAPSession    *sock,
399                                  guint32         uid,
400                                  const gchar    *filename,
401                                  gboolean        headers,
402                                  gboolean        body);
403 static gint imap_cmd_append     (IMAPSession    *session,
404                                  const gchar    *destfolder,
405                                  const gchar    *file,
406                                  IMAPFlags       flags,
407                                  guint32        *new_uid);
408 static gint imap_cmd_copy       (IMAPSession    *session, 
409                                  const gchar    *seq_set, 
410                                  const gchar    *destfolder,
411                                  GRelation      *uid_mapping);
412 static gint imap_cmd_store      (IMAPSession    *session,
413                                  IMAPSet         set,
414                                  gchar          *sub_cmd);
415 static gint imap_cmd_expunge    (IMAPSession    *session,
416                                  IMAPSet         seq_set);
417 static gint imap_cmd_close      (IMAPSession    *session);
418
419 static gint imap_cmd_ok         (IMAPSession    *session,
420                                  GPtrArray      *argbuf);
421 static gint imap_cmd_ok_block   (IMAPSession    *session,
422                                  GPtrArray      *argbuf);
423 static gint imap_cmd_ok_with_block
424                                 (IMAPSession    *session, 
425                                  GPtrArray      *argbuf, 
426                                  gboolean       block);
427 static void imap_gen_send       (IMAPSession    *session,
428                                  const gchar    *format, ...);
429 static gint imap_gen_recv       (IMAPSession    *session,
430                                  gchar         **ret);
431 static gint imap_gen_recv_block (IMAPSession    *session,
432                                  gchar         **ret);
433 static gint imap_gen_recv_with_block    
434                                 (IMAPSession    *session,
435                                  gchar         **ret,
436                                  gboolean        block);
437
438 /* misc utility functions */
439 static gchar *strchr_cpy                        (const gchar    *src,
440                                                  gchar           ch,
441                                                  gchar          *dest,
442                                                  gint            len);
443 static gchar *get_quoted                        (const gchar    *src,
444                                                  gchar           ch,
445                                                  gchar          *dest,
446                                                  gint            len);
447 static gchar *search_array_contain_str          (GPtrArray      *array,
448                                                  const gchar    *str);
449 static gchar *search_array_str                  (GPtrArray      *array,
450                                                  const gchar    *str);
451 static void imap_path_separator_subst           (gchar          *str,
452                                                  gchar           separator);
453
454 static gchar *imap_utf8_to_modified_utf7        (const gchar    *from);
455 static gchar *imap_modified_utf7_to_utf8        (const gchar    *mutf7_str);
456
457 static GSList *imap_get_seq_set_from_numlist    (MsgNumberList  *msglist);
458 static GSList *imap_get_seq_set_from_msglist    (MsgInfoList    *msglist);
459 static void imap_seq_set_free                   (GSList         *seq_list);
460
461 static gboolean imap_rename_folder_func         (GNode          *node,
462                                                  gpointer        data);
463 static gint imap_get_num_list                   (Folder         *folder,
464                                                  FolderItem     *item,
465                                                  GSList        **list,
466                                                  gboolean       *old_uids_valid);
467 static GSList *imap_get_msginfos                (Folder         *folder,
468                                                  FolderItem     *item,
469                                                  GSList         *msgnum_list);
470 static MsgInfo *imap_get_msginfo                (Folder         *folder,
471                                                  FolderItem     *item,
472                                                  gint            num);
473 static gboolean imap_scan_required              (Folder         *folder,
474                                                  FolderItem     *item);
475 static void imap_change_flags                   (Folder         *folder,
476                                                  FolderItem     *item,
477                                                  MsgInfo        *msginfo,
478                                                  MsgPermFlags    newflags);
479 static gint imap_get_flags                      (Folder         *folder,
480                                                  FolderItem     *item,
481                                                  MsgInfoList    *msglist,
482                                                  GRelation      *msgflags);
483 static gchar *imap_folder_get_path              (Folder         *folder);
484 static gchar *imap_item_get_path                (Folder         *folder,
485                                                  FolderItem     *item);
486 static MsgInfo *imap_parse_msg(const gchar *file, FolderItem *item);
487 static GHashTable *flags_set_table = NULL;
488 static GHashTable *flags_unset_table = NULL;
489 typedef struct _hashtable_data {
490         IMAPSession *session;
491         GSList *msglist;
492 } hashtable_data;
493
494 static FolderClass imap_class;
495
496 typedef struct _thread_data {
497         gchar *server;
498         gushort port;
499         gboolean done;
500         SockInfo *sock;
501 #ifdef USE_OPENSSL
502         SSLType ssl_type;
503 #endif
504 } thread_data;
505
506 #ifdef USE_PTHREAD
507 void *imap_getline_thread(void *data)
508 {
509         thread_data *td = (thread_data *)data;
510         gchar *line = NULL;
511         
512         line = sock_getline(td->sock);
513         
514         td->done = TRUE;
515         
516         return (void *)line;
517 }
518 #endif
519
520 /* imap_getline just wraps sock_getline inside a thread, 
521  * performing gtk updates so that the interface isn't frozen.
522  */
523 static gchar *imap_getline(SockInfo *sock)
524 {
525 #if (defined USE_PTHREAD && defined __GLIBC__ && (__GLIBC__ > 2 || (__GLIBC__ == 2 && __GLIBC_MINOR__ >= 3)))
526         thread_data *td = g_new0(thread_data, 1);
527         pthread_t pt;
528         gchar *line;
529         td->sock = sock;
530         td->done = FALSE;
531         
532         if (pthread_create(&pt, PTHREAD_CREATE_JOINABLE,
533                         imap_getline_thread, td) != 0) {
534                 g_free(td);
535                 return sock_getline(sock);
536         }
537         
538         debug_print("+++waiting for imap_getline_thread...\n");
539         while(!td->done) {
540                 /* don't let the interface freeze while waiting */
541                 sylpheed_do_idle();
542         }
543         debug_print("---imap_getline_thread done\n");
544
545         /* get the thread's return value and clean its resources */
546         pthread_join(pt, (void *)&line);
547         g_free(td);
548
549         return line;
550 #else
551         return sock_getline(sock);
552 #endif
553 }
554
555 FolderClass *imap_get_class(void)
556 {
557         if (imap_class.idstr == NULL) {
558                 imap_class.type = F_IMAP;
559                 imap_class.idstr = "imap";
560                 imap_class.uistr = "IMAP4";
561
562                 /* Folder functions */
563                 imap_class.new_folder = imap_folder_new;
564                 imap_class.destroy_folder = imap_folder_destroy;
565                 imap_class.scan_tree = imap_scan_tree;
566                 imap_class.create_tree = imap_create_tree;
567
568                 /* FolderItem functions */
569                 imap_class.item_new = imap_folder_item_new;
570                 imap_class.item_destroy = imap_folder_item_destroy;
571                 imap_class.item_get_path = imap_item_get_path;
572                 imap_class.create_folder = imap_create_folder;
573                 imap_class.rename_folder = imap_rename_folder;
574                 imap_class.remove_folder = imap_remove_folder;
575                 imap_class.close = imap_close;
576                 imap_class.get_num_list = imap_get_num_list;
577                 imap_class.scan_required = imap_scan_required;
578
579                 /* Message functions */
580                 imap_class.get_msginfo = imap_get_msginfo;
581                 imap_class.get_msginfos = imap_get_msginfos;
582                 imap_class.fetch_msg = imap_fetch_msg;
583                 imap_class.fetch_msg_full = imap_fetch_msg_full;
584                 imap_class.add_msg = imap_add_msg;
585                 imap_class.add_msgs = imap_add_msgs;
586                 imap_class.copy_msg = imap_copy_msg;
587                 imap_class.copy_msgs = imap_copy_msgs;
588                 imap_class.remove_msg = imap_remove_msg;
589                 imap_class.remove_msgs = imap_remove_msgs;
590                 imap_class.remove_all_msg = imap_remove_all_msg;
591                 imap_class.is_msg_changed = imap_is_msg_changed;
592                 imap_class.change_flags = imap_change_flags;
593                 imap_class.get_flags = imap_get_flags;
594                 imap_class.set_batch = imap_set_batch;
595         }
596         
597         return &imap_class;
598 }
599
600 static gchar *get_seq_set_from_seq_list(GSList *seq_list)
601 {
602         gchar *val = NULL;
603         gchar *tmp = NULL;
604         GSList *cur = NULL;
605         
606         for (cur = seq_list; cur != NULL; cur = cur->next) {
607                 tmp = val?g_strdup(val):NULL;
608                 g_free(val);
609                 val = g_strconcat(tmp?tmp:"", tmp?",":"",(gchar *)cur->data,
610                                   NULL);
611                 g_free(tmp);
612         }
613         return val;
614 }
615
616
617 static Folder *imap_folder_new(const gchar *name, const gchar *path)
618 {
619         Folder *folder;
620
621         folder = (Folder *)g_new0(IMAPFolder, 1);
622         folder->klass = &imap_class;
623         imap_folder_init(folder, name, path);
624
625         return folder;
626 }
627
628 static void imap_folder_destroy(Folder *folder)
629 {
630         gchar *dir;
631
632         dir = imap_folder_get_path(folder);
633         if (is_dir_exist(dir))
634                 remove_dir_recursive(dir);
635         g_free(dir);
636
637         folder_remote_folder_destroy(REMOTE_FOLDER(folder));
638 }
639
640 static void imap_folder_init(Folder *folder, const gchar *name,
641                              const gchar *path)
642 {
643         folder_remote_folder_init((Folder *)folder, name, path);
644 }
645
646 static FolderItem *imap_folder_item_new(Folder *folder)
647 {
648         IMAPFolderItem *item;
649         
650         item = g_new0(IMAPFolderItem, 1);
651         item->lastuid = 0;
652         item->uid_next = 0;
653         item->uid_list = NULL;
654
655         return (FolderItem *)item;
656 }
657
658 static void imap_folder_item_destroy(Folder *folder, FolderItem *_item)
659 {
660         IMAPFolderItem *item = (IMAPFolderItem *)_item;
661
662         g_return_if_fail(item != NULL);
663         g_slist_free(item->uid_list);
664
665         g_free(_item);
666 }
667
668 static gboolean imap_reset_uid_lists_func(GNode *node, gpointer data)
669 {
670         IMAPFolderItem *item = (IMAPFolderItem *)node->data;
671         
672         item->lastuid = 0;
673         item->uid_next = 0;
674         g_slist_free(item->uid_list);
675         item->uid_list = NULL;
676         
677         return FALSE;
678 }
679
680 static void imap_reset_uid_lists(Folder *folder)
681 {
682         if(folder->node == NULL)
683                 return;
684         
685         /* Destroy all uid lists and rest last uid */
686         g_node_traverse(folder->node, G_IN_ORDER, G_TRAVERSE_ALL, -1, imap_reset_uid_lists_func, NULL); 
687 }
688
689 /* Send CAPABILITY, and examine the server's response to see whether this
690  * connection is pre-authenticated or not and build a list of CAPABILITIES. */
691 static gint imap_greeting(IMAPSession *session)
692 {
693         gchar *capstr;
694         GPtrArray *argbuf;
695
696         imap_gen_send(session, "CAPABILITY");
697         
698         argbuf = g_ptr_array_new();
699
700         if (imap_cmd_ok(session, argbuf) != IMAP_SUCCESS ||
701             ((capstr = search_array_str(argbuf, "CAPABILITY ")) == NULL)) {
702                 ptr_array_free_strings(argbuf);
703                 g_ptr_array_free(argbuf, TRUE);
704                 return -1;
705         }
706
707         session->authenticated = search_array_str(argbuf, "PREAUTH") != NULL;
708         
709         capstr += strlen("CAPABILITY ");
710
711         IMAP_SESSION(session)->capability = g_strsplit(capstr, " ", 0);
712         
713         ptr_array_free_strings(argbuf);
714         g_ptr_array_free(argbuf, TRUE);
715
716         if (imap_has_capability(session, "UIDPLUS")) 
717                 session->uidplus = TRUE; 
718
719         return 0;
720 }
721
722 static gint imap_auth(IMAPSession *session, const gchar *user, const gchar *pass,
723                       IMAPAuthType type)
724 {
725         gint ok;
726
727         if (type == 0 || type == IMAP_AUTH_LOGIN)
728                 ok = imap_cmd_login(session, user, pass);
729         else
730                 ok = imap_cmd_authenticate(session, user, pass, type);
731
732         if (ok == IMAP_SUCCESS)
733                 session->authenticated = TRUE;
734
735         return ok;
736 }
737
738 static IMAPSession *imap_session_get(Folder *folder)
739 {
740         RemoteFolder *rfolder = REMOTE_FOLDER(folder);
741         IMAPSession *session = NULL;
742
743         g_return_val_if_fail(folder != NULL, NULL);
744         g_return_val_if_fail(FOLDER_CLASS(folder) == &imap_class, NULL);
745         g_return_val_if_fail(folder->account != NULL, NULL);
746         
747         if (prefs_common.work_offline && !imap_gtk_should_override()) {
748                 return NULL;
749         }
750
751         /* Make sure we have a session */
752         if (rfolder->session != NULL) {
753                 session = IMAP_SESSION(rfolder->session);
754         } else {
755                 imap_reset_uid_lists(folder);
756                 session = imap_session_new(folder->account);
757         }
758         if(session == NULL)
759                 return NULL;
760
761         if (SESSION(session)->sock->state == CONN_DISCONNECTED) {
762                 debug_print("IMAP server disconnected\n");
763                 session_destroy(SESSION(session));
764                 imap_reset_uid_lists(folder);
765                 session = imap_session_new(folder->account);
766         }
767
768         /* Make sure session is authenticated */
769         if (!IMAP_SESSION(session)->authenticated)
770                 imap_session_authenticate(IMAP_SESSION(session), folder->account);
771         if (!IMAP_SESSION(session)->authenticated) {
772                 session_destroy(SESSION(session));
773                 rfolder->session = NULL;
774                 return NULL;
775         }
776
777         /* Make sure we have parsed the IMAP namespace */
778         imap_parse_namespace(IMAP_SESSION(session),
779                              IMAP_FOLDER(folder));
780
781         /* I think the point of this code is to avoid sending a
782          * keepalive if we've used the session recently and therefore
783          * think it's still alive.  Unfortunately, most of the code
784          * does not yet check for errors on the socket, and so if the
785          * connection drops we don't notice until the timeout expires.
786          * A better solution than sending a NOOP every time would be
787          * for every command to be prepared to retry until it is
788          * successfully sent. -- mbp */
789         if (time(NULL) - SESSION(session)->last_access_time > SESSION_TIMEOUT_INTERVAL) {
790                 /* verify that the session is still alive */
791                 if (imap_cmd_noop(session) != IMAP_SUCCESS) {
792                         /* Check if this is the first try to establish a
793                            connection, if yes we don't try to reconnect */
794                         if (rfolder->session == NULL) {
795                                 log_warning(_("Connecting to %s failed"),
796                                             folder->account->recv_server);
797                                 session_destroy(SESSION(session));
798                                 session = NULL;
799                         } else {
800                                 log_warning(_("IMAP4 connection to %s has been"
801                                             " disconnected. Reconnecting...\n"),
802                                             folder->account->recv_server);
803                                 statusbar_print_all(_("IMAP4 connection to %s has been"
804                                             " disconnected. Reconnecting...\n"),
805                                             folder->account->recv_server);
806                                 session_destroy(SESSION(session));
807                                 /* Clear folders session to make imap_session_get create
808                                    a new session, because of rfolder->session == NULL
809                                    it will not try to reconnect again and so avoid an
810                                    endless loop */
811                                 rfolder->session = NULL;
812                                 session = imap_session_get(folder);
813                                 statusbar_pop_all();
814                         }
815                 }
816         }
817
818         rfolder->session = SESSION(session);
819         
820         return IMAP_SESSION(session);
821 }
822
823 static IMAPSession *imap_session_new(const PrefsAccount *account)
824 {
825         IMAPSession *session;
826         SockInfo *imap_sock;
827         gushort port;
828
829 #ifdef USE_OPENSSL
830         /* FIXME: IMAP over SSL only... */ 
831         SSLType ssl_type;
832
833         port = account->set_imapport ? account->imapport
834                 : account->ssl_imap == SSL_TUNNEL ? IMAPS_PORT : IMAP4_PORT;
835         ssl_type = account->ssl_imap;   
836 #else
837         port = account->set_imapport ? account->imapport
838                 : IMAP4_PORT;
839 #endif
840
841         if (account->set_tunnelcmd) {
842                 log_message(_("creating tunneled IMAP4 connection\n"));
843 #if USE_OPENSSL
844                 if ((imap_sock = imap_open_tunnel(account->recv_server, 
845                                                   account->tunnelcmd,
846                                                   ssl_type)) == NULL)
847 #else
848                 if ((imap_sock = imap_open_tunnel(account->recv_server, 
849                                                   account->tunnelcmd)) == NULL)
850 #endif
851                         return NULL;
852         } else {
853                 g_return_val_if_fail(account->recv_server != NULL, NULL);
854
855                 log_message(_("creating IMAP4 connection to %s:%d ...\n"),
856                             account->recv_server, port);
857                 
858 #if USE_OPENSSL
859                 if ((imap_sock = imap_open(account->recv_server, port,
860                                            ssl_type)) == NULL)
861 #else
862                 if ((imap_sock = imap_open(account->recv_server, port)) == NULL)
863 #endif
864                         return NULL;
865         }
866
867         session = g_new0(IMAPSession, 1);
868         session_init(SESSION(session));
869         SESSION(session)->type             = SESSION_IMAP;
870         SESSION(session)->server           = g_strdup(account->recv_server);
871         SESSION(session)->sock             = imap_sock;
872
873         SESSION(session)->destroy          = imap_session_destroy;
874
875         session->capability = NULL;
876
877         session->authenticated = FALSE;
878         session->mbox = NULL;
879         session->cmd_count = 0;
880
881         /* Only need to log in if the connection was not PREAUTH */
882         if (imap_greeting(session) != IMAP_SUCCESS) {
883                 session_destroy(SESSION(session));
884                 return NULL;
885         }
886
887 #if USE_OPENSSL
888         if (account->ssl_imap == SSL_STARTTLS && 
889             imap_has_capability(session, "STARTTLS")) {
890                 gint ok;
891
892                 ok = imap_cmd_starttls(session);
893                 if (ok != IMAP_SUCCESS) {
894                         log_warning(_("Can't start TLS session.\n"));
895                         session_destroy(SESSION(session));
896                         return NULL;
897                 }
898                 if (!ssl_init_socket_with_method(SESSION(session)->sock, 
899                     SSL_METHOD_TLSv1)) {
900                         session_destroy(SESSION(session));
901                         return NULL;
902                 }
903
904                 imap_free_capabilities(session);
905                 session->authenticated = FALSE;
906                 session->uidplus = FALSE;
907                 session->cmd_count = 1;
908
909                 if (imap_greeting(session) != IMAP_SUCCESS) {
910                         session_destroy(SESSION(session));
911                         return NULL;
912                 }               
913         }
914 #endif
915         log_message("IMAP connection is %s-authenticated\n",
916                     (session->authenticated) ? "pre" : "un");
917
918         return session;
919 }
920
921 static void imap_session_authenticate(IMAPSession *session, 
922                                       const PrefsAccount *account)
923 {
924         gchar *pass;
925
926         g_return_if_fail(account->userid != NULL);
927
928         pass = account->passwd;
929         if (!pass) {
930                 gchar *tmp_pass;
931                 tmp_pass = input_dialog_query_password(account->recv_server, account->userid);
932                 if (!tmp_pass)
933                         return;
934                 Xstrdup_a(pass, tmp_pass, {g_free(tmp_pass); return;});
935                 g_free(tmp_pass);
936         }
937         statusbar_print_all(_("Connecting to IMAP4 server %s...\n"),
938                                 account->recv_server);
939         if (imap_auth(session, account->userid, pass, account->imap_auth_type) != IMAP_SUCCESS) {
940                 imap_cmd_logout(session);
941                 statusbar_pop_all();
942                 
943                 return;
944         }
945         statusbar_pop_all();
946         session->authenticated = TRUE;
947 }
948
949 static void imap_session_destroy(Session *session)
950 {
951         imap_free_capabilities(IMAP_SESSION(session));
952         g_free(IMAP_SESSION(session)->mbox);
953         sock_close(session->sock);
954         session->sock = NULL;
955 }
956
957 static gchar *imap_fetch_msg(Folder *folder, FolderItem *item, gint uid)
958 {
959         return imap_fetch_msg_full(folder, item, uid, TRUE, TRUE);
960 }
961
962 static guint get_size_with_lfs(MsgInfo *info) 
963 {
964         FILE *fp = NULL;
965         guint cnt = 0;
966         gchar buf[4096];
967         
968         if (info == NULL)
969                 return -1;
970         
971         fp = procmsg_open_message(info);
972         if (!fp)
973                 return -1;
974         
975         while (fgets(buf, sizeof (buf), fp) != NULL) {
976                 cnt += strlen(buf);
977                 if (!strstr(buf, "\r") && strstr(buf, "\n"))
978                         cnt++;
979         }
980         
981         fclose(fp);
982         return cnt;
983 }
984
985 static gchar *imap_fetch_msg_full(Folder *folder, FolderItem *item, gint uid,
986                                   gboolean headers, gboolean body)
987 {
988         gchar *path, *filename;
989         IMAPSession *session;
990         gint ok;
991
992         g_return_val_if_fail(folder != NULL, NULL);
993         g_return_val_if_fail(item != NULL, NULL);
994
995         if (uid == 0)
996                 return NULL;
997
998         path = folder_item_get_path(item);
999         if (!is_dir_exist(path))
1000                 make_dir_hier(path);
1001         filename = g_strconcat(path, G_DIR_SEPARATOR_S, itos(uid), NULL);
1002         g_free(path);
1003
1004         if (is_file_exist(filename)) {
1005                 /* see whether the local file represents the whole message
1006                  * or not. As the IMAP server reports size with \r chars,
1007                  * we have to update the local file (UNIX \n only) size */
1008                 MsgInfo *msginfo = imap_parse_msg(filename, item);
1009                 MsgInfo *cached = msgcache_get_msg(item->cache,uid);
1010                 guint have_size = get_size_with_lfs(msginfo);
1011                 debug_print("message %d has been already %scached (%d/%d).\n", uid,
1012                                 have_size == cached->size ? "fully ":"",
1013                                 have_size, cached? (int)cached->size : -1);
1014                                 
1015                 if (cached && (cached->size == have_size || !body)) {
1016                         procmsg_msginfo_free(cached);
1017                         procmsg_msginfo_free(msginfo);
1018                         return filename;
1019                 } else {
1020                         procmsg_msginfo_free(cached);
1021                         procmsg_msginfo_free(msginfo);
1022                 }
1023         }
1024
1025         session = imap_session_get(folder);
1026         if (!session) {
1027                 g_free(filename);
1028                 return NULL;
1029         }
1030
1031         debug_print("IMAP fetching messages\n");
1032         ok = imap_select(session, IMAP_FOLDER(folder), item->path,
1033                          NULL, NULL, NULL, NULL, FALSE);
1034         if (ok != IMAP_SUCCESS) {
1035                 g_warning("can't select mailbox %s\n", item->path);
1036                 g_free(filename);
1037                 return NULL;
1038         }
1039
1040         debug_print("getting message %d...\n", uid);
1041         ok = imap_cmd_fetch(session, (guint32)uid, filename, headers, body);
1042
1043         if (ok != IMAP_SUCCESS) {
1044                 g_warning("can't fetch message %d\n", uid);
1045                 g_free(filename);
1046                 return NULL;
1047         }
1048
1049         return filename;
1050 }
1051
1052 static gint imap_add_msg(Folder *folder, FolderItem *dest, 
1053                          const gchar *file, MsgFlags *flags)
1054 {
1055         gint ret;
1056         GSList file_list;
1057         MsgFileInfo fileinfo;
1058
1059         g_return_val_if_fail(file != NULL, -1);
1060
1061         fileinfo.msginfo = NULL;
1062         fileinfo.file = (gchar *)file;
1063         fileinfo.flags = flags;
1064         file_list.data = &fileinfo;
1065         file_list.next = NULL;
1066
1067         ret = imap_add_msgs(folder, dest, &file_list, NULL);
1068         return ret;
1069 }
1070
1071 static gint imap_add_msgs(Folder *folder, FolderItem *dest, GSList *file_list,
1072                    GRelation *relation)
1073 {
1074         gchar *destdir;
1075         IMAPSession *session;
1076         guint32 last_uid = 0;
1077         GSList *cur;
1078         MsgFileInfo *fileinfo;
1079         gint ok;
1080
1081         g_return_val_if_fail(folder != NULL, -1);
1082         g_return_val_if_fail(dest != NULL, -1);
1083         g_return_val_if_fail(file_list != NULL, -1);
1084
1085         session = imap_session_get(folder);
1086         if (!session) return -1;
1087
1088         destdir = imap_get_real_path(IMAP_FOLDER(folder), dest->path);
1089
1090         for (cur = file_list; cur != NULL; cur = cur->next) {
1091                 IMAPFlags iflags = 0;
1092                 guint32 new_uid = 0;
1093
1094                 fileinfo = (MsgFileInfo *)cur->data;
1095
1096                 if (fileinfo->flags) {
1097                         if (MSG_IS_MARKED(*fileinfo->flags))
1098                                 iflags |= IMAP_FLAG_FLAGGED;
1099                         if (MSG_IS_REPLIED(*fileinfo->flags))
1100                                 iflags |= IMAP_FLAG_ANSWERED;
1101                         if (!MSG_IS_UNREAD(*fileinfo->flags))
1102                                 iflags |= IMAP_FLAG_SEEN;
1103                 }
1104
1105                 if (dest->stype == F_OUTBOX ||
1106                     dest->stype == F_QUEUE  ||
1107                     dest->stype == F_DRAFT  ||
1108                     dest->stype == F_TRASH)
1109                         iflags |= IMAP_FLAG_SEEN;
1110
1111                 ok = imap_cmd_append(session, destdir, fileinfo->file, iflags, 
1112                                      &new_uid);
1113
1114                 if (ok != IMAP_SUCCESS) {
1115                         g_warning("can't append message %s\n", fileinfo->file);
1116                         g_free(destdir);
1117                         return -1;
1118                 }
1119
1120                 if (relation != NULL)
1121                         g_relation_insert(relation, fileinfo->msginfo != NULL ? 
1122                                           (gpointer) fileinfo->msginfo : (gpointer) fileinfo,
1123                                           GINT_TO_POINTER(dest->last_num + 1));
1124                 if (last_uid < new_uid)
1125                         last_uid = new_uid;
1126         }
1127
1128         g_free(destdir);
1129
1130         return last_uid;
1131 }
1132
1133 static gint imap_do_copy_msgs(Folder *folder, FolderItem *dest, 
1134                               MsgInfoList *msglist, GRelation *relation)
1135 {
1136         FolderItem *src;
1137         gchar *destdir;
1138         GSList *seq_list, *cur;
1139         MsgInfo *msginfo;
1140         IMAPSession *session;
1141         gint ok = IMAP_SUCCESS;
1142         GRelation *uid_mapping;
1143         gint last_num = 0;
1144         
1145         g_return_val_if_fail(folder != NULL, -1);
1146         g_return_val_if_fail(dest != NULL, -1);
1147         g_return_val_if_fail(msglist != NULL, -1);
1148
1149         session = imap_session_get(folder);
1150         if (!session) {
1151                 return -1;
1152         }
1153         msginfo = (MsgInfo *)msglist->data;
1154
1155         src = msginfo->folder;
1156         if (src == dest) {
1157                 g_warning("the src folder is identical to the dest.\n");
1158                 return -1;
1159         }
1160
1161         ok = imap_select(session, IMAP_FOLDER(folder), msginfo->folder->path,
1162                          NULL, NULL, NULL, NULL, FALSE);
1163         if (ok != IMAP_SUCCESS) {
1164                 return ok;
1165         }
1166
1167         destdir = imap_get_real_path(IMAP_FOLDER(folder), dest->path);
1168         seq_list = imap_get_seq_set_from_msglist(msglist);
1169         uid_mapping = g_relation_new(2);
1170         g_relation_index(uid_mapping, 0, g_direct_hash, g_direct_equal);
1171         
1172         for (cur = seq_list; cur != NULL; cur = g_slist_next(cur)) {
1173                 gchar *seq_set = (gchar *)cur->data;
1174
1175                 debug_print("Copying message %s%c[%s] to %s ...\n",
1176                             src->path, G_DIR_SEPARATOR,
1177                             seq_set, destdir);
1178
1179                 ok = imap_cmd_copy(session, seq_set, destdir, uid_mapping);
1180                 if (ok != IMAP_SUCCESS) {
1181                         g_relation_destroy(uid_mapping);
1182                         imap_seq_set_free(seq_list);
1183                         return -1;
1184                 }
1185         }
1186
1187         for (cur = msglist; cur != NULL; cur = g_slist_next(cur)) {
1188                 MsgInfo *msginfo = (MsgInfo *)cur->data;
1189                 GTuples *tuples;
1190
1191                 tuples = g_relation_select(uid_mapping, 
1192                                            GINT_TO_POINTER(msginfo->msgnum),
1193                                            0);
1194                 if (tuples->len > 0) {
1195                         gint num = GPOINTER_TO_INT(g_tuples_index(tuples, 0, 1));
1196                         g_relation_insert(relation, msginfo,
1197                                           GPOINTER_TO_INT(num));
1198                         if (num > last_num)
1199                                 last_num = num;
1200                 } else
1201                         g_relation_insert(relation, msginfo,
1202                                           GPOINTER_TO_INT(0));
1203                 g_tuples_destroy(tuples);
1204         }
1205
1206         g_relation_destroy(uid_mapping);
1207         imap_seq_set_free(seq_list);
1208
1209         g_free(destdir);
1210         
1211         IMAP_FOLDER_ITEM(dest)->lastuid = 0;
1212         IMAP_FOLDER_ITEM(dest)->uid_next = 0;
1213         g_slist_free(IMAP_FOLDER_ITEM(dest)->uid_list);
1214         IMAP_FOLDER_ITEM(dest)->uid_list = NULL;
1215
1216         if (ok == IMAP_SUCCESS)
1217                 return last_num;
1218         else
1219                 return -1;
1220 }
1221
1222 static gint imap_copy_msg(Folder *folder, FolderItem *dest, MsgInfo *msginfo)
1223 {
1224         GSList msglist;
1225
1226         g_return_val_if_fail(msginfo != NULL, -1);
1227
1228         msglist.data = msginfo;
1229         msglist.next = NULL;
1230
1231         return imap_copy_msgs(folder, dest, &msglist, NULL);
1232 }
1233
1234 static gint imap_copy_msgs(Folder *folder, FolderItem *dest, 
1235                     MsgInfoList *msglist, GRelation *relation)
1236 {
1237         MsgInfo *msginfo;
1238         GSList *file_list;
1239         gint ret;
1240
1241         g_return_val_if_fail(folder != NULL, -1);
1242         g_return_val_if_fail(dest != NULL, -1);
1243         g_return_val_if_fail(msglist != NULL, -1);
1244
1245         msginfo = (MsgInfo *)msglist->data;
1246         g_return_val_if_fail(msginfo->folder != NULL, -1);
1247
1248         if (folder == msginfo->folder->folder)
1249                 return imap_do_copy_msgs(folder, dest, msglist, relation);
1250
1251         file_list = procmsg_get_message_file_list(msglist);
1252         g_return_val_if_fail(file_list != NULL, -1);
1253
1254         ret = imap_add_msgs(folder, dest, file_list, relation);
1255
1256         procmsg_message_file_list_free(file_list);
1257
1258         return ret;
1259 }
1260
1261
1262 static gint imap_do_remove_msgs(Folder *folder, FolderItem *dest, 
1263                                 MsgInfoList *msglist, GRelation *relation)
1264 {
1265         gchar *destdir;
1266         GSList *seq_list = NULL, *cur;
1267         MsgInfo *msginfo;
1268         IMAPSession *session;
1269         gint ok = IMAP_SUCCESS;
1270         GRelation *uid_mapping;
1271         
1272         g_return_val_if_fail(folder != NULL, -1);
1273         g_return_val_if_fail(dest != NULL, -1);
1274         g_return_val_if_fail(msglist != NULL, -1);
1275
1276         session = imap_session_get(folder);
1277         if (!session) {
1278                 return -1;
1279         }
1280         msginfo = (MsgInfo *)msglist->data;
1281
1282         ok = imap_select(session, IMAP_FOLDER(folder), msginfo->folder->path,
1283                          NULL, NULL, NULL, NULL, FALSE);
1284         if (ok != IMAP_SUCCESS) {
1285                 return ok;
1286         }
1287
1288         destdir = imap_get_real_path(IMAP_FOLDER(folder), dest->path);
1289         for (cur = msglist; cur; cur = cur->next) {
1290                 msginfo = (MsgInfo *)cur->data;
1291                 seq_list = g_slist_append(seq_list, GINT_TO_POINTER(msginfo->msgnum));
1292         }
1293
1294         uid_mapping = g_relation_new(2);
1295         g_relation_index(uid_mapping, 0, g_direct_hash, g_direct_equal);
1296
1297         ok = imap_set_message_flags
1298                 (IMAP_SESSION(REMOTE_FOLDER(folder)->session),
1299                 seq_list, IMAP_FLAG_DELETED, TRUE);
1300         if (ok != IMAP_SUCCESS) {
1301                 log_warning(_("can't set deleted flags\n"));
1302                 return ok;
1303         }
1304         ok = imap_cmd_expunge(session, NULL);
1305         if (ok != IMAP_SUCCESS) {
1306                 log_warning(_("can't expunge\n"));
1307                 return ok;
1308         }
1309         
1310         g_relation_destroy(uid_mapping);
1311         g_slist_free(seq_list);
1312
1313         g_free(destdir);
1314
1315         if (ok == IMAP_SUCCESS)
1316                 return 0;
1317         else
1318                 return -1;
1319 }
1320
1321 static gint imap_remove_msgs(Folder *folder, FolderItem *dest, 
1322                     MsgInfoList *msglist, GRelation *relation)
1323 {
1324         MsgInfo *msginfo;
1325
1326         g_return_val_if_fail(folder != NULL, -1);
1327         g_return_val_if_fail(dest != NULL, -1);
1328         g_return_val_if_fail(msglist != NULL, -1);
1329
1330         msginfo = (MsgInfo *)msglist->data;
1331         g_return_val_if_fail(msginfo->folder != NULL, -1);
1332
1333         return imap_do_remove_msgs(folder, dest, msglist, relation);
1334 }
1335
1336 static gint imap_remove_all_msg(Folder *folder, FolderItem *item)
1337 {
1338         GSList *list = folder_item_get_msg_list(item);
1339         gint res = imap_remove_msgs(folder, item, list, NULL);
1340         g_slist_free(list);
1341         return res;
1342 }
1343
1344 static gboolean imap_is_msg_changed(Folder *folder, FolderItem *item,
1345                                     MsgInfo *msginfo)
1346 {
1347         /* TODO: properly implement this method */
1348         return FALSE;
1349 }
1350
1351 static gint imap_close(Folder *folder, FolderItem *item)
1352 {
1353         gint ok;
1354         IMAPSession *session;
1355
1356         g_return_val_if_fail(folder != NULL, -1);
1357         g_return_val_if_fail(item != NULL, -1);
1358         g_return_val_if_fail(item->path != NULL, -1);
1359
1360         session = imap_session_get(folder);
1361         if (!session) return -1;
1362
1363         if (session->mbox) {
1364                 if (strcmp2(session->mbox, item->path) != 0) return -1;
1365
1366                 ok = imap_cmd_close(session);
1367                 if (ok != IMAP_SUCCESS)
1368                         log_warning(_("can't close folder\n"));
1369
1370                 g_free(session->mbox);
1371
1372                 session->mbox = NULL;
1373
1374                 return ok;
1375         }
1376
1377         return 0;
1378 }
1379
1380 static gint imap_scan_tree(Folder *folder)
1381 {
1382         FolderItem *item = NULL;
1383         IMAPSession *session;
1384         gchar *root_folder = NULL;
1385
1386         g_return_val_if_fail(folder != NULL, -1);
1387         g_return_val_if_fail(folder->account != NULL, -1);
1388
1389         session = imap_session_get(folder);
1390         if (!session) {
1391                 if (!folder->node) {
1392                         folder_tree_destroy(folder);
1393                         item = folder_item_new(folder, folder->name, NULL);
1394                         item->folder = folder;
1395                         folder->node = item->node = g_node_new(item);
1396                 }
1397                 return -1;
1398         }
1399
1400         if (folder->account->imap_dir && *folder->account->imap_dir) {
1401                 gchar *real_path;
1402                 GPtrArray *argbuf;
1403                 gint ok;
1404
1405                 Xstrdup_a(root_folder, folder->account->imap_dir, return -1);
1406                 extract_quote(root_folder, '"');
1407                 subst_char(root_folder,
1408                            imap_get_path_separator(IMAP_FOLDER(folder),
1409                                                    root_folder),
1410                            '/');
1411                 strtailchomp(root_folder, '/');
1412                 real_path = imap_get_real_path
1413                         (IMAP_FOLDER(folder), root_folder);
1414                 debug_print("IMAP root directory: %s\n", real_path);
1415
1416                 /* check if root directory exist */
1417                 argbuf = g_ptr_array_new();
1418                 ok = imap_cmd_list(session, NULL, real_path, argbuf);
1419                 if (ok != IMAP_SUCCESS ||
1420                     search_array_str(argbuf, "LIST ") == NULL) {
1421                         log_warning(_("root folder %s does not exist\n"), real_path);
1422                         g_ptr_array_free(argbuf, TRUE);
1423                         g_free(real_path);
1424
1425                         if (!folder->node) {
1426                                 item = folder_item_new(folder, folder->name, NULL);
1427                                 item->folder = folder;
1428                                 folder->node = item->node = g_node_new(item);
1429                         }
1430                         return -1;
1431                 }
1432                 g_ptr_array_free(argbuf, TRUE);
1433                 g_free(real_path);
1434         }
1435
1436         if (folder->node)
1437                 item = FOLDER_ITEM(folder->node->data);
1438         if (!item || ((item->path || root_folder) &&
1439                       strcmp2(item->path, root_folder) != 0)) {
1440                 folder_tree_destroy(folder);
1441                 item = folder_item_new(folder, folder->name, root_folder);
1442                 item->folder = folder;
1443                 folder->node = item->node = g_node_new(item);
1444         }
1445
1446         imap_scan_tree_recursive(session, FOLDER_ITEM(folder->node->data));
1447         imap_create_missing_folders(folder);
1448
1449         return 0;
1450 }
1451
1452 static gint imap_scan_tree_recursive(IMAPSession *session, FolderItem *item)
1453 {
1454         Folder *folder;
1455         IMAPFolder *imapfolder;
1456         FolderItem *new_item;
1457         GSList *item_list, *cur;
1458         GNode *node;
1459         gchar *real_path;
1460         gchar *wildcard_path;
1461         gchar separator;
1462         gchar wildcard[3];
1463
1464         g_return_val_if_fail(item != NULL, -1);
1465         g_return_val_if_fail(item->folder != NULL, -1);
1466         g_return_val_if_fail(item->no_sub == FALSE, -1);
1467
1468         folder = item->folder;
1469         imapfolder = IMAP_FOLDER(folder);
1470
1471         separator = imap_get_path_separator(imapfolder, item->path);
1472
1473         if (folder->ui_func)
1474                 folder->ui_func(folder, item, folder->ui_func_data);
1475
1476         if (item->path) {
1477                 wildcard[0] = separator;
1478                 wildcard[1] = '%';
1479                 wildcard[2] = '\0';
1480                 real_path = imap_get_real_path(imapfolder, item->path);
1481         } else {
1482                 wildcard[0] = '%';
1483                 wildcard[1] = '\0';
1484                 real_path = g_strdup("");
1485         }
1486
1487         Xstrcat_a(wildcard_path, real_path, wildcard,
1488                   {g_free(real_path); return IMAP_ERROR;});
1489         QUOTE_IF_REQUIRED(wildcard_path, wildcard_path);
1490
1491         imap_gen_send(session, "LIST \"\" %s",
1492                       wildcard_path);
1493
1494         strtailchomp(real_path, separator);
1495         item_list = imap_parse_list(imapfolder, session, real_path, NULL);
1496         g_free(real_path);
1497
1498         node = item->node->children;
1499         while (node != NULL) {
1500                 FolderItem *old_item = FOLDER_ITEM(node->data);
1501                 GNode *next = node->next;
1502
1503                 new_item = NULL;
1504                 for (cur = item_list; cur != NULL; cur = cur->next) {
1505                         FolderItem *cur_item = FOLDER_ITEM(cur->data);
1506                         if (!strcmp2(old_item->path, cur_item->path)) {
1507                                 new_item = cur_item;
1508                                 break;
1509                         }
1510                 }
1511                 if (!new_item) {
1512                         debug_print("folder '%s' not found. removing...\n",
1513                                     old_item->path);
1514                         folder_item_remove(old_item);
1515                 } else {
1516                         old_item->no_sub = new_item->no_sub;
1517                         old_item->no_select = new_item->no_select;
1518                         if (old_item->no_sub == TRUE && node->children) {
1519                                 debug_print("folder '%s' doesn't have "
1520                                             "subfolders. removing...\n",
1521                                             old_item->path);
1522                                 folder_item_remove_children(old_item);
1523                         }
1524                 }
1525
1526                 node = next;
1527         }
1528
1529         for (cur = item_list; cur != NULL; cur = cur->next) {
1530                 FolderItem *cur_item = FOLDER_ITEM(cur->data);
1531                 new_item = NULL;
1532                 for (node = item->node->children; node != NULL;
1533                      node = node->next) {
1534                         if (!strcmp2(FOLDER_ITEM(node->data)->path,
1535                                      cur_item->path)) {
1536                                 new_item = FOLDER_ITEM(node->data);
1537                                 folder_item_destroy(cur_item);
1538                                 cur_item = NULL;
1539                                 break;
1540                         }
1541                 }
1542                 if (!new_item) {
1543                         new_item = cur_item;
1544                         debug_print("new folder '%s' found.\n", new_item->path);
1545                         folder_item_append(item, new_item);
1546                 }
1547
1548                 if (!strcmp(new_item->path, "INBOX")) {
1549                         new_item->stype = F_INBOX;
1550                         folder->inbox = new_item;
1551                 } else if (!folder_item_parent(item) || item->stype == F_INBOX) {
1552                         gchar *base;
1553
1554                         base = g_path_get_basename(new_item->path);
1555
1556                         if (!folder->outbox && !g_ascii_strcasecmp(base, "Sent")) {
1557                                 new_item->stype = F_OUTBOX;
1558                                 folder->outbox = new_item;
1559                         } else if (!folder->draft && !g_ascii_strcasecmp(base, "Drafts")) {
1560                                 new_item->stype = F_DRAFT;
1561                                 folder->draft = new_item;
1562                         } else if (!folder->queue && !g_ascii_strcasecmp(base, "Queue")) {
1563                                 new_item->stype = F_QUEUE;
1564                                 folder->queue = new_item;
1565                         } else if (!folder->trash && !g_ascii_strcasecmp(base, "Trash")) {
1566                                 new_item->stype = F_TRASH;
1567                                 folder->trash = new_item;
1568                         }
1569                         g_free(base);
1570                 }
1571
1572                 if (new_item->no_sub == FALSE)
1573                         imap_scan_tree_recursive(session, new_item);
1574         }
1575
1576         g_slist_free(item_list);
1577
1578         return IMAP_SUCCESS;
1579 }
1580
1581 static GSList *imap_parse_list(IMAPFolder *folder, IMAPSession *session,
1582                                const gchar *real_path, gchar *separator)
1583 {
1584         gchar buf[IMAPBUFSIZE];
1585         gchar flags[256];
1586         gchar separator_str[16];
1587         gchar *p;
1588         gchar *base;
1589         gchar *loc_name, *loc_path;
1590         GSList *item_list = NULL;
1591         GString *str;
1592         FolderItem *new_item;
1593
1594         debug_print("getting list of %s ...\n",
1595                     *real_path ? real_path : "\"\"");
1596
1597         str = g_string_new(NULL);
1598
1599         for (;;) {
1600                 if (sock_gets(SESSION(session)->sock, buf, sizeof(buf)) <= 0) {
1601                         log_warning(_("error occurred while getting LIST.\n"));
1602                         break;
1603                 }
1604                 strretchomp(buf);
1605                 if (buf[0] != '*' || buf[1] != ' ') {
1606                         log_print("IMAP4< %s\n", buf);
1607                         if (sscanf(buf, "%*d %16s", buf) < 1 ||
1608                             strcmp(buf, "OK") != 0)
1609                                 log_warning(_("error occurred while getting LIST.\n"));
1610                                 
1611                         break;
1612                 }
1613                 debug_print("IMAP4< %s\n", buf);
1614
1615                 g_string_assign(str, buf);
1616                 p = str->str + 2;
1617                 if (strncmp(p, "LIST ", 5) != 0) continue;
1618                 p += 5;
1619
1620                 if (*p != '(') continue;
1621                 p++;
1622                 p = strchr_cpy(p, ')', flags, sizeof(flags));
1623                 if (!p) continue;
1624                 while (*p == ' ') p++;
1625
1626                 p = strchr_cpy(p, ' ', separator_str, sizeof(separator_str));
1627                 if (!p) continue;
1628                 extract_quote(separator_str, '"');
1629                 if (!strcmp(separator_str, "NIL"))
1630                         separator_str[0] = '\0';
1631                 if (separator)
1632                         *separator = separator_str[0];
1633
1634                 buf[0] = '\0';
1635                 while (*p == ' ') p++;
1636                 if (*p == '{' || *p == '"')
1637                         p = imap_parse_atom(SESSION(session)->sock, p,
1638                                             buf, sizeof(buf), str);
1639                 else
1640                         strncpy2(buf, p, sizeof(buf));
1641                 strtailchomp(buf, separator_str[0]);
1642                 if (buf[0] == '\0') continue;
1643                 if (!strcmp(buf, real_path)) continue;
1644
1645                 if (separator_str[0] != '\0')
1646                         subst_char(buf, separator_str[0], '/');
1647                 base = g_path_get_basename(buf);
1648                 if (base[0] == '.') continue;
1649
1650                 loc_name = imap_modified_utf7_to_utf8(base);
1651                 loc_path = imap_modified_utf7_to_utf8(buf);
1652                 new_item = folder_item_new(FOLDER(folder), loc_name, loc_path);
1653                 if (strcasestr(flags, "\\Noinferiors") != NULL)
1654                         new_item->no_sub = TRUE;
1655                 if (strcmp(buf, "INBOX") != 0 &&
1656                     strcasestr(flags, "\\Noselect") != NULL)
1657                         new_item->no_select = TRUE;
1658
1659                 item_list = g_slist_append(item_list, new_item);
1660
1661                 debug_print("folder '%s' found.\n", loc_path);
1662                 g_free(base);
1663                 g_free(loc_path);
1664                 g_free(loc_name);
1665         }
1666
1667         g_string_free(str, TRUE);
1668
1669         return item_list;
1670 }
1671
1672 static gint imap_create_tree(Folder *folder)
1673 {
1674         g_return_val_if_fail(folder != NULL, -1);
1675         g_return_val_if_fail(folder->node != NULL, -1);
1676         g_return_val_if_fail(folder->node->data != NULL, -1);
1677         g_return_val_if_fail(folder->account != NULL, -1);
1678
1679         imap_scan_tree(folder);
1680         imap_create_missing_folders(folder);
1681
1682         return 0;
1683 }
1684
1685 static void imap_create_missing_folders(Folder *folder)
1686 {
1687         g_return_if_fail(folder != NULL);
1688
1689         if (!folder->inbox)
1690                 folder->inbox = imap_create_special_folder
1691                         (folder, F_INBOX, "INBOX");
1692 #if 0
1693         if (!folder->outbox)
1694                 folder->outbox = imap_create_special_folder
1695                         (folder, F_OUTBOX, "Sent");
1696         if (!folder->draft)
1697                 folder->draft = imap_create_special_folder
1698                         (folder, F_DRAFT, "Drafts");
1699         if (!folder->queue)
1700                 folder->queue = imap_create_special_folder
1701                         (folder, F_QUEUE, "Queue");
1702 #endif
1703         if (!folder->trash)
1704                 folder->trash = imap_create_special_folder
1705                         (folder, F_TRASH, "Trash");
1706 }
1707
1708 static FolderItem *imap_create_special_folder(Folder *folder,
1709                                               SpecialFolderItemType stype,
1710                                               const gchar *name)
1711 {
1712         FolderItem *item;
1713         FolderItem *new_item;
1714
1715         g_return_val_if_fail(folder != NULL, NULL);
1716         g_return_val_if_fail(folder->node != NULL, NULL);
1717         g_return_val_if_fail(folder->node->data != NULL, NULL);
1718         g_return_val_if_fail(folder->account != NULL, NULL);
1719         g_return_val_if_fail(name != NULL, NULL);
1720
1721         item = FOLDER_ITEM(folder->node->data);
1722         new_item = imap_create_folder(folder, item, name);
1723
1724         if (!new_item) {
1725                 g_warning("Can't create '%s'\n", name);
1726                 if (!folder->inbox) return NULL;
1727
1728                 new_item = imap_create_folder(folder, folder->inbox, name);
1729                 if (!new_item)
1730                         g_warning("Can't create '%s' under INBOX\n", name);
1731                 else
1732                         new_item->stype = stype;
1733         } else
1734                 new_item->stype = stype;
1735
1736         return new_item;
1737 }
1738
1739 static gchar *imap_folder_get_path(Folder *folder)
1740 {
1741         gchar *folder_path;
1742
1743         g_return_val_if_fail(folder != NULL, NULL);
1744         g_return_val_if_fail(folder->account != NULL, NULL);
1745
1746         folder_path = g_strconcat(get_imap_cache_dir(),
1747                                   G_DIR_SEPARATOR_S,
1748                                   folder->account->recv_server,
1749                                   G_DIR_SEPARATOR_S,
1750                                   folder->account->userid,
1751                                   NULL);
1752
1753         return folder_path;
1754 }
1755
1756 static gchar *imap_item_get_path(Folder *folder, FolderItem *item)
1757 {
1758         gchar *folder_path, *path;
1759
1760         g_return_val_if_fail(folder != NULL, NULL);
1761         g_return_val_if_fail(item != NULL, NULL);
1762         folder_path = imap_folder_get_path(folder);
1763
1764         g_return_val_if_fail(folder_path != NULL, NULL);
1765         if (folder_path[0] == G_DIR_SEPARATOR) {
1766                 if (item->path)
1767                         path = g_strconcat(folder_path, G_DIR_SEPARATOR_S,
1768                                            item->path, NULL);
1769                 else
1770                         path = g_strdup(folder_path);
1771         } else {
1772                 if (item->path)
1773                         path = g_strconcat(get_home_dir(), G_DIR_SEPARATOR_S,
1774                                            folder_path, G_DIR_SEPARATOR_S,
1775                                            item->path, NULL);
1776                 else
1777                         path = g_strconcat(get_home_dir(), G_DIR_SEPARATOR_S,
1778                                            folder_path, NULL);
1779         }
1780         g_free(folder_path);
1781
1782         return path;
1783 }
1784
1785 static FolderItem *imap_create_folder(Folder *folder, FolderItem *parent,
1786                                const gchar *name)
1787 {
1788         gchar *dirpath, *imap_path;
1789         IMAPSession *session;
1790         FolderItem *new_item;
1791         gchar separator;
1792         gchar *new_name;
1793         const gchar *p;
1794         gint ok;
1795
1796         g_return_val_if_fail(folder != NULL, NULL);
1797         g_return_val_if_fail(folder->account != NULL, NULL);
1798         g_return_val_if_fail(parent != NULL, NULL);
1799         g_return_val_if_fail(name != NULL, NULL);
1800
1801         session = imap_session_get(folder);
1802         if (!session) return NULL;
1803
1804         if (!folder_item_parent(parent) && strcmp(name, "INBOX") == 0)
1805                 dirpath = g_strdup(name);
1806         else if (parent->path)
1807                 dirpath = g_strconcat(parent->path, "/", name, NULL);
1808         else if ((p = strchr(name, '/')) != NULL && *(p + 1) != '\0')
1809                 dirpath = g_strdup(name);
1810         else if (folder->account->imap_dir && *folder->account->imap_dir) {
1811                 gchar *imap_dir;
1812
1813                 Xstrdup_a(imap_dir, folder->account->imap_dir, return NULL);
1814                 strtailchomp(imap_dir, '/');
1815                 dirpath = g_strconcat(imap_dir, "/", name, NULL);
1816         } else
1817                 dirpath = g_strdup(name);
1818
1819         /* keep trailing directory separator to create a folder that contains
1820            sub folder */
1821         imap_path = imap_utf8_to_modified_utf7(dirpath);
1822         strtailchomp(dirpath, '/');
1823         Xstrdup_a(new_name, name, {g_free(dirpath); return NULL;});
1824         strtailchomp(new_name, '/');
1825         separator = imap_get_path_separator(IMAP_FOLDER(folder), imap_path);
1826         imap_path_separator_subst(imap_path, separator);
1827         subst_char(new_name, '/', separator);
1828
1829         if (strcmp(name, "INBOX") != 0) {
1830                 GPtrArray *argbuf;
1831                 gint i;
1832                 gboolean exist = FALSE;
1833
1834                 argbuf = g_ptr_array_new();
1835                 ok = imap_cmd_list(session, NULL, imap_path,
1836                                    argbuf);
1837                 if (ok != IMAP_SUCCESS) {
1838                         log_warning(_("can't create mailbox: LIST failed\n"));
1839                         g_free(imap_path);
1840                         g_free(dirpath);
1841                         ptr_array_free_strings(argbuf);
1842                         g_ptr_array_free(argbuf, TRUE);
1843                         return NULL;
1844                 }
1845
1846                 for (i = 0; i < argbuf->len; i++) {
1847                         gchar *str;
1848                         str = g_ptr_array_index(argbuf, i);
1849                         if (!strncmp(str, "LIST ", 5)) {
1850                                 exist = TRUE;
1851                                 break;
1852                         }
1853                 }
1854                 ptr_array_free_strings(argbuf);
1855                 g_ptr_array_free(argbuf, TRUE);
1856
1857                 if (!exist) {
1858                         ok = imap_cmd_create(session, imap_path);
1859                         if (ok != IMAP_SUCCESS) {
1860                                 log_warning(_("can't create mailbox\n"));
1861                                 g_free(imap_path);
1862                                 g_free(dirpath);
1863                                 return NULL;
1864                         }
1865                 }
1866         }
1867
1868         new_item = folder_item_new(folder, new_name, dirpath);
1869         folder_item_append(parent, new_item);
1870         g_free(imap_path);
1871         g_free(dirpath);
1872
1873         dirpath = folder_item_get_path(new_item);
1874         if (!is_dir_exist(dirpath))
1875                 make_dir_hier(dirpath);
1876         g_free(dirpath);
1877
1878         return new_item;
1879 }
1880
1881 static gint imap_rename_folder(Folder *folder, FolderItem *item,
1882                                const gchar *name)
1883 {
1884         gchar *dirpath;
1885         gchar *newpath;
1886         gchar *real_oldpath;
1887         gchar *real_newpath;
1888         gchar *paths[2];
1889         gchar *old_cache_dir;
1890         gchar *new_cache_dir;
1891         IMAPSession *session;
1892         gchar separator;
1893         gint ok;
1894         gint exists, recent, unseen;
1895         guint32 uid_validity;
1896
1897         g_return_val_if_fail(folder != NULL, -1);
1898         g_return_val_if_fail(item != NULL, -1);
1899         g_return_val_if_fail(item->path != NULL, -1);
1900         g_return_val_if_fail(name != NULL, -1);
1901
1902         if (strchr(name, imap_get_path_separator(IMAP_FOLDER(folder), item->path)) != NULL) {
1903                 g_warning(_("New folder name must not contain the namespace "
1904                             "path separator"));
1905                 return -1;
1906         }
1907
1908         session = imap_session_get(folder);
1909         if (!session) return -1;
1910
1911         real_oldpath = imap_get_real_path(IMAP_FOLDER(folder), item->path);
1912
1913         g_free(session->mbox);
1914         session->mbox = NULL;
1915         ok = imap_cmd_examine(session, "INBOX",
1916                               &exists, &recent, &unseen, &uid_validity, FALSE);
1917         if (ok != IMAP_SUCCESS) {
1918                 g_free(real_oldpath);
1919                 return -1;
1920         }
1921
1922         separator = imap_get_path_separator(IMAP_FOLDER(folder), item->path);
1923         if (strchr(item->path, G_DIR_SEPARATOR)) {
1924                 dirpath = g_path_get_dirname(item->path);
1925                 newpath = g_strconcat(dirpath, G_DIR_SEPARATOR_S, name, NULL);
1926                 g_free(dirpath);
1927         } else
1928                 newpath = g_strdup(name);
1929
1930         real_newpath = imap_utf8_to_modified_utf7(newpath);
1931         imap_path_separator_subst(real_newpath, separator);
1932
1933         ok = imap_cmd_rename(session, real_oldpath, real_newpath);
1934         if (ok != IMAP_SUCCESS) {
1935                 log_warning(_("can't rename mailbox: %s to %s\n"),
1936                             real_oldpath, real_newpath);
1937                 g_free(real_oldpath);
1938                 g_free(newpath);
1939                 g_free(real_newpath);
1940                 return -1;
1941         }
1942
1943         g_free(item->name);
1944         item->name = g_strdup(name);
1945
1946         old_cache_dir = folder_item_get_path(item);
1947
1948         paths[0] = g_strdup(item->path);
1949         paths[1] = newpath;
1950         g_node_traverse(item->node, G_PRE_ORDER, G_TRAVERSE_ALL, -1,
1951                         imap_rename_folder_func, paths);
1952
1953         if (is_dir_exist(old_cache_dir)) {
1954                 new_cache_dir = folder_item_get_path(item);
1955                 if (rename(old_cache_dir, new_cache_dir) < 0) {
1956                         FILE_OP_ERROR(old_cache_dir, "rename");
1957                 }
1958                 g_free(new_cache_dir);
1959         }
1960
1961         g_free(old_cache_dir);
1962         g_free(paths[0]);
1963         g_free(newpath);
1964         g_free(real_oldpath);
1965         g_free(real_newpath);
1966
1967         return 0;
1968 }
1969
1970 static gint imap_remove_folder(Folder *folder, FolderItem *item)
1971 {
1972         gint ok;
1973         IMAPSession *session;
1974         gchar *path;
1975         gchar *cache_dir;
1976         gint exists, recent, unseen;
1977         guint32 uid_validity;
1978
1979         g_return_val_if_fail(folder != NULL, -1);
1980         g_return_val_if_fail(item != NULL, -1);
1981         g_return_val_if_fail(item->path != NULL, -1);
1982
1983         session = imap_session_get(folder);
1984         if (!session) return -1;
1985
1986         path = imap_get_real_path(IMAP_FOLDER(folder), item->path);
1987
1988         ok = imap_cmd_examine(session, "INBOX",
1989                               &exists, &recent, &unseen, &uid_validity, FALSE);
1990         if (ok != IMAP_SUCCESS) {
1991                 g_free(path);
1992                 return -1;
1993         }
1994
1995         ok = imap_cmd_delete(session, path);
1996         if (ok != IMAP_SUCCESS) {
1997                 log_warning(_("can't delete mailbox\n"));
1998                 g_free(path);
1999                 return -1;
2000         }
2001
2002         g_free(path);
2003         cache_dir = folder_item_get_path(item);
2004         if (is_dir_exist(cache_dir) && remove_dir_recursive(cache_dir) < 0)
2005                 g_warning("can't remove directory '%s'\n", cache_dir);
2006         g_free(cache_dir);
2007         folder_item_remove(item);
2008
2009         return 0;
2010 }
2011
2012 typedef struct _uncached_data {
2013         IMAPSession *session;
2014         FolderItem *item;
2015         MsgNumberList *numlist;
2016         gboolean done;
2017 } uncached_data;
2018
2019 static void *imap_get_uncached_messages_thread(void *data)
2020 {
2021         uncached_data *stuff = (uncached_data *)data;
2022         IMAPSession *session = stuff->session;
2023         FolderItem *item = stuff->item;
2024         MsgNumberList *numlist = stuff->numlist;
2025
2026         gchar *tmp;
2027         GSList *newlist = NULL;
2028         GSList *llast = NULL;
2029         GString *str = NULL;
2030         MsgInfo *msginfo;
2031         GSList *seq_list, *cur;
2032         IMAPSet imapset;
2033
2034         if (session == NULL || item == NULL || item->folder == NULL
2035             || FOLDER_CLASS(item->folder) != &imap_class) {
2036                 stuff->done = TRUE;
2037                 return NULL;
2038         }
2039
2040         seq_list = imap_get_seq_set_from_numlist(numlist);
2041         for (cur = seq_list; cur != NULL; cur = g_slist_next(cur)) {
2042                 imapset = cur->data;
2043
2044                 if (imap_cmd_envelope(session, imapset)
2045                     != IMAP_SUCCESS) {
2046                         log_warning(_("can't get envelope\n"));
2047                         continue;
2048                 }
2049
2050                 str = g_string_new(NULL);
2051
2052                 for (;;) {
2053                         if ((tmp =sock_getline(SESSION(session)->sock)) == NULL) {
2054                                 log_warning(_("error occurred while getting envelope.\n"));
2055                                 g_string_free(str, TRUE);
2056                                 str = NULL;
2057                                 break;
2058                         }
2059                         strretchomp(tmp);
2060                         if (tmp[0] != '*' || tmp[1] != ' ') {
2061                                 log_print("IMAP4< %s\n", tmp);
2062                                 g_free(tmp);
2063                                 break;
2064                         }
2065                         if (strstr(tmp, "FETCH") == NULL) {
2066                                 log_print("IMAP4< %s\n", tmp);
2067                                 g_free(tmp);
2068                                 continue;
2069                         }
2070                         log_print("IMAP4< %s\n", tmp);
2071                         g_string_assign(str, tmp);
2072                         g_free(tmp);
2073
2074                         msginfo = imap_parse_envelope
2075                                 (SESSION(session)->sock, item, str);
2076                         if (!msginfo) {
2077                                 log_warning(_("can't parse envelope: %s\n"), str->str);
2078                                 continue;
2079                         }
2080                         if (item->stype == F_QUEUE) {
2081                                 MSG_SET_TMP_FLAGS(msginfo->flags, MSG_QUEUED);
2082                         } else if (item->stype == F_DRAFT) {
2083                                 MSG_SET_TMP_FLAGS(msginfo->flags, MSG_DRAFT);
2084                         }
2085
2086                         msginfo->folder = item;
2087
2088                         if (!newlist)
2089                                 llast = newlist = g_slist_append(newlist, msginfo);
2090                         else {
2091                                 llast = g_slist_append(llast, msginfo);
2092                                 llast = llast->next;
2093                         }
2094                 }
2095                 if (str)
2096                         g_string_free(str, TRUE);
2097         }
2098         imap_seq_set_free(seq_list);
2099         
2100         session_set_access_time(SESSION(session));
2101         stuff->done = TRUE;
2102
2103         return newlist;
2104 }
2105
2106 static GSList *imap_get_uncached_messages(IMAPSession *session,
2107                                         FolderItem *item,
2108                                         MsgNumberList *numlist)
2109 {
2110         uncached_data *data = g_new0(uncached_data, 1);
2111         GSList *result = NULL;
2112 #ifdef USE_PTHREAD
2113         pthread_t pt;
2114 #endif
2115         data->done = FALSE;
2116         data->session = session;
2117         data->item = item;
2118         data->numlist = numlist;
2119
2120         if (prefs_common.work_offline && !imap_gtk_should_override()) {
2121                 g_free(data);
2122                 return NULL;
2123         }
2124
2125 #if (defined USE_PTHREAD && defined __GLIBC__ && (__GLIBC__ > 2 || (__GLIBC__ == 2 && __GLIBC_MINOR__ >= 3)))
2126         if (pthread_create(&pt, PTHREAD_CREATE_JOINABLE,
2127                         imap_get_uncached_messages_thread, data) != 0) {
2128                 result = (GSList *)imap_get_uncached_messages_thread(data);
2129                 g_free(data);
2130                 return result;
2131         }
2132         debug_print("+++waiting for imap_get_uncached_messages_thread...\n");
2133         while(!data->done) {
2134                 /* don't let the interface freeze while waiting */
2135                 sylpheed_do_idle();
2136         }
2137         debug_print("---imap_get_uncached_messages_thread done\n");
2138
2139         /* get the thread's return value and clean its resources */
2140         pthread_join(pt, (void *)&result);
2141 #else
2142         result = (GSList *)imap_get_uncached_messages_thread(data);
2143 #endif
2144         g_free(data);
2145         return result;
2146 }
2147
2148 static void imap_delete_all_cached_messages(FolderItem *item)
2149 {
2150         gchar *dir;
2151
2152         g_return_if_fail(item != NULL);
2153         g_return_if_fail(item->folder != NULL);
2154         g_return_if_fail(FOLDER_CLASS(item->folder) == &imap_class);
2155
2156         debug_print("Deleting all cached messages...\n");
2157
2158         dir = folder_item_get_path(item);
2159         if (is_dir_exist(dir))
2160                 remove_all_numbered_files(dir);
2161         g_free(dir);
2162
2163         debug_print("done.\n");
2164 }
2165
2166 #if USE_OPENSSL
2167 static SockInfo *imap_open_tunnel(const gchar *server,
2168                            const gchar *tunnelcmd,
2169                            SSLType ssl_type)
2170 #else
2171 static SockInfo *imap_open_tunnel(const gchar *server,
2172                            const gchar *tunnelcmd)
2173 #endif
2174 {
2175         SockInfo *sock;
2176
2177         if ((sock = sock_connect_cmd(server, tunnelcmd)) == NULL) {
2178                 log_warning(_("Can't establish IMAP4 session with: %s\n"),
2179                             server);
2180                 return NULL;
2181         }
2182 #if USE_OPENSSL
2183         return imap_init_sock(sock, ssl_type);
2184 #else
2185         return imap_init_sock(sock);
2186 #endif
2187 }
2188
2189 void *imap_open_thread(void *data)
2190 {
2191         SockInfo *sock = NULL;
2192         thread_data *td = (thread_data *)data;
2193         if ((sock = sock_connect(td->server, td->port)) == NULL) {
2194                 log_warning(_("Can't connect to IMAP4 server: %s:%d\n"),
2195                             td->server, td->port);
2196                 td->done = TRUE;
2197                 return NULL;
2198         }
2199
2200         td->done = TRUE;
2201         return sock;
2202 }
2203
2204 #if USE_OPENSSL
2205 static SockInfo *imap_open(const gchar *server, gushort port,
2206                            SSLType ssl_type)
2207 #else
2208 static SockInfo *imap_open(const gchar *server, gushort port)
2209 #endif
2210 {
2211         thread_data *td = g_new0(thread_data, 1);
2212 #if USE_PTHREAD
2213         pthread_t pt;
2214 #endif
2215         SockInfo *sock = NULL;
2216
2217 #if USE_OPENSSL
2218         td->ssl_type = ssl_type;
2219 #endif
2220         td->server = g_strdup(server);
2221         td->port = port;
2222         td->done = FALSE;
2223
2224         statusbar_print_all(_("Connecting to IMAP4 server: %s..."), server);
2225
2226 #if (defined USE_PTHREAD && defined __GLIBC__ && (__GLIBC__ > 2 || (__GLIBC__ == 2 && __GLIBC_MINOR__ >= 3)))
2227         if (pthread_create(&pt, PTHREAD_CREATE_JOINABLE,
2228                         imap_open_thread, td) != 0) {
2229                 statusbar_pop_all();
2230                 sock = imap_open_thread(td);
2231         } else {        
2232                 debug_print("+++waiting for imap_open_thread...\n");
2233                 while(!td->done) {
2234                         /* don't let the interface freeze while waiting */
2235                         sylpheed_do_idle();
2236                 }
2237
2238                 /* get the thread's return value and clean its resources */
2239                 pthread_join(pt, (void *)&sock);
2240         }
2241 #else
2242         sock = imap_open_thread(td);
2243 #endif
2244 #if USE_OPENSSL
2245         if (sock && td->ssl_type == SSL_TUNNEL && !ssl_init_socket(sock)) {
2246                 log_warning(_("Can't establish IMAP4 session with: %s:%d\n"),
2247                             td->server, td->port);
2248                 sock_close(sock);
2249                 sock = NULL;
2250                 td->done = TRUE;
2251         }
2252 #endif
2253         g_free(td->server);
2254         g_free(td);
2255
2256         debug_print("---imap_open_thread returned %p\n", sock);
2257         statusbar_pop_all();
2258
2259         if(!sock && !prefs_common.no_recv_err_panel) {
2260                 alertpanel_error(_("Can't connect to IMAP4 server: %s:%d"),
2261                                  server, port);
2262         }
2263
2264         return sock;
2265 }
2266
2267 #if USE_OPENSSL
2268 static SockInfo *imap_init_sock(SockInfo *sock, SSLType ssl_type)
2269 #else
2270 static SockInfo *imap_init_sock(SockInfo *sock)
2271 #endif
2272 {
2273
2274         return sock;
2275 }
2276
2277 static GList *imap_parse_namespace_str(gchar *str)
2278 {
2279         guchar *p = str;
2280         gchar *name;
2281         gchar *separator;
2282         IMAPNameSpace *namespace;
2283         GList *ns_list = NULL;
2284
2285         while (*p != '\0') {
2286                 /* parse ("#foo" "/") */
2287
2288                 while (*p && *p != '(') p++;
2289                 if (*p == '\0') break;
2290                 p++;
2291
2292                 while (*p && *p != '"') p++;
2293                 if (*p == '\0') break;
2294                 p++;
2295                 name = p;
2296
2297                 while (*p && *p != '"') p++;
2298                 if (*p == '\0') break;
2299                 *p = '\0';
2300                 p++;
2301
2302                 while (*p && isspace(*p)) p++;
2303                 if (*p == '\0') break;
2304                 if (strncmp(p, "NIL", 3) == 0)
2305                         separator = NULL;
2306                 else if (*p == '"') {
2307                         p++;
2308                         separator = p;
2309                         while (*p && *p != '"') p++;
2310                         if (*p == '\0') break;
2311                         *p = '\0';
2312                         p++;
2313                 } else break;
2314
2315                 while (*p && *p != ')') p++;
2316                 if (*p == '\0') break;
2317                 p++;
2318
2319                 namespace = g_new(IMAPNameSpace, 1);
2320                 namespace->name = g_strdup(name);
2321                 namespace->separator = separator ? separator[0] : '\0';
2322                 ns_list = g_list_append(ns_list, namespace);
2323         }
2324
2325         return ns_list;
2326 }
2327
2328 static void imap_parse_namespace(IMAPSession *session, IMAPFolder *folder)
2329 {
2330         gchar *ns_str;
2331         gchar **str_array;
2332
2333         g_return_if_fail(session != NULL);
2334         g_return_if_fail(folder != NULL);
2335
2336         if (folder->ns_personal != NULL ||
2337             folder->ns_others   != NULL ||
2338             folder->ns_shared   != NULL)
2339                 return;
2340
2341         if (!imap_has_capability(session, "NAMESPACE")) {
2342                 imap_get_namespace_by_list(session, folder);
2343                 return;
2344         }
2345         
2346         if (imap_cmd_namespace(session, &ns_str)
2347             != IMAP_SUCCESS) {
2348                 log_warning(_("can't get namespace\n"));
2349                 return;
2350         }
2351
2352         str_array = strsplit_parenthesis(ns_str, '(', ')', 3);
2353         if (str_array == NULL) {
2354                 g_free(ns_str);
2355                 imap_get_namespace_by_list(session, folder);
2356                 return;
2357         }
2358         if (str_array[0])
2359                 folder->ns_personal = imap_parse_namespace_str(str_array[0]);
2360         if (str_array[0] && str_array[1])
2361                 folder->ns_others = imap_parse_namespace_str(str_array[1]);
2362         if (str_array[0] && str_array[1] && str_array[2])
2363                 folder->ns_shared = imap_parse_namespace_str(str_array[2]);
2364         g_strfreev(str_array);
2365         g_free(ns_str);
2366 }
2367
2368 static void imap_get_namespace_by_list(IMAPSession *session, IMAPFolder *folder)
2369 {
2370         GSList *item_list, *cur;
2371         gchar separator = '\0';
2372         IMAPNameSpace *namespace;
2373
2374         g_return_if_fail(session != NULL);
2375         g_return_if_fail(folder != NULL);
2376
2377         if (folder->ns_personal != NULL ||
2378             folder->ns_others   != NULL ||
2379             folder->ns_shared   != NULL)
2380                 return;
2381
2382         imap_gen_send(session, "LIST \"\" \"\"");
2383         item_list = imap_parse_list(folder, session, "", &separator);
2384         for (cur = item_list; cur != NULL; cur = cur->next)
2385                 folder_item_destroy(FOLDER_ITEM(cur->data));
2386         g_slist_free(item_list);
2387
2388         namespace = g_new(IMAPNameSpace, 1);
2389         namespace->name = g_strdup("");
2390         namespace->separator = separator;
2391         folder->ns_personal = g_list_append(NULL, namespace);
2392 }
2393
2394 static IMAPNameSpace *imap_find_namespace_from_list(GList *ns_list,
2395                                                     const gchar *path)
2396 {
2397         IMAPNameSpace *namespace = NULL;
2398         gchar *tmp_path, *name;
2399
2400         if (!path) path = "";
2401
2402         for (; ns_list != NULL; ns_list = ns_list->next) {
2403                 IMAPNameSpace *tmp_ns = ns_list->data;
2404
2405                 Xstrcat_a(tmp_path, path, "/", return namespace);
2406                 Xstrdup_a(name, tmp_ns->name, return namespace);
2407                 if (tmp_ns->separator && tmp_ns->separator != '/') {
2408                         subst_char(tmp_path, tmp_ns->separator, '/');
2409                         subst_char(name, tmp_ns->separator, '/');
2410                 }
2411                 if (strncmp(tmp_path, name, strlen(name)) == 0)
2412                         namespace = tmp_ns;
2413         }
2414
2415         return namespace;
2416 }
2417
2418 static IMAPNameSpace *imap_find_namespace(IMAPFolder *folder,
2419                                           const gchar *path)
2420 {
2421         IMAPNameSpace *namespace;
2422
2423         g_return_val_if_fail(folder != NULL, NULL);
2424
2425         namespace = imap_find_namespace_from_list(folder->ns_personal, path);
2426         if (namespace) return namespace;
2427         namespace = imap_find_namespace_from_list(folder->ns_others, path);
2428         if (namespace) return namespace;
2429         namespace = imap_find_namespace_from_list(folder->ns_shared, path);
2430         if (namespace) return namespace;
2431
2432         return NULL;
2433 }
2434
2435 static gchar imap_get_path_separator(IMAPFolder *folder, const gchar *path)
2436 {
2437         IMAPNameSpace *namespace;
2438         gchar separator = '/';
2439
2440         namespace = imap_find_namespace(folder, path);
2441         if (namespace && namespace->separator)
2442                 separator = namespace->separator;
2443
2444         return separator;
2445 }
2446
2447 static gchar *imap_get_real_path(IMAPFolder *folder, const gchar *path)
2448 {
2449         gchar *real_path;
2450         gchar separator;
2451
2452         g_return_val_if_fail(folder != NULL, NULL);
2453         g_return_val_if_fail(path != NULL, NULL);
2454
2455         real_path = imap_utf8_to_modified_utf7(path);
2456         separator = imap_get_path_separator(folder, path);
2457         imap_path_separator_subst(real_path, separator);
2458
2459         return real_path;
2460 }
2461
2462 static gchar *imap_parse_atom(SockInfo *sock, gchar *src,
2463                               gchar *dest, gint dest_len, GString *str)
2464 {
2465         gchar *cur_pos = src;
2466         gchar *nextline;
2467
2468         g_return_val_if_fail(str != NULL, cur_pos);
2469
2470         /* read the next line if the current response buffer is empty */
2471         while (isspace(*(guchar *)cur_pos)) cur_pos++;
2472         while (*cur_pos == '\0') {
2473                 if ((nextline = imap_getline(sock)) == NULL)
2474                         return cur_pos;
2475                 g_string_assign(str, nextline);
2476                 cur_pos = str->str;
2477                 strretchomp(nextline);
2478                 /* log_print("IMAP4< %s\n", nextline); */
2479                 debug_print("IMAP4< %s\n", nextline);
2480                 g_free(nextline);
2481
2482                 while (isspace(*(guchar *)cur_pos)) cur_pos++;
2483         }
2484
2485         if (!strncmp(cur_pos, "NIL", 3)) {
2486                 *dest = '\0';
2487                 cur_pos += 3;
2488         } else if (*cur_pos == '\"') {
2489                 gchar *p;
2490
2491                 p = get_quoted(cur_pos, '\"', dest, dest_len);
2492                 cur_pos = p ? p : cur_pos + 2;
2493         } else if (*cur_pos == '{') {
2494                 gchar buf[32];
2495                 gint len;
2496                 gint line_len = 0;
2497
2498                 cur_pos = strchr_cpy(cur_pos + 1, '}', buf, sizeof(buf));
2499                 len = atoi(buf);
2500                 g_return_val_if_fail(len >= 0, cur_pos);
2501
2502                 g_string_truncate(str, 0);
2503                 cur_pos = str->str;
2504
2505                 do {
2506                         if ((nextline = imap_getline(sock)) == NULL)
2507                                 return cur_pos;
2508                         line_len += strlen(nextline);
2509                         g_string_append(str, nextline);
2510                         cur_pos = str->str;
2511                         strretchomp(nextline);
2512                         /* log_print("IMAP4< %s\n", nextline); */
2513                         debug_print("IMAP4< %s\n", nextline);
2514                         g_free(nextline);
2515                 } while (line_len < len);
2516
2517                 memcpy(dest, cur_pos, MIN(len, dest_len - 1));
2518                 dest[MIN(len, dest_len - 1)] = '\0';
2519                 cur_pos += len;
2520         }
2521
2522         return cur_pos;
2523 }
2524
2525 static gchar *imap_get_header(SockInfo *sock, gchar *cur_pos, gchar **headers,
2526                               GString *str)
2527 {
2528         gchar *nextline;
2529         gchar buf[32];
2530         gint len;
2531         gint block_len = 0;
2532
2533         *headers = NULL;
2534
2535         g_return_val_if_fail(str != NULL, cur_pos);
2536
2537         while (isspace(*(guchar *)cur_pos)) cur_pos++;
2538
2539         g_return_val_if_fail(*cur_pos == '{', cur_pos);
2540
2541         cur_pos = strchr_cpy(cur_pos + 1, '}', buf, sizeof(buf));
2542         len = atoi(buf);
2543         g_return_val_if_fail(len >= 0, cur_pos);
2544
2545         g_string_truncate(str, 0);
2546         cur_pos = str->str;
2547
2548         do {
2549                 if ((nextline = sock_getline(sock)) == NULL) {
2550                         *headers = NULL;
2551                         return cur_pos;
2552                 }
2553                 block_len += strlen(nextline);
2554                 g_string_append(str, nextline);
2555                 cur_pos = str->str;
2556                 strretchomp(nextline);
2557                 /* debug_print("IMAP4< %s\n", nextline); */
2558                 g_free(nextline);
2559         } while (block_len < len);
2560
2561         debug_print("IMAP4< [contents of BODY.PEEK[HEADER_FIELDS (...)]\n");
2562
2563         *headers = g_strndup(cur_pos, len);
2564         cur_pos += len;
2565
2566         while (isspace(*(guchar *)cur_pos)) cur_pos++;
2567         while (*cur_pos == '\0') {
2568                 if ((nextline = sock_getline(sock)) == NULL)
2569                         return cur_pos;
2570                 g_string_assign(str, nextline);
2571                 cur_pos = str->str;
2572                 strretchomp(nextline);
2573                 debug_print("IMAP4< %s\n", nextline);
2574                 g_free(nextline);
2575
2576                 while (isspace(*(guchar *)cur_pos)) cur_pos++;
2577         }
2578
2579         return cur_pos;
2580 }
2581
2582 static MsgFlags imap_parse_flags(const gchar *flag_str)  
2583 {
2584         const gchar *p = flag_str;
2585         MsgFlags flags = {0, 0};
2586
2587         flags.perm_flags = MSG_UNREAD;
2588
2589         while ((p = strchr(p, '\\')) != NULL) {
2590                 p++;
2591
2592                 if (g_ascii_strncasecmp(p, "Recent", 6) == 0 && MSG_IS_UNREAD(flags)) {
2593                         MSG_SET_PERM_FLAGS(flags, MSG_NEW);
2594                 } else if (g_ascii_strncasecmp(p, "Seen", 4) == 0) {
2595                         MSG_UNSET_PERM_FLAGS(flags, MSG_NEW|MSG_UNREAD);
2596                 } else if (g_ascii_strncasecmp(p, "Deleted", 7) == 0) {
2597                         MSG_SET_PERM_FLAGS(flags, MSG_DELETED);
2598                 } else if (g_ascii_strncasecmp(p, "Flagged", 7) == 0) {
2599                         MSG_SET_PERM_FLAGS(flags, MSG_MARKED);
2600                 } else if (g_ascii_strncasecmp(p, "Answered", 8) == 0) {
2601                         MSG_SET_PERM_FLAGS(flags, MSG_REPLIED);
2602                 }
2603         }
2604
2605         return flags;
2606 }
2607
2608 static MsgInfo *imap_parse_envelope(SockInfo *sock, FolderItem *item,
2609                                     GString *line_str)
2610 {
2611         gchar buf[IMAPBUFSIZE];
2612         MsgInfo *msginfo = NULL;
2613         gchar *cur_pos;
2614         gint msgnum;
2615         guint32 uid = 0;
2616         size_t size = 0;
2617         MsgFlags flags = {0, 0}, imap_flags = {0, 0};
2618
2619         g_return_val_if_fail(line_str != NULL, NULL);
2620         g_return_val_if_fail(line_str->str[0] == '*' &&
2621                              line_str->str[1] == ' ', NULL);
2622
2623         MSG_SET_TMP_FLAGS(flags, MSG_IMAP);
2624         if (item->stype == F_QUEUE) {
2625                 MSG_SET_TMP_FLAGS(flags, MSG_QUEUED);
2626         } else if (item->stype == F_DRAFT) {
2627                 MSG_SET_TMP_FLAGS(flags, MSG_DRAFT);
2628         }
2629
2630         cur_pos = line_str->str + 2;
2631
2632 #define PARSE_ONE_ELEMENT(ch)                                   \
2633 {                                                               \
2634         cur_pos = strchr_cpy(cur_pos, ch, buf, sizeof(buf));    \
2635         if (cur_pos == NULL) {                                  \
2636                 g_warning("cur_pos == NULL\n");                 \
2637                 procmsg_msginfo_free(msginfo);                  \
2638                 return NULL;                                    \
2639         }                                                       \
2640 }
2641
2642         PARSE_ONE_ELEMENT(' ');
2643         msgnum = atoi(buf);
2644
2645         PARSE_ONE_ELEMENT(' ');
2646         g_return_val_if_fail(!strcmp(buf, "FETCH"), NULL);
2647
2648         g_return_val_if_fail(*cur_pos == '(', NULL);
2649         cur_pos++;
2650
2651         while (*cur_pos != '\0' && *cur_pos != ')') {
2652                 while (*cur_pos == ' ') cur_pos++;
2653
2654                 if (!strncmp(cur_pos, "UID ", 4)) {
2655                         cur_pos += 4;
2656                         uid = strtoul(cur_pos, &cur_pos, 10);
2657                 } else if (!strncmp(cur_pos, "FLAGS ", 6)) {
2658                         cur_pos += 6;
2659                         if (*cur_pos != '(') {
2660                                 g_warning("*cur_pos != '('\n");
2661                                 procmsg_msginfo_free(msginfo);
2662                                 return NULL;
2663                         }
2664                         cur_pos++;
2665                         PARSE_ONE_ELEMENT(')');
2666                         imap_flags = imap_parse_flags(buf);
2667                 } else if (!strncmp(cur_pos, "RFC822.SIZE ", 12)) {
2668                         cur_pos += 12;
2669                         size = strtol(cur_pos, &cur_pos, 10);
2670                 } else if (!strncmp(cur_pos, "BODY[HEADER.FIELDS ", 19)) {
2671                         gchar *headers;
2672
2673                         cur_pos += 19;
2674                         if (*cur_pos != '(') {
2675                                 g_warning("*cur_pos != '('\n");
2676                                 procmsg_msginfo_free(msginfo);
2677                                 return NULL;
2678                         }
2679                         cur_pos++;
2680                         PARSE_ONE_ELEMENT(')');
2681                         if (*cur_pos != ']') {
2682                                 g_warning("*cur_pos != ']'\n");
2683                                 procmsg_msginfo_free(msginfo);
2684                                 return NULL;
2685                         }
2686                         cur_pos++;
2687                         cur_pos = imap_get_header(sock, cur_pos, &headers,
2688                                                   line_str);
2689                         msginfo = procheader_parse_str(headers, flags, FALSE, FALSE);
2690                         g_free(headers);
2691                 } else {
2692                         g_warning("invalid FETCH response: %s\n", cur_pos);
2693                         break;
2694                 }
2695         }
2696
2697         if (msginfo) {
2698                 msginfo->msgnum = uid;
2699                 msginfo->size = size;
2700                 msginfo->flags.tmp_flags |= imap_flags.tmp_flags;
2701                 msginfo->flags.perm_flags = imap_flags.perm_flags;
2702         }
2703
2704         return msginfo;
2705 }
2706
2707 static gchar *imap_get_flag_str(IMAPFlags flags)
2708 {
2709         GString *str;
2710         gchar *ret;
2711
2712         str = g_string_new(NULL);
2713
2714         if (IMAP_IS_SEEN(flags))        g_string_append(str, "\\Seen ");
2715         if (IMAP_IS_ANSWERED(flags))    g_string_append(str, "\\Answered ");
2716         if (IMAP_IS_FLAGGED(flags))     g_string_append(str, "\\Flagged ");
2717         if (IMAP_IS_DELETED(flags))     g_string_append(str, "\\Deleted ");
2718         if (IMAP_IS_DRAFT(flags))       g_string_append(str, "\\Draft");
2719
2720         if (str->len > 0 && str->str[str->len - 1] == ' ')
2721                 g_string_truncate(str, str->len - 1);
2722
2723         ret = str->str;
2724         g_string_free(str, FALSE);
2725
2726         return ret;
2727 }
2728
2729 static gint imap_set_message_flags(IMAPSession *session,
2730                                    MsgNumberList *numlist,
2731                                    IMAPFlags flags,
2732                                    gboolean is_set)
2733 {
2734         gchar *cmd;
2735         gchar *flag_str;
2736         gint ok = 0;
2737         GSList *seq_list;
2738         IMAPSet imapset;
2739
2740         flag_str = imap_get_flag_str(flags);
2741         cmd = g_strconcat(is_set ? "+FLAGS.SILENT (" : "-FLAGS.SILENT (",
2742                           flag_str, ")", NULL);
2743         g_free(flag_str);
2744
2745         seq_list = imap_get_seq_set_from_numlist(numlist);
2746         imapset = get_seq_set_from_seq_list(seq_list);
2747
2748         ok = imap_cmd_store(session, imapset, cmd);
2749         
2750         g_free(imapset);
2751         imap_seq_set_free(seq_list);
2752         g_free(cmd);
2753
2754         return ok;
2755 }
2756
2757 typedef struct _select_data {
2758         IMAPSession *session;
2759         gchar *real_path;
2760         gint *exists;
2761         gint *recent;
2762         gint *unseen;
2763         guint32 *uid_validity;
2764         gboolean done;
2765 } select_data;
2766
2767 static void *imap_select_thread(void *data)
2768 {
2769         select_data *stuff = (select_data *)data;
2770         IMAPSession *session = stuff->session;
2771         gchar *real_path = stuff->real_path;
2772         gint *exists = stuff->exists;
2773         gint *recent = stuff->recent;
2774         gint *unseen = stuff->unseen;
2775         guint32 *uid_validity = stuff->uid_validity;
2776         gint ok;
2777         
2778         ok = imap_cmd_select(session, real_path,
2779                              exists, recent, unseen, uid_validity, TRUE);
2780         stuff->done = TRUE;
2781         return GINT_TO_POINTER(ok);
2782 }
2783                 
2784 static gint imap_select(IMAPSession *session, IMAPFolder *folder,
2785                         const gchar *path,
2786                         gint *exists, gint *recent, gint *unseen,
2787                         guint32 *uid_validity, gboolean block)
2788 {
2789         gchar *real_path;
2790         gint ok;
2791         gint exists_, recent_, unseen_;
2792         guint32 uid_validity_;
2793         
2794         if (!exists || !recent || !unseen || !uid_validity) {
2795                 if (session->mbox && strcmp(session->mbox, path) == 0)
2796                         return IMAP_SUCCESS;
2797                 exists = &exists_;
2798                 recent = &recent_;
2799                 unseen = &unseen_;
2800                 uid_validity = &uid_validity_;
2801         }
2802
2803         g_free(session->mbox);
2804         session->mbox = NULL;
2805
2806         real_path = imap_get_real_path(folder, path);
2807         
2808 #if (defined USE_PTHREAD && defined __GLIBC__ && (__GLIBC__ > 2 || (__GLIBC__ == 2 && __GLIBC_MINOR__ >= 3)))
2809         if (block == FALSE) {
2810                 select_data *data = g_new0(select_data, 1);
2811                 void *tmp;
2812                 pthread_t pt;
2813                 data->session = session;
2814                 data->real_path = real_path;
2815                 data->exists = exists;
2816                 data->recent = recent;
2817                 data->unseen = unseen;
2818                 data->uid_validity = uid_validity;
2819                 data->done = FALSE;
2820                 
2821                 if (pthread_create(&pt, PTHREAD_CREATE_JOINABLE,
2822                                 imap_select_thread, data) != 0) {
2823                         ok = GPOINTER_TO_INT(imap_select_thread(data));
2824                         g_free(data);
2825                 } else {
2826                         debug_print("+++waiting for imap_select_thread...\n");
2827                         while(!data->done) {
2828                                 /* don't let the interface freeze while waiting */
2829                                 sylpheed_do_idle();
2830                         }
2831                         debug_print("---imap_select_thread done\n");
2832
2833                         /* get the thread's return value and clean its resources */
2834                         pthread_join(pt, &tmp);
2835                         ok = GPOINTER_TO_INT(tmp);
2836                         g_free(data);
2837                 }
2838         } else {
2839                 ok = imap_cmd_select(session, real_path,
2840                              exists, recent, unseen, uid_validity, block);
2841         }
2842 #else
2843         ok = imap_cmd_select(session, real_path,
2844                              exists, recent, unseen, uid_validity, block);
2845 #endif
2846         if (ok != IMAP_SUCCESS)
2847                 log_warning(_("can't select folder: %s\n"), real_path);
2848         else {
2849                 session->mbox = g_strdup(path);
2850                 session->folder_content_changed = FALSE;
2851         }
2852         g_free(real_path);
2853
2854         return ok;
2855 }
2856
2857 #define THROW(err) { ok = err; goto catch; }
2858
2859 static gint imap_status(IMAPSession *session, IMAPFolder *folder,
2860                         const gchar *path,
2861                         gint *messages, gint *recent,
2862                         guint32 *uid_next, guint32 *uid_validity,
2863                         gint *unseen, gboolean block)
2864 {
2865         gchar *real_path;
2866         gchar *real_path_;
2867         gint ok;
2868         GPtrArray *argbuf = NULL;
2869         gchar *str;
2870
2871         if (messages && recent && uid_next && uid_validity && unseen) {
2872                 *messages = *recent = *uid_next = *uid_validity = *unseen = 0;
2873                 argbuf = g_ptr_array_new();
2874         }
2875
2876         real_path = imap_get_real_path(folder, path);
2877         QUOTE_IF_REQUIRED(real_path_, real_path);
2878         imap_gen_send(session, "STATUS %s "
2879                           "(MESSAGES RECENT UIDNEXT UIDVALIDITY UNSEEN)",
2880                           real_path_);
2881
2882         ok = imap_cmd_ok_with_block(session, argbuf, block);
2883         if (ok != IMAP_SUCCESS || !argbuf) THROW(ok);
2884
2885         str = search_array_str(argbuf, "STATUS");
2886         if (!str) THROW(IMAP_ERROR);
2887
2888         str = strchr(str, '(');
2889         if (!str) THROW(IMAP_ERROR);
2890         str++;
2891         while (*str != '\0' && *str != ')') {
2892                 while (*str == ' ') str++;
2893
2894                 if (!strncmp(str, "MESSAGES ", 9)) {
2895                         str += 9;
2896                         *messages = strtol(str, &str, 10);
2897                 } else if (!strncmp(str, "RECENT ", 7)) {
2898                         str += 7;
2899                         *recent = strtol(str, &str, 10);
2900                 } else if (!strncmp(str, "UIDNEXT ", 8)) {
2901                         str += 8;
2902                         *uid_next = strtoul(str, &str, 10);
2903                 } else if (!strncmp(str, "UIDVALIDITY ", 12)) {
2904                         str += 12;
2905                         *uid_validity = strtoul(str, &str, 10);
2906                 } else if (!strncmp(str, "UNSEEN ", 7)) {
2907                         str += 7;
2908                         *unseen = strtol(str, &str, 10);
2909                 } else {
2910                         g_warning("invalid STATUS response: %s\n", str);
2911                         break;
2912                 }
2913         }
2914
2915 catch:
2916         g_free(real_path);
2917         if (argbuf) {
2918                 ptr_array_free_strings(argbuf);
2919                 g_ptr_array_free(argbuf, TRUE);
2920         }
2921
2922         return ok;
2923 }
2924
2925 #undef THROW
2926
2927 static gboolean imap_has_capability(IMAPSession *session, const gchar *cap)
2928 {
2929         gchar **p;
2930         
2931         for (p = session->capability; *p != NULL; ++p) {
2932                 if (!g_ascii_strcasecmp(*p, cap))
2933                         return TRUE;
2934         }
2935
2936         return FALSE;
2937 }
2938
2939 static void imap_free_capabilities(IMAPSession *session)
2940 {
2941         g_strfreev(session->capability);
2942         session->capability = NULL;
2943 }
2944
2945 /* low-level IMAP4rev1 commands */
2946
2947 static gint imap_cmd_authenticate(IMAPSession *session, const gchar *user,
2948                                   const gchar *pass, IMAPAuthType type)
2949 {
2950         gchar *auth_type;
2951         gint ok;
2952         gchar *buf = NULL;
2953         gchar *challenge;
2954         gint challenge_len;
2955         gchar hexdigest[33];
2956         gchar *response;
2957         gchar *response64;
2958
2959         auth_type = "CRAM-MD5";
2960
2961         imap_gen_send(session, "AUTHENTICATE %s", auth_type);
2962         ok = imap_gen_recv(session, &buf);
2963         if (ok != IMAP_SUCCESS || buf[0] != '+' || buf[1] != ' ') {
2964                 g_free(buf);
2965                 return IMAP_ERROR;
2966         }
2967
2968         challenge = g_malloc(strlen(buf + 2) + 1);
2969         challenge_len = base64_decode(challenge, buf + 2, -1);
2970         challenge[challenge_len] = '\0';
2971         g_free(buf);
2972         log_print("IMAP< [Decoded: %s]\n", challenge);
2973
2974         md5_hex_hmac(hexdigest, challenge, challenge_len, pass, strlen(pass));
2975         g_free(challenge);
2976
2977         response = g_strdup_printf("%s %s", user, hexdigest);
2978         log_print("IMAP> [Encoded: %s]\n", response);
2979         response64 = g_malloc((strlen(response) + 3) * 2 + 1);
2980         base64_encode(response64, response, strlen(response));
2981         g_free(response);
2982
2983         log_print("IMAP> %s\n", response64);
2984         sock_puts(SESSION(session)->sock, response64);
2985         ok = imap_cmd_ok(session, NULL);
2986         if (ok != IMAP_SUCCESS)
2987                 log_warning(_("IMAP4 authentication failed.\n"));
2988
2989         return ok;
2990 }
2991
2992 static gint imap_cmd_login(IMAPSession *session,
2993                            const gchar *user, const gchar *pass)
2994 {
2995         gchar *ans;
2996         gint ok;
2997
2998         imap_gen_send(session, "LOGIN {%d}\r\n%s {%d}\r\n%s", 
2999                                 strlen(user), user, 
3000                                 strlen(pass), pass);
3001                                 
3002         ok = imap_gen_recv_with_block(session, &ans, TRUE);
3003         if (ok != IMAP_SUCCESS || ans[0] != '+' || ans[1] != ' ') {
3004                 g_free(ans);
3005                 return IMAP_ERROR;
3006         }
3007         g_free(ans);
3008         ok = imap_gen_recv_with_block(session, &ans, TRUE);
3009         if (ok != IMAP_SUCCESS || ans[0] != '+' || ans[1] != ' ') {
3010                 g_free(ans);
3011                 return IMAP_ERROR;
3012         }
3013         g_free(ans);
3014         ok = imap_cmd_ok(session, NULL);
3015         if (ok != IMAP_SUCCESS)
3016                 log_warning(_("IMAP4 login failed.\n"));
3017
3018         return ok;
3019 }
3020
3021 static gint imap_cmd_logout(IMAPSession *session)
3022 {
3023         imap_gen_send(session, "LOGOUT");
3024         return imap_cmd_ok(session, NULL);
3025 }
3026
3027 static gint imap_cmd_noop(IMAPSession *session)
3028 {
3029         imap_gen_send(session, "NOOP");
3030         return imap_cmd_ok(session, NULL);
3031 }
3032
3033 #if USE_OPENSSL
3034 static gint imap_cmd_starttls(IMAPSession *session)
3035 {
3036         imap_gen_send(session, "STARTTLS");
3037         return imap_cmd_ok(session, NULL);
3038 }
3039 #endif
3040
3041 #define THROW(err) { ok = err; goto catch; }
3042
3043 static gint imap_cmd_namespace(IMAPSession *session, gchar **ns_str)
3044 {
3045         gint ok;
3046         GPtrArray *argbuf;
3047         gchar *str;
3048
3049         argbuf = g_ptr_array_new();
3050
3051         imap_gen_send(session, "NAMESPACE");
3052         if ((ok = imap_cmd_ok(session, argbuf)) != IMAP_SUCCESS) THROW(ok);
3053
3054         str = search_array_str(argbuf, "NAMESPACE");
3055         if (!str) THROW(IMAP_ERROR);
3056
3057         *ns_str = g_strdup(str);
3058
3059 catch:
3060         ptr_array_free_strings(argbuf);
3061         g_ptr_array_free(argbuf, TRUE);
3062
3063         return ok;
3064 }
3065
3066 #undef THROW
3067
3068 static gint imap_cmd_list(IMAPSession *session, const gchar *ref,
3069                           const gchar *mailbox, GPtrArray *argbuf)
3070 {
3071         gchar *ref_, *mailbox_;
3072
3073         if (!ref) ref = "\"\"";
3074         if (!mailbox) mailbox = "\"\"";
3075
3076         QUOTE_IF_REQUIRED(ref_, ref);
3077         QUOTE_IF_REQUIRED(mailbox_, mailbox);
3078         imap_gen_send(session, "LIST %s %s", ref_, mailbox_);
3079
3080         return imap_cmd_ok(session, argbuf);
3081 }
3082
3083 #define THROW goto catch
3084
3085 static gint imap_cmd_do_select(IMAPSession *session, const gchar *folder,
3086                                gboolean examine,
3087                                gint *exists, gint *recent, gint *unseen,
3088                                guint32 *uid_validity, gboolean block)
3089 {
3090         gint ok;
3091         gchar *resp_str;
3092         GPtrArray *argbuf;
3093         gchar *select_cmd;
3094         gchar *folder_;
3095         unsigned int uid_validity_;
3096
3097         *exists = *recent = *unseen = *uid_validity = 0;
3098         argbuf = g_ptr_array_new();
3099
3100         if (examine)
3101                 select_cmd = "EXAMINE";
3102         else
3103                 select_cmd = "SELECT";
3104
3105         QUOTE_IF_REQUIRED(folder_, folder);
3106         imap_gen_send(session, "%s %s", select_cmd, folder_);
3107
3108         if ((ok = imap_cmd_ok_with_block(session, argbuf, block)) != IMAP_SUCCESS) THROW;
3109
3110         resp_str = search_array_contain_str(argbuf, "EXISTS");
3111         if (resp_str) {
3112                 if (sscanf(resp_str,"%d EXISTS", exists) != 1) {
3113                         g_warning("imap_cmd_select(): invalid EXISTS line.\n");
3114                         THROW;
3115                 }
3116         }
3117
3118         resp_str = search_array_contain_str(argbuf, "RECENT");
3119         if (resp_str) {
3120                 if (sscanf(resp_str, "%d RECENT", recent) != 1) {
3121                         g_warning("imap_cmd_select(): invalid RECENT line.\n");
3122                         THROW;
3123                 }
3124         }
3125
3126         resp_str = search_array_contain_str(argbuf, "UIDVALIDITY");
3127         if (resp_str) {
3128                 if (sscanf(resp_str, "OK [UIDVALIDITY %u] ", &uid_validity_)
3129                     != 1) {
3130                         g_warning("imap_cmd_select(): invalid UIDVALIDITY line.\n");
3131                         THROW;
3132                 }
3133                 *uid_validity = uid_validity_;
3134         }
3135
3136         resp_str = search_array_contain_str(argbuf, "UNSEEN");
3137         if (resp_str) {
3138                 if (sscanf(resp_str, "OK [UNSEEN %d] ", unseen) != 1) {
3139                         g_warning("imap_cmd_select(): invalid UNSEEN line.\n");
3140                         THROW;
3141                 }
3142         }
3143
3144 catch:
3145         ptr_array_free_strings(argbuf);
3146         g_ptr_array_free(argbuf, TRUE);
3147
3148         return ok;
3149 }
3150
3151 static gint imap_cmd_select(IMAPSession *session, const gchar *folder,
3152                             gint *exists, gint *recent, gint *unseen,
3153                             guint32 *uid_validity, gboolean block)
3154 {
3155         return imap_cmd_do_select(session, folder, FALSE,
3156                                   exists, recent, unseen, uid_validity, block);
3157 }
3158
3159 static gint imap_cmd_examine(IMAPSession *session, const gchar *folder,
3160                              gint *exists, gint *recent, gint *unseen,
3161                              guint32 *uid_validity, gboolean block)
3162 {
3163         return imap_cmd_do_select(session, folder, TRUE,
3164                                   exists, recent, unseen, uid_validity, block);
3165 }
3166
3167 #undef THROW
3168
3169 static gint imap_cmd_create(IMAPSession *session, const gchar *folder)
3170 {
3171         gchar *folder_;
3172
3173         QUOTE_IF_REQUIRED(folder_, folder);
3174         imap_gen_send(session, "CREATE %s", folder_);
3175
3176         return imap_cmd_ok(session, NULL);
3177 }
3178
3179 static gint imap_cmd_rename(IMAPSession *session, const gchar *old_folder,
3180                             const gchar *new_folder)
3181 {
3182         gchar *old_folder_, *new_folder_;
3183
3184         QUOTE_IF_REQUIRED(old_folder_, old_folder);
3185         QUOTE_IF_REQUIRED(new_folder_, new_folder);
3186         imap_gen_send(session, "RENAME %s %s", old_folder_, new_folder_);
3187
3188         return imap_cmd_ok(session, NULL);
3189 }
3190
3191 static gint imap_cmd_delete(IMAPSession *session, const gchar *folder)
3192 {
3193         gchar *folder_;
3194
3195         QUOTE_IF_REQUIRED(folder_, folder);
3196         imap_gen_send(session, "DELETE %s", folder_);
3197
3198         return imap_cmd_ok(session, NULL);
3199 }
3200
3201 static gint imap_cmd_search(IMAPSession *session, const gchar *criteria, 
3202                             GSList **list, gboolean block)
3203 {
3204         gint ok;
3205         gchar *uidlist;
3206         GPtrArray *argbuf;
3207
3208         g_return_val_if_fail(criteria != NULL, IMAP_ERROR);
3209         g_return_val_if_fail(list != NULL, IMAP_ERROR);
3210
3211         *list = NULL;
3212         
3213         argbuf = g_ptr_array_new();
3214         imap_gen_send(session, "UID SEARCH %s", criteria);
3215
3216         ok = imap_cmd_ok_with_block(session, argbuf, block);
3217         if (ok != IMAP_SUCCESS) {
3218                 ptr_array_free_strings(argbuf);
3219                 g_ptr_array_free(argbuf, TRUE);
3220                 return ok;
3221         }
3222
3223         if ((uidlist = search_array_str(argbuf, "SEARCH ")) != NULL) {
3224                 gchar **strlist, **p;
3225
3226                 strlist = g_strsplit(uidlist + 7, " ", 0);
3227                 for (p = strlist; *p != NULL; ++p) {
3228                         guint msgnum;
3229
3230                         if (sscanf(*p, "%u", &msgnum) == 1)
3231                                 *list = g_slist_append(*list, GINT_TO_POINTER(msgnum));
3232                 }
3233                 g_strfreev(strlist);
3234         }
3235         ptr_array_free_strings(argbuf);
3236         g_ptr_array_free(argbuf, TRUE);
3237
3238         return IMAP_SUCCESS;
3239 }
3240
3241 typedef struct _fetch_data {
3242         IMAPSession *session;
3243         guint32 uid;
3244         const gchar *filename;
3245         gboolean headers;
3246         gboolean body;
3247         gboolean done;
3248 } fetch_data;
3249
3250 static void *imap_cmd_fetch_thread(void *data)
3251 {
3252         fetch_data *stuff = (fetch_data *)data;
3253         IMAPSession *session = stuff->session;
3254         guint32 uid = stuff->uid;
3255         const gchar *filename = stuff->filename;
3256         
3257         gint ok;
3258         gchar *buf = NULL;
3259         gchar *cur_pos;
3260         gchar size_str[32];
3261         glong size_num;
3262
3263         if (filename == NULL) {
3264                 stuff->done = TRUE;
3265                 return GINT_TO_POINTER(IMAP_ERROR);
3266         }
3267         
3268         if (stuff->body)
3269                 imap_gen_send(session, "UID FETCH %d BODY.PEEK[]", uid);
3270         else
3271                 imap_gen_send(session, "UID FETCH %d BODY.PEEK[HEADER]",
3272                                 uid);
3273         while ((ok = imap_gen_recv_block(session, &buf)) == IMAP_SUCCESS) {
3274                 if (buf[0] != '*' || buf[1] != ' ') {
3275                         g_free(buf);
3276                         stuff->done = TRUE;
3277                         return GINT_TO_POINTER(IMAP_ERROR);
3278                 }
3279                 if (strstr(buf, "FETCH") != NULL) break;
3280                 g_free(buf);
3281         }
3282         if (ok != IMAP_SUCCESS) {
3283                 g_free(buf);
3284                 stuff->done = TRUE;
3285                 return GINT_TO_POINTER(ok);
3286         }
3287
3288 #define RETURN_ERROR_IF_FAIL(cond)      \
3289         if (!(cond)) {                  \
3290                 g_free(buf);            \
3291                 stuff->done = TRUE;     \
3292                 return GINT_TO_POINTER(IMAP_ERROR);     \
3293         }
3294
3295         cur_pos = strchr(buf, '{');
3296         RETURN_ERROR_IF_FAIL(cur_pos != NULL);
3297         cur_pos = strchr_cpy(cur_pos + 1, '}', size_str, sizeof(size_str));
3298         RETURN_ERROR_IF_FAIL(cur_pos != NULL);
3299         size_num = atol(size_str);
3300         RETURN_ERROR_IF_FAIL(size_num >= 0);
3301
3302         RETURN_ERROR_IF_FAIL(*cur_pos == '\0');
3303
3304 #undef RETURN_ERROR_IF_FAIL
3305
3306         g_free(buf);
3307
3308         if (recv_bytes_write_to_file(SESSION(session)->sock,
3309                                      size_num, filename) != 0) {
3310                 stuff->done = TRUE;
3311                 return GINT_TO_POINTER(IMAP_ERROR);
3312         }
3313         if (imap_gen_recv_block(session, &buf) != IMAP_SUCCESS) {
3314                 g_free(buf);
3315                 stuff->done = TRUE;
3316                 return GINT_TO_POINTER(IMAP_ERROR);
3317         }
3318
3319         if (buf[0] == '\0' || buf[strlen(buf) - 1] != ')') {
3320                 g_free(buf);
3321                 stuff->done = TRUE;
3322                 return GINT_TO_POINTER(IMAP_ERROR);
3323         }
3324         g_free(buf);
3325
3326         ok = imap_cmd_ok_block(session, NULL);
3327
3328         stuff->done = TRUE;
3329         return GINT_TO_POINTER(ok);
3330 }
3331
3332 static gint imap_cmd_fetch(IMAPSession *session, guint32 uid,
3333                                 const gchar *filename, gboolean headers,
3334                                 gboolean body)
3335 {
3336         fetch_data *data = g_new0(fetch_data, 1);
3337         int result = 0;
3338 #ifdef USE_PTHREAD
3339         void *tmp;
3340         pthread_t pt;
3341 #endif
3342         data->done = FALSE;
3343         data->session = session;
3344         data->uid = uid;
3345         data->filename = filename;
3346         data->headers = headers;
3347         data->body = body;
3348
3349         if (prefs_common.work_offline && !imap_gtk_should_override()) {
3350                 g_free(data);
3351                 return -1;
3352         }
3353
3354 #if (defined USE_PTHREAD && defined __GLIBC__ && (__GLIBC__ > 2 || (__GLIBC__ == 2 && __GLIBC_MINOR__ >= 3)))
3355         if (pthread_create(&pt, PTHREAD_CREATE_JOINABLE,
3356                         imap_cmd_fetch_thread, data) != 0) {
3357                 result = GPOINTER_TO_INT(imap_cmd_fetch_thread(data));
3358                 g_free(data);
3359                 return result;
3360         }
3361         debug_print("+++waiting for imap_cmd_fetch_thread...\n");
3362         while(!data->done) {
3363                 /* don't let the interface freeze while waiting */
3364                 sylpheed_do_idle();
3365         }
3366         debug_print("---imap_cmd_fetch_thread done\n");
3367
3368         /* get the thread's return value and clean its resources */
3369         pthread_join(pt, &tmp);
3370         result = GPOINTER_TO_INT(tmp);
3371 #else
3372         result = GPOINTER_TO_INT(imap_cmd_fetch_thread(data));
3373 #endif
3374         g_free(data);
3375         return result;
3376 }
3377
3378 static gint imap_cmd_append(IMAPSession *session, const gchar *destfolder,
3379                             const gchar *file, IMAPFlags flags, 
3380                             guint32 *new_uid)
3381 {
3382         gint ok;
3383         gint size;
3384         gchar *destfolder_;
3385         gchar *flag_str;
3386         unsigned int new_uid_;
3387         gchar *ret = NULL;
3388         gchar buf[BUFFSIZE];
3389         FILE *fp;
3390         GPtrArray *argbuf;
3391         gchar *resp_str;
3392
3393         g_return_val_if_fail(file != NULL, IMAP_ERROR);
3394
3395         size = get_file_size_as_crlf(file);
3396         if ((fp = fopen(file, "rb")) == NULL) {
3397                 FILE_OP_ERROR(file, "fopen");
3398                 return -1;
3399         }
3400         QUOTE_IF_REQUIRED(destfolder_, destfolder);
3401         flag_str = imap_get_flag_str(flags);
3402         imap_gen_send(session, "APPEND %s (%s) {%d}", 
3403                       destfolder_, flag_str, size);
3404         g_free(flag_str);
3405
3406         ok = imap_gen_recv(session, &ret);
3407         if (ok != IMAP_SUCCESS || ret[0] != '+' || ret[1] != ' ') {
3408                 log_warning(_("can't append %s to %s\n"), file, destfolder_);
3409                 g_free(ret);
3410                 fclose(fp);
3411                 return IMAP_ERROR;
3412         }
3413         g_free(ret);
3414
3415         log_print("IMAP4> %s\n", "(sending file...)");
3416
3417         while (fgets(buf, sizeof(buf), fp) != NULL) {
3418                 strretchomp(buf);
3419                 if (sock_puts(SESSION(session)->sock, buf) < 0) {
3420                         fclose(fp);
3421                         return -1;
3422                 }
3423         }
3424
3425         if (ferror(fp)) {
3426                 FILE_OP_ERROR(file, "fgets");
3427                 fclose(fp);
3428                 return -1;
3429         }
3430
3431         sock_puts(SESSION(session)->sock, "");
3432
3433         fclose(fp);
3434
3435         if (new_uid != NULL)
3436                 *new_uid = 0;
3437
3438         if (new_uid != NULL && session->uidplus) {
3439                 argbuf = g_ptr_array_new();
3440
3441                 ok = imap_cmd_ok(session, argbuf);
3442                 if ((ok == IMAP_SUCCESS) && (argbuf->len > 0)) {
3443                         resp_str = g_ptr_array_index(argbuf, argbuf->len - 1);
3444                         if (resp_str &&
3445                             sscanf(resp_str, "%*u OK [APPENDUID %*u %u]",
3446                                    &new_uid_) == 1) {
3447                                 *new_uid = new_uid_;
3448                         }
3449                 }
3450
3451                 ptr_array_free_strings(argbuf);
3452                 g_ptr_array_free(argbuf, TRUE);
3453         } else
3454                 ok = imap_cmd_ok(session, NULL);
3455
3456         if (ok != IMAP_SUCCESS)
3457                 log_warning(_("can't append message to %s\n"),
3458                             destfolder_);
3459
3460         return ok;
3461 }
3462
3463 static MsgNumberList *imapset_to_numlist(IMAPSet imapset)
3464 {
3465         gchar **ranges, **range;
3466         unsigned int low, high;
3467         MsgNumberList *uids = NULL;
3468         
3469         ranges = g_strsplit(imapset, ",", 0);
3470         for (range = ranges; *range != NULL; range++) {
3471                 if (sscanf(*range, "%u:%u", &low, &high) == 1)
3472                         uids = g_slist_prepend(uids, GINT_TO_POINTER(low));
3473                 else {
3474                         int i;
3475                         for (i = low; i <= high; i++)
3476                                 uids = g_slist_prepend(uids, GINT_TO_POINTER(i));
3477                 }
3478         }
3479         uids = g_slist_reverse(uids);
3480         g_strfreev(ranges);
3481
3482         return uids;
3483 }
3484
3485 static gint imap_cmd_copy(IMAPSession *session, const gchar *seq_set,
3486                           const gchar *destfolder, GRelation *uid_mapping)
3487 {
3488         gint ok;
3489         gchar *destfolder_;
3490         
3491         g_return_val_if_fail(session != NULL, IMAP_ERROR);
3492         g_return_val_if_fail(seq_set != NULL, IMAP_ERROR);
3493         g_return_val_if_fail(destfolder != NULL, IMAP_ERROR);
3494
3495         QUOTE_IF_REQUIRED(destfolder_, destfolder);
3496         imap_gen_send(session, "UID COPY %s %s", seq_set, destfolder_);
3497
3498         if (uid_mapping != NULL && session->uidplus) {
3499                 GPtrArray *reply;               
3500                 gchar *resp_str = NULL, *olduids_str, *newuids_str;
3501                 MsgNumberList *olduids, *old_cur, *newuids, *new_cur;
3502
3503                 reply = g_ptr_array_new();
3504                 ok = imap_cmd_ok(session, reply);
3505                 if ((ok == IMAP_SUCCESS) && (reply->len > 0)) {
3506                         resp_str = g_ptr_array_index(reply, reply->len - 1);
3507                         if (resp_str) {
3508                                 olduids_str = g_new0(gchar, strlen(resp_str));
3509                                 newuids_str = g_new0(gchar, strlen(resp_str));
3510                                 if (sscanf(resp_str, "%*s OK [COPYUID %*u %[0-9,:] %[0-9,:]]",
3511                                            olduids_str, newuids_str) == 2) {
3512                                         olduids = imapset_to_numlist(olduids_str);
3513                                         newuids = imapset_to_numlist(newuids_str);
3514
3515                                         old_cur = olduids;
3516                                         new_cur = newuids;
3517                                         while(old_cur != NULL && new_cur != NULL) {
3518                                                 g_relation_insert(uid_mapping, 
3519                                                                   GPOINTER_TO_INT(old_cur->data),
3520                                                                   GPOINTER_TO_INT(new_cur->data));
3521                                                 old_cur = g_slist_next(old_cur);
3522                                                 new_cur = g_slist_next(new_cur);
3523                                         }
3524
3525                                         g_slist_free(olduids);
3526                                         g_slist_free(newuids);
3527                                 }
3528                                 g_free(olduids_str);
3529                                 g_free(newuids_str);
3530                         }
3531                 }
3532                 ptr_array_free_strings(reply);
3533                 g_ptr_array_free(reply, TRUE);
3534         } else
3535                 ok = imap_cmd_ok(session, NULL);
3536
3537         if (ok != IMAP_SUCCESS)
3538                 log_warning(_("can't copy %s to %s\n"), seq_set, destfolder_);
3539
3540         return ok;
3541 }
3542
3543 gint imap_cmd_envelope(IMAPSession *session, IMAPSet set)
3544 {
3545         static gchar *header_fields = 
3546                 "Date From To Cc Subject Message-ID References In-Reply-To" ;
3547
3548         imap_gen_send
3549                 (session, "UID FETCH %s (UID FLAGS RFC822.SIZE BODY.PEEK[HEADER.FIELDS (%s)])",
3550                  set, header_fields);
3551
3552         return IMAP_SUCCESS;
3553 }
3554
3555 static gint imap_cmd_store(IMAPSession *session, IMAPSet seq_set,
3556                            gchar *sub_cmd)
3557 {
3558         gint ok;
3559
3560         imap_gen_send(session, "UID STORE %s %s", seq_set, sub_cmd);
3561
3562         if ((ok = imap_cmd_ok(session, NULL)) != IMAP_SUCCESS) {
3563                 log_warning(_("error while imap command: STORE %s %s\n"),
3564                             seq_set, sub_cmd);
3565                 return ok;
3566         }
3567
3568         return IMAP_SUCCESS;
3569 }
3570
3571 typedef struct _expunge_data {
3572         IMAPSession *session;
3573         IMAPSet seq_set;
3574         gboolean done;
3575 } expunge_data;
3576
3577 static void *imap_cmd_expunge_thread(void *data)
3578 {
3579         expunge_data *stuff = (expunge_data *)data;
3580         IMAPSession *session = stuff->session;
3581         IMAPSet seq_set = stuff->seq_set;
3582
3583         gint ok;
3584
3585         if (seq_set && session->uidplus)
3586                 imap_gen_send(session, "UID EXPUNGE %s", seq_set);
3587         else    
3588                 imap_gen_send(session, "EXPUNGE");
3589         if ((ok = imap_cmd_ok_with_block(session, NULL, TRUE)) != IMAP_SUCCESS) {
3590                 log_warning(_("error while imap command: EXPUNGE\n"));
3591                 stuff->done = TRUE;
3592                 return GINT_TO_POINTER(ok);
3593         }
3594
3595         stuff->done = TRUE;
3596         return GINT_TO_POINTER(IMAP_SUCCESS);
3597 }
3598
3599 static gint imap_cmd_expunge(IMAPSession *session, IMAPSet seq_set)
3600 {
3601         expunge_data *data = g_new0(expunge_data, 1);
3602         int result;
3603 #ifdef USE_PTHREAD
3604         void *tmp;
3605         pthread_t pt;
3606 #endif
3607         data->done = FALSE;
3608         data->session = session;
3609         data->seq_set = seq_set;
3610
3611         if (prefs_common.work_offline && !imap_gtk_should_override()) {
3612                 g_free(data);
3613                 return -1;
3614         }
3615
3616 #if (defined USE_PTHREAD && defined __GLIBC__ && (__GLIBC__ > 2 || (__GLIBC__ == 2 && __GLIBC_MINOR__ >= 3)))
3617         if (pthread_create(&pt, PTHREAD_CREATE_JOINABLE,
3618                         imap_cmd_expunge_thread, data) != 0) {
3619                 result = GPOINTER_TO_INT(imap_cmd_expunge_thread(data));
3620                 g_free(data);
3621                 return result;
3622         }
3623         debug_print("+++waiting for imap_cmd_expunge_thread...\n");
3624         while(!data->done) {
3625                 /* don't let the interface freeze while waiting */
3626                 sylpheed_do_idle();
3627         }
3628         debug_print("---imap_cmd_expunge_thread done\n");
3629
3630         /* get the thread's return value and clean its resources */
3631         pthread_join(pt, &tmp);
3632         result = GPOINTER_TO_INT(tmp);
3633 #else
3634         result = GPOINTER_TO_INT(imap_cmd_expunge_thread(data));
3635 #endif
3636         g_free(data);
3637         return result;
3638 }
3639
3640 static gint imap_cmd_close(IMAPSession *session)
3641 {
3642         gint ok;
3643
3644         imap_gen_send(session, "CLOSE");
3645         if ((ok = imap_cmd_ok(session, NULL)) != IMAP_SUCCESS)
3646                 log_warning(_("error while imap command: CLOSE\n"));
3647
3648         return ok;
3649 }
3650
3651 static gint imap_cmd_ok_with_block(IMAPSession *session, GPtrArray *argbuf, gboolean block)
3652 {
3653         gint ok = IMAP_SUCCESS;
3654         gchar *buf;
3655         gint cmd_num;
3656         gchar *data;
3657
3658         while ((ok = imap_gen_recv_with_block(session, &buf, block))
3659                == IMAP_SUCCESS) {
3660                 /* make sure data is long enough for any substring of buf */
3661                 data = alloca(strlen(buf) + 1);
3662
3663                 /* untagged line read */
3664                 if (buf[0] == '*' && buf[1] == ' ') {
3665                         gint num;
3666                         if (argbuf)
3667                                 g_ptr_array_add(argbuf, g_strdup(buf + 2));
3668
3669                         if (sscanf(buf + 2, "%d %s", &num, data) >= 2) {
3670                                 if (!strcmp(data, "EXISTS")) {
3671                                         session->exists = num;
3672                                         session->folder_content_changed = TRUE;
3673                                 }
3674
3675                                 if(!strcmp(data, "EXPUNGE")) {
3676                                         session->exists--;
3677                                         session->folder_content_changed = TRUE;
3678                                 }
3679                         }
3680                 /* tagged line with correct tag and OK response found */
3681                 } else if ((sscanf(buf, "%d %s", &cmd_num, data) >= 2) &&
3682                            (cmd_num == session->cmd_count) &&
3683                            !strcmp(data, "OK")) {
3684                         if (argbuf)
3685                                 g_ptr_array_add(argbuf, g_strdup(buf));
3686                         break;
3687                 /* everything else */
3688                 } else {
3689                         ok = IMAP_ERROR;
3690                         break;
3691                 }
3692                 g_free(buf);
3693         }
3694         g_free(buf);
3695
3696         return ok;
3697 }
3698 static gint imap_cmd_ok(IMAPSession *session, GPtrArray *argbuf)
3699 {
3700         return imap_cmd_ok_with_block(session, argbuf, FALSE);
3701 }
3702 static gint imap_cmd_ok_block(IMAPSession *session, GPtrArray *argbuf)
3703 {
3704         return imap_cmd_ok_with_block(session, argbuf, TRUE);
3705 }
3706 static void imap_gen_send(IMAPSession *session, const gchar *format, ...)
3707 {
3708         gchar *buf;
3709         gchar *tmp;
3710         gchar *p;
3711         va_list args;
3712
3713         va_start(args, format);
3714         tmp = g_strdup_vprintf(format, args);
3715         va_end(args);
3716
3717         session->cmd_count++;
3718
3719         buf = g_strdup_printf("%d %s\r\n", session->cmd_count, tmp);
3720         if (!g_ascii_strncasecmp(tmp, "LOGIN ", 6) && (p = strchr(tmp + 6, ' '))) {
3721                 *p = '\0';
3722                 log_print("IMAP4> %d %s ********\n", session->cmd_count, tmp);
3723         } else
3724                 log_print("IMAP4> %d %s\n", session->cmd_count, tmp);
3725
3726         sock_write_all(SESSION(session)->sock, buf, strlen(buf));
3727         g_free(tmp);
3728         g_free(buf);
3729 }
3730
3731 static gint imap_gen_recv_with_block(IMAPSession *session, gchar **ret, gboolean block)
3732 {
3733         if (!block) {
3734                 if ((*ret = imap_getline(SESSION(session)->sock)) == NULL)
3735                         return IMAP_SOCKET;
3736         } else {
3737                 if ((*ret = sock_getline(SESSION(session)->sock)) == NULL)
3738                         return IMAP_SOCKET;
3739         }
3740         strretchomp(*ret);
3741
3742         log_print("IMAP4< %s\n", *ret);
3743         
3744         session_set_access_time(SESSION(session));
3745
3746         return IMAP_SUCCESS;
3747 }
3748
3749 static gint imap_gen_recv_block(IMAPSession *session, gchar **ret)
3750 {
3751         return imap_gen_recv_with_block(session, ret, TRUE);
3752 }
3753
3754 static gint imap_gen_recv(IMAPSession *session, gchar **ret)
3755 {
3756         return imap_gen_recv_with_block(session, ret, FALSE);
3757 }
3758 /* misc utility functions */
3759
3760 static gchar *strchr_cpy(const gchar *src, gchar ch, gchar *dest, gint len)
3761 {
3762         gchar *tmp;
3763
3764         dest[0] = '\0';
3765         tmp = strchr(src, ch);
3766         if (!tmp)
3767                 return NULL;
3768
3769         memcpy(dest, src, MIN(tmp - src, len - 1));
3770         dest[MIN(tmp - src, len - 1)] = '\0';
3771
3772         return tmp + 1;
3773 }
3774
3775 static gchar *get_quoted(const gchar *src, gchar ch, gchar *dest, gint len)
3776 {
3777         const gchar *p = src;
3778         gint n = 0;
3779
3780         g_return_val_if_fail(*p == ch, NULL);
3781
3782         *dest = '\0';
3783         p++;
3784
3785         while (*p != '\0' && *p != ch) {
3786                 if (n < len - 1) {
3787                         if (*p == '\\' && *(p + 1) != '\0')
3788                                 p++;
3789                         *dest++ = *p++;
3790                 } else
3791                         p++;
3792                 n++;
3793         }
3794
3795         *dest = '\0';
3796         return (gchar *)(*p == ch ? p + 1 : p);
3797 }
3798
3799 static gchar *search_array_contain_str(GPtrArray *array, const gchar *str)
3800 {
3801         gint i;
3802
3803         for (i = 0; i < array->len; i++) {
3804                 gchar *tmp;
3805
3806                 tmp = g_ptr_array_index(array, i);
3807                 if (strstr(tmp, str) != NULL)
3808                         return tmp;
3809         }
3810
3811         return NULL;
3812 }
3813
3814 static gchar *search_array_str(GPtrArray *array, const gchar *str)
3815 {
3816         gint i;
3817         gint len;
3818
3819         len = strlen(str);
3820
3821         for (i = 0; i < array->len; i++) {
3822                 gchar *tmp;
3823
3824                 tmp = g_ptr_array_index(array, i);
3825                 if (!strncmp(tmp, str, len))
3826                         return tmp;
3827         }
3828
3829         return NULL;
3830 }
3831
3832 static void imap_path_separator_subst(gchar *str, gchar separator)
3833 {
3834         gchar *p;
3835         gboolean in_escape = FALSE;
3836
3837         if (!separator || separator == '/') return;
3838
3839         for (p = str; *p != '\0'; p++) {
3840                 if (*p == '/' && !in_escape)
3841                         *p = separator;
3842                 else if (*p == '&' && *(p + 1) != '-' && !in_escape)
3843                         in_escape = TRUE;
3844                 else if (*p == '-' && in_escape)
3845                         in_escape = FALSE;
3846         }
3847 }
3848
3849 static gchar *imap_modified_utf7_to_utf8(const gchar *mutf7_str)
3850 {
3851         static iconv_t cd = (iconv_t)-1;
3852         static gboolean iconv_ok = TRUE;
3853         GString *norm_utf7;
3854         gchar *norm_utf7_p;
3855         size_t norm_utf7_len;
3856         const gchar *p;
3857         gchar *to_str, *to_p;
3858         size_t to_len;
3859         gboolean in_escape = FALSE;
3860
3861         if (!iconv_ok) return g_strdup(mutf7_str);
3862
3863         if (cd == (iconv_t)-1) {
3864                 cd = iconv_open(CS_INTERNAL, CS_UTF_7);
3865                 if (cd == (iconv_t)-1) {
3866                         g_warning("iconv cannot convert UTF-7 to %s\n",
3867                                   CS_INTERNAL);
3868                         iconv_ok = FALSE;
3869                         return g_strdup(mutf7_str);
3870                 }
3871         }
3872
3873         /* modified UTF-7 to normal UTF-7 conversion */
3874         norm_utf7 = g_string_new(NULL);
3875
3876         for (p = mutf7_str; *p != '\0'; p++) {
3877                 /* replace: '&'  -> '+',
3878                             "&-" -> '&',
3879                             escaped ','  -> '/' */
3880                 if (!in_escape && *p == '&') {
3881                         if (*(p + 1) != '-') {
3882                                 g_string_append_c(norm_utf7, '+');
3883                                 in_escape = TRUE;
3884                         } else {
3885                                 g_string_append_c(norm_utf7, '&');
3886                                 p++;
3887                         }
3888                 } else if (in_escape && *p == ',') {
3889                         g_string_append_c(norm_utf7, '/');
3890                 } else if (in_escape && *p == '-') {
3891                         g_string_append_c(norm_utf7, '-');
3892                         in_escape = FALSE;
3893                 } else {
3894                         g_string_append_c(norm_utf7, *p);
3895                 }
3896         }
3897
3898         norm_utf7_p = norm_utf7->str;
3899         norm_utf7_len = norm_utf7->len;
3900         to_len = strlen(mutf7_str) * 5;
3901         to_p = to_str = g_malloc(to_len + 1);
3902
3903         if (iconv(cd, (ICONV_CONST gchar **)&norm_utf7_p, &norm_utf7_len,
3904                   &to_p, &to_len) == -1) {
3905                 g_warning(_("iconv cannot convert UTF-7 to %s\n"),
3906                           conv_get_locale_charset_str());
3907                 g_string_free(norm_utf7, TRUE);
3908                 g_free(to_str);
3909                 return g_strdup(mutf7_str);
3910         }
3911
3912         /* second iconv() call for flushing */
3913         iconv(cd, NULL, NULL, &to_p, &to_len);
3914         g_string_free(norm_utf7, TRUE);
3915         *to_p = '\0';
3916
3917         return to_str;
3918 }
3919
3920 static gchar *imap_utf8_to_modified_utf7(const gchar *from)
3921 {
3922         static iconv_t cd = (iconv_t)-1;
3923         static gboolean iconv_ok = TRUE;
3924         gchar *norm_utf7, *norm_utf7_p;
3925         size_t from_len, norm_utf7_len;
3926         GString *to_str;
3927         gchar *from_tmp, *to, *p;
3928         gboolean in_escape = FALSE;
3929
3930         if (!iconv_ok) return g_strdup(from);
3931
3932         if (cd == (iconv_t)-1) {
3933                 cd = iconv_open(CS_UTF_7, CS_INTERNAL);
3934                 if (cd == (iconv_t)-1) {
3935                         g_warning(_("iconv cannot convert %s to UTF-7\n"),
3936                                   CS_INTERNAL);
3937                         iconv_ok = FALSE;
3938                         return g_strdup(from);
3939                 }
3940         }
3941
3942         /* UTF-8 to normal UTF-7 conversion */
3943         Xstrdup_a(from_tmp, from, return g_strdup(from));
3944         from_len = strlen(from);
3945         norm_utf7_len = from_len * 5;
3946         Xalloca(norm_utf7, norm_utf7_len + 1, return g_strdup(from));
3947         norm_utf7_p = norm_utf7;
3948
3949 #define IS_PRINT(ch) (isprint(ch) && IS_ASCII(ch))
3950
3951         while (from_len > 0) {
3952                 if (*from_tmp == '+') {
3953                         *norm_utf7_p++ = '+';
3954                         *norm_utf7_p++ = '-';
3955                         norm_utf7_len -= 2;
3956                         from_tmp++;
3957                         from_len--;
3958                 } else if (IS_PRINT(*(guchar *)from_tmp)) {
3959                         /* printable ascii char */
3960                         *norm_utf7_p = *from_tmp;
3961                         norm_utf7_p++;
3962                         norm_utf7_len--;
3963                         from_tmp++;
3964                         from_len--;
3965                 } else {
3966                         size_t conv_len = 0;
3967
3968                         /* unprintable char: convert to UTF-7 */
3969                         p = from_tmp;
3970                         while (!IS_PRINT(*(guchar *)p) && conv_len < from_len) {
3971                                 conv_len += g_utf8_skip[*(guchar *)p];
3972                                 p += g_utf8_skip[*(guchar *)p];
3973                         }
3974
3975                         from_len -= conv_len;
3976                         if (iconv(cd, (ICONV_CONST gchar **)&from_tmp,
3977                                   &conv_len,
3978                                   &norm_utf7_p, &norm_utf7_len) == -1) {
3979                                 g_warning(_("iconv cannot convert UTF-8 to UTF-7\n"));
3980                                 return g_strdup(from);
3981                         }
3982
3983                         /* second iconv() call for flushing */
3984                         iconv(cd, NULL, NULL, &norm_utf7_p, &norm_utf7_len);
3985                 }
3986         }
3987
3988 #undef IS_PRINT
3989
3990         *norm_utf7_p = '\0';
3991         to_str = g_string_new(NULL);
3992         for (p = norm_utf7; p < norm_utf7_p; p++) {
3993                 /* replace: '&' -> "&-",
3994                             '+' -> '&',
3995                             "+-" -> '+',
3996                             BASE64 '/' -> ',' */
3997                 if (!in_escape && *p == '&') {
3998                         g_string_append(to_str, "&-");
3999                 } else if (!in_escape && *p == '+') {
4000                         if (*(p + 1) == '-') {
4001                                 g_string_append_c(to_str, '+');
4002                                 p++;
4003                         } else {
4004                                 g_string_append_c(to_str, '&');
4005                                 in_escape = TRUE;
4006                         }
4007                 } else if (in_escape && *p == '/') {
4008                         g_string_append_c(to_str, ',');
4009                 } else if (in_escape && *p == '-') {
4010                         g_string_append_c(to_str, '-');
4011                         in_escape = FALSE;
4012                 } else {
4013                         g_string_append_c(to_str, *p);
4014                 }
4015         }
4016
4017         if (in_escape) {
4018                 in_escape = FALSE;
4019                 g_string_append_c(to_str, '-');
4020         }
4021
4022         to = to_str->str;
4023         g_string_free(to_str, FALSE);
4024
4025         return to;
4026 }
4027
4028 static GSList *imap_get_seq_set_from_numlist(MsgNumberList *numlist)
4029 {
4030         GString *str;
4031         GSList *sorted_list, *cur;
4032         guint first, last, next;
4033         gchar *ret_str;
4034         GSList *ret_list = NULL;
4035
4036         if (numlist == NULL)
4037                 return NULL;
4038
4039         str = g_string_sized_new(256);
4040
4041         sorted_list = g_slist_copy(numlist);
4042         sorted_list = g_slist_sort(sorted_list, g_int_compare);
4043
4044         first = GPOINTER_TO_INT(sorted_list->data);
4045
4046         for (cur = sorted_list; cur != NULL; cur = g_slist_next(cur)) {
4047                 last = GPOINTER_TO_INT(cur->data);
4048                 if (cur->next)
4049                         next = GPOINTER_TO_INT(cur->next->data);
4050                 else
4051                         next = 0;
4052
4053                 if (last + 1 != next || next == 0) {
4054                         if (str->len > 0)
4055                                 g_string_append_c(str, ',');
4056                         if (first == last)
4057                                 g_string_append_printf(str, "%u", first);
4058                         else
4059                                 g_string_append_printf(str, "%u:%u", first, last);
4060
4061                         first = next;
4062
4063                         if (str->len > IMAP_CMD_LIMIT) {
4064                                 ret_str = g_strdup(str->str);
4065                                 ret_list = g_slist_append(ret_list, ret_str);
4066                                 g_string_truncate(str, 0);
4067                         }
4068                 }
4069         }
4070
4071         if (str->len > 0) {
4072                 ret_str = g_strdup(str->str);
4073                 ret_list = g_slist_append(ret_list, ret_str);
4074         }
4075
4076         g_slist_free(sorted_list);
4077         g_string_free(str, TRUE);
4078
4079         return ret_list;
4080 }
4081
4082 static GSList *imap_get_seq_set_from_msglist(MsgInfoList *msglist)
4083 {
4084         MsgNumberList *numlist = NULL;
4085         MsgInfoList *cur;
4086         GSList *seq_list;
4087
4088         for (cur = msglist; cur != NULL; cur = g_slist_next(cur)) {
4089                 MsgInfo *msginfo = (MsgInfo *) cur->data;
4090
4091                 numlist = g_slist_append(numlist, GINT_TO_POINTER(msginfo->msgnum));
4092         }
4093         seq_list = imap_get_seq_set_from_numlist(numlist);
4094         g_slist_free(numlist);
4095
4096         return seq_list;
4097 }
4098
4099 static void imap_seq_set_free(GSList *seq_list)
4100 {
4101         slist_free_strings(seq_list);
4102         g_slist_free(seq_list);
4103 }
4104
4105
4106 static gboolean imap_rename_folder_func(GNode *node, gpointer data)
4107 {
4108         FolderItem *item = node->data;
4109         gchar **paths = data;
4110         const gchar *oldpath = paths[0];
4111         const gchar *newpath = paths[1];
4112         gchar *base;
4113         gchar *new_itempath;
4114         gint oldpathlen;
4115
4116         oldpathlen = strlen(oldpath);
4117         if (strncmp(oldpath, item->path, oldpathlen) != 0) {
4118                 g_warning("path doesn't match: %s, %s\n", oldpath, item->path);
4119                 return TRUE;
4120         }
4121
4122         base = item->path + oldpathlen;
4123         while (*base == G_DIR_SEPARATOR) base++;
4124         if (*base == '\0')
4125                 new_itempath = g_strdup(newpath);
4126         else
4127                 new_itempath = g_strconcat(newpath, G_DIR_SEPARATOR_S, base,
4128                                            NULL);
4129         g_free(item->path);
4130         item->path = new_itempath;
4131
4132         return FALSE;
4133 }
4134
4135 typedef struct _get_list_uid_data {
4136         Folder *folder;
4137         IMAPFolderItem *item;
4138         GSList **msgnum_list;
4139         gboolean done;
4140 } get_list_uid_data;
4141
4142 static void *get_list_of_uids_thread(void *data)
4143 {
4144         get_list_uid_data *stuff = (get_list_uid_data *)data;
4145         Folder *folder = stuff->folder;
4146         IMAPFolderItem *item = stuff->item;
4147         GSList **msgnum_list = stuff->msgnum_list;
4148         gint ok, nummsgs = 0, lastuid_old;
4149         IMAPSession *session;
4150         GSList *uidlist, *elem;
4151         gchar *cmd_buf;
4152
4153         session = imap_session_get(folder);
4154         if (session == NULL) {
4155                 stuff->done = TRUE;
4156                 return GINT_TO_POINTER(-1);
4157         }
4158
4159         ok = imap_select(session, IMAP_FOLDER(folder), item->item.path,
4160                          NULL, NULL, NULL, NULL, TRUE);
4161         if (ok != IMAP_SUCCESS) {
4162                 stuff->done = TRUE;
4163                 return GINT_TO_POINTER(-1);
4164         }
4165
4166         cmd_buf = g_strdup_printf("UID %d:*", item->lastuid + 1);
4167         ok = imap_cmd_search(session, cmd_buf, &uidlist, TRUE);
4168         g_free(cmd_buf);
4169
4170         if (ok == IMAP_SOCKET) {
4171                 session_destroy((Session *)session);
4172                 ((RemoteFolder *)folder)->session = NULL;
4173                 stuff->done = TRUE;
4174                 return GINT_TO_POINTER(-1);
4175         }
4176
4177         if (ok != IMAP_SUCCESS) {
4178                 gint i;
4179                 GPtrArray *argbuf;
4180
4181                 argbuf = g_ptr_array_new();
4182
4183                 cmd_buf = g_strdup_printf("UID FETCH %d:* (UID)", item->lastuid + 1);
4184                 imap_gen_send(session, cmd_buf);
4185                 g_free(cmd_buf);
4186                 ok = imap_cmd_ok_block(session, argbuf);
4187                 if (ok != IMAP_SUCCESS) {
4188                         ptr_array_free_strings(argbuf);
4189                         g_ptr_array_free(argbuf, TRUE);
4190                         stuff->done = TRUE;
4191                         return GINT_TO_POINTER(-1);
4192                 }
4193         
4194                 for(i = 0; i < argbuf->len; i++) {
4195                         int ret, msgnum;
4196         
4197                         if((ret = sscanf(g_ptr_array_index(argbuf, i), 
4198                                     "%*d FETCH (UID %d)", &msgnum)) == 1)
4199                                 uidlist = g_slist_prepend(uidlist, GINT_TO_POINTER(msgnum));
4200                 }
4201                 ptr_array_free_strings(argbuf);
4202                 g_ptr_array_free(argbuf, TRUE);
4203         }
4204
4205         lastuid_old = item->lastuid;
4206         *msgnum_list = g_slist_copy(item->uid_list);
4207         nummsgs = g_slist_length(*msgnum_list);
4208         debug_print("Got %d uids from cache\n", g_slist_length(item->uid_list));
4209
4210         for (elem = uidlist; elem != NULL; elem = g_slist_next(elem)) {
4211                 guint msgnum;
4212
4213                 msgnum = GPOINTER_TO_INT(elem->data);
4214                 if (msgnum > lastuid_old) {
4215                         *msgnum_list = g_slist_prepend(*msgnum_list, GINT_TO_POINTER(msgnum));
4216                         item->uid_list = g_slist_prepend(item->uid_list, GINT_TO_POINTER(msgnum));
4217                         nummsgs++;
4218
4219                         if(msgnum > item->lastuid)
4220                                 item->lastuid = msgnum;
4221                 }
4222         }
4223         g_slist_free(uidlist);
4224
4225         stuff->done = TRUE;
4226         return GINT_TO_POINTER(nummsgs);
4227 }
4228
4229 static gint get_list_of_uids(Folder *folder, IMAPFolderItem *item, GSList **msgnum_list)
4230 {
4231         gint result;
4232         get_list_uid_data *data = g_new0(get_list_uid_data, 1);
4233 #ifdef USE_PTHREAD
4234         void *tmp;
4235         pthread_t pt;
4236 #endif
4237         data->done = FALSE;
4238         data->folder = folder;
4239         data->item = item;
4240         data->msgnum_list = msgnum_list;
4241
4242         if (prefs_common.work_offline && !imap_gtk_should_override()) {
4243                 g_free(data);
4244                 return -1;
4245         }
4246
4247 #if (defined USE_PTHREAD && defined __GLIBC__ && (__GLIBC__ > 2 || (__GLIBC__ == 2 && __GLIBC_MINOR__ >= 3)))
4248         if (pthread_create(&pt, PTHREAD_CREATE_JOINABLE,
4249                         get_list_of_uids_thread, data) != 0) {
4250                 result = GPOINTER_TO_INT(get_list_of_uids_thread(data));
4251                 g_free(data);
4252                 return result;
4253         }
4254         debug_print("+++waiting for get_list_of_uids_thread...\n");
4255         while(!data->done) {
4256                 /* don't let the interface freeze while waiting */
4257                 sylpheed_do_idle();
4258         }
4259         debug_print("---get_list_of_uids_thread done\n");
4260
4261         /* get the thread's return value and clean its resources */
4262         pthread_join(pt, &tmp);
4263         result = GPOINTER_TO_INT(tmp);
4264 #else
4265         result = GPOINTER_TO_INT(get_list_of_uids_thread(data));
4266 #endif
4267         g_free(data);
4268         return result;
4269
4270 }
4271
4272 gint imap_get_num_list(Folder *folder, FolderItem *_item, GSList **msgnum_list, gboolean *old_uids_valid)
4273 {
4274         IMAPFolderItem *item = (IMAPFolderItem *)_item;
4275         IMAPSession *session;
4276         gint ok, nummsgs = 0, exists, recent, uid_val, uid_next, unseen;
4277         GSList *uidlist = NULL;
4278         gchar *dir;
4279         gboolean selected_folder;
4280
4281         g_return_val_if_fail(folder != NULL, -1);
4282         g_return_val_if_fail(item != NULL, -1);
4283         g_return_val_if_fail(item->item.path != NULL, -1);
4284         g_return_val_if_fail(FOLDER_CLASS(folder) == &imap_class, -1);
4285         g_return_val_if_fail(folder->account != NULL, -1);
4286
4287         session = imap_session_get(folder);
4288         g_return_val_if_fail(session != NULL, -1);
4289
4290         selected_folder = (session->mbox != NULL) &&
4291                           (!strcmp(session->mbox, item->item.path));
4292         if (selected_folder) {
4293                 ok = imap_cmd_noop(session);
4294                 if (ok != IMAP_SUCCESS)
4295                         return -1;
4296                 exists = session->exists;
4297
4298                 *old_uids_valid = TRUE;
4299         } else {
4300                 ok = imap_status(session, IMAP_FOLDER(folder), item->item.path,
4301                                  &exists, &recent, &uid_next, &uid_val, &unseen, FALSE);
4302                 if (ok != IMAP_SUCCESS)
4303                         return -1;
4304
4305                 if(item->item.mtime == uid_val)
4306                         *old_uids_valid = TRUE;
4307                 else {
4308                         *old_uids_valid = FALSE;
4309
4310                         debug_print("Freeing imap uid cache\n");
4311                         item->lastuid = 0;
4312                         g_slist_free(item->uid_list);
4313                         item->uid_list = NULL;
4314                 
4315                         item->item.mtime = uid_val;
4316
4317                         imap_delete_all_cached_messages((FolderItem *)item);
4318                 }
4319         }
4320
4321         if (!selected_folder)
4322                 item->uid_next = uid_next;
4323
4324         /* If old uid_next matches new uid_next we can be sure no message
4325            was added to the folder */
4326         if (( selected_folder && !session->folder_content_changed) ||
4327             (!selected_folder && uid_next == item->uid_next)) {
4328                 nummsgs = g_slist_length(item->uid_list);
4329
4330                 /* If number of messages is still the same we
4331                    know our caches message numbers are still valid,
4332                    otherwise if the number of messages has decrease
4333                    we discard our cache to start a new scan to find
4334                    out which numbers have been removed */
4335                 if (exists == nummsgs) {
4336                         *msgnum_list = g_slist_copy(item->uid_list);
4337                         return nummsgs;
4338                 } else if (exists < nummsgs) {
4339                         debug_print("Freeing imap uid cache");
4340                         item->lastuid = 0;
4341                         g_slist_free(item->uid_list);
4342                         item->uid_list = NULL;
4343                 }
4344         }
4345
4346         if (exists == 0) {
4347                 *msgnum_list = NULL;
4348                 return 0;
4349         }
4350
4351         nummsgs = get_list_of_uids(folder, item, &uidlist);
4352
4353         if (nummsgs != exists) {
4354                 /* Cache contains more messages then folder, we have cached
4355                    an old UID of a message that was removed and new messages
4356                    have been added too, otherwise the uid_next check would
4357                    not have failed */
4358                 debug_print("Freeing imap uid cache");
4359                 item->lastuid = 0;
4360                 g_slist_free(item->uid_list);
4361                 item->uid_list = NULL;
4362
4363                 g_slist_free(*msgnum_list);
4364
4365                 nummsgs = get_list_of_uids(folder, item, &uidlist);
4366         }
4367
4368         *msgnum_list = uidlist;
4369
4370         dir = folder_item_get_path((FolderItem *)item);
4371         debug_print("removing old messages from %s\n", dir);
4372         remove_numbered_files_not_in_list(dir, *msgnum_list);
4373         g_free(dir);
4374
4375         return nummsgs;
4376 }
4377
4378 static MsgInfo *imap_parse_msg(const gchar *file, FolderItem *item)
4379 {
4380         MsgInfo *msginfo;
4381         MsgFlags flags;
4382
4383         flags.perm_flags = MSG_NEW|MSG_UNREAD;
4384         flags.tmp_flags = 0;
4385
4386         g_return_val_if_fail(item != NULL, NULL);
4387         g_return_val_if_fail(file != NULL, NULL);
4388
4389         if (item->stype == F_QUEUE) {
4390                 MSG_SET_TMP_FLAGS(flags, MSG_QUEUED);
4391         } else if (item->stype == F_DRAFT) {
4392                 MSG_SET_TMP_FLAGS(flags, MSG_DRAFT);
4393         }
4394
4395         msginfo = procheader_parse_file(file, flags, FALSE, FALSE);
4396         if (!msginfo) return NULL;
4397         
4398         msginfo->plaintext_file = g_strdup(file);
4399         msginfo->folder = item;
4400
4401         return msginfo;
4402 }
4403
4404 GSList *imap_get_msginfos(Folder *folder, FolderItem *item, GSList *msgnum_list)
4405 {
4406         IMAPSession *session;
4407         MsgInfoList *ret = NULL;
4408         gint ok;
4409
4410         g_return_val_if_fail(folder != NULL, NULL);
4411         g_return_val_if_fail(item != NULL, NULL);
4412         g_return_val_if_fail(msgnum_list != NULL, NULL);
4413
4414         session = imap_session_get(folder);
4415         g_return_val_if_fail(session != NULL, NULL);
4416
4417         debug_print("IMAP getting msginfos\n");
4418         ok = imap_select(session, IMAP_FOLDER(folder), item->path,
4419                          NULL, NULL, NULL, NULL, FALSE);
4420         if (ok != IMAP_SUCCESS)
4421                 return NULL;
4422
4423         if (!(item->stype == F_QUEUE || item->stype == F_DRAFT)) {
4424                 ret = g_slist_concat(ret,
4425                         imap_get_uncached_messages(
4426                         session, item, msgnum_list));
4427         } else {
4428                 MsgNumberList *sorted_list, *elem;
4429                 gint startnum, lastnum;
4430
4431                 sorted_list = g_slist_sort(g_slist_copy(msgnum_list), g_int_compare);
4432
4433                 startnum = lastnum = GPOINTER_TO_INT(sorted_list->data);
4434
4435                 for (elem = sorted_list;; elem = g_slist_next(elem)) {
4436                         guint num = 0;
4437
4438                         if (elem)
4439                                 num = GPOINTER_TO_INT(elem->data);
4440
4441                         if (num > lastnum + 1 || elem == NULL) {
4442                                 int i;
4443                                 for (i = startnum; i <= lastnum; ++i) {
4444                                         gchar *file;
4445                         
4446                                         file = imap_fetch_msg(folder, item, i);
4447                                         if (file != NULL) {
4448                                                 MsgInfo *msginfo = imap_parse_msg(file, item);
4449                                                 if (msginfo != NULL) {
4450                                                         msginfo->msgnum = i;
4451                                                         ret = g_slist_append(ret, msginfo);
4452                                                 }
4453                                                 g_free(file);
4454                                         }
4455                                 }
4456
4457                                 if (elem == NULL)
4458                                         break;
4459
4460                                 startnum = num;
4461                         }
4462                         lastnum = num;
4463                 }
4464
4465                 g_slist_free(sorted_list);
4466         }
4467
4468         return ret;
4469 }
4470
4471 MsgInfo *imap_get_msginfo(Folder *folder, FolderItem *item, gint uid)
4472 {
4473         MsgInfo *msginfo = NULL;
4474         MsgInfoList *msginfolist;
4475         MsgNumberList numlist;
4476
4477         numlist.next = NULL;
4478         numlist.data = GINT_TO_POINTER(uid);
4479
4480         msginfolist = imap_get_msginfos(folder, item, &numlist);
4481         if (msginfolist != NULL) {
4482                 msginfo = msginfolist->data;
4483                 g_slist_free(msginfolist);
4484         }
4485
4486         return msginfo;
4487 }
4488
4489 gboolean imap_scan_required(Folder *folder, FolderItem *_item)
4490 {
4491         IMAPSession *session;
4492         IMAPFolderItem *item = (IMAPFolderItem *)_item;
4493         gint ok, exists = 0, recent = 0, unseen = 0;
4494         guint32 uid_next, uid_val = 0;
4495         gboolean selected_folder;
4496         
4497         g_return_val_if_fail(folder != NULL, FALSE);
4498         g_return_val_if_fail(item != NULL, FALSE);
4499         g_return_val_if_fail(item->item.folder != NULL, FALSE);
4500         g_return_val_if_fail(FOLDER_CLASS(item->item.folder) == &imap_class, FALSE);
4501
4502         if (item->item.path == NULL)
4503                 return FALSE;
4504
4505         session = imap_session_get(folder);
4506         g_return_val_if_fail(session != NULL, FALSE);
4507
4508         selected_folder = (session->mbox != NULL) &&
4509                           (!strcmp(session->mbox, item->item.path));
4510         if (selected_folder) {
4511                 ok = imap_cmd_noop(session);
4512                 if (ok != IMAP_SUCCESS)
4513                         return FALSE;
4514
4515                 if (session->folder_content_changed)
4516                         return TRUE;
4517         } else {
4518                 ok = imap_status(session, IMAP_FOLDER(folder), item->item.path,
4519                                  &exists, &recent, &uid_next, &uid_val, &unseen, FALSE);
4520                 if (ok != IMAP_SUCCESS)
4521                         return FALSE;
4522
4523                 if ((uid_next != item->uid_next) || (exists != item->item.total_msgs))
4524                         return TRUE;
4525         }
4526
4527         return FALSE;
4528 }
4529
4530 void imap_change_flags(Folder *folder, FolderItem *item, MsgInfo *msginfo, MsgPermFlags newflags)
4531 {
4532         IMAPSession *session;
4533         IMAPFlags flags_set = 0, flags_unset = 0;
4534         gint ok = IMAP_SUCCESS;
4535         MsgNumberList numlist;
4536         hashtable_data *ht_data = NULL;
4537
4538         g_return_if_fail(folder != NULL);
4539         g_return_if_fail(folder->klass == &imap_class);
4540         g_return_if_fail(item != NULL);
4541         g_return_if_fail(item->folder == folder);
4542         g_return_if_fail(msginfo != NULL);
4543         g_return_if_fail(msginfo->folder == item);
4544
4545         session = imap_session_get(folder);
4546         if (!session) return;
4547         
4548         if ((ok = imap_select(session, IMAP_FOLDER(folder), msginfo->folder->path,
4549             NULL, NULL, NULL, NULL, FALSE)) != IMAP_SUCCESS)
4550                 return;
4551                 
4552         if (!MSG_IS_MARKED(msginfo->flags) &&  (newflags & MSG_MARKED))
4553                 flags_set |= IMAP_FLAG_FLAGGED;
4554         if ( MSG_IS_MARKED(msginfo->flags) && !(newflags & MSG_MARKED))
4555                 flags_unset |= IMAP_FLAG_FLAGGED;
4556
4557         if (!MSG_IS_UNREAD(msginfo->flags) &&  (newflags & MSG_UNREAD))
4558                 flags_unset |= IMAP_FLAG_SEEN;
4559         if ( MSG_IS_UNREAD(msginfo->flags) && !(newflags & MSG_UNREAD))
4560                 flags_set |= IMAP_FLAG_SEEN;
4561
4562         if (!MSG_IS_REPLIED(msginfo->flags) &&  (newflags & MSG_REPLIED))
4563                 flags_set |= IMAP_FLAG_ANSWERED;
4564         if ( MSG_IS_REPLIED(msginfo->flags) && !(newflags & MSG_REPLIED))
4565                 flags_set |= IMAP_FLAG_ANSWERED;
4566
4567         numlist.next = NULL;
4568         numlist.data = GINT_TO_POINTER(msginfo->msgnum);
4569
4570         if (IMAP_FOLDER_ITEM(item)->batching) {
4571                 /* instead of performing an UID STORE command for each message change,
4572                  * as a lot of them can change "together", we just fill in hashtables
4573                  * and defer the treatment so that we're able to send only one
4574                  * command.
4575                  */
4576                 debug_print("IMAP batch mode on, deferring flags change\n");
4577                 if (flags_set) {
4578                         ht_data = g_hash_table_lookup(flags_set_table, GINT_TO_POINTER(flags_set));
4579                         if (ht_data == NULL) {
4580                                 ht_data = g_new0(hashtable_data, 1);
4581                                 ht_data->session = session;
4582                                 g_hash_table_insert(flags_set_table, GINT_TO_POINTER(flags_set), ht_data);
4583                         }
4584                         if (!g_slist_find(ht_data->msglist, GINT_TO_POINTER(msginfo->msgnum)))
4585                                 ht_data->msglist = g_slist_prepend(ht_data->msglist, GINT_TO_POINTER(msginfo->msgnum));
4586                 } 
4587                 if (flags_unset) {
4588                         ht_data = g_hash_table_lookup(flags_unset_table, GINT_TO_POINTER(flags_unset));
4589                         if (ht_data == NULL) {
4590                                 ht_data = g_new0(hashtable_data, 1);
4591                                 ht_data->session = session;
4592                                 g_hash_table_insert(flags_unset_table, GINT_TO_POINTER(flags_unset), ht_data);
4593                         }
4594                         if (!g_slist_find(ht_data->msglist, GINT_TO_POINTER(msginfo->msgnum)))
4595                                 ht_data->msglist = g_slist_prepend(ht_data->msglist, GINT_TO_POINTER(msginfo->msgnum));         
4596                 }
4597         } else {
4598                 debug_print("IMAP changing flags\n");
4599                 if (flags_set) {
4600                         ok = imap_set_message_flags(session, &numlist, flags_set, TRUE);
4601                         if (ok != IMAP_SUCCESS) return;
4602                 }
4603
4604                 if (flags_unset) {
4605                         ok = imap_set_message_flags(session, &numlist, flags_unset, FALSE);
4606                         if (ok != IMAP_SUCCESS) return;
4607                 }
4608         }
4609         msginfo->flags.perm_flags = newflags;
4610         
4611         return;
4612 }
4613
4614 static gint imap_remove_msg(Folder *folder, FolderItem *item, gint uid)
4615 {
4616         gint ok;
4617         IMAPSession *session;
4618         gchar *dir;
4619         MsgNumberList numlist;
4620         
4621         g_return_val_if_fail(folder != NULL, -1);
4622         g_return_val_if_fail(FOLDER_CLASS(folder) == &imap_class, -1);
4623         g_return_val_if_fail(item != NULL, -1);
4624
4625         session = imap_session_get(folder);
4626         if (!session) return -1;
4627
4628         ok = imap_select(session, IMAP_FOLDER(folder), item->path,
4629                          NULL, NULL, NULL, NULL, FALSE);
4630         if (ok != IMAP_SUCCESS)
4631                 return ok;
4632
4633         numlist.next = NULL;
4634         numlist.data = GINT_TO_POINTER(uid);
4635         
4636         ok = imap_set_message_flags
4637                 (IMAP_SESSION(REMOTE_FOLDER(folder)->session),
4638                 &numlist, IMAP_FLAG_DELETED, TRUE);
4639         if (ok != IMAP_SUCCESS) {
4640                 log_warning(_("can't set deleted flags: %d\n"), uid);
4641                 return ok;
4642         }
4643
4644         if (!session->uidplus) {
4645                 ok = imap_cmd_expunge(session, NULL);
4646         } else {
4647                 gchar *uidstr;
4648
4649                 uidstr = g_strdup_printf("%u", uid);
4650                 ok = imap_cmd_expunge(session, uidstr);
4651                 g_free(uidstr);
4652         }
4653         if (ok != IMAP_SUCCESS) {
4654                 log_warning(_("can't expunge\n"));
4655                 return ok;
4656         }
4657
4658         IMAP_FOLDER_ITEM(item)->uid_list = g_slist_remove(
4659             IMAP_FOLDER_ITEM(item)->uid_list, numlist.data);
4660         dir = folder_item_get_path(item);
4661         if (is_dir_exist(dir))
4662                 remove_numbered_files(dir, uid, uid);
4663         g_free(dir);
4664
4665         return IMAP_SUCCESS;
4666 }
4667
4668 static gint compare_msginfo(gconstpointer a, gconstpointer b)
4669 {
4670         return ((MsgInfo *)a)->msgnum - ((MsgInfo *)b)->msgnum;
4671 }
4672
4673 static guint gslist_find_next_num(MsgNumberList **list, guint num)
4674 {
4675         GSList *elem;
4676
4677         g_return_val_if_fail(list != NULL, -1);
4678
4679         for (elem = *list; elem != NULL; elem = g_slist_next(elem))
4680                 if (GPOINTER_TO_INT(elem->data) >= num)
4681                         break;
4682         *list = elem;
4683         return elem != NULL ? GPOINTER_TO_INT(elem->data) : (gint)-1;
4684 }
4685
4686 /*
4687  * NEW and DELETED flags are not syncronized
4688  * - The NEW/RECENT flags in IMAP folders can not really be directly
4689  *   modified by Sylpheed
4690  * - The DELETE/DELETED flag in IMAP and Sylpheed don't have the same
4691  *   meaning, in IMAP it always removes the messages from the FolderItem
4692  *   in Sylpheed it can mean to move the message to trash
4693  */
4694
4695 typedef struct _get_flags_data {
4696         Folder *folder;
4697         FolderItem *item;
4698         MsgInfoList *msginfo_list;
4699         GRelation *msgflags;
4700         gboolean done;
4701 } get_flags_data;
4702
4703 static /*gint*/ void *imap_get_flags_thread(void *data)
4704 {
4705         get_flags_data *stuff = (get_flags_data *)data;
4706         Folder *folder = stuff->folder;
4707         FolderItem *item = stuff->item;
4708         MsgInfoList *msginfo_list = stuff->msginfo_list;
4709         GRelation *msgflags = stuff->msgflags;
4710         IMAPSession *session;
4711         GSList *sorted_list;
4712         GSList *unseen = NULL, *answered = NULL, *flagged = NULL;
4713         GSList *p_unseen, *p_answered, *p_flagged;
4714         GSList *elem;
4715         GSList *seq_list, *cur;
4716         gboolean reverse_seen = FALSE;
4717         GString *cmd_buf;
4718         gint ok;
4719         gint exists_cnt, recent_cnt, unseen_cnt, uid_next;
4720         guint32 uidvalidity;
4721         gboolean selected_folder;
4722         
4723         if (folder == NULL || item == NULL) {
4724                 stuff->done = TRUE;
4725                 return GINT_TO_POINTER(-1);
4726         }
4727         if (msginfo_list == NULL) {
4728                 stuff->done = TRUE;
4729                 return GINT_TO_POINTER(0);
4730         }
4731
4732         session = imap_session_get(folder);
4733         if (session == NULL) {
4734                 stuff->done = TRUE;
4735                 return GINT_TO_POINTER(-1);
4736         }
4737
4738         selected_folder = (session->mbox != NULL) &&
4739                           (!strcmp(session->mbox, item->path));
4740
4741         if (!selected_folder) {
4742                 ok = imap_status(session, IMAP_FOLDER(folder), item->path,
4743                          &exists_cnt, &recent_cnt, &uid_next, &uidvalidity, &unseen_cnt, TRUE);
4744                 ok = imap_select(session, IMAP_FOLDER(folder), item->path,
4745                         NULL, NULL, NULL, NULL, TRUE);
4746                 if (ok != IMAP_SUCCESS) {
4747                         stuff->done = TRUE;
4748                         return GINT_TO_POINTER(-1);
4749                 }
4750
4751         } 
4752
4753         if (unseen_cnt > exists_cnt / 2)
4754                 reverse_seen = TRUE;
4755
4756         cmd_buf = g_string_new(NULL);
4757
4758         sorted_list = g_slist_sort(g_slist_copy(msginfo_list), compare_msginfo);
4759
4760         seq_list = imap_get_seq_set_from_msglist(msginfo_list);
4761
4762         for (cur = seq_list; cur != NULL; cur = g_slist_next(cur)) {
4763                 IMAPSet imapset = cur->data;
4764
4765                 g_string_sprintf(cmd_buf, "%sSEEN UID %s", reverse_seen ? "" : "UN", imapset);
4766                 imap_cmd_search(session, cmd_buf->str, &p_unseen, TRUE);
4767                 unseen = g_slist_concat(unseen, p_unseen);
4768
4769                 g_string_sprintf(cmd_buf, "ANSWERED UID %s", imapset);
4770                 imap_cmd_search(session, cmd_buf->str, &p_answered, TRUE);
4771                 answered = g_slist_concat(answered, p_answered);
4772
4773                 g_string_sprintf(cmd_buf, "FLAGGED UID %s", imapset);
4774                 imap_cmd_search(session, cmd_buf->str, &p_flagged, TRUE);
4775                 flagged = g_slist_concat(flagged, p_flagged);
4776         }
4777
4778         p_unseen = unseen;
4779         p_answered = answered;
4780         p_flagged = flagged;
4781
4782         for (elem = sorted_list; elem != NULL; elem = g_slist_next(elem)) {
4783                 MsgInfo *msginfo;
4784                 MsgPermFlags flags;
4785                 gboolean wasnew;
4786                 
4787                 msginfo = (MsgInfo *) elem->data;
4788                 flags = msginfo->flags.perm_flags;
4789                 wasnew = (flags & MSG_NEW);
4790                 flags &= ~((reverse_seen ? 0 : MSG_UNREAD | MSG_NEW) | MSG_REPLIED | MSG_MARKED);
4791                 if (reverse_seen)
4792                         flags |= MSG_UNREAD | (wasnew ? MSG_NEW : 0);
4793                 if (gslist_find_next_num(&p_unseen, msginfo->msgnum) == msginfo->msgnum) {
4794                         if (!reverse_seen) {
4795                                 flags |= MSG_UNREAD | (wasnew ? MSG_NEW : 0);
4796                         } else {
4797                                 flags &= ~(MSG_UNREAD | MSG_NEW);
4798                         }
4799                 }
4800                 if (gslist_find_next_num(&p_answered, msginfo->msgnum) == msginfo->msgnum)
4801                         flags |= MSG_REPLIED;
4802                 if (gslist_find_next_num(&p_flagged, msginfo->msgnum) == msginfo->msgnum)
4803                         flags |= MSG_MARKED;
4804                 g_relation_insert(msgflags, msginfo, GINT_TO_POINTER(flags));
4805         }
4806
4807         imap_seq_set_free(seq_list);
4808         g_slist_free(flagged);
4809         g_slist_free(answered);
4810         g_slist_free(unseen);
4811         g_slist_free(sorted_list);
4812         g_string_free(cmd_buf, TRUE);
4813
4814         stuff->done = TRUE;
4815         return GINT_TO_POINTER(0);
4816 }
4817
4818 static gint imap_get_flags(Folder *folder, FolderItem *item,
4819                            MsgInfoList *msginfo_list, GRelation *msgflags)
4820 {
4821         gint result;
4822         get_flags_data *data = g_new0(get_flags_data, 1);
4823 #ifdef USE_PTHREAD
4824         void *tmp;
4825         pthread_t pt;
4826 #endif
4827         data->done = FALSE;
4828         data->folder = folder;
4829         data->item = item;
4830         data->msginfo_list = msginfo_list;
4831         data->msgflags = msgflags;
4832
4833         if (prefs_common.work_offline && !imap_gtk_should_override()) {
4834                 g_free(data);
4835                 return -1;
4836         }
4837
4838 #if (defined USE_PTHREAD && defined __GLIBC__ && (__GLIBC__ > 2 || (__GLIBC__ == 2 && __GLIBC_MINOR__ >= 3)))
4839         if (pthread_create(&pt, PTHREAD_CREATE_JOINABLE,
4840                         imap_get_flags_thread, data) != 0) {
4841                 result = GPOINTER_TO_INT(imap_get_flags_thread(data));
4842                 g_free(data);
4843                 return result;
4844         }
4845         debug_print("+++waiting for imap_get_flags_thread...\n");
4846         while(!data->done) {
4847                 /* don't let the interface freeze while waiting */
4848                 sylpheed_do_idle();
4849         }
4850         debug_print("---imap_get_flags_thread done\n");
4851
4852         /* get the thread's return value and clean its resources */
4853         pthread_join(pt, &tmp);
4854         result = GPOINTER_TO_INT(tmp);
4855 #else
4856         result = GPOINTER_TO_INT(imap_get_flags_thread(data));
4857 #endif
4858         g_free(data);
4859         return result;
4860
4861 }
4862
4863 static gboolean process_flags(gpointer key, gpointer value, gpointer user_data)
4864 {
4865         gboolean flags_set = GPOINTER_TO_INT(user_data);
4866         gint flags_value = GPOINTER_TO_INT(key);
4867         hashtable_data *data = (hashtable_data *)value;
4868         
4869         data->msglist = g_slist_reverse(data->msglist);
4870         
4871         debug_print("IMAP %ssetting flags to %d for %d messages\n",
4872                 flags_set?"":"un",
4873                 flags_value,
4874                 g_slist_length(data->msglist));
4875         imap_set_message_flags(data->session, data->msglist, flags_value, flags_set);
4876         
4877         g_slist_free(data->msglist);    
4878         g_free(data);
4879         return TRUE;
4880 }
4881
4882 static void process_hashtable(void)
4883 {
4884         if (flags_set_table) {
4885                 g_hash_table_foreach_remove(flags_set_table, process_flags, GINT_TO_POINTER(TRUE));
4886                 g_free(flags_set_table);
4887                 flags_set_table = NULL;
4888         }
4889         if (flags_unset_table) {
4890                 g_hash_table_foreach_remove(flags_unset_table, process_flags, GINT_TO_POINTER(FALSE));
4891                 g_free(flags_unset_table);
4892                 flags_unset_table = NULL;
4893         }
4894 }
4895
4896 static IMAPFolderItem *batching_item = NULL;
4897
4898 static void imap_set_batch (Folder *folder, FolderItem *_item, gboolean batch)
4899 {
4900         IMAPFolderItem *item = (IMAPFolderItem *)_item;
4901
4902         g_return_if_fail(item != NULL);
4903         
4904         if (batch && batching_item != NULL) {
4905                 g_warning("already batching on %s\n", batching_item->item.path);
4906                 return;
4907         }
4908         
4909         if (item->batching == batch)
4910                 return;
4911         
4912         item->batching = batch;
4913         
4914         batching_item = batch?item:NULL;
4915         
4916         if (batch) {
4917                 debug_print("IMAP switching to batch mode\n");
4918                 if (flags_set_table) {
4919                         g_warning("flags_set_table non-null but we just entered batch mode!\n");
4920                         flags_set_table = NULL;
4921                 }
4922                 if (flags_unset_table) {
4923                         g_warning("flags_unset_table non-null but we just entered batch mode!\n");
4924                         flags_unset_table = NULL;
4925                 }
4926                 flags_set_table = g_hash_table_new(NULL, g_direct_equal);
4927                 flags_unset_table = g_hash_table_new(NULL, g_direct_equal);
4928         } else {
4929                 debug_print("IMAP switching away from batch mode\n");
4930                 /* process stuff */
4931                 process_hashtable();
4932         }
4933 }