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