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