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