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