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