sync with sylpheed 0.4.99cvs8
[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
31 #include "intl.h"
32 #include "imap.h"
33 #include "socket.h"
34 #include "recv.h"
35 #include "procmsg.h"
36 #include "procheader.h"
37 #include "folder.h"
38 #include "statusbar.h"
39 #include "prefs_account.h"
40 #include "codeconv.h"
41 #include "utils.h"
42
43 #define IMAP4_PORT      143
44
45 static GList *session_list = NULL;
46
47 static gint imap_cmd_count = 0;
48
49 static IMAPSession *imap_session_connect_if_not (Folder         *folder);
50
51 static gint imap_do_copy                (Folder         *folder,
52                                          FolderItem     *dest,
53                                          MsgInfo        *msginfo,
54                                          gboolean        remove_source);
55 static gint imap_do_copy_msgs_with_dest (Folder         *folder,
56                                          FolderItem     *dest,
57                                          GSList         *msglist,
58                                          gboolean        remove_source);
59
60 static GSList *imap_get_uncached_messages       (IMAPSession    *session,
61                                                  FolderItem     *item,
62                                                  gint            first,
63                                                  gint            last);
64 static GSList *imap_delete_cached_messages      (GSList         *mlist,
65                                                  gint            first,
66                                                  gint            last);
67 static void imap_delete_all_cached_messages     (FolderItem     *item);
68
69 static SockInfo *imap_open              (const gchar    *server,
70                                          gushort         port,
71                                          gchar          *buf);
72
73 static gint imap_set_article_flags      (IMAPSession    *session,
74                                          gint            first,
75                                          gint            last,
76                                          IMAPFlags       flag,
77                                          gboolean        is_set);
78
79 static gchar *imap_parse_atom           (SockInfo *sock,
80                                          gchar    *src,
81                                          gchar    *dest,
82                                          gchar    *orig_buf);
83 static gchar *imap_parse_one_address    (SockInfo *sock,
84                                          gchar    *start,
85                                          gchar    *out_from_str,
86                                          gchar    *out_fromname_str,
87                                          gchar    *orig_buf);
88 static gchar *imap_parse_address        (SockInfo *sock,
89                                          gchar    *start,
90                                          gchar   **out_from_str,
91                                          gchar   **out_fromname_str,
92                                          gchar    *orig_buf);
93 static MsgFlags imap_parse_flags        (const gchar    *flag_str);
94 static MsgInfo *imap_parse_envelope     (SockInfo *sock,
95                                          gchar    *line_str);
96
97 /* low-level IMAP4rev1 commands */
98 static gint imap_cmd_login      (SockInfo       *sock,
99                                  const gchar    *user,
100                                  const gchar    *pass);
101 static gint imap_cmd_logout     (SockInfo       *sock);
102 static gint imap_cmd_noop       (SockInfo       *sock);
103 static gint imap_cmd_select     (SockInfo       *sock,
104                                  const gchar    *folder,
105                                  gint           *exists,
106                                  gint           *recent,
107                                  gint           *unseen,
108                                  gulong         *uid);
109 static gint imap_cmd_status     (SockInfo       *sock,
110                                  const gchar    *folder,
111                                  const gchar    *status);
112 static gint imap_cmd_create     (SockInfo       *sock,
113                                  const gchar    *folder);
114 static gint imap_cmd_delete     (SockInfo       *sock,
115                                  const gchar    *folder);
116 static gint imap_cmd_envelope   (SockInfo       *sock,
117                                  gint            first,
118                                  gint            last);
119 #if 0
120 static gint imap_cmd_search     (SockInfo       *sock,
121                                  GSList         *numlist);
122 #endif
123 static gint imap_cmd_fetch      (SockInfo       *sock,
124                                  gint            num,
125                                  const gchar    *filename);
126 static gint imap_cmd_append     (SockInfo       *sock,
127                                  const gchar    *destfolder,
128                                  const gchar    *file);
129 static gint imap_cmd_copy       (SockInfo       *sock,
130                                  gint            num,
131                                  const gchar    *destfolder);
132 static gint imap_cmd_store      (SockInfo       *sock,
133                                  gint            first,
134                                  gint            last,
135                                  gchar          *sub_cmd);
136 static gint imap_cmd_expunge    (SockInfo       *sock);
137
138 static gint imap_cmd_ok         (SockInfo       *sock,
139                                  GPtrArray      *argbuf);
140 static void imap_cmd_gen_send   (SockInfo       *sock,
141                                  const gchar    *format, ...);
142 static gint imap_cmd_gen_recv   (SockInfo       *sock,
143                                  gchar          *buf,
144                                  gint            size);
145
146 /* misc utility functions */
147 static gchar *strchr_cpy                (const gchar    *src,
148                                          gchar           ch,
149                                          gchar          *dest,
150                                          gint            len);
151 static gchar *search_array_contain_str  (GPtrArray      *array,
152                                          gchar          *str);
153 static void imap_path_subst_slash_to_dot(gchar *str);
154
155
156 static IMAPSession *imap_session_connect_if_not(Folder *folder)
157 {
158         RemoteFolder *rfolder = REMOTE_FOLDER(folder);
159
160         g_return_val_if_fail(folder != NULL, NULL);
161         g_return_val_if_fail(folder->type == F_IMAP, NULL);
162         g_return_val_if_fail(folder->account != NULL, NULL);
163
164         if (!rfolder->session) {
165                 rfolder->session =
166                         imap_session_new(folder->account->recv_server,
167                                          IMAP4_PORT,
168                                          folder->account->userid,
169                                          folder->account->passwd);
170                 statusbar_pop_all();
171                 return IMAP_SESSION(rfolder->session);
172         }
173
174         if (imap_cmd_noop(rfolder->session->sock) != IMAP_SUCCESS) {
175                 log_warning(_("IMAP4 connection to %s:%d has been"
176                               " disconnected. Reconnecting...\n"),
177                             folder->account->recv_server, IMAP4_PORT);
178                 session_destroy(rfolder->session);
179                 rfolder->session =
180                         imap_session_new(folder->account->recv_server,
181                                          IMAP4_PORT, folder->account->userid,
182                                          folder->account->passwd);
183         }
184
185         statusbar_pop_all();
186         return IMAP_SESSION(rfolder->session);
187 }
188
189 Session *imap_session_new(const gchar *server, gushort port,
190                           const gchar *user, const gchar *pass)
191 {
192         gchar buf[IMAPBUFSIZE];
193         IMAPSession *session;
194         SockInfo *imap_sock;
195
196         g_return_val_if_fail(server != NULL, NULL);
197
198         log_message(_("creating IMAP4 connection to %s:%d ...\n"),
199                     server, port);
200
201         if ((imap_sock = imap_open(server, port, buf)) == NULL)
202                 return NULL;
203         if (imap_cmd_login(imap_sock, user, pass) != IMAP_SUCCESS) {
204                 imap_cmd_logout(imap_sock);
205                 sock_close(imap_sock);
206                 return NULL;
207         }
208
209         session = g_new(IMAPSession, 1);
210         SESSION(session)->type      = SESSION_IMAP;
211         SESSION(session)->server    = g_strdup(server);
212         SESSION(session)->sock      = imap_sock;
213         SESSION(session)->connected = TRUE;
214         SESSION(session)->phase     = SESSION_READY;
215         SESSION(session)->data      = NULL;
216         session->mbox = NULL;
217
218         session_list = g_list_append(session_list, session);
219
220         return SESSION(session);
221 }
222
223 void imap_session_destroy(IMAPSession *session)
224 {
225         sock_close(SESSION(session)->sock);
226         SESSION(session)->sock = NULL;
227
228         g_free(session->mbox);
229
230         session_list = g_list_remove(session_list, session);
231 }
232
233 void imap_session_destroy_all(void)
234 {
235         while (session_list != NULL) {
236                 IMAPSession *session = (IMAPSession *)session_list->data;
237
238                 imap_cmd_logout(SESSION(session)->sock);
239                 imap_session_destroy(session);
240         }
241 }
242
243 GSList *imap_get_msg_list(Folder *folder, FolderItem *item, gboolean use_cache)
244 {
245         GSList *mlist = NULL;
246         IMAPSession *session;
247         gint ok, exists = 0, recent = 0, unseen = 0, begin = 1;
248         gulong uid = 0, last_uid;
249
250         g_return_val_if_fail(folder != NULL, NULL);
251         g_return_val_if_fail(item != NULL, NULL);
252         g_return_val_if_fail(folder->type == F_IMAP, NULL);
253         g_return_val_if_fail(folder->account != NULL, NULL);
254
255         session = imap_session_connect_if_not(folder);
256
257         if (!session) {
258                 mlist = procmsg_read_cache(item, FALSE);
259                 item->last_num = procmsg_get_last_num_in_cache(mlist);
260                 procmsg_set_flags(mlist, item);
261                 statusbar_pop_all();
262                 return mlist;
263         }
264
265         last_uid = item->mtime;
266
267         ok = imap_cmd_select(SESSION(session)->sock, item->path,
268                              &exists, &recent, &unseen, &uid);
269         if (ok != IMAP_SUCCESS) {
270                 log_warning(_("can't select folder: %s\n"), item->path);
271                 statusbar_pop_all();
272                 return NULL;
273         }
274
275         if (use_cache) {
276                 gint cache_last;
277
278                 mlist = procmsg_read_cache(item, FALSE);
279                 procmsg_set_flags(mlist, item);
280                 cache_last = procmsg_get_last_num_in_cache(mlist);
281
282                 /* calculating the range of envelope to get */
283                 if (exists < cache_last) {
284                         /* some messages are deleted (get all) */
285                         begin = 1;
286                 } else if (exists == cache_last) {
287                         if (last_uid != 0 && last_uid != uid) {
288                                 /* some recent but deleted (get all) */
289                                 begin = 1;
290                         } else {
291                                 /* mailbox unchanged (get none)*/
292                                 begin = -1;
293                         }
294                 } else {
295                         if (exists == cache_last + recent) {
296                                 /* some recent */
297                                 begin = cache_last + 1;
298                         } else {
299                                 /* some recent but deleted (get all) */
300                                 begin = 1;
301                         }
302                 }
303
304                 item->mtime = uid;
305         }
306
307         if (1 < begin && begin <= exists) {
308                 GSList *newlist;
309
310                 newlist = imap_get_uncached_messages
311                         (session, item, begin, exists);
312                 imap_delete_cached_messages(mlist, begin, INT_MAX);
313                 mlist = g_slist_concat(mlist, newlist);
314         } else if (begin == 1) {
315                 if (begin <= exists)
316                         mlist = imap_get_uncached_messages
317                                 (session, item, begin, exists);
318                 imap_delete_all_cached_messages(item);
319         }
320
321         item->last_num = exists;
322
323         statusbar_pop_all();
324
325         return mlist;
326 }
327
328 gchar *imap_fetch_msg(Folder *folder, FolderItem *item, gint num)
329 {
330         gchar *path, *filename;
331         IMAPSession *session;
332         gint ok;
333
334         g_return_val_if_fail(folder != NULL, NULL);
335         g_return_val_if_fail(item != NULL, NULL);
336
337         session = imap_session_connect_if_not(folder);
338
339         path = folder_item_get_path(item);
340         filename = g_strconcat(path, G_DIR_SEPARATOR_S, itos(num), NULL);
341         g_free(path);
342  
343         if (is_file_exist(filename)) {
344                 debug_print(_("message %d has been already cached.\n"), num);
345                 return filename;
346         }
347
348         if (!session) {
349                 g_free(filename);
350                 return NULL;
351         }
352
353         debug_print(_("getting message %d...\n"), num);
354         ok = imap_cmd_fetch(SESSION(session)->sock, num, filename);
355
356         statusbar_pop_all();
357
358         if (ok != IMAP_SUCCESS) {
359                 g_warning(_("can't fetch message %d\n"), num);
360                 g_free(filename);
361                 return NULL;
362         }
363
364         return filename;
365 }
366
367 gint imap_add_msg(Folder *folder, FolderItem *dest, const gchar *file,
368                   gboolean remove_source)
369 {
370         IMAPSession *session;
371         gint ok;
372
373         g_return_val_if_fail(folder != NULL, -1);
374         g_return_val_if_fail(dest != NULL, -1);
375         g_return_val_if_fail(file != NULL, -1);
376
377         session = imap_session_connect_if_not(folder);
378         if (!session)
379                 return -1;
380
381         if (dest->last_num < 0) {
382                 imap_scan_folder(folder, dest);
383                 if (dest->last_num < 0) return -1;
384         }
385
386         ok = imap_cmd_append(SESSION(session)->sock, dest->path, file);
387         if (ok != IMAP_SUCCESS) {
388                 g_warning(_("can't append message %s\n"), file);
389                 return -1;
390         }
391
392         if (remove_source) {
393                 if (unlink(file) < 0)
394                         FILE_OP_ERROR(file, "unlink");
395         }
396
397         return dest->last_num;
398 }
399
400 static gint imap_do_copy(Folder *folder, FolderItem *dest, MsgInfo *msginfo,
401                          gboolean remove_source)
402 {
403         gchar *destdir;
404         IMAPSession *session;
405
406         g_return_val_if_fail(folder != NULL, -1);
407         g_return_val_if_fail(dest != NULL, -1);
408         g_return_val_if_fail(msginfo != NULL, -1);
409
410         session = imap_session_connect_if_not(folder);
411         if (!session) return -1;
412
413         if (msginfo->folder == dest) {
414                 g_warning(_("the src folder is identical to the dest.\n"));
415                 return -1;
416         }
417
418         Xstrdup_a(destdir, dest->path, return -1);
419         /* imap_path_subst_slash_to_dot(destdir); */
420
421         debug_print(_("%s message %s%c%d to %s ...\n"),
422                     remove_source ? "Moving" : "Copying",
423                     msginfo->folder->path, G_DIR_SEPARATOR,
424                     msginfo->msgnum, destdir);
425
426         imap_cmd_copy(SESSION(session)->sock, msginfo->msgnum, destdir);
427
428         if (remove_source) {
429                 imap_set_article_flags(session, msginfo->msgnum, msginfo->msgnum,
430                                        IMAP_FLAG_DELETED, TRUE);
431                 imap_cmd_expunge(SESSION(session)->sock);
432         }
433
434         return IMAP_SUCCESS;
435 }
436
437 static gint imap_do_copy_msgs_with_dest(Folder *folder, FolderItem *dest,
438                                         GSList *msglist,
439                                         gboolean remove_source)
440 {
441         gchar *destdir;
442         GSList *cur;
443         MsgInfo *msginfo;
444         IMAPSession *session;
445
446         g_return_val_if_fail(folder != NULL, -1);
447         g_return_val_if_fail(dest != NULL, -1);
448         g_return_val_if_fail(msglist != NULL, -1);
449
450         session = imap_session_connect_if_not(folder);
451         if (!session) return -1;
452
453         Xstrdup_a(destdir, dest->path, return -1);
454         /* imap_path_subst_slash_to_dot(destdir); */
455
456         for (cur = msglist; cur != NULL; cur = cur->next) {
457                 msginfo = (MsgInfo *)cur->data;
458
459                 if (msginfo->folder == dest) {
460                         g_warning(_("the src folder is identical to the dest.\n"));
461                         continue;
462                 }
463
464                 debug_print(_("%s message %s%c%d to %s ...\n"),
465                             remove_source ? "Moving" : "Copying",
466                             msginfo->folder->path, G_DIR_SEPARATOR,
467                             msginfo->msgnum, destdir);
468
469                 imap_cmd_copy(SESSION(session)->sock, msginfo->msgnum,
470                               destdir);
471
472                 if (remove_source) {
473                         imap_set_article_flags
474                                 (session, msginfo->msgnum, msginfo->msgnum,
475                                  IMAP_FLAG_DELETED, TRUE);
476                 }
477         }
478
479         if (remove_source)
480                 imap_cmd_expunge(SESSION(session)->sock);
481
482         return IMAP_SUCCESS;
483
484 }
485
486 gint imap_move_msg(Folder *folder, FolderItem *dest, MsgInfo *msginfo)
487 {
488         return imap_do_copy(folder, dest, msginfo, TRUE);
489 }
490
491 gint imap_move_msgs_with_dest(Folder *folder, FolderItem *dest,
492                               GSList *msglist)
493 {
494         return imap_do_copy_msgs_with_dest(folder, dest, msglist, TRUE);
495 }
496
497 gint imap_copy_msg(Folder *folder, FolderItem *dest, MsgInfo *msginfo)
498 {
499         return imap_do_copy(folder, dest, msginfo, FALSE);
500 }
501
502 gint imap_copy_msgs_with_dest(Folder *folder, FolderItem *dest,
503                               GSList *msglist)
504 {
505         return imap_do_copy_msgs_with_dest(folder, dest, msglist, FALSE);
506 }
507
508 gint imap_remove_msg(Folder *folder, FolderItem *item, gint num)
509 {
510         gint exists, recent, unseen;
511         gulong uid;
512         gint ok;
513         IMAPSession *session;
514
515         g_return_val_if_fail(folder != NULL, -1);
516         g_return_val_if_fail(item != NULL, -1);
517         g_return_val_if_fail(num > 0 && num <= item->last_num, -1);
518
519         session = imap_session_connect_if_not(folder);
520         if (!session) return -1;
521
522         ok = imap_cmd_select(SESSION(session)->sock, item->path,
523                              &exists, &recent, &unseen, &uid);
524         if (ok != IMAP_SUCCESS) {
525                 log_warning(_("can't select folder: %s\n"), item->path);
526                 return ok;
527         }
528
529         ok = imap_set_article_flags(IMAP_SESSION(REMOTE_FOLDER(folder)->session),
530                                     num, num, IMAP_FLAG_DELETED, TRUE);
531         if (ok != IMAP_SUCCESS) {
532                 log_warning(_("can't set deleted flags: %d\n"), num);
533                 return ok;
534         }
535
536         ok = imap_cmd_expunge(SESSION(session)->sock);
537         if (ok != IMAP_SUCCESS) {
538                 log_warning(_("can't expunge\n"));
539                 return ok;
540         }
541
542         return IMAP_SUCCESS;
543 }
544
545 gint imap_remove_all_msg(Folder *folder, FolderItem *item)
546 {
547         gint exists, recent, unseen;
548         gulong uid;
549         gint ok;
550         IMAPSession *session;
551
552         g_return_val_if_fail(folder != NULL, -1);
553         g_return_val_if_fail(item != NULL, -1);
554
555         session = imap_session_connect_if_not(folder);
556         if (!session) return -1;
557
558         ok = imap_cmd_select(SESSION(session)->sock, item->path,
559                              &exists, &recent, &unseen, &uid);
560         if (ok != IMAP_SUCCESS) {
561                 log_warning(_("can't select folder: %s\n"), item->path);
562                 return ok;
563         }
564
565         ok = imap_set_article_flags(session, 1, exists,
566                                     IMAP_FLAG_DELETED, TRUE);
567         if (ok != IMAP_SUCCESS) {
568                 log_warning(_("can't set deleted flags: 1:%d\n"), exists);
569                 return ok;
570         }
571
572         ok = imap_cmd_expunge(SESSION(session)->sock);
573         if (ok != IMAP_SUCCESS) {
574                 log_warning(_("can't expunge\n"));
575                 return ok;
576         }
577
578         return IMAP_SUCCESS;
579 }
580
581 void imap_scan_folder(Folder *folder, FolderItem *item)
582 {
583 }
584
585 FolderItem *imap_create_folder(Folder *folder, FolderItem *parent,
586                                const gchar *name)
587 {
588         gchar *dirpath, *imappath;
589         IMAPSession *session;
590         FolderItem *new_item;
591         gint ok;
592
593         g_return_val_if_fail(folder != NULL, NULL);
594         g_return_val_if_fail(parent != NULL, NULL);
595         g_return_val_if_fail(name != NULL, NULL);
596
597         session = imap_session_connect_if_not(folder);
598         if (!session) return NULL;
599
600         if (parent->path)
601                 dirpath = g_strconcat(parent->path, G_DIR_SEPARATOR_S, name,
602                                       NULL);
603         else
604                 dirpath = g_strdup(name);
605
606         imappath = g_strdup(dirpath);
607         /* imap_path_subst_slash_to_dot(imappath); */
608
609         if (strcmp(name, "INBOX") != 0) {
610                 ok = imap_cmd_status(SESSION(session)->sock, imappath,
611                                      "MESSAGES");
612                 if (ok != IMAP_SUCCESS) {
613                         ok = imap_cmd_create(SESSION(session)->sock, imappath);
614                         statusbar_pop_all();
615                         if (ok != IMAP_SUCCESS) {
616                                 log_warning(_("can't create mailbox\n"));
617                                 g_free(imappath);
618                                 g_free(dirpath);
619                                 return NULL;
620                         }
621                 }
622                 statusbar_pop_all();
623         }
624
625         new_item = folder_item_new(name, dirpath);
626         folder_item_append(parent, new_item);
627         g_free(imappath);
628         g_free(dirpath);
629
630         return new_item;
631 }
632
633 gint imap_remove_folder(Folder *folder, FolderItem *item)
634 {
635         gint ok;
636         IMAPSession *session;
637
638         g_return_val_if_fail(folder != NULL, -1);
639         g_return_val_if_fail(item != NULL, -1);
640         g_return_val_if_fail(item->path != NULL, -1);
641
642         session = imap_session_connect_if_not(folder);
643         if (!session) return -1;
644
645         ok = imap_cmd_delete(SESSION(session)->sock, item->path);
646         statusbar_pop_all();
647         if (ok != IMAP_SUCCESS) {
648                 log_warning(_("can't delete mailbox\n"));
649                 return -1;
650         }
651
652         folder_item_remove(item);
653
654         return 0;
655 }
656
657 static GSList *imap_get_uncached_messages(IMAPSession *session,
658                                           FolderItem *item,
659                                           gint first, gint last)
660 {
661         gchar buf[IMAPBUFSIZE];
662         GSList *newlist = NULL;
663         GSList *llast = NULL;
664         MsgInfo *msginfo;
665
666         g_return_val_if_fail(session != NULL, NULL);
667         g_return_val_if_fail(item != NULL, NULL);
668         g_return_val_if_fail(item->folder != NULL, NULL);
669         g_return_val_if_fail(item->folder->type == F_IMAP, NULL);
670         g_return_val_if_fail(first <= last, NULL);
671
672         if (imap_cmd_envelope(SESSION(session)->sock, first, last)
673             != IMAP_SUCCESS) {
674                 log_warning(_("can't get envelope\n"));
675                 return NULL;
676         }
677
678         for (;;) {
679                 if (sock_gets(SESSION(session)->sock, buf, sizeof(buf)) < 0) {
680                         log_warning(_("error occurred while getting envelope.\n"));
681                         return newlist;
682                 }
683                 strretchomp(buf);
684                 if (buf[0] != '*' || buf[1] != ' ') break;
685
686                 msginfo = imap_parse_envelope(SESSION(session)->sock, buf);
687                 if (!msginfo) {
688                         log_warning(_("can't parse envelope: %s\n"), buf);
689                         continue;
690                 }
691
692                 msginfo->folder = item;
693
694                 if (!newlist)
695                         llast = newlist = g_slist_append(newlist, msginfo);
696                 else {
697                         llast = g_slist_append(llast, msginfo);
698                         llast = llast->next;
699                 }
700         }
701
702         return newlist;
703 }
704
705 static GSList *imap_delete_cached_messages(GSList *mlist,
706                                            gint first, gint last)
707 {
708         GSList *cur, *next;
709         MsgInfo *msginfo;
710         gchar *cache_file;
711
712         for (cur = mlist; cur != NULL; ) {
713                 next = cur->next;
714
715                 msginfo = (MsgInfo *)cur->data;
716                 if (msginfo != NULL && first <= msginfo->msgnum &&
717                     msginfo->msgnum <= last) {
718                         debug_print(_("deleting message %d...\n"),
719                                     msginfo->msgnum);
720
721                         cache_file = procmsg_get_message_file_path(msginfo);
722                         if (is_file_exist(cache_file)) unlink(cache_file);
723                         g_free(cache_file);
724
725                         procmsg_msginfo_free(msginfo);
726                         mlist = g_slist_remove(mlist, msginfo);
727                 }
728
729                 cur = next;
730         }
731
732         return mlist;
733 }
734
735 static void imap_delete_all_cached_messages(FolderItem *item)
736 {
737         gchar *dir;
738
739         g_return_if_fail(item != NULL);
740         g_return_if_fail(item->folder != NULL);
741         g_return_if_fail(item->folder->type == F_IMAP);
742
743         debug_print(_("\tDeleting all cached messages... "));
744
745         dir = folder_item_get_path(item);
746         remove_all_numbered_files(dir);
747         g_free(dir);
748
749         debug_print(_("done.\n"));
750 }
751
752 static SockInfo *imap_open(const gchar *server, gushort port, gchar *buf)
753 {
754         SockInfo *sock;
755
756         if ((sock = sock_connect(server, port)) == NULL) {
757                 log_warning(_("Can't connect to IMAP4 server: %s:%d\n"),
758                             server, port);
759                 return NULL;
760         }
761
762         imap_cmd_count = 0;
763
764         if (imap_cmd_noop(sock) != IMAP_SUCCESS) {
765                 sock_close(sock);
766                 return NULL;
767         }
768
769         return sock;
770 }
771
772 static gchar *imap_parse_atom(SockInfo *sock, gchar *src, gchar *dest,
773                               gchar *orig_buf)
774 {
775         gchar *cur_pos = src;
776
777         while (*cur_pos == ' ') cur_pos++;
778
779         if (!strncmp(cur_pos, "NIL", 3)) {
780                 *dest = '\0';
781                 cur_pos += 3;
782         } else if (*cur_pos == '\"') {
783                 gchar *p;
784
785                 p = strchr_cpy(cur_pos + 1, '\"', dest, IMAPBUFSIZE);
786                 cur_pos = p ? p : cur_pos + 2;
787         } else if (*cur_pos == '{') {
788                 gchar buf[32];
789                 gint len;
790
791                 cur_pos = strchr_cpy(cur_pos + 1, '}', buf, sizeof(buf));
792                 len = atoi(buf);
793
794                 g_return_val_if_fail(orig_buf != NULL, cur_pos);
795
796                 if (sock_gets(sock, orig_buf, IMAPBUFSIZE) < 0)
797                         return cur_pos;
798                 strretchomp(orig_buf);
799                 log_print("IMAP4< %s\n", orig_buf);
800                 memcpy(dest, orig_buf, len);
801                 dest[len] = '\0';
802                 cur_pos = orig_buf + len;
803         }
804
805         return cur_pos;
806 }
807
808 static gchar *imap_parse_one_address(SockInfo *sock, gchar *start,
809                                      gchar *out_from_str,
810                                      gchar *out_fromname_str,
811                                      gchar *orig_buf)
812 {
813         gchar buf[IMAPBUFSIZE];
814         gchar *cur_pos = start;
815
816         cur_pos = imap_parse_atom(sock, cur_pos, buf, orig_buf);
817         conv_unmime_header(out_fromname_str, 256, buf, NULL);
818
819         if (out_fromname_str[0] != '\0') {
820                 strcat(out_from_str, "\"");
821                 strcat(out_from_str, out_fromname_str);
822                 strcat(out_from_str, "\"");
823         }
824
825         cur_pos = imap_parse_atom(sock, cur_pos, buf, orig_buf);
826
827         strcat(out_from_str, " <");
828
829         cur_pos = imap_parse_atom(sock, cur_pos, buf, orig_buf);
830         strcat(out_from_str, buf);
831
832         cur_pos = imap_parse_atom(sock, cur_pos, buf, orig_buf);
833         strcat(out_from_str, "@");
834         strcat(out_from_str, buf);
835         strcat(out_from_str, ">");
836
837         while (*cur_pos == ' ') cur_pos++;
838
839         g_return_val_if_fail(*cur_pos == ')', cur_pos + 1);
840
841         return cur_pos + 1;
842 }
843
844 static gchar *imap_parse_address(SockInfo *sock, gchar *start,
845                                  gchar **out_from_str,
846                                  gchar **out_fromname_str,
847                                  gchar *orig_buf)
848 {
849         gchar buf[IMAPBUFSIZE];
850         gchar name_buf[IMAPBUFSIZE];
851         gchar *cur_pos = start;
852         gboolean first = TRUE;
853
854         if (out_from_str)     *out_from_str     = NULL;
855         if (out_fromname_str) *out_fromname_str = NULL;
856         buf[0] = name_buf[0] = '\0';
857
858         if (!strncmp(cur_pos, "NIL", 3)) {
859                 if (out_from_str)     *out_from_str     = g_strdup("");
860                 if (out_fromname_str) *out_fromname_str = g_strdup("");
861                 return cur_pos + 3;
862         }
863
864         g_return_val_if_fail(*cur_pos == '(', NULL);
865         cur_pos++;
866
867         for (;;) {
868                 gchar ch = *cur_pos++;
869                 if (ch == ')') break;
870                 if (ch == '(') {
871                         if (!first) strcat(buf, ", ");
872                         first = FALSE;
873                         cur_pos = imap_parse_one_address
874                                 (sock, cur_pos, buf, name_buf, orig_buf);
875                         if (!cur_pos) return NULL;
876                 }
877         }
878
879         if (out_from_str)     *out_from_str     = g_strdup(buf);
880         if (out_fromname_str) *out_fromname_str = g_strdup(name_buf);
881
882         return cur_pos;
883 }
884
885 static MsgFlags imap_parse_flags(const gchar *flag_str)  
886 {
887         gchar buf[32];
888         const gchar *cur_pos = flag_str;
889         const gchar *last_pos;
890         MsgFlags flags;
891
892         flags = 0;
893         MSG_SET_FLAGS(flags, MSG_UNREAD|MSG_IMAP);
894
895         while (cur_pos != NULL) {
896                 cur_pos = strchr(cur_pos, '\\');
897                 if (cur_pos == NULL) break;
898
899                 last_pos = cur_pos + 1;
900                 cur_pos = strchr_cpy(last_pos, ' ', buf, sizeof(buf));
901                 if (cur_pos == NULL)
902                         strncpy2(buf, last_pos, sizeof(buf));
903
904                 if (g_strcasecmp(buf, "Recent") == 0) {
905                         MSG_SET_FLAGS(flags, MSG_NEW|MSG_UNREAD);
906                 } else if (g_strcasecmp(buf, "Seen") == 0) {
907                         MSG_UNSET_FLAGS(flags, MSG_NEW|MSG_UNREAD);
908                 } else if (g_strcasecmp(buf, "Deleted") == 0) {
909                         MSG_SET_FLAGS(flags, MSG_DELETED);
910                 } else if (g_strcasecmp(buf, "Flagged") == 0) {
911                         MSG_SET_FLAGS(flags, MSG_MARKED);
912                 }
913         }
914
915         return flags;
916 }
917
918 static MsgInfo *imap_parse_envelope(SockInfo *sock, gchar *line_str)
919 {
920         MsgInfo *msginfo;
921         gchar buf[IMAPBUFSIZE];
922         gchar tmp[IMAPBUFSIZE];
923         gchar *cur_pos;
924         gint msgnum;
925         size_t size;
926         gchar *date = NULL;
927         time_t date_t;
928         gchar *subject = NULL;
929         gchar *tmp_from;
930         gchar *tmp_fromname;
931         gchar *from = NULL;
932         gchar *fromname = NULL;
933         gchar *tmp_to;
934         gchar *to = NULL;
935         gchar *inreplyto = NULL;
936         gchar *msgid = NULL;
937         MsgFlags flags;
938
939         log_print("IMAP4< %s\n", line_str);
940
941         g_return_val_if_fail(line_str != NULL, NULL);
942         g_return_val_if_fail(line_str[0] == '*' && line_str[1] == ' ', NULL);
943
944         cur_pos = line_str + 2;
945
946 #define PARSE_ONE_ELEMENT(ch) \
947 { \
948         cur_pos = strchr_cpy(cur_pos, ch, buf, sizeof(buf)); \
949         g_return_val_if_fail(cur_pos != NULL, NULL); \
950 }
951
952         PARSE_ONE_ELEMENT(' ');
953         msgnum = atoi(buf);
954
955         PARSE_ONE_ELEMENT(' ');
956         g_return_val_if_fail(!strcmp(buf, "FETCH"), NULL);
957
958         PARSE_ONE_ELEMENT(' ');
959         g_return_val_if_fail(!strcmp(buf, "(FLAGS"), NULL);
960
961         PARSE_ONE_ELEMENT(')');
962         g_return_val_if_fail(*buf == '(', NULL);
963         flags = imap_parse_flags(buf + 1);
964
965         g_return_val_if_fail(*cur_pos == ' ', NULL);
966         g_return_val_if_fail
967                 ((cur_pos = strchr_cpy(cur_pos + 1, ' ', buf, sizeof(buf))),
968                  NULL);
969         g_return_val_if_fail(!strcmp(buf, "RFC822.SIZE"), NULL);
970
971         PARSE_ONE_ELEMENT(' ');
972         size = atoi(buf);
973
974         PARSE_ONE_ELEMENT(' ');
975         g_return_val_if_fail(!strcmp(buf, "ENVELOPE"), NULL);
976
977         g_return_val_if_fail(*cur_pos == '(', NULL);
978         cur_pos = imap_parse_atom(sock, cur_pos + 1, buf, line_str);
979         Xstrdup_a(date, buf, return NULL);
980         date_t = procheader_date_parse(NULL, date, 0);
981
982         cur_pos = imap_parse_atom(sock, cur_pos, buf, line_str);
983         if (buf[0] != '\0') {
984                 conv_unmime_header(tmp, sizeof(tmp), buf, NULL);
985                 Xstrdup_a(subject, tmp, return NULL);
986         }
987
988         g_return_val_if_fail(*cur_pos == ' ', NULL);
989         cur_pos = imap_parse_address(sock, cur_pos + 1,
990                                      &tmp_from, &tmp_fromname, line_str);
991         Xstrdup_a(from, tmp_from,
992                   {g_free(tmp_from); g_free(tmp_fromname); return NULL;});
993         Xstrdup_a(fromname, tmp_fromname,
994                   {g_free(tmp_from); g_free(tmp_fromname); return NULL;});
995         g_free(tmp_from);
996         g_free(tmp_fromname);
997
998 #define SKIP_ONE_ELEMENT() \
999 { \
1000         g_return_val_if_fail(*cur_pos == ' ', NULL); \
1001         cur_pos = imap_parse_address(sock, cur_pos + 1, \
1002                                      NULL, NULL, line_str); \
1003 }
1004
1005         /* skip sender and reply-to */
1006         SKIP_ONE_ELEMENT();
1007         SKIP_ONE_ELEMENT();
1008
1009         g_return_val_if_fail(*cur_pos == ' ', NULL);
1010         cur_pos = imap_parse_address(sock, cur_pos + 1, &tmp_to, NULL, line_str);
1011         Xstrdup_a(to, tmp_to, {g_free(tmp_to); return NULL;});
1012         g_free(tmp_to);
1013
1014         /* skip Cc and Bcc */
1015         SKIP_ONE_ELEMENT();
1016         SKIP_ONE_ELEMENT();
1017
1018 #undef SKIP_ONE_ELEMENT
1019
1020         g_return_val_if_fail(*cur_pos == ' ', NULL);
1021         cur_pos = imap_parse_atom(sock, cur_pos, buf, line_str);
1022         if (buf[0] != '\0') {
1023                 eliminate_parenthesis(buf, '(', ')');
1024                 extract_parenthesis(buf, '<', '>');
1025                 remove_space(buf);
1026                 Xstrdup_a(inreplyto, buf, return NULL);
1027         }
1028
1029         g_return_val_if_fail(*cur_pos == ' ', NULL);
1030         cur_pos = imap_parse_atom(sock, cur_pos, buf, line_str);
1031         if (buf[0] != '\0') {
1032                 extract_parenthesis(buf, '<', '>');
1033                 remove_space(buf);
1034                 Xstrdup_a(msgid, buf, return NULL);
1035         }
1036
1037         msginfo = g_new0(MsgInfo, 1);
1038         msginfo->msgnum = msgnum;
1039         msginfo->size = size;
1040         msginfo->date = g_strdup(date);
1041         msginfo->date_t = date_t;
1042         msginfo->subject = g_strdup(subject);
1043         msginfo->from = g_strdup(from);
1044         msginfo->fromname = g_strdup(fromname);
1045         msginfo->to = g_strdup(to);
1046         msginfo->inreplyto = g_strdup(inreplyto);
1047         msginfo->msgid = g_strdup(msgid);
1048         msginfo->flags = flags;
1049
1050         return msginfo;
1051 }
1052
1053 static gint imap_set_article_flags(IMAPSession *session,
1054                                    gint first,
1055                                    gint last,
1056                                    IMAPFlags flags,
1057                                    gboolean is_set)
1058 {
1059         GString *buf;
1060         gint ok;
1061
1062         buf = g_string_new(is_set ? "+FLAGS (" : "-FLAGS (");
1063
1064         if (IMAP_IS_SEEN(flags))        g_string_append(buf, "\\Seen ");
1065         if (IMAP_IS_ANSWERED(flags))    g_string_append(buf, "\\Answered ");
1066         if (IMAP_IS_FLAGGED(flags))     g_string_append(buf, "\\Flagged ");
1067         if (IMAP_IS_DELETED(flags))     g_string_append(buf, "\\Deleted ");
1068         if (IMAP_IS_DRAFT(flags))       g_string_append(buf, "\\Draft");
1069
1070         if (buf->str[buf->len - 1] == ' ')
1071                 g_string_truncate(buf, buf->len - 1);
1072
1073         g_string_append_c(buf, ')');
1074
1075         ok = imap_cmd_store(SESSION(session)->sock, first, last, buf->str);
1076         g_string_free(buf, TRUE);
1077
1078         return ok;
1079 }
1080
1081
1082
1083 static gint imap_cmd_login(SockInfo *sock,
1084                            const gchar *user, const gchar *pass)
1085 {
1086         gint ok;
1087
1088         imap_cmd_gen_send(sock, "LOGIN \"%s\" %s", user, pass);
1089         ok = imap_cmd_ok(sock, NULL);
1090         if (ok != IMAP_SUCCESS)
1091                 log_warning(_("IMAP4 login failed.\n"));
1092
1093         return ok;
1094 }
1095
1096 static gint imap_cmd_logout(SockInfo *sock)
1097 {
1098         imap_cmd_gen_send(sock, "LOGOUT");
1099         return imap_cmd_ok(sock, NULL);
1100 }
1101
1102 static gint imap_cmd_noop(SockInfo *sock)
1103 {
1104         imap_cmd_gen_send(sock, "NOOP");
1105         return imap_cmd_ok(sock, NULL);
1106 }
1107
1108 static gint imap_cmd_select(SockInfo *sock, const gchar *folder,
1109                             gint *exists, gint *recent, gint *unseen,
1110                             gulong *uid)
1111 {
1112         gint ok;
1113         gchar *resp_str;
1114         GPtrArray *argbuf;
1115         gchar *imappath;
1116
1117         *exists = *recent = *unseen = *uid = 0;
1118         argbuf = g_ptr_array_new();
1119
1120         Xstrdup_a(imappath, folder, return -1);
1121         /* imap_path_subst_slash_to_dot(imappath); */
1122
1123         imap_cmd_gen_send(sock, "SELECT \"%s\"", imappath);
1124         if ((ok = imap_cmd_ok(sock, argbuf)) != IMAP_SUCCESS)
1125                 goto bail;
1126
1127         resp_str = search_array_contain_str(argbuf, "EXISTS");
1128         if (resp_str) {
1129                 if (sscanf(resp_str,"%d EXISTS", exists) != 1) {
1130                         g_warning("imap_select(): invalid EXISTS line.\n");
1131                         goto bail;
1132                 }
1133         }
1134
1135         resp_str = search_array_contain_str(argbuf, "RECENT");
1136         if (resp_str) {
1137                 if (sscanf(resp_str, "%d RECENT", recent) != 1) {
1138                         g_warning("imap_select(): invalid RECENT line.\n");
1139                         goto bail;
1140                 }
1141         }
1142
1143         resp_str = search_array_contain_str(argbuf, "UIDVALIDITY");
1144         if (resp_str) {
1145                 if (sscanf(resp_str, "OK [UIDVALIDITY %lu] ", uid) != 1) {
1146                         g_warning("imap_select(): invalid UIDVALIDITY line.\n");
1147                         goto bail;
1148                 }
1149         }
1150
1151         resp_str = search_array_contain_str(argbuf, "UNSEEN");
1152         if (resp_str) {
1153                 if (sscanf(resp_str, "OK [UNSEEN %d] ", unseen) != 1) {
1154                         g_warning("imap_select(): invalid UNSEEN line.\n");
1155                         goto bail;
1156                 }
1157         }
1158
1159 bail:
1160         ptr_array_free_strings(argbuf);
1161         g_ptr_array_free(argbuf, TRUE);
1162
1163         return ok;
1164 }
1165
1166 static gint imap_cmd_status(SockInfo *sock, const gchar *folder,
1167                             const gchar *status)
1168 {
1169         imap_cmd_gen_send(sock, "STATUS \"%s\" (%s)", folder, status);
1170         return imap_cmd_ok(sock, NULL);
1171 }
1172
1173 static gint imap_cmd_create(SockInfo *sock, const gchar *folder)
1174 {
1175         imap_cmd_gen_send(sock, "CREATE \"%s\"", folder);
1176         return imap_cmd_ok(sock, NULL);
1177 }
1178
1179 static gint imap_cmd_delete(SockInfo *sock, const gchar *folder)
1180 {
1181         imap_cmd_gen_send(sock, "DELETE \"%s\"", folder);
1182         return imap_cmd_ok(sock, NULL);
1183 }
1184
1185 static gint imap_cmd_fetch(SockInfo *sock, gint num, const gchar *filename)
1186 {
1187         gint ok;
1188         gchar buf[IMAPBUFSIZE];
1189         gchar *cur_pos;
1190         gchar size_str[32];
1191         glong size_num;
1192
1193         g_return_val_if_fail(filename != NULL, IMAP_ERROR);
1194
1195         imap_cmd_gen_send(sock, "FETCH %d BODY[]", num);
1196
1197         if (sock_gets(sock, buf, sizeof(buf)) < 0)
1198                 return IMAP_ERROR;
1199         strretchomp(buf);
1200         if (buf[0] != '*' || buf[1] != ' ')
1201                 return IMAP_ERROR;
1202         log_print("IMAP4< %s\n", buf);
1203
1204         cur_pos = strchr(buf, '{');
1205         g_return_val_if_fail(cur_pos != NULL, IMAP_ERROR);
1206         cur_pos = strchr_cpy(cur_pos + 1, '}', size_str, sizeof(size_str));
1207         g_return_val_if_fail(cur_pos != NULL, IMAP_ERROR);
1208         size_num = atol(size_str);
1209
1210         if (*cur_pos != '\0') return IMAP_ERROR;
1211
1212         if (recv_bytes_write_to_file(sock, size_num, filename) != 0)
1213                 return IMAP_ERROR;
1214
1215         if (imap_cmd_gen_recv(sock, buf, sizeof(buf)) != IMAP_SUCCESS)
1216                 return IMAP_ERROR;
1217
1218         if (buf[0] != ')')
1219                 return IMAP_ERROR;
1220
1221         ok = imap_cmd_ok(sock, NULL);
1222
1223         return ok;
1224 }
1225
1226 static gint imap_cmd_append(SockInfo *sock, const gchar *destfolder,
1227                             const gchar *file)
1228 {
1229         gint ok;
1230         gint size;
1231
1232         g_return_val_if_fail(file != NULL, IMAP_ERROR);
1233
1234         imap_cmd_gen_send(sock, "APPEND %s {%d}", destfolder, size);
1235         ok = imap_cmd_ok(sock, NULL);
1236         if (ok != IMAP_SUCCESS) {
1237                 log_warning(_("can't append %s to %s\n"), file, destfolder);
1238                 return -1;
1239         }
1240
1241         return ok;
1242 }
1243
1244 static gint imap_cmd_copy(SockInfo *sock, gint num, const gchar *destfolder)
1245 {
1246         gint ok;
1247
1248         g_return_val_if_fail(destfolder != NULL, IMAP_ERROR);
1249
1250         imap_cmd_gen_send(sock, "COPY %d %s", num, destfolder);
1251         ok = imap_cmd_ok(sock, NULL);
1252         if (ok != IMAP_SUCCESS) {
1253                 log_warning(_("can't copy %d to %s\n"), num, destfolder);
1254                 return -1;
1255         }
1256
1257         return ok;
1258 }
1259
1260
1261 gint imap_cmd_envelope(SockInfo *sock, gint first, gint last)
1262 {
1263         imap_cmd_gen_send(sock, "FETCH %d:%d (FLAGS RFC822.SIZE ENVELOPE)",
1264                           first, last);
1265
1266         return IMAP_SUCCESS;
1267 }
1268
1269 static gint imap_cmd_store(SockInfo *sock, gint first, gint last,
1270                            gchar *sub_cmd)
1271 {
1272         gint ok;
1273
1274         imap_cmd_gen_send(sock, "STORE %d:%d %s", first, last, sub_cmd);
1275
1276         if ((ok = imap_cmd_ok(sock, NULL)) != IMAP_SUCCESS) {
1277                 log_warning(_("error while imap command: STORE %d:%d %s\n"),
1278                             first, last, sub_cmd);
1279                 return ok;
1280         }
1281
1282         return IMAP_SUCCESS;
1283 }
1284
1285 static gint imap_cmd_expunge(SockInfo *sock)
1286 {
1287         gint ok;
1288
1289         imap_cmd_gen_send(sock, "EXPUNGE");
1290         if ((ok = imap_cmd_ok(sock, NULL)) != IMAP_SUCCESS) {
1291                 log_warning(_("error while imap command: EXPUNGE\n"));
1292                 return ok;
1293         }
1294
1295         return IMAP_SUCCESS;
1296 }
1297
1298 static gint imap_cmd_ok(SockInfo *sock, GPtrArray *argbuf)
1299 {
1300         gint ok;
1301         gchar buf[IMAPBUFSIZE];
1302         gint cmd_num;
1303         gchar cmd_status[IMAPBUFSIZE];
1304
1305         while ((ok = imap_cmd_gen_recv(sock, buf, sizeof(buf)))
1306                == IMAP_SUCCESS) {
1307                 if (buf[0] == '*' && buf[1] == ' ') {
1308                         if (argbuf)
1309                                 g_ptr_array_add(argbuf, g_strdup(&buf[2]));
1310                         continue;
1311                 }
1312
1313                 if (sscanf(buf, "%d %s", &cmd_num, cmd_status) < 2)
1314                         return IMAP_ERROR;
1315                 else if (cmd_num == imap_cmd_count &&
1316                          !strcmp(cmd_status, "OK")) {
1317                         if (argbuf)
1318                                 g_ptr_array_add(argbuf, g_strdup(buf));
1319                         return IMAP_SUCCESS;
1320                 } else
1321                         return IMAP_ERROR;
1322         }
1323
1324         return ok;
1325 }
1326
1327 static void imap_cmd_gen_send(SockInfo *sock, const gchar *format, ...)
1328 {
1329         gchar buf[IMAPBUFSIZE];
1330         gchar tmp[IMAPBUFSIZE];
1331         gchar *p;
1332         va_list args;
1333
1334         va_start(args, format);
1335         g_vsnprintf(tmp, sizeof(tmp), format, args);
1336         va_end(args);
1337
1338         imap_cmd_count++;
1339
1340         g_snprintf(buf, sizeof(buf), "%d %s\r\n", imap_cmd_count, tmp);
1341         if (!strncasecmp(tmp, "LOGIN ", 6) && (p = strchr(tmp + 6, ' '))) {
1342                 *(p + 1) = '\0';
1343                 log_print("IMAP4> %d %s ********\n", imap_cmd_count, tmp);
1344         } else
1345                 log_print("IMAP4> %d %s\n", imap_cmd_count, tmp);
1346
1347         sock_write(sock, buf, strlen(buf));
1348 }
1349
1350 static gint imap_cmd_gen_recv(SockInfo *sock, gchar *buf, gint size)
1351 {
1352         if (sock_gets(sock, buf, size) == -1)
1353                 return IMAP_SOCKET;
1354
1355         strretchomp(buf);
1356
1357         log_print("IMAP4< %s\n", buf);
1358
1359         return IMAP_SUCCESS;
1360 }
1361
1362
1363 /* misc utility functions */
1364
1365 static gchar *strchr_cpy(const gchar *src, gchar ch, gchar *dest, gint len)
1366 {
1367         gchar *tmp;
1368
1369         dest[0] = '\0';
1370         tmp = strchr(src, ch);
1371         if (!tmp || tmp == src)
1372                 return NULL;
1373
1374         memcpy(dest, src, MIN(tmp - src, len - 1));
1375         dest[MIN(tmp - src, len - 1)] = '\0';
1376
1377         return tmp + 1;
1378 }
1379
1380 static gchar *search_array_contain_str(GPtrArray *array, gchar *str)
1381 {
1382         gint i;
1383
1384         for (i = 0; i < array->len; i++) {
1385                 gchar *tmp;
1386
1387                 tmp = g_ptr_array_index(array, i);
1388                 if (strstr(tmp, str) != NULL)
1389                         return tmp;
1390         }
1391
1392         return NULL;
1393 }
1394
1395 static void imap_path_subst_slash_to_dot(gchar *str)
1396 {
1397         subst_char(str, '/', '.');
1398 }