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