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