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