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