update translations
[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         g_relation_destroy(uid_mapping);
1025         imap_seq_set_free(seq_list);
1026
1027         g_free(destdir);
1028
1029         if (ok == IMAP_SUCCESS)
1030                 return last_num;
1031         else
1032                 return -1;
1033 }
1034
1035 static gint imap_copy_msg(Folder *folder, FolderItem *dest, MsgInfo *msginfo)
1036 {
1037         GSList msglist;
1038
1039         g_return_val_if_fail(msginfo != NULL, -1);
1040
1041         msglist.data = msginfo;
1042         msglist.next = NULL;
1043
1044         return imap_copy_msgs(folder, dest, &msglist, NULL);
1045 }
1046
1047 static gint imap_copy_msgs(Folder *folder, FolderItem *dest, 
1048                     MsgInfoList *msglist, GRelation *relation)
1049 {
1050         MsgInfo *msginfo;
1051         GSList *file_list;
1052         gint ret;
1053
1054         g_return_val_if_fail(folder != NULL, -1);
1055         g_return_val_if_fail(dest != NULL, -1);
1056         g_return_val_if_fail(msglist != NULL, -1);
1057
1058         msginfo = (MsgInfo *)msglist->data;
1059         g_return_val_if_fail(msginfo->folder != NULL, -1);
1060
1061         if (folder == msginfo->folder->folder)
1062                 return imap_do_copy_msgs(folder, dest, msglist, relation);
1063
1064         file_list = procmsg_get_message_file_list(msglist);
1065         g_return_val_if_fail(file_list != NULL, -1);
1066
1067         ret = imap_add_msgs(folder, dest, file_list, relation);
1068
1069         procmsg_message_file_list_free(file_list);
1070
1071         return ret;
1072 }
1073
1074 static gint imap_remove_msg(Folder *folder, FolderItem *item, gint uid)
1075 {
1076         gint ok;
1077         IMAPSession *session;
1078         gchar *dir;
1079         MsgNumberList numlist;
1080         
1081         g_return_val_if_fail(folder != NULL, -1);
1082         g_return_val_if_fail(FOLDER_CLASS(folder) == &imap_class, -1);
1083         g_return_val_if_fail(item != NULL, -1);
1084
1085         session = imap_session_get(folder);
1086         if (!session) return -1;
1087
1088         ok = imap_select(session, IMAP_FOLDER(folder), item->path,
1089                          NULL, NULL, NULL, NULL);
1090         if (ok != IMAP_SUCCESS)
1091                 return ok;
1092
1093         numlist.next = NULL;
1094         numlist.data = GINT_TO_POINTER(uid);
1095         
1096         ok = imap_set_message_flags
1097                 (IMAP_SESSION(REMOTE_FOLDER(folder)->session),
1098                 &numlist, IMAP_FLAG_DELETED, TRUE);
1099         if (ok != IMAP_SUCCESS) {
1100                 log_warning(_("can't set deleted flags: %d\n"), uid);
1101                 return ok;
1102         }
1103
1104         if (!session->uidplus) {
1105                 ok = imap_cmd_expunge(session, NULL);
1106         } else {
1107                 gchar *uidstr;
1108
1109                 uidstr = g_strdup_printf("%u", uid);
1110                 ok = imap_cmd_expunge(session, uidstr);
1111                 g_free(uidstr);
1112         }
1113         if (ok != IMAP_SUCCESS) {
1114                 log_warning(_("can't expunge\n"));
1115                 return ok;
1116         }
1117
1118         IMAP_FOLDER_ITEM(item)->uid_list = g_slist_remove(
1119             IMAP_FOLDER_ITEM(item)->uid_list, numlist.data);
1120         dir = folder_item_get_path(item);
1121         if (is_dir_exist(dir))
1122                 remove_numbered_files(dir, uid, uid);
1123         g_free(dir);
1124
1125         return IMAP_SUCCESS;
1126 }
1127
1128 static gint imap_remove_all_msg(Folder *folder, FolderItem *item)
1129 {
1130         gint ok;
1131         IMAPSession *session;
1132         gchar *dir;
1133
1134         g_return_val_if_fail(folder != NULL, -1);
1135         g_return_val_if_fail(item != NULL, -1);
1136
1137         session = imap_session_get(folder);
1138         if (!session) return -1;
1139
1140         ok = imap_select(session, IMAP_FOLDER(folder), item->path,
1141                          NULL, NULL, NULL, NULL);
1142         if (ok != IMAP_SUCCESS)
1143                 return ok;
1144
1145         imap_gen_send(session, "STORE 1:* +FLAGS.SILENT (\\Deleted)");
1146         ok = imap_cmd_ok(session, NULL);
1147         if (ok != IMAP_SUCCESS) {
1148                 log_warning(_("can't set deleted flags: 1:*\n"));
1149                 return ok;
1150         }
1151
1152         ok = imap_cmd_expunge(session, NULL);
1153         if (ok != IMAP_SUCCESS) {
1154                 log_warning(_("can't expunge\n"));
1155                 return ok;
1156         }
1157
1158         dir = folder_item_get_path(item);
1159         if (is_dir_exist(dir))
1160                 remove_all_numbered_files(dir);
1161         g_free(dir);
1162
1163         return IMAP_SUCCESS;
1164 }
1165
1166 static gboolean imap_is_msg_changed(Folder *folder, FolderItem *item,
1167                                     MsgInfo *msginfo)
1168 {
1169         /* TODO: properly implement this method */
1170         return FALSE;
1171 }
1172
1173 static gint imap_close(Folder *folder, FolderItem *item)
1174 {
1175         gint ok;
1176         IMAPSession *session;
1177
1178         g_return_val_if_fail(folder != NULL, -1);
1179         g_return_val_if_fail(item != NULL, -1);
1180         g_return_val_if_fail(item->path != NULL, -1);
1181
1182         session = imap_session_get(folder);
1183         if (!session) return -1;
1184
1185         if (session->mbox) {
1186                 if (strcmp2(session->mbox, item->path) != 0) return -1;
1187
1188                 ok = imap_cmd_close(session);
1189                 if (ok != IMAP_SUCCESS)
1190                         log_warning(_("can't close folder\n"));
1191
1192                 g_free(session->mbox);
1193                 session->mbox = NULL;
1194
1195                 return ok;
1196         }
1197
1198         return 0;
1199 }
1200
1201 static gint imap_scan_tree(Folder *folder)
1202 {
1203         FolderItem *item = NULL;
1204         IMAPSession *session;
1205         gchar *root_folder = NULL;
1206
1207         g_return_val_if_fail(folder != NULL, -1);
1208         g_return_val_if_fail(folder->account != NULL, -1);
1209
1210         session = imap_session_get(folder);
1211         if (!session) {
1212                 if (!folder->node) {
1213                         folder_tree_destroy(folder);
1214                         item = folder_item_new(folder, folder->name, NULL);
1215                         item->folder = folder;
1216                         folder->node = item->node = g_node_new(item);
1217                 }
1218                 return -1;
1219         }
1220
1221         if (folder->account->imap_dir && *folder->account->imap_dir) {
1222                 gchar *real_path;
1223                 GPtrArray *argbuf;
1224                 gint ok;
1225
1226                 Xstrdup_a(root_folder, folder->account->imap_dir, return -1);
1227                 extract_quote(root_folder, '"');
1228                 subst_char(root_folder,
1229                            imap_get_path_separator(IMAP_FOLDER(folder),
1230                                                    root_folder),
1231                            '/');
1232                 strtailchomp(root_folder, '/');
1233                 real_path = imap_get_real_path
1234                         (IMAP_FOLDER(folder), root_folder);
1235                 debug_print("IMAP root directory: %s\n", real_path);
1236
1237                 /* check if root directory exist */
1238                 argbuf = g_ptr_array_new();
1239                 ok = imap_cmd_list(session, NULL, real_path, argbuf);
1240                 if (ok != IMAP_SUCCESS ||
1241                     search_array_str(argbuf, "LIST ") == NULL) {
1242                         log_warning(_("root folder %s does not exist\n"), real_path);
1243                         g_ptr_array_free(argbuf, TRUE);
1244                         g_free(real_path);
1245                         return -1;
1246                 }
1247                 g_ptr_array_free(argbuf, TRUE);
1248                 g_free(real_path);
1249         }
1250
1251         if (folder->node)
1252                 item = FOLDER_ITEM(folder->node->data);
1253         if (!item || ((item->path || root_folder) &&
1254                       strcmp2(item->path, root_folder) != 0)) {
1255                 folder_tree_destroy(folder);
1256                 item = folder_item_new(folder, folder->name, root_folder);
1257                 item->folder = folder;
1258                 folder->node = item->node = g_node_new(item);
1259         }
1260
1261         imap_scan_tree_recursive(session, FOLDER_ITEM(folder->node->data));
1262         imap_create_missing_folders(folder);
1263
1264         return 0;
1265 }
1266
1267 static gint imap_scan_tree_recursive(IMAPSession *session, FolderItem *item)
1268 {
1269         Folder *folder;
1270         IMAPFolder *imapfolder;
1271         FolderItem *new_item;
1272         GSList *item_list, *cur;
1273         GNode *node;
1274         gchar *real_path;
1275         gchar *wildcard_path;
1276         gchar separator;
1277         gchar wildcard[3];
1278
1279         g_return_val_if_fail(item != NULL, -1);
1280         g_return_val_if_fail(item->folder != NULL, -1);
1281         g_return_val_if_fail(item->no_sub == FALSE, -1);
1282
1283         folder = item->folder;
1284         imapfolder = IMAP_FOLDER(folder);
1285
1286         separator = imap_get_path_separator(imapfolder, item->path);
1287
1288         if (folder->ui_func)
1289                 folder->ui_func(folder, item, folder->ui_func_data);
1290
1291         if (item->path) {
1292                 wildcard[0] = separator;
1293                 wildcard[1] = '%';
1294                 wildcard[2] = '\0';
1295                 real_path = imap_get_real_path(imapfolder, item->path);
1296         } else {
1297                 wildcard[0] = '%';
1298                 wildcard[1] = '\0';
1299                 real_path = g_strdup("");
1300         }
1301
1302         Xstrcat_a(wildcard_path, real_path, wildcard,
1303                   {g_free(real_path); return IMAP_ERROR;});
1304         QUOTE_IF_REQUIRED(wildcard_path, wildcard_path);
1305
1306         imap_gen_send(session, "LIST \"\" %s",
1307                       wildcard_path);
1308
1309         strtailchomp(real_path, separator);
1310         item_list = imap_parse_list(imapfolder, session, real_path, NULL);
1311         g_free(real_path);
1312
1313         node = item->node->children;
1314         while (node != NULL) {
1315                 FolderItem *old_item = FOLDER_ITEM(node->data);
1316                 GNode *next = node->next;
1317
1318                 new_item = NULL;
1319                 for (cur = item_list; cur != NULL; cur = cur->next) {
1320                         FolderItem *cur_item = FOLDER_ITEM(cur->data);
1321                         if (!strcmp2(old_item->path, cur_item->path)) {
1322                                 new_item = cur_item;
1323                                 break;
1324                         }
1325                 }
1326                 if (!new_item) {
1327                         debug_print("folder '%s' not found. removing...\n",
1328                                     old_item->path);
1329                         folder_item_remove(old_item);
1330                 } else {
1331                         old_item->no_sub = new_item->no_sub;
1332                         old_item->no_select = new_item->no_select;
1333                         if (old_item->no_sub == TRUE && node->children) {
1334                                 debug_print("folder '%s' doesn't have "
1335                                             "subfolders. removing...\n",
1336                                             old_item->path);
1337                                 folder_item_remove_children(old_item);
1338                         }
1339                 }
1340
1341                 node = next;
1342         }
1343
1344         for (cur = item_list; cur != NULL; cur = cur->next) {
1345                 FolderItem *cur_item = FOLDER_ITEM(cur->data);
1346                 new_item = NULL;
1347                 for (node = item->node->children; node != NULL;
1348                      node = node->next) {
1349                         if (!strcmp2(FOLDER_ITEM(node->data)->path,
1350                                      cur_item->path)) {
1351                                 new_item = FOLDER_ITEM(node->data);
1352                                 folder_item_destroy(cur_item);
1353                                 cur_item = NULL;
1354                                 break;
1355                         }
1356                 }
1357                 if (!new_item) {
1358                         new_item = cur_item;
1359                         debug_print("new folder '%s' found.\n", new_item->path);
1360                         folder_item_append(item, new_item);
1361                 }
1362
1363                 if (!strcmp(new_item->path, "INBOX")) {
1364                         new_item->stype = F_INBOX;
1365                         folder->inbox = new_item;
1366                 } else if (!item->parent || item->stype == F_INBOX) {
1367                         gchar *base;
1368
1369                         base = g_basename(new_item->path);
1370
1371                         if (!folder->outbox && !strcasecmp(base, "Sent")) {
1372                                 new_item->stype = F_OUTBOX;
1373                                 folder->outbox = new_item;
1374                         } else if (!folder->draft && !strcasecmp(base, "Drafts")) {
1375                                 new_item->stype = F_DRAFT;
1376                                 folder->draft = new_item;
1377                         } else if (!folder->queue && !strcasecmp(base, "Queue")) {
1378                                 new_item->stype = F_QUEUE;
1379                                 folder->queue = new_item;
1380                         } else if (!folder->trash && !strcasecmp(base, "Trash")) {
1381                                 new_item->stype = F_TRASH;
1382                                 folder->trash = new_item;
1383                         }
1384                 }
1385
1386                 if (new_item->no_sub == FALSE)
1387                         imap_scan_tree_recursive(session, new_item);
1388         }
1389
1390         g_slist_free(item_list);
1391
1392         return IMAP_SUCCESS;
1393 }
1394
1395 static GSList *imap_parse_list(IMAPFolder *folder, IMAPSession *session,
1396                                const gchar *real_path, gchar *separator)
1397 {
1398         gchar buf[IMAPBUFSIZE];
1399         gchar flags[256];
1400         gchar separator_str[16];
1401         gchar *p;
1402         gchar *name;
1403         gchar *loc_name, *loc_path;
1404         GSList *item_list = NULL;
1405         GString *str;
1406         FolderItem *new_item;
1407
1408         debug_print("getting list of %s ...\n",
1409                     *real_path ? real_path : "\"\"");
1410
1411         str = g_string_new(NULL);
1412
1413         for (;;) {
1414                 if (sock_gets(SESSION(session)->sock, buf, sizeof(buf)) <= 0) {
1415                         log_warning(_("error occurred while getting LIST.\n"));
1416                         break;
1417                 }
1418                 strretchomp(buf);
1419                 if (buf[0] != '*' || buf[1] != ' ') {
1420                         log_print("IMAP4< %s\n", buf);
1421                         if (sscanf(buf, "%*d %16s", buf) < 1 ||
1422                             strcmp(buf, "OK") != 0)
1423                                 log_warning(_("error occurred while getting LIST.\n"));
1424                                 
1425                         break;
1426                 }
1427                 debug_print("IMAP4< %s\n", buf);
1428
1429                 g_string_assign(str, buf);
1430                 p = str->str + 2;
1431                 if (strncmp(p, "LIST ", 5) != 0) continue;
1432                 p += 5;
1433
1434                 if (*p != '(') continue;
1435                 p++;
1436                 p = strchr_cpy(p, ')', flags, sizeof(flags));
1437                 if (!p) continue;
1438                 while (*p == ' ') p++;
1439
1440                 p = strchr_cpy(p, ' ', separator_str, sizeof(separator_str));
1441                 if (!p) continue;
1442                 extract_quote(separator_str, '"');
1443                 if (!strcmp(separator_str, "NIL"))
1444                         separator_str[0] = '\0';
1445                 if (separator)
1446                         *separator = separator_str[0];
1447
1448                 buf[0] = '\0';
1449                 while (*p == ' ') p++;
1450                 if (*p == '{' || *p == '"')
1451                         p = imap_parse_atom(SESSION(session)->sock, p,
1452                                             buf, sizeof(buf), str);
1453                 else
1454                         strncpy2(buf, p, sizeof(buf));
1455                 strtailchomp(buf, separator_str[0]);
1456                 if (buf[0] == '\0') continue;
1457                 if (!strcmp(buf, real_path)) continue;
1458
1459                 if (separator_str[0] != '\0')
1460                         subst_char(buf, separator_str[0], '/');
1461                 name = g_basename(buf);
1462                 if (name[0] == '.') continue;
1463
1464                 loc_name = imap_modified_utf7_to_locale(name);
1465                 loc_path = imap_modified_utf7_to_locale(buf);
1466                 new_item = folder_item_new(FOLDER(folder), loc_name, loc_path);
1467                 if (strcasestr(flags, "\\Noinferiors") != NULL)
1468                         new_item->no_sub = TRUE;
1469                 if (strcmp(buf, "INBOX") != 0 &&
1470                     strcasestr(flags, "\\Noselect") != NULL)
1471                         new_item->no_select = TRUE;
1472
1473                 item_list = g_slist_append(item_list, new_item);
1474
1475                 debug_print("folder '%s' found.\n", loc_path);
1476                 g_free(loc_path);
1477                 g_free(loc_name);
1478         }
1479
1480         g_string_free(str, TRUE);
1481
1482         return item_list;
1483 }
1484
1485 static gint imap_create_tree(Folder *folder)
1486 {
1487         g_return_val_if_fail(folder != NULL, -1);
1488         g_return_val_if_fail(folder->node != NULL, -1);
1489         g_return_val_if_fail(folder->node->data != NULL, -1);
1490         g_return_val_if_fail(folder->account != NULL, -1);
1491
1492         imap_scan_tree(folder);
1493         imap_create_missing_folders(folder);
1494
1495         return 0;
1496 }
1497
1498 static void imap_create_missing_folders(Folder *folder)
1499 {
1500         g_return_if_fail(folder != NULL);
1501
1502         if (!folder->inbox)
1503                 folder->inbox = imap_create_special_folder
1504                         (folder, F_INBOX, "INBOX");
1505 #if 0
1506         if (!folder->outbox)
1507                 folder->outbox = imap_create_special_folder
1508                         (folder, F_OUTBOX, "Sent");
1509         if (!folder->draft)
1510                 folder->draft = imap_create_special_folder
1511                         (folder, F_DRAFT, "Drafts");
1512         if (!folder->queue)
1513                 folder->queue = imap_create_special_folder
1514                         (folder, F_QUEUE, "Queue");
1515 #endif
1516         if (!folder->trash)
1517                 folder->trash = imap_create_special_folder
1518                         (folder, F_TRASH, "Trash");
1519 }
1520
1521 static FolderItem *imap_create_special_folder(Folder *folder,
1522                                               SpecialFolderItemType stype,
1523                                               const gchar *name)
1524 {
1525         FolderItem *item;
1526         FolderItem *new_item;
1527
1528         g_return_val_if_fail(folder != NULL, NULL);
1529         g_return_val_if_fail(folder->node != NULL, NULL);
1530         g_return_val_if_fail(folder->node->data != NULL, NULL);
1531         g_return_val_if_fail(folder->account != NULL, NULL);
1532         g_return_val_if_fail(name != NULL, NULL);
1533
1534         item = FOLDER_ITEM(folder->node->data);
1535         new_item = imap_create_folder(folder, item, name);
1536
1537         if (!new_item) {
1538                 g_warning("Can't create '%s'\n", name);
1539                 if (!folder->inbox) return NULL;
1540
1541                 new_item = imap_create_folder(folder, folder->inbox, name);
1542                 if (!new_item)
1543                         g_warning("Can't create '%s' under INBOX\n", name);
1544                 else
1545                         new_item->stype = stype;
1546         } else
1547                 new_item->stype = stype;
1548
1549         return new_item;
1550 }
1551
1552 static gchar *imap_folder_get_path(Folder *folder)
1553 {
1554         gchar *folder_path;
1555
1556         g_return_val_if_fail(folder != NULL, NULL);
1557         g_return_val_if_fail(folder->account != NULL, NULL);
1558
1559         folder_path = g_strconcat(get_imap_cache_dir(),
1560                                   G_DIR_SEPARATOR_S,
1561                                   folder->account->recv_server,
1562                                   G_DIR_SEPARATOR_S,
1563                                   folder->account->userid,
1564                                   NULL);
1565
1566         return folder_path;
1567 }
1568
1569 static gchar *imap_item_get_path(Folder *folder, FolderItem *item)
1570 {
1571         gchar *folder_path, *path;
1572
1573         g_return_val_if_fail(folder != NULL, NULL);
1574         g_return_val_if_fail(item != NULL, NULL);
1575         folder_path = imap_folder_get_path(folder);
1576
1577         g_return_val_if_fail(folder_path != NULL, NULL);
1578         if (folder_path[0] == G_DIR_SEPARATOR) {
1579                 if (item->path)
1580                         path = g_strconcat(folder_path, G_DIR_SEPARATOR_S,
1581                                            item->path, NULL);
1582                 else
1583                         path = g_strdup(folder_path);
1584         } else {
1585                 if (item->path)
1586                         path = g_strconcat(get_home_dir(), G_DIR_SEPARATOR_S,
1587                                            folder_path, G_DIR_SEPARATOR_S,
1588                                            item->path, NULL);
1589                 else
1590                         path = g_strconcat(get_home_dir(), G_DIR_SEPARATOR_S,
1591                                            folder_path, NULL);
1592         }
1593         g_free(folder_path);
1594
1595         return path;
1596 }
1597
1598 static FolderItem *imap_create_folder(Folder *folder, FolderItem *parent,
1599                                const gchar *name)
1600 {
1601         gchar *dirpath, *imap_path;
1602         IMAPSession *session;
1603         FolderItem *new_item;
1604         gchar separator;
1605         gchar *new_name;
1606         const gchar *p;
1607         gint ok;
1608
1609         g_return_val_if_fail(folder != NULL, NULL);
1610         g_return_val_if_fail(folder->account != NULL, NULL);
1611         g_return_val_if_fail(parent != NULL, NULL);
1612         g_return_val_if_fail(name != NULL, NULL);
1613
1614         session = imap_session_get(folder);
1615         if (!session) return NULL;
1616
1617         if (!parent->parent && strcmp(name, "INBOX") == 0)
1618                 dirpath = g_strdup(name);
1619         else if (parent->path)
1620                 dirpath = g_strconcat(parent->path, "/", name, NULL);
1621         else if ((p = strchr(name, '/')) != NULL && *(p + 1) != '\0')
1622                 dirpath = g_strdup(name);
1623         else if (folder->account->imap_dir && *folder->account->imap_dir) {
1624                 gchar *imap_dir;
1625
1626                 Xstrdup_a(imap_dir, folder->account->imap_dir, return NULL);
1627                 strtailchomp(imap_dir, '/');
1628                 dirpath = g_strconcat(imap_dir, "/", name, NULL);
1629         } else
1630                 dirpath = g_strdup(name);
1631
1632         /* keep trailing directory separator to create a folder that contains
1633            sub folder */
1634         imap_path = imap_locale_to_modified_utf7(dirpath);
1635         strtailchomp(dirpath, '/');
1636         Xstrdup_a(new_name, name, {g_free(dirpath); return NULL;});
1637         strtailchomp(new_name, '/');
1638         separator = imap_get_path_separator(IMAP_FOLDER(folder), imap_path);
1639         imap_path_separator_subst(imap_path, separator);
1640         subst_char(new_name, '/', separator);
1641
1642         if (strcmp(name, "INBOX") != 0) {
1643                 GPtrArray *argbuf;
1644                 gint i;
1645                 gboolean exist = FALSE;
1646
1647                 argbuf = g_ptr_array_new();
1648                 ok = imap_cmd_list(session, NULL, imap_path,
1649                                    argbuf);
1650                 if (ok != IMAP_SUCCESS) {
1651                         log_warning(_("can't create mailbox: LIST failed\n"));
1652                         g_free(imap_path);
1653                         g_free(dirpath);
1654                         ptr_array_free_strings(argbuf);
1655                         g_ptr_array_free(argbuf, TRUE);
1656                         return NULL;
1657                 }
1658
1659                 for (i = 0; i < argbuf->len; i++) {
1660                         gchar *str;
1661                         str = g_ptr_array_index(argbuf, i);
1662                         if (!strncmp(str, "LIST ", 5)) {
1663                                 exist = TRUE;
1664                                 break;
1665                         }
1666                 }
1667                 ptr_array_free_strings(argbuf);
1668                 g_ptr_array_free(argbuf, TRUE);
1669
1670                 if (!exist) {
1671                         ok = imap_cmd_create(session, imap_path);
1672                         if (ok != IMAP_SUCCESS) {
1673                                 log_warning(_("can't create mailbox\n"));
1674                                 g_free(imap_path);
1675                                 g_free(dirpath);
1676                                 return NULL;
1677                         }
1678                 }
1679         }
1680
1681         new_item = folder_item_new(folder, new_name, dirpath);
1682         folder_item_append(parent, new_item);
1683         g_free(imap_path);
1684         g_free(dirpath);
1685
1686         dirpath = folder_item_get_path(new_item);
1687         if (!is_dir_exist(dirpath))
1688                 make_dir_hier(dirpath);
1689         g_free(dirpath);
1690
1691         return new_item;
1692 }
1693
1694 static gint imap_rename_folder(Folder *folder, FolderItem *item,
1695                                const gchar *name)
1696 {
1697         gchar *dirpath;
1698         gchar *newpath;
1699         gchar *real_oldpath;
1700         gchar *real_newpath;
1701         gchar *paths[2];
1702         gchar *old_cache_dir;
1703         gchar *new_cache_dir;
1704         IMAPSession *session;
1705         gchar separator;
1706         gint ok;
1707         gint exists, recent, unseen;
1708         guint32 uid_validity;
1709
1710         g_return_val_if_fail(folder != NULL, -1);
1711         g_return_val_if_fail(item != NULL, -1);
1712         g_return_val_if_fail(item->path != NULL, -1);
1713         g_return_val_if_fail(name != NULL, -1);
1714
1715         session = imap_session_get(folder);
1716         if (!session) return -1;
1717
1718         real_oldpath = imap_get_real_path(IMAP_FOLDER(folder), item->path);
1719
1720         g_free(session->mbox);
1721         session->mbox = NULL;
1722         ok = imap_cmd_examine(session, "INBOX",
1723                               &exists, &recent, &unseen, &uid_validity);
1724         if (ok != IMAP_SUCCESS) {
1725                 g_free(real_oldpath);
1726                 return -1;
1727         }
1728
1729         separator = imap_get_path_separator(IMAP_FOLDER(folder), item->path);
1730         if (strchr(item->path, G_DIR_SEPARATOR)) {
1731                 dirpath = g_dirname(item->path);
1732                 newpath = g_strconcat(dirpath, G_DIR_SEPARATOR_S, name, NULL);
1733                 g_free(dirpath);
1734         } else
1735                 newpath = g_strdup(name);
1736
1737         real_newpath = imap_locale_to_modified_utf7(newpath);
1738         imap_path_separator_subst(real_newpath, separator);
1739
1740         ok = imap_cmd_rename(session, real_oldpath, real_newpath);
1741         if (ok != IMAP_SUCCESS) {
1742                 log_warning(_("can't rename mailbox: %s to %s\n"),
1743                             real_oldpath, real_newpath);
1744                 g_free(real_oldpath);
1745                 g_free(newpath);
1746                 g_free(real_newpath);
1747                 return -1;
1748         }
1749
1750         g_free(item->name);
1751         item->name = g_strdup(name);
1752
1753         old_cache_dir = folder_item_get_path(item);
1754
1755         paths[0] = g_strdup(item->path);
1756         paths[1] = newpath;
1757         g_node_traverse(item->node, G_PRE_ORDER, G_TRAVERSE_ALL, -1,
1758                         imap_rename_folder_func, paths);
1759
1760         if (is_dir_exist(old_cache_dir)) {
1761                 new_cache_dir = folder_item_get_path(item);
1762                 if (rename(old_cache_dir, new_cache_dir) < 0) {
1763                         FILE_OP_ERROR(old_cache_dir, "rename");
1764                 }
1765                 g_free(new_cache_dir);
1766         }
1767
1768         g_free(old_cache_dir);
1769         g_free(paths[0]);
1770         g_free(newpath);
1771         g_free(real_oldpath);
1772         g_free(real_newpath);
1773
1774         return 0;
1775 }
1776
1777 static gint imap_remove_folder(Folder *folder, FolderItem *item)
1778 {
1779         gint ok;
1780         IMAPSession *session;
1781         gchar *path;
1782         gchar *cache_dir;
1783         gint exists, recent, unseen;
1784         guint32 uid_validity;
1785
1786         g_return_val_if_fail(folder != NULL, -1);
1787         g_return_val_if_fail(item != NULL, -1);
1788         g_return_val_if_fail(item->path != NULL, -1);
1789
1790         session = imap_session_get(folder);
1791         if (!session) return -1;
1792
1793         path = imap_get_real_path(IMAP_FOLDER(folder), item->path);
1794
1795         ok = imap_cmd_examine(session, "INBOX",
1796                               &exists, &recent, &unseen, &uid_validity);
1797         if (ok != IMAP_SUCCESS) {
1798                 g_free(path);
1799                 return -1;
1800         }
1801
1802         ok = imap_cmd_delete(session, path);
1803         if (ok != IMAP_SUCCESS) {
1804                 log_warning(_("can't delete mailbox\n"));
1805                 g_free(path);
1806                 return -1;
1807         }
1808
1809         g_free(path);
1810         cache_dir = folder_item_get_path(item);
1811         if (is_dir_exist(cache_dir) && remove_dir_recursive(cache_dir) < 0)
1812                 g_warning("can't remove directory '%s'\n", cache_dir);
1813         g_free(cache_dir);
1814         folder_item_remove(item);
1815
1816         return 0;
1817 }
1818
1819 static GSList *imap_get_uncached_messages(IMAPSession *session,
1820                                           FolderItem *item,
1821                                           MsgNumberList *numlist)
1822 {
1823         gchar *tmp;
1824         GSList *newlist = NULL;
1825         GSList *llast = NULL;
1826         GString *str;
1827         MsgInfo *msginfo;
1828         GSList *seq_list, *cur;
1829         IMAPSet imapset;
1830
1831         g_return_val_if_fail(session != NULL, NULL);
1832         g_return_val_if_fail(item != NULL, NULL);
1833         g_return_val_if_fail(item->folder != NULL, NULL);
1834         g_return_val_if_fail(FOLDER_CLASS(item->folder) == &imap_class, NULL);
1835
1836         seq_list = imap_get_seq_set_from_numlist(numlist);
1837         for (cur = seq_list; cur != NULL; cur = g_slist_next(cur)) {
1838                 imapset = cur->data;
1839
1840                 if (imap_cmd_envelope(session, imapset)
1841                     != IMAP_SUCCESS) {
1842                         log_warning(_("can't get envelope\n"));
1843                         continue;
1844                 }
1845
1846                 str = g_string_new(NULL);
1847
1848                 for (;;) {
1849                         if ((tmp = sock_getline(SESSION(session)->sock)) == NULL) {
1850                                 log_warning(_("error occurred while getting envelope.\n"));
1851                                 g_string_free(str, TRUE);
1852                                 break;
1853                         }
1854                         strretchomp(tmp);
1855                         if (tmp[0] != '*' || tmp[1] != ' ') {
1856                                 log_print("IMAP4< %s\n", tmp);
1857                                 g_free(tmp);
1858                                 break;
1859                         }
1860                         if (strstr(tmp, "FETCH") == NULL) {
1861                                 log_print("IMAP4< %s\n", tmp);
1862                                 g_free(tmp);
1863                                 continue;
1864                         }
1865                         log_print("IMAP4< %s\n", tmp);
1866                         g_string_assign(str, tmp);
1867                         g_free(tmp);
1868
1869                         msginfo = imap_parse_envelope
1870                                 (SESSION(session)->sock, item, str);
1871                         if (!msginfo) {
1872                                 log_warning(_("can't parse envelope: %s\n"), str->str);
1873                                 continue;
1874                         }
1875                         if (item->stype == F_QUEUE) {
1876                                 MSG_SET_TMP_FLAGS(msginfo->flags, MSG_QUEUED);
1877                         } else if (item->stype == F_DRAFT) {
1878                                 MSG_SET_TMP_FLAGS(msginfo->flags, MSG_DRAFT);
1879                         }
1880
1881                         msginfo->folder = item;
1882
1883                         if (!newlist)
1884                                 llast = newlist = g_slist_append(newlist, msginfo);
1885                         else {
1886                                 llast = g_slist_append(llast, msginfo);
1887                                 llast = llast->next;
1888                         }
1889                 }
1890
1891                 g_string_free(str, TRUE);
1892         }
1893         imap_seq_set_free(seq_list);
1894
1895         return newlist;
1896 }
1897
1898 static void imap_delete_all_cached_messages(FolderItem *item)
1899 {
1900         gchar *dir;
1901
1902         g_return_if_fail(item != NULL);
1903         g_return_if_fail(item->folder != NULL);
1904         g_return_if_fail(FOLDER_CLASS(item->folder) == &imap_class);
1905
1906         debug_print("Deleting all cached messages...\n");
1907
1908         dir = folder_item_get_path(item);
1909         if (is_dir_exist(dir))
1910                 remove_all_numbered_files(dir);
1911         g_free(dir);
1912
1913         debug_print("done.\n");
1914 }
1915
1916 #if USE_OPENSSL
1917 static SockInfo *imap_open_tunnel(const gchar *server,
1918                            const gchar *tunnelcmd,
1919                            SSLType ssl_type)
1920 #else
1921 static SockInfo *imap_open_tunnel(const gchar *server,
1922                            const gchar *tunnelcmd)
1923 #endif
1924 {
1925         SockInfo *sock;
1926
1927         if ((sock = sock_connect_cmd(server, tunnelcmd)) == NULL) {
1928                 log_warning(_("Can't establish IMAP4 session with: %s\n"),
1929                             server);
1930                 return NULL;
1931         }
1932 #if USE_OPENSSL
1933         return imap_init_sock(sock, ssl_type);
1934 #else
1935         return imap_init_sock(sock);
1936 #endif
1937 }
1938
1939
1940 #if USE_OPENSSL
1941 static SockInfo *imap_open(const gchar *server, gushort port,
1942                            SSLType ssl_type)
1943 #else
1944 static SockInfo *imap_open(const gchar *server, gushort port)
1945 #endif
1946 {
1947         SockInfo *sock;
1948
1949         if ((sock = sock_connect(server, port)) == NULL) {
1950                 log_warning(_("Can't connect to IMAP4 server: %s:%d\n"),
1951                             server, port);
1952                 return NULL;
1953         }
1954
1955 #if USE_OPENSSL
1956         if (ssl_type == SSL_TUNNEL && !ssl_init_socket(sock)) {
1957                 log_warning(_("Can't establish IMAP4 session with: %s:%d\n"),
1958                             server, port);
1959                 sock_close(sock);
1960                 return NULL;
1961         }
1962 #endif
1963         return sock;
1964 }
1965
1966 #if USE_OPENSSL
1967 static SockInfo *imap_init_sock(SockInfo *sock, SSLType ssl_type)
1968 #else
1969 static SockInfo *imap_init_sock(SockInfo *sock)
1970 #endif
1971 {
1972
1973         return sock;
1974 }
1975
1976 static GList *imap_parse_namespace_str(gchar *str)
1977 {
1978         gchar *p = str;
1979         gchar *name;
1980         gchar *separator;
1981         IMAPNameSpace *namespace;
1982         GList *ns_list = NULL;
1983
1984         while (*p != '\0') {
1985                 /* parse ("#foo" "/") */
1986
1987                 while (*p && *p != '(') p++;
1988                 if (*p == '\0') break;
1989                 p++;
1990
1991                 while (*p && *p != '"') p++;
1992                 if (*p == '\0') break;
1993                 p++;
1994                 name = p;
1995
1996                 while (*p && *p != '"') p++;
1997                 if (*p == '\0') break;
1998                 *p = '\0';
1999                 p++;
2000
2001                 while (*p && isspace(*p)) p++;
2002                 if (*p == '\0') break;
2003                 if (strncmp(p, "NIL", 3) == 0)
2004                         separator = NULL;
2005                 else if (*p == '"') {
2006                         p++;
2007                         separator = p;
2008                         while (*p && *p != '"') p++;
2009                         if (*p == '\0') break;
2010                         *p = '\0';
2011                         p++;
2012                 } else break;
2013
2014                 while (*p && *p != ')') p++;
2015                 if (*p == '\0') break;
2016                 p++;
2017
2018                 namespace = g_new(IMAPNameSpace, 1);
2019                 namespace->name = g_strdup(name);
2020                 namespace->separator = separator ? separator[0] : '\0';
2021                 ns_list = g_list_append(ns_list, namespace);
2022         }
2023
2024         return ns_list;
2025 }
2026
2027 static void imap_parse_namespace(IMAPSession *session, IMAPFolder *folder)
2028 {
2029         gchar *ns_str;
2030         gchar **str_array;
2031
2032         g_return_if_fail(session != NULL);
2033         g_return_if_fail(folder != NULL);
2034
2035         if (folder->ns_personal != NULL ||
2036             folder->ns_others   != NULL ||
2037             folder->ns_shared   != NULL)
2038                 return;
2039
2040         if (!imap_has_capability(session, "NAMESPACE")) {
2041                 imap_get_namespace_by_list(session, folder);
2042                 return;
2043         }
2044         
2045         if (imap_cmd_namespace(session, &ns_str)
2046             != IMAP_SUCCESS) {
2047                 log_warning(_("can't get namespace\n"));
2048                 return;
2049         }
2050
2051         str_array = strsplit_parenthesis(ns_str, '(', ')', 3);
2052         if (str_array == NULL) {
2053                 g_free(ns_str);
2054                 imap_get_namespace_by_list(session, folder);
2055                 return;
2056         }
2057         if (str_array[0])
2058                 folder->ns_personal = imap_parse_namespace_str(str_array[0]);
2059         if (str_array[0] && str_array[1])
2060                 folder->ns_others = imap_parse_namespace_str(str_array[1]);
2061         if (str_array[0] && str_array[1] && str_array[2])
2062                 folder->ns_shared = imap_parse_namespace_str(str_array[2]);
2063         g_strfreev(str_array);
2064         g_free(ns_str);
2065 }
2066
2067 static void imap_get_namespace_by_list(IMAPSession *session, IMAPFolder *folder)
2068 {
2069         GSList *item_list, *cur;
2070         gchar separator = '\0';
2071         IMAPNameSpace *namespace;
2072
2073         g_return_if_fail(session != NULL);
2074         g_return_if_fail(folder != NULL);
2075
2076         if (folder->ns_personal != NULL ||
2077             folder->ns_others   != NULL ||
2078             folder->ns_shared   != NULL)
2079                 return;
2080
2081         imap_gen_send(session, "LIST \"\" \"\"");
2082         item_list = imap_parse_list(folder, session, "", &separator);
2083         for (cur = item_list; cur != NULL; cur = cur->next)
2084                 folder_item_destroy(FOLDER_ITEM(cur->data));
2085         g_slist_free(item_list);
2086
2087         namespace = g_new(IMAPNameSpace, 1);
2088         namespace->name = g_strdup("");
2089         namespace->separator = separator;
2090         folder->ns_personal = g_list_append(NULL, namespace);
2091 }
2092
2093 static IMAPNameSpace *imap_find_namespace_from_list(GList *ns_list,
2094                                                     const gchar *path)
2095 {
2096         IMAPNameSpace *namespace = NULL;
2097         gchar *tmp_path, *name;
2098
2099         if (!path) path = "";
2100
2101         for (; ns_list != NULL; ns_list = ns_list->next) {
2102                 IMAPNameSpace *tmp_ns = ns_list->data;
2103
2104                 Xstrcat_a(tmp_path, path, "/", return namespace);
2105                 Xstrdup_a(name, tmp_ns->name, return namespace);
2106                 if (tmp_ns->separator && tmp_ns->separator != '/') {
2107                         subst_char(tmp_path, tmp_ns->separator, '/');
2108                         subst_char(name, tmp_ns->separator, '/');
2109                 }
2110                 if (strncmp(tmp_path, name, strlen(name)) == 0)
2111                         namespace = tmp_ns;
2112         }
2113
2114         return namespace;
2115 }
2116
2117 static IMAPNameSpace *imap_find_namespace(IMAPFolder *folder,
2118                                           const gchar *path)
2119 {
2120         IMAPNameSpace *namespace;
2121
2122         g_return_val_if_fail(folder != NULL, NULL);
2123
2124         namespace = imap_find_namespace_from_list(folder->ns_personal, path);
2125         if (namespace) return namespace;
2126         namespace = imap_find_namespace_from_list(folder->ns_others, path);
2127         if (namespace) return namespace;
2128         namespace = imap_find_namespace_from_list(folder->ns_shared, path);
2129         if (namespace) return namespace;
2130
2131         return NULL;
2132 }
2133
2134 static gchar imap_get_path_separator(IMAPFolder *folder, const gchar *path)
2135 {
2136         IMAPNameSpace *namespace;
2137         gchar separator = '/';
2138
2139         namespace = imap_find_namespace(folder, path);
2140         if (namespace && namespace->separator)
2141                 separator = namespace->separator;
2142
2143         return separator;
2144 }
2145
2146 static gchar *imap_get_real_path(IMAPFolder *folder, const gchar *path)
2147 {
2148         gchar *real_path;
2149         gchar separator;
2150
2151         g_return_val_if_fail(folder != NULL, NULL);
2152         g_return_val_if_fail(path != NULL, NULL);
2153
2154         real_path = imap_locale_to_modified_utf7(path);
2155         separator = imap_get_path_separator(folder, path);
2156         imap_path_separator_subst(real_path, separator);
2157
2158         return real_path;
2159 }
2160
2161 static gchar *imap_parse_atom(SockInfo *sock, gchar *src,
2162                               gchar *dest, gint dest_len, GString *str)
2163 {
2164         gchar *cur_pos = src;
2165         gchar *nextline;
2166
2167         g_return_val_if_fail(str != NULL, cur_pos);
2168
2169         /* read the next line if the current response buffer is empty */
2170         while (isspace(*cur_pos)) cur_pos++;
2171         while (*cur_pos == '\0') {
2172                 if ((nextline = sock_getline(sock)) == NULL)
2173                         return cur_pos;
2174                 g_string_assign(str, nextline);
2175                 cur_pos = str->str;
2176                 strretchomp(nextline);
2177                 /* log_print("IMAP4< %s\n", nextline); */
2178                 debug_print("IMAP4< %s\n", nextline);
2179                 g_free(nextline);
2180
2181                 while (isspace(*cur_pos)) cur_pos++;
2182         }
2183
2184         if (!strncmp(cur_pos, "NIL", 3)) {
2185                 *dest = '\0';
2186                 cur_pos += 3;
2187         } else if (*cur_pos == '\"') {
2188                 gchar *p;
2189
2190                 p = get_quoted(cur_pos, '\"', dest, dest_len);
2191                 cur_pos = p ? p : cur_pos + 2;
2192         } else if (*cur_pos == '{') {
2193                 gchar buf[32];
2194                 gint len;
2195                 gint line_len = 0;
2196
2197                 cur_pos = strchr_cpy(cur_pos + 1, '}', buf, sizeof(buf));
2198                 len = atoi(buf);
2199                 g_return_val_if_fail(len >= 0, cur_pos);
2200
2201                 g_string_truncate(str, 0);
2202                 cur_pos = str->str;
2203
2204                 do {
2205                         if ((nextline = sock_getline(sock)) == NULL)
2206                                 return cur_pos;
2207                         line_len += strlen(nextline);
2208                         g_string_append(str, nextline);
2209                         cur_pos = str->str;
2210                         strretchomp(nextline);
2211                         /* log_print("IMAP4< %s\n", nextline); */
2212                         debug_print("IMAP4< %s\n", nextline);
2213                         g_free(nextline);
2214                 } while (line_len < len);
2215
2216                 memcpy(dest, cur_pos, MIN(len, dest_len - 1));
2217                 dest[MIN(len, dest_len - 1)] = '\0';
2218                 cur_pos += len;
2219         }
2220
2221         return cur_pos;
2222 }
2223
2224 static gchar *imap_get_header(SockInfo *sock, gchar *cur_pos, gchar **headers,
2225                               GString *str)
2226 {
2227         gchar *nextline;
2228         gchar buf[32];
2229         gint len;
2230         gint block_len = 0;
2231
2232         *headers = NULL;
2233
2234         g_return_val_if_fail(str != NULL, cur_pos);
2235
2236         while (isspace(*cur_pos)) cur_pos++;
2237
2238         g_return_val_if_fail(*cur_pos == '{', cur_pos);
2239
2240         cur_pos = strchr_cpy(cur_pos + 1, '}', buf, sizeof(buf));
2241         len = atoi(buf);
2242         g_return_val_if_fail(len >= 0, cur_pos);
2243
2244         g_string_truncate(str, 0);
2245         cur_pos = str->str;
2246
2247         do {
2248                 if ((nextline = sock_getline(sock)) == NULL)
2249                         return cur_pos;
2250                 block_len += strlen(nextline);
2251                 g_string_append(str, nextline);
2252                 cur_pos = str->str;
2253                 strretchomp(nextline);
2254                 /* debug_print("IMAP4< %s\n", nextline); */
2255                 g_free(nextline);
2256         } while (block_len < len);
2257
2258         debug_print("IMAP4< [contents of BODY.PEEK[HEADER.FIELDS (...)]]\n");
2259
2260         *headers = g_strndup(cur_pos, len);
2261         cur_pos += len;
2262
2263         while (isspace(*cur_pos)) cur_pos++;
2264         while (*cur_pos == '\0') {
2265                 if ((nextline = sock_getline(sock)) == NULL)
2266                         return cur_pos;
2267                 g_string_assign(str, nextline);
2268                 cur_pos = str->str;
2269                 strretchomp(nextline);
2270                 debug_print("IMAP4< %s\n", nextline);
2271                 g_free(nextline);
2272
2273                 while (isspace(*cur_pos)) cur_pos++;
2274         }
2275
2276         return cur_pos;
2277 }
2278
2279 static MsgFlags imap_parse_flags(const gchar *flag_str)  
2280 {
2281         const gchar *p = flag_str;
2282         MsgFlags flags = {0, 0};
2283
2284         flags.perm_flags = MSG_UNREAD;
2285
2286         while ((p = strchr(p, '\\')) != NULL) {
2287                 p++;
2288
2289                 if (g_strncasecmp(p, "Recent", 6) == 0 && MSG_IS_UNREAD(flags)) {
2290                         MSG_SET_PERM_FLAGS(flags, MSG_NEW);
2291                 } else if (g_strncasecmp(p, "Seen", 4) == 0) {
2292                         MSG_UNSET_PERM_FLAGS(flags, MSG_NEW|MSG_UNREAD);
2293                 } else if (g_strncasecmp(p, "Deleted", 7) == 0) {
2294                         MSG_SET_PERM_FLAGS(flags, MSG_DELETED);
2295                 } else if (g_strncasecmp(p, "Flagged", 7) == 0) {
2296                         MSG_SET_PERM_FLAGS(flags, MSG_MARKED);
2297                 } else if (g_strncasecmp(p, "Answered", 8) == 0) {
2298                         MSG_SET_PERM_FLAGS(flags, MSG_REPLIED);
2299                 }
2300         }
2301
2302         return flags;
2303 }
2304
2305 static MsgInfo *imap_parse_envelope(SockInfo *sock, FolderItem *item,
2306                                     GString *line_str)
2307 {
2308         gchar buf[IMAPBUFSIZE];
2309         MsgInfo *msginfo = NULL;
2310         gchar *cur_pos;
2311         gint msgnum;
2312         guint32 uid = 0;
2313         size_t size = 0;
2314         MsgFlags flags = {0, 0}, imap_flags = {0, 0};
2315
2316         g_return_val_if_fail(line_str != NULL, NULL);
2317         g_return_val_if_fail(line_str->str[0] == '*' &&
2318                              line_str->str[1] == ' ', NULL);
2319
2320         MSG_SET_TMP_FLAGS(flags, MSG_IMAP);
2321         if (item->stype == F_QUEUE) {
2322                 MSG_SET_TMP_FLAGS(flags, MSG_QUEUED);
2323         } else if (item->stype == F_DRAFT) {
2324                 MSG_SET_TMP_FLAGS(flags, MSG_DRAFT);
2325         }
2326
2327         cur_pos = line_str->str + 2;
2328
2329 #define PARSE_ONE_ELEMENT(ch)                                   \
2330 {                                                               \
2331         cur_pos = strchr_cpy(cur_pos, ch, buf, sizeof(buf));    \
2332         if (cur_pos == NULL) {                                  \
2333                 g_warning("cur_pos == NULL\n");                 \
2334                 procmsg_msginfo_free(msginfo);                  \
2335                 return NULL;                                    \
2336         }                                                       \
2337 }
2338
2339         PARSE_ONE_ELEMENT(' ');
2340         msgnum = atoi(buf);
2341
2342         PARSE_ONE_ELEMENT(' ');
2343         g_return_val_if_fail(!strcmp(buf, "FETCH"), NULL);
2344
2345         g_return_val_if_fail(*cur_pos == '(', NULL);
2346         cur_pos++;
2347
2348         while (*cur_pos != '\0' && *cur_pos != ')') {
2349                 while (*cur_pos == ' ') cur_pos++;
2350
2351                 if (!strncmp(cur_pos, "UID ", 4)) {
2352                         cur_pos += 4;
2353                         uid = strtoul(cur_pos, &cur_pos, 10);
2354                 } else if (!strncmp(cur_pos, "FLAGS ", 6)) {
2355                         cur_pos += 6;
2356                         if (*cur_pos != '(') {
2357                                 g_warning("*cur_pos != '('\n");
2358                                 procmsg_msginfo_free(msginfo);
2359                                 return NULL;
2360                         }
2361                         cur_pos++;
2362                         PARSE_ONE_ELEMENT(')');
2363                         imap_flags = imap_parse_flags(buf);
2364                 } else if (!strncmp(cur_pos, "RFC822.SIZE ", 12)) {
2365                         cur_pos += 12;
2366                         size = strtol(cur_pos, &cur_pos, 10);
2367                 } else if (!strncmp(cur_pos, "BODY[HEADER.FIELDS ", 19)) {
2368                         gchar *headers;
2369
2370                         cur_pos += 19;
2371                         if (*cur_pos != '(') {
2372                                 g_warning("*cur_pos != '('\n");
2373                                 procmsg_msginfo_free(msginfo);
2374                                 return NULL;
2375                         }
2376                         cur_pos++;
2377                         PARSE_ONE_ELEMENT(')');
2378                         if (*cur_pos != ']') {
2379                                 g_warning("*cur_pos != ']'\n");
2380                                 procmsg_msginfo_free(msginfo);
2381                                 return NULL;
2382                         }
2383                         cur_pos++;
2384
2385                         cur_pos = imap_get_header(sock, cur_pos, &headers,
2386                                                   line_str);
2387                         msginfo = procheader_parse_str(headers, flags, FALSE, FALSE);
2388                         g_free(headers);
2389                 } else {
2390                         g_warning("invalid FETCH response: %s\n", cur_pos);
2391                         break;
2392                 }
2393         }
2394
2395         if (msginfo) {
2396                 msginfo->msgnum = uid;
2397                 msginfo->size = size;
2398                 msginfo->flags.tmp_flags |= imap_flags.tmp_flags;
2399                 msginfo->flags.perm_flags = imap_flags.perm_flags;
2400         }
2401
2402         return msginfo;
2403 }
2404
2405 static gchar *imap_get_flag_str(IMAPFlags flags)
2406 {
2407         GString *str;
2408         gchar *ret;
2409
2410         str = g_string_new(NULL);
2411
2412         if (IMAP_IS_SEEN(flags))        g_string_append(str, "\\Seen ");
2413         if (IMAP_IS_ANSWERED(flags))    g_string_append(str, "\\Answered ");
2414         if (IMAP_IS_FLAGGED(flags))     g_string_append(str, "\\Flagged ");
2415         if (IMAP_IS_DELETED(flags))     g_string_append(str, "\\Deleted ");
2416         if (IMAP_IS_DRAFT(flags))       g_string_append(str, "\\Draft");
2417
2418         if (str->len > 0 && str->str[str->len - 1] == ' ')
2419                 g_string_truncate(str, str->len - 1);
2420
2421         ret = str->str;
2422         g_string_free(str, FALSE);
2423
2424         return ret;
2425 }
2426
2427 static gint imap_set_message_flags(IMAPSession *session,
2428                                    MsgNumberList *numlist,
2429                                    IMAPFlags flags,
2430                                    gboolean is_set)
2431 {
2432         gchar *cmd;
2433         gchar *flag_str;
2434         gint ok;
2435         GSList *seq_list, *cur;
2436         IMAPSet imapset;
2437
2438         flag_str = imap_get_flag_str(flags);
2439         cmd = g_strconcat(is_set ? "+FLAGS.SILENT (" : "-FLAGS.SILENT (",
2440                           flag_str, ")", NULL);
2441         g_free(flag_str);
2442
2443         seq_list = imap_get_seq_set_from_numlist(numlist);
2444         for (cur = seq_list; cur != NULL; cur = g_slist_next(cur)) {
2445                 imapset = cur->data;
2446
2447                 ok = imap_cmd_store(session, imapset, cmd);
2448         }
2449         imap_seq_set_free(seq_list);
2450         g_free(cmd);
2451
2452         return ok;
2453 }
2454
2455 static gint imap_select(IMAPSession *session, IMAPFolder *folder,
2456                         const gchar *path,
2457                         gint *exists, gint *recent, gint *unseen,
2458                         guint32 *uid_validity)
2459 {
2460         gchar *real_path;
2461         gint ok;
2462         gint exists_, recent_, unseen_, uid_validity_;
2463
2464         if (!exists || !recent || !unseen || !uid_validity) {
2465                 if (session->mbox && strcmp(session->mbox, path) == 0)
2466                         return IMAP_SUCCESS;
2467                 exists = &exists_;
2468                 recent = &recent_;
2469                 unseen = &unseen_;
2470                 uid_validity = &uid_validity_;
2471         }
2472
2473         g_free(session->mbox);
2474         session->mbox = NULL;
2475
2476         real_path = imap_get_real_path(folder, path);
2477         ok = imap_cmd_select(session, real_path,
2478                              exists, recent, unseen, uid_validity);
2479         if (ok != IMAP_SUCCESS)
2480                 log_warning(_("can't select folder: %s\n"), real_path);
2481         else {
2482                 session->mbox = g_strdup(path);
2483                 session->folder_content_changed = FALSE;
2484         }
2485         g_free(real_path);
2486
2487         return ok;
2488 }
2489
2490 #define THROW(err) { ok = err; goto catch; }
2491
2492 static gint imap_status(IMAPSession *session, IMAPFolder *folder,
2493                         const gchar *path,
2494                         gint *messages, gint *recent,
2495                         guint32 *uid_next, guint32 *uid_validity,
2496                         gint *unseen)
2497 {
2498         gchar *real_path;
2499         gchar *real_path_;
2500         gint ok;
2501         GPtrArray *argbuf = NULL;
2502         gchar *str;
2503
2504         if (messages && recent && uid_next && uid_validity && unseen) {
2505                 *messages = *recent = *uid_next = *uid_validity = *unseen = 0;
2506                 argbuf = g_ptr_array_new();
2507         }
2508
2509         real_path = imap_get_real_path(folder, path);
2510         QUOTE_IF_REQUIRED(real_path_, real_path);
2511         imap_gen_send(session, "STATUS %s "
2512                           "(MESSAGES RECENT UIDNEXT UIDVALIDITY UNSEEN)",
2513                           real_path_);
2514
2515         ok = imap_cmd_ok(session, argbuf);
2516         if (ok != IMAP_SUCCESS || !argbuf) THROW(ok);
2517
2518         str = search_array_str(argbuf, "STATUS");
2519         if (!str) THROW(IMAP_ERROR);
2520
2521         str = strchr(str, '(');
2522         if (!str) THROW(IMAP_ERROR);
2523         str++;
2524         while (*str != '\0' && *str != ')') {
2525                 while (*str == ' ') str++;
2526
2527                 if (!strncmp(str, "MESSAGES ", 9)) {
2528                         str += 9;
2529                         *messages = strtol(str, &str, 10);
2530                 } else if (!strncmp(str, "RECENT ", 7)) {
2531                         str += 7;
2532                         *recent = strtol(str, &str, 10);
2533                 } else if (!strncmp(str, "UIDNEXT ", 8)) {
2534                         str += 8;
2535                         *uid_next = strtoul(str, &str, 10);
2536                 } else if (!strncmp(str, "UIDVALIDITY ", 12)) {
2537                         str += 12;
2538                         *uid_validity = strtoul(str, &str, 10);
2539                 } else if (!strncmp(str, "UNSEEN ", 7)) {
2540                         str += 7;
2541                         *unseen = strtol(str, &str, 10);
2542                 } else {
2543                         g_warning("invalid STATUS response: %s\n", str);
2544                         break;
2545                 }
2546         }
2547
2548 catch:
2549         g_free(real_path);
2550         if (argbuf) {
2551                 ptr_array_free_strings(argbuf);
2552                 g_ptr_array_free(argbuf, TRUE);
2553         }
2554
2555         return ok;
2556 }
2557
2558 #undef THROW
2559
2560 static gboolean imap_has_capability(IMAPSession *session, const gchar *cap)
2561 {
2562         gchar **p;
2563         
2564         for (p = session->capability; *p != NULL; ++p) {
2565                 if (!g_strcasecmp(*p, cap))
2566                         return TRUE;
2567         }
2568
2569         return FALSE;
2570 }
2571
2572 static void imap_free_capabilities(IMAPSession *session)
2573 {
2574         g_strfreev(session->capability);
2575         session->capability = NULL;
2576 }
2577
2578 /* low-level IMAP4rev1 commands */
2579
2580 static gint imap_cmd_authenticate(IMAPSession *session, const gchar *user,
2581                                   const gchar *pass, IMAPAuthType type)
2582 {
2583         gchar *auth_type;
2584         gint ok;
2585         gchar *buf = NULL;
2586         gchar *challenge;
2587         gint challenge_len;
2588         gchar hexdigest[33];
2589         gchar *response;
2590         gchar *response64;
2591
2592         auth_type = "CRAM-MD5";
2593
2594         imap_gen_send(session, "AUTHENTICATE %s", auth_type);
2595         ok = imap_gen_recv(session, &buf);
2596         if (ok != IMAP_SUCCESS || buf[0] != '+' || buf[1] != ' ') {
2597                 g_free(buf);
2598                 return IMAP_ERROR;
2599         }
2600
2601         challenge = g_malloc(strlen(buf + 2) + 1);
2602         challenge_len = base64_decode(challenge, buf + 2, -1);
2603         challenge[challenge_len] = '\0';
2604         g_free(buf);
2605         log_print("IMAP< [Decoded: %s]\n", challenge);
2606
2607         md5_hex_hmac(hexdigest, challenge, challenge_len, pass, strlen(pass));
2608         g_free(challenge);
2609
2610         response = g_strdup_printf("%s %s", user, hexdigest);
2611         log_print("IMAP> [Encoded: %s]\n", response);
2612         response64 = g_malloc((strlen(response) + 3) * 2 + 1);
2613         base64_encode(response64, response, strlen(response));
2614         g_free(response);
2615
2616         log_print("IMAP> %s\n", response64);
2617         sock_puts(SESSION(session)->sock, response64);
2618         ok = imap_cmd_ok(session, NULL);
2619         if (ok != IMAP_SUCCESS)
2620                 log_warning(_("IMAP4 authentication failed.\n"));
2621
2622         return ok;
2623 }
2624
2625 static gint imap_cmd_login(IMAPSession *session,
2626                            const gchar *user, const gchar *pass)
2627 {
2628         gchar *user_, *pass_;
2629         gint ok;
2630
2631         QUOTE_IF_REQUIRED(user_, user);
2632         QUOTE_IF_REQUIRED(pass_, pass);
2633         imap_gen_send(session, "LOGIN %s %s", user_, pass_);
2634
2635         ok = imap_cmd_ok(session, NULL);
2636         if (ok != IMAP_SUCCESS)
2637                 log_warning(_("IMAP4 login failed.\n"));
2638
2639         return ok;
2640 }
2641
2642 static gint imap_cmd_logout(IMAPSession *session)
2643 {
2644         imap_gen_send(session, "LOGOUT");
2645         return imap_cmd_ok(session, NULL);
2646 }
2647
2648 static gint imap_cmd_noop(IMAPSession *session)
2649 {
2650         imap_gen_send(session, "NOOP");
2651         return imap_cmd_ok(session, NULL);
2652 }
2653
2654 static gint imap_cmd_starttls(IMAPSession *session)
2655 {
2656         imap_gen_send(session, "STARTTLS");
2657         return imap_cmd_ok(session, NULL);
2658 }
2659
2660 #define THROW(err) { ok = err; goto catch; }
2661
2662 static gint imap_cmd_namespace(IMAPSession *session, gchar **ns_str)
2663 {
2664         gint ok;
2665         GPtrArray *argbuf;
2666         gchar *str;
2667
2668         argbuf = g_ptr_array_new();
2669
2670         imap_gen_send(session, "NAMESPACE");
2671         if ((ok = imap_cmd_ok(session, argbuf)) != IMAP_SUCCESS) THROW(ok);
2672
2673         str = search_array_str(argbuf, "NAMESPACE");
2674         if (!str) THROW(IMAP_ERROR);
2675
2676         *ns_str = g_strdup(str);
2677
2678 catch:
2679         ptr_array_free_strings(argbuf);
2680         g_ptr_array_free(argbuf, TRUE);
2681
2682         return ok;
2683 }
2684
2685 #undef THROW
2686
2687 static gint imap_cmd_list(IMAPSession *session, const gchar *ref,
2688                           const gchar *mailbox, GPtrArray *argbuf)
2689 {
2690         gchar *ref_, *mailbox_;
2691
2692         if (!ref) ref = "\"\"";
2693         if (!mailbox) mailbox = "\"\"";
2694
2695         QUOTE_IF_REQUIRED(ref_, ref);
2696         QUOTE_IF_REQUIRED(mailbox_, mailbox);
2697         imap_gen_send(session, "LIST %s %s", ref_, mailbox_);
2698
2699         return imap_cmd_ok(session, argbuf);
2700 }
2701
2702 #define THROW goto catch
2703
2704 static gint imap_cmd_do_select(IMAPSession *session, const gchar *folder,
2705                                gboolean examine,
2706                                gint *exists, gint *recent, gint *unseen,
2707                                guint32 *uid_validity)
2708 {
2709         gint ok;
2710         gchar *resp_str;
2711         GPtrArray *argbuf;
2712         gchar *select_cmd;
2713         gchar *folder_;
2714
2715         *exists = *recent = *unseen = *uid_validity = 0;
2716         argbuf = g_ptr_array_new();
2717
2718         if (examine)
2719                 select_cmd = "EXAMINE";
2720         else
2721                 select_cmd = "SELECT";
2722
2723         QUOTE_IF_REQUIRED(folder_, folder);
2724         imap_gen_send(session, "%s %s", select_cmd, folder_);
2725
2726         if ((ok = imap_cmd_ok(session, argbuf)) != IMAP_SUCCESS) THROW;
2727
2728         resp_str = search_array_contain_str(argbuf, "EXISTS");
2729         if (resp_str) {
2730                 if (sscanf(resp_str,"%d EXISTS", exists) != 1) {
2731                         g_warning("imap_cmd_select(): invalid EXISTS line.\n");
2732                         THROW;
2733                 }
2734         }
2735
2736         resp_str = search_array_contain_str(argbuf, "RECENT");
2737         if (resp_str) {
2738                 if (sscanf(resp_str, "%d RECENT", recent) != 1) {
2739                         g_warning("imap_cmd_select(): invalid RECENT line.\n");
2740                         THROW;
2741                 }
2742         }
2743
2744         resp_str = search_array_contain_str(argbuf, "UIDVALIDITY");
2745         if (resp_str) {
2746                 if (sscanf(resp_str, "OK [UIDVALIDITY %u] ", uid_validity)
2747                     != 1) {
2748                         g_warning("imap_cmd_select(): invalid UIDVALIDITY line.\n");
2749                         THROW;
2750                 }
2751         }
2752
2753         resp_str = search_array_contain_str(argbuf, "UNSEEN");
2754         if (resp_str) {
2755                 if (sscanf(resp_str, "OK [UNSEEN %d] ", unseen) != 1) {
2756                         g_warning("imap_cmd_select(): invalid UNSEEN line.\n");
2757                         THROW;
2758                 }
2759         }
2760
2761 catch:
2762         ptr_array_free_strings(argbuf);
2763         g_ptr_array_free(argbuf, TRUE);
2764
2765         return ok;
2766 }
2767
2768 static gint imap_cmd_select(IMAPSession *session, const gchar *folder,
2769                             gint *exists, gint *recent, gint *unseen,
2770                             guint32 *uid_validity)
2771 {
2772         return imap_cmd_do_select(session, folder, FALSE,
2773                                   exists, recent, unseen, uid_validity);
2774 }
2775
2776 static gint imap_cmd_examine(IMAPSession *session, const gchar *folder,
2777                              gint *exists, gint *recent, gint *unseen,
2778                              guint32 *uid_validity)
2779 {
2780         return imap_cmd_do_select(session, folder, TRUE,
2781                                   exists, recent, unseen, uid_validity);
2782 }
2783
2784 #undef THROW
2785
2786 static gint imap_cmd_create(IMAPSession *session, const gchar *folder)
2787 {
2788         gchar *folder_;
2789
2790         QUOTE_IF_REQUIRED(folder_, folder);
2791         imap_gen_send(session, "CREATE %s", folder_);
2792
2793         return imap_cmd_ok(session, NULL);
2794 }
2795
2796 static gint imap_cmd_rename(IMAPSession *session, const gchar *old_folder,
2797                             const gchar *new_folder)
2798 {
2799         gchar *old_folder_, *new_folder_;
2800
2801         QUOTE_IF_REQUIRED(old_folder_, old_folder);
2802         QUOTE_IF_REQUIRED(new_folder_, new_folder);
2803         imap_gen_send(session, "RENAME %s %s", old_folder_, new_folder_);
2804
2805         return imap_cmd_ok(session, NULL);
2806 }
2807
2808 static gint imap_cmd_delete(IMAPSession *session, const gchar *folder)
2809 {
2810         gchar *folder_;
2811
2812         QUOTE_IF_REQUIRED(folder_, folder);
2813         imap_gen_send(session, "DELETE %s", folder_);
2814
2815         return imap_cmd_ok(session, NULL);
2816 }
2817
2818 static gint imap_cmd_search(IMAPSession *session, const gchar *criteria, 
2819                             GSList **list)
2820 {
2821         gint ok;
2822         gchar *uidlist;
2823         GPtrArray *argbuf;
2824
2825         g_return_val_if_fail(criteria != NULL, IMAP_ERROR);
2826         g_return_val_if_fail(list != NULL, IMAP_ERROR);
2827
2828         *list = NULL;
2829         
2830         argbuf = g_ptr_array_new();
2831         imap_gen_send(session, "UID SEARCH %s", criteria);
2832
2833         ok = imap_cmd_ok(session, argbuf);
2834         if (ok != IMAP_SUCCESS) {
2835                 ptr_array_free_strings(argbuf);
2836                 g_ptr_array_free(argbuf, TRUE);
2837                 return ok;
2838         }
2839
2840         if ((uidlist = search_array_str(argbuf, "SEARCH ")) != NULL) {
2841                 gchar **strlist, **p;
2842
2843                 strlist = g_strsplit(uidlist + 7, " ", 0);
2844                 for (p = strlist; *p != NULL; ++p) {
2845                         guint msgnum;
2846
2847                         if (sscanf(*p, "%d", &msgnum) == 1)
2848                                 *list = g_slist_append(*list, GINT_TO_POINTER(msgnum));
2849                 }
2850                 g_strfreev(strlist);
2851         }
2852         ptr_array_free_strings(argbuf);
2853         g_ptr_array_free(argbuf, TRUE);
2854
2855         return IMAP_SUCCESS;
2856 }
2857
2858 static gint imap_cmd_fetch(IMAPSession *session, guint32 uid, 
2859                            const gchar *filename)
2860 {
2861         gint ok;
2862         gchar *buf = NULL;
2863         gchar *cur_pos;
2864         gchar size_str[32];
2865         glong size_num;
2866
2867         g_return_val_if_fail(filename != NULL, IMAP_ERROR);
2868
2869         imap_gen_send(session, "UID FETCH %d BODY.PEEK[]", uid);
2870
2871         while ((ok = imap_gen_recv(session, &buf)) == IMAP_SUCCESS) {
2872                 if (buf[0] != '*' || buf[1] != ' ') {
2873                         g_free(buf);
2874                         return IMAP_ERROR;
2875                 }
2876                 if (strstr(buf, "FETCH") != NULL) break;
2877                 g_free(buf);
2878         }
2879         if (ok != IMAP_SUCCESS) {
2880                 g_free(buf);
2881                 return ok;
2882         }
2883
2884 #define RETURN_ERROR_IF_FAIL(cond)      \
2885         if (!(cond)) {                  \
2886                 g_free(buf);            \
2887                 return IMAP_ERROR;      \
2888         }
2889
2890         cur_pos = strchr(buf, '{');
2891         RETURN_ERROR_IF_FAIL(cur_pos != NULL);
2892         cur_pos = strchr_cpy(cur_pos + 1, '}', size_str, sizeof(size_str));
2893         RETURN_ERROR_IF_FAIL(cur_pos != NULL);
2894         size_num = atol(size_str);
2895         RETURN_ERROR_IF_FAIL(size_num >= 0);
2896
2897         RETURN_ERROR_IF_FAIL(*cur_pos == '\0');
2898
2899 #undef RETURN_ERROR_IF_FAIL
2900
2901         g_free(buf);
2902
2903         if (recv_bytes_write_to_file(SESSION(session)->sock,
2904                                      size_num, filename) != 0)
2905                 return IMAP_ERROR;
2906
2907         if (imap_gen_recv(session, &buf) != IMAP_SUCCESS) {
2908                 g_free(buf);
2909                 return IMAP_ERROR;
2910         }
2911
2912         if (buf[0] == '\0' || buf[strlen(buf) - 1] != ')') {
2913                 g_free(buf);
2914                 return IMAP_ERROR;
2915         }
2916         g_free(buf);
2917
2918         ok = imap_cmd_ok(session, NULL);
2919
2920         return ok;
2921 }
2922
2923 static gint imap_cmd_append(IMAPSession *session, const gchar *destfolder,
2924                             const gchar *file, IMAPFlags flags, 
2925                             guint32 *new_uid)
2926 {
2927         gint ok;
2928         gint size;
2929         gchar *destfolder_;
2930         gchar *flag_str;
2931         guint32 new_uid_;
2932         gchar *ret = NULL;
2933         gchar buf[BUFFSIZE];
2934         FILE *fp;
2935         GPtrArray *argbuf;
2936         gchar *resp_str;
2937
2938         g_return_val_if_fail(file != NULL, IMAP_ERROR);
2939
2940         size = get_file_size_as_crlf(file);
2941         if ((fp = fopen(file, "rb")) == NULL) {
2942                 FILE_OP_ERROR(file, "fopen");
2943                 return -1;
2944         }
2945         QUOTE_IF_REQUIRED(destfolder_, destfolder);
2946         flag_str = imap_get_flag_str(flags);
2947         imap_gen_send(session, "APPEND %s (%s) {%d}", 
2948                       destfolder_, flag_str, size);
2949         g_free(flag_str);
2950
2951         ok = imap_gen_recv(session, &ret);
2952         if (ok != IMAP_SUCCESS || ret[0] != '+' || ret[1] != ' ') {
2953                 log_warning(_("can't append %s to %s\n"), file, destfolder_);
2954                 g_free(ret);
2955                 fclose(fp);
2956                 return IMAP_ERROR;
2957         }
2958         g_free(ret);
2959
2960         log_print("IMAP4> %s\n", _("(sending file...)"));
2961
2962         while (fgets(buf, sizeof(buf), fp) != NULL) {
2963                 strretchomp(buf);
2964                 if (sock_puts(SESSION(session)->sock, buf) < 0) {
2965                         fclose(fp);
2966                         return -1;
2967                 }
2968         }
2969
2970         if (ferror(fp)) {
2971                 FILE_OP_ERROR(file, "fgets");
2972                 fclose(fp);
2973                 return -1;
2974         }
2975
2976         sock_puts(SESSION(session)->sock, "");
2977
2978         fclose(fp);
2979
2980         if (new_uid != NULL)
2981                 *new_uid = 0;
2982
2983         if (new_uid != NULL && session->uidplus) {
2984                 argbuf = g_ptr_array_new();
2985
2986                 ok = imap_cmd_ok(session, argbuf);
2987                 if ((ok == IMAP_SUCCESS) && (argbuf->len > 0)) {
2988                         resp_str = g_ptr_array_index(argbuf, argbuf->len - 1);
2989                         if (resp_str &&
2990                             sscanf(resp_str, "%*u OK [APPENDUID %*u %u]",
2991                                    &new_uid_) == 1) {
2992                                 *new_uid = new_uid_;
2993                         }
2994                 }
2995
2996                 ptr_array_free_strings(argbuf);
2997                 g_ptr_array_free(argbuf, TRUE);
2998         } else
2999                 ok = imap_cmd_ok(session, NULL);
3000
3001         if (ok != IMAP_SUCCESS)
3002                 log_warning(_("can't append message to %s\n"),
3003                             destfolder_);
3004
3005         return ok;
3006 }
3007
3008 static MsgNumberList *imapset_to_numlist(IMAPSet imapset)
3009 {
3010         gchar **ranges, **range;
3011         guint32 low, high;
3012         MsgNumberList *uids = NULL;
3013         
3014         ranges = g_strsplit(imapset, ",", 0);
3015         for (range = ranges; *range != NULL; range++) {
3016                 printf("%s\n", *range);
3017                 if(sscanf(*range, "%u:%u", &low, &high) == 1)
3018                         uids = g_slist_prepend(uids, GINT_TO_POINTER(low));
3019                 else {
3020                         int i;
3021                         for (i = low; i <= high; i++)
3022                                 uids = g_slist_prepend(uids, GINT_TO_POINTER(i));
3023                 }
3024         }
3025         uids = g_slist_reverse(uids);
3026         g_strfreev(ranges);
3027
3028         return uids;
3029 }
3030
3031 static gint imap_cmd_copy(IMAPSession *session, const gchar *seq_set,
3032                           const gchar *destfolder, GRelation *uid_mapping)
3033 {
3034         gint ok;
3035         gchar *destfolder_;
3036         
3037         g_return_val_if_fail(session != NULL, IMAP_ERROR);
3038         g_return_val_if_fail(seq_set != NULL, IMAP_ERROR);
3039         g_return_val_if_fail(destfolder != NULL, IMAP_ERROR);
3040
3041         QUOTE_IF_REQUIRED(destfolder_, destfolder);
3042         imap_gen_send(session, "UID COPY %s %s", seq_set, destfolder_);
3043
3044         if (uid_mapping != NULL && session->uidplus) {
3045                 GPtrArray *reply;               
3046                 gchar *resp_str = NULL, *olduids_str, *newuids_str;
3047                 MsgNumberList *olduids, *old_cur, *newuids, *new_cur;
3048
3049                 reply = g_ptr_array_new();
3050                 ok = imap_cmd_ok(session, reply);
3051                 if ((ok == IMAP_SUCCESS) && (reply->len > 0)) {
3052                         resp_str = g_ptr_array_index(reply, reply->len - 1);
3053                         if (resp_str) {
3054                                 olduids_str = g_new0(gchar, strlen(resp_str));
3055                                 newuids_str = g_new0(gchar, strlen(resp_str));
3056                                 if (sscanf(resp_str, "%*s OK [COPYUID %*u %[0-9,:] %[0-9,:]]",
3057                                            olduids_str, newuids_str) == 2) {
3058                                         olduids = imapset_to_numlist(olduids_str);
3059                                         newuids = imapset_to_numlist(newuids_str);
3060
3061                                         old_cur = olduids;
3062                                         new_cur = newuids;
3063                                         while(old_cur != NULL && new_cur != NULL) {
3064                                                 g_relation_insert(uid_mapping, 
3065                                                                   GPOINTER_TO_INT(old_cur->data),
3066                                                                   GPOINTER_TO_INT(new_cur->data));
3067                                                 old_cur = g_slist_next(old_cur);
3068                                                 new_cur = g_slist_next(new_cur);
3069                                         }
3070
3071                                         g_slist_free(olduids);
3072                                         g_slist_free(newuids);
3073                                 }
3074                                 g_free(olduids_str);
3075                                 g_free(newuids_str);
3076                         }
3077                 }
3078                 ptr_array_free_strings(reply);
3079                 g_ptr_array_free(reply, TRUE);
3080         } else
3081                 ok = imap_cmd_ok(session, NULL);
3082
3083         if (ok != IMAP_SUCCESS)
3084                 log_warning(_("can't copy %s to %s\n"), seq_set, destfolder_);
3085
3086         return ok;
3087 }
3088
3089 gint imap_cmd_envelope(IMAPSession *session, IMAPSet set)
3090 {
3091         static GString *header_fields = NULL;
3092
3093         if (header_fields == NULL) {
3094                 const HeaderEntry *headers, *elem;
3095
3096                 headers = procheader_get_headernames(FALSE);
3097                 header_fields = g_string_new("");
3098
3099                 for (elem = headers; elem->name != NULL; ++elem) {
3100                         gint namelen = strlen(elem->name);
3101
3102                         /* Header fields ending with space are not rfc822 headers */
3103                         if (elem->name[namelen - 1] == ' ')
3104                                 continue;
3105
3106                         /* strip : at the of header field */
3107                         if(elem->name[namelen - 1] == ':')
3108                                 namelen--;
3109                         
3110                         if (namelen <= 0)
3111                                 continue;
3112
3113                         g_string_sprintfa(header_fields, "%s%.*s",
3114                                         header_fields->str[0] != '\0' ? " " : "",
3115                                         namelen, elem->name);
3116                 }
3117         }
3118
3119         imap_gen_send
3120                 (session, "UID FETCH %s (UID FLAGS RFC822.SIZE BODY.PEEK[HEADER.FIELDS (%s)])",
3121                  set, header_fields->str);
3122
3123         return IMAP_SUCCESS;
3124 }
3125
3126 static gint imap_cmd_store(IMAPSession *session, IMAPSet seq_set,
3127                            gchar *sub_cmd)
3128 {
3129         gint ok;
3130
3131         imap_gen_send(session, "UID STORE %s %s", seq_set, sub_cmd);
3132
3133         if ((ok = imap_cmd_ok(session, NULL)) != IMAP_SUCCESS) {
3134                 log_warning(_("error while imap command: STORE %s %s\n"),
3135                             seq_set, sub_cmd);
3136                 return ok;
3137         }
3138
3139         return IMAP_SUCCESS;
3140 }
3141
3142 static gint imap_cmd_expunge(IMAPSession *session, IMAPSet seq_set)
3143 {
3144         gint ok;
3145
3146         if (seq_set && session->uidplus)
3147                 imap_gen_send(session, "UID EXPUNGE %s", seq_set);
3148         else    
3149                 imap_gen_send(session, "EXPUNGE");
3150         if ((ok = imap_cmd_ok(session, NULL)) != IMAP_SUCCESS) {
3151                 log_warning(_("error while imap command: EXPUNGE\n"));
3152                 return ok;
3153         }
3154
3155         return IMAP_SUCCESS;
3156 }
3157
3158 static gint imap_cmd_close(IMAPSession *session)
3159 {
3160         gint ok;
3161
3162         imap_gen_send(session, "CLOSE");
3163         if ((ok = imap_cmd_ok(session, NULL)) != IMAP_SUCCESS)
3164                 log_warning(_("error while imap command: CLOSE\n"));
3165
3166         return ok;
3167 }
3168
3169 static gint imap_cmd_ok(IMAPSession *session, GPtrArray *argbuf)
3170 {
3171         gint ok = IMAP_SUCCESS;
3172         gchar *buf;
3173         gint cmd_num;
3174         gchar *data;
3175
3176         while ((ok = imap_gen_recv(session, &buf))
3177                == IMAP_SUCCESS) {
3178                 // make sure data is long enough for any substring of buf
3179                 data = alloca(strlen(buf) + 1);
3180
3181                 // untagged line read
3182                 if (buf[0] == '*' && buf[1] == ' ') {
3183                         gint num;
3184                         if (argbuf)
3185                                 g_ptr_array_add(argbuf, g_strdup(buf + 2));
3186
3187                         if (sscanf(buf + 2, "%d %s", &num, data) >= 2) {
3188                                 if (!strcmp(data, "EXISTS")) {
3189                                         session->exists = num;
3190                                         session->folder_content_changed = TRUE;
3191                                 }
3192
3193                                 if(!strcmp(data, "EXPUNGE")) {
3194                                         session->exists--;
3195                                         session->folder_content_changed = TRUE;
3196                                 }
3197                         }
3198                 // tagged line with correct tag and OK response found
3199                 } else if ((sscanf(buf, "%d %s", &cmd_num, data) >= 2) &&
3200                            (cmd_num == session->cmd_count) &&
3201                            !strcmp(data, "OK")) {
3202                         if (argbuf)
3203                                 g_ptr_array_add(argbuf, g_strdup(buf));
3204                         break;
3205                 // everything else
3206                 } else {
3207                         ok = IMAP_ERROR;
3208                         break;
3209                 }
3210                 g_free(buf);
3211         }
3212         g_free(buf);
3213
3214         return ok;
3215 }
3216
3217 static void imap_gen_send(IMAPSession *session, const gchar *format, ...)
3218 {
3219         gchar *buf;
3220         gchar *tmp;
3221         gchar *p;
3222         va_list args;
3223
3224         va_start(args, format);
3225         tmp = g_strdup_vprintf(format, args);
3226         va_end(args);
3227
3228         session->cmd_count++;
3229
3230         buf = g_strdup_printf("%d %s\r\n", session->cmd_count, tmp);
3231         if (!strncasecmp(tmp, "LOGIN ", 6) && (p = strchr(tmp + 6, ' '))) {
3232                 *p = '\0';
3233                 log_print("IMAP4> %d %s ********\n", session->cmd_count, tmp);
3234         } else
3235                 log_print("IMAP4> %d %s\n", session->cmd_count, tmp);
3236
3237         sock_write_all(SESSION(session)->sock, buf, strlen(buf));
3238         g_free(tmp);
3239         g_free(buf);
3240 }
3241
3242 static gint imap_gen_recv(IMAPSession *session, gchar **ret)
3243 {
3244         if ((*ret = sock_getline(SESSION(session)->sock)) == NULL)
3245                 return IMAP_SOCKET;
3246
3247         strretchomp(*ret);
3248
3249         log_print("IMAP4< %s\n", *ret);
3250
3251         return IMAP_SUCCESS;
3252 }
3253
3254
3255 /* misc utility functions */
3256
3257 static gchar *strchr_cpy(const gchar *src, gchar ch, gchar *dest, gint len)
3258 {
3259         gchar *tmp;
3260
3261         dest[0] = '\0';
3262         tmp = strchr(src, ch);
3263         if (!tmp)
3264                 return NULL;
3265
3266         memcpy(dest, src, MIN(tmp - src, len - 1));
3267         dest[MIN(tmp - src, len - 1)] = '\0';
3268
3269         return tmp + 1;
3270 }
3271
3272 static gchar *get_quoted(const gchar *src, gchar ch, gchar *dest, gint len)
3273 {
3274         const gchar *p = src;
3275         gint n = 0;
3276
3277         g_return_val_if_fail(*p == ch, NULL);
3278
3279         *dest = '\0';
3280         p++;
3281
3282         while (*p != '\0' && *p != ch) {
3283                 if (n < len - 1) {
3284                         if (*p == '\\' && *(p + 1) != '\0')
3285                                 p++;
3286                         *dest++ = *p++;
3287                 } else
3288                         p++;
3289                 n++;
3290         }
3291
3292         *dest = '\0';
3293         return (gchar *)(*p == ch ? p + 1 : p);
3294 }
3295
3296 static gchar *search_array_contain_str(GPtrArray *array, const gchar *str)
3297 {
3298         gint i;
3299
3300         for (i = 0; i < array->len; i++) {
3301                 gchar *tmp;
3302
3303                 tmp = g_ptr_array_index(array, i);
3304                 if (strstr(tmp, str) != NULL)
3305                         return tmp;
3306         }
3307
3308         return NULL;
3309 }
3310
3311 static gchar *search_array_str(GPtrArray *array, const gchar *str)
3312 {
3313         gint i;
3314         gint len;
3315
3316         len = strlen(str);
3317
3318         for (i = 0; i < array->len; i++) {
3319                 gchar *tmp;
3320
3321                 tmp = g_ptr_array_index(array, i);
3322                 if (!strncmp(tmp, str, len))
3323                         return tmp;
3324         }
3325
3326         return NULL;
3327 }
3328
3329 static void imap_path_separator_subst(gchar *str, gchar separator)
3330 {
3331         gchar *p;
3332         gboolean in_escape = FALSE;
3333
3334         if (!separator || separator == '/') return;
3335
3336         for (p = str; *p != '\0'; p++) {
3337                 if (*p == '/' && !in_escape)
3338                         *p = separator;
3339                 else if (*p == '&' && *(p + 1) != '-' && !in_escape)
3340                         in_escape = TRUE;
3341                 else if (*p == '-' && in_escape)
3342                         in_escape = FALSE;
3343         }
3344 }
3345
3346 static gchar *imap_modified_utf7_to_locale(const gchar *mutf7_str)
3347 {
3348 #if !HAVE_ICONV
3349         const gchar *from_p;
3350         gchar *to, *to_p;
3351
3352         to = g_malloc(strlen(mutf7_str) + 1);
3353         to_p = to;
3354
3355         for (from_p = mutf7_str; *from_p != '\0'; from_p++) {
3356                 if (*from_p == '&' && *(from_p + 1) == '-') {
3357                         *to_p++ = '&';
3358                         from_p++;
3359                 } else
3360                         *to_p++ = *from_p;
3361         }
3362         *to_p = '\0';
3363
3364         return to;
3365 #else
3366         static iconv_t cd = (iconv_t)-1;
3367         static gboolean iconv_ok = TRUE;
3368         GString *norm_utf7;
3369         gchar *norm_utf7_p;
3370         size_t norm_utf7_len;
3371         const gchar *p;
3372         gchar *to_str, *to_p;
3373         size_t to_len;
3374         gboolean in_escape = FALSE;
3375
3376         if (!iconv_ok) return g_strdup(mutf7_str);
3377
3378         if (cd == (iconv_t)-1) {
3379                 cd = iconv_open(conv_get_current_charset_str(), "UTF-7");
3380                 if (cd == (iconv_t)-1) {
3381                         g_warning("iconv cannot convert UTF-7 to %s\n",
3382                                   conv_get_current_charset_str());
3383                         iconv_ok = FALSE;
3384                         return g_strdup(mutf7_str);
3385                 }
3386         }
3387
3388         norm_utf7 = g_string_new(NULL);
3389
3390         for (p = mutf7_str; *p != '\0'; p++) {
3391                 /* replace: '&'  -> '+',
3392                             "&-" -> '&',
3393                             escaped ','  -> '/' */
3394                 if (!in_escape && *p == '&') {
3395                         if (*(p + 1) != '-') {
3396                                 g_string_append_c(norm_utf7, '+');
3397                                 in_escape = TRUE;
3398                         } else {
3399                                 g_string_append_c(norm_utf7, '&');
3400                                 p++;
3401                         }
3402                 } else if (in_escape && *p == ',') {
3403                         g_string_append_c(norm_utf7, '/');
3404                 } else if (in_escape && *p == '-') {
3405                         g_string_append_c(norm_utf7, '-');
3406                         in_escape = FALSE;
3407                 } else {
3408                         g_string_append_c(norm_utf7, *p);
3409                 }
3410         }
3411
3412         norm_utf7_p = norm_utf7->str;
3413         norm_utf7_len = norm_utf7->len;
3414         to_len = strlen(mutf7_str) * 5;
3415         to_p = to_str = g_malloc(to_len + 1);
3416
3417         if (iconv(cd, (ICONV_CONST gchar **)&norm_utf7_p, &norm_utf7_len,
3418                   &to_p, &to_len) == -1) {
3419                 g_warning(_("iconv cannot convert UTF-7 to %s\n"),
3420                           conv_get_current_charset_str());
3421                 g_string_free(norm_utf7, TRUE);
3422                 g_free(to_str);
3423                 return g_strdup(mutf7_str);
3424         }
3425
3426         /* second iconv() call for flushing */
3427         iconv(cd, NULL, NULL, &to_p, &to_len);
3428         g_string_free(norm_utf7, TRUE);
3429         *to_p = '\0';
3430
3431         return to_str;
3432 #endif /* !HAVE_ICONV */
3433 }
3434
3435 static gchar *imap_locale_to_modified_utf7(const gchar *from)
3436 {
3437 #if !HAVE_ICONV
3438         const gchar *from_p;
3439         gchar *to, *to_p;
3440
3441         to = g_malloc(strlen(from) * 2 + 1);
3442         to_p = to;
3443
3444         for (from_p = from; *from_p != '\0'; from_p++) {
3445                 if (*from_p == '&') {
3446                         *to_p++ = '&';
3447                         *to_p++ = '-';
3448                 } else
3449                         *to_p++ = *from_p;
3450         }
3451         *to_p = '\0';
3452
3453         return to;
3454 #else
3455         static iconv_t cd = (iconv_t)-1;
3456         static gboolean iconv_ok = TRUE;
3457         gchar *norm_utf7, *norm_utf7_p;
3458         size_t from_len, norm_utf7_len;
3459         GString *to_str;
3460         gchar *from_tmp, *to, *p;
3461         gboolean in_escape = FALSE;
3462
3463         if (!iconv_ok) return g_strdup(from);
3464
3465         if (cd == (iconv_t)-1) {
3466                 cd = iconv_open("UTF-7", conv_get_current_charset_str());
3467                 if (cd == (iconv_t)-1) {
3468                         g_warning("iconv cannot convert %s to UTF-7\n",
3469                                   conv_get_current_charset_str());
3470                         iconv_ok = FALSE;
3471                         return g_strdup(from);
3472                 }
3473         }
3474
3475         Xstrdup_a(from_tmp, from, return g_strdup(from));
3476         from_len = strlen(from);
3477         norm_utf7_len = from_len * 5;
3478         Xalloca(norm_utf7, norm_utf7_len + 1, return g_strdup(from));
3479         norm_utf7_p = norm_utf7;
3480
3481 #define IS_PRINT(ch) (isprint(ch) && isascii(ch))
3482
3483         while (from_len > 0) {
3484                 if (*from_tmp == '+') {
3485                         *norm_utf7_p++ = '+';
3486                         *norm_utf7_p++ = '-';
3487                         norm_utf7_len -= 2;
3488                         from_tmp++;
3489                         from_len--;
3490                 } else if (IS_PRINT(*from_tmp)) {
3491                         /* printable ascii char */
3492                         *norm_utf7_p = *from_tmp;
3493                         norm_utf7_p++;
3494                         norm_utf7_len--;
3495                         from_tmp++;
3496                         from_len--;
3497                 } else {
3498                         size_t mb_len = 0, conv_len = 0;
3499
3500                         /* unprintable char: convert to UTF-7 */
3501                         p = from_tmp;
3502                         while (!IS_PRINT(*p) && conv_len < from_len) {
3503                                 mb_len = mblen(p, MB_LEN_MAX);
3504                                 if (mb_len <= 0) {
3505                                         g_warning("wrong multibyte sequence\n");
3506                                         return g_strdup(from);
3507                                 }
3508                                 conv_len += mb_len;
3509                                 p += mb_len;
3510                         }
3511
3512                         from_len -= conv_len;
3513                         if (iconv(cd, (ICONV_CONST gchar **)&from_tmp,
3514                                   &conv_len,
3515                                   &norm_utf7_p, &norm_utf7_len) == -1) {
3516                                 g_warning("iconv cannot convert %s to UTF-7\n",
3517                                           conv_get_current_charset_str());
3518                                 return g_strdup(from);
3519                         }
3520
3521                         /* second iconv() call for flushing */
3522                         iconv(cd, NULL, NULL, &norm_utf7_p, &norm_utf7_len);
3523                 }
3524         }
3525
3526 #undef IS_PRINT
3527
3528         *norm_utf7_p = '\0';
3529         to_str = g_string_new(NULL);
3530         for (p = norm_utf7; p < norm_utf7_p; p++) {
3531                 /* replace: '&' -> "&-",
3532                             '+' -> '&',
3533                             "+-" -> '+',
3534                             BASE64 '/' -> ',' */
3535                 if (!in_escape && *p == '&') {
3536                         g_string_append(to_str, "&-");
3537                 } else if (!in_escape && *p == '+') {
3538                         if (*(p + 1) == '-') {
3539                                 g_string_append_c(to_str, '+');
3540                                 p++;
3541                         } else {
3542                                 g_string_append_c(to_str, '&');
3543                                 in_escape = TRUE;
3544                         }
3545                 } else if (in_escape && *p == '/') {
3546                         g_string_append_c(to_str, ',');
3547                 } else if (in_escape && *p == '-') {
3548                         g_string_append_c(to_str, '-');
3549                         in_escape = FALSE;
3550                 } else {
3551                         g_string_append_c(to_str, *p);
3552                 }
3553         }
3554
3555         if (in_escape) {
3556                 in_escape = FALSE;
3557                 g_string_append_c(to_str, '-');
3558         }
3559
3560         to = to_str->str;
3561         g_string_free(to_str, FALSE);
3562
3563         return to;
3564 #endif /* !HAVE_ICONV */
3565 }
3566
3567 static GSList *imap_get_seq_set_from_numlist(MsgNumberList *numlist)
3568 {
3569         GString *str;
3570         GSList *sorted_list, *cur;
3571         guint first, last, next;
3572         gchar *ret_str;
3573         GSList *ret_list = NULL;
3574
3575         if (numlist == NULL)
3576                 return NULL;
3577
3578         str = g_string_sized_new(256);
3579
3580         sorted_list = g_slist_copy(numlist);
3581         sorted_list = g_slist_sort(sorted_list, g_int_compare);
3582
3583         first = GPOINTER_TO_INT(sorted_list->data);
3584
3585         for (cur = sorted_list; cur != NULL; cur = g_slist_next(cur)) {
3586                 last = GPOINTER_TO_INT(cur->data);
3587                 if (cur->next)
3588                         next = GPOINTER_TO_INT(cur->next->data);
3589                 else
3590                         next = 0;
3591
3592                 if (last + 1 != next || next == 0) {
3593                         if (str->len > 0)
3594                                 g_string_append_c(str, ',');
3595                         if (first == last)
3596                                 g_string_sprintfa(str, "%u", first);
3597                         else
3598                                 g_string_sprintfa(str, "%u:%u", first, last);
3599
3600                         first = next;
3601
3602                         if (str->len > IMAP_CMD_LIMIT) {
3603                                 ret_str = g_strdup(str->str);
3604                                 ret_list = g_slist_append(ret_list, ret_str);
3605                                 g_string_truncate(str, 0);
3606                         }
3607                 }
3608         }
3609
3610         if (str->len > 0) {
3611                 ret_str = g_strdup(str->str);
3612                 ret_list = g_slist_append(ret_list, ret_str);
3613         }
3614
3615         g_slist_free(sorted_list);
3616         g_string_free(str, TRUE);
3617
3618         return ret_list;
3619 }
3620
3621 static GSList *imap_get_seq_set_from_msglist(MsgInfoList *msglist)
3622 {
3623         MsgNumberList *numlist = NULL;
3624         MsgInfoList *cur;
3625         GSList *seq_list;
3626
3627         for (cur = msglist; cur != NULL; cur = g_slist_next(cur)) {
3628                 MsgInfo *msginfo = (MsgInfo *) cur->data;
3629
3630                 numlist = g_slist_append(numlist, GINT_TO_POINTER(msginfo->msgnum));
3631         }
3632         seq_list = imap_get_seq_set_from_numlist(numlist);
3633         g_slist_free(numlist);
3634
3635         return seq_list;
3636 }
3637
3638 static void imap_seq_set_free(GSList *seq_list)
3639 {
3640         slist_free_strings(seq_list);
3641         g_slist_free(seq_list);
3642 }
3643
3644
3645 static gboolean imap_rename_folder_func(GNode *node, gpointer data)
3646 {
3647         FolderItem *item = node->data;
3648         gchar **paths = data;
3649         const gchar *oldpath = paths[0];
3650         const gchar *newpath = paths[1];
3651         gchar *base;
3652         gchar *new_itempath;
3653         gint oldpathlen;
3654
3655         oldpathlen = strlen(oldpath);
3656         if (strncmp(oldpath, item->path, oldpathlen) != 0) {
3657                 g_warning("path doesn't match: %s, %s\n", oldpath, item->path);
3658                 return TRUE;
3659         }
3660
3661         base = item->path + oldpathlen;
3662         while (*base == G_DIR_SEPARATOR) base++;
3663         if (*base == '\0')
3664                 new_itempath = g_strdup(newpath);
3665         else
3666                 new_itempath = g_strconcat(newpath, G_DIR_SEPARATOR_S, base,
3667                                            NULL);
3668         g_free(item->path);
3669         item->path = new_itempath;
3670
3671         return FALSE;
3672 }
3673
3674 static gint get_list_of_uids(Folder *folder, IMAPFolderItem *item, GSList **msgnum_list)
3675 {
3676         gint ok, nummsgs = 0, lastuid_old;
3677         IMAPSession *session;
3678         GSList *uidlist, *elem;
3679         gchar *cmd_buf;
3680
3681         session = imap_session_get(folder);
3682         g_return_val_if_fail(session != NULL, -1);
3683
3684         ok = imap_select(session, IMAP_FOLDER(folder), item->item.path,
3685                          NULL, NULL, NULL, NULL);
3686         if (ok != IMAP_SUCCESS)
3687                 return -1;
3688
3689         cmd_buf = g_strdup_printf("UID %d:*", item->lastuid + 1);
3690         ok = imap_cmd_search(session, cmd_buf, &uidlist);
3691         g_free(cmd_buf);
3692
3693         if (ok == IMAP_SOCKET) {
3694                 session_destroy((Session *)session);
3695                 ((RemoteFolder *)folder)->session = NULL;
3696                 return -1;
3697         }
3698
3699         if (ok != IMAP_SUCCESS) {
3700                 gint i;
3701                 GPtrArray *argbuf;
3702
3703                 argbuf = g_ptr_array_new();
3704
3705                 cmd_buf = g_strdup_printf("UID FETCH %d:* (UID)", item->lastuid + 1);
3706                 imap_gen_send(session, cmd_buf);
3707                 g_free(cmd_buf);
3708                 ok = imap_cmd_ok(session, argbuf);
3709                 if (ok != IMAP_SUCCESS) {
3710                         ptr_array_free_strings(argbuf);
3711                         g_ptr_array_free(argbuf, TRUE);
3712                         return -1;
3713                 }
3714         
3715                 for(i = 0; i < argbuf->len; i++) {
3716                         int ret, msgnum;
3717         
3718                         if((ret = sscanf(g_ptr_array_index(argbuf, i), 
3719                                     "%*d FETCH (UID %d)", &msgnum)) == 1)
3720                                 uidlist = g_slist_prepend(uidlist, GINT_TO_POINTER(msgnum));
3721                 }
3722                 ptr_array_free_strings(argbuf);
3723                 g_ptr_array_free(argbuf, TRUE);
3724         }
3725
3726         lastuid_old = item->lastuid;
3727         *msgnum_list = g_slist_copy(item->uid_list);
3728         nummsgs = g_slist_length(*msgnum_list);
3729         debug_print("Got %d uids from cache\n", g_slist_length(item->uid_list));
3730
3731         for (elem = uidlist; elem != NULL; elem = g_slist_next(elem)) {
3732                 guint msgnum;
3733
3734                 msgnum = GPOINTER_TO_INT(elem->data);
3735                 if (msgnum > lastuid_old) {
3736                         *msgnum_list = g_slist_prepend(*msgnum_list, GINT_TO_POINTER(msgnum));
3737                         item->uid_list = g_slist_prepend(item->uid_list, GINT_TO_POINTER(msgnum));
3738                         nummsgs++;
3739
3740                         if(msgnum > item->lastuid)
3741                                 item->lastuid = msgnum;
3742                 }
3743         }
3744         g_slist_free(uidlist);
3745
3746         return nummsgs;
3747 }
3748
3749 gint imap_get_num_list(Folder *folder, FolderItem *_item, GSList **msgnum_list, gboolean *old_uids_valid)
3750 {
3751         IMAPFolderItem *item = (IMAPFolderItem *)_item;
3752         IMAPSession *session;
3753         gint ok, nummsgs = 0, exists, recent, uid_val, uid_next, unseen;
3754         GSList *uidlist;
3755         gchar *dir;
3756         gboolean selected_folder;
3757
3758         g_return_val_if_fail(folder != NULL, -1);
3759         g_return_val_if_fail(item != NULL, -1);
3760         g_return_val_if_fail(item->item.path != NULL, -1);
3761         g_return_val_if_fail(FOLDER_CLASS(folder) == &imap_class, -1);
3762         g_return_val_if_fail(folder->account != NULL, -1);
3763
3764         session = imap_session_get(folder);
3765         g_return_val_if_fail(session != NULL, -1);
3766
3767         selected_folder = (session->mbox != NULL) &&
3768                           (!strcmp(session->mbox, item->item.path));
3769         if (selected_folder) {
3770                 ok = imap_cmd_noop(session);
3771                 if (ok != IMAP_SUCCESS)
3772                         return -1;
3773                 exists = session->exists;
3774
3775                 *old_uids_valid = TRUE;
3776         } else {
3777                 ok = imap_status(session, IMAP_FOLDER(folder), item->item.path,
3778                                  &exists, &recent, &uid_next, &uid_val, &unseen);
3779                 if (ok != IMAP_SUCCESS)
3780                         return -1;
3781
3782                 if(item->item.mtime == uid_val)
3783                         *old_uids_valid = TRUE;
3784                 else {
3785                         *old_uids_valid = FALSE;
3786
3787                         debug_print("Freeing imap uid cache\n");
3788                         item->lastuid = 0;
3789                         g_slist_free(item->uid_list);
3790                         item->uid_list = NULL;
3791                 
3792                         item->item.mtime = uid_val;
3793
3794                         imap_delete_all_cached_messages((FolderItem *)item);
3795                 }
3796         }
3797
3798         if (!selected_folder)
3799                 item->uid_next = uid_next;
3800
3801         /* If old uid_next matches new uid_next we can be sure no message
3802            was added to the folder */
3803         if (( selected_folder && !session->folder_content_changed) ||
3804             (!selected_folder && uid_next == item->uid_next)) {
3805                 nummsgs = g_slist_length(item->uid_list);
3806
3807                 /* If number of messages is still the same we
3808                    know our caches message numbers are still valid,
3809                    otherwise if the number of messages has decrease
3810                    we discard our cache to start a new scan to find
3811                    out which numbers have been removed */
3812                 if (exists == nummsgs) {
3813                         *msgnum_list = g_slist_copy(item->uid_list);
3814                         return nummsgs;
3815                 } else if (exists < nummsgs) {
3816                         debug_print("Freeing imap uid cache");
3817                         item->lastuid = 0;
3818                         g_slist_free(item->uid_list);
3819                         item->uid_list = NULL;
3820                 }
3821         }
3822
3823         if (exists == 0) {
3824                 *msgnum_list = NULL;
3825                 return 0;
3826         }
3827
3828         nummsgs = get_list_of_uids(folder, item, &uidlist);
3829
3830         if (nummsgs != exists) {
3831                 /* Cache contains more messages then folder, we have cached
3832                    an old UID of a message that was removed and new messages
3833                    have been added too, otherwise the uid_next check would
3834                    not have failed */
3835                 debug_print("Freeing imap uid cache");
3836                 item->lastuid = 0;
3837                 g_slist_free(item->uid_list);
3838                 item->uid_list = NULL;
3839
3840                 g_slist_free(*msgnum_list);
3841
3842                 nummsgs = get_list_of_uids(folder, item, &uidlist);
3843         }
3844
3845         *msgnum_list = uidlist;
3846
3847         dir = folder_item_get_path((FolderItem *)item);
3848         debug_print("removing old messages from %s\n", dir);
3849         remove_numbered_files_not_in_list(dir, *msgnum_list);
3850         g_free(dir);
3851
3852         return nummsgs;
3853 }
3854
3855 static MsgInfo *imap_parse_msg(const gchar *file, FolderItem *item)
3856 {
3857         MsgInfo *msginfo;
3858         MsgFlags flags;
3859
3860         flags.perm_flags = MSG_NEW|MSG_UNREAD;
3861         flags.tmp_flags = 0;
3862
3863         g_return_val_if_fail(item != NULL, NULL);
3864         g_return_val_if_fail(file != NULL, NULL);
3865
3866         if (item->stype == F_QUEUE) {
3867                 MSG_SET_TMP_FLAGS(flags, MSG_QUEUED);
3868         } else if (item->stype == F_DRAFT) {
3869                 MSG_SET_TMP_FLAGS(flags, MSG_DRAFT);
3870         }
3871
3872         msginfo = procheader_parse_file(file, flags, FALSE, FALSE);
3873         if (!msginfo) return NULL;
3874
3875         msginfo->folder = item;
3876
3877         return msginfo;
3878 }
3879
3880 GSList *imap_get_msginfos(Folder *folder, FolderItem *item, GSList *msgnum_list)
3881 {
3882         IMAPSession *session;
3883         MsgInfoList *ret = NULL;
3884         gint ok;
3885
3886         g_return_val_if_fail(folder != NULL, NULL);
3887         g_return_val_if_fail(item != NULL, NULL);
3888         g_return_val_if_fail(msgnum_list != NULL, NULL);
3889
3890         session = imap_session_get(folder);
3891         g_return_val_if_fail(session != NULL, NULL);
3892
3893         ok = imap_select(session, IMAP_FOLDER(folder), item->path,
3894                          NULL, NULL, NULL, NULL);
3895         if (ok != IMAP_SUCCESS)
3896                 return NULL;
3897
3898         if (!(item->stype == F_QUEUE || item->stype == F_DRAFT)) {
3899                 ret = g_slist_concat(ret,
3900                         imap_get_uncached_messages(
3901                         session, item, msgnum_list));
3902         } else {
3903                 MsgNumberList *sorted_list, *elem;
3904                 gint startnum, lastnum;
3905
3906                 sorted_list = g_slist_sort(g_slist_copy(msgnum_list), g_int_compare);
3907
3908                 startnum = lastnum = GPOINTER_TO_INT(sorted_list->data);
3909
3910                 for (elem = sorted_list;; elem = g_slist_next(elem)) {
3911                         guint num;
3912
3913                         if (elem)
3914                                 num = GPOINTER_TO_INT(elem->data);
3915
3916                         if (num > lastnum + 1 || elem == NULL) {
3917                                 int i;
3918                                 for (i = startnum; i <= lastnum; ++i) {
3919                                         gchar *file;
3920                         
3921                                         file = imap_fetch_msg(folder, item, i);
3922                                         if (file != NULL) {
3923                                                 MsgInfo *msginfo = imap_parse_msg(file, item);
3924                                                 if (msginfo != NULL) {
3925                                                         msginfo->msgnum = i;
3926                                                         ret = g_slist_append(ret, msginfo);
3927                                                 }
3928                                                 g_free(file);
3929                                         }
3930                                 }
3931
3932                                 if (elem == NULL)
3933                                         break;
3934
3935                                 startnum = num;
3936                         }
3937                         lastnum = num;
3938                 }
3939
3940                 g_slist_free(sorted_list);
3941         }
3942
3943         return ret;
3944 }
3945
3946 MsgInfo *imap_get_msginfo(Folder *folder, FolderItem *item, gint uid)
3947 {
3948         MsgInfo *msginfo = NULL;
3949         MsgInfoList *msginfolist;
3950         MsgNumberList numlist;
3951
3952         numlist.next = NULL;
3953         numlist.data = GINT_TO_POINTER(uid);
3954
3955         msginfolist = imap_get_msginfos(folder, item, &numlist);
3956         if (msginfolist != NULL) {
3957                 msginfo = msginfolist->data;
3958                 g_slist_free(msginfolist);
3959         }
3960
3961         return msginfo;
3962 }
3963
3964 gboolean imap_scan_required(Folder *folder, FolderItem *_item)
3965 {
3966         IMAPSession *session;
3967         IMAPFolderItem *item = (IMAPFolderItem *)_item;
3968         gint ok, exists = 0, recent = 0, unseen = 0;
3969         guint32 uid_next, uid_val = 0;
3970         gboolean selected_folder;
3971         
3972         g_return_val_if_fail(folder != NULL, FALSE);
3973         g_return_val_if_fail(item != NULL, FALSE);
3974         g_return_val_if_fail(item->item.folder != NULL, FALSE);
3975         g_return_val_if_fail(FOLDER_CLASS(item->item.folder) == &imap_class, FALSE);
3976
3977         if (item->item.path == NULL)
3978                 return FALSE;
3979
3980         session = imap_session_get(folder);
3981         g_return_val_if_fail(session != NULL, FALSE);
3982
3983         selected_folder = (session->mbox != NULL) &&
3984                           (!strcmp(session->mbox, item->item.path));
3985         if (selected_folder) {
3986                 ok = imap_cmd_noop(session);
3987                 if (ok != IMAP_SUCCESS)
3988                         return FALSE;
3989
3990                 if (session->folder_content_changed)
3991                         return TRUE;
3992         } else {
3993                 ok = imap_status(session, IMAP_FOLDER(folder), item->item.path,
3994                                  &exists, &recent, &uid_next, &uid_val, &unseen);
3995                 if (ok != IMAP_SUCCESS)
3996                         return FALSE;
3997
3998                 if ((uid_next != item->uid_next) || (exists < item->item.total_msgs))
3999                         return TRUE;
4000         }
4001
4002         return FALSE;
4003 }
4004
4005 void imap_change_flags(Folder *folder, FolderItem *item, MsgInfo *msginfo, MsgPermFlags newflags)
4006 {
4007         IMAPSession *session;
4008         IMAPFlags flags_set = 0, flags_unset = 0;
4009         gint ok = IMAP_SUCCESS;
4010         MsgNumberList numlist;
4011         
4012         g_return_if_fail(folder != NULL);
4013         g_return_if_fail(folder->klass == &imap_class);
4014         g_return_if_fail(item != NULL);
4015         g_return_if_fail(item->folder == folder);
4016         g_return_if_fail(msginfo != NULL);
4017         g_return_if_fail(msginfo->folder == item);
4018
4019         session = imap_session_get(folder);
4020         if (!session) return;
4021
4022         if ((ok = imap_select(session, IMAP_FOLDER(folder), msginfo->folder->path,
4023             NULL, NULL, NULL, NULL)) != IMAP_SUCCESS)
4024                 return;
4025
4026         if (!MSG_IS_MARKED(msginfo->flags) &&  (newflags & MSG_MARKED))
4027                 flags_set |= IMAP_FLAG_FLAGGED;
4028         if ( MSG_IS_MARKED(msginfo->flags) && !(newflags & MSG_MARKED))
4029                 flags_unset |= IMAP_FLAG_FLAGGED;
4030
4031         if (!MSG_IS_UNREAD(msginfo->flags) &&  (newflags & MSG_UNREAD))
4032                 flags_unset |= IMAP_FLAG_SEEN;
4033         if ( MSG_IS_UNREAD(msginfo->flags) && !(newflags & MSG_UNREAD))
4034                 flags_set |= IMAP_FLAG_SEEN;
4035
4036         if (!MSG_IS_REPLIED(msginfo->flags) &&  (newflags & MSG_REPLIED))
4037                 flags_set |= IMAP_FLAG_ANSWERED;
4038         if ( MSG_IS_REPLIED(msginfo->flags) && !(newflags & MSG_REPLIED))
4039                 flags_set |= IMAP_FLAG_ANSWERED;
4040
4041         numlist.next = NULL;
4042         numlist.data = GINT_TO_POINTER(msginfo->msgnum);
4043         
4044         if (flags_set) {
4045                 ok = imap_set_message_flags(session, &numlist, flags_set, TRUE);
4046                 if (ok != IMAP_SUCCESS) return;
4047         }
4048
4049         if (flags_unset) {
4050                 ok = imap_set_message_flags(session, &numlist, flags_unset, FALSE);
4051                 if (ok != IMAP_SUCCESS) return;
4052         }
4053
4054         msginfo->flags.perm_flags = newflags;
4055
4056         return;
4057 }