8a03969daa225556b343c86d7bcb3832738f252f
[claws.git] / src / imap.c
1 /*
2  * Sylpheed -- a GTK+ based, lightweight, and fast e-mail client
3  * Copyright (C) 1999-2004 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 FolderClass *imap_get_class(void)
446 {
447         if (imap_class.idstr == NULL) {
448                 imap_class.type = F_IMAP;
449                 imap_class.idstr = "imap";
450                 imap_class.uistr = "IMAP4";
451
452                 /* Folder functions */
453                 imap_class.new_folder = imap_folder_new;
454                 imap_class.destroy_folder = imap_folder_destroy;
455                 imap_class.scan_tree = imap_scan_tree;
456                 imap_class.create_tree = imap_create_tree;
457
458                 /* FolderItem functions */
459                 imap_class.item_new = imap_folder_item_new;
460                 imap_class.item_destroy = imap_folder_item_destroy;
461                 imap_class.item_get_path = imap_item_get_path;
462                 imap_class.create_folder = imap_create_folder;
463                 imap_class.rename_folder = imap_rename_folder;
464                 imap_class.remove_folder = imap_remove_folder;
465                 imap_class.close = imap_close;
466                 imap_class.get_num_list = imap_get_num_list;
467                 imap_class.scan_required = imap_scan_required;
468
469                 /* Message functions */
470                 imap_class.get_msginfo = imap_get_msginfo;
471                 imap_class.get_msginfos = imap_get_msginfos;
472                 imap_class.fetch_msg = imap_fetch_msg;
473                 imap_class.add_msg = imap_add_msg;
474                 imap_class.add_msgs = imap_add_msgs;
475                 imap_class.copy_msg = imap_copy_msg;
476                 imap_class.copy_msgs = imap_copy_msgs;
477                 imap_class.remove_msg = imap_remove_msg;
478                 imap_class.remove_all_msg = imap_remove_all_msg;
479                 imap_class.is_msg_changed = imap_is_msg_changed;
480                 imap_class.change_flags = imap_change_flags;
481         }
482         
483         return &imap_class;
484 }
485
486 static Folder *imap_folder_new(const gchar *name, const gchar *path)
487 {
488         Folder *folder;
489
490         folder = (Folder *)g_new0(IMAPFolder, 1);
491         folder->klass = &imap_class;
492         imap_folder_init(folder, name, path);
493
494         return folder;
495 }
496
497 static void imap_folder_destroy(Folder *folder)
498 {
499         gchar *dir;
500
501         dir = imap_folder_get_path(folder);
502         if (is_dir_exist(dir))
503                 remove_dir_recursive(dir);
504         g_free(dir);
505
506         folder_remote_folder_destroy(REMOTE_FOLDER(folder));
507 }
508
509 static void imap_folder_init(Folder *folder, const gchar *name,
510                              const gchar *path)
511 {
512         folder_remote_folder_init((Folder *)folder, name, path);
513 }
514
515 static FolderItem *imap_folder_item_new(Folder *folder)
516 {
517         IMAPFolderItem *item;
518         
519         item = g_new0(IMAPFolderItem, 1);
520         item->lastuid = 0;
521         item->uid_next = 0;
522         item->uid_list = NULL;
523
524         return (FolderItem *)item;
525 }
526
527 static void imap_folder_item_destroy(Folder *folder, FolderItem *_item)
528 {
529         IMAPFolderItem *item = (IMAPFolderItem *)_item;
530
531         g_return_if_fail(item != NULL);
532         g_slist_free(item->uid_list);
533
534         g_free(_item);
535 }
536
537 static gboolean imap_reset_uid_lists_func(GNode *node, gpointer data)
538 {
539         IMAPFolderItem *item = (IMAPFolderItem *)node->data;
540         
541         item->lastuid = 0;
542         item->uid_next = 0;
543         g_slist_free(item->uid_list);
544         item->uid_list = NULL;
545         
546         return FALSE;
547 }
548
549 static void imap_reset_uid_lists(Folder *folder)
550 {
551         if(folder->node == NULL)
552                 return;
553         
554         /* Destroy all uid lists and rest last uid */
555         g_node_traverse(folder->node, G_IN_ORDER, G_TRAVERSE_ALL, -1, imap_reset_uid_lists_func, NULL); 
556 }
557
558 /* Send CAPABILITY, and examine the server's response to see whether this
559  * connection is pre-authenticated or not and build a list of CAPABILITIES. */
560 static gint imap_greeting(IMAPSession *session)
561 {
562         gchar *capstr;
563         GPtrArray *argbuf;
564
565         imap_gen_send(session, "CAPABILITY");
566         
567         argbuf = g_ptr_array_new();
568
569         if (imap_cmd_ok(session, argbuf) != IMAP_SUCCESS ||
570             ((capstr = search_array_str(argbuf, "CAPABILITY ")) == NULL)) {
571                 ptr_array_free_strings(argbuf);
572                 g_ptr_array_free(argbuf, TRUE);
573                 return -1;
574         }
575
576         session->authenticated = search_array_str(argbuf, "PREAUTH") != NULL;
577         
578         capstr += strlen("CAPABILITY ");
579
580         IMAP_SESSION(session)->capability = g_strsplit(capstr, " ", 0);
581         
582         ptr_array_free_strings(argbuf);
583         g_ptr_array_free(argbuf, TRUE);
584
585         if (imap_has_capability(session, "UIDPLUS")) 
586                 session->uidplus = TRUE; 
587
588         return 0;
589 }
590
591 static gint imap_auth(IMAPSession *session, const gchar *user, const gchar *pass,
592                       IMAPAuthType type)
593 {
594         gint ok;
595
596         if (type == 0 || type == IMAP_AUTH_LOGIN)
597                 ok = imap_cmd_login(session, user, pass);
598         else
599                 ok = imap_cmd_authenticate(session, user, pass, type);
600
601         if (ok == IMAP_SUCCESS)
602                 session->authenticated = TRUE;
603
604         return ok;
605 }
606
607 static IMAPSession *imap_session_get(Folder *folder)
608 {
609         RemoteFolder *rfolder = REMOTE_FOLDER(folder);
610         IMAPSession *session = NULL;
611
612         g_return_val_if_fail(folder != NULL, NULL);
613         g_return_val_if_fail(FOLDER_CLASS(folder) == &imap_class, NULL);
614         g_return_val_if_fail(folder->account != NULL, NULL);
615         
616         if (prefs_common.work_offline)
617                 return NULL;
618
619         /* Make sure we have a session */
620         if (rfolder->session != NULL) {
621                 session = IMAP_SESSION(rfolder->session);
622         } else {
623                 imap_reset_uid_lists(folder);
624                 session = imap_session_new(folder->account);
625         }
626         if(session == NULL)
627                 return NULL;
628
629         if (SESSION(session)->sock->state == CONN_DISCONNECTED) {
630                 debug_print("IMAP server disconnected\n");
631                 session_destroy(SESSION(session));
632                 imap_reset_uid_lists(folder);
633                 session = imap_session_new(folder->account);
634         }
635
636         /* Make sure session is authenticated */
637         if (!IMAP_SESSION(session)->authenticated)
638                 imap_session_authenticate(IMAP_SESSION(session), folder->account);
639         if (!IMAP_SESSION(session)->authenticated) {
640                 session_destroy(SESSION(session));
641                 rfolder->session = NULL;
642                 return NULL;
643         }
644
645         /* Make sure we have parsed the IMAP namespace */
646         imap_parse_namespace(IMAP_SESSION(session),
647                              IMAP_FOLDER(folder));
648
649         /* I think the point of this code is to avoid sending a
650          * keepalive if we've used the session recently and therefore
651          * think it's still alive.  Unfortunately, most of the code
652          * does not yet check for errors on the socket, and so if the
653          * connection drops we don't notice until the timeout expires.
654          * A better solution than sending a NOOP every time would be
655          * for every command to be prepared to retry until it is
656          * successfully sent. -- mbp */
657         if (time(NULL) - session->last_access_time > SESSION_TIMEOUT) {
658                 /* verify that the session is still alive */
659                 if (imap_cmd_noop(session) != IMAP_SUCCESS) {
660                         /* Check if this is the first try to establish a
661                            connection, if yes we don't try to reconnect */
662                         if (rfolder->session == NULL) {
663                                 log_warning(_("Connecting %s failed"),
664                                             folder->account->recv_server);
665                                 session_destroy(SESSION(session));
666                                 session = NULL;
667                         } else {
668                                 log_warning(_("IMAP4 connection to %s has been"
669                                               " disconnected. Reconnecting...\n"),
670                                             folder->account->recv_server);
671                                 session_destroy(SESSION(session));
672                                 /* Clear folders session to make imap_session_get create
673                                    a new session, because of rfolder->session == NULL
674                                    it will not try to reconnect again and so avoid an
675                                    endless loop */
676                                 rfolder->session = NULL;
677                                 session = imap_session_get(folder);
678                         }
679                 }
680         }
681
682         rfolder->session = SESSION(session);
683         if (session) {
684                 session->last_access_time = time(NULL);
685         }
686         return IMAP_SESSION(session);
687 }
688
689 static IMAPSession *imap_session_new(const PrefsAccount *account)
690 {
691         IMAPSession *session;
692         SockInfo *imap_sock;
693         gushort port;
694
695 #ifdef USE_OPENSSL
696         /* FIXME: IMAP over SSL only... */ 
697         SSLType ssl_type;
698
699         port = account->set_imapport ? account->imapport
700                 : account->ssl_imap == SSL_TUNNEL ? IMAPS_PORT : IMAP4_PORT;
701         ssl_type = account->ssl_imap;   
702 #else
703         port = account->set_imapport ? account->imapport
704                 : IMAP4_PORT;
705 #endif
706
707         if (account->set_tunnelcmd) {
708                 log_message(_("creating tunneled IMAP4 connection\n"));
709 #if USE_OPENSSL
710                 if ((imap_sock = imap_open_tunnel(account->recv_server, 
711                                                   account->tunnelcmd,
712                                                   ssl_type)) == NULL)
713 #else
714                 if ((imap_sock = imap_open_tunnel(account->recv_server, 
715                                                   account->tunnelcmd)) == NULL)
716 #endif
717                         return NULL;
718         } else {
719                 g_return_val_if_fail(account->recv_server != NULL, NULL);
720
721                 log_message(_("creating IMAP4 connection to %s:%d ...\n"),
722                             account->recv_server, port);
723                 
724 #if USE_OPENSSL
725                 if ((imap_sock = imap_open(account->recv_server, port,
726                                            ssl_type)) == NULL)
727 #else
728                 if ((imap_sock = imap_open(account->recv_server, port)) == NULL)
729 #endif
730                         return NULL;
731         }
732
733         session = g_new0(IMAPSession, 1);
734         session_init(SESSION(session));
735         SESSION(session)->type             = SESSION_IMAP;
736         SESSION(session)->server           = g_strdup(account->recv_server);
737         SESSION(session)->sock             = imap_sock;
738
739         SESSION(session)->destroy          = imap_session_destroy;
740
741         session->capability = NULL;
742
743         session->authenticated = FALSE;
744         session->mbox = NULL;
745         session->cmd_count = 0;
746
747         /* Only need to log in if the connection was not PREAUTH */
748         if (imap_greeting(session) != IMAP_SUCCESS) {
749                 session_destroy(SESSION(session));
750                 return NULL;
751         }
752
753 #if USE_OPENSSL
754         if (account->ssl_imap == SSL_STARTTLS && 
755             imap_has_capability(session, "STARTTLS")) {
756                 gint ok;
757
758                 ok = imap_cmd_starttls(session);
759                 if (ok != IMAP_SUCCESS) {
760                         log_warning(_("Can't start TLS session.\n"));
761                         session_destroy(SESSION(session));
762                         return NULL;
763                 }
764                 if (!ssl_init_socket_with_method(SESSION(session)->sock, 
765                     SSL_METHOD_TLSv1)) {
766                         session_destroy(SESSION(session));
767                         return NULL;
768                 }
769
770                 imap_free_capabilities(session);
771                 session->authenticated = FALSE;
772                 session->uidplus = FALSE;
773                 session->cmd_count = 1;
774
775                 if (imap_greeting(session) != IMAP_SUCCESS) {
776                         session_destroy(SESSION(session));
777                         return NULL;
778                 }               
779         }
780 #endif
781         log_message("IMAP connection is %s-authenticated\n",
782                     (session->authenticated) ? "pre" : "un");
783
784         return session;
785 }
786
787 static void imap_session_authenticate(IMAPSession *session, 
788                                       const PrefsAccount *account)
789 {
790         gchar *pass;
791
792         g_return_if_fail(account->userid != NULL);
793
794         pass = account->passwd;
795         if (!pass) {
796                 gchar *tmp_pass;
797                 tmp_pass = input_dialog_query_password(account->recv_server, account->userid);
798                 if (!tmp_pass)
799                         return;
800                 Xstrdup_a(pass, tmp_pass, {g_free(tmp_pass); return;});
801                 g_free(tmp_pass);
802         }
803
804         if (imap_auth(session, account->userid, pass, account->imap_auth_type) != IMAP_SUCCESS) {
805                 imap_cmd_logout(session);
806                 return;
807         }
808
809         session->authenticated = TRUE;
810 }
811
812 static void imap_session_destroy(Session *session)
813 {
814         imap_free_capabilities(IMAP_SESSION(session));
815         g_free(IMAP_SESSION(session)->mbox);
816         sock_close(session->sock);
817         session->sock = NULL;
818 }
819
820 static gchar *imap_fetch_msg(Folder *folder, FolderItem *item, gint uid)
821 {
822         gchar *path, *filename;
823         IMAPSession *session;
824         gint ok;
825
826         g_return_val_if_fail(folder != NULL, NULL);
827         g_return_val_if_fail(item != NULL, NULL);
828
829         path = folder_item_get_path(item);
830         if (!is_dir_exist(path))
831                 make_dir_hier(path);
832         filename = g_strconcat(path, G_DIR_SEPARATOR_S, itos(uid), NULL);
833         g_free(path);
834
835         if (is_file_exist(filename)) {
836                 debug_print("message %d has been already cached.\n", uid);
837                 return filename;
838         }
839
840         session = imap_session_get(folder);
841         if (!session) {
842                 g_free(filename);
843                 return NULL;
844         }
845
846         ok = imap_select(session, IMAP_FOLDER(folder), item->path,
847                          NULL, NULL, NULL, NULL);
848         if (ok != IMAP_SUCCESS) {
849                 g_warning("can't select mailbox %s\n", item->path);
850                 g_free(filename);
851                 return NULL;
852         }
853
854         debug_print("getting message %d...\n", uid);
855         ok = imap_cmd_fetch(session, (guint32)uid, filename);
856
857         if (ok != IMAP_SUCCESS) {
858                 g_warning("can't fetch message %d\n", uid);
859                 g_free(filename);
860                 return NULL;
861         }
862
863         return filename;
864 }
865
866 static gint imap_add_msg(Folder *folder, FolderItem *dest, 
867                          const gchar *file, MsgFlags *flags)
868 {
869         gint ret;
870         GSList file_list;
871         MsgFileInfo fileinfo;
872
873         g_return_val_if_fail(file != NULL, -1);
874
875         fileinfo.msginfo = NULL;
876         fileinfo.file = (gchar *)file;
877         fileinfo.flags = flags;
878         file_list.data = &fileinfo;
879         file_list.next = NULL;
880
881         ret = imap_add_msgs(folder, dest, &file_list, NULL);
882         return ret;
883 }
884
885 static gint imap_add_msgs(Folder *folder, FolderItem *dest, GSList *file_list,
886                    GRelation *relation)
887 {
888         gchar *destdir;
889         IMAPSession *session;
890         guint32 last_uid = 0;
891         GSList *cur;
892         MsgFileInfo *fileinfo;
893         gint ok;
894
895         g_return_val_if_fail(folder != NULL, -1);
896         g_return_val_if_fail(dest != NULL, -1);
897         g_return_val_if_fail(file_list != NULL, -1);
898
899         session = imap_session_get(folder);
900         if (!session) return -1;
901
902         destdir = imap_get_real_path(IMAP_FOLDER(folder), dest->path);
903
904         for (cur = file_list; cur != NULL; cur = cur->next) {
905                 IMAPFlags iflags = 0;
906                 guint32 new_uid = 0;
907
908                 fileinfo = (MsgFileInfo *)cur->data;
909
910                 if (fileinfo->flags) {
911                         if (MSG_IS_MARKED(*fileinfo->flags))
912                                 iflags |= IMAP_FLAG_FLAGGED;
913                         if (MSG_IS_REPLIED(*fileinfo->flags))
914                                 iflags |= IMAP_FLAG_ANSWERED;
915                         if (!MSG_IS_UNREAD(*fileinfo->flags))
916                                 iflags |= IMAP_FLAG_SEEN;
917                 }
918
919                 if (dest->stype == F_OUTBOX ||
920                     dest->stype == F_QUEUE  ||
921                     dest->stype == F_DRAFT  ||
922                     dest->stype == F_TRASH)
923                         iflags |= IMAP_FLAG_SEEN;
924
925                 ok = imap_cmd_append(session, destdir, fileinfo->file, iflags, 
926                                      &new_uid);
927
928                 if (ok != IMAP_SUCCESS) {
929                         g_warning("can't append message %s\n", fileinfo->file);
930                         g_free(destdir);
931                         return -1;
932                 }
933
934                 if (relation != NULL)
935                         g_relation_insert(relation, fileinfo->msginfo != NULL ? 
936                                           (gpointer) fileinfo->msginfo : (gpointer) fileinfo,
937                                           GINT_TO_POINTER(dest->last_num + 1));
938                 if (last_uid < new_uid)
939                         last_uid = new_uid;
940         }
941
942         g_free(destdir);
943
944         return last_uid;
945 }
946
947 static gint imap_do_copy_msgs(Folder *folder, FolderItem *dest, 
948                               MsgInfoList *msglist, GRelation *relation)
949 {
950         FolderItem *src;
951         gchar *destdir;
952         GSList *seq_list, *cur;
953         MsgInfo *msginfo;
954         IMAPSession *session;
955         gint ok = IMAP_SUCCESS;
956         GRelation *uid_mapping;
957         gint last_num = 0;
958         
959         g_return_val_if_fail(folder != NULL, -1);
960         g_return_val_if_fail(dest != NULL, -1);
961         g_return_val_if_fail(msglist != NULL, -1);
962
963         session = imap_session_get(folder);
964         if (!session) return -1;
965
966         msginfo = (MsgInfo *)msglist->data;
967
968         src = msginfo->folder;
969         if (src == dest) {
970                 g_warning("the src folder is identical to the dest.\n");
971                 return -1;
972         }
973
974         ok = imap_select(session, IMAP_FOLDER(folder), msginfo->folder->path,
975                          NULL, NULL, NULL, NULL);
976         if (ok != IMAP_SUCCESS)
977                 return ok;
978
979         destdir = imap_get_real_path(IMAP_FOLDER(folder), dest->path);
980         seq_list = imap_get_seq_set_from_msglist(msglist);
981         uid_mapping = g_relation_new(2);
982         g_relation_index(uid_mapping, 0, g_direct_hash, g_direct_equal);
983         
984         for (cur = seq_list; cur != NULL; cur = g_slist_next(cur)) {
985                 gchar *seq_set = (gchar *)cur->data;
986
987                 debug_print("Copying message %s%c[%s] to %s ...\n",
988                             src->path, G_DIR_SEPARATOR,
989                             seq_set, destdir);
990
991                 ok = imap_cmd_copy(session, seq_set, destdir, uid_mapping);
992                 if (ok != IMAP_SUCCESS) {
993                         g_relation_destroy(uid_mapping);
994                         imap_seq_set_free(seq_list);
995                         return -1;
996                 }
997         }
998
999         for (cur = msglist; cur != NULL; cur = g_slist_next(cur)) {
1000                 MsgInfo *msginfo = (MsgInfo *)cur->data;
1001                 GTuples *tuples;
1002
1003                 tuples = g_relation_select(uid_mapping, 
1004                                            GINT_TO_POINTER(msginfo->msgnum),
1005                                            0);
1006                 if (tuples->len > 0) {
1007                         gint num = GPOINTER_TO_INT(g_tuples_index(tuples, 0, 1));
1008                         g_relation_insert(relation, msginfo,
1009                                           GPOINTER_TO_INT(num));
1010                         if (num > last_num)
1011                                 last_num = num;
1012                 } else
1013                         g_relation_insert(relation, msginfo,
1014                                           GPOINTER_TO_INT(0));
1015                 g_tuples_destroy(tuples);
1016         }
1017
1018         g_relation_destroy(uid_mapping);
1019         imap_seq_set_free(seq_list);
1020
1021         g_free(destdir);
1022
1023         if (ok == IMAP_SUCCESS)
1024                 return last_num;
1025         else
1026                 return -1;
1027 }
1028
1029 static gint imap_copy_msg(Folder *folder, FolderItem *dest, MsgInfo *msginfo)
1030 {
1031         GSList msglist;
1032
1033         g_return_val_if_fail(msginfo != NULL, -1);
1034
1035         msglist.data = msginfo;
1036         msglist.next = NULL;
1037
1038         return imap_copy_msgs(folder, dest, &msglist, NULL);
1039 }
1040
1041 static gint imap_copy_msgs(Folder *folder, FolderItem *dest, 
1042                     MsgInfoList *msglist, GRelation *relation)
1043 {
1044         MsgInfo *msginfo;
1045         GSList *file_list;
1046         gint ret;
1047
1048         g_return_val_if_fail(folder != NULL, -1);
1049         g_return_val_if_fail(dest != NULL, -1);
1050         g_return_val_if_fail(msglist != NULL, -1);
1051
1052         msginfo = (MsgInfo *)msglist->data;
1053         g_return_val_if_fail(msginfo->folder != NULL, -1);
1054
1055         if (folder == msginfo->folder->folder)
1056                 return imap_do_copy_msgs(folder, dest, msglist, relation);
1057
1058         file_list = procmsg_get_message_file_list(msglist);
1059         g_return_val_if_fail(file_list != NULL, -1);
1060
1061         ret = imap_add_msgs(folder, dest, file_list, relation);
1062
1063         procmsg_message_file_list_free(file_list);
1064
1065         return ret;
1066 }
1067
1068 static gint imap_remove_msg(Folder *folder, FolderItem *item, gint uid)
1069 {
1070         gint ok;
1071         IMAPSession *session;
1072         gchar *dir;
1073         MsgNumberList numlist;
1074         
1075         g_return_val_if_fail(folder != NULL, -1);
1076         g_return_val_if_fail(FOLDER_CLASS(folder) == &imap_class, -1);
1077         g_return_val_if_fail(item != NULL, -1);
1078
1079         session = imap_session_get(folder);
1080         if (!session) return -1;
1081
1082         ok = imap_select(session, IMAP_FOLDER(folder), item->path,
1083                          NULL, NULL, NULL, NULL);
1084         if (ok != IMAP_SUCCESS)
1085                 return ok;
1086
1087         numlist.next = NULL;
1088         numlist.data = GINT_TO_POINTER(uid);
1089         
1090         ok = imap_set_message_flags
1091                 (IMAP_SESSION(REMOTE_FOLDER(folder)->session),
1092                 &numlist, IMAP_FLAG_DELETED, TRUE);
1093         if (ok != IMAP_SUCCESS) {
1094                 log_warning(_("can't set deleted flags: %d\n"), uid);
1095                 return ok;
1096         }
1097
1098         if (!session->uidplus) {
1099                 ok = imap_cmd_expunge(session, NULL);
1100         } else {
1101                 gchar *uidstr;
1102
1103                 uidstr = g_strdup_printf("%u", uid);
1104                 ok = imap_cmd_expunge(session, uidstr);
1105                 g_free(uidstr);
1106         }
1107         if (ok != IMAP_SUCCESS) {
1108                 log_warning(_("can't expunge\n"));
1109                 return ok;
1110         }
1111
1112         IMAP_FOLDER_ITEM(item)->uid_list = g_slist_remove(
1113             IMAP_FOLDER_ITEM(item)->uid_list, numlist.data);
1114         dir = folder_item_get_path(item);
1115         if (is_dir_exist(dir))
1116                 remove_numbered_files(dir, uid, uid);
1117         g_free(dir);
1118
1119         return IMAP_SUCCESS;
1120 }
1121
1122 static gint imap_remove_all_msg(Folder *folder, FolderItem *item)
1123 {
1124         gint ok;
1125         IMAPSession *session;
1126         gchar *dir;
1127
1128         g_return_val_if_fail(folder != NULL, -1);
1129         g_return_val_if_fail(item != NULL, -1);
1130
1131         session = imap_session_get(folder);
1132         if (!session) return -1;
1133
1134         ok = imap_select(session, IMAP_FOLDER(folder), item->path,
1135                          NULL, NULL, NULL, NULL);
1136         if (ok != IMAP_SUCCESS)
1137                 return ok;
1138
1139         imap_gen_send(session, "STORE 1:* +FLAGS.SILENT (\\Deleted)");
1140         ok = imap_cmd_ok(session, NULL);
1141         if (ok != IMAP_SUCCESS) {
1142                 log_warning(_("can't set deleted flags: 1:*\n"));
1143                 return ok;
1144         }
1145
1146         ok = imap_cmd_expunge(session, NULL);
1147         if (ok != IMAP_SUCCESS) {
1148                 log_warning(_("can't expunge\n"));
1149                 return ok;
1150         }
1151
1152         dir = folder_item_get_path(item);
1153         if (is_dir_exist(dir))
1154                 remove_all_numbered_files(dir);
1155         g_free(dir);
1156
1157         return IMAP_SUCCESS;
1158 }
1159
1160 static gboolean imap_is_msg_changed(Folder *folder, FolderItem *item,
1161                                     MsgInfo *msginfo)
1162 {
1163         /* TODO: properly implement this method */
1164         return FALSE;
1165 }
1166
1167 static gint imap_close(Folder *folder, FolderItem *item)
1168 {
1169         gint ok;
1170         IMAPSession *session;
1171
1172         g_return_val_if_fail(folder != NULL, -1);
1173         g_return_val_if_fail(item != NULL, -1);
1174         g_return_val_if_fail(item->path != NULL, -1);
1175
1176         session = imap_session_get(folder);
1177         if (!session) return -1;
1178
1179         if (session->mbox) {
1180                 if (strcmp2(session->mbox, item->path) != 0) return -1;
1181
1182                 ok = imap_cmd_close(session);
1183                 if (ok != IMAP_SUCCESS)
1184                         log_warning(_("can't close folder\n"));
1185
1186                 g_free(session->mbox);
1187                 session->mbox = NULL;
1188
1189                 return ok;
1190         }
1191
1192         return 0;
1193 }
1194
1195 static gint imap_scan_tree(Folder *folder)
1196 {
1197         FolderItem *item = NULL;
1198         IMAPSession *session;
1199         gchar *root_folder = NULL;
1200
1201         g_return_val_if_fail(folder != NULL, -1);
1202         g_return_val_if_fail(folder->account != NULL, -1);
1203
1204         session = imap_session_get(folder);
1205         if (!session) {
1206                 if (!folder->node) {
1207                         folder_tree_destroy(folder);
1208                         item = folder_item_new(folder, folder->name, NULL);
1209                         item->folder = folder;
1210                         folder->node = item->node = g_node_new(item);
1211                 }
1212                 return -1;
1213         }
1214
1215         if (folder->account->imap_dir && *folder->account->imap_dir) {
1216                 gchar *real_path;
1217                 GPtrArray *argbuf;
1218                 gint ok;
1219
1220                 Xstrdup_a(root_folder, folder->account->imap_dir, return -1);
1221                 extract_quote(root_folder, '"');
1222                 subst_char(root_folder,
1223                            imap_get_path_separator(IMAP_FOLDER(folder),
1224                                                    root_folder),
1225                            '/');
1226                 strtailchomp(root_folder, '/');
1227                 real_path = imap_get_real_path
1228                         (IMAP_FOLDER(folder), root_folder);
1229                 debug_print("IMAP root directory: %s\n", real_path);
1230
1231                 /* check if root directory exist */
1232                 argbuf = g_ptr_array_new();
1233                 ok = imap_cmd_list(session, NULL, real_path, argbuf);
1234                 if (ok != IMAP_SUCCESS ||
1235                     search_array_str(argbuf, "LIST ") == NULL) {
1236                         log_warning(_("root folder %s does not exist\n"), real_path);
1237                         g_ptr_array_free(argbuf, TRUE);
1238                         g_free(real_path);
1239
1240                         if (!folder->node) {
1241                                 item = folder_item_new(folder, folder->name, NULL);
1242                                 item->folder = folder;
1243                                 folder->node = item->node = g_node_new(item);
1244                         }
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 (!folder_item_parent(item) || 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 (!folder_item_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         guchar *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(*(guchar *)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(*(guchar *)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(*(guchar *)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(*(guchar *)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(*(guchar *)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(*(guchar *)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(*(guchar *)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 }