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