more sync with sylpheed 0.5.0pre3
[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 *root_folder = NULL;
690
691         g_return_if_fail(folder != NULL);
692         g_return_if_fail(folder->account != NULL);
693
694         session = imap_session_get(folder);
695         if (!session) return;
696
697         if (imapfolder->namespace && imapfolder->namespace->data)
698                 namespace = (IMAPNameSpace *)imapfolder->namespace->data;
699
700         if (folder->account->imap_dir && *folder->account->imap_dir) {
701                 gchar *imap_dir;
702                 Xstrdup_a(imap_dir, folder->account->imap_dir, return);
703                 strtailchomp(imap_dir, '/');
704                 root_folder = g_strconcat
705                         (namespace && namespace->name ? namespace->name : "",
706                          imap_dir, NULL);
707                 if (namespace && namespace->separator)
708                         subst_char(root_folder, namespace->separator, '/');
709         }
710
711         if (root_folder)
712                 debug_print("IMAP root directory: %s\n", root_folder);
713
714         folder_tree_destroy(folder);
715         item = folder_item_new(folder->name, root_folder);
716         item->folder = folder;
717         folder->node = g_node_new(item);
718         g_free(root_folder);
719
720         inbox = folder_item_new("INBOX", "INBOX");
721         inbox->stype = F_INBOX;
722         folder_item_append(item, inbox);
723         folder->inbox = inbox;
724
725         imap_scan_tree_recursive(session, item, namespace);
726 }
727
728 static void imap_scan_tree_recursive(IMAPSession *session,
729                                      FolderItem *item,
730                                      IMAPNameSpace *namespace)
731 {
732         IMAPFolder *imapfolder;
733         FolderItem *new_item;
734         GSList *item_list, *cur;
735         gchar *real_path;
736
737         g_return_if_fail(item != NULL);
738         g_return_if_fail(item->folder != NULL);
739         g_return_if_fail(item->no_sub == FALSE);
740
741         imapfolder = IMAP_FOLDER(item->folder);
742
743         if (item->folder->ui_func)
744                 item->folder->ui_func(item->folder, item,
745                                       item->folder->ui_func_data);
746
747         if (item->path) {
748                 real_path = imap_get_real_path(imapfolder, item->path);
749                 imap_cmd_gen_send(SESSION(session)->sock, "LIST \"\" %s%c%%",
750                                   real_path,
751                                   namespace && namespace->separator
752                                   ? namespace->separator : '/');
753         } else {
754                 real_path = g_strdup(namespace && namespace->name
755                                      ? namespace->name : "");
756                 imap_cmd_gen_send(SESSION(session)->sock, "LIST \"\" %s%%",
757                                   real_path);
758         }
759
760         strtailchomp(real_path, namespace && namespace->separator
761                      ? namespace->separator : '/');
762
763         item_list = imap_parse_list(session, real_path);
764         for (cur = item_list; cur != NULL; cur = cur->next) {
765                 new_item = cur->data;
766                 folder_item_append(item, new_item);
767                 if (new_item->no_sub == FALSE)
768                         imap_scan_tree_recursive(session, new_item, namespace);
769         }
770 }
771
772 static GSList *imap_parse_list(IMAPSession *session, const gchar *path)
773 {
774         gchar buf[IMAPBUFSIZE];
775         gchar flags[256];
776         gchar separator[16];
777         gchar *p;
778         gchar *name;
779         GSList *item_list = NULL;
780         GString *str;
781         FolderItem *new_item;
782
783         debug_print("getting list of %s ...\n", *path ? path : "\"\"");
784
785         str = g_string_new(NULL);
786
787         for (;;) {
788                 if (sock_gets(SESSION(session)->sock, buf, sizeof(buf)) <= 0) {
789                         log_warning(_("error occured while getting LIST.\n"));
790                         break;
791                 }
792                 strretchomp(buf);
793                 if (buf[0] != '*' || buf[1] != ' ') {
794                         log_print("IMAP4< %s\n", buf);
795                         break;
796                 }
797                 debug_print("IMAP4< %s\n", buf);
798
799                 g_string_assign(str, buf);
800                 p = str->str + 2;
801                 if (strncmp(p, "LIST ", 5) != 0) continue;
802                 p += 5;
803
804                 if (*p != '(') continue;
805                 p++;
806                 p = strchr_cpy(p, ')', flags, sizeof(flags));
807                 if (!p) continue;
808                 while (*p == ' ') p++;
809
810                 p = strchr_cpy(p, ' ', separator, sizeof(separator));
811                 if (!p) continue;
812                 extract_quote(separator, '"');
813                 if (!strcmp(separator, "NIL"))
814                         separator[0] = '\0';
815
816                 buf[0] = '\0';
817                 while (*p == ' ') p++;
818                 if (*p == '{' || *p == '"')
819                         p = imap_parse_atom(SESSION(session)->sock, p,
820                                             buf, sizeof(buf), str);
821                 else
822                         strncpy2(buf, p, sizeof(buf));
823                 strtailchomp(buf, separator[0]);
824                 if (buf[0] == '\0') continue;
825                 if (!strcmp(buf, path)) continue;
826                 if (!strcmp(buf, "INBOX")) continue;
827
828                 if (separator[0] != '\0')
829                         subst_char(buf, separator[0], '/');
830                 name = g_basename(buf);
831                 if (name[0] == '.') continue;
832
833                 new_item = folder_item_new(name, buf);
834                 if (strcasestr(flags, "\\Noinferiors") != NULL)
835                         new_item->no_sub = TRUE;
836                 if (strcasestr(flags, "\\Noselect") != NULL)
837                         new_item->no_select = TRUE;
838
839                 item_list = g_slist_append(item_list, new_item);
840
841                 debug_print("folder %s has been added.\n", buf);
842         }
843
844         g_string_free(str, TRUE);
845         statusbar_pop_all();
846
847         return item_list;
848 }
849
850 gint imap_create_tree(Folder *folder)
851 {
852         IMAPFolder *imapfolder = IMAP_FOLDER(folder);
853         FolderItem *item;
854         FolderItem *new_item;
855         gchar *trash_path;
856         gchar *imap_dir = "";
857
858         g_return_val_if_fail(folder != NULL, -1);
859         g_return_val_if_fail(folder->node != NULL, -1);
860         g_return_val_if_fail(folder->node->data != NULL, -1);
861         g_return_val_if_fail(folder->account != NULL, -1);
862
863         imap_session_get(folder);
864
865         item = FOLDER_ITEM(folder->node->data);
866
867         new_item = folder_item_new("INBOX", "INBOX");
868         new_item->stype = F_INBOX;
869         folder_item_append(item, new_item);
870         folder->inbox = new_item;
871
872         if (folder->account->imap_dir && *folder->account->imap_dir) {
873                 gchar *tmpdir;
874
875                 Xstrdup_a(tmpdir, folder->account->imap_dir, return -1);
876                 strtailchomp(tmpdir, '/');
877                 Xalloca(imap_dir, strlen(tmpdir) + 2, return -1);
878                 g_snprintf(imap_dir, strlen(tmpdir) + 2, "%s%c", tmpdir, '/');
879         }
880
881         if (imapfolder->namespace && imapfolder->namespace->data) {
882                 IMAPNameSpace *namespace =
883                         (IMAPNameSpace *)imapfolder->namespace->data;
884
885                 if (*namespace->name != '\0') {
886                         gchar *name;
887
888                         Xstrdup_a(name, namespace->name, return -1);
889                         subst_char(name, namespace->separator, '/');
890                         trash_path = g_strconcat(name, imap_dir, "trash", NULL);
891                 } else
892                         trash_path = g_strconcat(imap_dir, "trash", NULL);
893         } else
894                 trash_path = g_strconcat(imap_dir, "trash", NULL);
895
896         new_item = imap_create_folder(folder, item, trash_path);
897
898         if (!new_item) {
899                 gchar *path;
900
901                 new_item = folder_item_new("Trash", trash_path);
902                 folder_item_append(item, new_item);
903
904                 path = folder_item_get_path(new_item);
905                 if (!is_dir_exist(path))
906                         make_dir_hier(path);
907                 g_free(path);
908         } else {
909                 g_free(new_item->name);
910                 new_item->name = g_strdup("Trash");
911         }
912         new_item->stype = F_TRASH;
913         folder->trash = new_item;
914
915         g_free(trash_path);
916         return 0;
917 }
918
919 FolderItem *imap_create_folder(Folder *folder, FolderItem *parent,
920                                const gchar *name)
921 {
922         gchar *dirpath, *imappath;
923         IMAPSession *session;
924         IMAPNameSpace *namespace;
925         FolderItem *new_item;
926         gchar *new_name;
927         const gchar *p;
928         gint ok;
929
930         g_return_val_if_fail(folder != NULL, NULL);
931         g_return_val_if_fail(folder->account != NULL, NULL);
932         g_return_val_if_fail(parent != NULL, NULL);
933         g_return_val_if_fail(name != NULL, NULL);
934
935         session = imap_session_get(folder);
936         if (!session) return NULL;
937
938         if (parent->path)
939                 dirpath = g_strconcat(parent->path, "/", name, NULL);
940         else if ((p = strchr(name, '/')) != NULL && *(p + 1) != '\0')
941                 dirpath = g_strdup(name);
942         else if (folder->account->imap_dir && *folder->account->imap_dir) {
943                 gchar *imap_dir;
944
945                 Xstrdup_a(imap_dir, folder->account->imap_dir, return NULL);
946                 strtailchomp(imap_dir, '/');
947                 dirpath = g_strconcat(imap_dir, "/", name, NULL);
948         } else
949                 dirpath = g_strdup(name);
950
951         Xstrdup_a(imappath, dirpath, {g_free(dirpath); return NULL;});
952         Xstrdup_a(new_name, name, {g_free(dirpath); return NULL;});
953         namespace = imap_find_namespace(IMAP_FOLDER(folder), imappath);
954         if (namespace && namespace->separator) {
955                 imap_path_separator_subst(imappath, namespace->separator);
956                 imap_path_separator_subst(new_name, namespace->separator);
957                 strtailchomp(new_name, namespace->separator);
958         }
959         strtailchomp(dirpath, '/');
960
961         if (strcmp(name, "INBOX") != 0) {
962                 GPtrArray *argbuf;
963                 gint i;
964                 gboolean exist = FALSE;
965
966                 argbuf = g_ptr_array_new();
967                 ok = imap_cmd_list(SESSION(session)->sock, NULL, imappath,
968                                    argbuf);
969                 statusbar_pop_all();
970                 if (ok != IMAP_SUCCESS) {
971                         log_warning(_("can't create mailbox: LIST failed\n"));
972                         g_free(dirpath);
973                         g_ptr_array_free(argbuf, TRUE);
974                         return NULL;
975                 }
976
977                 for (i = 0; i < argbuf->len; i++) {
978                         gchar *str;
979                         str = g_ptr_array_index(argbuf, i);
980                         if (!strncmp(str, "LIST ", 5)) {
981                                 exist = TRUE;
982                                 break;
983                         }
984                 }
985                 g_ptr_array_free(argbuf, TRUE);
986
987                 if (!exist) {
988                         ok = imap_cmd_create(SESSION(session)->sock, imappath);
989                         statusbar_pop_all();
990                         if (ok != IMAP_SUCCESS) {
991                                 log_warning(_("can't create mailbox\n"));
992                                 g_free(dirpath);
993                                 return NULL;
994                         }
995                 }
996         }
997
998         new_item = folder_item_new(new_name, dirpath);
999         folder_item_append(parent, new_item);
1000         g_free(dirpath);
1001
1002         dirpath = folder_item_get_path(new_item);
1003         if (!is_dir_exist(dirpath))
1004                 make_dir_hier(dirpath);
1005         g_free(dirpath);
1006
1007         return new_item;
1008 }
1009
1010 gint imap_remove_folder(Folder *folder, FolderItem *item)
1011 {
1012         gint ok;
1013         IMAPSession *session;
1014         gchar *path;
1015
1016         g_return_val_if_fail(folder != NULL, -1);
1017         g_return_val_if_fail(item != NULL, -1);
1018         g_return_val_if_fail(item->path != NULL, -1);
1019
1020         session = imap_session_get(folder);
1021         if (!session) return -1;
1022
1023         path = imap_get_real_path(IMAP_FOLDER(folder), item->path);
1024         ok = imap_cmd_delete(SESSION(session)->sock, path);
1025         statusbar_pop_all();
1026         if (ok != IMAP_SUCCESS) {
1027                 log_warning(_("can't delete mailbox\n"));
1028                 g_free(path);
1029                 return -1;
1030         }
1031
1032         g_free(path);
1033         folder_item_remove(item);
1034
1035         return 0;
1036 }
1037
1038 static GSList *imap_get_uncached_messages(IMAPSession *session,
1039                                           FolderItem *item,
1040                                           guint32 first_uid, guint32 last_uid)
1041 {
1042         gchar *tmp;
1043         GSList *newlist = NULL;
1044         GSList *llast = NULL;
1045         GString *str;
1046         MsgInfo *msginfo;
1047
1048         g_return_val_if_fail(session != NULL, NULL);
1049         g_return_val_if_fail(item != NULL, NULL);
1050         g_return_val_if_fail(item->folder != NULL, NULL);
1051         g_return_val_if_fail(item->folder->type == F_IMAP, NULL);
1052         g_return_val_if_fail(first_uid <= last_uid, NULL);
1053
1054         if (imap_cmd_envelope(SESSION(session)->sock, first_uid, last_uid)
1055             != IMAP_SUCCESS) {
1056                 log_warning(_("can't get envelope\n"));
1057                 return NULL;
1058         }
1059
1060         str = g_string_new(NULL);
1061
1062         for (;;) {
1063                 if ((tmp = sock_getline(SESSION(session)->sock)) == NULL) {
1064                         log_warning(_("error occurred while getting envelope.\n"));
1065                         g_string_free(str, TRUE);
1066                         return newlist;
1067                 }
1068                 strretchomp(tmp);
1069                 log_print("IMAP4< %s\n", tmp);
1070                 if (tmp[0] != '*' || tmp[1] != ' ') {
1071                         g_free(tmp);
1072                         break;
1073                 }
1074                 g_string_assign(str, tmp);
1075                 g_free(tmp);
1076
1077                 msginfo = imap_parse_envelope(SESSION(session)->sock, str);
1078                 if (!msginfo) {
1079                         log_warning(_("can't parse envelope: %s\n"), str->str);
1080                         continue;
1081                 }
1082
1083                 msginfo->folder = item;
1084
1085                 if (!newlist)
1086                         llast = newlist = g_slist_append(newlist, msginfo);
1087                 else {
1088                         llast = g_slist_append(llast, msginfo);
1089                         llast = llast->next;
1090                 }
1091         }
1092
1093         g_string_free(str, TRUE);
1094
1095         return newlist;
1096 }
1097
1098 static GSList *imap_delete_cached_messages(GSList *mlist, FolderItem *item,
1099                                            guint32 first_uid, guint32 last_uid)
1100 {
1101         GSList *cur, *next;
1102         MsgInfo *msginfo;
1103         gchar *dir;
1104
1105         g_return_val_if_fail(item != NULL, mlist);
1106         g_return_val_if_fail(item->folder != NULL, mlist);
1107         g_return_val_if_fail(item->folder->type == F_IMAP, mlist);
1108
1109         debug_print(_("Deleting cached messages %d - %d ... "),
1110                     first_uid, last_uid);
1111
1112         dir = folder_item_get_path(item);
1113         remove_numbered_files(dir, first_uid, last_uid);
1114         g_free(dir);
1115
1116         for (cur = mlist; cur != NULL; ) {
1117                 next = cur->next;
1118
1119                 msginfo = (MsgInfo *)cur->data;
1120                 if (msginfo != NULL && first_uid <= msginfo->msgnum &&
1121                     msginfo->msgnum <= last_uid) {
1122                         procmsg_msginfo_free(msginfo);
1123                         mlist = g_slist_remove(mlist, msginfo);
1124                 }
1125
1126                 cur = next;
1127         }
1128
1129         debug_print(_("done.\n"));
1130
1131         return mlist;
1132 }
1133
1134 static void imap_delete_all_cached_messages(FolderItem *item)
1135 {
1136         gchar *dir;
1137
1138         g_return_if_fail(item != NULL);
1139         g_return_if_fail(item->folder != NULL);
1140         g_return_if_fail(item->folder->type == F_IMAP);
1141
1142         debug_print(_("Deleting all cached messages... "));
1143
1144         dir = folder_item_get_path(item);
1145         remove_all_numbered_files(dir);
1146         g_free(dir);
1147
1148         debug_print(_("done.\n"));
1149 }
1150
1151 static SockInfo *imap_open(const gchar *server, gushort port, gchar *buf)
1152 {
1153         SockInfo *sock;
1154
1155         if ((sock = sock_connect(server, port)) == NULL) {
1156                 log_warning(_("Can't connect to IMAP4 server: %s:%d\n"),
1157                             server, port);
1158                 return NULL;
1159         }
1160
1161         imap_cmd_count = 0;
1162
1163         if (imap_cmd_noop(sock) != IMAP_SUCCESS) {
1164                 sock_close(sock);
1165                 return NULL;
1166         }
1167
1168         return sock;
1169 }
1170
1171 #define THROW goto catch
1172
1173 static void imap_parse_namespace(IMAPSession *session, IMAPFolder *folder)
1174 {
1175         gchar *ns_str;
1176         gchar *name;
1177         gchar *separator;
1178         gchar *p;
1179         IMAPNameSpace *namespace;
1180         GList *ns_list = NULL;
1181
1182         g_return_if_fail(session != NULL);
1183         g_return_if_fail(folder != NULL);
1184
1185         if (folder->namespace != NULL) return;
1186
1187         if (imap_cmd_namespace(SESSION(session)->sock, &ns_str)
1188             != IMAP_SUCCESS) {
1189                 log_warning(_("can't get namespace\n"));
1190                 return;
1191         }
1192
1193         /* get the first element */
1194         extract_one_parenthesis_with_skip_quote(ns_str, '"', '(', ')');
1195         g_strstrip(ns_str);
1196         p = ns_str;
1197
1198         while (*p != '\0') {
1199                 /* parse ("#foo" "/") */
1200
1201                 while (*p && *p != '(') p++;
1202                 if (*p == '\0') THROW;
1203                 p++;
1204
1205                 while (*p && *p != '"') p++;
1206                 if (*p == '\0') THROW;
1207                 p++;
1208                 name = p;
1209
1210                 while (*p && *p != '"') p++;
1211                 if (*p == '\0') THROW;
1212                 *p = '\0';
1213                 p++;
1214
1215                 while (*p && isspace(*p)) p++;
1216                 if (*p == '\0') THROW;
1217                 if (strncmp(p, "NIL", 3) == 0)
1218                         separator = NULL;
1219                 else if (*p == '"') {
1220                         p++;
1221                         separator = p;
1222                         while (*p && *p != '"') p++;
1223                         if (*p == '\0') THROW;
1224                         *p = '\0';
1225                         p++;
1226                 } else THROW;
1227
1228                 while (*p && *p != ')') p++;
1229                 if (*p == '\0') THROW;
1230                 p++;
1231
1232                 namespace = g_new(IMAPNameSpace, 1);
1233                 namespace->name = g_strdup(name);
1234                 namespace->separator = separator ? separator[0] : '\0';
1235                 ns_list = g_list_append(ns_list, namespace);
1236                 IMAP_FOLDER(folder)->namespace = ns_list;
1237         }
1238
1239 catch:
1240         g_free(ns_str);
1241         return;
1242 }
1243
1244 #undef THROW
1245
1246 static IMAPNameSpace *imap_find_namespace(IMAPFolder *folder,
1247                                           const gchar *path)
1248 {
1249         IMAPNameSpace *namespace = NULL;
1250         GList *ns_list;
1251         gchar *name;
1252
1253         g_return_val_if_fail(folder != NULL, NULL);
1254         g_return_val_if_fail(path != NULL, NULL);
1255
1256         ns_list = folder->namespace;
1257
1258         for (; ns_list != NULL; ns_list = ns_list->next) {
1259                 IMAPNameSpace *tmp_ns = ns_list->data;
1260
1261                 Xstrdup_a(name, tmp_ns->name, return namespace);
1262                 if (tmp_ns->separator && tmp_ns->separator != '/')
1263                         subst_char(name, tmp_ns->separator, '/');
1264                 if (strncmp(path, name, strlen(name)) == 0)
1265                         namespace = tmp_ns;
1266         }
1267
1268         return namespace;
1269 }
1270
1271 static gchar *imap_get_real_path(IMAPFolder *folder, const gchar *path)
1272 {
1273         gchar *real_path;
1274         IMAPNameSpace *namespace;
1275
1276         g_return_val_if_fail(folder != NULL, NULL);
1277         g_return_val_if_fail(path != NULL, NULL);
1278
1279         real_path = g_strdup(path);
1280         namespace = imap_find_namespace(folder, path);
1281         if (namespace && namespace->separator)
1282                 imap_path_separator_subst(real_path, namespace->separator);
1283
1284         return real_path;
1285 }
1286
1287 static gchar *imap_parse_atom(SockInfo *sock, gchar *src,
1288                               gchar *dest, gint dest_len, GString *str)
1289 {
1290         gchar *cur_pos = src;
1291
1292         g_return_val_if_fail(str != NULL, cur_pos);
1293
1294         while (*cur_pos == ' ') cur_pos++;
1295
1296         if (!strncmp(cur_pos, "NIL", 3)) {
1297                 *dest = '\0';
1298                 cur_pos += 3;
1299         } else if (*cur_pos == '\"') {
1300                 gchar *p;
1301
1302                 p = strchr_cpy(cur_pos + 1, '\"', dest, dest_len);
1303                 cur_pos = p ? p : cur_pos + 2;
1304         } else if (*cur_pos == '{') {
1305                 gchar buf[32];
1306                 gint len;
1307                 gchar *nextline;
1308
1309                 cur_pos = strchr_cpy(cur_pos + 1, '}', buf, sizeof(buf));
1310                 len = atoi(buf);
1311
1312                 if ((nextline = sock_getline(sock)) == NULL)
1313                         return cur_pos;
1314                 strretchomp(nextline);
1315                 log_print("IMAP4< %s\n", nextline);
1316                 g_string_assign(str, nextline);
1317
1318                 len = MIN(len, strlen(nextline));
1319                 memcpy(dest, nextline, MIN(len, dest_len - 1));
1320                 dest[MIN(len, dest_len - 1)] = '\0';
1321                 cur_pos = str->str + len;
1322         }
1323
1324         return cur_pos;
1325 }
1326
1327 static gchar *imap_parse_one_address(SockInfo *sock, gchar *start,
1328                                      gchar *out_from_str,
1329                                      gchar *out_fromname_str,
1330                                      GString *str)
1331 {
1332         gchar buf[IMAPBUFSIZE];
1333         gchar *userid;
1334         gchar *domain;
1335         gchar *cur_pos = start;
1336
1337         cur_pos = imap_parse_atom(sock, cur_pos, buf, sizeof(buf), str);
1338         conv_unmime_header(out_fromname_str, IMAPBUFSIZE, buf, NULL);
1339
1340         cur_pos = imap_parse_atom(sock, cur_pos, buf, sizeof(buf), str);
1341
1342         cur_pos = imap_parse_atom(sock, cur_pos, buf, sizeof(buf), str);
1343         Xstrdup_a(userid, buf, return cur_pos + 1);
1344
1345         cur_pos = imap_parse_atom(sock, cur_pos, buf, sizeof(buf), str);
1346         Xstrdup_a(domain, buf, return cur_pos + 1);
1347
1348         if (out_fromname_str[0] != '\0') {
1349                 g_snprintf(out_from_str, IMAPBUFSIZE, "\"%s\" <%s@%s>",
1350                            out_fromname_str, userid, domain);
1351         } else {
1352                 g_snprintf(out_from_str, IMAPBUFSIZE, "%s@%s",
1353                            userid, domain);
1354                 strcpy(out_fromname_str, out_from_str);
1355         }
1356
1357         while (*cur_pos == ' ') cur_pos++;
1358         g_return_val_if_fail(*cur_pos == ')', NULL);
1359
1360         return cur_pos + 1;
1361 }
1362
1363 static gchar *imap_parse_address(SockInfo *sock, gchar *start,
1364                                  gchar **out_from_str,
1365                                  gchar **out_fromname_str,
1366                                  GString *str)
1367 {
1368         gchar buf[IMAPBUFSIZE];
1369         gchar name_buf[IMAPBUFSIZE];
1370         gchar *cur_pos = start;
1371         GString *addr_str;
1372
1373         if (out_from_str)     *out_from_str     = NULL;
1374         if (out_fromname_str) *out_fromname_str = NULL;
1375         buf[0] = name_buf[0] = '\0';
1376
1377         if (!strncmp(cur_pos, "NIL", 3)) {
1378                 if (out_from_str)     *out_from_str     = g_strdup("");
1379                 if (out_fromname_str) *out_fromname_str = g_strdup("");
1380                 return cur_pos + 3;
1381         }
1382
1383         g_return_val_if_fail(*cur_pos == '(', NULL);
1384         cur_pos++;
1385
1386         addr_str = g_string_new(NULL);
1387
1388         for (;;) {
1389                 gchar ch = *cur_pos++;
1390                 if (ch == ')') break;
1391                 if (ch == '(') {
1392                         cur_pos = imap_parse_one_address
1393                                 (sock, cur_pos, buf, name_buf, str);
1394                         if (!cur_pos) {
1395                                 g_string_free(addr_str, TRUE);
1396                                 return NULL;
1397                         }
1398                         if (addr_str->str[0] != '\0')
1399                                 g_string_append(addr_str, ", ");
1400                         g_string_append(addr_str, buf);
1401                 }
1402         }
1403
1404         if (out_from_str)     *out_from_str     = g_strdup(addr_str->str);
1405         if (out_fromname_str) *out_fromname_str = g_strdup(name_buf);
1406
1407         g_string_free(addr_str, TRUE);
1408
1409         return cur_pos;
1410 }
1411
1412 static MsgFlags imap_parse_flags(const gchar *flag_str)  
1413 {
1414         const gchar *p = flag_str;
1415         MsgFlags flags;
1416
1417         flags = 0;
1418         MSG_SET_FLAGS(flags, MSG_UNREAD|MSG_IMAP);
1419
1420         while ((p = strchr(p, '\\')) != NULL) {
1421                 p++;
1422
1423                 if (g_strncasecmp(p, "Recent", 6) == 0) {
1424                         MSG_SET_FLAGS(flags, MSG_NEW|MSG_UNREAD);
1425                 } else if (g_strncasecmp(p, "Seen", 4) == 0) {
1426                         MSG_UNSET_FLAGS(flags, MSG_NEW|MSG_UNREAD);
1427                 } else if (g_strncasecmp(p, "Deleted", 7) == 0) {
1428                         MSG_SET_FLAGS(flags, MSG_DELETED);
1429                 } else if (g_strncasecmp(p, "Flagged", 7) == 0) {
1430                         MSG_SET_FLAGS(flags, MSG_MARKED);
1431                 }
1432         }
1433
1434         return flags;
1435 }
1436
1437 static MsgInfo *imap_parse_envelope(SockInfo *sock, GString *line_str)
1438 {
1439         MsgInfo *msginfo;
1440         gchar buf[IMAPBUFSIZE];
1441         gchar tmp[IMAPBUFSIZE];
1442         gchar *cur_pos;
1443         gint msgnum;
1444         guint32 uid = 0;
1445         size_t size = 0;
1446         gchar *date = NULL;
1447         time_t date_t = 0;
1448         gchar *subject = NULL;
1449         gchar *tmp_from;
1450         gchar *tmp_fromname;
1451         gchar *from = NULL;
1452         gchar *fromname = NULL;
1453         gchar *tmp_to;
1454         gchar *to = NULL;
1455         gchar *inreplyto = NULL;
1456         gchar *msgid = NULL;
1457         MsgFlags flags = 0;
1458         gint n_element = 0;
1459
1460         g_return_val_if_fail(line_str != NULL, NULL);
1461         g_return_val_if_fail(line_str->str[0] == '*' &&
1462                              line_str->str[1] == ' ', NULL);
1463
1464         cur_pos = line_str->str + 2;
1465
1466 #define PARSE_ONE_ELEMENT(ch) \
1467 { \
1468         cur_pos = strchr_cpy(cur_pos, ch, buf, sizeof(buf)); \
1469         g_return_val_if_fail(cur_pos != NULL, NULL); \
1470 }
1471
1472         PARSE_ONE_ELEMENT(' ');
1473         msgnum = atoi(buf);
1474
1475         PARSE_ONE_ELEMENT(' ');
1476         g_return_val_if_fail(!strcmp(buf, "FETCH"), NULL);
1477
1478         g_return_val_if_fail(*cur_pos == '(', NULL);
1479         cur_pos++;
1480
1481         while (n_element < 4) {
1482                 while (*cur_pos == ' ') cur_pos++;
1483
1484                 if (!strncmp(cur_pos, "UID ", 4)) {
1485                         cur_pos += 4;
1486                         uid = strtoul(cur_pos, &cur_pos, 10);
1487                 } else if (!strncmp(cur_pos, "FLAGS ", 6)) {
1488                         cur_pos += 6;
1489                         g_return_val_if_fail(*cur_pos == '(', NULL);
1490                         cur_pos++;
1491                         PARSE_ONE_ELEMENT(')');
1492                         flags = imap_parse_flags(buf);
1493                 } else if (!strncmp(cur_pos, "RFC822.SIZE ", 12)) {
1494                         cur_pos += 12;
1495                         size = strtol(cur_pos, &cur_pos, 10);
1496                 } else if (!strncmp(cur_pos, "ENVELOPE ", 9)) {
1497                         cur_pos += 9;
1498                         g_return_val_if_fail(*cur_pos == '(', NULL);
1499                         cur_pos = imap_parse_atom
1500                                 (sock, cur_pos + 1, buf, sizeof(buf), line_str);
1501                         Xstrdup_a(date, buf, return NULL);
1502                         date_t = procheader_date_parse(NULL, date, 0);
1503
1504                         cur_pos = imap_parse_atom
1505                                 (sock, cur_pos, buf, sizeof(buf), line_str);
1506                         if (buf[0] != '\0') {
1507                                 conv_unmime_header(tmp, sizeof(tmp), buf, NULL);
1508                                 Xstrdup_a(subject, tmp, return NULL);
1509                         }
1510
1511                         g_return_val_if_fail(*cur_pos == ' ', NULL);
1512                         cur_pos = imap_parse_address(sock, cur_pos + 1,
1513                                                      &tmp_from, &tmp_fromname,
1514                                                      line_str);
1515                         Xstrdup_a(from, tmp_from,
1516                                   {g_free(tmp_from); g_free(tmp_fromname);
1517                                    return NULL;});
1518                         Xstrdup_a(fromname, tmp_fromname,
1519                                   {g_free(tmp_from); g_free(tmp_fromname);
1520                                    return NULL;});
1521                         g_free(tmp_from);
1522                         g_free(tmp_fromname);
1523
1524 #define SKIP_ONE_ELEMENT() \
1525 { \
1526         g_return_val_if_fail(*cur_pos == ' ', NULL); \
1527         cur_pos = imap_parse_address(sock, cur_pos + 1, NULL, NULL, \
1528                                      line_str); \
1529 }
1530
1531                         /* skip sender and reply-to */
1532                         SKIP_ONE_ELEMENT();
1533                         SKIP_ONE_ELEMENT();
1534
1535                         g_return_val_if_fail(*cur_pos == ' ', NULL);
1536                         cur_pos = imap_parse_address(sock, cur_pos + 1,
1537                                                      &tmp_to, NULL, line_str);
1538                         Xstrdup_a(to, tmp_to, {g_free(tmp_to); return NULL;});
1539                         g_free(tmp_to);
1540
1541                         /* skip Cc and Bcc */
1542                         SKIP_ONE_ELEMENT();
1543                         SKIP_ONE_ELEMENT();
1544
1545 #undef SKIP_ONE_ELEMENT
1546
1547                         g_return_val_if_fail(*cur_pos == ' ', NULL);
1548                         cur_pos = imap_parse_atom
1549                                 (sock, cur_pos, buf, sizeof(buf), line_str);
1550                         if (buf[0] != '\0') {
1551                                 eliminate_parenthesis(buf, '(', ')');
1552                                 extract_parenthesis(buf, '<', '>');
1553                                 remove_space(buf);
1554                                 Xstrdup_a(inreplyto, buf, return NULL);
1555                         }
1556
1557                         g_return_val_if_fail(*cur_pos == ' ', NULL);
1558                         cur_pos = imap_parse_atom
1559                                 (sock, cur_pos, buf, sizeof(buf), line_str);
1560                         if (buf[0] != '\0') {
1561                                 extract_parenthesis(buf, '<', '>');
1562                                 remove_space(buf);
1563                                 Xstrdup_a(msgid, buf, return NULL);
1564                         }
1565
1566                         g_return_val_if_fail(*cur_pos == ')', NULL);
1567                         cur_pos++;
1568                 } else {
1569                         g_warning("invalid FETCH response: %s\n", cur_pos);
1570                         break;
1571                 }
1572
1573                 if (*cur_pos == '\0' || *cur_pos == ')') break;
1574                 n_element++;
1575         }
1576
1577         msginfo = g_new0(MsgInfo, 1);
1578         msginfo->msgnum = uid;
1579         msginfo->size = size;
1580         msginfo->date = g_strdup(date);
1581         msginfo->date_t = date_t;
1582         msginfo->subject = g_strdup(subject);
1583         msginfo->from = g_strdup(from);
1584         msginfo->fromname = g_strdup(fromname);
1585         msginfo->to = g_strdup(to);
1586         msginfo->inreplyto = g_strdup(inreplyto);
1587         msginfo->msgid = g_strdup(msgid);
1588         msginfo->flags = flags;
1589
1590         return msginfo;
1591 }
1592
1593 static gint imap_set_message_flags(IMAPSession *session,
1594                                    guint32 first_uid,
1595                                    guint32 last_uid,
1596                                    IMAPFlags flags,
1597                                    gboolean is_set)
1598 {
1599         GString *buf;
1600         gint ok;
1601
1602         buf = g_string_new(is_set ? "+FLAGS (" : "-FLAGS (");
1603
1604         if (IMAP_IS_SEEN(flags))        g_string_append(buf, "\\Seen ");
1605         if (IMAP_IS_ANSWERED(flags))    g_string_append(buf, "\\Answered ");
1606         if (IMAP_IS_FLAGGED(flags))     g_string_append(buf, "\\Flagged ");
1607         if (IMAP_IS_DELETED(flags))     g_string_append(buf, "\\Deleted ");
1608         if (IMAP_IS_DRAFT(flags))       g_string_append(buf, "\\Draft");
1609
1610         if (buf->str[buf->len - 1] == ' ')
1611                 g_string_truncate(buf, buf->len - 1);
1612
1613         g_string_append_c(buf, ')');
1614
1615         ok = imap_cmd_store(SESSION(session)->sock, first_uid, last_uid,
1616                             buf->str);
1617         g_string_free(buf, TRUE);
1618
1619         return ok;
1620 }
1621
1622 static gint imap_select(IMAPSession *session, IMAPFolder *folder,
1623                         const gchar *path,
1624                         gint *exists, gint *recent, gint *unseen,
1625                         guint32 *uid_validity)
1626 {
1627         gchar *real_path;
1628         gint ok;
1629
1630         real_path = imap_get_real_path(folder, path);
1631         ok = imap_cmd_select(SESSION(session)->sock, real_path,
1632                              exists, recent, unseen, uid_validity);
1633         if (ok != IMAP_SUCCESS)
1634                 log_warning(_("can't select folder: %s\n"), real_path);
1635         g_free(real_path);
1636
1637         return ok;
1638 }
1639
1640 #define THROW(err) { ok = err; goto catch; }
1641
1642 static gint imap_get_uid(IMAPSession *session, gint msgnum, guint32 *uid)
1643 {
1644         gint ok;
1645         GPtrArray *argbuf;
1646         gchar *str;
1647         gint num;
1648
1649         *uid = 0;
1650         argbuf = g_ptr_array_new();
1651
1652         imap_cmd_gen_send(SESSION(session)->sock, "FETCH %d (UID)", msgnum);
1653         if ((ok = imap_cmd_ok(SESSION(session)->sock, argbuf)) != IMAP_SUCCESS)
1654                 THROW(ok);
1655
1656         str = search_array_contain_str(argbuf, "FETCH");
1657         if (!str) THROW(IMAP_ERROR);
1658
1659         if (sscanf(str, "%d FETCH (UID %d)", &num, uid) != 2 ||
1660             num != msgnum) {
1661                 g_warning("imap_get_uid(): invalid FETCH line.\n");
1662                 THROW(IMAP_ERROR);
1663         }
1664
1665 catch:
1666         ptr_array_free_strings(argbuf);
1667         g_ptr_array_free(argbuf, TRUE);
1668
1669         return ok;
1670 }
1671
1672 #undef THROW
1673
1674 #if 0
1675 static gint imap_status_uidnext(IMAPSession *session, IMAPFolder *folder,
1676                                 const gchar *path, guint32 *uid_next)
1677 {
1678         gchar *real_path;
1679         gint ok;
1680
1681         real_path = imap_get_real_path(folder, path);
1682         ok = imap_cmd_status(SESSION(session)->sock, real_path,
1683                              "UIDNEXT", uid_next);
1684         if (ok != IMAP_SUCCESS)
1685                 log_warning(_("can't get the next uid of folder: %s\n"),
1686                             real_path);
1687         g_free(real_path);
1688
1689         return ok;
1690 }
1691 #endif
1692
1693
1694 /* low-level IMAP4rev1 commands */
1695
1696 static gint imap_cmd_login(SockInfo *sock,
1697                            const gchar *user, const gchar *pass)
1698 {
1699         gint ok;
1700
1701         if (strchr(user, ' ') != NULL)
1702                 imap_cmd_gen_send(sock, "LOGIN \"%s\" %s", user, pass);
1703         else
1704                 imap_cmd_gen_send(sock, "LOGIN %s %s", user, pass);
1705
1706         ok = imap_cmd_ok(sock, NULL);
1707         if (ok != IMAP_SUCCESS)
1708                 log_warning(_("IMAP4 login failed.\n"));
1709
1710         return ok;
1711 }
1712
1713 static gint imap_cmd_logout(SockInfo *sock)
1714 {
1715         imap_cmd_gen_send(sock, "LOGOUT");
1716         return imap_cmd_ok(sock, NULL);
1717 }
1718
1719 static gint imap_cmd_noop(SockInfo *sock)
1720 {
1721         imap_cmd_gen_send(sock, "NOOP");
1722         return imap_cmd_ok(sock, NULL);
1723 }
1724
1725 #define THROW(err) { ok = err; goto catch; }
1726
1727 static gint imap_cmd_namespace(SockInfo *sock, gchar **ns_str)
1728 {
1729         gint ok;
1730         GPtrArray *argbuf;
1731         gchar *str;
1732
1733         argbuf = g_ptr_array_new();
1734
1735         imap_cmd_gen_send(sock, "NAMESPACE");
1736         if ((ok = imap_cmd_ok(sock, argbuf)) != IMAP_SUCCESS) THROW(ok);
1737
1738         str = search_array_contain_str(argbuf, "NAMESPACE");
1739         if (!str) THROW(IMAP_ERROR);
1740
1741         *ns_str = g_strdup(str);
1742
1743 catch:
1744         ptr_array_free_strings(argbuf);
1745         g_ptr_array_free(argbuf, TRUE);
1746
1747         return ok;
1748 }
1749
1750 #undef THROW
1751
1752 static gint imap_cmd_list(SockInfo *sock, const gchar *ref,
1753                           const gchar *mailbox, GPtrArray *argbuf)
1754 {
1755         gchar *new_ref;
1756         gchar *new_mailbox;
1757
1758         if (!ref) ref = "\"\"";
1759         if (!mailbox) mailbox = "\"\"";
1760
1761         if (*ref != '"' && strchr(ref, ' ') != NULL)
1762                 new_ref = g_strdup_printf("\"%s\"", ref);
1763         else
1764                 new_ref = g_strdup(ref);
1765         if (*mailbox != '"' && strchr(mailbox, ' ') != NULL)
1766                 new_mailbox = g_strdup_printf("\"%s\"", mailbox);
1767         else
1768                 new_mailbox = g_strdup(mailbox);
1769
1770         imap_cmd_gen_send(sock, "LIST %s %s", new_ref, new_mailbox);
1771
1772         g_free(new_ref);
1773         g_free(new_mailbox);
1774
1775         return imap_cmd_ok(sock, argbuf);
1776 }
1777
1778 #define THROW goto catch
1779
1780 static gint imap_cmd_select(SockInfo *sock, const gchar *folder,
1781                             gint *exists, gint *recent, gint *unseen,
1782                             guint32 *uid_validity)
1783 {
1784         gint ok;
1785         gchar *resp_str;
1786         GPtrArray *argbuf;
1787
1788         *exists = *recent = *unseen = *uid_validity = 0;
1789         argbuf = g_ptr_array_new();
1790
1791         if (strchr(folder, ' ') != NULL)
1792                 imap_cmd_gen_send(sock, "SELECT \"%s\"", folder);
1793         else
1794                 imap_cmd_gen_send(sock, "SELECT %s", folder);
1795
1796         if ((ok = imap_cmd_ok(sock, argbuf)) != IMAP_SUCCESS) THROW;
1797
1798         resp_str = search_array_contain_str(argbuf, "EXISTS");
1799         if (resp_str) {
1800                 if (sscanf(resp_str,"%d EXISTS", exists) != 1) {
1801                         g_warning("imap_cmd_select(): invalid EXISTS line.\n");
1802                         THROW;
1803                 }
1804         }
1805
1806         resp_str = search_array_contain_str(argbuf, "RECENT");
1807         if (resp_str) {
1808                 if (sscanf(resp_str, "%d RECENT", recent) != 1) {
1809                         g_warning("imap_cmd_select(): invalid RECENT line.\n");
1810                         THROW;
1811                 }
1812         }
1813
1814         resp_str = search_array_contain_str(argbuf, "UIDVALIDITY");
1815         if (resp_str) {
1816                 if (sscanf(resp_str, "OK [UIDVALIDITY %u] ", uid_validity)
1817                     != 1) {
1818                         g_warning("imap_cmd_select(): invalid UIDVALIDITY line.\n");
1819                         THROW;
1820                 }
1821         }
1822
1823         resp_str = search_array_contain_str(argbuf, "UNSEEN");
1824         if (resp_str) {
1825                 if (sscanf(resp_str, "OK [UNSEEN %d] ", unseen) != 1) {
1826                         g_warning("imap_cmd_select(): invalid UNSEEN line.\n");
1827                         THROW;
1828                 }
1829         }
1830
1831 catch:
1832         ptr_array_free_strings(argbuf);
1833         g_ptr_array_free(argbuf, TRUE);
1834
1835         return ok;
1836 }
1837
1838 #if 0
1839 static gint imap_cmd_status(SockInfo *sock, const gchar *folder,
1840                             const gchar *status, gint *value)
1841 {
1842         gint ok;
1843         GPtrArray *argbuf;
1844         gchar *str;
1845
1846         if (value) *value = 0;
1847         argbuf = g_ptr_array_new();
1848
1849         if (strchr(folder, ' ') != NULL)
1850                 imap_cmd_gen_send(sock, "STATUS \"%s\" (%s)", folder, status);
1851         else
1852                 imap_cmd_gen_send(sock, "STATUS %s (%s)", folder, status);
1853
1854         if ((ok = imap_cmd_ok(sock, argbuf)) != IMAP_SUCCESS) THROW;
1855
1856         str = search_array_contain_str(argbuf, "STATUS");
1857         if (!str) THROW;
1858         str = strchr(str, '(');
1859         if (!str) THROW;
1860         str++;
1861         if (strncmp(str, status, strlen(status)) != 0) THROW;
1862         str += strlen(status);
1863         if (*str != ' ') THROW;
1864         str++;
1865         if (value) *value = atoi(str);
1866
1867 catch:
1868         ptr_array_free_strings(argbuf);
1869         g_ptr_array_free(argbuf, TRUE);
1870
1871         return ok;
1872 }
1873 #endif
1874
1875 #undef THROW
1876
1877 static gint imap_cmd_create(SockInfo *sock, const gchar *folder)
1878 {
1879         if (strchr(folder, ' ') != NULL)
1880                 imap_cmd_gen_send(sock, "CREATE \"%s\"", folder);
1881         else
1882                 imap_cmd_gen_send(sock, "CREATE %s", folder);
1883
1884         return imap_cmd_ok(sock, NULL);
1885 }
1886
1887 static gint imap_cmd_delete(SockInfo *sock, const gchar *folder)
1888 {
1889         if (strchr(folder, ' ') != NULL)
1890                 imap_cmd_gen_send(sock, "DELETE \"%s\"", folder);
1891         else
1892                 imap_cmd_gen_send(sock, "DELETE %s", folder);
1893
1894         return imap_cmd_ok(sock, NULL);
1895 }
1896
1897 static gint imap_cmd_fetch(SockInfo *sock, guint32 uid, const gchar *filename)
1898 {
1899         gint ok;
1900         gchar buf[IMAPBUFSIZE];
1901         gchar *cur_pos;
1902         gchar size_str[32];
1903         glong size_num;
1904
1905         g_return_val_if_fail(filename != NULL, IMAP_ERROR);
1906
1907         imap_cmd_gen_send(sock, "UID FETCH %d BODY[]", uid);
1908
1909         if (sock_gets(sock, buf, sizeof(buf)) < 0)
1910                 return IMAP_ERROR;
1911         strretchomp(buf);
1912         if (buf[0] != '*' || buf[1] != ' ')
1913                 return IMAP_ERROR;
1914         log_print("IMAP4< %s\n", buf);
1915
1916         cur_pos = strchr(buf, '{');
1917         g_return_val_if_fail(cur_pos != NULL, IMAP_ERROR);
1918         cur_pos = strchr_cpy(cur_pos + 1, '}', size_str, sizeof(size_str));
1919         g_return_val_if_fail(cur_pos != NULL, IMAP_ERROR);
1920         size_num = atol(size_str);
1921
1922         if (*cur_pos != '\0') return IMAP_ERROR;
1923
1924         if (recv_bytes_write_to_file(sock, size_num, filename) != 0)
1925                 return IMAP_ERROR;
1926
1927         if (imap_cmd_gen_recv(sock, buf, sizeof(buf)) != IMAP_SUCCESS)
1928                 return IMAP_ERROR;
1929
1930         if (buf[0] == '\0' || buf[strlen(buf) - 1] != ')')
1931                 return IMAP_ERROR;
1932
1933         ok = imap_cmd_ok(sock, NULL);
1934
1935         return ok;
1936 }
1937
1938 static gint imap_cmd_append(SockInfo *sock, const gchar *destfolder,
1939                             const gchar *file)
1940 {
1941         gint ok;
1942         gint size;
1943
1944         g_return_val_if_fail(file != NULL, IMAP_ERROR);
1945
1946         size = get_file_size(file);
1947         imap_cmd_gen_send(sock, "APPEND %s {%d}", destfolder, size);
1948         ok = imap_cmd_ok(sock, NULL);
1949         if (ok != IMAP_SUCCESS) {
1950                 log_warning(_("can't append %s to %s\n"), file, destfolder);
1951                 return -1;
1952         }
1953
1954         return ok;
1955 }
1956
1957 static gint imap_cmd_copy(SockInfo *sock, guint32 uid, const gchar *destfolder)
1958 {
1959         gint ok;
1960
1961         g_return_val_if_fail(destfolder != NULL, IMAP_ERROR);
1962
1963         if (strchr(destfolder, ' ') != NULL)
1964                 imap_cmd_gen_send(sock, "UID COPY %d \"%s\"", uid, destfolder);
1965         else
1966                 imap_cmd_gen_send(sock, "UID COPY %d %s", uid, destfolder);
1967
1968         ok = imap_cmd_ok(sock, NULL);
1969         if (ok != IMAP_SUCCESS) {
1970                 log_warning(_("can't copy %d to %s\n"), uid, destfolder);
1971                 return -1;
1972         }
1973
1974         return ok;
1975 }
1976
1977 gint imap_cmd_envelope(SockInfo *sock, guint32 first_uid, guint32 last_uid)
1978 {
1979         imap_cmd_gen_send
1980                 (sock, "UID FETCH %d:%d (UID FLAGS RFC822.SIZE ENVELOPE)",
1981                  first_uid, last_uid);
1982
1983         return IMAP_SUCCESS;
1984 }
1985
1986 static gint imap_cmd_store(SockInfo *sock, guint32 first_uid, guint32 last_uid,
1987                            gchar *sub_cmd)
1988 {
1989         gint ok;
1990
1991         imap_cmd_gen_send(sock, "UID STORE %d:%d %s",
1992                           first_uid, last_uid, sub_cmd);
1993
1994         if ((ok = imap_cmd_ok(sock, NULL)) != IMAP_SUCCESS) {
1995                 log_warning(_("error while imap command: STORE %d:%d %s\n"),
1996                             first_uid, last_uid, sub_cmd);
1997                 return ok;
1998         }
1999
2000         return IMAP_SUCCESS;
2001 }
2002
2003 static gint imap_cmd_expunge(SockInfo *sock)
2004 {
2005         gint ok;
2006
2007         imap_cmd_gen_send(sock, "EXPUNGE");
2008         if ((ok = imap_cmd_ok(sock, NULL)) != IMAP_SUCCESS) {
2009                 log_warning(_("error while imap command: EXPUNGE\n"));
2010                 return ok;
2011         }
2012
2013         return IMAP_SUCCESS;
2014 }
2015
2016 static gint imap_cmd_ok(SockInfo *sock, GPtrArray *argbuf)
2017 {
2018         gint ok;
2019         gchar buf[IMAPBUFSIZE];
2020         gint cmd_num;
2021         gchar cmd_status[IMAPBUFSIZE];
2022
2023         while ((ok = imap_cmd_gen_recv(sock, buf, sizeof(buf)))
2024                == IMAP_SUCCESS) {
2025                 if (buf[0] == '*' && buf[1] == ' ') {
2026                         if (argbuf)
2027                                 g_ptr_array_add(argbuf, g_strdup(buf + 2));
2028                         continue;
2029                 }
2030
2031                 if (sscanf(buf, "%d %s", &cmd_num, cmd_status) < 2)
2032                         return IMAP_ERROR;
2033                 else if (cmd_num == imap_cmd_count &&
2034                          !strcmp(cmd_status, "OK")) {
2035                         if (argbuf)
2036                                 g_ptr_array_add(argbuf, g_strdup(buf));
2037                         return IMAP_SUCCESS;
2038                 } else
2039                         return IMAP_ERROR;
2040         }
2041
2042         return ok;
2043 }
2044
2045 static void imap_cmd_gen_send(SockInfo *sock, const gchar *format, ...)
2046 {
2047         gchar buf[IMAPBUFSIZE];
2048         gchar tmp[IMAPBUFSIZE];
2049         gchar *p;
2050         va_list args;
2051
2052         va_start(args, format);
2053         g_vsnprintf(tmp, sizeof(tmp), format, args);
2054         va_end(args);
2055
2056         imap_cmd_count++;
2057
2058         g_snprintf(buf, sizeof(buf), "%d %s\r\n", imap_cmd_count, tmp);
2059         if (!strncasecmp(tmp, "LOGIN ", 6) && (p = strchr(tmp + 6, ' '))) {
2060                 *p = '\0';
2061                 log_print("IMAP4> %d %s ********\n", imap_cmd_count, tmp);
2062         } else
2063                 log_print("IMAP4> %d %s\n", imap_cmd_count, tmp);
2064
2065         sock_write(sock, buf, strlen(buf));
2066 }
2067
2068 static gint imap_cmd_gen_recv(SockInfo *sock, gchar *buf, gint size)
2069 {
2070         if (sock_gets(sock, buf, size) == -1)
2071                 return IMAP_SOCKET;
2072
2073         strretchomp(buf);
2074
2075         log_print("IMAP4< %s\n", buf);
2076
2077         return IMAP_SUCCESS;
2078 }
2079
2080
2081 /* misc utility functions */
2082
2083 static gchar *strchr_cpy(const gchar *src, gchar ch, gchar *dest, gint len)
2084 {
2085         gchar *tmp;
2086
2087         dest[0] = '\0';
2088         tmp = strchr(src, ch);
2089         if (!tmp)
2090                 return NULL;
2091
2092         memcpy(dest, src, MIN(tmp - src, len - 1));
2093         dest[MIN(tmp - src, len - 1)] = '\0';
2094
2095         return tmp + 1;
2096 }
2097
2098 static gchar *search_array_contain_str(GPtrArray *array, gchar *str)
2099 {
2100         gint i;
2101
2102         for (i = 0; i < array->len; i++) {
2103                 gchar *tmp;
2104
2105                 tmp = g_ptr_array_index(array, i);
2106                 if (strstr(tmp, str) != NULL)
2107                         return tmp;
2108         }
2109
2110         return NULL;
2111 }
2112
2113 static void imap_path_separator_subst(gchar *str, gchar separator)
2114 {
2115         if (separator && separator != '/')
2116                 subst_char(str, '/', separator);
2117 }