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