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