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