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