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