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