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);