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