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