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