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