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