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