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