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