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