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