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