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