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