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