f86c1302dd2cf8ff451f29bf730dad1e416455dc
[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
64 #ifdef USE_PTHREAD
65 #include <pthread.h>
66 #endif
67
68 typedef struct _IMAPFolder      IMAPFolder;
69 typedef struct _IMAPSession     IMAPSession;
70 typedef struct _IMAPNameSpace   IMAPNameSpace;
71 typedef struct _IMAPFolderItem  IMAPFolderItem;
72
73 #include "prefs_account.h"
74
75 #define IMAP_FOLDER(obj)        ((IMAPFolder *)obj)
76 #define IMAP_FOLDER_ITEM(obj)   ((IMAPFolderItem *)obj)
77 #define IMAP_SESSION(obj)       ((IMAPSession *)obj)
78
79 struct _IMAPFolder
80 {
81         RemoteFolder rfolder;
82
83         /* list of IMAPNameSpace */
84         GList *ns_personal;
85         GList *ns_others;
86         GList *ns_shared;
87 };
88
89 struct _IMAPSession
90 {
91         Session session;
92
93         gboolean authenticated;
94
95         gchar **capability;
96         gboolean uidplus;
97
98         gchar *mbox;
99         guint cmd_count;
100
101         /* CLAWS */
102         gboolean folder_content_changed;
103         guint exists;
104 };
105
106 struct _IMAPNameSpace
107 {
108         gchar *name;
109         gchar separator;
110 };
111
112 #define IMAP_SUCCESS    0
113 #define IMAP_SOCKET     2
114 #define IMAP_AUTHFAIL   3
115 #define IMAP_PROTOCOL   4
116 #define IMAP_SYNTAX     5
117 #define IMAP_IOERR      6
118 #define IMAP_ERROR      7
119
120 #define IMAPBUFSIZE     8192
121
122 typedef enum
123 {
124         IMAP_FLAG_SEEN          = 1 << 0,
125         IMAP_FLAG_ANSWERED      = 1 << 1,
126         IMAP_FLAG_FLAGGED       = 1 << 2,
127         IMAP_FLAG_DELETED       = 1 << 3,
128         IMAP_FLAG_DRAFT         = 1 << 4
129 } IMAPFlags;
130
131 #define IMAP_IS_SEEN(flags)     ((flags & IMAP_FLAG_SEEN) != 0)
132 #define IMAP_IS_ANSWERED(flags) ((flags & IMAP_FLAG_ANSWERED) != 0)
133 #define IMAP_IS_FLAGGED(flags)  ((flags & IMAP_FLAG_FLAGGED) != 0)
134 #define IMAP_IS_DELETED(flags)  ((flags & IMAP_FLAG_DELETED) != 0)
135 #define IMAP_IS_DRAFT(flags)    ((flags & IMAP_FLAG_DRAFT) != 0)
136
137
138 #define IMAP4_PORT      143
139 #if USE_OPENSSL
140 #define IMAPS_PORT      993
141 #endif
142
143 #define IMAP_CMD_LIMIT  1000
144
145 #define QUOTE_IF_REQUIRED(out, str)                             \
146 {                                                               \
147         if (*str != '"' && strpbrk(str, " \t(){}[]%*") != NULL) {       \
148                 gchar *__tmp;                                   \
149                 gint len;                                       \
150                                                                 \
151                 len = strlen(str) + 3;                          \
152                 Xalloca(__tmp, len, return IMAP_ERROR);         \
153                 g_snprintf(__tmp, len, "\"%s\"", str);          \
154                 out = __tmp;                                    \
155         } else {                                                \
156                 Xstrdup_a(out, str, return IMAP_ERROR);         \
157         }                                                       \
158 }
159
160 typedef gchar * IMAPSet;
161
162 struct _IMAPFolderItem
163 {
164         FolderItem item;
165
166         guint lastuid;
167         guint uid_next;
168         GSList *uid_list;
169 };
170
171 static void imap_folder_init            (Folder         *folder,
172                                          const gchar    *name,
173                                          const gchar    *path);
174
175 static Folder   *imap_folder_new        (const gchar    *name,
176                                          const gchar    *path);
177 static void      imap_folder_destroy    (Folder         *folder);
178
179 static IMAPSession *imap_session_new    (const PrefsAccount     *account);
180 static void     imap_session_authenticate(IMAPSession           *session,
181                                           const PrefsAccount    *account);
182 static void     imap_session_destroy    (Session        *session);
183
184 static gchar   *imap_fetch_msg          (Folder         *folder, 
185                                          FolderItem     *item, 
186                                          gint            uid);
187 static gint     imap_add_msg            (Folder         *folder,
188                                          FolderItem     *dest,
189                                          const gchar    *file, 
190                                          MsgFlags       *flags);
191 static gint     imap_add_msgs           (Folder         *folder, 
192                                          FolderItem     *dest,
193                                          GSList         *file_list,
194                                          GRelation      *relation);
195
196 static gint     imap_copy_msg           (Folder         *folder,
197                                          FolderItem     *dest, 
198                                          MsgInfo        *msginfo);
199 static gint     imap_copy_msgs          (Folder         *folder, 
200                                          FolderItem     *dest, 
201                                          MsgInfoList    *msglist, 
202                                          GRelation      *relation);
203
204 static gint     imap_remove_msg         (Folder         *folder, 
205                                          FolderItem     *item, 
206                                          gint            uid);
207 static gint     imap_remove_all_msg     (Folder         *folder, 
208                                          FolderItem     *item);
209
210 static gboolean imap_is_msg_changed     (Folder         *folder,
211                                          FolderItem     *item, 
212                                          MsgInfo        *msginfo);
213
214 static gint     imap_close              (Folder         *folder, 
215                                          FolderItem     *item);
216
217 static gint     imap_scan_tree          (Folder         *folder);
218
219 static gint     imap_create_tree        (Folder         *folder);
220
221 static FolderItem *imap_create_folder   (Folder         *folder,
222                                          FolderItem     *parent,
223                                          const gchar    *name);
224 static gint     imap_rename_folder      (Folder         *folder,
225                                          FolderItem     *item, 
226                                          const gchar    *name);
227 static gint     imap_remove_folder      (Folder         *folder, 
228                                          FolderItem     *item);
229
230 static FolderItem *imap_folder_item_new (Folder         *folder);
231 static void imap_folder_item_destroy    (Folder         *folder,
232                                          FolderItem     *item);
233
234 static IMAPSession *imap_session_get    (Folder         *folder);
235
236 static gint imap_greeting               (IMAPSession    *session);
237 static gint imap_auth                   (IMAPSession    *session,
238                                          const gchar    *user,
239                                          const gchar    *pass,
240                                          IMAPAuthType    type);
241
242 static gint imap_scan_tree_recursive    (IMAPSession    *session,
243                                          FolderItem     *item);
244 static GSList *imap_parse_list          (IMAPFolder     *folder,
245                                          IMAPSession    *session,
246                                          const gchar    *real_path,
247                                          gchar          *separator);
248
249 static void imap_create_missing_folders (Folder         *folder);
250 static FolderItem *imap_create_special_folder
251                                         (Folder                 *folder,
252                                          SpecialFolderItemType   stype,
253                                          const gchar            *name);
254
255 static gint imap_do_copy_msgs           (Folder         *folder,
256                                          FolderItem     *dest,
257                                          MsgInfoList    *msglist,
258                                          GRelation      *relation);
259
260 static void imap_delete_all_cached_messages     (FolderItem     *item);
261
262 #if USE_OPENSSL
263 static SockInfo *imap_open              (const gchar    *server,
264                                          gushort         port,
265                                          SSLType         ssl_type);
266 #else
267 static SockInfo *imap_open              (const gchar    *server,
268                                          gushort         port);
269 #endif
270
271 #if USE_OPENSSL
272 static SockInfo *imap_open_tunnel(const gchar *server,
273                                   const gchar *tunnelcmd,
274                                   SSLType ssl_type);
275 #else
276 static SockInfo *imap_open_tunnel(const gchar *server,
277                                   const gchar *tunnelcmd);
278 #endif
279
280 #if USE_OPENSSL
281 static SockInfo *imap_init_sock(SockInfo *sock, SSLType ssl_type);
282 #else
283 static SockInfo *imap_init_sock(SockInfo *sock);
284 #endif
285
286 static gchar *imap_get_flag_str         (IMAPFlags       flags);
287 static gint imap_set_message_flags      (IMAPSession    *session,
288                                          MsgNumberList  *numlist,
289                                          IMAPFlags       flags,
290                                          gboolean        is_set);
291 static gint imap_select                 (IMAPSession    *session,
292                                          IMAPFolder     *folder,
293                                          const gchar    *path,
294                                          gint           *exists,
295                                          gint           *recent,
296                                          gint           *unseen,
297                                          guint32        *uid_validity,
298                                          gboolean        block);
299 static gint imap_status                 (IMAPSession    *session,
300                                          IMAPFolder     *folder,
301                                          const gchar    *path,
302                                          gint           *messages,
303                                          gint           *recent,
304                                          guint32        *uid_next,
305                                          guint32        *uid_validity,
306                                          gint           *unseen,
307                                          gboolean        block);
308
309 static void imap_parse_namespace                (IMAPSession    *session,
310                                                  IMAPFolder     *folder);
311 static void imap_get_namespace_by_list          (IMAPSession    *session,
312                                                  IMAPFolder     *folder);
313 static IMAPNameSpace *imap_find_namespace       (IMAPFolder     *folder,
314                                                  const gchar    *path);
315 static gchar imap_get_path_separator            (IMAPFolder     *folder,
316                                                  const gchar    *path);
317 static gchar *imap_get_real_path                (IMAPFolder     *folder,
318                                                  const gchar    *path);
319
320 static gchar *imap_parse_atom           (SockInfo       *sock,
321                                          gchar          *src,
322                                          gchar          *dest,
323                                          gint            dest_len,
324                                          GString        *str);
325 static MsgFlags imap_parse_flags        (const gchar    *flag_str);
326 static MsgInfo *imap_parse_envelope     (SockInfo       *sock,
327                                          FolderItem     *item,
328                                          GString        *line_str);
329
330 static gboolean imap_has_capability     (IMAPSession    *session,
331                                          const gchar    *cap);
332 static void imap_free_capabilities      (IMAPSession    *session);
333
334 /* low-level IMAP4rev1 commands */
335 static gint imap_cmd_authenticate
336                                 (IMAPSession    *session,
337                                  const gchar    *user,
338                                  const gchar    *pass,
339                                  IMAPAuthType    type);
340 static gint imap_cmd_login      (IMAPSession    *session,
341                                  const gchar    *user,
342                                  const gchar    *pass);
343 static gint imap_cmd_logout     (IMAPSession    *session);
344 static gint imap_cmd_noop       (IMAPSession    *session);
345 #if USE_OPENSSL
346 static gint imap_cmd_starttls   (IMAPSession    *session);
347 #endif
348 static gint imap_cmd_namespace  (IMAPSession    *session,
349                                  gchar         **ns_str);
350 static gint imap_cmd_list       (IMAPSession    *session,
351                                  const gchar    *ref,
352                                  const gchar    *mailbox,
353                                  GPtrArray      *argbuf);
354 static gint imap_cmd_do_select  (IMAPSession    *session,
355                                  const gchar    *folder,
356                                  gboolean        examine,
357                                  gint           *exists,
358                                  gint           *recent,
359                                  gint           *unseen,
360                                  guint32        *uid_validity,
361                                  gboolean        block);
362 static gint imap_cmd_select     (IMAPSession    *session,
363                                  const gchar    *folder,
364                                  gint           *exists,
365                                  gint           *recent,
366                                  gint           *unseen,
367                                  guint32        *uid_validity,
368                                  gboolean        block);
369 static gint imap_cmd_examine    (IMAPSession    *session,
370                                  const gchar    *folder,
371                                  gint           *exists,
372                                  gint           *recent,
373                                  gint           *unseen,
374                                  guint32        *uid_validity,
375                                  gboolean        block);
376 static gint imap_cmd_create     (IMAPSession    *sock,
377                                  const gchar    *folder);
378 static gint imap_cmd_rename     (IMAPSession    *sock,
379                                  const gchar    *oldfolder,
380                                  const gchar    *newfolder);
381 static gint imap_cmd_delete     (IMAPSession    *session,
382                                  const gchar    *folder);
383 static gint imap_cmd_envelope   (IMAPSession    *session,
384                                  IMAPSet         set);
385 static gint imap_cmd_fetch      (IMAPSession    *sock,
386                                  guint32         uid,
387                                  const gchar    *filename);
388 static gint imap_cmd_append     (IMAPSession    *session,
389                                  const gchar    *destfolder,
390                                  const gchar    *file,
391                                  IMAPFlags       flags,
392                                  guint32        *new_uid);
393 static gint imap_cmd_copy       (IMAPSession    *session, 
394                                  const gchar    *seq_set, 
395                                  const gchar    *destfolder,
396                                  GRelation      *uid_mapping);
397 static gint imap_cmd_store      (IMAPSession    *session,
398                                  IMAPSet         set,
399                                  gchar          *sub_cmd);
400 static gint imap_cmd_expunge    (IMAPSession    *session,
401                                  IMAPSet         seq_set);
402 static gint imap_cmd_close      (IMAPSession    *session);
403
404 static gint imap_cmd_ok         (IMAPSession    *session,
405                                  GPtrArray      *argbuf);
406 static gint imap_cmd_ok_block   (IMAPSession    *session,
407                                  GPtrArray      *argbuf);
408 static gint imap_cmd_ok_with_block
409                                 (IMAPSession    *session, 
410                                  GPtrArray      *argbuf, 
411                                  gboolean       block);
412 static void imap_gen_send       (IMAPSession    *session,
413                                  const gchar    *format, ...);
414 static gint imap_gen_recv       (IMAPSession    *session,
415                                  gchar         **ret);
416 static gint imap_gen_recv_block (IMAPSession    *session,
417                                  gchar         **ret);
418 static gint imap_gen_recv_with_block    
419                                 (IMAPSession    *session,
420                                  gchar         **ret,
421                                  gboolean        block);
422
423 /* misc utility functions */
424 static gchar *strchr_cpy                        (const gchar    *src,
425                                                  gchar           ch,
426                                                  gchar          *dest,
427                                                  gint            len);
428 static gchar *get_quoted                        (const gchar    *src,
429                                                  gchar           ch,
430                                                  gchar          *dest,
431                                                  gint            len);
432 static gchar *search_array_contain_str          (GPtrArray      *array,
433                                                  const gchar    *str);
434 static gchar *search_array_str                  (GPtrArray      *array,
435                                                  const gchar    *str);
436 static void imap_path_separator_subst           (gchar          *str,
437                                                  gchar           separator);
438
439 static gchar *imap_utf8_to_modified_utf7        (const gchar    *from);
440 static gchar *imap_modified_utf7_to_utf8        (const gchar    *mutf7_str);
441
442 static GSList *imap_get_seq_set_from_numlist    (MsgNumberList  *msglist);
443 static GSList *imap_get_seq_set_from_msglist    (MsgInfoList    *msglist);
444 static void imap_seq_set_free                   (GSList         *seq_list);
445
446 static gboolean imap_rename_folder_func         (GNode          *node,
447                                                  gpointer        data);
448 static gint imap_get_num_list                   (Folder         *folder,
449                                                  FolderItem     *item,
450                                                  GSList        **list,
451                                                  gboolean       *old_uids_valid);
452 static GSList *imap_get_msginfos                (Folder         *folder,
453                                                  FolderItem     *item,
454                                                  GSList         *msgnum_list);
455 static MsgInfo *imap_get_msginfo                (Folder         *folder,
456                                                  FolderItem     *item,
457                                                  gint            num);
458 static gboolean imap_scan_required              (Folder         *folder,
459                                                  FolderItem     *item);
460 static void imap_change_flags                   (Folder         *folder,
461                                                  FolderItem     *item,
462                                                  MsgInfo        *msginfo,
463                                                  MsgPermFlags    newflags);
464 static gint imap_get_flags                      (Folder         *folder,
465                                                  FolderItem     *item,
466                                                  MsgInfoList    *msglist,
467                                                  GRelation      *msgflags);
468 static gchar *imap_folder_get_path              (Folder         *folder);
469 static gchar *imap_item_get_path                (Folder         *folder,
470                                                  FolderItem     *item);
471
472 static FolderClass imap_class;
473
474 typedef struct _thread_data {
475         gchar *server;
476         gushort port;
477         gboolean done;
478         SockInfo *sock;
479 #ifdef USE_OPENSSL
480         SSLType ssl_type;
481 #endif
482 } thread_data;
483
484 #ifdef USE_PTHREAD
485 void *imap_getline_thread(void *data)
486 {
487         thread_data *td = (thread_data *)data;
488         gchar *line = NULL;
489         
490         line = sock_getline(td->sock);
491         
492         td->done = TRUE;
493         
494         return (void *)line;
495 }
496 #endif
497
498 /* imap_getline just wraps sock_getline inside a thread, 
499  * performing gtk updates so that the interface isn't frozen.
500  */
501 static gchar *imap_getline(SockInfo *sock)
502 {
503 #if (defined USE_PTHREAD && defined __GLIBC__ && (__GLIBC__ > 2 || (__GLIBC__ == 2 && __GLIBC_MINOR__ >= 3)))
504         thread_data *td = g_new0(thread_data, 1);
505         pthread_t pt;
506         gchar *line;
507         td->sock = sock;
508         td->done = FALSE;
509         
510         if (pthread_create(&pt, PTHREAD_CREATE_JOINABLE,
511                         imap_getline_thread, td) != 0) {
512                 g_free(td);
513                 return sock_getline(sock);
514         }
515         
516         debug_print("+++waiting for imap_getline_thread...\n");
517         while(!td->done) {
518                 /* don't let the interface freeze while waiting */
519                 sylpheed_do_idle();
520         }
521         debug_print("---imap_getline_thread done\n");
522
523         /* get the thread's return value and clean its resources */
524         pthread_join(pt, (void *)&line);
525         g_free(td);
526
527         return line;
528 #else
529         return sock_getline(sock);
530 #endif
531 }
532
533 FolderClass *imap_get_class(void)
534 {
535         if (imap_class.idstr == NULL) {
536                 imap_class.type = F_IMAP;
537                 imap_class.idstr = "imap";
538                 imap_class.uistr = "IMAP4";
539
540                 /* Folder functions */
541                 imap_class.new_folder = imap_folder_new;
542                 imap_class.destroy_folder = imap_folder_destroy;
543                 imap_class.scan_tree = imap_scan_tree;
544                 imap_class.create_tree = imap_create_tree;
545
546                 /* FolderItem functions */
547                 imap_class.item_new = imap_folder_item_new;
548                 imap_class.item_destroy = imap_folder_item_destroy;
549                 imap_class.item_get_path = imap_item_get_path;
550                 imap_class.create_folder = imap_create_folder;
551                 imap_class.rename_folder = imap_rename_folder;
552                 imap_class.remove_folder = imap_remove_folder;
553                 imap_class.close = imap_close;
554                 imap_class.get_num_list = imap_get_num_list;
555                 imap_class.scan_required = imap_scan_required;
556
557                 /* Message functions */
558                 imap_class.get_msginfo = imap_get_msginfo;
559                 imap_class.get_msginfos = imap_get_msginfos;
560                 imap_class.fetch_msg = imap_fetch_msg;
561                 imap_class.add_msg = imap_add_msg;
562                 imap_class.add_msgs = imap_add_msgs;
563                 imap_class.copy_msg = imap_copy_msg;
564                 imap_class.copy_msgs = imap_copy_msgs;
565                 imap_class.remove_msg = imap_remove_msg;
566                 imap_class.remove_all_msg = imap_remove_all_msg;
567                 imap_class.is_msg_changed = imap_is_msg_changed;
568                 imap_class.change_flags = imap_change_flags;
569                 imap_class.get_flags = imap_get_flags;
570         }
571         
572         return &imap_class;
573 }
574
575 static gchar *get_seq_set_from_seq_list(GSList *seq_list)
576 {
577         gchar *val = NULL;
578         gchar *tmp = NULL;
579         GSList *cur = NULL;
580         
581         for (cur = seq_list; cur != NULL; cur = cur->next) {
582                 tmp = val?g_strdup(val):NULL;
583                 g_free(val);
584                 val = g_strconcat(tmp?tmp:"", tmp?",":"",(gchar *)cur->data,
585                                   NULL);
586                 g_free(tmp);
587         }
588         return val;
589 }
590
591
592 static Folder *imap_folder_new(const gchar *name, const gchar *path)
593 {
594         Folder *folder;
595
596         folder = (Folder *)g_new0(IMAPFolder, 1);
597         folder->klass = &imap_class;
598         imap_folder_init(folder, name, path);
599
600         return folder;
601 }
602
603 static void imap_folder_destroy(Folder *folder)
604 {
605         gchar *dir;
606
607         dir = imap_folder_get_path(folder);
608         if (is_dir_exist(dir))
609                 remove_dir_recursive(dir);
610         g_free(dir);
611
612         folder_remote_folder_destroy(REMOTE_FOLDER(folder));
613 }
614
615 static void imap_folder_init(Folder *folder, const gchar *name,
616                              const gchar *path)
617 {
618         folder_remote_folder_init((Folder *)folder, name, path);
619 }
620
621 static FolderItem *imap_folder_item_new(Folder *folder)
622 {
623         IMAPFolderItem *item;
624         
625         item = g_new0(IMAPFolderItem, 1);
626         item->lastuid = 0;
627         item->uid_next = 0;
628         item->uid_list = NULL;
629
630         return (FolderItem *)item;
631 }
632
633 static void imap_folder_item_destroy(Folder *folder, FolderItem *_item)
634 {
635         IMAPFolderItem *item = (IMAPFolderItem *)_item;
636
637         g_return_if_fail(item != NULL);
638         g_slist_free(item->uid_list);
639
640         g_free(_item);
641 }
642
643 static gboolean imap_reset_uid_lists_func(GNode *node, gpointer data)
644 {
645         IMAPFolderItem *item = (IMAPFolderItem *)node->data;
646         
647         item->lastuid = 0;
648         item->uid_next = 0;
649         g_slist_free(item->uid_list);
650         item->uid_list = NULL;
651         
652         return FALSE;
653 }
654
655 static void imap_reset_uid_lists(Folder *folder)
656 {
657         if(folder->node == NULL)
658                 return;
659         
660         /* Destroy all uid lists and rest last uid */
661         g_node_traverse(folder->node, G_IN_ORDER, G_TRAVERSE_ALL, -1, imap_reset_uid_lists_func, NULL); 
662 }
663
664 /* Send CAPABILITY, and examine the server's response to see whether this
665  * connection is pre-authenticated or not and build a list of CAPABILITIES. */
666 static gint imap_greeting(IMAPSession *session)
667 {
668         gchar *capstr;
669         GPtrArray *argbuf;
670
671         imap_gen_send(session, "CAPABILITY");
672         
673         argbuf = g_ptr_array_new();
674
675         if (imap_cmd_ok(session, argbuf) != IMAP_SUCCESS ||
676             ((capstr = search_array_str(argbuf, "CAPABILITY ")) == NULL)) {
677                 ptr_array_free_strings(argbuf);
678                 g_ptr_array_free(argbuf, TRUE);
679                 return -1;
680         }
681
682         session->authenticated = search_array_str(argbuf, "PREAUTH") != NULL;
683         
684         capstr += strlen("CAPABILITY ");
685
686         IMAP_SESSION(session)->capability = g_strsplit(capstr, " ", 0);
687         
688         ptr_array_free_strings(argbuf);
689         g_ptr_array_free(argbuf, TRUE);
690
691         if (imap_has_capability(session, "UIDPLUS")) 
692                 session->uidplus = TRUE; 
693
694         return 0;
695 }
696
697 static gint imap_auth(IMAPSession *session, const gchar *user, const gchar *pass,
698                       IMAPAuthType type)
699 {
700         gint ok;
701
702         if (type == 0 || type == IMAP_AUTH_LOGIN)
703                 ok = imap_cmd_login(session, user, pass);
704         else
705                 ok = imap_cmd_authenticate(session, user, pass, type);
706
707         if (ok == IMAP_SUCCESS)
708                 session->authenticated = TRUE;
709
710         return ok;
711 }
712
713 static IMAPSession *imap_session_get(Folder *folder)
714 {
715         RemoteFolder *rfolder = REMOTE_FOLDER(folder);
716         IMAPSession *session = NULL;
717
718         g_return_val_if_fail(folder != NULL, NULL);
719         g_return_val_if_fail(FOLDER_CLASS(folder) == &imap_class, NULL);
720         g_return_val_if_fail(folder->account != NULL, NULL);
721         
722         if (prefs_common.work_offline && !imap_gtk_should_override()) {
723                 return NULL;
724         }
725
726         /* Make sure we have a session */
727         if (rfolder->session != NULL) {
728                 session = IMAP_SESSION(rfolder->session);
729         } else {
730                 imap_reset_uid_lists(folder);
731                 session = imap_session_new(folder->account);
732         }
733         if(session == NULL)
734                 return NULL;
735
736         if (SESSION(session)->sock->state == CONN_DISCONNECTED) {
737                 debug_print("IMAP server disconnected\n");
738                 session_destroy(SESSION(session));
739                 imap_reset_uid_lists(folder);
740                 session = imap_session_new(folder->account);
741         }
742
743         /* Make sure session is authenticated */
744         if (!IMAP_SESSION(session)->authenticated)
745                 imap_session_authenticate(IMAP_SESSION(session), folder->account);
746         if (!IMAP_SESSION(session)->authenticated) {
747                 session_destroy(SESSION(session));
748                 rfolder->session = NULL;
749                 return NULL;
750         }
751
752         /* Make sure we have parsed the IMAP namespace */
753         imap_parse_namespace(IMAP_SESSION(session),
754                              IMAP_FOLDER(folder));
755
756         /* I think the point of this code is to avoid sending a
757          * keepalive if we've used the session recently and therefore
758          * think it's still alive.  Unfortunately, most of the code
759          * does not yet check for errors on the socket, and so if the
760          * connection drops we don't notice until the timeout expires.
761          * A better solution than sending a NOOP every time would be
762          * for every command to be prepared to retry until it is
763          * successfully sent. -- mbp */
764         if (time(NULL) - SESSION(session)->last_access_time > SESSION_TIMEOUT_INTERVAL) {
765                 /* verify that the session is still alive */
766                 if (imap_cmd_noop(session) != IMAP_SUCCESS) {
767                         /* Check if this is the first try to establish a
768                            connection, if yes we don't try to reconnect */
769                         if (rfolder->session == NULL) {
770                                 log_warning(_("Connecting to %s failed"),
771                                             folder->account->recv_server);
772                                 session_destroy(SESSION(session));
773                                 session = NULL;
774                         } else {
775                                 log_warning(_("IMAP4 connection to %s has been"
776                                             " disconnected. Reconnecting...\n"),
777                                             folder->account->recv_server);
778                                 statusbar_print_all(_("IMAP4 connection to %s has been"
779                                             " disconnected. Reconnecting...\n"),
780                                             folder->account->recv_server);
781                                 session_destroy(SESSION(session));
782                                 /* Clear folders session to make imap_session_get create
783                                    a new session, because of rfolder->session == NULL
784                                    it will not try to reconnect again and so avoid an
785                                    endless loop */
786                                 rfolder->session = NULL;
787                                 session = imap_session_get(folder);
788                                 statusbar_pop_all();
789                         }
790                 }
791         }
792
793         rfolder->session = SESSION(session);
794         
795         return IMAP_SESSION(session);
796 }
797
798 static IMAPSession *imap_session_new(const PrefsAccount *account)
799 {
800         IMAPSession *session;
801         SockInfo *imap_sock;
802         gushort port;
803
804 #ifdef USE_OPENSSL
805         /* FIXME: IMAP over SSL only... */ 
806         SSLType ssl_type;
807
808         port = account->set_imapport ? account->imapport
809                 : account->ssl_imap == SSL_TUNNEL ? IMAPS_PORT : IMAP4_PORT;
810         ssl_type = account->ssl_imap;   
811 #else
812         port = account->set_imapport ? account->imapport
813                 : IMAP4_PORT;
814 #endif
815
816         if (account->set_tunnelcmd) {
817                 log_message(_("creating tunneled IMAP4 connection\n"));
818 #if USE_OPENSSL
819                 if ((imap_sock = imap_open_tunnel(account->recv_server, 
820                                                   account->tunnelcmd,
821                                                   ssl_type)) == NULL)
822 #else
823                 if ((imap_sock = imap_open_tunnel(account->recv_server, 
824                                                   account->tunnelcmd)) == NULL)
825 #endif
826                         return NULL;
827         } else {
828                 g_return_val_if_fail(account->recv_server != NULL, NULL);
829
830                 log_message(_("creating IMAP4 connection to %s:%d ...\n"),
831                             account->recv_server, port);
832                 
833 #if USE_OPENSSL
834                 if ((imap_sock = imap_open(account->recv_server, port,
835                                            ssl_type)) == NULL)
836 #else
837                 if ((imap_sock = imap_open(account->recv_server, port)) == NULL)
838 #endif
839                         return NULL;
840         }
841
842         session = g_new0(IMAPSession, 1);
843         session_init(SESSION(session));
844         SESSION(session)->type             = SESSION_IMAP;
845         SESSION(session)->server           = g_strdup(account->recv_server);
846         SESSION(session)->sock             = imap_sock;
847
848         SESSION(session)->destroy          = imap_session_destroy;
849
850         session->capability = NULL;
851
852         session->authenticated = FALSE;
853         session->mbox = NULL;
854         session->cmd_count = 0;
855
856         /* Only need to log in if the connection was not PREAUTH */
857         if (imap_greeting(session) != IMAP_SUCCESS) {
858                 session_destroy(SESSION(session));
859                 return NULL;
860         }
861
862 #if USE_OPENSSL
863         if (account->ssl_imap == SSL_STARTTLS && 
864             imap_has_capability(session, "STARTTLS")) {
865                 gint ok;
866
867                 ok = imap_cmd_starttls(session);
868                 if (ok != IMAP_SUCCESS) {
869                         log_warning(_("Can't start TLS session.\n"));
870                         session_destroy(SESSION(session));
871                         return NULL;
872                 }
873                 if (!ssl_init_socket_with_method(SESSION(session)->sock, 
874                     SSL_METHOD_TLSv1)) {
875                         session_destroy(SESSION(session));
876                         return NULL;
877                 }
878
879                 imap_free_capabilities(session);
880                 session->authenticated = FALSE;
881                 session->uidplus = FALSE;
882                 session->cmd_count = 1;
883
884                 if (imap_greeting(session) != IMAP_SUCCESS) {
885                         session_destroy(SESSION(session));
886                         return NULL;
887                 }               
888         }
889 #endif
890         log_message("IMAP connection is %s-authenticated\n",
891                     (session->authenticated) ? "pre" : "un");
892
893         return session;
894 }
895
896 static void imap_session_authenticate(IMAPSession *session, 
897                                       const PrefsAccount *account)
898 {
899         gchar *pass;
900
901         g_return_if_fail(account->userid != NULL);
902
903         pass = account->passwd;
904         if (!pass) {
905                 gchar *tmp_pass;
906                 tmp_pass = input_dialog_query_password(account->recv_server, account->userid);
907                 if (!tmp_pass)
908                         return;
909                 Xstrdup_a(pass, tmp_pass, {g_free(tmp_pass); return;});
910                 g_free(tmp_pass);
911         }
912         statusbar_print_all(_("Connecting to IMAP4 server %s...\n"),
913                                 account->recv_server);
914         if (imap_auth(session, account->userid, pass, account->imap_auth_type) != IMAP_SUCCESS) {
915                 imap_cmd_logout(session);
916                 statusbar_pop_all();
917                 alertpanel_error(_("Can't authenticate on IMAP4 server: %s"),
918                                  account->recv_server);
919                 
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         debug_print("-> 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 void *imap_open_thread(void *data)
2066 {
2067         SockInfo *sock = NULL;
2068         thread_data *td = (thread_data *)data;
2069         if ((sock = sock_connect(td->server, td->port)) == NULL) {
2070                 log_warning(_("Can't connect to IMAP4 server: %s:%d\n"),
2071                             td->server, td->port);
2072                 td->done = TRUE;
2073                 return NULL;
2074         }
2075
2076         td->done = TRUE;
2077         return sock;
2078 }
2079
2080 #if USE_OPENSSL
2081 static SockInfo *imap_open(const gchar *server, gushort port,
2082                            SSLType ssl_type)
2083 #else
2084 static SockInfo *imap_open(const gchar *server, gushort port)
2085 #endif
2086 {
2087         thread_data *td = g_new0(thread_data, 1);
2088 #if USE_PTHREAD
2089         pthread_t pt;
2090 #endif
2091         SockInfo *sock = NULL;
2092
2093 #if USE_OPENSSL
2094         td->ssl_type = ssl_type;
2095 #endif
2096         td->server = g_strdup(server);
2097         td->port = port;
2098         td->done = FALSE;
2099
2100         statusbar_print_all(_("Connecting to IMAP4 server: %s..."), server);
2101
2102 #if (defined USE_PTHREAD && defined __GLIBC__ && (__GLIBC__ > 2 || (__GLIBC__ == 2 && __GLIBC_MINOR__ >= 3)))
2103         if (pthread_create(&pt, PTHREAD_CREATE_JOINABLE,
2104                         imap_open_thread, td) != 0) {
2105                 statusbar_pop_all();
2106                 sock = imap_open_thread(td);
2107         } else {        
2108                 debug_print("+++waiting for imap_open_thread...\n");
2109                 while(!td->done) {
2110                         /* don't let the interface freeze while waiting */
2111                         sylpheed_do_idle();
2112                 }
2113
2114                 /* get the thread's return value and clean its resources */
2115                 pthread_join(pt, (void *)&sock);
2116         }
2117 #else
2118         sock = imap_open_thread(td);
2119 #endif
2120 #if USE_OPENSSL
2121         if (sock && td->ssl_type == SSL_TUNNEL && !ssl_init_socket(sock)) {
2122                 log_warning(_("Can't establish IMAP4 session with: %s:%d\n"),
2123                             td->server, td->port);
2124                 sock_close(sock);
2125                 sock = NULL;
2126                 td->done = TRUE;
2127         }
2128 #endif
2129         g_free(td->server);
2130         g_free(td);
2131
2132         debug_print("---imap_open_thread returned %p\n", sock);
2133         statusbar_pop_all();
2134
2135         if(!sock && !prefs_common.no_recv_err_panel) {
2136                 alertpanel_error(_("Can't connect to IMAP4 server: %s:%d"),
2137                                  server, port);
2138         }
2139
2140         return sock;
2141 }
2142
2143 #if USE_OPENSSL
2144 static SockInfo *imap_init_sock(SockInfo *sock, SSLType ssl_type)
2145 #else
2146 static SockInfo *imap_init_sock(SockInfo *sock)
2147 #endif
2148 {
2149
2150         return sock;
2151 }
2152
2153 static GList *imap_parse_namespace_str(gchar *str)
2154 {
2155         guchar *p = str;
2156         gchar *name;
2157         gchar *separator;
2158         IMAPNameSpace *namespace;
2159         GList *ns_list = NULL;
2160
2161         while (*p != '\0') {
2162                 /* parse ("#foo" "/") */
2163
2164                 while (*p && *p != '(') p++;
2165                 if (*p == '\0') break;
2166                 p++;
2167
2168                 while (*p && *p != '"') p++;
2169                 if (*p == '\0') break;
2170                 p++;
2171                 name = p;
2172
2173                 while (*p && *p != '"') p++;
2174                 if (*p == '\0') break;
2175                 *p = '\0';
2176                 p++;
2177
2178                 while (*p && isspace(*p)) p++;
2179                 if (*p == '\0') break;
2180                 if (strncmp(p, "NIL", 3) == 0)
2181                         separator = NULL;
2182                 else if (*p == '"') {
2183                         p++;
2184                         separator = p;
2185                         while (*p && *p != '"') p++;
2186                         if (*p == '\0') break;
2187                         *p = '\0';
2188                         p++;
2189                 } else break;
2190
2191                 while (*p && *p != ')') p++;
2192                 if (*p == '\0') break;
2193                 p++;
2194
2195                 namespace = g_new(IMAPNameSpace, 1);
2196                 namespace->name = g_strdup(name);
2197                 namespace->separator = separator ? separator[0] : '\0';
2198                 ns_list = g_list_append(ns_list, namespace);
2199         }
2200
2201         return ns_list;
2202 }
2203
2204 static void imap_parse_namespace(IMAPSession *session, IMAPFolder *folder)
2205 {
2206         gchar *ns_str;
2207         gchar **str_array;
2208
2209         g_return_if_fail(session != NULL);
2210         g_return_if_fail(folder != NULL);
2211
2212         if (folder->ns_personal != NULL ||
2213             folder->ns_others   != NULL ||
2214             folder->ns_shared   != NULL)
2215                 return;
2216
2217         if (!imap_has_capability(session, "NAMESPACE")) {
2218                 imap_get_namespace_by_list(session, folder);
2219                 return;
2220         }
2221         
2222         if (imap_cmd_namespace(session, &ns_str)
2223             != IMAP_SUCCESS) {
2224                 log_warning(_("can't get namespace\n"));
2225                 return;
2226         }
2227
2228         str_array = strsplit_parenthesis(ns_str, '(', ')', 3);
2229         if (str_array == NULL) {
2230                 g_free(ns_str);
2231                 imap_get_namespace_by_list(session, folder);
2232                 return;
2233         }
2234         if (str_array[0])
2235                 folder->ns_personal = imap_parse_namespace_str(str_array[0]);
2236         if (str_array[0] && str_array[1])
2237                 folder->ns_others = imap_parse_namespace_str(str_array[1]);
2238         if (str_array[0] && str_array[1] && str_array[2])
2239                 folder->ns_shared = imap_parse_namespace_str(str_array[2]);
2240         g_strfreev(str_array);
2241         g_free(ns_str);
2242 }
2243
2244 static void imap_get_namespace_by_list(IMAPSession *session, IMAPFolder *folder)
2245 {
2246         GSList *item_list, *cur;
2247         gchar separator = '\0';
2248         IMAPNameSpace *namespace;
2249
2250         g_return_if_fail(session != NULL);
2251         g_return_if_fail(folder != NULL);
2252
2253         if (folder->ns_personal != NULL ||
2254             folder->ns_others   != NULL ||
2255             folder->ns_shared   != NULL)
2256                 return;
2257
2258         imap_gen_send(session, "LIST \"\" \"\"");
2259         item_list = imap_parse_list(folder, session, "", &separator);
2260         for (cur = item_list; cur != NULL; cur = cur->next)
2261                 folder_item_destroy(FOLDER_ITEM(cur->data));
2262         g_slist_free(item_list);
2263
2264         namespace = g_new(IMAPNameSpace, 1);
2265         namespace->name = g_strdup("");
2266         namespace->separator = separator;
2267         folder->ns_personal = g_list_append(NULL, namespace);
2268 }
2269
2270 static IMAPNameSpace *imap_find_namespace_from_list(GList *ns_list,
2271                                                     const gchar *path)
2272 {
2273         IMAPNameSpace *namespace = NULL;
2274         gchar *tmp_path, *name;
2275
2276         if (!path) path = "";
2277
2278         for (; ns_list != NULL; ns_list = ns_list->next) {
2279                 IMAPNameSpace *tmp_ns = ns_list->data;
2280
2281                 Xstrcat_a(tmp_path, path, "/", return namespace);
2282                 Xstrdup_a(name, tmp_ns->name, return namespace);
2283                 if (tmp_ns->separator && tmp_ns->separator != '/') {
2284                         subst_char(tmp_path, tmp_ns->separator, '/');
2285                         subst_char(name, tmp_ns->separator, '/');
2286                 }
2287                 if (strncmp(tmp_path, name, strlen(name)) == 0)
2288                         namespace = tmp_ns;
2289         }
2290
2291         return namespace;
2292 }
2293
2294 static IMAPNameSpace *imap_find_namespace(IMAPFolder *folder,
2295                                           const gchar *path)
2296 {
2297         IMAPNameSpace *namespace;
2298
2299         g_return_val_if_fail(folder != NULL, NULL);
2300
2301         namespace = imap_find_namespace_from_list(folder->ns_personal, path);
2302         if (namespace) return namespace;
2303         namespace = imap_find_namespace_from_list(folder->ns_others, path);
2304         if (namespace) return namespace;
2305         namespace = imap_find_namespace_from_list(folder->ns_shared, path);
2306         if (namespace) return namespace;
2307
2308         return NULL;
2309 }
2310
2311 static gchar imap_get_path_separator(IMAPFolder *folder, const gchar *path)
2312 {
2313         IMAPNameSpace *namespace;
2314         gchar separator = '/';
2315
2316         namespace = imap_find_namespace(folder, path);
2317         if (namespace && namespace->separator)
2318                 separator = namespace->separator;
2319
2320         return separator;
2321 }
2322
2323 static gchar *imap_get_real_path(IMAPFolder *folder, const gchar *path)
2324 {
2325         gchar *real_path;
2326         gchar separator;
2327
2328         g_return_val_if_fail(folder != NULL, NULL);
2329         g_return_val_if_fail(path != NULL, NULL);
2330
2331         real_path = imap_utf8_to_modified_utf7(path);
2332         separator = imap_get_path_separator(folder, path);
2333         imap_path_separator_subst(real_path, separator);
2334
2335         return real_path;
2336 }
2337
2338 static gchar *imap_parse_atom(SockInfo *sock, gchar *src,
2339                               gchar *dest, gint dest_len, GString *str)
2340 {
2341         gchar *cur_pos = src;
2342         gchar *nextline;
2343
2344         g_return_val_if_fail(str != NULL, cur_pos);
2345
2346         /* read the next line if the current response buffer is empty */
2347         while (isspace(*(guchar *)cur_pos)) cur_pos++;
2348         while (*cur_pos == '\0') {
2349                 if ((nextline = imap_getline(sock)) == NULL)
2350                         return cur_pos;
2351                 g_string_assign(str, nextline);
2352                 cur_pos = str->str;
2353                 strretchomp(nextline);
2354                 /* log_print("IMAP4< %s\n", nextline); */
2355                 debug_print("IMAP4< %s\n", nextline);
2356                 g_free(nextline);
2357
2358                 while (isspace(*(guchar *)cur_pos)) cur_pos++;
2359         }
2360
2361         if (!strncmp(cur_pos, "NIL", 3)) {
2362                 *dest = '\0';
2363                 cur_pos += 3;
2364         } else if (*cur_pos == '\"') {
2365                 gchar *p;
2366
2367                 p = get_quoted(cur_pos, '\"', dest, dest_len);
2368                 cur_pos = p ? p : cur_pos + 2;
2369         } else if (*cur_pos == '{') {
2370                 gchar buf[32];
2371                 gint len;
2372                 gint line_len = 0;
2373
2374                 cur_pos = strchr_cpy(cur_pos + 1, '}', buf, sizeof(buf));
2375                 len = atoi(buf);
2376                 g_return_val_if_fail(len >= 0, cur_pos);
2377
2378                 g_string_truncate(str, 0);
2379                 cur_pos = str->str;
2380
2381                 do {
2382                         if ((nextline = imap_getline(sock)) == NULL)
2383                                 return cur_pos;
2384                         line_len += strlen(nextline);
2385                         g_string_append(str, nextline);
2386                         cur_pos = str->str;
2387                         strretchomp(nextline);
2388                         /* log_print("IMAP4< %s\n", nextline); */
2389                         debug_print("IMAP4< %s\n", nextline);
2390                         g_free(nextline);
2391                 } while (line_len < len);
2392
2393                 memcpy(dest, cur_pos, MIN(len, dest_len - 1));
2394                 dest[MIN(len, dest_len - 1)] = '\0';
2395                 cur_pos += len;
2396         }
2397
2398         return cur_pos;
2399 }
2400
2401 static gchar *imap_get_header(SockInfo *sock, gchar *cur_pos, gchar **headers,
2402                               GString *str)
2403 {
2404         gchar *nextline;
2405         gchar buf[32];
2406         gint len;
2407         gint block_len = 0;
2408
2409         *headers = NULL;
2410
2411         g_return_val_if_fail(str != NULL, cur_pos);
2412
2413         while (isspace(*(guchar *)cur_pos)) cur_pos++;
2414
2415         g_return_val_if_fail(*cur_pos == '{', cur_pos);
2416
2417         cur_pos = strchr_cpy(cur_pos + 1, '}', buf, sizeof(buf));
2418         len = atoi(buf);
2419         g_return_val_if_fail(len >= 0, cur_pos);
2420
2421         g_string_truncate(str, 0);
2422         cur_pos = str->str;
2423
2424         do {
2425                 if ((nextline = sock_getline(sock)) == NULL)
2426                         return cur_pos;
2427                 block_len += strlen(nextline);
2428                 g_string_append(str, nextline);
2429                 cur_pos = str->str;
2430                 strretchomp(nextline);
2431                 /* debug_print("IMAP4< %s\n", nextline); */
2432                 g_free(nextline);
2433         } while (block_len < len);
2434
2435         debug_print("IMAP4< [contents of RFC822.HEADER]\n");
2436
2437         *headers = g_strndup(cur_pos, len);
2438         cur_pos += len;
2439
2440         while (isspace(*(guchar *)cur_pos)) cur_pos++;
2441         while (*cur_pos == '\0') {
2442                 if ((nextline = sock_getline(sock)) == NULL)
2443                         return cur_pos;
2444                 g_string_assign(str, nextline);
2445                 cur_pos = str->str;
2446                 strretchomp(nextline);
2447                 debug_print("IMAP4< %s\n", nextline);
2448                 g_free(nextline);
2449
2450                 while (isspace(*(guchar *)cur_pos)) cur_pos++;
2451         }
2452
2453         return cur_pos;
2454 }
2455
2456 static MsgFlags imap_parse_flags(const gchar *flag_str)  
2457 {
2458         const gchar *p = flag_str;
2459         MsgFlags flags = {0, 0};
2460
2461         flags.perm_flags = MSG_UNREAD;
2462
2463         while ((p = strchr(p, '\\')) != NULL) {
2464                 p++;
2465
2466                 if (g_ascii_strncasecmp(p, "Recent", 6) == 0 && MSG_IS_UNREAD(flags)) {
2467                         MSG_SET_PERM_FLAGS(flags, MSG_NEW);
2468                 } else if (g_ascii_strncasecmp(p, "Seen", 4) == 0) {
2469                         MSG_UNSET_PERM_FLAGS(flags, MSG_NEW|MSG_UNREAD);
2470                 } else if (g_ascii_strncasecmp(p, "Deleted", 7) == 0) {
2471                         MSG_SET_PERM_FLAGS(flags, MSG_DELETED);
2472                 } else if (g_ascii_strncasecmp(p, "Flagged", 7) == 0) {
2473                         MSG_SET_PERM_FLAGS(flags, MSG_MARKED);
2474                 } else if (g_ascii_strncasecmp(p, "Answered", 8) == 0) {
2475                         MSG_SET_PERM_FLAGS(flags, MSG_REPLIED);
2476                 }
2477         }
2478
2479         return flags;
2480 }
2481
2482 static MsgInfo *imap_parse_envelope(SockInfo *sock, FolderItem *item,
2483                                     GString *line_str)
2484 {
2485         gchar buf[IMAPBUFSIZE];
2486         MsgInfo *msginfo = NULL;
2487         gchar *cur_pos;
2488         gint msgnum;
2489         guint32 uid = 0;
2490         size_t size = 0;
2491         MsgFlags flags = {0, 0}, imap_flags = {0, 0};
2492
2493         g_return_val_if_fail(line_str != NULL, NULL);
2494         g_return_val_if_fail(line_str->str[0] == '*' &&
2495                              line_str->str[1] == ' ', NULL);
2496
2497         MSG_SET_TMP_FLAGS(flags, MSG_IMAP);
2498         if (item->stype == F_QUEUE) {
2499                 MSG_SET_TMP_FLAGS(flags, MSG_QUEUED);
2500         } else if (item->stype == F_DRAFT) {
2501                 MSG_SET_TMP_FLAGS(flags, MSG_DRAFT);
2502         }
2503
2504         cur_pos = line_str->str + 2;
2505
2506 #define PARSE_ONE_ELEMENT(ch)                                   \
2507 {                                                               \
2508         cur_pos = strchr_cpy(cur_pos, ch, buf, sizeof(buf));    \
2509         if (cur_pos == NULL) {                                  \
2510                 g_warning("cur_pos == NULL\n");                 \
2511                 procmsg_msginfo_free(msginfo);                  \
2512                 return NULL;                                    \
2513         }                                                       \
2514 }
2515
2516         PARSE_ONE_ELEMENT(' ');
2517         msgnum = atoi(buf);
2518
2519         PARSE_ONE_ELEMENT(' ');
2520         g_return_val_if_fail(!strcmp(buf, "FETCH"), NULL);
2521
2522         g_return_val_if_fail(*cur_pos == '(', NULL);
2523         cur_pos++;
2524
2525         while (*cur_pos != '\0' && *cur_pos != ')') {
2526                 while (*cur_pos == ' ') cur_pos++;
2527
2528                 if (!strncmp(cur_pos, "UID ", 4)) {
2529                         cur_pos += 4;
2530                         uid = strtoul(cur_pos, &cur_pos, 10);
2531                 } else if (!strncmp(cur_pos, "FLAGS ", 6)) {
2532                         cur_pos += 6;
2533                         if (*cur_pos != '(') {
2534                                 g_warning("*cur_pos != '('\n");
2535                                 procmsg_msginfo_free(msginfo);
2536                                 return NULL;
2537                         }
2538                         cur_pos++;
2539                         PARSE_ONE_ELEMENT(')');
2540                         imap_flags = imap_parse_flags(buf);
2541                 } else if (!strncmp(cur_pos, "RFC822.SIZE ", 12)) {
2542                         cur_pos += 12;
2543                         size = strtol(cur_pos, &cur_pos, 10);
2544                 } else if (!strncmp(cur_pos, "RFC822.HEADER ", 14)) {
2545                         gchar *headers;
2546
2547                         cur_pos += 14;
2548                         cur_pos = imap_get_header(sock, cur_pos, &headers,
2549                                                   line_str);
2550                         msginfo = procheader_parse_str(headers, flags, FALSE, FALSE);
2551                         g_free(headers);
2552                 } else {
2553                         g_warning("invalid FETCH response: %s\n", cur_pos);
2554                         break;
2555                 }
2556         }
2557
2558         if (msginfo) {
2559                 msginfo->msgnum = uid;
2560                 msginfo->size = size;
2561                 msginfo->flags.tmp_flags |= imap_flags.tmp_flags;
2562                 msginfo->flags.perm_flags = imap_flags.perm_flags;
2563         }
2564
2565         return msginfo;
2566 }
2567
2568 static gchar *imap_get_flag_str(IMAPFlags flags)
2569 {
2570         GString *str;
2571         gchar *ret;
2572
2573         str = g_string_new(NULL);
2574
2575         if (IMAP_IS_SEEN(flags))        g_string_append(str, "\\Seen ");
2576         if (IMAP_IS_ANSWERED(flags))    g_string_append(str, "\\Answered ");
2577         if (IMAP_IS_FLAGGED(flags))     g_string_append(str, "\\Flagged ");
2578         if (IMAP_IS_DELETED(flags))     g_string_append(str, "\\Deleted ");
2579         if (IMAP_IS_DRAFT(flags))       g_string_append(str, "\\Draft");
2580
2581         if (str->len > 0 && str->str[str->len - 1] == ' ')
2582                 g_string_truncate(str, str->len - 1);
2583
2584         ret = str->str;
2585         g_string_free(str, FALSE);
2586
2587         return ret;
2588 }
2589
2590 static gint imap_set_message_flags(IMAPSession *session,
2591                                    MsgNumberList *numlist,
2592                                    IMAPFlags flags,
2593                                    gboolean is_set)
2594 {
2595         gchar *cmd;
2596         gchar *flag_str;
2597         gint ok = 0;
2598         GSList *seq_list;
2599         IMAPSet imapset;
2600
2601         flag_str = imap_get_flag_str(flags);
2602         cmd = g_strconcat(is_set ? "+FLAGS.SILENT (" : "-FLAGS.SILENT (",
2603                           flag_str, ")", NULL);
2604         g_free(flag_str);
2605
2606         seq_list = imap_get_seq_set_from_numlist(numlist);
2607         imapset = get_seq_set_from_seq_list(seq_list);
2608
2609         ok = imap_cmd_store(session, imapset, cmd);
2610         
2611         g_free(imapset);
2612         imap_seq_set_free(seq_list);
2613         g_free(cmd);
2614
2615         return ok;
2616 }
2617
2618 typedef struct _select_data {
2619         IMAPSession *session;
2620         gchar *real_path;
2621         gint *exists;
2622         gint *recent;
2623         gint *unseen;
2624         guint32 *uid_validity;
2625         gboolean done;
2626 } select_data;
2627
2628 static void *imap_select_thread(void *data)
2629 {
2630         select_data *stuff = (select_data *)data;
2631         IMAPSession *session = stuff->session;
2632         gchar *real_path = stuff->real_path;
2633         gint *exists = stuff->exists;
2634         gint *recent = stuff->recent;
2635         gint *unseen = stuff->unseen;
2636         guint32 *uid_validity = stuff->uid_validity;
2637         gint ok;
2638         
2639         ok = imap_cmd_select(session, real_path,
2640                              exists, recent, unseen, uid_validity, TRUE);
2641         stuff->done = TRUE;
2642         return GINT_TO_POINTER(ok);
2643 }
2644                 
2645 static gint imap_select(IMAPSession *session, IMAPFolder *folder,
2646                         const gchar *path,
2647                         gint *exists, gint *recent, gint *unseen,
2648                         guint32 *uid_validity, gboolean block)
2649 {
2650         gchar *real_path;
2651         gint ok;
2652         gint exists_, recent_, unseen_;
2653         guint32 uid_validity_;
2654         
2655         if (!exists || !recent || !unseen || !uid_validity) {
2656                 if (session->mbox && strcmp(session->mbox, path) == 0)
2657                         return IMAP_SUCCESS;
2658                 exists = &exists_;
2659                 recent = &recent_;
2660                 unseen = &unseen_;
2661                 uid_validity = &uid_validity_;
2662         }
2663
2664         g_free(session->mbox);
2665         session->mbox = NULL;
2666
2667         real_path = imap_get_real_path(folder, path);
2668         
2669 #if (defined USE_PTHREAD && defined __GLIBC__ && (__GLIBC__ > 2 || (__GLIBC__ == 2 && __GLIBC_MINOR__ >= 3)))
2670         if (block == FALSE) {
2671                 select_data *data = g_new0(select_data, 1);
2672                 void *tmp;
2673                 pthread_t pt;
2674                 data->session = session;
2675                 data->real_path = real_path;
2676                 data->exists = exists;
2677                 data->recent = recent;
2678                 data->unseen = unseen;
2679                 data->uid_validity = uid_validity;
2680                 data->done = FALSE;
2681                 
2682                 if (pthread_create(&pt, PTHREAD_CREATE_JOINABLE,
2683                                 imap_select_thread, data) != 0) {
2684                         ok = GPOINTER_TO_INT(imap_select_thread(data));
2685                         g_free(data);
2686                 } else {
2687                         debug_print("+++waiting for imap_select_thread...\n");
2688                         while(!data->done) {
2689                                 /* don't let the interface freeze while waiting */
2690                                 sylpheed_do_idle();
2691                         }
2692                         debug_print("---imap_select_thread done\n");
2693
2694                         /* get the thread's return value and clean its resources */
2695                         pthread_join(pt, &tmp);
2696                         ok = GPOINTER_TO_INT(tmp);
2697                         g_free(data);
2698                 }
2699         } else {
2700                 ok = imap_cmd_select(session, real_path,
2701                              exists, recent, unseen, uid_validity, block);
2702         }
2703 #else
2704         ok = imap_cmd_select(session, real_path,
2705                              exists, recent, unseen, uid_validity, block);
2706 #endif
2707         if (ok != IMAP_SUCCESS)
2708                 log_warning(_("can't select folder: %s\n"), real_path);
2709         else {
2710                 session->mbox = g_strdup(path);
2711                 session->folder_content_changed = FALSE;
2712         }
2713         g_free(real_path);
2714
2715         return ok;
2716 }
2717
2718 #define THROW(err) { ok = err; goto catch; }
2719
2720 static gint imap_status(IMAPSession *session, IMAPFolder *folder,
2721                         const gchar *path,
2722                         gint *messages, gint *recent,
2723                         guint32 *uid_next, guint32 *uid_validity,
2724                         gint *unseen, gboolean block)
2725 {
2726         gchar *real_path;
2727         gchar *real_path_;
2728         gint ok;
2729         GPtrArray *argbuf = NULL;
2730         gchar *str;
2731
2732         if (messages && recent && uid_next && uid_validity && unseen) {
2733                 *messages = *recent = *uid_next = *uid_validity = *unseen = 0;
2734                 argbuf = g_ptr_array_new();
2735         }
2736
2737         real_path = imap_get_real_path(folder, path);
2738         QUOTE_IF_REQUIRED(real_path_, real_path);
2739         imap_gen_send(session, "STATUS %s "
2740                           "(MESSAGES RECENT UIDNEXT UIDVALIDITY UNSEEN)",
2741                           real_path_);
2742
2743         ok = imap_cmd_ok_with_block(session, argbuf, block);
2744         if (ok != IMAP_SUCCESS || !argbuf) THROW(ok);
2745
2746         str = search_array_str(argbuf, "STATUS");
2747         if (!str) THROW(IMAP_ERROR);
2748
2749         str = strchr(str, '(');
2750         if (!str) THROW(IMAP_ERROR);
2751         str++;
2752         while (*str != '\0' && *str != ')') {
2753                 while (*str == ' ') str++;
2754
2755                 if (!strncmp(str, "MESSAGES ", 9)) {
2756                         str += 9;
2757                         *messages = strtol(str, &str, 10);
2758                 } else if (!strncmp(str, "RECENT ", 7)) {
2759                         str += 7;
2760                         *recent = strtol(str, &str, 10);
2761                 } else if (!strncmp(str, "UIDNEXT ", 8)) {
2762                         str += 8;
2763                         *uid_next = strtoul(str, &str, 10);
2764                 } else if (!strncmp(str, "UIDVALIDITY ", 12)) {
2765                         str += 12;
2766                         *uid_validity = strtoul(str, &str, 10);
2767                 } else if (!strncmp(str, "UNSEEN ", 7)) {
2768                         str += 7;
2769                         *unseen = strtol(str, &str, 10);
2770                 } else {
2771                         g_warning("invalid STATUS response: %s\n", str);
2772                         break;
2773                 }
2774         }
2775
2776 catch:
2777         g_free(real_path);
2778         if (argbuf) {
2779                 ptr_array_free_strings(argbuf);
2780                 g_ptr_array_free(argbuf, TRUE);
2781         }
2782
2783         return ok;
2784 }
2785
2786 #undef THROW
2787
2788 static gboolean imap_has_capability(IMAPSession *session, const gchar *cap)
2789 {
2790         gchar **p;
2791         
2792         for (p = session->capability; *p != NULL; ++p) {
2793                 if (!g_ascii_strcasecmp(*p, cap))
2794                         return TRUE;
2795         }
2796
2797         return FALSE;
2798 }
2799
2800 static void imap_free_capabilities(IMAPSession *session)
2801 {
2802         g_strfreev(session->capability);
2803         session->capability = NULL;
2804 }
2805
2806 /* low-level IMAP4rev1 commands */
2807
2808 static gint imap_cmd_authenticate(IMAPSession *session, const gchar *user,
2809                                   const gchar *pass, IMAPAuthType type)
2810 {
2811         gchar *auth_type;
2812         gint ok;
2813         gchar *buf = NULL;
2814         gchar *challenge;
2815         gint challenge_len;
2816         gchar hexdigest[33];
2817         gchar *response;
2818         gchar *response64;
2819
2820         auth_type = "CRAM-MD5";
2821
2822         imap_gen_send(session, "AUTHENTICATE %s", auth_type);
2823         ok = imap_gen_recv(session, &buf);
2824         if (ok != IMAP_SUCCESS || buf[0] != '+' || buf[1] != ' ') {
2825                 g_free(buf);
2826                 return IMAP_ERROR;
2827         }
2828
2829         challenge = g_malloc(strlen(buf + 2) + 1);
2830         challenge_len = base64_decode(challenge, buf + 2, -1);
2831         challenge[challenge_len] = '\0';
2832         g_free(buf);
2833         log_print("IMAP< [Decoded: %s]\n", challenge);
2834
2835         md5_hex_hmac(hexdigest, challenge, challenge_len, pass, strlen(pass));
2836         g_free(challenge);
2837
2838         response = g_strdup_printf("%s %s", user, hexdigest);
2839         log_print("IMAP> [Encoded: %s]\n", response);
2840         response64 = g_malloc((strlen(response) + 3) * 2 + 1);
2841         base64_encode(response64, response, strlen(response));
2842         g_free(response);
2843
2844         log_print("IMAP> %s\n", response64);
2845         sock_puts(SESSION(session)->sock, response64);
2846         ok = imap_cmd_ok(session, NULL);
2847         if (ok != IMAP_SUCCESS)
2848                 log_warning(_("IMAP4 authentication failed.\n"));
2849
2850         return ok;
2851 }
2852
2853 static gint imap_cmd_login(IMAPSession *session,
2854                            const gchar *user, const gchar *pass)
2855 {
2856         gchar *user_, *pass_;
2857         gint ok;
2858
2859         QUOTE_IF_REQUIRED(user_, user);
2860         QUOTE_IF_REQUIRED(pass_, pass);
2861         imap_gen_send(session, "LOGIN %s %s", user_, pass_);
2862
2863         ok = imap_cmd_ok(session, NULL);
2864         if (ok != IMAP_SUCCESS)
2865                 log_warning(_("IMAP4 login failed.\n"));
2866
2867         return ok;
2868 }
2869
2870 static gint imap_cmd_logout(IMAPSession *session)
2871 {
2872         imap_gen_send(session, "LOGOUT");
2873         return imap_cmd_ok(session, NULL);
2874 }
2875
2876 static gint imap_cmd_noop(IMAPSession *session)
2877 {
2878         imap_gen_send(session, "NOOP");
2879         return imap_cmd_ok(session, NULL);
2880 }
2881
2882 #if USE_OPENSSL
2883 static gint imap_cmd_starttls(IMAPSession *session)
2884 {
2885         imap_gen_send(session, "STARTTLS");
2886         return imap_cmd_ok(session, NULL);
2887 }
2888 #endif
2889
2890 #define THROW(err) { ok = err; goto catch; }
2891
2892 static gint imap_cmd_namespace(IMAPSession *session, gchar **ns_str)
2893 {
2894         gint ok;
2895         GPtrArray *argbuf;
2896         gchar *str;
2897
2898         argbuf = g_ptr_array_new();
2899
2900         imap_gen_send(session, "NAMESPACE");
2901         if ((ok = imap_cmd_ok(session, argbuf)) != IMAP_SUCCESS) THROW(ok);
2902
2903         str = search_array_str(argbuf, "NAMESPACE");
2904         if (!str) THROW(IMAP_ERROR);
2905
2906         *ns_str = g_strdup(str);
2907
2908 catch:
2909         ptr_array_free_strings(argbuf);
2910         g_ptr_array_free(argbuf, TRUE);
2911
2912         return ok;
2913 }
2914
2915 #undef THROW
2916
2917 static gint imap_cmd_list(IMAPSession *session, const gchar *ref,
2918                           const gchar *mailbox, GPtrArray *argbuf)
2919 {
2920         gchar *ref_, *mailbox_;
2921
2922         if (!ref) ref = "\"\"";
2923         if (!mailbox) mailbox = "\"\"";
2924
2925         QUOTE_IF_REQUIRED(ref_, ref);
2926         QUOTE_IF_REQUIRED(mailbox_, mailbox);
2927         imap_gen_send(session, "LIST %s %s", ref_, mailbox_);
2928
2929         return imap_cmd_ok(session, argbuf);
2930 }
2931
2932 #define THROW goto catch
2933
2934 static gint imap_cmd_do_select(IMAPSession *session, const gchar *folder,
2935                                gboolean examine,
2936                                gint *exists, gint *recent, gint *unseen,
2937                                guint32 *uid_validity, gboolean block)
2938 {
2939         gint ok;
2940         gchar *resp_str;
2941         GPtrArray *argbuf;
2942         gchar *select_cmd;
2943         gchar *folder_;
2944         unsigned int uid_validity_;
2945
2946         *exists = *recent = *unseen = *uid_validity = 0;
2947         argbuf = g_ptr_array_new();
2948
2949         if (examine)
2950                 select_cmd = "EXAMINE";
2951         else
2952                 select_cmd = "SELECT";
2953
2954         QUOTE_IF_REQUIRED(folder_, folder);
2955         imap_gen_send(session, "%s %s", select_cmd, folder_);
2956
2957         if ((ok = imap_cmd_ok_with_block(session, argbuf, block)) != IMAP_SUCCESS) THROW;
2958
2959         resp_str = search_array_contain_str(argbuf, "EXISTS");
2960         if (resp_str) {
2961                 if (sscanf(resp_str,"%d EXISTS", exists) != 1) {
2962                         g_warning("imap_cmd_select(): invalid EXISTS line.\n");
2963                         THROW;
2964                 }
2965         }
2966
2967         resp_str = search_array_contain_str(argbuf, "RECENT");
2968         if (resp_str) {
2969                 if (sscanf(resp_str, "%d RECENT", recent) != 1) {
2970                         g_warning("imap_cmd_select(): invalid RECENT line.\n");
2971                         THROW;
2972                 }
2973         }
2974
2975         resp_str = search_array_contain_str(argbuf, "UIDVALIDITY");
2976         if (resp_str) {
2977                 if (sscanf(resp_str, "OK [UIDVALIDITY %u] ", &uid_validity_)
2978                     != 1) {
2979                         g_warning("imap_cmd_select(): invalid UIDVALIDITY line.\n");
2980                         THROW;
2981                 }
2982                 *uid_validity = uid_validity_;
2983         }
2984
2985         resp_str = search_array_contain_str(argbuf, "UNSEEN");
2986         if (resp_str) {
2987                 if (sscanf(resp_str, "OK [UNSEEN %d] ", unseen) != 1) {
2988                         g_warning("imap_cmd_select(): invalid UNSEEN line.\n");
2989                         THROW;
2990                 }
2991         }
2992
2993 catch:
2994         ptr_array_free_strings(argbuf);
2995         g_ptr_array_free(argbuf, TRUE);
2996
2997         return ok;
2998 }
2999
3000 static gint imap_cmd_select(IMAPSession *session, const gchar *folder,
3001                             gint *exists, gint *recent, gint *unseen,
3002                             guint32 *uid_validity, gboolean block)
3003 {
3004         return imap_cmd_do_select(session, folder, FALSE,
3005                                   exists, recent, unseen, uid_validity, block);
3006 }
3007
3008 static gint imap_cmd_examine(IMAPSession *session, const gchar *folder,
3009                              gint *exists, gint *recent, gint *unseen,
3010                              guint32 *uid_validity, gboolean block)
3011 {
3012         return imap_cmd_do_select(session, folder, TRUE,
3013                                   exists, recent, unseen, uid_validity, block);
3014 }
3015
3016 #undef THROW
3017
3018 static gint imap_cmd_create(IMAPSession *session, const gchar *folder)
3019 {
3020         gchar *folder_;
3021
3022         QUOTE_IF_REQUIRED(folder_, folder);
3023         imap_gen_send(session, "CREATE %s", folder_);
3024
3025         return imap_cmd_ok(session, NULL);
3026 }
3027
3028 static gint imap_cmd_rename(IMAPSession *session, const gchar *old_folder,
3029                             const gchar *new_folder)
3030 {
3031         gchar *old_folder_, *new_folder_;
3032
3033         QUOTE_IF_REQUIRED(old_folder_, old_folder);
3034         QUOTE_IF_REQUIRED(new_folder_, new_folder);
3035         imap_gen_send(session, "RENAME %s %s", old_folder_, new_folder_);
3036
3037         return imap_cmd_ok(session, NULL);
3038 }
3039
3040 static gint imap_cmd_delete(IMAPSession *session, const gchar *folder)
3041 {
3042         gchar *folder_;
3043
3044         QUOTE_IF_REQUIRED(folder_, folder);
3045         imap_gen_send(session, "DELETE %s", folder_);
3046
3047         return imap_cmd_ok(session, NULL);
3048 }
3049
3050 static gint imap_cmd_search(IMAPSession *session, const gchar *criteria, 
3051                             GSList **list, gboolean block)
3052 {
3053         gint ok;
3054         gchar *uidlist;
3055         GPtrArray *argbuf;
3056
3057         g_return_val_if_fail(criteria != NULL, IMAP_ERROR);
3058         g_return_val_if_fail(list != NULL, IMAP_ERROR);
3059
3060         *list = NULL;
3061         
3062         argbuf = g_ptr_array_new();
3063         imap_gen_send(session, "UID SEARCH %s", criteria);
3064
3065         ok = imap_cmd_ok_with_block(session, argbuf, block);
3066         if (ok != IMAP_SUCCESS) {
3067                 ptr_array_free_strings(argbuf);
3068                 g_ptr_array_free(argbuf, TRUE);
3069                 return ok;
3070         }
3071
3072         if ((uidlist = search_array_str(argbuf, "SEARCH ")) != NULL) {
3073                 gchar **strlist, **p;
3074
3075                 strlist = g_strsplit(uidlist + 7, " ", 0);
3076                 for (p = strlist; *p != NULL; ++p) {
3077                         guint msgnum;
3078
3079                         if (sscanf(*p, "%u", &msgnum) == 1)
3080                                 *list = g_slist_append(*list, GINT_TO_POINTER(msgnum));
3081                 }
3082                 g_strfreev(strlist);
3083         }
3084         ptr_array_free_strings(argbuf);
3085         g_ptr_array_free(argbuf, TRUE);
3086
3087         return IMAP_SUCCESS;
3088 }
3089
3090 typedef struct _fetch_data {
3091         IMAPSession *session;
3092         guint32 uid;
3093         const gchar *filename;
3094         gboolean done;
3095 } fetch_data;
3096
3097 static void *imap_cmd_fetch_thread(void *data)
3098 {
3099         fetch_data *stuff = (fetch_data *)data;
3100         IMAPSession *session = stuff->session;
3101         guint32 uid = stuff->uid;
3102         const gchar *filename = stuff->filename;
3103         
3104         gint ok;
3105         gchar *buf = NULL;
3106         gchar *cur_pos;
3107         gchar size_str[32];
3108         glong size_num;
3109
3110         if (filename == NULL) {
3111                 stuff->done = TRUE;
3112                 return GINT_TO_POINTER(IMAP_ERROR);
3113         }
3114
3115         imap_gen_send(session, "UID FETCH %d BODY.PEEK[]", uid);
3116
3117         while ((ok = imap_gen_recv_block(session, &buf)) == IMAP_SUCCESS) {
3118                 if (buf[0] != '*' || buf[1] != ' ') {
3119                         g_free(buf);
3120                         stuff->done = TRUE;
3121                         return GINT_TO_POINTER(IMAP_ERROR);
3122                 }
3123                 if (strstr(buf, "FETCH") != NULL) break;
3124                 g_free(buf);
3125         }
3126         if (ok != IMAP_SUCCESS) {
3127                 g_free(buf);
3128                 stuff->done = TRUE;
3129                 return GINT_TO_POINTER(ok);
3130         }
3131
3132 #define RETURN_ERROR_IF_FAIL(cond)      \
3133         if (!(cond)) {                  \
3134                 g_free(buf);            \
3135                 stuff->done = TRUE;     \
3136                 return GINT_TO_POINTER(IMAP_ERROR);     \
3137         }
3138
3139         cur_pos = strchr(buf, '{');
3140         RETURN_ERROR_IF_FAIL(cur_pos != NULL);
3141         cur_pos = strchr_cpy(cur_pos + 1, '}', size_str, sizeof(size_str));
3142         RETURN_ERROR_IF_FAIL(cur_pos != NULL);
3143         size_num = atol(size_str);
3144         RETURN_ERROR_IF_FAIL(size_num >= 0);
3145
3146         RETURN_ERROR_IF_FAIL(*cur_pos == '\0');
3147
3148 #undef RETURN_ERROR_IF_FAIL
3149
3150         g_free(buf);
3151
3152         if (recv_bytes_write_to_file(SESSION(session)->sock,
3153                                      size_num, filename) != 0) {
3154                 stuff->done = TRUE;
3155                 return GINT_TO_POINTER(IMAP_ERROR);
3156         }
3157         if (imap_gen_recv_block(session, &buf) != IMAP_SUCCESS) {
3158                 g_free(buf);
3159                 stuff->done = TRUE;
3160                 return GINT_TO_POINTER(IMAP_ERROR);
3161         }
3162
3163         if (buf[0] == '\0' || buf[strlen(buf) - 1] != ')') {
3164                 g_free(buf);
3165                 stuff->done = TRUE;
3166                 return GINT_TO_POINTER(IMAP_ERROR);
3167         }
3168         g_free(buf);
3169
3170         ok = imap_cmd_ok_block(session, NULL);
3171
3172         stuff->done = TRUE;
3173         return GINT_TO_POINTER(ok);
3174 }
3175
3176 static gint imap_cmd_fetch(IMAPSession *session, guint32 uid,
3177                                 const gchar *filename)
3178 {
3179         fetch_data *data = g_new0(fetch_data, 1);
3180         int result = 0;
3181 #ifdef USE_PTHREAD
3182         void *tmp;
3183         pthread_t pt;
3184 #endif
3185         data->done = FALSE;
3186         data->session = session;
3187         data->uid = uid;
3188         data->filename = filename;
3189
3190         if (prefs_common.work_offline && !imap_gtk_should_override()) {
3191                 g_free(data);
3192                 return -1;
3193         }
3194
3195 #if (defined USE_PTHREAD && defined __GLIBC__ && (__GLIBC__ > 2 || (__GLIBC__ == 2 && __GLIBC_MINOR__ >= 3)))
3196         if (pthread_create(&pt, PTHREAD_CREATE_JOINABLE,
3197                         imap_cmd_fetch_thread, data) != 0) {
3198                 result = GPOINTER_TO_INT(imap_cmd_fetch_thread(data));
3199                 g_free(data);
3200                 return result;
3201         }
3202         debug_print("+++waiting for imap_cmd_fetch_thread...\n");
3203         while(!data->done) {
3204                 /* don't let the interface freeze while waiting */
3205                 sylpheed_do_idle();
3206         }
3207         debug_print("---imap_cmd_fetch_thread done\n");
3208
3209         /* get the thread's return value and clean its resources */
3210         pthread_join(pt, &tmp);
3211         result = GPOINTER_TO_INT(tmp);
3212 #else
3213         result = GPOINTER_TO_INT(imap_cmd_fetch_thread(data));
3214 #endif
3215         g_free(data);
3216         return result;
3217 }
3218
3219 static gint imap_cmd_append(IMAPSession *session, const gchar *destfolder,
3220                             const gchar *file, IMAPFlags flags, 
3221                             guint32 *new_uid)
3222 {
3223         gint ok;
3224         gint size;
3225         gchar *destfolder_;
3226         gchar *flag_str;
3227         unsigned int new_uid_;
3228         gchar *ret = NULL;
3229         gchar buf[BUFFSIZE];
3230         FILE *fp;
3231         GPtrArray *argbuf;
3232         gchar *resp_str;
3233
3234         g_return_val_if_fail(file != NULL, IMAP_ERROR);
3235
3236         size = get_file_size_as_crlf(file);
3237         if ((fp = fopen(file, "rb")) == NULL) {
3238                 FILE_OP_ERROR(file, "fopen");
3239                 return -1;
3240         }
3241         QUOTE_IF_REQUIRED(destfolder_, destfolder);
3242         flag_str = imap_get_flag_str(flags);
3243         imap_gen_send(session, "APPEND %s (%s) {%d}", 
3244                       destfolder_, flag_str, size);
3245         g_free(flag_str);
3246
3247         ok = imap_gen_recv(session, &ret);
3248         if (ok != IMAP_SUCCESS || ret[0] != '+' || ret[1] != ' ') {
3249                 log_warning(_("can't append %s to %s\n"), file, destfolder_);
3250                 g_free(ret);
3251                 fclose(fp);
3252                 return IMAP_ERROR;
3253         }
3254         g_free(ret);
3255
3256         log_print("IMAP4> %s\n", "(sending file...)");
3257
3258         while (fgets(buf, sizeof(buf), fp) != NULL) {
3259                 strretchomp(buf);
3260                 if (sock_puts(SESSION(session)->sock, buf) < 0) {
3261                         fclose(fp);
3262                         return -1;
3263                 }
3264         }
3265
3266         if (ferror(fp)) {
3267                 FILE_OP_ERROR(file, "fgets");
3268                 fclose(fp);
3269                 return -1;
3270         }
3271
3272         sock_puts(SESSION(session)->sock, "");
3273
3274         fclose(fp);
3275
3276         if (new_uid != NULL)
3277                 *new_uid = 0;
3278
3279         if (new_uid != NULL && session->uidplus) {
3280                 argbuf = g_ptr_array_new();
3281
3282                 ok = imap_cmd_ok(session, argbuf);
3283                 if ((ok == IMAP_SUCCESS) && (argbuf->len > 0)) {
3284                         resp_str = g_ptr_array_index(argbuf, argbuf->len - 1);
3285                         if (resp_str &&
3286                             sscanf(resp_str, "%*u OK [APPENDUID %*u %u]",
3287                                    &new_uid_) == 1) {
3288                                 *new_uid = new_uid_;
3289                         }
3290                 }
3291
3292                 ptr_array_free_strings(argbuf);
3293                 g_ptr_array_free(argbuf, TRUE);
3294         } else
3295                 ok = imap_cmd_ok(session, NULL);
3296
3297         if (ok != IMAP_SUCCESS)
3298                 log_warning(_("can't append message to %s\n"),
3299                             destfolder_);
3300
3301         return ok;
3302 }
3303
3304 static MsgNumberList *imapset_to_numlist(IMAPSet imapset)
3305 {
3306         gchar **ranges, **range;
3307         unsigned int low, high;
3308         MsgNumberList *uids = NULL;
3309         
3310         ranges = g_strsplit(imapset, ",", 0);
3311         for (range = ranges; *range != NULL; range++) {
3312                 if (sscanf(*range, "%u:%u", &low, &high) == 1)
3313                         uids = g_slist_prepend(uids, GINT_TO_POINTER(low));
3314                 else {
3315                         int i;
3316                         for (i = low; i <= high; i++)
3317                                 uids = g_slist_prepend(uids, GINT_TO_POINTER(i));
3318                 }
3319         }
3320         uids = g_slist_reverse(uids);
3321         g_strfreev(ranges);
3322
3323         return uids;
3324 }
3325
3326 static gint imap_cmd_copy(IMAPSession *session, const gchar *seq_set,
3327                           const gchar *destfolder, GRelation *uid_mapping)
3328 {
3329         gint ok;
3330         gchar *destfolder_;
3331         
3332         g_return_val_if_fail(session != NULL, IMAP_ERROR);
3333         g_return_val_if_fail(seq_set != NULL, IMAP_ERROR);
3334         g_return_val_if_fail(destfolder != NULL, IMAP_ERROR);
3335
3336         QUOTE_IF_REQUIRED(destfolder_, destfolder);
3337         imap_gen_send(session, "UID COPY %s %s", seq_set, destfolder_);
3338
3339         if (uid_mapping != NULL && session->uidplus) {
3340                 GPtrArray *reply;               
3341                 gchar *resp_str = NULL, *olduids_str, *newuids_str;
3342                 MsgNumberList *olduids, *old_cur, *newuids, *new_cur;
3343
3344                 reply = g_ptr_array_new();
3345                 ok = imap_cmd_ok(session, reply);
3346                 if ((ok == IMAP_SUCCESS) && (reply->len > 0)) {
3347                         resp_str = g_ptr_array_index(reply, reply->len - 1);
3348                         if (resp_str) {
3349                                 olduids_str = g_new0(gchar, strlen(resp_str));
3350                                 newuids_str = g_new0(gchar, strlen(resp_str));
3351                                 if (sscanf(resp_str, "%*s OK [COPYUID %*u %[0-9,:] %[0-9,:]]",
3352                                            olduids_str, newuids_str) == 2) {
3353                                         olduids = imapset_to_numlist(olduids_str);
3354                                         newuids = imapset_to_numlist(newuids_str);
3355
3356                                         old_cur = olduids;
3357                                         new_cur = newuids;
3358                                         while(old_cur != NULL && new_cur != NULL) {
3359                                                 g_relation_insert(uid_mapping, 
3360                                                                   GPOINTER_TO_INT(old_cur->data),
3361                                                                   GPOINTER_TO_INT(new_cur->data));
3362                                                 old_cur = g_slist_next(old_cur);
3363                                                 new_cur = g_slist_next(new_cur);
3364                                         }
3365
3366                                         g_slist_free(olduids);
3367                                         g_slist_free(newuids);
3368                                 }
3369                                 g_free(olduids_str);
3370                                 g_free(newuids_str);
3371                         }
3372                 }
3373                 ptr_array_free_strings(reply);
3374                 g_ptr_array_free(reply, TRUE);
3375         } else
3376                 ok = imap_cmd_ok(session, NULL);
3377
3378         if (ok != IMAP_SUCCESS)
3379                 log_warning(_("can't copy %s to %s\n"), seq_set, destfolder_);
3380
3381         return ok;
3382 }
3383
3384 gint imap_cmd_envelope(IMAPSession *session, IMAPSet set)
3385 {
3386         imap_gen_send
3387                 (session, "UID FETCH %s (UID FLAGS RFC822.SIZE RFC822.HEADER)",
3388                  set);
3389
3390         return IMAP_SUCCESS;
3391 }
3392
3393 static gint imap_cmd_store(IMAPSession *session, IMAPSet seq_set,
3394                            gchar *sub_cmd)
3395 {
3396         gint ok;
3397
3398         imap_gen_send(session, "UID STORE %s %s", seq_set, sub_cmd);
3399
3400         if ((ok = imap_cmd_ok(session, NULL)) != IMAP_SUCCESS) {
3401                 log_warning(_("error while imap command: STORE %s %s\n"),
3402                             seq_set, sub_cmd);
3403                 return ok;
3404         }
3405
3406         return IMAP_SUCCESS;
3407 }
3408
3409 static gint imap_cmd_expunge(IMAPSession *session, IMAPSet seq_set)
3410 {
3411         gint ok;
3412
3413         if (seq_set && session->uidplus)
3414                 imap_gen_send(session, "UID EXPUNGE %s", seq_set);
3415         else    
3416                 imap_gen_send(session, "EXPUNGE");
3417         if ((ok = imap_cmd_ok(session, NULL)) != IMAP_SUCCESS) {
3418                 log_warning(_("error while imap command: EXPUNGE\n"));
3419                 return ok;
3420         }
3421
3422         return IMAP_SUCCESS;
3423 }
3424
3425 static gint imap_cmd_close(IMAPSession *session)
3426 {
3427         gint ok;
3428
3429         imap_gen_send(session, "CLOSE");
3430         if ((ok = imap_cmd_ok(session, NULL)) != IMAP_SUCCESS)
3431                 log_warning(_("error while imap command: CLOSE\n"));
3432
3433         return ok;
3434 }
3435
3436 static gint imap_cmd_ok_with_block(IMAPSession *session, GPtrArray *argbuf, gboolean block)
3437 {
3438         gint ok = IMAP_SUCCESS;
3439         gchar *buf;
3440         gint cmd_num;
3441         gchar *data;
3442
3443         while ((ok = imap_gen_recv_with_block(session, &buf, block))
3444                == IMAP_SUCCESS) {
3445                 /* make sure data is long enough for any substring of buf */
3446                 data = alloca(strlen(buf) + 1);
3447
3448                 /* untagged line read */
3449                 if (buf[0] == '*' && buf[1] == ' ') {
3450                         gint num;
3451                         if (argbuf)
3452                                 g_ptr_array_add(argbuf, g_strdup(buf + 2));
3453
3454                         if (sscanf(buf + 2, "%d %s", &num, data) >= 2) {
3455                                 if (!strcmp(data, "EXISTS")) {
3456                                         session->exists = num;
3457                                         session->folder_content_changed = TRUE;
3458                                 }
3459
3460                                 if(!strcmp(data, "EXPUNGE")) {
3461                                         session->exists--;
3462                                         session->folder_content_changed = TRUE;
3463                                 }
3464                         }
3465                 /* tagged line with correct tag and OK response found */
3466                 } else if ((sscanf(buf, "%d %s", &cmd_num, data) >= 2) &&
3467                            (cmd_num == session->cmd_count) &&
3468                            !strcmp(data, "OK")) {
3469                         if (argbuf)
3470                                 g_ptr_array_add(argbuf, g_strdup(buf));
3471                         break;
3472                 /* everything else */
3473                 } else {
3474                         ok = IMAP_ERROR;
3475                         break;
3476                 }
3477                 g_free(buf);
3478         }
3479         g_free(buf);
3480
3481         return ok;
3482 }
3483 static gint imap_cmd_ok(IMAPSession *session, GPtrArray *argbuf)
3484 {
3485         return imap_cmd_ok_with_block(session, argbuf, FALSE);
3486 }
3487 static gint imap_cmd_ok_block(IMAPSession *session, GPtrArray *argbuf)
3488 {
3489         return imap_cmd_ok_with_block(session, argbuf, TRUE);
3490 }
3491 static void imap_gen_send(IMAPSession *session, const gchar *format, ...)
3492 {
3493         gchar *buf;
3494         gchar *tmp;
3495         gchar *p;
3496         va_list args;
3497
3498         va_start(args, format);
3499         tmp = g_strdup_vprintf(format, args);
3500         va_end(args);
3501
3502         session->cmd_count++;
3503
3504         buf = g_strdup_printf("%d %s\r\n", session->cmd_count, tmp);
3505         if (!g_ascii_strncasecmp(tmp, "LOGIN ", 6) && (p = strchr(tmp + 6, ' '))) {
3506                 *p = '\0';
3507                 log_print("IMAP4> %d %s ********\n", session->cmd_count, tmp);
3508         } else
3509                 log_print("IMAP4> %d %s\n", session->cmd_count, tmp);
3510
3511         sock_write_all(SESSION(session)->sock, buf, strlen(buf));
3512         g_free(tmp);
3513         g_free(buf);
3514 }
3515
3516 static gint imap_gen_recv_with_block(IMAPSession *session, gchar **ret, gboolean block)
3517 {
3518         if (!block) {
3519                 if ((*ret = imap_getline(SESSION(session)->sock)) == NULL)
3520                         return IMAP_SOCKET;
3521         } else {
3522                 if ((*ret = sock_getline(SESSION(session)->sock)) == NULL)
3523                         return IMAP_SOCKET;
3524         }
3525         strretchomp(*ret);
3526
3527         log_print("IMAP4< %s\n", *ret);
3528         
3529         session_set_access_time(SESSION(session));
3530
3531         return IMAP_SUCCESS;
3532 }
3533
3534 static gint imap_gen_recv_block(IMAPSession *session, gchar **ret)
3535 {
3536         return imap_gen_recv_with_block(session, ret, TRUE);
3537 }
3538
3539 static gint imap_gen_recv(IMAPSession *session, gchar **ret)
3540 {
3541         return imap_gen_recv_with_block(session, ret, FALSE);
3542 }
3543 /* misc utility functions */
3544
3545 static gchar *strchr_cpy(const gchar *src, gchar ch, gchar *dest, gint len)
3546 {
3547         gchar *tmp;
3548
3549         dest[0] = '\0';
3550         tmp = strchr(src, ch);
3551         if (!tmp)
3552                 return NULL;
3553
3554         memcpy(dest, src, MIN(tmp - src, len - 1));
3555         dest[MIN(tmp - src, len - 1)] = '\0';
3556
3557         return tmp + 1;
3558 }
3559
3560 static gchar *get_quoted(const gchar *src, gchar ch, gchar *dest, gint len)
3561 {
3562         const gchar *p = src;
3563         gint n = 0;
3564
3565         g_return_val_if_fail(*p == ch, NULL);
3566
3567         *dest = '\0';
3568         p++;
3569
3570         while (*p != '\0' && *p != ch) {
3571                 if (n < len - 1) {
3572                         if (*p == '\\' && *(p + 1) != '\0')
3573                                 p++;
3574                         *dest++ = *p++;
3575                 } else
3576                         p++;
3577                 n++;
3578         }
3579
3580         *dest = '\0';
3581         return (gchar *)(*p == ch ? p + 1 : p);
3582 }
3583
3584 static gchar *search_array_contain_str(GPtrArray *array, const gchar *str)
3585 {
3586         gint i;
3587
3588         for (i = 0; i < array->len; i++) {
3589                 gchar *tmp;
3590
3591                 tmp = g_ptr_array_index(array, i);
3592                 if (strstr(tmp, str) != NULL)
3593                         return tmp;
3594         }
3595
3596         return NULL;
3597 }
3598
3599 static gchar *search_array_str(GPtrArray *array, const gchar *str)
3600 {
3601         gint i;
3602         gint len;
3603
3604         len = strlen(str);
3605
3606         for (i = 0; i < array->len; i++) {
3607                 gchar *tmp;
3608
3609                 tmp = g_ptr_array_index(array, i);
3610                 if (!strncmp(tmp, str, len))
3611                         return tmp;
3612         }
3613
3614         return NULL;
3615 }
3616
3617 static void imap_path_separator_subst(gchar *str, gchar separator)
3618 {
3619         gchar *p;
3620         gboolean in_escape = FALSE;
3621
3622         if (!separator || separator == '/') return;
3623
3624         for (p = str; *p != '\0'; p++) {
3625                 if (*p == '/' && !in_escape)
3626                         *p = separator;
3627                 else if (*p == '&' && *(p + 1) != '-' && !in_escape)
3628                         in_escape = TRUE;
3629                 else if (*p == '-' && in_escape)
3630                         in_escape = FALSE;
3631         }
3632 }
3633
3634 static gchar *imap_modified_utf7_to_utf8(const gchar *mutf7_str)
3635 {
3636         static iconv_t cd = (iconv_t)-1;
3637         static gboolean iconv_ok = TRUE;
3638         GString *norm_utf7;
3639         gchar *norm_utf7_p;
3640         size_t norm_utf7_len;
3641         const gchar *p;
3642         gchar *to_str, *to_p;
3643         size_t to_len;
3644         gboolean in_escape = FALSE;
3645
3646         if (!iconv_ok) return g_strdup(mutf7_str);
3647
3648         if (cd == (iconv_t)-1) {
3649                 cd = iconv_open(CS_INTERNAL, CS_UTF_7);
3650                 if (cd == (iconv_t)-1) {
3651                         g_warning("iconv cannot convert UTF-7 to %s\n",
3652                                   CS_INTERNAL);
3653                         iconv_ok = FALSE;
3654                         return g_strdup(mutf7_str);
3655                 }
3656         }
3657
3658         /* modified UTF-7 to normal UTF-7 conversion */
3659         norm_utf7 = g_string_new(NULL);
3660
3661         for (p = mutf7_str; *p != '\0'; p++) {
3662                 /* replace: '&'  -> '+',
3663                             "&-" -> '&',
3664                             escaped ','  -> '/' */
3665                 if (!in_escape && *p == '&') {
3666                         if (*(p + 1) != '-') {
3667                                 g_string_append_c(norm_utf7, '+');
3668                                 in_escape = TRUE;
3669                         } else {
3670                                 g_string_append_c(norm_utf7, '&');
3671                                 p++;
3672                         }
3673                 } else if (in_escape && *p == ',') {
3674                         g_string_append_c(norm_utf7, '/');
3675                 } else if (in_escape && *p == '-') {
3676                         g_string_append_c(norm_utf7, '-');
3677                         in_escape = FALSE;
3678                 } else {
3679                         g_string_append_c(norm_utf7, *p);
3680                 }
3681         }
3682
3683         norm_utf7_p = norm_utf7->str;
3684         norm_utf7_len = norm_utf7->len;
3685         to_len = strlen(mutf7_str) * 5;
3686         to_p = to_str = g_malloc(to_len + 1);
3687
3688         if (iconv(cd, (ICONV_CONST gchar **)&norm_utf7_p, &norm_utf7_len,
3689                   &to_p, &to_len) == -1) {
3690                 g_warning(_("iconv cannot convert UTF-7 to %s\n"),
3691                           conv_get_locale_charset_str());
3692                 g_string_free(norm_utf7, TRUE);
3693                 g_free(to_str);
3694                 return g_strdup(mutf7_str);
3695         }
3696
3697         /* second iconv() call for flushing */
3698         iconv(cd, NULL, NULL, &to_p, &to_len);
3699         g_string_free(norm_utf7, TRUE);
3700         *to_p = '\0';
3701
3702         return to_str;
3703 }
3704
3705 static gchar *imap_utf8_to_modified_utf7(const gchar *from)
3706 {
3707         static iconv_t cd = (iconv_t)-1;
3708         static gboolean iconv_ok = TRUE;
3709         gchar *norm_utf7, *norm_utf7_p;
3710         size_t from_len, norm_utf7_len;
3711         GString *to_str;
3712         gchar *from_tmp, *to, *p;
3713         gboolean in_escape = FALSE;
3714
3715         if (!iconv_ok) return g_strdup(from);
3716
3717         if (cd == (iconv_t)-1) {
3718                 cd = iconv_open(CS_UTF_7, CS_INTERNAL);
3719                 if (cd == (iconv_t)-1) {
3720                         g_warning(_("iconv cannot convert %s to UTF-7\n"),
3721                                   CS_INTERNAL);
3722                         iconv_ok = FALSE;
3723                         return g_strdup(from);
3724                 }
3725         }
3726
3727         /* UTF-8 to normal UTF-7 conversion */
3728         Xstrdup_a(from_tmp, from, return g_strdup(from));
3729         from_len = strlen(from);
3730         norm_utf7_len = from_len * 5;
3731         Xalloca(norm_utf7, norm_utf7_len + 1, return g_strdup(from));
3732         norm_utf7_p = norm_utf7;
3733
3734 #define IS_PRINT(ch) (isprint(ch) && IS_ASCII(ch))
3735
3736         while (from_len > 0) {
3737                 if (*from_tmp == '+') {
3738                         *norm_utf7_p++ = '+';
3739                         *norm_utf7_p++ = '-';
3740                         norm_utf7_len -= 2;
3741                         from_tmp++;
3742                         from_len--;
3743                 } else if (IS_PRINT(*(guchar *)from_tmp)) {
3744                         /* printable ascii char */
3745                         *norm_utf7_p = *from_tmp;
3746                         norm_utf7_p++;
3747                         norm_utf7_len--;
3748                         from_tmp++;
3749                         from_len--;
3750                 } else {
3751                         size_t conv_len = 0;
3752
3753                         /* unprintable char: convert to UTF-7 */
3754                         p = from_tmp;
3755                         while (!IS_PRINT(*(guchar *)p) && conv_len < from_len) {
3756                                 conv_len += g_utf8_skip[*(guchar *)p];
3757                                 p += g_utf8_skip[*(guchar *)p];
3758                         }
3759
3760                         from_len -= conv_len;
3761                         if (iconv(cd, (ICONV_CONST gchar **)&from_tmp,
3762                                   &conv_len,
3763                                   &norm_utf7_p, &norm_utf7_len) == -1) {
3764                                 g_warning(_("iconv cannot convert UTF-8 to UTF-7\n"));
3765                                 return g_strdup(from);
3766                         }
3767
3768                         /* second iconv() call for flushing */
3769                         iconv(cd, NULL, NULL, &norm_utf7_p, &norm_utf7_len);
3770                 }
3771         }
3772
3773 #undef IS_PRINT
3774
3775         *norm_utf7_p = '\0';
3776         to_str = g_string_new(NULL);
3777         for (p = norm_utf7; p < norm_utf7_p; p++) {
3778                 /* replace: '&' -> "&-",
3779                             '+' -> '&',
3780                             "+-" -> '+',
3781                             BASE64 '/' -> ',' */
3782                 if (!in_escape && *p == '&') {
3783                         g_string_append(to_str, "&-");
3784                 } else if (!in_escape && *p == '+') {
3785                         if (*(p + 1) == '-') {
3786                                 g_string_append_c(to_str, '+');
3787                                 p++;
3788                         } else {
3789                                 g_string_append_c(to_str, '&');
3790                                 in_escape = TRUE;
3791                         }
3792                 } else if (in_escape && *p == '/') {
3793                         g_string_append_c(to_str, ',');
3794                 } else if (in_escape && *p == '-') {
3795                         g_string_append_c(to_str, '-');
3796                         in_escape = FALSE;
3797                 } else {
3798                         g_string_append_c(to_str, *p);
3799                 }
3800         }
3801
3802         if (in_escape) {
3803                 in_escape = FALSE;
3804                 g_string_append_c(to_str, '-');
3805         }
3806
3807         to = to_str->str;
3808         g_string_free(to_str, FALSE);
3809
3810         return to;
3811 }
3812
3813 static GSList *imap_get_seq_set_from_numlist(MsgNumberList *numlist)
3814 {
3815         GString *str;
3816         GSList *sorted_list, *cur;
3817         guint first, last, next;
3818         gchar *ret_str;
3819         GSList *ret_list = NULL;
3820
3821         if (numlist == NULL)
3822                 return NULL;
3823
3824         str = g_string_sized_new(256);
3825
3826         sorted_list = g_slist_copy(numlist);
3827         sorted_list = g_slist_sort(sorted_list, g_int_compare);
3828
3829         first = GPOINTER_TO_INT(sorted_list->data);
3830
3831         for (cur = sorted_list; cur != NULL; cur = g_slist_next(cur)) {
3832                 last = GPOINTER_TO_INT(cur->data);
3833                 if (cur->next)
3834                         next = GPOINTER_TO_INT(cur->next->data);
3835                 else
3836                         next = 0;
3837
3838                 if (last + 1 != next || next == 0) {
3839                         if (str->len > 0)
3840                                 g_string_append_c(str, ',');
3841                         if (first == last)
3842                                 g_string_append_printf(str, "%u", first);
3843                         else
3844                                 g_string_append_printf(str, "%u:%u", first, last);
3845
3846                         first = next;
3847
3848                         if (str->len > IMAP_CMD_LIMIT) {
3849                                 ret_str = g_strdup(str->str);
3850                                 ret_list = g_slist_append(ret_list, ret_str);
3851                                 g_string_truncate(str, 0);
3852                         }
3853                 }
3854         }
3855
3856         if (str->len > 0) {
3857                 ret_str = g_strdup(str->str);
3858                 ret_list = g_slist_append(ret_list, ret_str);
3859         }
3860
3861         g_slist_free(sorted_list);
3862         g_string_free(str, TRUE);
3863
3864         return ret_list;
3865 }
3866
3867 static GSList *imap_get_seq_set_from_msglist(MsgInfoList *msglist)
3868 {
3869         MsgNumberList *numlist = NULL;
3870         MsgInfoList *cur;
3871         GSList *seq_list;
3872
3873         for (cur = msglist; cur != NULL; cur = g_slist_next(cur)) {
3874                 MsgInfo *msginfo = (MsgInfo *) cur->data;
3875
3876                 numlist = g_slist_append(numlist, GINT_TO_POINTER(msginfo->msgnum));
3877         }
3878         seq_list = imap_get_seq_set_from_numlist(numlist);
3879         g_slist_free(numlist);
3880
3881         return seq_list;
3882 }
3883
3884 static void imap_seq_set_free(GSList *seq_list)
3885 {
3886         slist_free_strings(seq_list);
3887         g_slist_free(seq_list);
3888 }
3889
3890
3891 static gboolean imap_rename_folder_func(GNode *node, gpointer data)
3892 {
3893         FolderItem *item = node->data;
3894         gchar **paths = data;
3895         const gchar *oldpath = paths[0];
3896         const gchar *newpath = paths[1];
3897         gchar *base;
3898         gchar *new_itempath;
3899         gint oldpathlen;
3900
3901         oldpathlen = strlen(oldpath);
3902         if (strncmp(oldpath, item->path, oldpathlen) != 0) {
3903                 g_warning("path doesn't match: %s, %s\n", oldpath, item->path);
3904                 return TRUE;
3905         }
3906
3907         base = item->path + oldpathlen;
3908         while (*base == G_DIR_SEPARATOR) base++;
3909         if (*base == '\0')
3910                 new_itempath = g_strdup(newpath);
3911         else
3912                 new_itempath = g_strconcat(newpath, G_DIR_SEPARATOR_S, base,
3913                                            NULL);
3914         g_free(item->path);
3915         item->path = new_itempath;
3916
3917         return FALSE;
3918 }
3919
3920 typedef struct _get_list_uid_data {
3921         Folder *folder;
3922         IMAPFolderItem *item;
3923         GSList **msgnum_list;
3924         gboolean done;
3925 } get_list_uid_data;
3926
3927 static void *get_list_of_uids_thread(void *data)
3928 {
3929         get_list_uid_data *stuff = (get_list_uid_data *)data;
3930         Folder *folder = stuff->folder;
3931         IMAPFolderItem *item = stuff->item;
3932         GSList **msgnum_list = stuff->msgnum_list;
3933         gint ok, nummsgs = 0, lastuid_old;
3934         IMAPSession *session;
3935         GSList *uidlist, *elem;
3936         gchar *cmd_buf;
3937
3938         session = imap_session_get(folder);
3939         if (session == NULL) {
3940                 stuff->done = TRUE;
3941                 return GINT_TO_POINTER(-1);
3942         }
3943
3944         ok = imap_select(session, IMAP_FOLDER(folder), item->item.path,
3945                          NULL, NULL, NULL, NULL, TRUE);
3946         if (ok != IMAP_SUCCESS) {
3947                 stuff->done = TRUE;
3948                 return GINT_TO_POINTER(-1);
3949         }
3950
3951         cmd_buf = g_strdup_printf("UID %d:*", item->lastuid + 1);
3952         ok = imap_cmd_search(session, cmd_buf, &uidlist, TRUE);
3953         g_free(cmd_buf);
3954
3955         if (ok == IMAP_SOCKET) {
3956                 session_destroy((Session *)session);
3957                 ((RemoteFolder *)folder)->session = NULL;
3958                 stuff->done = TRUE;
3959                 return GINT_TO_POINTER(-1);
3960         }
3961
3962         if (ok != IMAP_SUCCESS) {
3963                 gint i;
3964                 GPtrArray *argbuf;
3965
3966                 argbuf = g_ptr_array_new();
3967
3968                 cmd_buf = g_strdup_printf("UID FETCH %d:* (UID)", item->lastuid + 1);
3969                 imap_gen_send(session, cmd_buf);
3970                 g_free(cmd_buf);
3971                 ok = imap_cmd_ok_block(session, argbuf);
3972                 if (ok != IMAP_SUCCESS) {
3973                         ptr_array_free_strings(argbuf);
3974                         g_ptr_array_free(argbuf, TRUE);
3975                         stuff->done = TRUE;
3976                         return GINT_TO_POINTER(-1);
3977                 }
3978         
3979                 for(i = 0; i < argbuf->len; i++) {
3980                         int ret, msgnum;
3981         
3982                         if((ret = sscanf(g_ptr_array_index(argbuf, i), 
3983                                     "%*d FETCH (UID %d)", &msgnum)) == 1)
3984                                 uidlist = g_slist_prepend(uidlist, GINT_TO_POINTER(msgnum));
3985                 }
3986                 ptr_array_free_strings(argbuf);
3987                 g_ptr_array_free(argbuf, TRUE);
3988         }
3989
3990         lastuid_old = item->lastuid;
3991         *msgnum_list = g_slist_copy(item->uid_list);
3992         nummsgs = g_slist_length(*msgnum_list);
3993         debug_print("Got %d uids from cache\n", g_slist_length(item->uid_list));
3994
3995         for (elem = uidlist; elem != NULL; elem = g_slist_next(elem)) {
3996                 guint msgnum;
3997
3998                 msgnum = GPOINTER_TO_INT(elem->data);
3999                 if (msgnum > lastuid_old) {
4000                         *msgnum_list = g_slist_prepend(*msgnum_list, GINT_TO_POINTER(msgnum));
4001                         item->uid_list = g_slist_prepend(item->uid_list, GINT_TO_POINTER(msgnum));
4002                         nummsgs++;
4003
4004                         if(msgnum > item->lastuid)
4005                                 item->lastuid = msgnum;
4006                 }
4007         }
4008         g_slist_free(uidlist);
4009
4010         stuff->done = TRUE;
4011         return GINT_TO_POINTER(nummsgs);
4012 }
4013
4014 static gint get_list_of_uids(Folder *folder, IMAPFolderItem *item, GSList **msgnum_list)
4015 {
4016         gint result;
4017         get_list_uid_data *data = g_new0(get_list_uid_data, 1);
4018 #ifdef USE_PTHREAD
4019         void *tmp;
4020         pthread_t pt;
4021 #endif
4022         data->done = FALSE;
4023         data->folder = folder;
4024         data->item = item;
4025         data->msgnum_list = msgnum_list;
4026
4027         if (prefs_common.work_offline && !imap_gtk_should_override()) {
4028                 g_free(data);
4029                 return -1;
4030         }
4031
4032 #if (defined USE_PTHREAD && defined __GLIBC__ && (__GLIBC__ > 2 || (__GLIBC__ == 2 && __GLIBC_MINOR__ >= 3)))
4033         if (pthread_create(&pt, PTHREAD_CREATE_JOINABLE,
4034                         get_list_of_uids_thread, data) != 0) {
4035                 result = GPOINTER_TO_INT(get_list_of_uids_thread(data));
4036                 g_free(data);
4037                 return result;
4038         }
4039         debug_print("+++waiting for get_list_of_uids_thread...\n");
4040         while(!data->done) {
4041                 /* don't let the interface freeze while waiting */
4042                 sylpheed_do_idle();
4043         }
4044         debug_print("---get_list_of_uids_thread done\n");
4045
4046         /* get the thread's return value and clean its resources */
4047         pthread_join(pt, &tmp);
4048         result = GPOINTER_TO_INT(tmp);
4049 #else
4050         result = GPOINTER_TO_INT(get_list_of_uids_thread(data));
4051 #endif
4052         g_free(data);
4053         return result;
4054
4055 }
4056
4057 gint imap_get_num_list(Folder *folder, FolderItem *_item, GSList **msgnum_list, gboolean *old_uids_valid)
4058 {
4059         IMAPFolderItem *item = (IMAPFolderItem *)_item;
4060         IMAPSession *session;
4061         gint ok, nummsgs = 0, exists, recent, uid_val, uid_next, unseen;
4062         GSList *uidlist = NULL;
4063         gchar *dir;
4064         gboolean selected_folder;
4065
4066         g_return_val_if_fail(folder != NULL, -1);
4067         g_return_val_if_fail(item != NULL, -1);
4068         g_return_val_if_fail(item->item.path != NULL, -1);
4069         g_return_val_if_fail(FOLDER_CLASS(folder) == &imap_class, -1);
4070         g_return_val_if_fail(folder->account != NULL, -1);
4071
4072         session = imap_session_get(folder);
4073         g_return_val_if_fail(session != NULL, -1);
4074
4075         selected_folder = (session->mbox != NULL) &&
4076                           (!strcmp(session->mbox, item->item.path));
4077         if (selected_folder) {
4078                 ok = imap_cmd_noop(session);
4079                 if (ok != IMAP_SUCCESS)
4080                         return -1;
4081                 exists = session->exists;
4082
4083                 *old_uids_valid = TRUE;
4084         } else {
4085                 ok = imap_status(session, IMAP_FOLDER(folder), item->item.path,
4086                                  &exists, &recent, &uid_next, &uid_val, &unseen, FALSE);
4087                 if (ok != IMAP_SUCCESS)
4088                         return -1;
4089
4090                 if(item->item.mtime == uid_val)
4091                         *old_uids_valid = TRUE;
4092                 else {
4093                         *old_uids_valid = FALSE;
4094
4095                         debug_print("Freeing imap uid cache\n");
4096                         item->lastuid = 0;
4097                         g_slist_free(item->uid_list);
4098                         item->uid_list = NULL;
4099                 
4100                         item->item.mtime = uid_val;
4101
4102                         imap_delete_all_cached_messages((FolderItem *)item);
4103                 }
4104         }
4105
4106         if (!selected_folder)
4107                 item->uid_next = uid_next;
4108
4109         /* If old uid_next matches new uid_next we can be sure no message
4110            was added to the folder */
4111         if (( selected_folder && !session->folder_content_changed) ||
4112             (!selected_folder && uid_next == item->uid_next)) {
4113                 nummsgs = g_slist_length(item->uid_list);
4114
4115                 /* If number of messages is still the same we
4116                    know our caches message numbers are still valid,
4117                    otherwise if the number of messages has decrease
4118                    we discard our cache to start a new scan to find
4119                    out which numbers have been removed */
4120                 if (exists == nummsgs) {
4121                         *msgnum_list = g_slist_copy(item->uid_list);
4122                         return nummsgs;
4123                 } else if (exists < nummsgs) {
4124                         debug_print("Freeing imap uid cache");
4125                         item->lastuid = 0;
4126                         g_slist_free(item->uid_list);
4127                         item->uid_list = NULL;
4128                 }
4129         }
4130
4131         if (exists == 0) {
4132                 *msgnum_list = NULL;
4133                 return 0;
4134         }
4135
4136         nummsgs = get_list_of_uids(folder, item, &uidlist);
4137
4138         if (nummsgs != exists) {
4139                 /* Cache contains more messages then folder, we have cached
4140                    an old UID of a message that was removed and new messages
4141                    have been added too, otherwise the uid_next check would
4142                    not have failed */
4143                 debug_print("Freeing imap uid cache");
4144                 item->lastuid = 0;
4145                 g_slist_free(item->uid_list);
4146                 item->uid_list = NULL;
4147
4148                 g_slist_free(*msgnum_list);
4149
4150                 nummsgs = get_list_of_uids(folder, item, &uidlist);
4151         }
4152
4153         *msgnum_list = uidlist;
4154
4155         dir = folder_item_get_path((FolderItem *)item);
4156         debug_print("removing old messages from %s\n", dir);
4157         remove_numbered_files_not_in_list(dir, *msgnum_list);
4158         g_free(dir);
4159
4160         return nummsgs;
4161 }
4162
4163 static MsgInfo *imap_parse_msg(const gchar *file, FolderItem *item)
4164 {
4165         MsgInfo *msginfo;
4166         MsgFlags flags;
4167
4168         flags.perm_flags = MSG_NEW|MSG_UNREAD;
4169         flags.tmp_flags = 0;
4170
4171         g_return_val_if_fail(item != NULL, NULL);
4172         g_return_val_if_fail(file != NULL, NULL);
4173
4174         if (item->stype == F_QUEUE) {
4175                 MSG_SET_TMP_FLAGS(flags, MSG_QUEUED);
4176         } else if (item->stype == F_DRAFT) {
4177                 MSG_SET_TMP_FLAGS(flags, MSG_DRAFT);
4178         }
4179
4180         msginfo = procheader_parse_file(file, flags, FALSE, FALSE);
4181         if (!msginfo) return NULL;
4182
4183         msginfo->folder = item;
4184
4185         return msginfo;
4186 }
4187
4188 GSList *imap_get_msginfos(Folder *folder, FolderItem *item, GSList *msgnum_list)
4189 {
4190         IMAPSession *session;
4191         MsgInfoList *ret = NULL;
4192         gint ok;
4193
4194         g_return_val_if_fail(folder != NULL, NULL);
4195         g_return_val_if_fail(item != NULL, NULL);
4196         g_return_val_if_fail(msgnum_list != NULL, NULL);
4197
4198         session = imap_session_get(folder);
4199         g_return_val_if_fail(session != NULL, NULL);
4200
4201         debug_print("-> getting msginfos\n");
4202         ok = imap_select(session, IMAP_FOLDER(folder), item->path,
4203                          NULL, NULL, NULL, NULL, FALSE);
4204         if (ok != IMAP_SUCCESS)
4205                 return NULL;
4206
4207         if (!(item->stype == F_QUEUE || item->stype == F_DRAFT)) {
4208                 ret = g_slist_concat(ret,
4209                         imap_get_uncached_messages(
4210                         session, item, msgnum_list));
4211         } else {
4212                 MsgNumberList *sorted_list, *elem;
4213                 gint startnum, lastnum;
4214
4215                 sorted_list = g_slist_sort(g_slist_copy(msgnum_list), g_int_compare);
4216
4217                 startnum = lastnum = GPOINTER_TO_INT(sorted_list->data);
4218
4219                 for (elem = sorted_list;; elem = g_slist_next(elem)) {
4220                         guint num = 0;
4221
4222                         if (elem)
4223                                 num = GPOINTER_TO_INT(elem->data);
4224
4225                         if (num > lastnum + 1 || elem == NULL) {
4226                                 int i;
4227                                 for (i = startnum; i <= lastnum; ++i) {
4228                                         gchar *file;
4229                         
4230                                         file = imap_fetch_msg(folder, item, i);
4231                                         if (file != NULL) {
4232                                                 MsgInfo *msginfo = imap_parse_msg(file, item);
4233                                                 if (msginfo != NULL) {
4234                                                         msginfo->msgnum = i;
4235                                                         ret = g_slist_append(ret, msginfo);
4236                                                 }
4237                                                 g_free(file);
4238                                         }
4239                                 }
4240
4241                                 if (elem == NULL)
4242                                         break;
4243
4244                                 startnum = num;
4245                         }
4246                         lastnum = num;
4247                 }
4248
4249                 g_slist_free(sorted_list);
4250         }
4251
4252         return ret;
4253 }
4254
4255 MsgInfo *imap_get_msginfo(Folder *folder, FolderItem *item, gint uid)
4256 {
4257         MsgInfo *msginfo = NULL;
4258         MsgInfoList *msginfolist;
4259         MsgNumberList numlist;
4260
4261         numlist.next = NULL;
4262         numlist.data = GINT_TO_POINTER(uid);
4263
4264         msginfolist = imap_get_msginfos(folder, item, &numlist);
4265         if (msginfolist != NULL) {
4266                 msginfo = msginfolist->data;
4267                 g_slist_free(msginfolist);
4268         }
4269
4270         return msginfo;
4271 }
4272
4273 gboolean imap_scan_required(Folder *folder, FolderItem *_item)
4274 {
4275         IMAPSession *session;
4276         IMAPFolderItem *item = (IMAPFolderItem *)_item;
4277         gint ok, exists = 0, recent = 0, unseen = 0;
4278         guint32 uid_next, uid_val = 0;
4279         gboolean selected_folder;
4280         
4281         g_return_val_if_fail(folder != NULL, FALSE);
4282         g_return_val_if_fail(item != NULL, FALSE);
4283         g_return_val_if_fail(item->item.folder != NULL, FALSE);
4284         g_return_val_if_fail(FOLDER_CLASS(item->item.folder) == &imap_class, FALSE);
4285
4286         if (item->item.path == NULL)
4287                 return FALSE;
4288
4289         session = imap_session_get(folder);
4290         g_return_val_if_fail(session != NULL, FALSE);
4291
4292         selected_folder = (session->mbox != NULL) &&
4293                           (!strcmp(session->mbox, item->item.path));
4294         if (selected_folder) {
4295                 ok = imap_cmd_noop(session);
4296                 if (ok != IMAP_SUCCESS)
4297                         return FALSE;
4298
4299                 if (session->folder_content_changed)
4300                         return TRUE;
4301         } else {
4302                 ok = imap_status(session, IMAP_FOLDER(folder), item->item.path,
4303                                  &exists, &recent, &uid_next, &uid_val, &unseen, FALSE);
4304                 if (ok != IMAP_SUCCESS)
4305                         return FALSE;
4306
4307                 if ((uid_next != item->uid_next) || (exists != item->item.total_msgs))
4308                         return TRUE;
4309         }
4310
4311         return FALSE;
4312 }
4313
4314 static GHashTable *flags_set_table = NULL;
4315 static GHashTable *flags_unset_table = NULL;
4316 static gint hashtable_process_tag = -1;
4317
4318 typedef struct _hashtable_data {
4319         IMAPSession *session;
4320         GSList *msglist;
4321         FolderItem *item;
4322 } hashtable_data;
4323
4324 static gboolean process_flags(gpointer key, gpointer value, gpointer user_data)
4325 {
4326         gboolean flags_set = GPOINTER_TO_INT(user_data);
4327         gint flags_value = GPOINTER_TO_INT(key);
4328         hashtable_data *data = (hashtable_data *)value;
4329         
4330         data->msglist = g_slist_reverse(data->msglist);
4331         
4332         imap_set_message_flags(data->session, data->msglist, flags_value, flags_set);
4333
4334         if (flags_value == IMAP_FLAG_DELETED && flags_set == TRUE) {
4335                 if (!data->session->uidplus) {
4336                         imap_cmd_expunge(data->session, NULL);
4337                 } else {
4338                         GSList *seq_list;
4339                         IMAPSet imapset;
4340                         seq_list = imap_get_seq_set_from_numlist(data->msglist);
4341                         imapset = get_seq_set_from_seq_list(seq_list);
4342
4343                         imap_cmd_expunge(data->session, imapset);
4344                         g_free(imapset);
4345                         g_free(seq_list);
4346                 }
4347         }
4348         g_slist_free(data->msglist);    
4349         g_free(data);
4350         return TRUE;
4351 }
4352
4353 static FolderItem *last_deferred_item = NULL;
4354 static gboolean process_hashtable(void *data)
4355 {
4356         FolderItem *item = (FolderItem *)data;
4357         debug_print("processing flags change hashtables\n");
4358         if (flags_set_table) {
4359                 g_hash_table_foreach_remove(flags_set_table, process_flags, GINT_TO_POINTER(TRUE));
4360                 g_free(flags_set_table);
4361                 flags_set_table = NULL;
4362         }
4363         if (flags_unset_table) {
4364                 g_hash_table_foreach_remove(flags_unset_table, process_flags, GINT_TO_POINTER(FALSE));
4365                 g_free(flags_unset_table);
4366                 flags_unset_table = NULL;
4367         }
4368         
4369         folder_item_scan_full(item, FALSE);
4370         hashtable_process_tag = -1;
4371         return FALSE;
4372 }
4373
4374 void imap_change_flags(Folder *folder, FolderItem *item, MsgInfo *msginfo, MsgPermFlags newflags)
4375 {
4376         IMAPSession *session;
4377         IMAPFlags flags_set = 0, flags_unset = 0;
4378         gint ok = IMAP_SUCCESS;
4379         hashtable_data *ht_data = NULL;
4380         
4381         g_return_if_fail(folder != NULL);
4382         g_return_if_fail(folder->klass == &imap_class);
4383         g_return_if_fail(item != NULL);
4384         g_return_if_fail(item->folder == folder);
4385         g_return_if_fail(msginfo != NULL);
4386         g_return_if_fail(msginfo->folder == item);
4387
4388         session = imap_session_get(folder);
4389         if (!session) return;
4390
4391         debug_print("-> changing flags\n");
4392
4393         if (hashtable_process_tag != -1 && item != last_deferred_item
4394         &&  last_deferred_item != NULL) {
4395                 debug_print("forcing flush for %s (!= %s)\n",
4396                                 last_deferred_item->path,
4397                                 item->path);
4398                 gtk_timeout_remove(hashtable_process_tag);
4399                 process_hashtable(last_deferred_item);
4400                 hashtable_process_tag = -1;
4401         }
4402         last_deferred_item = item;
4403         
4404         if ((ok = imap_select(session, IMAP_FOLDER(folder), msginfo->folder->path,
4405             NULL, NULL, NULL, NULL, FALSE)) != IMAP_SUCCESS)
4406                 return;
4407                 
4408         if (!flags_set_table) {
4409                 flags_set_table = g_hash_table_new(NULL, g_direct_equal);
4410         }
4411         if (!flags_unset_table) {
4412                 flags_unset_table = g_hash_table_new(NULL, g_direct_equal);
4413         }
4414
4415         if (!MSG_IS_MARKED(msginfo->flags) &&  (newflags & MSG_MARKED))
4416                 flags_set |= IMAP_FLAG_FLAGGED;
4417         if ( MSG_IS_MARKED(msginfo->flags) && !(newflags & MSG_MARKED))
4418                 flags_unset |= IMAP_FLAG_FLAGGED;
4419
4420         if (!MSG_IS_UNREAD(msginfo->flags) &&  (newflags & MSG_UNREAD))
4421                 flags_unset |= IMAP_FLAG_SEEN;
4422         if ( MSG_IS_UNREAD(msginfo->flags) && !(newflags & MSG_UNREAD))
4423                 flags_set |= IMAP_FLAG_SEEN;
4424
4425         if (!MSG_IS_REPLIED(msginfo->flags) &&  (newflags & MSG_REPLIED))
4426                 flags_set |= IMAP_FLAG_ANSWERED;
4427         if ( MSG_IS_REPLIED(msginfo->flags) && !(newflags & MSG_REPLIED))
4428                 flags_set |= IMAP_FLAG_ANSWERED;
4429
4430         /* instead of performing an UID STORE command for each message change,
4431          * as a lot of them can change "together", we just fill in hashtables
4432          * and defer the treatment 100ms so that we're able to send only one
4433          * command.
4434          */
4435         if (flags_set) {
4436                 ht_data = g_hash_table_lookup(flags_set_table, GINT_TO_POINTER(flags_set));
4437                 if (ht_data == NULL) {
4438                         ht_data = g_new0(hashtable_data, 1);
4439                         ht_data->session = session;
4440                         g_hash_table_insert(flags_set_table, GINT_TO_POINTER(flags_set), ht_data);
4441                 }
4442                 if (!g_slist_find(ht_data->msglist, GINT_TO_POINTER(msginfo->msgnum)))
4443                         ht_data->msglist = g_slist_prepend(ht_data->msglist, GINT_TO_POINTER(msginfo->msgnum));
4444         }
4445
4446         if (flags_unset) {
4447                 ht_data = g_hash_table_lookup(flags_unset_table, GINT_TO_POINTER(flags_unset));
4448                 if (ht_data == NULL) {
4449                         ht_data = g_new0(hashtable_data, 1);
4450                         ht_data->session = session;
4451                         g_hash_table_insert(flags_unset_table, GINT_TO_POINTER(flags_unset), ht_data);
4452                 }
4453                 if (!g_slist_find(ht_data->msglist, GINT_TO_POINTER(msginfo->msgnum)))
4454                         ht_data->msglist = g_slist_prepend(ht_data->msglist, GINT_TO_POINTER(msginfo->msgnum));
4455         }
4456
4457         msginfo->flags.perm_flags = newflags;
4458         
4459         if (hashtable_process_tag != -1)
4460                 gtk_timeout_remove(hashtable_process_tag);
4461                 
4462         hashtable_process_tag = gtk_timeout_add(100, process_hashtable, item);
4463         
4464         return;
4465 }
4466
4467 static gint imap_remove_msg(Folder *folder, FolderItem *item, gint uid)
4468 {
4469         gint ok;
4470         IMAPSession *session;
4471         gchar *dir;
4472         MsgNumberList numlist;
4473         hashtable_data *ht_data;
4474         
4475         if (!flags_set_table) {
4476                 flags_set_table = g_hash_table_new(NULL, g_direct_equal);
4477         }
4478         if (!flags_unset_table) {
4479                 flags_unset_table = g_hash_table_new(NULL, g_direct_equal);
4480         }
4481
4482         g_return_val_if_fail(folder != NULL, -1);
4483         g_return_val_if_fail(FOLDER_CLASS(folder) == &imap_class, -1);
4484         g_return_val_if_fail(item != NULL, -1);
4485
4486         session = imap_session_get(folder);
4487         if (!session) return -1;
4488
4489         if (hashtable_process_tag != -1 && item != last_deferred_item
4490         &&  last_deferred_item != NULL) {
4491                 debug_print("forcing flush for %s (!= %s)\n",
4492                                 last_deferred_item->path,
4493                                 item->path);
4494                 gtk_timeout_remove(hashtable_process_tag);
4495                 process_hashtable(last_deferred_item);
4496                 hashtable_process_tag = -1;
4497         }
4498         last_deferred_item = item;
4499
4500 debug_print("-> removing messages\n");
4501         ok = imap_select(session, IMAP_FOLDER(folder), item->path,
4502                          NULL, NULL, NULL, NULL, FALSE);
4503         if (ok != IMAP_SUCCESS)
4504                 return ok;
4505
4506         ht_data = g_hash_table_lookup(flags_set_table, GINT_TO_POINTER(IMAP_FLAG_DELETED));
4507         if (ht_data == NULL) {
4508                 ht_data = g_new0(hashtable_data, 1);
4509                 ht_data->session = session;
4510                 g_hash_table_insert(flags_set_table, GINT_TO_POINTER(IMAP_FLAG_DELETED), ht_data);
4511         }
4512         if (!g_slist_find(ht_data->msglist, GINT_TO_POINTER(uid)))
4513                 ht_data->msglist = g_slist_prepend(ht_data->msglist, 
4514                                 GINT_TO_POINTER(uid));
4515         
4516         if (hashtable_process_tag != -1)
4517                 gtk_timeout_remove(hashtable_process_tag);
4518                 
4519         hashtable_process_tag = gtk_timeout_add(100, process_hashtable, item);
4520
4521         IMAP_FOLDER_ITEM(item)->uid_list = g_slist_remove(
4522             IMAP_FOLDER_ITEM(item)->uid_list, numlist.data);
4523         dir = folder_item_get_path(item);
4524         if (is_dir_exist(dir))
4525                 remove_numbered_files(dir, uid, uid);
4526         g_free(dir);
4527
4528         return IMAP_SUCCESS;
4529 }
4530
4531 static gint compare_msginfo(gconstpointer a, gconstpointer b)
4532 {
4533         return ((MsgInfo *)a)->msgnum - ((MsgInfo *)b)->msgnum;
4534 }
4535
4536 static guint gslist_find_next_num(MsgNumberList **list, guint num)
4537 {
4538         GSList *elem;
4539
4540         g_return_val_if_fail(list != NULL, -1);
4541
4542         for (elem = *list; elem != NULL; elem = g_slist_next(elem))
4543                 if (GPOINTER_TO_INT(elem->data) >= num)
4544                         break;
4545         *list = elem;
4546         return elem != NULL ? GPOINTER_TO_INT(elem->data) : (gint)-1;
4547 }
4548
4549 /*
4550  * NEW and DELETED flags are not syncronized
4551  * - The NEW/RECENT flags in IMAP folders can not really be directly
4552  *   modified by Sylpheed
4553  * - The DELETE/DELETED flag in IMAP and Sylpheed don't have the same
4554  *   meaning, in IMAP it always removes the messages from the FolderItem
4555  *   in Sylpheed it can mean to move the message to trash
4556  */
4557
4558 typedef struct _get_flags_data {
4559         Folder *folder;
4560         FolderItem *item;
4561         MsgInfoList *msginfo_list;
4562         GRelation *msgflags;
4563         gboolean done;
4564 } get_flags_data;
4565
4566 static /*gint*/ void *imap_get_flags_thread(void *data)
4567 {
4568         get_flags_data *stuff = (get_flags_data *)data;
4569         Folder *folder = stuff->folder;
4570         FolderItem *item = stuff->item;
4571         MsgInfoList *msginfo_list = stuff->msginfo_list;
4572         GRelation *msgflags = stuff->msgflags;
4573         IMAPSession *session;
4574         GSList *sorted_list;
4575         GSList *unseen = NULL, *answered = NULL, *flagged = NULL;
4576         GSList *p_unseen, *p_answered, *p_flagged;
4577         GSList *elem;
4578         GSList *seq_list, *cur;
4579         gboolean reverse_seen = FALSE;
4580         GString *cmd_buf;
4581         gint ok;
4582         gint exists_cnt, recent_cnt, unseen_cnt, uid_next;
4583         guint32 uidvalidity;
4584         gboolean selected_folder;
4585         
4586         if (folder == NULL || item == NULL) {
4587                 stuff->done = TRUE;
4588                 return GINT_TO_POINTER(-1);
4589         }
4590         if (msginfo_list == NULL) {
4591                 stuff->done = TRUE;
4592                 return GINT_TO_POINTER(0);
4593         }
4594
4595         session = imap_session_get(folder);
4596         if (session == NULL) {
4597                 stuff->done = TRUE;
4598                 return GINT_TO_POINTER(-1);
4599         }
4600
4601         selected_folder = (session->mbox != NULL) &&
4602                           (!strcmp(session->mbox, item->path));
4603
4604         if (!selected_folder) {
4605                 ok = imap_status(session, IMAP_FOLDER(folder), item->path,
4606                          &exists_cnt, &recent_cnt, &uid_next, &uidvalidity, &unseen_cnt, TRUE);
4607                 ok = imap_select(session, IMAP_FOLDER(folder), item->path,
4608                         NULL, NULL, NULL, NULL, TRUE);
4609                 if (ok != IMAP_SUCCESS) {
4610                         stuff->done = TRUE;
4611                         return GINT_TO_POINTER(-1);
4612                 }
4613
4614         } 
4615
4616         if (unseen_cnt > exists_cnt / 2)
4617                 reverse_seen = TRUE;
4618
4619         cmd_buf = g_string_new(NULL);
4620
4621         sorted_list = g_slist_sort(g_slist_copy(msginfo_list), compare_msginfo);
4622
4623         seq_list = imap_get_seq_set_from_msglist(msginfo_list);
4624
4625         for (cur = seq_list; cur != NULL; cur = g_slist_next(cur)) {
4626                 IMAPSet imapset = cur->data;
4627
4628                 g_string_sprintf(cmd_buf, "%sSEEN UID %s", reverse_seen ? "" : "UN", imapset);
4629                 imap_cmd_search(session, cmd_buf->str, &p_unseen, TRUE);
4630                 unseen = g_slist_concat(unseen, p_unseen);
4631
4632                 g_string_sprintf(cmd_buf, "ANSWERED UID %s", imapset);
4633                 imap_cmd_search(session, cmd_buf->str, &p_answered, TRUE);
4634                 answered = g_slist_concat(answered, p_answered);
4635
4636                 g_string_sprintf(cmd_buf, "FLAGGED UID %s", imapset);
4637                 imap_cmd_search(session, cmd_buf->str, &p_flagged, TRUE);
4638                 flagged = g_slist_concat(flagged, p_flagged);
4639         }
4640
4641         p_unseen = unseen;
4642         p_answered = answered;
4643         p_flagged = flagged;
4644
4645         for (elem = sorted_list; elem != NULL; elem = g_slist_next(elem)) {
4646                 MsgInfo *msginfo;
4647                 MsgPermFlags flags;
4648                 gboolean wasnew;
4649                 
4650                 msginfo = (MsgInfo *) elem->data;
4651                 flags = msginfo->flags.perm_flags;
4652                 wasnew = (flags & MSG_NEW);
4653                 flags &= ~((reverse_seen ? 0 : MSG_UNREAD | MSG_NEW) | MSG_REPLIED | MSG_MARKED);
4654                 if (reverse_seen)
4655                         flags |= MSG_UNREAD | (wasnew ? MSG_NEW : 0);
4656                 if (gslist_find_next_num(&p_unseen, msginfo->msgnum) == msginfo->msgnum) {
4657                         if (!reverse_seen) {
4658                                 flags |= MSG_UNREAD | (wasnew ? MSG_NEW : 0);
4659                         } else {
4660                                 flags &= ~(MSG_UNREAD | MSG_NEW);
4661                         }
4662                 }
4663                 if (gslist_find_next_num(&p_answered, msginfo->msgnum) == msginfo->msgnum)
4664                         flags |= MSG_REPLIED;
4665                 if (gslist_find_next_num(&p_flagged, msginfo->msgnum) == msginfo->msgnum)
4666                         flags |= MSG_MARKED;
4667                 g_relation_insert(msgflags, msginfo, GINT_TO_POINTER(flags));
4668         }
4669
4670         imap_seq_set_free(seq_list);
4671         g_slist_free(flagged);
4672         g_slist_free(answered);
4673         g_slist_free(unseen);
4674         g_slist_free(sorted_list);
4675         g_string_free(cmd_buf, TRUE);
4676
4677         stuff->done = TRUE;
4678         return GINT_TO_POINTER(0);
4679 }
4680
4681 static gint imap_get_flags(Folder *folder, FolderItem *item,
4682                            MsgInfoList *msginfo_list, GRelation *msgflags)
4683 {
4684         gint result;
4685         get_flags_data *data = g_new0(get_flags_data, 1);
4686 #ifdef USE_PTHREAD
4687         void *tmp;
4688         pthread_t pt;
4689 #endif
4690         data->done = FALSE;
4691         data->folder = folder;
4692         data->item = item;
4693         data->msginfo_list = msginfo_list;
4694         data->msgflags = msgflags;
4695
4696         if (prefs_common.work_offline && !imap_gtk_should_override()) {
4697                 g_free(data);
4698                 return -1;
4699         }
4700
4701 #if (defined USE_PTHREAD && defined __GLIBC__ && (__GLIBC__ > 2 || (__GLIBC__ == 2 && __GLIBC_MINOR__ >= 3)))
4702         if (pthread_create(&pt, PTHREAD_CREATE_JOINABLE,
4703                         imap_get_flags_thread, data) != 0) {
4704                 result = GPOINTER_TO_INT(imap_get_flags_thread(data));
4705                 g_free(data);
4706                 return result;
4707         }
4708         debug_print("+++waiting for imap_get_flags_thread...\n");
4709         while(!data->done) {
4710                 /* don't let the interface freeze while waiting */
4711                 sylpheed_do_idle();
4712         }
4713         debug_print("---imap_get_flags_thread done\n");
4714
4715         /* get the thread's return value and clean its resources */
4716         pthread_join(pt, &tmp);
4717         result = GPOINTER_TO_INT(tmp);
4718 #else
4719         result = GPOINTER_TO_INT(imap_get_flags_thread(data));
4720 #endif
4721         g_free(data);
4722         return result;
4723
4724 }