allow spaces in imap foldernames
[claws.git] / src / imap.c
1 /*
2  * Sylpheed -- a GTK+ based, lightweight, and fast e-mail client
3  * Copyright (C) 1999-2001 Hiroyuki Yamamoto
4  *
5  * This program is free software; you can redistribute it and/or modify
6  * it under the terms of the GNU General Public License as published by
7  * the Free Software Foundation; either version 2 of the License, or
8  * (at your option) any later version.
9  *
10  * This program is distributed in the hope that it will be useful,
11  * but WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13  * GNU General Public License for more details.
14  *
15  * You should have received a copy of the GNU General Public License
16  * along with this program; if not, write to the Free Software
17  * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
18  */
19
20 #ifdef HAVE_CONFIG_H
21 #  include "config.h"
22 #endif
23
24 #include "defs.h"
25
26 #include <glib.h>
27 #include <stdio.h>
28 #include <string.h>
29 #include <stdlib.h>
30 #include <dirent.h>
31 #include <unistd.h>
32 #include <ctype.h>
33 #include <time.h>
34
35 #include "intl.h"
36 #include "imap.h"
37 #include "socket.h"
38 #include "ssl.h"
39 #include "recv.h"
40 #include "procmsg.h"
41 #include "procheader.h"
42 #include "folder.h"
43 #include "statusbar.h"
44 #include "prefs_account.h"
45 #include "codeconv.h"
46 #include "utils.h"
47 #include "inputdialog.h"
48
49 #define IMAP4_PORT      143
50 #if USE_SSL
51 #define IMAPS_PORT      993
52 #endif
53
54 static GList *session_list = NULL;
55
56 static gint imap_cmd_count = 0;
57
58 static IMAPSession *imap_session_get    (Folder         *folder);
59 static gchar *imap_query_password       (const gchar    *server,
60                                          const gchar    *user);
61
62 static void imap_scan_tree_recursive    (IMAPSession    *session,
63                                          FolderItem     *item,
64                                          IMAPNameSpace  *namespace);
65 static GSList *imap_parse_list          (IMAPSession    *session,
66                                          const gchar    *path);
67 static gint imap_create_trash           (Folder         *folder);
68
69 static gint imap_do_copy                (Folder         *folder,
70                                          FolderItem     *dest,
71                                          MsgInfo        *msginfo,
72                                          gboolean        remove_source);
73 static gint imap_do_copy_msgs_with_dest (Folder         *folder,
74                                          FolderItem     *dest, 
75                                          GSList         *msglist,
76                                          gboolean        remove_source);
77
78 static GSList *imap_get_uncached_messages       (IMAPSession    *session,
79                                                  FolderItem     *item,
80                                                  guint32         first_uid,
81                                                  guint32         last_uid);
82 static GSList *imap_delete_cached_messages      (GSList         *mlist,
83                                                  FolderItem     *item,
84                                                  guint32         first_uid,
85                                                  guint32         last_uid);
86 static void imap_delete_all_cached_messages     (FolderItem     *item);
87
88 #if USE_SSL
89 static SockInfo *imap_open              (const gchar    *server,
90                                          gushort         port,
91                                          gchar          *buf,
92                                          gboolean        use_ssl);
93 #else
94 static SockInfo *imap_open              (const gchar    *server,
95                                          gushort         port,
96                                          gchar          *buf);
97 #endif
98
99 static gint imap_set_message_flags      (IMAPSession    *session,
100                                          guint32         first_uid,
101                                          guint32         last_uid,
102                                          IMAPFlags       flag,
103                                          gboolean        is_set);
104 static gint imap_select                 (IMAPSession    *session,
105                                          IMAPFolder     *folder,
106                                          const gchar    *path,
107                                          gint           *exists,
108                                          gint           *recent,
109                                          gint           *unseen,
110                                          guint32        *uid_validity);
111 static gint imap_get_uid                (IMAPSession    *session,
112                                          gint            msgnum,
113                                          guint32        *uid);
114 static gint imap_status                 (IMAPSession    *session,
115                                          IMAPFolder     *folder,
116                                          const gchar    *path,
117                                          gint           *messages,
118                                          gint           *recent,
119                                          gint           *unseen,
120                                          guint32        *uid_validity);
121
122 static void imap_parse_namespace                (IMAPSession    *session,
123                                                  IMAPFolder     *folder);
124 static IMAPNameSpace *imap_find_namespace       (IMAPFolder     *folder,
125                                                  const gchar    *path);
126 static gchar *imap_get_real_path                (IMAPFolder     *folder,
127                                                  const gchar    *path);
128
129 static gchar *imap_parse_atom           (SockInfo       *sock,
130                                          gchar          *src,
131                                          gchar          *dest,
132                                          gint            dest_len,
133                                          GString        *str);
134 static gchar *imap_parse_one_address    (SockInfo       *sock,
135                                          gchar          *start,
136                                          gchar          *out_from_str,
137                                          gchar          *out_fromname_str,
138                                          GString        *str);
139 static gchar *imap_parse_address        (SockInfo       *sock,
140                                          gchar          *start,
141                                          gchar         **out_from_str,
142                                          gchar         **out_fromname_str,
143                                          GString        *str);
144 static MsgFlags imap_parse_flags        (const gchar    *flag_str);
145 static MsgInfo *imap_parse_envelope     (SockInfo       *sock,
146                                          GString        *line_str);
147
148 /* low-level IMAP4rev1 commands */
149 static gint imap_cmd_login      (SockInfo       *sock,
150                                  const gchar    *user,
151                                  const gchar    *pass);
152 static gint imap_cmd_logout     (SockInfo       *sock);
153 static gint imap_cmd_noop       (SockInfo       *sock);
154 static gint imap_cmd_namespace  (SockInfo       *sock,
155                                  gchar         **ns_str);
156 static gint imap_cmd_list       (SockInfo       *sock,
157                                  const gchar    *ref,
158                                  const gchar    *mailbox,
159                                  GPtrArray      *argbuf);
160 static gint imap_cmd_do_select  (SockInfo       *sock,
161                                  const gchar    *folder,
162                                  gboolean        examine,
163                                  gint           *exists,
164                                  gint           *recent,
165                                  gint           *unseen,
166                                  guint32        *uid_validity);
167 static gint imap_cmd_select     (SockInfo       *sock,
168                                  const gchar    *folder,
169                                  gint           *exists,
170                                  gint           *recent,
171                                  gint           *unseen,
172                                  guint32        *uid_validity);
173 static gint imap_cmd_examine    (SockInfo       *sock,
174                                  const gchar    *folder,
175                                  gint           *exists,
176                                  gint           *recent,
177                                  gint           *unseen,
178                                  guint32        *uid_validity);
179 static gint imap_cmd_create     (SockInfo       *sock,
180                                  const gchar    *folder);
181 static gint imap_cmd_delete     (SockInfo       *sock,
182                                  const gchar    *folder);
183 static gint imap_cmd_envelope   (SockInfo       *sock,
184                                  guint32         first_uid,
185                                  guint32         last_uid);
186 #if 0
187 static gint imap_cmd_search     (SockInfo       *sock,
188                                  GSList         *numlist);
189 #endif
190 static gint imap_cmd_fetch      (SockInfo       *sock,
191                                  guint32         uid,
192                                  const gchar    *filename);
193 static gint imap_cmd_append     (SockInfo       *sock,
194                                  const gchar    *destfolder,
195                                  const gchar    *file);
196 static gint imap_cmd_copy       (SockInfo       *sock,
197                                  guint32         uid,
198                                  const gchar    *destfolder);
199 static gint imap_cmd_store      (SockInfo       *sock,
200                                  guint32         first_uid,
201                                  guint32         last_uid,
202                                  gchar          *sub_cmd);
203 static gint imap_cmd_expunge    (SockInfo       *sock);
204
205 static gint imap_cmd_ok         (SockInfo       *sock,
206                                  GPtrArray      *argbuf);
207 static void imap_cmd_gen_send   (SockInfo       *sock,
208                                  const gchar    *format, ...);
209 static gint imap_cmd_gen_recv   (SockInfo       *sock,
210                                  gchar          *buf,
211                                  gint            size);
212
213 /* misc utility functions */
214 static gchar *strchr_cpy                        (const gchar    *src,
215                                                  gchar           ch,
216                                                  gchar          *dest,
217                                                  gint            len);
218 static gchar *get_quoted                        (const gchar    *src,
219                                                  gchar           ch,
220                                                  gchar          *dest,
221                                                  gint            len);
222 static gchar *search_array_contain_str          (GPtrArray      *array,
223                                                  gchar          *str);
224 static void imap_path_separator_subst           (gchar          *str,
225                                                  gchar           separator);
226
227 static IMAPSession *imap_session_get(Folder *folder)
228 {
229         RemoteFolder *rfolder = REMOTE_FOLDER(folder);
230         gushort port;
231
232         g_return_val_if_fail(folder != NULL, NULL);
233         g_return_val_if_fail(folder->type == F_IMAP, NULL);
234         g_return_val_if_fail(folder->account != NULL, NULL);
235
236 #if USE_SSL
237         port = folder->account->set_imapport ? folder->account->imapport
238                 : folder->account->ssl_imap ? IMAPS_PORT : IMAP4_PORT;
239 #else
240         port = folder->account->set_imapport ? folder->account->imapport
241                 : IMAP4_PORT;
242 #endif
243
244         if (!rfolder->session) {
245                 rfolder->session =
246 #if USE_SSL
247                         imap_session_new(folder->account->recv_server, port,
248                                          folder->account->userid,
249                                          folder->account->passwd,
250                                          folder->account->ssl_imap);
251 #else
252                         imap_session_new(folder->account->recv_server, port,
253                                          folder->account->userid,
254                                          folder->account->passwd);
255 #endif
256                 if (rfolder->session) {
257                         imap_parse_namespace(IMAP_SESSION(rfolder->session),
258                                              IMAP_FOLDER(folder));
259                         rfolder->session->last_access_time = time(NULL);
260                 }
261                 statusbar_pop_all();
262                 return IMAP_SESSION(rfolder->session);
263         }
264
265         if (time(NULL) - rfolder->session->last_access_time < SESSION_TIMEOUT) {
266                 rfolder->session->last_access_time = time(NULL);
267                 statusbar_pop_all();
268                 return IMAP_SESSION(rfolder->session);
269         }
270
271         if (imap_cmd_noop(rfolder->session->sock) != IMAP_SUCCESS) {
272                 log_warning(_("IMAP4 connection to %s:%d has been"
273                               " disconnected. Reconnecting...\n"),
274                             folder->account->recv_server, port);
275                 session_destroy(rfolder->session);
276                 rfolder->session =
277 #if USE_SSL
278                         imap_session_new(folder->account->recv_server, port,
279                                          folder->account->userid,
280                                          folder->account->passwd,
281                                          folder->account->ssl_imap);
282 #else
283                         imap_session_new(folder->account->recv_server, port,
284                                          folder->account->userid,
285                                          folder->account->passwd);
286 #endif
287                 if (rfolder->session)
288                         imap_parse_namespace(IMAP_SESSION(rfolder->session),
289                                              IMAP_FOLDER(folder));
290         }
291
292         if (rfolder->session)
293                 rfolder->session->last_access_time = time(NULL);
294         statusbar_pop_all();
295         return IMAP_SESSION(rfolder->session);
296 }
297
298 static gchar *imap_query_password(const gchar *server, const gchar *user)
299 {
300         gchar *message;
301         gchar *pass;
302
303         message = g_strdup_printf(_("Input password for %s on %s:"),
304                                   user, server);
305         pass = input_dialog_with_invisible(_("Input password"), message, NULL);
306         g_free(message);
307
308         return pass;
309 }
310
311 #if USE_SSL
312 Session *imap_session_new(const gchar *server, gushort port,
313                           const gchar *user, const gchar *pass,
314                           gboolean use_ssl)
315 #else
316 Session *imap_session_new(const gchar *server, gushort port,
317                           const gchar *user, const gchar *pass)
318 #endif
319 {
320         gchar buf[IMAPBUFSIZE];
321         IMAPSession *session;
322         SockInfo *imap_sock;
323
324         g_return_val_if_fail(server != NULL, NULL);
325         g_return_val_if_fail(user != NULL, NULL);
326
327         if (!pass) {
328                 gchar *tmp_pass;
329                 tmp_pass = imap_query_password(server, user);
330                 if (!tmp_pass)
331                         return NULL;
332                 Xstrdup_a(pass, tmp_pass, {g_free(tmp_pass); return NULL;});
333                 g_free(tmp_pass);
334         }
335
336         log_message(_("creating IMAP4 connection to %s:%d ...\n"),
337                     server, port);
338
339 #if USE_SSL
340         if ((imap_sock = imap_open(server, port, buf, use_ssl)) == NULL)
341 #else
342         if ((imap_sock = imap_open(server, port, buf)) == NULL)
343 #endif
344                 return NULL;
345         if (imap_cmd_login(imap_sock, user, pass) != IMAP_SUCCESS) {
346                 imap_cmd_logout(imap_sock);
347                 sock_close(imap_sock);
348                 return NULL;
349         }
350
351         session = g_new(IMAPSession, 1);
352         SESSION(session)->type             = SESSION_IMAP;
353         SESSION(session)->server           = g_strdup(server);
354         SESSION(session)->sock             = imap_sock;
355         SESSION(session)->connected        = TRUE;
356         SESSION(session)->phase            = SESSION_READY;
357         SESSION(session)->last_access_time = time(NULL);
358         SESSION(session)->data             = NULL;
359         session->mbox = NULL;
360
361         session_list = g_list_append(session_list, session);
362
363         return SESSION(session);
364 }
365
366 void imap_session_destroy(IMAPSession *session)
367 {
368         sock_close(SESSION(session)->sock);
369         SESSION(session)->sock = NULL;
370
371         g_free(session->mbox);
372
373         session_list = g_list_remove(session_list, session);
374 }
375
376 void imap_session_destroy_all(void)
377 {
378         while (session_list != NULL) {
379                 IMAPSession *session = (IMAPSession *)session_list->data;
380
381                 imap_cmd_logout(SESSION(session)->sock);
382                 imap_session_destroy(session);
383         }
384 }
385
386 #define THROW goto catch
387
388 GSList *imap_get_msg_list(Folder *folder, FolderItem *item, gboolean use_cache)
389 {
390         GSList *mlist = NULL;
391         IMAPSession *session;
392         gint ok, exists = 0, recent = 0, unseen = 0;
393         guint32 uid_validity = 0;
394         guint32 first_uid = 0, last_uid = 0, begin;
395
396         g_return_val_if_fail(folder != NULL, NULL);
397         g_return_val_if_fail(item != NULL, NULL);
398         g_return_val_if_fail(folder->type == F_IMAP, NULL);
399         g_return_val_if_fail(folder->account != NULL, NULL);
400
401         session = imap_session_get(folder);
402
403         if (!session) {
404                 mlist = procmsg_read_cache(item, FALSE);
405                 item->last_num = procmsg_get_last_num_in_cache(mlist);
406                 procmsg_set_flags(mlist, item);
407                 statusbar_pop_all();
408                 return mlist;
409         }
410
411         ok = imap_select(session, IMAP_FOLDER(folder), item->path,
412                          &exists, &recent, &unseen, &uid_validity);
413         if (ok != IMAP_SUCCESS) THROW;
414         if (exists > 0) {
415                 ok = imap_get_uid(session, 1, &first_uid);
416                 if (ok != IMAP_SUCCESS) THROW;
417                 if (1 != exists) {
418                         ok = imap_get_uid(session, exists, &last_uid);
419                         if (ok != IMAP_SUCCESS) THROW;
420                 } else
421                         last_uid = first_uid;
422         } else {
423                 imap_delete_all_cached_messages(item);
424                 statusbar_pop_all();
425                 return NULL;
426         }
427
428         if (use_cache) {
429                 guint32 cache_last;
430
431                 mlist = procmsg_read_cache(item, FALSE);
432                 procmsg_set_flags(mlist, item);
433                 cache_last = procmsg_get_last_num_in_cache(mlist);
434
435                 /* calculating the range of envelope to get */
436                 if (item->mtime != uid_validity) {
437                         /* mailbox is changed (get all) */
438                         begin = first_uid;
439                 } else if (last_uid < cache_last) {
440                         /* mailbox is changed (get all) */
441                         begin = first_uid;
442                 } else if (last_uid == cache_last) {
443                         /* mailbox unchanged (get none)*/
444                         begin = 0;
445                 } else {
446                         begin = cache_last + 1;
447                 }
448
449                 item->mtime = uid_validity;
450
451                 if (first_uid > 0 && last_uid > 0) {
452                         mlist = imap_delete_cached_messages(mlist, item,
453                                                             0, first_uid - 1);
454                         mlist = imap_delete_cached_messages(mlist, item,
455                                                             last_uid + 1,
456                                                             UINT_MAX);
457                 }
458                 if (begin > 0)
459                         mlist = imap_delete_cached_messages(mlist, item,
460                                                             begin, UINT_MAX);
461         } else {
462                 imap_delete_all_cached_messages(item);
463                 begin = first_uid;
464         }
465
466         if (begin > 0 && begin <= last_uid) {
467                 GSList *newlist;
468                 newlist = imap_get_uncached_messages(session, item,
469                                                      begin, last_uid);
470                 mlist = g_slist_concat(mlist, newlist);
471         }
472
473         item->last_num = last_uid;
474
475 catch:
476         statusbar_pop_all();
477         return mlist;
478 }
479
480 #undef THROW
481
482 gchar *imap_fetch_msg(Folder *folder, FolderItem *item, gint uid)
483 {
484         gchar *path, *filename;
485         IMAPSession *session;
486         gint ok;
487
488         g_return_val_if_fail(folder != NULL, NULL);
489         g_return_val_if_fail(item != NULL, NULL);
490
491         path = folder_item_get_path(item);
492         if (!is_dir_exist(path))
493                 make_dir_hier(path);
494         filename = g_strconcat(path, G_DIR_SEPARATOR_S, itos(uid), NULL);
495         g_free(path);
496  
497         if (is_file_exist(filename)) {
498                 debug_print(_("message %d has been already cached.\n"), uid);
499                 return filename;
500         }
501
502         session = imap_session_get(folder);
503         if (!session) {
504                 g_free(filename);
505                 return NULL;
506         }
507
508         debug_print(_("getting message %d...\n"), uid);
509         ok = imap_cmd_fetch(SESSION(session)->sock, (guint32)uid, filename);
510
511         statusbar_pop_all();
512
513         if (ok != IMAP_SUCCESS) {
514                 g_warning(_("can't fetch message %d\n"), uid);
515                 g_free(filename);
516                 return NULL;
517         }
518
519         return filename;
520 }
521
522 gint imap_add_msg(Folder *folder, FolderItem *dest, const gchar *file,
523                   gboolean remove_source)
524 {
525         IMAPSession *session;
526         gint ok;
527
528         g_return_val_if_fail(folder != NULL, -1);
529         g_return_val_if_fail(dest != NULL, -1);
530         g_return_val_if_fail(file != NULL, -1);
531
532         session = imap_session_get(folder);
533         if (!session)
534                 return -1;
535
536         ok = imap_cmd_append(SESSION(session)->sock, dest->path, file);
537         if (ok != IMAP_SUCCESS) {
538                 g_warning(_("can't append message %s\n"), file);
539                 return -1;
540         }
541
542         if (remove_source) {
543                 if (unlink(file) < 0)
544                         FILE_OP_ERROR(file, "unlink");
545         }
546
547         return dest->last_num;
548 }
549
550 static gint imap_do_copy(Folder *folder, FolderItem *dest, MsgInfo *msginfo,
551                          gboolean remove_source)
552 {
553         gchar *destdir;
554         IMAPSession *session;
555         gint ok;
556
557         g_return_val_if_fail(folder != NULL, -1);
558         g_return_val_if_fail(folder->type == F_IMAP, -1);
559         g_return_val_if_fail(dest != NULL, -1);
560         g_return_val_if_fail(msginfo != NULL, -1);
561
562         session = imap_session_get(folder);
563         if (!session) return -1;
564
565         if (msginfo->folder == dest) {
566                 g_warning(_("the src folder is identical to the dest.\n"));
567                 return -1;
568         }
569
570         destdir = imap_get_real_path(IMAP_FOLDER(folder), dest->path);
571
572         if (remove_source)
573                 debug_print(_("Moving message %s%c%d to %s ...\n"),
574                             msginfo->folder->path, G_DIR_SEPARATOR,
575                             msginfo->msgnum, destdir);
576         else
577                 debug_print(_("Copying message %s%c%d to %s ...\n"),
578                             msginfo->folder->path, G_DIR_SEPARATOR,
579                             msginfo->msgnum, destdir);
580
581         ok = imap_cmd_copy(SESSION(session)->sock, msginfo->msgnum, destdir);
582
583         if (ok == IMAP_SUCCESS && remove_source) {
584                 imap_set_message_flags(session, msginfo->msgnum, msginfo->msgnum,
585                                        IMAP_FLAG_DELETED, TRUE);
586                 ok = imap_cmd_expunge(SESSION(session)->sock);
587         }
588
589         g_free(destdir);
590         statusbar_pop_all();
591
592         return ok;
593 }
594
595 static gint imap_do_copy_msgs_with_dest(Folder *folder, FolderItem *dest, 
596                                         GSList *msglist,
597                                         gboolean remove_source)
598 {
599         gchar *destdir;
600         GSList *cur;
601         MsgInfo *msginfo;
602         IMAPSession *session;
603         gint ok;
604
605         g_return_val_if_fail(folder != NULL, -1);
606         g_return_val_if_fail(dest != NULL, -1);
607         g_return_val_if_fail(msglist != NULL, -1);
608
609         session = imap_session_get(folder);
610         if (!session) return -1;
611
612         destdir = imap_get_real_path(IMAP_FOLDER(folder), dest->path);
613
614         for (cur = msglist; cur != NULL; cur = cur->next) {
615                 msginfo = (MsgInfo *)cur->data;
616
617                 if (msginfo->folder == dest) {
618                         g_warning(_("the src folder is identical to the dest.\n"));
619                         continue;
620                 }
621
622                 if (remove_source)
623                         debug_print(_("Moving message %s%c%d to %s ...\n"),
624                                     msginfo->folder->path, G_DIR_SEPARATOR,
625                                     msginfo->msgnum, destdir);
626                 else
627                         debug_print(_("Copying message %s%c%d to %s ...\n"),
628                                     msginfo->folder->path, G_DIR_SEPARATOR,
629                                     msginfo->msgnum, destdir);
630
631                 ok = imap_cmd_copy(SESSION(session)->sock, msginfo->msgnum,
632                                    destdir);
633
634                 if (ok == IMAP_SUCCESS && remove_source) {
635                         imap_set_message_flags
636                                 (session, msginfo->msgnum, msginfo->msgnum,
637                                  IMAP_FLAG_DELETED, TRUE);
638                 }
639         }
640
641         if (remove_source)
642                 ok = imap_cmd_expunge(SESSION(session)->sock);
643
644         g_free(destdir);
645         statusbar_pop_all();
646
647         return IMAP_SUCCESS;
648 }
649
650 gint imap_move_msg(Folder *folder, FolderItem *dest, MsgInfo *msginfo)
651 {
652         return imap_do_copy(folder, dest, msginfo, TRUE);
653 }
654
655 gint imap_move_msgs_with_dest(Folder *folder, FolderItem *dest, 
656                               GSList *msglist)
657 {
658         return imap_do_copy_msgs_with_dest(folder, dest, msglist, TRUE);
659 }
660
661 gint imap_copy_msg(Folder *folder, FolderItem *dest, MsgInfo *msginfo)
662 {
663         return imap_do_copy(folder, dest, msginfo, FALSE);
664 }
665
666 gint imap_copy_msgs_with_dest(Folder *folder, FolderItem *dest, 
667                               GSList *msglist)
668 {
669         return imap_do_copy_msgs_with_dest(folder, dest, msglist, FALSE);
670 }
671
672 gint imap_remove_msg(Folder *folder, FolderItem *item, gint uid)
673 {
674         gint exists, recent, unseen;
675         guint32 uid_validity;
676         gint ok;
677         IMAPSession *session;
678
679         g_return_val_if_fail(folder != NULL, -1);
680         g_return_val_if_fail(folder->type == F_IMAP, -1);
681         g_return_val_if_fail(item != NULL, -1);
682
683         session = imap_session_get(folder);
684         if (!session) return -1;
685
686         ok = imap_select(session, IMAP_FOLDER(folder), item->path,
687                          &exists, &recent, &unseen, &uid_validity);
688         statusbar_pop_all();
689         if (ok != IMAP_SUCCESS)
690                 return ok;
691
692         ok = imap_set_message_flags
693                 (IMAP_SESSION(REMOTE_FOLDER(folder)->session),
694                  (guint32)uid, (guint32)uid, IMAP_FLAG_DELETED, TRUE);
695         statusbar_pop_all();
696         if (ok != IMAP_SUCCESS) {
697                 log_warning(_("can't set deleted flags: %d\n"), uid);
698                 return ok;
699         }
700
701         ok = imap_cmd_expunge(SESSION(session)->sock);
702         statusbar_pop_all();
703         if (ok != IMAP_SUCCESS) {
704                 log_warning(_("can't expunge\n"));
705                 return ok;
706         }
707
708         return IMAP_SUCCESS;
709 }
710
711 #define QUOTE_IF_REQUIRED(out, str) \
712 { \
713         if (*str != '"' && strchr(str, ' ')) { \
714                 gint len; \
715                 len = strlen(str) + 3; \
716                 Xalloca(out, len, return IMAP_ERROR); \
717                 g_snprintf(out, len, "\"%s\"", str); \
718         } else { \
719                 Xstrdup_a(out, str, return IMAP_ERROR); \
720         } \
721 }
722
723 gint imap_remove_all_msg(Folder *folder, FolderItem *item)
724 {
725         gint exists, recent, unseen;
726         guint32 uid_validity;
727         gint ok;
728         IMAPSession *session;
729
730         g_return_val_if_fail(folder != NULL, -1);
731         g_return_val_if_fail(item != NULL, -1);
732
733         session = imap_session_get(folder);
734         if (!session) return -1;
735
736         ok = imap_select(session, IMAP_FOLDER(folder), item->path,
737                          &exists, &recent, &unseen, &uid_validity);
738         statusbar_pop_all();
739         if (ok != IMAP_SUCCESS)
740                 return ok;
741         if (exists == 0)
742                 return IMAP_SUCCESS;
743
744         imap_cmd_gen_send(SESSION(session)->sock,
745                           "STORE 1:%d +FLAGS (\\Deleted)", exists);
746         ok = imap_cmd_ok(SESSION(session)->sock, NULL);
747         statusbar_pop_all();
748         if (ok != IMAP_SUCCESS) {
749                 log_warning(_("can't set deleted flags: 1:%d\n"), exists);
750                 return ok;
751         }
752
753         ok = imap_cmd_expunge(SESSION(session)->sock);
754         statusbar_pop_all();
755         if (ok != IMAP_SUCCESS) {
756                 log_warning(_("can't expunge\n"));
757                 return ok;
758         }
759
760         return IMAP_SUCCESS;
761 }
762
763 void imap_scan_folder(Folder *folder, FolderItem *item)
764 {
765         IMAPSession *session;
766         gint messages, recent, unseen;
767         guint32 uid_validity;
768         gint ok;
769
770         g_return_if_fail(folder != NULL);
771         g_return_if_fail(item != NULL);
772
773         session = imap_session_get(folder);
774         if (!session) return;
775
776         ok = imap_status(session, IMAP_FOLDER(folder), item->path,
777                          &messages, &recent, &unseen, &uid_validity);
778         statusbar_pop_all();
779         if (ok != IMAP_SUCCESS) return;
780
781         item->new = recent;
782         item->unread = unseen;
783         item->total = messages;
784         /* item->mtime = uid_validity; */
785 }
786
787 void imap_scan_tree(Folder *folder)
788 {
789         IMAPFolder *imapfolder = IMAP_FOLDER(folder);
790         FolderItem *item, *inbox;
791         IMAPSession *session;
792         IMAPNameSpace *namespace = NULL;
793         gchar *root_folder = NULL;
794
795         g_return_if_fail(folder != NULL);
796         g_return_if_fail(folder->account != NULL);
797
798         session = imap_session_get(folder);
799         if (!session) return;
800
801         if (imapfolder->namespace && imapfolder->namespace->data)
802                 namespace = (IMAPNameSpace *)imapfolder->namespace->data;
803
804         if (folder->account->imap_dir && *folder->account->imap_dir) {
805                 gchar *imap_dir;
806                 Xstrdup_a(imap_dir, folder->account->imap_dir, return);
807                 strtailchomp(imap_dir, '/');
808                 root_folder = g_strconcat
809                         (namespace && namespace->name ? namespace->name : "",
810                          imap_dir, NULL);
811                 if (namespace && namespace->separator)
812                         subst_char(root_folder, namespace->separator, '/');
813         }
814
815         if (root_folder)
816                 debug_print("IMAP root directory: %s\n", root_folder);
817
818         folder_tree_destroy(folder);
819         item = folder_item_new(folder->name, root_folder);
820         item->folder = folder;
821         folder->node = g_node_new(item);
822         g_free(root_folder);
823
824         imap_scan_tree_recursive(session, item, namespace);
825
826         if (!folder->inbox) {
827                 inbox = folder_item_new("INBOX", "INBOX");
828                 inbox->stype = F_INBOX;
829                 folder_item_append(item, inbox);
830                 folder->inbox = inbox;
831         }
832         if (!folder->trash)
833                 imap_create_trash(folder);
834 }
835
836 static void imap_scan_tree_recursive(IMAPSession *session,
837                                      FolderItem *item,
838                                      IMAPNameSpace *namespace)
839 {
840         IMAPFolder *imapfolder;
841         FolderItem *new_item;
842         GSList *item_list, *cur;
843         gchar *real_path, *wildcard_path, *wildcard_path_;
844
845         g_return_if_fail(item != NULL);
846         g_return_if_fail(item->folder != NULL);
847         g_return_if_fail(item->no_sub == FALSE);
848
849         imapfolder = IMAP_FOLDER(item->folder);
850
851         if (item->folder->ui_func)
852                 item->folder->ui_func(item->folder, item,
853                                       item->folder->ui_func_data);
854
855         if (item->path) {
856                 real_path = imap_get_real_path(imapfolder, item->path);
857                 Xstrconcat_a(wildcard_path, real_path,"/%", return IMAP_ERROR);
858                 QUOTE_IF_REQUIRED(wildcard_path_, wildcard_path);
859                 imap_cmd_gen_send(SESSION(session)->sock, "LIST \"\" %s",
860                                   wildcard_path_,
861                                   namespace && namespace->separator
862                                   ? namespace->separator : '/');
863                 g_free(wildcard_path);
864         } else {
865                 real_path = g_strdup(namespace && namespace->name
866                                      ? namespace->name : "");
867                 Xstrconcat_a(wildcard_path, real_path, "%", return IMAP_ERROR);
868                 QUOTE_IF_REQUIRED(wildcard_path_, wildcard_path);
869                 imap_cmd_gen_send(SESSION(session)->sock, "LIST \"\" %s",
870                                   wildcard_path_);
871                 g_free(wildcard_path);
872         }
873
874         strtailchomp(real_path, namespace && namespace->separator
875                      ? namespace->separator : '/');
876
877         item_list = imap_parse_list(session, real_path);
878         for (cur = item_list; cur != NULL; cur = cur->next) {
879                 new_item = cur->data;
880                 if (!strcmp(new_item->path, "INBOX")) {
881                         if (!item->folder->inbox) {
882                                 new_item->stype = F_INBOX;
883                                 item->folder->inbox = new_item;
884                         } else {
885                                 folder_item_destroy(new_item);
886                                 continue;
887                         }
888                 } else if (!item->parent && !item->folder->trash) {
889                         if (!strcasecmp(g_basename(new_item->path), "Trash")) {
890                                 new_item->stype = F_TRASH;
891                                 item->folder->trash = new_item;
892                         }
893                 }
894                 folder_item_append(item, new_item);
895                 if (new_item->no_select == FALSE)
896                         imap_scan_folder(new_item->folder, new_item);
897                 if (new_item->no_sub == FALSE)
898                         imap_scan_tree_recursive(session, new_item, namespace);
899         }
900 }
901
902 static GSList *imap_parse_list(IMAPSession *session, const gchar *path)
903 {
904         gchar buf[IMAPBUFSIZE];
905         gchar flags[256];
906         gchar separator[16];
907         gchar *p;
908         gchar *name;
909         GSList *item_list = NULL;
910         GString *str;
911         FolderItem *new_item;
912
913         debug_print("getting list of %s ...\n", *path ? path : "\"\"");
914
915         str = g_string_new(NULL);
916
917         for (;;) {
918                 if (sock_gets(SESSION(session)->sock, buf, sizeof(buf)) <= 0) {
919                         log_warning(_("error occured while getting LIST.\n"));
920                         break;
921                 }
922                 strretchomp(buf);
923                 if (buf[0] != '*' || buf[1] != ' ') {
924                         log_print("IMAP4< %s\n", buf);
925                         break;
926                 }
927                 debug_print("IMAP4< %s\n", buf);
928
929                 g_string_assign(str, buf);
930                 p = str->str + 2;
931                 if (strncmp(p, "LIST ", 5) != 0) continue;
932                 p += 5;
933
934                 if (*p != '(') continue;
935                 p++;
936                 p = strchr_cpy(p, ')', flags, sizeof(flags));
937                 if (!p) continue;
938                 while (*p == ' ') p++;
939
940                 p = strchr_cpy(p, ' ', separator, sizeof(separator));
941                 if (!p) continue;
942                 extract_quote(separator, '"');
943                 if (!strcmp(separator, "NIL"))
944                         separator[0] = '\0';
945
946                 buf[0] = '\0';
947                 while (*p == ' ') p++;
948                 if (*p == '{' || *p == '"')
949                         p = imap_parse_atom(SESSION(session)->sock, p,
950                                             buf, sizeof(buf), str);
951                 else
952                         strncpy2(buf, p, sizeof(buf));
953                 strtailchomp(buf, separator[0]);
954                 if (buf[0] == '\0') continue;
955                 if (!strcmp(buf, path)) continue;
956
957                 if (separator[0] != '\0')
958                         subst_char(buf, separator[0], '/');
959                 name = g_basename(buf);
960                 if (name[0] == '.') continue;
961
962                 new_item = folder_item_new(name, buf);
963                 if (strcasestr(flags, "\\Noinferiors") != NULL)
964                         new_item->no_sub = TRUE;
965                 if (strcasestr(flags, "\\Noselect") != NULL)
966                         new_item->no_select = TRUE;
967
968                 item_list = g_slist_append(item_list, new_item);
969
970                 debug_print("folder %s has been added.\n", buf);
971         }
972
973         g_string_free(str, TRUE);
974         statusbar_pop_all();
975
976         return item_list;
977 }
978
979 gint imap_create_tree(Folder *folder)
980 {
981         FolderItem *item;
982
983         g_return_val_if_fail(folder != NULL, -1);
984         g_return_val_if_fail(folder->node != NULL, -1);
985         g_return_val_if_fail(folder->node->data != NULL, -1);
986         g_return_val_if_fail(folder->account != NULL, -1);
987
988         imap_scan_tree(folder);
989
990         item = FOLDER_ITEM(folder->node->data);
991
992         if (!folder->inbox) {
993                 FolderItem *inbox;
994
995                 inbox = folder_item_new("INBOX", "INBOX");
996                 inbox->stype = F_INBOX;
997                 folder_item_append(item, inbox);
998                 folder->inbox = inbox;
999         }
1000         if (!folder->trash)
1001                 imap_create_trash(folder);
1002
1003         return 0;
1004 }
1005
1006 static gint imap_create_trash(Folder *folder)
1007 {
1008         IMAPFolder *imapfolder = IMAP_FOLDER(folder);
1009         FolderItem *item;
1010         FolderItem *new_item;
1011         gchar *trash_path;
1012         gchar *imap_dir = "";
1013
1014         g_return_val_if_fail(folder != NULL, -1);
1015         g_return_val_if_fail(folder->node != NULL, -1);
1016         g_return_val_if_fail(folder->node->data != NULL, -1);
1017         g_return_val_if_fail(folder->account != NULL, -1);
1018
1019         if (folder->account->imap_dir && *folder->account->imap_dir) {
1020                 gchar *tmpdir;
1021
1022                 Xstrdup_a(tmpdir, folder->account->imap_dir, return -1);
1023                 strtailchomp(tmpdir, '/');
1024                 Xalloca(imap_dir, strlen(tmpdir) + 2, return -1);
1025                 g_snprintf(imap_dir, strlen(tmpdir) + 2, "%s%c", tmpdir, '/');
1026         }
1027
1028         if (imapfolder->namespace && imapfolder->namespace->data) {
1029                 IMAPNameSpace *namespace =
1030                         (IMAPNameSpace *)imapfolder->namespace->data;
1031
1032                 if (*namespace->name != '\0') {
1033                         gchar *name;
1034
1035                         Xstrdup_a(name, namespace->name, return -1);
1036                         subst_char(name, namespace->separator, '/');
1037                         trash_path = g_strconcat(name, imap_dir, "Trash", NULL);
1038                 } else
1039                         trash_path = g_strconcat(imap_dir, "Trash", NULL);
1040         } else
1041                 trash_path = g_strconcat(imap_dir, "Trash", NULL);
1042
1043         item = FOLDER_ITEM(folder->node->data);
1044         new_item = imap_create_folder(folder, item, trash_path);
1045
1046         if (!new_item) {
1047                 gchar *path;
1048
1049                 new_item = folder_item_new("Trash", trash_path);
1050                 folder_item_append(item, new_item);
1051
1052                 path = folder_item_get_path(new_item);
1053                 if (!is_dir_exist(path))
1054                         make_dir_hier(path);
1055                 g_free(path);
1056         } else {
1057                 g_free(new_item->name);
1058                 new_item->name = g_strdup("Trash");
1059         }
1060         new_item->stype = F_TRASH;
1061         folder->trash = new_item;
1062
1063         g_free(trash_path);
1064
1065         return 0;
1066 }
1067
1068 FolderItem *imap_create_folder(Folder *folder, FolderItem *parent,
1069                                const gchar *name)
1070 {
1071         gchar *dirpath, *imappath;
1072         IMAPSession *session;
1073         IMAPNameSpace *namespace;
1074         FolderItem *new_item;
1075         gchar *new_name;
1076         const gchar *p;
1077         gint ok;
1078
1079         g_return_val_if_fail(folder != NULL, NULL);
1080         g_return_val_if_fail(folder->account != NULL, NULL);
1081         g_return_val_if_fail(parent != NULL, NULL);
1082         g_return_val_if_fail(name != NULL, NULL);
1083
1084         session = imap_session_get(folder);
1085         if (!session) return NULL;
1086
1087         if (parent->path)
1088                 dirpath = g_strconcat(parent->path, "/", name, NULL);
1089         else if ((p = strchr(name, '/')) != NULL && *(p + 1) != '\0')
1090                 dirpath = g_strdup(name);
1091         else if (folder->account->imap_dir && *folder->account->imap_dir) {
1092                 gchar *imap_dir;
1093
1094                 Xstrdup_a(imap_dir, folder->account->imap_dir, return NULL);
1095                 strtailchomp(imap_dir, '/');
1096                 dirpath = g_strconcat(imap_dir, "/", name, NULL);
1097         } else
1098                 dirpath = g_strdup(name);
1099
1100         Xstrdup_a(imappath, dirpath, {g_free(dirpath); return NULL;});
1101         Xstrdup_a(new_name, name, {g_free(dirpath); return NULL;});
1102         namespace = imap_find_namespace(IMAP_FOLDER(folder), imappath);
1103         if (namespace && namespace->separator) {
1104                 imap_path_separator_subst(imappath, namespace->separator);
1105                 imap_path_separator_subst(new_name, namespace->separator);
1106                 strtailchomp(new_name, namespace->separator);
1107         }
1108         strtailchomp(dirpath, '/');
1109
1110         if (strcmp(name, "INBOX") != 0) {
1111                 GPtrArray *argbuf;
1112                 gint i;
1113                 gboolean exist = FALSE;
1114
1115                 argbuf = g_ptr_array_new();
1116                 ok = imap_cmd_list(SESSION(session)->sock, NULL, imappath,
1117                                    argbuf);
1118                 statusbar_pop_all();
1119                 if (ok != IMAP_SUCCESS) {
1120                         log_warning(_("can't create mailbox: LIST failed\n"));
1121                         g_free(dirpath);
1122                         g_ptr_array_free(argbuf, TRUE);
1123                         return NULL;
1124                 }
1125
1126                 for (i = 0; i < argbuf->len; i++) {
1127                         gchar *str;
1128                         str = g_ptr_array_index(argbuf, i);
1129                         if (!strncmp(str, "LIST ", 5)) {
1130                                 exist = TRUE;
1131                                 break;
1132                         }
1133                 }
1134                 g_ptr_array_free(argbuf, TRUE);
1135
1136                 if (!exist) {
1137                         ok = imap_cmd_create(SESSION(session)->sock, imappath);
1138                         statusbar_pop_all();
1139                         if (ok != IMAP_SUCCESS) {
1140                                 log_warning(_("can't create mailbox\n"));
1141                                 g_free(dirpath);
1142                                 return NULL;
1143                         }
1144                 }
1145         }
1146
1147         new_item = folder_item_new(new_name, dirpath);
1148         folder_item_append(parent, new_item);
1149         g_free(dirpath);
1150
1151         dirpath = folder_item_get_path(new_item);
1152         if (!is_dir_exist(dirpath))
1153                 make_dir_hier(dirpath);
1154         g_free(dirpath);
1155
1156         return new_item;
1157 }
1158
1159 gint imap_remove_folder(Folder *folder, FolderItem *item)
1160 {
1161         gint ok;
1162         IMAPSession *session;
1163         gchar *path;
1164         gint exists, recent, unseen;
1165         guint32 uid_validity;
1166
1167         g_return_val_if_fail(folder != NULL, -1);
1168         g_return_val_if_fail(item != NULL, -1);
1169         g_return_val_if_fail(item->path != NULL, -1);
1170
1171         session = imap_session_get(folder);
1172         if (!session) return -1;
1173
1174         path = imap_get_real_path(IMAP_FOLDER(folder), item->path);
1175
1176         ok = imap_cmd_examine(SESSION(session)->sock, "INBOX",
1177                               &exists, &recent, &unseen, &uid_validity);
1178         statusbar_pop_all();
1179         if (ok != IMAP_SUCCESS) {
1180                 g_free(path);
1181                 return -1;
1182         }
1183
1184         ok = imap_cmd_delete(SESSION(session)->sock, path);
1185         statusbar_pop_all();
1186         if (ok != IMAP_SUCCESS) {
1187                 log_warning(_("can't delete mailbox\n"));
1188                 g_free(path);
1189                 return -1;
1190         }
1191
1192         g_free(path);
1193         folder_item_remove(item);
1194
1195         return 0;
1196 }
1197
1198 static GSList *imap_get_uncached_messages(IMAPSession *session,
1199                                           FolderItem *item,
1200                                           guint32 first_uid, guint32 last_uid)
1201 {
1202         gchar *tmp;
1203         GSList *newlist = NULL;
1204         GSList *llast = NULL;
1205         GString *str;
1206         MsgInfo *msginfo;
1207
1208         g_return_val_if_fail(session != NULL, NULL);
1209         g_return_val_if_fail(item != NULL, NULL);
1210         g_return_val_if_fail(item->folder != NULL, NULL);
1211         g_return_val_if_fail(item->folder->type == F_IMAP, NULL);
1212         g_return_val_if_fail(first_uid <= last_uid, NULL);
1213
1214         if (imap_cmd_envelope(SESSION(session)->sock, first_uid, last_uid)
1215             != IMAP_SUCCESS) {
1216                 log_warning(_("can't get envelope\n"));
1217                 return NULL;
1218         }
1219
1220         str = g_string_new(NULL);
1221
1222         for (;;) {
1223                 if ((tmp = sock_getline(SESSION(session)->sock)) == NULL) {
1224                         log_warning(_("error occurred while getting envelope.\n"));
1225                         g_string_free(str, TRUE);
1226                         return newlist;
1227                 }
1228                 strretchomp(tmp);
1229                 log_print("IMAP4< %s\n", tmp);
1230                 if (tmp[0] != '*' || tmp[1] != ' ') {
1231                         g_free(tmp);
1232                         break;
1233                 }
1234                 g_string_assign(str, tmp);
1235                 g_free(tmp);
1236
1237                 msginfo = imap_parse_envelope(SESSION(session)->sock, str);
1238                 if (!msginfo) {
1239                         log_warning(_("can't parse envelope: %s\n"), str->str);
1240                         continue;
1241                 }
1242
1243                 msginfo->folder = item;
1244
1245                 if (!newlist)
1246                         llast = newlist = g_slist_append(newlist, msginfo);
1247                 else {
1248                         llast = g_slist_append(llast, msginfo);
1249                         llast = llast->next;
1250                 }
1251         }
1252
1253         g_string_free(str, TRUE);
1254
1255         return newlist;
1256 }
1257
1258 static GSList *imap_delete_cached_messages(GSList *mlist, FolderItem *item,
1259                                            guint32 first_uid, guint32 last_uid)
1260 {
1261         GSList *cur, *next;
1262         MsgInfo *msginfo;
1263         gchar *dir;
1264
1265         g_return_val_if_fail(item != NULL, mlist);
1266         g_return_val_if_fail(item->folder != NULL, mlist);
1267         g_return_val_if_fail(item->folder->type == F_IMAP, mlist);
1268
1269         debug_print(_("Deleting cached messages %d - %d ... "),
1270                     first_uid, last_uid);
1271
1272         dir = folder_item_get_path(item);
1273         remove_numbered_files(dir, first_uid, last_uid);
1274         g_free(dir);
1275
1276         for (cur = mlist; cur != NULL; ) {
1277                 next = cur->next;
1278
1279                 msginfo = (MsgInfo *)cur->data;
1280                 if (msginfo != NULL && first_uid <= msginfo->msgnum &&
1281                     msginfo->msgnum <= last_uid) {
1282                         procmsg_msginfo_free(msginfo);
1283                         mlist = g_slist_remove(mlist, msginfo);
1284                 }
1285
1286                 cur = next;
1287         }
1288
1289         debug_print(_("done.\n"));
1290
1291         return mlist;
1292 }
1293
1294 static void imap_delete_all_cached_messages(FolderItem *item)
1295 {
1296         gchar *dir;
1297
1298         g_return_if_fail(item != NULL);
1299         g_return_if_fail(item->folder != NULL);
1300         g_return_if_fail(item->folder->type == F_IMAP);
1301
1302         debug_print(_("Deleting all cached messages... "));
1303
1304         dir = folder_item_get_path(item);
1305         remove_all_numbered_files(dir);
1306         g_free(dir);
1307
1308         debug_print(_("done.\n"));
1309 }
1310
1311 #if USE_SSL
1312 static SockInfo *imap_open(const gchar *server, gushort port, gchar *buf,
1313                            gboolean use_ssl)
1314 #else
1315 static SockInfo *imap_open(const gchar *server, gushort port, gchar *buf)
1316 #endif
1317 {
1318         SockInfo *sock;
1319
1320         if ((sock = sock_connect(server, port)) == NULL) {
1321                 log_warning(_("Can't connect to IMAP4 server: %s:%d\n"),
1322                             server, port);
1323                 return NULL;
1324         }
1325
1326 #if USE_SSL
1327         if (use_ssl && !ssl_init_socket(sock)) {
1328                 sock_close(sock);
1329                 return NULL;
1330         }
1331 #endif
1332
1333         imap_cmd_count = 0;
1334
1335         if (imap_cmd_noop(sock) != IMAP_SUCCESS) {
1336                 sock_close(sock);
1337                 return NULL;
1338         }
1339
1340         return sock;
1341 }
1342
1343 #define THROW goto catch
1344
1345 static void imap_parse_namespace(IMAPSession *session, IMAPFolder *folder)
1346 {
1347         gchar *ns_str;
1348         gchar *name;
1349         gchar *separator;
1350         gchar *p;
1351         IMAPNameSpace *namespace;
1352         GList *ns_list = NULL;
1353
1354         g_return_if_fail(session != NULL);
1355         g_return_if_fail(folder != NULL);
1356
1357         if (folder->namespace != NULL) return;
1358
1359         if (imap_cmd_namespace(SESSION(session)->sock, &ns_str)
1360             != IMAP_SUCCESS) {
1361                 log_warning(_("can't get namespace\n"));
1362                 return;
1363         }
1364
1365         /* get the first element */
1366         extract_one_parenthesis_with_skip_quote(ns_str, '"', '(', ')');
1367         g_strstrip(ns_str);
1368         p = ns_str;
1369
1370         while (*p != '\0') {
1371                 /* parse ("#foo" "/") */
1372
1373                 while (*p && *p != '(') p++;
1374                 if (*p == '\0') THROW;
1375                 p++;
1376
1377                 while (*p && *p != '"') p++;
1378                 if (*p == '\0') THROW;
1379                 p++;
1380                 name = p;
1381
1382                 while (*p && *p != '"') p++;
1383                 if (*p == '\0') THROW;
1384                 *p = '\0';
1385                 p++;
1386
1387                 while (*p && isspace(*p)) p++;
1388                 if (*p == '\0') THROW;
1389                 if (strncmp(p, "NIL", 3) == 0)
1390                         separator = NULL;
1391                 else if (*p == '"') {
1392                         p++;
1393                         separator = p;
1394                         while (*p && *p != '"') p++;
1395                         if (*p == '\0') THROW;
1396                         *p = '\0';
1397                         p++;
1398                 } else THROW;
1399
1400                 while (*p && *p != ')') p++;
1401                 if (*p == '\0') THROW;
1402                 p++;
1403
1404                 namespace = g_new(IMAPNameSpace, 1);
1405                 namespace->name = g_strdup(name);
1406                 namespace->separator = separator ? separator[0] : '\0';
1407                 ns_list = g_list_append(ns_list, namespace);
1408                 IMAP_FOLDER(folder)->namespace = ns_list;
1409         }
1410
1411 catch:
1412         g_free(ns_str);
1413         return;
1414 }
1415
1416 #undef THROW
1417
1418 static IMAPNameSpace *imap_find_namespace(IMAPFolder *folder,
1419                                           const gchar *path)
1420 {
1421         IMAPNameSpace *namespace = NULL;
1422         GList *ns_list;
1423         gchar *name;
1424
1425         g_return_val_if_fail(folder != NULL, NULL);
1426         g_return_val_if_fail(path != NULL, NULL);
1427
1428         ns_list = folder->namespace;
1429
1430         for (; ns_list != NULL; ns_list = ns_list->next) {
1431                 IMAPNameSpace *tmp_ns = ns_list->data;
1432
1433                 Xstrdup_a(name, tmp_ns->name, return namespace);
1434                 if (tmp_ns->separator && tmp_ns->separator != '/')
1435                         subst_char(name, tmp_ns->separator, '/');
1436                 if (strncmp(path, name, strlen(name)) == 0)
1437                         namespace = tmp_ns;
1438         }
1439
1440         return namespace;
1441 }
1442
1443 static gchar *imap_get_real_path(IMAPFolder *folder, const gchar *path)
1444 {
1445         gchar *real_path;
1446         IMAPNameSpace *namespace;
1447
1448         g_return_val_if_fail(folder != NULL, NULL);
1449         g_return_val_if_fail(path != NULL, NULL);
1450
1451         real_path = g_strdup(path);
1452         namespace = imap_find_namespace(folder, path);
1453         if (namespace && namespace->separator)
1454                 imap_path_separator_subst(real_path, namespace->separator);
1455
1456         return real_path;
1457 }
1458
1459 static gchar *imap_parse_atom(SockInfo *sock, gchar *src,
1460                               gchar *dest, gint dest_len, GString *str)
1461 {
1462         gchar *cur_pos = src;
1463
1464         g_return_val_if_fail(str != NULL, cur_pos);
1465
1466         while (*cur_pos == ' ') cur_pos++;
1467
1468         if (!strncmp(cur_pos, "NIL", 3)) {
1469                 *dest = '\0';
1470                 cur_pos += 3;
1471         } else if (*cur_pos == '\"') {
1472                 gchar *p;
1473
1474                 p = get_quoted(cur_pos, '\"', dest, dest_len);
1475                 cur_pos = p ? p : cur_pos + 2;
1476         } else if (*cur_pos == '{') {
1477                 gchar buf[32];
1478                 gint len;
1479                 gchar *nextline;
1480
1481                 cur_pos = strchr_cpy(cur_pos + 1, '}', buf, sizeof(buf));
1482                 len = atoi(buf);
1483
1484                 if ((nextline = sock_getline(sock)) == NULL)
1485                         return cur_pos;
1486                 strretchomp(nextline);
1487                 log_print("IMAP4< %s\n", nextline);
1488                 g_string_assign(str, nextline);
1489
1490                 len = MIN(len, strlen(nextline));
1491                 memcpy(dest, nextline, MIN(len, dest_len - 1));
1492                 dest[MIN(len, dest_len - 1)] = '\0';
1493                 cur_pos = str->str + len;
1494         }
1495
1496         return cur_pos;
1497 }
1498
1499 static gchar *imap_parse_one_address(SockInfo *sock, gchar *start,
1500                                      gchar *out_from_str,
1501                                      gchar *out_fromname_str,
1502                                      GString *str)
1503 {
1504         gchar buf[IMAPBUFSIZE];
1505         gchar *userid;
1506         gchar *domain;
1507         gchar *cur_pos = start;
1508
1509         cur_pos = imap_parse_atom(sock, cur_pos, buf, sizeof(buf), str);
1510         conv_unmime_header(out_fromname_str, IMAPBUFSIZE, buf, NULL);
1511
1512         cur_pos = imap_parse_atom(sock, cur_pos, buf, sizeof(buf), str);
1513
1514         cur_pos = imap_parse_atom(sock, cur_pos, buf, sizeof(buf), str);
1515         Xstrdup_a(userid, buf, return cur_pos + 1);
1516
1517         cur_pos = imap_parse_atom(sock, cur_pos, buf, sizeof(buf), str);
1518         Xstrdup_a(domain, buf, return cur_pos + 1);
1519
1520         if (out_fromname_str[0] != '\0') {
1521                 g_snprintf(out_from_str, IMAPBUFSIZE, "\"%s\" <%s@%s>",
1522                            out_fromname_str, userid, domain);
1523         } else {
1524                 g_snprintf(out_from_str, IMAPBUFSIZE, "%s@%s",
1525                            userid, domain);
1526                 strcpy(out_fromname_str, out_from_str);
1527         }
1528
1529         while (*cur_pos == ' ') cur_pos++;
1530         g_return_val_if_fail(*cur_pos == ')', NULL);
1531
1532         return cur_pos + 1;
1533 }
1534
1535 static gchar *imap_parse_address(SockInfo *sock, gchar *start,
1536                                  gchar **out_from_str,
1537                                  gchar **out_fromname_str,
1538                                  GString *str)
1539 {
1540         gchar buf[IMAPBUFSIZE];
1541         gchar name_buf[IMAPBUFSIZE];
1542         gchar *cur_pos = start;
1543         GString *addr_str;
1544
1545         if (out_from_str)     *out_from_str     = NULL;
1546         if (out_fromname_str) *out_fromname_str = NULL;
1547         buf[0] = name_buf[0] = '\0';
1548
1549         if (!strncmp(cur_pos, "NIL", 3)) {
1550                 if (out_from_str)     *out_from_str     = g_strdup("");
1551                 if (out_fromname_str) *out_fromname_str = g_strdup("");
1552                 return cur_pos + 3;
1553         }
1554
1555         g_return_val_if_fail(*cur_pos == '(', NULL);
1556         cur_pos++;
1557
1558         addr_str = g_string_new(NULL);
1559
1560         for (;;) {
1561                 gchar ch = *cur_pos++;
1562                 if (ch == ')') break;
1563                 if (ch == '(') {
1564                         cur_pos = imap_parse_one_address
1565                                 (sock, cur_pos, buf, name_buf, str);
1566                         if (!cur_pos) {
1567                                 g_string_free(addr_str, TRUE);
1568                                 return NULL;
1569                         }
1570                         if (addr_str->str[0] != '\0')
1571                                 g_string_append(addr_str, ", ");
1572                         g_string_append(addr_str, buf);
1573                 }
1574         }
1575
1576         if (out_from_str)     *out_from_str     = g_strdup(addr_str->str);
1577         if (out_fromname_str) *out_fromname_str = g_strdup(name_buf);
1578
1579         g_string_free(addr_str, TRUE);
1580
1581         return cur_pos;
1582 }
1583
1584 static MsgFlags imap_parse_flags(const gchar *flag_str)  
1585 {
1586         const gchar *p = flag_str;
1587         MsgFlags flags;
1588
1589         flags.perm_flags = MSG_UNREAD;
1590         flags.tmp_flags  = MSG_IMAP;
1591
1592         while ((p = strchr(p, '\\')) != NULL) {
1593                 p++;
1594
1595                 if (g_strncasecmp(p, "Recent", 6) == 0) {
1596                         MSG_SET_PERM_FLAGS(flags, MSG_NEW|MSG_UNREAD);
1597                 } else if (g_strncasecmp(p, "Seen", 4) == 0) {
1598                         MSG_UNSET_PERM_FLAGS(flags, MSG_NEW|MSG_UNREAD);
1599                 } else if (g_strncasecmp(p, "Deleted", 7) == 0) {
1600                         MSG_SET_PERM_FLAGS(flags, MSG_DELETED);
1601                 } else if (g_strncasecmp(p, "Flagged", 7) == 0) {
1602                         MSG_SET_PERM_FLAGS(flags, MSG_MARKED);
1603                 }
1604         }
1605
1606         return flags;
1607 }
1608
1609 static MsgInfo *imap_parse_envelope(SockInfo *sock, GString *line_str)
1610 {
1611         MsgInfo *msginfo;
1612         gchar buf[IMAPBUFSIZE];
1613         gchar tmp[IMAPBUFSIZE];
1614         gchar *cur_pos;
1615         gint msgnum;
1616         guint32 uid = 0;
1617         size_t size = 0;
1618         gchar *date = NULL;
1619         time_t date_t = 0;
1620         gchar *subject = NULL;
1621         gchar *tmp_from;
1622         gchar *tmp_fromname;
1623         gchar *from = NULL;
1624         gchar *fromname = NULL;
1625         gchar *tmp_to;
1626         gchar *to = NULL;
1627         gchar *inreplyto = NULL;
1628         gchar *msgid = NULL;
1629         MsgFlags flags = {0, 0};
1630
1631         g_return_val_if_fail(line_str != NULL, NULL);
1632         g_return_val_if_fail(line_str->str[0] == '*' &&
1633                              line_str->str[1] == ' ', NULL);
1634
1635         cur_pos = line_str->str + 2;
1636
1637 #define PARSE_ONE_ELEMENT(ch) \
1638 { \
1639         cur_pos = strchr_cpy(cur_pos, ch, buf, sizeof(buf)); \
1640         g_return_val_if_fail(cur_pos != NULL, NULL); \
1641 }
1642
1643         PARSE_ONE_ELEMENT(' ');
1644         msgnum = atoi(buf);
1645
1646         PARSE_ONE_ELEMENT(' ');
1647         g_return_val_if_fail(!strcmp(buf, "FETCH"), NULL);
1648
1649         g_return_val_if_fail(*cur_pos == '(', NULL);
1650         cur_pos++;
1651
1652         while (*cur_pos != '\0' && *cur_pos != ')') {
1653                 while (*cur_pos == ' ') cur_pos++;
1654
1655                 if (!strncmp(cur_pos, "UID ", 4)) {
1656                         cur_pos += 4;
1657                         uid = strtoul(cur_pos, &cur_pos, 10);
1658                 } else if (!strncmp(cur_pos, "FLAGS ", 6)) {
1659                         cur_pos += 6;
1660                         g_return_val_if_fail(*cur_pos == '(', NULL);
1661                         cur_pos++;
1662                         PARSE_ONE_ELEMENT(')');
1663                         flags = imap_parse_flags(buf);
1664                 } else if (!strncmp(cur_pos, "RFC822.SIZE ", 12)) {
1665                         cur_pos += 12;
1666                         size = strtol(cur_pos, &cur_pos, 10);
1667                 } else if (!strncmp(cur_pos, "ENVELOPE ", 9)) {
1668                         cur_pos += 9;
1669                         g_return_val_if_fail(*cur_pos == '(', NULL);
1670                         cur_pos = imap_parse_atom
1671                                 (sock, cur_pos + 1, buf, sizeof(buf), line_str);
1672                         Xstrdup_a(date, buf, return NULL);
1673                         date_t = procheader_date_parse(NULL, date, 0);
1674
1675                         cur_pos = imap_parse_atom
1676                                 (sock, cur_pos, buf, sizeof(buf), line_str);
1677                         if (buf[0] != '\0') {
1678                                 conv_unmime_header(tmp, sizeof(tmp), buf, NULL);
1679                                 Xstrdup_a(subject, tmp, return NULL);
1680                         }
1681
1682                         g_return_val_if_fail(*cur_pos == ' ', NULL);
1683                         cur_pos = imap_parse_address(sock, cur_pos + 1,
1684                                                      &tmp_from, &tmp_fromname,
1685                                                      line_str);
1686                         Xstrdup_a(from, tmp_from,
1687                                   {g_free(tmp_from); g_free(tmp_fromname);
1688                                    return NULL;});
1689                         Xstrdup_a(fromname, tmp_fromname,
1690                                   {g_free(tmp_from); g_free(tmp_fromname);
1691                                    return NULL;});
1692                         g_free(tmp_from);
1693                         g_free(tmp_fromname);
1694
1695 #define SKIP_ONE_ELEMENT() \
1696 { \
1697         g_return_val_if_fail(*cur_pos == ' ', NULL); \
1698         cur_pos = imap_parse_address(sock, cur_pos + 1, NULL, NULL, \
1699                                      line_str); \
1700 }
1701
1702                         /* skip sender and reply-to */
1703                         SKIP_ONE_ELEMENT();
1704                         SKIP_ONE_ELEMENT();
1705
1706                         g_return_val_if_fail(*cur_pos == ' ', NULL);
1707                         cur_pos = imap_parse_address(sock, cur_pos + 1,
1708                                                      &tmp_to, NULL, line_str);
1709                         Xstrdup_a(to, tmp_to, {g_free(tmp_to); return NULL;});
1710                         g_free(tmp_to);
1711
1712                         /* skip Cc and Bcc */
1713                         SKIP_ONE_ELEMENT();
1714                         SKIP_ONE_ELEMENT();
1715
1716 #undef SKIP_ONE_ELEMENT
1717
1718                         g_return_val_if_fail(*cur_pos == ' ', NULL);
1719                         cur_pos = imap_parse_atom
1720                                 (sock, cur_pos, buf, sizeof(buf), line_str);
1721                         if (buf[0] != '\0') {
1722                                 eliminate_parenthesis(buf, '(', ')');
1723                                 extract_parenthesis(buf, '<', '>');
1724                                 remove_space(buf);
1725                                 Xstrdup_a(inreplyto, buf, return NULL);
1726                         }
1727
1728                         g_return_val_if_fail(*cur_pos == ' ', NULL);
1729                         cur_pos = imap_parse_atom
1730                                 (sock, cur_pos, buf, sizeof(buf), line_str);
1731                         if (buf[0] != '\0') {
1732                                 extract_parenthesis(buf, '<', '>');
1733                                 remove_space(buf);
1734                                 Xstrdup_a(msgid, buf, return NULL);
1735                         }
1736
1737                         g_return_val_if_fail(*cur_pos == ')', NULL);
1738                         cur_pos++;
1739                 } else {
1740                         g_warning("invalid FETCH response: %s\n", cur_pos);
1741                         break;
1742                 }
1743         }
1744
1745         msginfo = g_new0(MsgInfo, 1);
1746         msginfo->msgnum = uid;
1747         msginfo->size = size;
1748         msginfo->date = g_strdup(date);
1749         msginfo->date_t = date_t;
1750         msginfo->subject = g_strdup(subject);
1751         msginfo->from = g_strdup(from);
1752         msginfo->fromname = g_strdup(fromname);
1753         msginfo->to = g_strdup(to);
1754         msginfo->inreplyto = g_strdup(inreplyto);
1755         msginfo->msgid = g_strdup(msgid);
1756         msginfo->flags = flags;
1757
1758         return msginfo;
1759 }
1760
1761 static gint imap_set_message_flags(IMAPSession *session,
1762                                    guint32 first_uid,
1763                                    guint32 last_uid,
1764                                    IMAPFlags flags,
1765                                    gboolean is_set)
1766 {
1767         GString *buf;
1768         gint ok;
1769
1770         buf = g_string_new(is_set ? "+FLAGS (" : "-FLAGS (");
1771
1772         if (IMAP_IS_SEEN(flags))        g_string_append(buf, "\\Seen ");
1773         if (IMAP_IS_ANSWERED(flags))    g_string_append(buf, "\\Answered ");
1774         if (IMAP_IS_FLAGGED(flags))     g_string_append(buf, "\\Flagged ");
1775         if (IMAP_IS_DELETED(flags))     g_string_append(buf, "\\Deleted ");
1776         if (IMAP_IS_DRAFT(flags))       g_string_append(buf, "\\Draft");
1777
1778         if (buf->str[buf->len - 1] == ' ')
1779                 g_string_truncate(buf, buf->len - 1);
1780
1781         g_string_append_c(buf, ')');
1782
1783         ok = imap_cmd_store(SESSION(session)->sock, first_uid, last_uid,
1784                             buf->str);
1785         g_string_free(buf, TRUE);
1786
1787         return ok;
1788 }
1789
1790 static gint imap_select(IMAPSession *session, IMAPFolder *folder,
1791                         const gchar *path,
1792                         gint *exists, gint *recent, gint *unseen,
1793                         guint32 *uid_validity)
1794 {
1795         gchar *real_path;
1796         gint ok;
1797
1798         real_path = imap_get_real_path(folder, path);
1799         ok = imap_cmd_select(SESSION(session)->sock, real_path,
1800                              exists, recent, unseen, uid_validity);
1801         if (ok != IMAP_SUCCESS)
1802                 log_warning(_("can't select folder: %s\n"), real_path);
1803         g_free(real_path);
1804
1805         return ok;
1806 }
1807
1808 #define THROW(err) { ok = err; goto catch; }
1809
1810 static gint imap_get_uid(IMAPSession *session, gint msgnum, guint32 *uid)
1811 {
1812         gint ok;
1813         GPtrArray *argbuf;
1814         gchar *str;
1815         gint num;
1816
1817         *uid = 0;
1818         argbuf = g_ptr_array_new();
1819
1820         imap_cmd_gen_send(SESSION(session)->sock, "FETCH %d (UID)", msgnum);
1821         if ((ok = imap_cmd_ok(SESSION(session)->sock, argbuf)) != IMAP_SUCCESS)
1822                 THROW(ok);
1823
1824         str = search_array_contain_str(argbuf, "FETCH");
1825         if (!str) THROW(IMAP_ERROR);
1826
1827         if (sscanf(str, "%d FETCH (UID %d)", &num, uid) != 2 ||
1828             num != msgnum) {
1829                 g_warning("imap_get_uid(): invalid FETCH line.\n");
1830                 THROW(IMAP_ERROR);
1831         }
1832
1833 catch:
1834         ptr_array_free_strings(argbuf);
1835         g_ptr_array_free(argbuf, TRUE);
1836
1837         return ok;
1838 }
1839
1840 static gint imap_status(IMAPSession *session, IMAPFolder *folder,
1841                         const gchar *path,
1842                         gint *messages, gint *recent, gint *unseen,
1843                         guint32 *uid_validity)
1844 {
1845         gchar *real_path;
1846         gchar *real_path_;
1847         gint ok;
1848         GPtrArray *argbuf;
1849         gchar *str;
1850
1851         *messages = *recent = *unseen = *uid_validity = 0;
1852
1853         argbuf = g_ptr_array_new();
1854
1855         real_path = imap_get_real_path(folder, path);
1856         QUOTE_IF_REQUIRED(real_path_, real_path);
1857         imap_cmd_gen_send(SESSION(session)->sock, "STATUS %s "
1858                           "(MESSAGES RECENT UNSEEN UIDVALIDITY)", real_path_);
1859
1860         ok = imap_cmd_ok(SESSION(session)->sock, argbuf);
1861         if (ok != IMAP_SUCCESS) THROW(ok);
1862
1863         str = search_array_contain_str(argbuf, "STATUS");
1864         if (!str) THROW(IMAP_ERROR);
1865
1866         str = strchr(str, '(');
1867         if (!str) THROW(IMAP_ERROR);
1868         str++;
1869         while (*str != '\0' && *str != ')') {
1870                 while (*str == ' ') str++;
1871
1872                 if (!strncmp(str, "MESSAGES ", 9)) {
1873                         str += 9;
1874                         *messages = strtol(str, &str, 10);
1875                 } else if (!strncmp(str, "RECENT ", 7)) {
1876                         str += 7;
1877                         *recent = strtol(str, &str, 10);
1878                 } else if (!strncmp(str, "UNSEEN ", 7)) {
1879                         str += 7;
1880                         *unseen = strtol(str, &str, 10);
1881                 } else if (!strncmp(str, "UIDVALIDITY ", 12)) {
1882                         str += 12;
1883                         *uid_validity = strtoul(str, &str, 10);
1884                 } else {
1885                         g_warning("invalid STATUS response: %s\n", str);
1886                         break;
1887                 }
1888         }
1889
1890 catch:
1891         g_free(real_path);
1892         ptr_array_free_strings(argbuf);
1893         g_ptr_array_free(argbuf, TRUE);
1894
1895         return ok;
1896 }
1897
1898 #undef THROW
1899
1900
1901 /* low-level IMAP4rev1 commands */
1902
1903 static gint imap_cmd_login(SockInfo *sock,
1904                            const gchar *user, const gchar *pass)
1905 {
1906         gchar *user_, *pass_;
1907         gint ok;
1908
1909         QUOTE_IF_REQUIRED(user_, user);
1910         QUOTE_IF_REQUIRED(pass_, pass);
1911         imap_cmd_gen_send(sock, "LOGIN %s %s", user_, pass_);
1912
1913         ok = imap_cmd_ok(sock, NULL);
1914         if (ok != IMAP_SUCCESS)
1915                 log_warning(_("IMAP4 login failed.\n"));
1916
1917         return ok;
1918 }
1919
1920 static gint imap_cmd_logout(SockInfo *sock)
1921 {
1922         imap_cmd_gen_send(sock, "LOGOUT");
1923         return imap_cmd_ok(sock, NULL);
1924 }
1925
1926 static gint imap_cmd_noop(SockInfo *sock)
1927 {
1928         imap_cmd_gen_send(sock, "NOOP");
1929         return imap_cmd_ok(sock, NULL);
1930 }
1931
1932 #define THROW(err) { ok = err; goto catch; }
1933
1934 static gint imap_cmd_namespace(SockInfo *sock, gchar **ns_str)
1935 {
1936         gint ok;
1937         GPtrArray *argbuf;
1938         gchar *str;
1939
1940         argbuf = g_ptr_array_new();
1941
1942         imap_cmd_gen_send(sock, "NAMESPACE");
1943         if ((ok = imap_cmd_ok(sock, argbuf)) != IMAP_SUCCESS) THROW(ok);
1944
1945         str = search_array_contain_str(argbuf, "NAMESPACE");
1946         if (!str) THROW(IMAP_ERROR);
1947
1948         *ns_str = g_strdup(str);
1949
1950 catch:
1951         ptr_array_free_strings(argbuf);
1952         g_ptr_array_free(argbuf, TRUE);
1953
1954         return ok;
1955 }
1956
1957 #undef THROW
1958
1959 static gint imap_cmd_list(SockInfo *sock, const gchar *ref,
1960                           const gchar *mailbox, GPtrArray *argbuf)
1961 {
1962         gchar *ref_, *mailbox_;
1963
1964         if (!ref) ref = "\"\"";
1965         if (!mailbox) mailbox = "\"\"";
1966
1967         QUOTE_IF_REQUIRED(ref_, ref);
1968         QUOTE_IF_REQUIRED(mailbox_, mailbox);
1969         imap_cmd_gen_send(sock, "LIST %s %s", ref_, mailbox_);
1970
1971         return imap_cmd_ok(sock, argbuf);
1972 }
1973
1974 #define THROW goto catch
1975
1976 static gint imap_cmd_do_select(SockInfo *sock, const gchar *folder,
1977                                gboolean examine,
1978                                gint *exists, gint *recent, gint *unseen,
1979                                guint32 *uid_validity)
1980 {
1981         gint ok;
1982         gchar *resp_str;
1983         GPtrArray *argbuf;
1984         gchar *select_cmd;
1985         gchar *folder_;
1986
1987         *exists = *recent = *unseen = *uid_validity = 0;
1988         argbuf = g_ptr_array_new();
1989
1990         if (examine)
1991                 select_cmd = "EXAMINE";
1992         else
1993                 select_cmd = "SELECT";
1994
1995         QUOTE_IF_REQUIRED(folder_, folder);
1996         imap_cmd_gen_send(sock, "%s %s", select_cmd, folder_);
1997
1998         if ((ok = imap_cmd_ok(sock, argbuf)) != IMAP_SUCCESS) THROW;
1999
2000         resp_str = search_array_contain_str(argbuf, "EXISTS");
2001         if (resp_str) {
2002                 if (sscanf(resp_str,"%d EXISTS", exists) != 1) {
2003                         g_warning("imap_cmd_select(): invalid EXISTS line.\n");
2004                         THROW;
2005                 }
2006         }
2007
2008         resp_str = search_array_contain_str(argbuf, "RECENT");
2009         if (resp_str) {
2010                 if (sscanf(resp_str, "%d RECENT", recent) != 1) {
2011                         g_warning("imap_cmd_select(): invalid RECENT line.\n");
2012                         THROW;
2013                 }
2014         }
2015
2016         resp_str = search_array_contain_str(argbuf, "UIDVALIDITY");
2017         if (resp_str) {
2018                 if (sscanf(resp_str, "OK [UIDVALIDITY %u] ", uid_validity)
2019                     != 1) {
2020                         g_warning("imap_cmd_select(): invalid UIDVALIDITY line.\n");
2021                         THROW;
2022                 }
2023         }
2024
2025         resp_str = search_array_contain_str(argbuf, "UNSEEN");
2026         if (resp_str) {
2027                 if (sscanf(resp_str, "OK [UNSEEN %d] ", unseen) != 1) {
2028                         g_warning("imap_cmd_select(): invalid UNSEEN line.\n");
2029                         THROW;
2030                 }
2031         }
2032
2033 catch:
2034         ptr_array_free_strings(argbuf);
2035         g_ptr_array_free(argbuf, TRUE);
2036
2037         return ok;
2038 }
2039
2040 static gint imap_cmd_select(SockInfo *sock, const gchar *folder,
2041                             gint *exists, gint *recent, gint *unseen,
2042                             guint32 *uid_validity)
2043 {
2044         return imap_cmd_do_select(sock, folder, FALSE,
2045                                   exists, recent, unseen, uid_validity);
2046 }
2047
2048 static gint imap_cmd_examine(SockInfo *sock, const gchar *folder,
2049                              gint *exists, gint *recent, gint *unseen,
2050                              guint32 *uid_validity)
2051 {
2052         return imap_cmd_do_select(sock, folder, TRUE,
2053                                   exists, recent, unseen, uid_validity);
2054 }
2055
2056 #undef THROW
2057
2058 static gint imap_cmd_create(SockInfo *sock, const gchar *folder)
2059 {
2060         gchar *folder_;
2061
2062         QUOTE_IF_REQUIRED(folder_, folder);
2063         imap_cmd_gen_send(sock, "CREATE %s", folder_);
2064
2065         return imap_cmd_ok(sock, NULL);
2066 }
2067
2068 static gint imap_cmd_delete(SockInfo *sock, const gchar *folder)
2069 {
2070         gchar *folder_;
2071
2072         QUOTE_IF_REQUIRED(folder_, folder);
2073         imap_cmd_gen_send(sock, "DELETE %s", folder_);
2074
2075         return imap_cmd_ok(sock, NULL);
2076 }
2077
2078 static gint imap_cmd_fetch(SockInfo *sock, guint32 uid, const gchar *filename)
2079 {
2080         gint ok;
2081         gchar buf[IMAPBUFSIZE];
2082         gchar *cur_pos;
2083         gchar size_str[32];
2084         glong size_num;
2085
2086         g_return_val_if_fail(filename != NULL, IMAP_ERROR);
2087
2088         imap_cmd_gen_send(sock, "UID FETCH %d BODY[]", uid);
2089
2090         if (sock_gets(sock, buf, sizeof(buf)) < 0)
2091                 return IMAP_ERROR;
2092         strretchomp(buf);
2093         if (buf[0] != '*' || buf[1] != ' ')
2094                 return IMAP_ERROR;
2095         log_print("IMAP4< %s\n", buf);
2096
2097         cur_pos = strchr(buf, '{');
2098         g_return_val_if_fail(cur_pos != NULL, IMAP_ERROR);
2099         cur_pos = strchr_cpy(cur_pos + 1, '}', size_str, sizeof(size_str));
2100         g_return_val_if_fail(cur_pos != NULL, IMAP_ERROR);
2101         size_num = atol(size_str);
2102
2103         if (*cur_pos != '\0') return IMAP_ERROR;
2104
2105         if (recv_bytes_write_to_file(sock, size_num, filename) != 0)
2106                 return IMAP_ERROR;
2107
2108         if (imap_cmd_gen_recv(sock, buf, sizeof(buf)) != IMAP_SUCCESS)
2109                 return IMAP_ERROR;
2110
2111         if (buf[0] == '\0' || buf[strlen(buf) - 1] != ')')
2112                 return IMAP_ERROR;
2113
2114         ok = imap_cmd_ok(sock, NULL);
2115
2116         return ok;
2117 }
2118
2119 static gint imap_cmd_append(SockInfo *sock, const gchar *destfolder,
2120                             const gchar *file)
2121 {
2122         gint ok;
2123         gint size;
2124         gchar *destfolder_;
2125
2126         g_return_val_if_fail(file != NULL, IMAP_ERROR);
2127
2128         size = get_file_size(file);
2129         QUOTE_IF_REQUIRED(destfolder_, destfolder);
2130         imap_cmd_gen_send(sock, "APPEND %s {%d}", destfolder_, size);
2131         ok = imap_cmd_ok(sock, NULL);
2132         if (ok != IMAP_SUCCESS) {
2133                 log_warning(_("can't append %s to %s\n"), file, destfolder_);
2134                 return -1;
2135         }
2136
2137         return ok;
2138 }
2139
2140 static gint imap_cmd_copy(SockInfo *sock, guint32 uid, const gchar *destfolder)
2141 {
2142         gint ok;
2143         gchar *destfolder_;
2144
2145         g_return_val_if_fail(destfolder != NULL, IMAP_ERROR);
2146
2147         QUOTE_IF_REQUIRED(destfolder_, destfolder);
2148         imap_cmd_gen_send(sock, "UID COPY %d %s", uid, destfolder_);
2149
2150         ok = imap_cmd_ok(sock, NULL);
2151         if (ok != IMAP_SUCCESS) {
2152                 log_warning(_("can't copy %d to %s\n"), uid, destfolder_);
2153                 return -1;
2154         }
2155
2156         return ok;
2157 }
2158
2159 gint imap_cmd_envelope(SockInfo *sock, guint32 first_uid, guint32 last_uid)
2160 {
2161         imap_cmd_gen_send
2162                 (sock, "UID FETCH %d:%d (UID FLAGS RFC822.SIZE ENVELOPE)",
2163                  first_uid, last_uid);
2164
2165         return IMAP_SUCCESS;
2166 }
2167
2168 static gint imap_cmd_store(SockInfo *sock, guint32 first_uid, guint32 last_uid,
2169                            gchar *sub_cmd)
2170 {
2171         gint ok;
2172
2173         imap_cmd_gen_send(sock, "UID STORE %d:%d %s",
2174                           first_uid, last_uid, sub_cmd);
2175
2176         if ((ok = imap_cmd_ok(sock, NULL)) != IMAP_SUCCESS) {
2177                 log_warning(_("error while imap command: STORE %d:%d %s\n"),
2178                             first_uid, last_uid, sub_cmd);
2179                 return ok;
2180         }
2181
2182         return IMAP_SUCCESS;
2183 }
2184
2185 static gint imap_cmd_expunge(SockInfo *sock)
2186 {
2187         gint ok;
2188
2189         imap_cmd_gen_send(sock, "EXPUNGE");
2190         if ((ok = imap_cmd_ok(sock, NULL)) != IMAP_SUCCESS) {
2191                 log_warning(_("error while imap command: EXPUNGE\n"));
2192                 return ok;
2193         }
2194
2195         return IMAP_SUCCESS;
2196 }
2197
2198 static gint imap_cmd_ok(SockInfo *sock, GPtrArray *argbuf)
2199 {
2200         gint ok;
2201         gchar buf[IMAPBUFSIZE];
2202         gint cmd_num;
2203         gchar cmd_status[IMAPBUFSIZE];
2204
2205         while ((ok = imap_cmd_gen_recv(sock, buf, sizeof(buf)))
2206                == IMAP_SUCCESS) {
2207                 if (buf[0] == '*' && buf[1] == ' ') {
2208                         if (argbuf)
2209                                 g_ptr_array_add(argbuf, g_strdup(buf + 2));
2210                         continue;
2211                 }
2212
2213                 if (sscanf(buf, "%d %s", &cmd_num, cmd_status) < 2)
2214                         return IMAP_ERROR;
2215                 else if (cmd_num == imap_cmd_count &&
2216                          !strcmp(cmd_status, "OK")) {
2217                         if (argbuf)
2218                                 g_ptr_array_add(argbuf, g_strdup(buf));
2219                         return IMAP_SUCCESS;
2220                 } else
2221                         return IMAP_ERROR;
2222         }
2223
2224         return ok;
2225 }
2226
2227 static void imap_cmd_gen_send(SockInfo *sock, const gchar *format, ...)
2228 {
2229         gchar buf[IMAPBUFSIZE];
2230         gchar tmp[IMAPBUFSIZE];
2231         gchar *p;
2232         va_list args;
2233
2234         va_start(args, format);
2235         g_vsnprintf(tmp, sizeof(tmp), format, args);
2236         va_end(args);
2237
2238         imap_cmd_count++;
2239
2240         g_snprintf(buf, sizeof(buf), "%d %s\r\n", imap_cmd_count, tmp);
2241         if (!strncasecmp(tmp, "LOGIN ", 6) && (p = strchr(tmp + 6, ' '))) {
2242                 *p = '\0';
2243                 log_print("IMAP4> %d %s ********\n", imap_cmd_count, tmp);
2244         } else
2245                 log_print("IMAP4> %d %s\n", imap_cmd_count, tmp);
2246
2247         sock_write(sock, buf, strlen(buf));
2248 }
2249
2250 static gint imap_cmd_gen_recv(SockInfo *sock, gchar *buf, gint size)
2251 {
2252         if (sock_gets(sock, buf, size) == -1)
2253                 return IMAP_SOCKET;
2254
2255         strretchomp(buf);
2256
2257         log_print("IMAP4< %s\n", buf);
2258
2259         return IMAP_SUCCESS;
2260 }
2261
2262
2263 /* misc utility functions */
2264
2265 static gchar *strchr_cpy(const gchar *src, gchar ch, gchar *dest, gint len)
2266 {
2267         gchar *tmp;
2268
2269         dest[0] = '\0';
2270         tmp = strchr(src, ch);
2271         if (!tmp)
2272                 return NULL;
2273
2274         memcpy(dest, src, MIN(tmp - src, len - 1));
2275         dest[MIN(tmp - src, len - 1)] = '\0';
2276
2277         return tmp + 1;
2278 }
2279
2280 static gchar *get_quoted(const gchar *src, gchar ch, gchar *dest, gint len)
2281 {
2282         const gchar *p = src;
2283         gint n = 0;
2284
2285         g_return_val_if_fail(*p == ch, NULL);
2286
2287         *dest = '\0';
2288         p++;
2289
2290         while (*p != '\0' && *p != ch) {
2291                 if (n < len - 1) {
2292                         if (*p == '\\' && *(p + 1) != '\0')
2293                                 p++;
2294                         *dest++ = *p++;
2295                 } else
2296                         p++;
2297                 n++;
2298         }
2299
2300         *dest = '\0';
2301         return (gchar *)(*p == ch ? p + 1 : p);
2302 }
2303
2304 static gchar *search_array_contain_str(GPtrArray *array, gchar *str)
2305 {
2306         gint i;
2307
2308         for (i = 0; i < array->len; i++) {
2309                 gchar *tmp;
2310
2311                 tmp = g_ptr_array_index(array, i);
2312                 if (strstr(tmp, str) != NULL)
2313                         return tmp;
2314         }
2315
2316         return NULL;
2317 }
2318
2319 static void imap_path_separator_subst(gchar *str, gchar separator)
2320 {
2321         if (separator && separator != '/')
2322                 subst_char(str, '/', separator);
2323 }