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