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