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