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