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