2 * Sylpheed -- a GTK+ based, lightweight, and fast e-mail client
3 * Copyright (C) 1999-2003 Hiroyuki Yamamoto
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.
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.
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.
44 #include "procheader.h"
46 #include "prefs_account.h"
49 #include "inputdialog.h"
52 #define IMAP4_PORT 143
54 #define IMAPS_PORT 993
57 #define QUOTE_IF_REQUIRED(out, str) \
59 if (*str != '"' && strpbrk(str, " \t(){}%*") != NULL) { \
63 len = strlen(str) + 3; \
64 Xalloca(__tmp, len, return IMAP_ERROR); \
65 g_snprintf(__tmp, len, "\"%s\"", str); \
68 Xstrdup_a(out, str, return IMAP_ERROR); \
72 typedef gchar * IMAPSet;
74 struct _IMAPFolderItem
83 static Folder *imap_folder_new(const gchar * name, const gchar * path);
84 static void imap_folder_destroy(Folder * folder);
86 static Session *imap_session_new(const PrefsAccount * account);
87 static void imap_session_authenticate(IMAPSession * session,
88 const PrefsAccount * account);
89 static void imap_session_destroy(Session * session);
91 static gchar *imap_fetch_msg(Folder * folder, FolderItem * item, gint uid);
92 static gint imap_add_msg(Folder * folder,
94 const gchar * file, gboolean remove_source);
96 static gint imap_copy_msg(Folder * folder,
97 FolderItem * dest, MsgInfo * msginfo);
99 static gint imap_remove_msg(Folder * folder, FolderItem * item, gint uid);
100 static gint imap_remove_all_msg(Folder * folder, FolderItem * item);
102 static gboolean imap_is_msg_changed(Folder * folder,
103 FolderItem * item, MsgInfo * msginfo);
105 static gint imap_scan_folder(Folder * folder, FolderItem * item);
106 static void imap_scan_tree(Folder * folder);
108 static gint imap_create_tree(Folder * folder);
110 static FolderItem *imap_create_folder(Folder * folder,
113 static gint imap_rename_folder(Folder * folder,
114 FolderItem * item, const gchar * name);
115 static gint imap_remove_folder(Folder * folder, FolderItem * item);
118 static gint imap_cmd_count = 0;
120 static void imap_folder_init (Folder *folder,
124 static FolderItem *imap_folder_item_new (Folder *folder);
125 static void imap_folder_item_destroy (Folder *folder,
128 static IMAPSession *imap_session_get (Folder *folder);
130 static gint imap_scan_tree_recursive (IMAPSession *session,
132 static GSList *imap_parse_list (Folder *folder,
133 IMAPSession *session,
134 const gchar *real_path,
137 static void imap_create_missing_folders (Folder *folder);
138 static FolderItem *imap_create_special_folder
140 SpecialFolderItemType stype,
143 static gint imap_do_copy (Folder *folder,
146 gboolean remove_source);
148 static void imap_delete_all_cached_messages (FolderItem *item);
151 static SockInfo *imap_open (const gchar *server,
155 static SockInfo *imap_open (const gchar *server,
160 static SockInfo *imap_open_tunnel(const gchar *server,
161 const gchar *tunnelcmd,
164 static SockInfo *imap_open_tunnel(const gchar *server,
165 const gchar *tunnelcmd);
169 static SockInfo *imap_init_sock(SockInfo *sock, SSLType ssl_type);
171 static SockInfo *imap_init_sock(SockInfo *sock);
174 static gint imap_set_message_flags (IMAPSession *session,
175 MsgNumberList *numlist,
178 static gint imap_select (IMAPSession *session,
184 guint32 *uid_validity);
185 static gint imap_status (IMAPSession *session,
191 guint32 *uid_validity,
194 static void imap_parse_namespace (IMAPSession *session,
196 static void imap_get_namespace_by_list (IMAPSession *session,
198 static IMAPNameSpace *imap_find_namespace (IMAPFolder *folder,
200 static gchar imap_get_path_separator (IMAPFolder *folder,
202 static gchar *imap_get_real_path (IMAPFolder *folder,
205 static gchar *imap_parse_atom (SockInfo *sock,
210 static MsgFlags imap_parse_flags (const gchar *flag_str);
211 static MsgInfo *imap_parse_envelope (SockInfo *sock,
214 static gint imap_greeting (SockInfo *sock,
215 gboolean *is_preauth);
216 static void imap_get_capability (Session *session);
217 static gboolean imap_has_capability (IMAPSession *session,
219 static const IMAPSet numberlist_to_imapset(MsgNumberList *list);
221 /* low-level IMAP4rev1 commands */
222 static gint imap_cmd_login (SockInfo *sock,
225 static gint imap_cmd_logout (SockInfo *sock);
226 static gint imap_cmd_noop (SockInfo *sock);
227 static gint imap_cmd_starttls (SockInfo *sock);
228 static gint imap_cmd_namespace (SockInfo *sock,
230 static gint imap_cmd_list (SockInfo *sock,
232 const gchar *mailbox,
234 static gint imap_cmd_do_select (SockInfo *sock,
240 guint32 *uid_validity);
241 static gint imap_cmd_select (SockInfo *sock,
246 guint32 *uid_validity);
247 static gint imap_cmd_examine (SockInfo *sock,
252 guint32 *uid_validity);
253 static gint imap_cmd_create (SockInfo *sock,
254 const gchar *folder);
255 static gint imap_cmd_rename (SockInfo *sock,
256 const gchar *oldfolder,
257 const gchar *newfolder);
258 static gint imap_cmd_delete (SockInfo *sock,
259 const gchar *folder);
260 static gint imap_cmd_envelope (SockInfo *sock,
262 static gint imap_cmd_fetch (SockInfo *sock,
264 const gchar *filename);
265 static gint imap_cmd_append (IMAPSession *session,
266 const gchar *destfolder,
269 static gint imap_cmd_copy (IMAPSession *session,
271 const gchar *destfolder,
273 static gint imap_cmd_store (SockInfo *sock,
276 static gint imap_cmd_expunge (SockInfo *sock);
278 static gint imap_cmd_ok (SockInfo *sock,
280 static void imap_cmd_gen_send (SockInfo *sock,
281 const gchar *format, ...);
282 static gint imap_cmd_gen_recv (SockInfo *sock,
285 /* misc utility functions */
286 static gchar *strchr_cpy (const gchar *src,
290 static gchar *get_quoted (const gchar *src,
294 static gchar *search_array_contain_str (GPtrArray *array,
296 static gchar *search_array_str (GPtrArray *array,
298 static void imap_path_separator_subst (gchar *str,
301 static gchar *imap_modified_utf7_to_locale (const gchar *mutf7_str);
302 static gchar *imap_locale_to_modified_utf7 (const gchar *from);
304 static gboolean imap_rename_folder_func (GNode *node,
306 static gint imap_get_num_list (Folder *folder,
309 static GSList *imap_get_msginfos (Folder *folder,
311 GSList *msgnum_list);
312 static MsgInfo *imap_get_msginfo (Folder *folder,
315 static gboolean imap_check_msgnum_validity (Folder *folder,
317 static void imap_change_flags (Folder *folder,
320 MsgPermFlags newflags);
322 FolderClass imap_class =
328 /* Folder functions */
334 /* FolderItem functions */
335 imap_folder_item_new,
336 imap_folder_item_destroy,
344 imap_check_msgnum_validity,
346 /* Message functions */
358 FolderClass *imap_get_class()
363 Folder *imap_folder_new(const gchar *name, const gchar *path)
367 folder = (Folder *)g_new0(IMAPFolder, 1);
368 folder->klass = &imap_class;
369 imap_folder_init(folder, name, path);
374 void imap_folder_destroy(Folder *folder)
378 dir = folder_get_path(folder);
379 if (is_dir_exist(dir))
380 remove_dir_recursive(dir);
383 folder_remote_folder_destroy(REMOTE_FOLDER(folder));
386 static void imap_folder_init(Folder *folder, const gchar *name,
389 folder_remote_folder_init((Folder *)folder, name, path);
392 static FolderItem *imap_folder_item_new(Folder *folder)
394 IMAPFolderItem *item;
396 item = g_new0(IMAPFolderItem, 1);
399 item->uid_list = NULL;
401 return (FolderItem *)item;
404 static void imap_folder_item_destroy(Folder *folder, FolderItem *_item)
406 IMAPFolderItem *item = (IMAPFolderItem *)_item;
408 g_return_if_fail(item != NULL);
409 g_slist_free(item->uid_list);
414 static gboolean imap_reset_uid_lists_func(GNode *node, gpointer data)
416 IMAPFolderItem *item = (IMAPFolderItem *)node->data;
420 g_slist_free(item->uid_list);
421 item->uid_list = NULL;
426 static void imap_reset_uid_lists(Folder *folder)
428 if(folder->node == NULL)
431 /* Destroy all uid lists and rest last uid */
432 g_node_traverse(folder->node, G_IN_ORDER, G_TRAVERSE_ALL, -1, imap_reset_uid_lists_func, NULL);
435 static IMAPSession *imap_session_get(Folder *folder)
437 RemoteFolder *rfolder = REMOTE_FOLDER(folder);
438 Session *session = NULL;
441 g_return_val_if_fail(folder != NULL, NULL);
442 g_return_val_if_fail(FOLDER_CLASS(folder) == &imap_class, NULL);
443 g_return_val_if_fail(folder->account != NULL, NULL);
446 port = folder->account->set_imapport ? folder->account->imapport
447 : folder->account->ssl_imap == SSL_TUNNEL
448 ? IMAPS_PORT : IMAP4_PORT;
450 port = folder->account->set_imapport ? folder->account->imapport
454 /* Make sure we have a session */
455 if (rfolder->session != NULL) {
456 session = rfolder->session;
458 imap_reset_uid_lists(folder);
459 session = imap_session_new(folder->account);
464 if (session->sock->state == CONN_DISCONNECTED) {
465 debug_print("IMAP server disconnected\n");
466 session_destroy(session);
467 imap_reset_uid_lists(folder);
468 session = imap_session_new(folder->account);
472 imap_get_capability(session);
474 /* Make sure session is authenticated */
475 if (!IMAP_SESSION(session)->authenticated)
476 imap_session_authenticate(IMAP_SESSION(session), folder->account);
477 if (!IMAP_SESSION(session)->authenticated) {
478 session_destroy(session);
479 rfolder->session = NULL;
483 /* Make sure we have parsed the IMAP namespace */
484 imap_parse_namespace(IMAP_SESSION(session),
485 IMAP_FOLDER(folder));
487 /* I think the point of this code is to avoid sending a
488 * keepalive if we've used the session recently and therefore
489 * think it's still alive. Unfortunately, most of the code
490 * does not yet check for errors on the socket, and so if the
491 * connection drops we don't notice until the timeout expires.
492 * A better solution than sending a NOOP every time would be
493 * for every command to be prepared to retry until it is
494 * successfully sent. -- mbp */
495 if (time(NULL) - session->last_access_time > SESSION_TIMEOUT) {
496 /* verify that the session is still alive */
497 if (imap_cmd_noop(session->sock) != IMAP_SUCCESS) {
498 /* Check if this is the first try to establish a
499 connection, if yes we don't try to reconnect */
500 if (rfolder->session == NULL) {
501 log_warning(_("Connecting %s:%d failed"),
502 folder->account->recv_server, port);
503 session_destroy(session);
506 log_warning(_("IMAP4 connection to %s:%d has been"
507 " disconnected. Reconnecting...\n"),
508 folder->account->recv_server, port);
509 session_destroy(session);
510 /* Clear folders session to make imap_session_get create
511 a new session, because of rfolder->session == NULL
512 it will not try to reconnect again and so avoid an
514 rfolder->session = NULL;
515 session = SESSION(imap_session_get(folder));
520 rfolder->session = session;
522 session->last_access_time = time(NULL);
524 return IMAP_SESSION(session);
527 Session *imap_session_new(const PrefsAccount *account)
529 IMAPSession *session;
535 /* FIXME: IMAP over SSL only... */
538 port = account->set_imapport ? account->imapport
539 : account->ssl_imap == SSL_TUNNEL ? IMAPS_PORT : IMAP4_PORT;
540 ssl_type = account->ssl_imap;
542 port = account->set_imapport ? account->imapport
546 if (account->set_tunnelcmd) {
547 log_message(_("creating tunneled IMAP4 connection\n"));
549 if ((imap_sock = imap_open_tunnel(account->recv_server,
553 if ((imap_sock = imap_open_tunnel(account->recv_server,
554 account->tunnelcmd)) == NULL)
558 g_return_val_if_fail(account->recv_server != NULL, NULL);
560 log_message(_("creating IMAP4 connection to %s:%d ...\n"),
561 account->recv_server, port);
564 if ((imap_sock = imap_open(account->recv_server, port,
567 if ((imap_sock = imap_open(account->recv_server, port)) == NULL)
572 /* Only need to log in if the connection was not PREAUTH */
573 if (imap_greeting(imap_sock, &is_preauth) != IMAP_SUCCESS) {
574 sock_close(imap_sock);
577 log_message("IMAP connection is %s-authenticated\n",
578 (is_preauth) ? "pre" : "un");
580 session = g_new0(IMAPSession, 1);
581 session_init(SESSION(session));
582 SESSION(session)->type = SESSION_IMAP;
583 SESSION(session)->server = g_strdup(account->recv_server);
584 SESSION(session)->sock = imap_sock;
586 SESSION(session)->destroy = imap_session_destroy;
588 session->capability = NULL;
590 session->mbox = NULL;
591 session->authenticated = is_preauth;
593 return SESSION(session);
596 void imap_session_authenticate(IMAPSession *session, const PrefsAccount *account)
600 g_return_if_fail(account->userid != NULL);
602 pass = account->passwd;
605 tmp_pass = input_dialog_query_password(account->recv_server, account->userid);
608 Xstrdup_a(pass, tmp_pass, {g_free(tmp_pass); return;});
612 if (imap_cmd_login(SESSION(session)->sock, account->userid, pass) != IMAP_SUCCESS) {
613 imap_cmd_logout(SESSION(session)->sock);
617 session->authenticated = TRUE;
620 void imap_session_destroy(Session *session)
622 sock_close(session->sock);
623 session->sock = NULL;
625 g_free(IMAP_SESSION(session)->mbox);
627 g_strfreev(IMAP_SESSION(session)->capability);
630 gchar *imap_fetch_msg(Folder *folder, FolderItem *item, gint uid)
632 gchar *path, *filename;
633 IMAPSession *session;
636 g_return_val_if_fail(folder != NULL, NULL);
637 g_return_val_if_fail(item != NULL, NULL);
639 path = folder_item_get_path(item);
640 if (!is_dir_exist(path))
642 filename = g_strconcat(path, G_DIR_SEPARATOR_S, itos(uid), NULL);
645 if (is_file_exist(filename)) {
646 debug_print("message %d has been already cached.\n", uid);
650 session = imap_session_get(folder);
656 ok = imap_select(session, IMAP_FOLDER(folder), item->path,
657 NULL, NULL, NULL, NULL);
658 if (ok != IMAP_SUCCESS) {
659 g_warning("can't select mailbox %s\n", item->path);
664 debug_print("getting message %d...\n", uid);
665 ok = imap_cmd_fetch(SESSION(session)->sock, (guint32)uid, filename);
667 if (ok != IMAP_SUCCESS) {
668 g_warning("can't fetch message %d\n", uid);
676 gint imap_add_msg(Folder *folder, FolderItem *dest, const gchar *file,
677 gboolean remove_source)
680 IMAPSession *session;
683 g_return_val_if_fail(folder != NULL, -1);
684 g_return_val_if_fail(dest != NULL, -1);
685 g_return_val_if_fail(file != NULL, -1);
687 session = imap_session_get(folder);
688 if (!session) return -1;
690 destdir = imap_get_real_path(IMAP_FOLDER(folder), dest->path);
691 ok = imap_cmd_append(session, destdir, file, &newuid);
694 if (ok != IMAP_SUCCESS) {
695 g_warning("can't append message %s\n", file);
700 if (unlink(file) < 0)
701 FILE_OP_ERROR(file, "unlink");
707 static gint imap_do_copy(Folder *folder, FolderItem *dest, MsgInfo *msginfo,
708 gboolean remove_source)
711 IMAPSession *session;
715 g_return_val_if_fail(folder != NULL, -1);
716 g_return_val_if_fail(FOLDER_CLASS(folder) == &imap_class, -1);
717 g_return_val_if_fail(dest != NULL, -1);
718 g_return_val_if_fail(msginfo != NULL, -1);
720 session = imap_session_get(folder);
721 if (!session) return -1;
723 if (msginfo->folder == dest) {
724 g_warning("the src folder is identical to the dest.\n");
728 destdir = imap_get_real_path(IMAP_FOLDER(folder), dest->path);
730 /* ensure source folder selected */
731 ok = imap_select(session, IMAP_FOLDER(folder), msginfo->folder->path,
732 NULL, NULL, NULL, NULL);
733 if (ok != IMAP_SUCCESS)
737 debug_print("Moving message %s%c%d to %s ...\n",
738 msginfo->folder->path, G_DIR_SEPARATOR,
739 msginfo->msgnum, destdir);
741 debug_print("Copying message %s%c%d to %s ...\n",
742 msginfo->folder->path, G_DIR_SEPARATOR,
743 msginfo->msgnum, destdir);
745 ok = imap_cmd_copy(session, msginfo->msgnum, destdir, &newuid);
747 if (ok == IMAP_SUCCESS && remove_source) {
748 MsgNumberList numlist;
751 numlist.data = GINT_TO_POINTER(msginfo->msgnum);
753 imap_set_message_flags(session, &numlist,
754 IMAP_FLAG_DELETED, TRUE);
755 ok = imap_cmd_expunge(SESSION(session)->sock);
760 if (ok == IMAP_SUCCESS)
766 gint imap_copy_msg(Folder *folder, FolderItem *dest, MsgInfo *msginfo)
771 g_return_val_if_fail(folder != NULL, -1);
772 g_return_val_if_fail(dest != NULL, -1);
773 g_return_val_if_fail(msginfo != NULL, -1);
774 g_return_val_if_fail(msginfo->folder != NULL, -1);
776 if (folder == msginfo->folder->folder)
777 return imap_do_copy(folder, dest, msginfo, FALSE);
779 srcfile = procmsg_get_message_file(msginfo);
780 if (!srcfile) return -1;
782 ret = imap_add_msg(folder, dest, srcfile, FALSE);
789 gint imap_remove_msg(Folder *folder, FolderItem *item, gint uid)
792 IMAPSession *session;
794 MsgNumberList numlist;
796 g_return_val_if_fail(folder != NULL, -1);
797 g_return_val_if_fail(FOLDER_CLASS(folder) == &imap_class, -1);
798 g_return_val_if_fail(item != NULL, -1);
800 session = imap_session_get(folder);
801 if (!session) return -1;
803 ok = imap_select(session, IMAP_FOLDER(folder), item->path,
804 NULL, NULL, NULL, NULL);
805 if (ok != IMAP_SUCCESS)
809 numlist.data = GINT_TO_POINTER(uid);
811 ok = imap_set_message_flags
812 (IMAP_SESSION(REMOTE_FOLDER(folder)->session),
813 &numlist, IMAP_FLAG_DELETED, TRUE);
814 if (ok != IMAP_SUCCESS) {
815 log_warning(_("can't set deleted flags: %d\n"), uid);
819 ok = imap_cmd_expunge(SESSION(session)->sock);
820 if (ok != IMAP_SUCCESS) {
821 log_warning(_("can't expunge\n"));
825 dir = folder_item_get_path(item);
826 if (is_dir_exist(dir))
827 remove_numbered_files(dir, uid, uid);
833 gint imap_remove_all_msg(Folder *folder, FolderItem *item)
835 gint exists, recent, unseen;
836 guint32 uid_validity;
838 IMAPSession *session;
841 g_return_val_if_fail(folder != NULL, -1);
842 g_return_val_if_fail(item != NULL, -1);
844 session = imap_session_get(folder);
845 if (!session) return -1;
847 ok = imap_select(session, IMAP_FOLDER(folder), item->path,
848 &exists, &recent, &unseen, &uid_validity);
849 if (ok != IMAP_SUCCESS)
854 imap_cmd_gen_send(SESSION(session)->sock,
855 "STORE 1:%d +FLAGS (\\Deleted)", exists);
856 ok = imap_cmd_ok(SESSION(session)->sock, NULL);
857 if (ok != IMAP_SUCCESS) {
858 log_warning(_("can't set deleted flags: 1:%d\n"), exists);
862 ok = imap_cmd_expunge(SESSION(session)->sock);
863 if (ok != IMAP_SUCCESS) {
864 log_warning(_("can't expunge\n"));
868 dir = folder_item_get_path(item);
869 if (is_dir_exist(dir))
870 remove_all_numbered_files(dir);
876 gboolean imap_is_msg_changed(Folder *folder, FolderItem *item, MsgInfo *msginfo)
878 /* TODO: properly implement this method */
882 gint imap_scan_folder(Folder *folder, FolderItem *item)
884 IMAPSession *session;
885 gint messages, recent, unseen;
886 guint32 uid_next, uid_validity;
889 g_return_val_if_fail(folder != NULL, -1);
890 g_return_val_if_fail(item != NULL, -1);
892 session = imap_session_get(folder);
893 if (!session) return -1;
895 ok = imap_status(session, IMAP_FOLDER(folder), item->path,
896 &messages, &recent, &uid_next, &uid_validity, &unseen);
897 if (ok != IMAP_SUCCESS) return -1;
899 item->new_msgs = unseen > 0 ? recent : 0;
900 item->unread_msgs = unseen;
901 item->total_msgs = messages;
902 item->last_num = (messages > 0 && uid_next > 0) ? uid_next - 1 : 0;
903 /* item->mtime = uid_validity; */
908 void imap_scan_tree(Folder *folder)
911 IMAPSession *session;
912 gchar *root_folder = NULL;
914 g_return_if_fail(folder != NULL);
915 g_return_if_fail(folder->account != NULL);
917 session = imap_session_get(folder);
920 folder_tree_destroy(folder);
921 item = folder_item_new(folder, folder->name, NULL);
922 item->folder = folder;
923 folder->node = g_node_new(item);
928 if (folder->account->imap_dir && *folder->account->imap_dir) {
929 Xstrdup_a(root_folder, folder->account->imap_dir, return);
930 strtailchomp(root_folder, '/');
931 debug_print("IMAP root directory: %s\n", root_folder);
934 item = folder_item_new(folder, folder->name, root_folder);
935 item->folder = folder;
936 item->no_select = TRUE;
937 folder->node = g_node_new(item);
939 imap_scan_tree_recursive(session, item);
941 imap_create_missing_folders(folder);
944 static gint imap_scan_tree_recursive(IMAPSession *session, FolderItem *item)
947 IMAPFolder *imapfolder;
948 FolderItem *new_item;
949 GSList *item_list, *cur;
951 gchar *wildcard_path;
955 g_return_val_if_fail(item != NULL, -1);
956 g_return_val_if_fail(item->folder != NULL, -1);
957 g_return_val_if_fail(item->no_sub == FALSE, -1);
959 folder = FOLDER(item->folder);
960 imapfolder = IMAP_FOLDER(folder);
962 separator = imap_get_path_separator(imapfolder, item->path);
964 if (item->folder->ui_func)
965 item->folder->ui_func(folder, item, folder->ui_func_data);
968 wildcard[0] = separator;
971 real_path = imap_get_real_path(imapfolder, item->path);
975 real_path = g_strdup("");
978 Xstrcat_a(wildcard_path, real_path, wildcard,
979 {g_free(real_path); return IMAP_ERROR;});
980 QUOTE_IF_REQUIRED(wildcard_path, wildcard_path);
982 imap_cmd_gen_send(SESSION(session)->sock, "LIST \"\" %s",
985 strtailchomp(real_path, separator);
986 item_list = imap_parse_list(folder, session, real_path, NULL);
989 for (cur = item_list; cur != NULL; cur = cur->next) {
990 new_item = cur->data;
991 if (!strcmp(new_item->path, "INBOX")) {
992 if (!folder->inbox) {
993 new_item->stype = F_INBOX;
994 item->folder->inbox = new_item;
996 folder_item_destroy(new_item);
999 } else if (!item->parent || item->stype == F_INBOX) {
1002 base = g_basename(new_item->path);
1004 if (!folder->outbox && !strcasecmp(base, "Sent")) {
1005 new_item->stype = F_OUTBOX;
1006 folder->outbox = new_item;
1007 } else if (!folder->draft && !strcasecmp(base, "Drafts")) {
1008 new_item->stype = F_DRAFT;
1009 folder->draft = new_item;
1010 } else if (!folder->queue && !strcasecmp(base, "Queue")) {
1011 new_item->stype = F_QUEUE;
1012 folder->queue = new_item;
1013 } else if (!folder->trash && !strcasecmp(base, "Trash")) {
1014 new_item->stype = F_TRASH;
1015 folder->trash = new_item;
1018 folder_item_append(item, new_item);
1019 if (new_item->no_select == FALSE)
1020 imap_scan_folder(folder, new_item);
1021 if (new_item->no_sub == FALSE)
1022 imap_scan_tree_recursive(session, new_item);
1025 return IMAP_SUCCESS;
1028 static GSList *imap_parse_list(Folder *folder, IMAPSession *session,
1029 const gchar *real_path, gchar *separator)
1031 gchar buf[IMAPBUFSIZE];
1033 gchar separator_str[16];
1036 gchar *loc_name, *loc_path;
1037 GSList *item_list = NULL;
1039 FolderItem *new_item;
1041 debug_print("getting list of %s ...\n",
1042 *real_path ? real_path : "\"\"");
1044 str = g_string_new(NULL);
1047 if (sock_gets(SESSION(session)->sock, buf, sizeof(buf)) <= 0) {
1048 log_warning(_("error occurred while getting LIST.\n"));
1052 if (buf[0] != '*' || buf[1] != ' ') {
1053 log_print("IMAP4< %s\n", buf);
1056 debug_print("IMAP4< %s\n", buf);
1058 g_string_assign(str, buf);
1060 if (strncmp(p, "LIST ", 5) != 0) continue;
1063 if (*p != '(') continue;
1065 p = strchr_cpy(p, ')', flags, sizeof(flags));
1067 while (*p == ' ') p++;
1069 p = strchr_cpy(p, ' ', separator_str, sizeof(separator_str));
1071 extract_quote(separator_str, '"');
1072 if (!strcmp(separator_str, "NIL"))
1073 separator_str[0] = '\0';
1075 *separator = separator_str[0];
1078 while (*p == ' ') p++;
1079 if (*p == '{' || *p == '"')
1080 p = imap_parse_atom(SESSION(session)->sock, p,
1081 buf, sizeof(buf), str);
1083 strncpy2(buf, p, sizeof(buf));
1084 strtailchomp(buf, separator_str[0]);
1085 if (buf[0] == '\0') continue;
1086 if (!strcmp(buf, real_path)) continue;
1088 if (separator_str[0] != '\0')
1089 subst_char(buf, separator_str[0], '/');
1090 name = g_basename(buf);
1091 if (name[0] == '.') continue;
1093 loc_name = imap_modified_utf7_to_locale(name);
1094 loc_path = imap_modified_utf7_to_locale(buf);
1095 new_item = folder_item_new(folder, loc_name, loc_path);
1096 if (strcasestr(flags, "\\Noinferiors") != NULL)
1097 new_item->no_sub = TRUE;
1098 if (strcmp(buf, "INBOX") != 0 &&
1099 strcasestr(flags, "\\Noselect") != NULL)
1100 new_item->no_select = TRUE;
1102 item_list = g_slist_append(item_list, new_item);
1104 debug_print("folder %s has been added.\n", loc_path);
1109 g_string_free(str, TRUE);
1114 gint imap_create_tree(Folder *folder)
1116 g_return_val_if_fail(folder != NULL, -1);
1117 g_return_val_if_fail(folder->node != NULL, -1);
1118 g_return_val_if_fail(folder->node->data != NULL, -1);
1119 g_return_val_if_fail(folder->account != NULL, -1);
1121 imap_scan_tree(folder);
1122 imap_create_missing_folders(folder);
1127 static void imap_create_missing_folders(Folder *folder)
1129 g_return_if_fail(folder != NULL);
1132 folder->inbox = imap_create_special_folder
1133 (folder, F_INBOX, "INBOX");
1135 if (!folder->outbox)
1136 folder->outbox = imap_create_special_folder
1137 (folder, F_OUTBOX, "Sent");
1139 folder->draft = imap_create_special_folder
1140 (folder, F_DRAFT, "Drafts");
1142 folder->queue = imap_create_special_folder
1143 (folder, F_QUEUE, "Queue");
1146 folder->trash = imap_create_special_folder
1147 (folder, F_TRASH, "Trash");
1150 static FolderItem *imap_create_special_folder(Folder *folder,
1151 SpecialFolderItemType stype,
1155 FolderItem *new_item;
1157 g_return_val_if_fail(folder != NULL, NULL);
1158 g_return_val_if_fail(folder->node != NULL, NULL);
1159 g_return_val_if_fail(folder->node->data != NULL, NULL);
1160 g_return_val_if_fail(folder->account != NULL, NULL);
1161 g_return_val_if_fail(name != NULL, NULL);
1163 item = FOLDER_ITEM(folder->node->data);
1164 new_item = imap_create_folder(folder, item, name);
1167 g_warning("Can't create '%s'\n", name);
1168 if (!folder->inbox) return NULL;
1170 new_item = imap_create_folder(folder, folder->inbox, name);
1172 g_warning("Can't create '%s' under INBOX\n", name);
1174 new_item->stype = stype;
1176 new_item->stype = stype;
1181 FolderItem *imap_create_folder(Folder *folder, FolderItem *parent,
1184 gchar *dirpath, *imap_path;
1185 IMAPSession *session;
1186 FolderItem *new_item;
1192 g_return_val_if_fail(folder != NULL, NULL);
1193 g_return_val_if_fail(folder->account != NULL, NULL);
1194 g_return_val_if_fail(parent != NULL, NULL);
1195 g_return_val_if_fail(name != NULL, NULL);
1197 session = imap_session_get(folder);
1198 if (!session) return NULL;
1200 if (!parent->parent && strcmp(name, "INBOX") == 0)
1201 dirpath = g_strdup(name);
1202 else if (parent->path)
1203 dirpath = g_strconcat(parent->path, "/", name, NULL);
1204 else if ((p = strchr(name, '/')) != NULL && *(p + 1) != '\0')
1205 dirpath = g_strdup(name);
1206 else if (folder->account->imap_dir && *folder->account->imap_dir) {
1209 Xstrdup_a(imap_dir, folder->account->imap_dir, return NULL);
1210 strtailchomp(imap_dir, '/');
1211 dirpath = g_strconcat(imap_dir, "/", name, NULL);
1213 dirpath = g_strdup(name);
1215 /* keep trailing directory separator to create a folder that contains
1217 imap_path = imap_locale_to_modified_utf7(dirpath);
1218 strtailchomp(dirpath, '/');
1219 Xstrdup_a(new_name, name, {g_free(dirpath); return NULL;});
1220 strtailchomp(new_name, '/');
1221 separator = imap_get_path_separator(IMAP_FOLDER(folder), imap_path);
1222 imap_path_separator_subst(imap_path, separator);
1223 subst_char(new_name, '/', separator);
1225 if (strcmp(name, "INBOX") != 0) {
1228 gboolean exist = FALSE;
1230 argbuf = g_ptr_array_new();
1231 ok = imap_cmd_list(SESSION(session)->sock, NULL, imap_path,
1233 if (ok != IMAP_SUCCESS) {
1234 log_warning(_("can't create mailbox: LIST failed\n"));
1237 ptr_array_free_strings(argbuf);
1238 g_ptr_array_free(argbuf, TRUE);
1242 for (i = 0; i < argbuf->len; i++) {
1244 str = g_ptr_array_index(argbuf, i);
1245 if (!strncmp(str, "LIST ", 5)) {
1250 ptr_array_free_strings(argbuf);
1251 g_ptr_array_free(argbuf, TRUE);
1254 ok = imap_cmd_create(SESSION(session)->sock, imap_path);
1255 if (ok != IMAP_SUCCESS) {
1256 log_warning(_("can't create mailbox\n"));
1264 new_item = folder_item_new(folder, new_name, dirpath);
1265 folder_item_append(parent, new_item);
1269 dirpath = folder_item_get_path(new_item);
1270 if (!is_dir_exist(dirpath))
1271 make_dir_hier(dirpath);
1277 gint imap_rename_folder(Folder *folder, FolderItem *item, const gchar *name)
1281 gchar *real_oldpath;
1282 gchar *real_newpath;
1285 gchar *old_cache_dir;
1286 gchar *new_cache_dir;
1287 IMAPSession *session;
1290 gint exists, recent, unseen;
1291 guint32 uid_validity;
1293 g_return_val_if_fail(folder != NULL, -1);
1294 g_return_val_if_fail(item != NULL, -1);
1295 g_return_val_if_fail(item->path != NULL, -1);
1296 g_return_val_if_fail(name != NULL, -1);
1298 session = imap_session_get(folder);
1299 if (!session) return -1;
1301 real_oldpath = imap_get_real_path(IMAP_FOLDER(folder), item->path);
1303 g_free(session->mbox);
1304 session->mbox = NULL;
1305 ok = imap_cmd_examine(SESSION(session)->sock, "INBOX",
1306 &exists, &recent, &unseen, &uid_validity);
1307 if (ok != IMAP_SUCCESS) {
1308 g_free(real_oldpath);
1312 separator = imap_get_path_separator(IMAP_FOLDER(folder), item->path);
1313 if (strchr(item->path, G_DIR_SEPARATOR)) {
1314 dirpath = g_dirname(item->path);
1315 newpath = g_strconcat(dirpath, G_DIR_SEPARATOR_S, name, NULL);
1318 newpath = g_strdup(name);
1320 real_newpath = imap_locale_to_modified_utf7(newpath);
1321 imap_path_separator_subst(real_newpath, separator);
1323 ok = imap_cmd_rename(SESSION(session)->sock, real_oldpath, real_newpath);
1324 if (ok != IMAP_SUCCESS) {
1325 log_warning(_("can't rename mailbox: %s to %s\n"),
1326 real_oldpath, real_newpath);
1327 g_free(real_oldpath);
1329 g_free(real_newpath);
1334 item->name = g_strdup(name);
1336 old_cache_dir = folder_item_get_path(item);
1338 node = g_node_find(item->folder->node, G_PRE_ORDER, G_TRAVERSE_ALL,
1340 paths[0] = g_strdup(item->path);
1342 g_node_traverse(node, G_PRE_ORDER, G_TRAVERSE_ALL, -1,
1343 imap_rename_folder_func, paths);
1345 if (is_dir_exist(old_cache_dir)) {
1346 new_cache_dir = folder_item_get_path(item);
1347 if (rename(old_cache_dir, new_cache_dir) < 0) {
1348 FILE_OP_ERROR(old_cache_dir, "rename");
1350 g_free(new_cache_dir);
1353 g_free(old_cache_dir);
1356 g_free(real_oldpath);
1357 g_free(real_newpath);
1362 gint imap_remove_folder(Folder *folder, FolderItem *item)
1365 IMAPSession *session;
1368 gint exists, recent, unseen;
1369 guint32 uid_validity;
1371 g_return_val_if_fail(folder != NULL, -1);
1372 g_return_val_if_fail(item != NULL, -1);
1373 g_return_val_if_fail(item->path != NULL, -1);
1375 session = imap_session_get(folder);
1376 if (!session) return -1;
1378 path = imap_get_real_path(IMAP_FOLDER(folder), item->path);
1380 ok = imap_cmd_examine(SESSION(session)->sock, "INBOX",
1381 &exists, &recent, &unseen, &uid_validity);
1382 if (ok != IMAP_SUCCESS) {
1387 ok = imap_cmd_delete(SESSION(session)->sock, path);
1388 if (ok != IMAP_SUCCESS) {
1389 log_warning(_("can't delete mailbox\n"));
1395 cache_dir = folder_item_get_path(item);
1396 if (is_dir_exist(cache_dir) && remove_dir_recursive(cache_dir) < 0)
1397 g_warning("can't remove directory '%s'\n", cache_dir);
1399 folder_item_remove(item);
1404 static GSList *imap_get_uncached_messages(IMAPSession *session,
1406 MsgNumberList *numlist)
1409 GSList *newlist = NULL;
1410 GSList *llast = NULL;
1414 g_return_val_if_fail(session != NULL, NULL);
1415 g_return_val_if_fail(item != NULL, NULL);
1416 g_return_val_if_fail(item->folder != NULL, NULL);
1417 g_return_val_if_fail(FOLDER_CLASS(item->folder) == &imap_class, NULL);
1419 if (imap_cmd_envelope(SESSION(session)->sock, numberlist_to_imapset(numlist))
1421 log_warning(_("can't get envelope\n"));
1425 str = g_string_new(NULL);
1428 if ((tmp = sock_getline(SESSION(session)->sock)) == NULL) {
1429 log_warning(_("error occurred while getting envelope.\n"));
1430 g_string_free(str, TRUE);
1434 if (tmp[0] != '*' || tmp[1] != ' ') {
1435 log_print("IMAP4< %s\n", tmp);
1439 if (strstr(tmp, "FETCH") == NULL) {
1440 log_print("IMAP4< %s\n", tmp);
1444 log_print("IMAP4< %s\n", tmp);
1445 g_string_assign(str, tmp);
1448 msginfo = imap_parse_envelope
1449 (SESSION(session)->sock, item, str);
1451 log_warning(_("can't parse envelope: %s\n"), str->str);
1454 if (item->stype == F_QUEUE) {
1455 MSG_SET_TMP_FLAGS(msginfo->flags, MSG_QUEUED);
1456 } else if (item->stype == F_DRAFT) {
1457 MSG_SET_TMP_FLAGS(msginfo->flags, MSG_DRAFT);
1460 msginfo->folder = item;
1463 llast = newlist = g_slist_append(newlist, msginfo);
1465 llast = g_slist_append(llast, msginfo);
1466 llast = llast->next;
1470 g_string_free(str, TRUE);
1475 static void imap_delete_all_cached_messages(FolderItem *item)
1479 g_return_if_fail(item != NULL);
1480 g_return_if_fail(item->folder != NULL);
1481 g_return_if_fail(FOLDER_CLASS(item->folder) == &imap_class);
1483 debug_print("Deleting all cached messages...\n");
1485 dir = folder_item_get_path(item);
1486 if (is_dir_exist(dir))
1487 remove_all_numbered_files(dir);
1490 debug_print("done.\n");
1494 static SockInfo *imap_open_tunnel(const gchar *server,
1495 const gchar *tunnelcmd,
1498 static SockInfo *imap_open_tunnel(const gchar *server,
1499 const gchar *tunnelcmd)
1504 if ((sock = sock_connect_cmd(server, tunnelcmd)) == NULL) {
1505 log_warning(_("Can't establish IMAP4 session with: %s\n"),
1510 return imap_init_sock(sock, ssl_type);
1512 return imap_init_sock(sock);
1518 static SockInfo *imap_open(const gchar *server, gushort port,
1521 static SockInfo *imap_open(const gchar *server, gushort port)
1526 if ((sock = sock_connect(server, port)) == NULL) {
1527 log_warning(_("Can't connect to IMAP4 server: %s:%d\n"),
1533 if (ssl_type == SSL_TUNNEL && !ssl_init_socket(sock)) {
1534 log_warning(_("Can't establish IMAP4 session with: %s:%d\n"),
1539 return imap_init_sock(sock, ssl_type);
1541 return imap_init_sock(sock);
1546 static SockInfo *imap_init_sock(SockInfo *sock, SSLType ssl_type)
1548 static SockInfo *imap_init_sock(SockInfo *sock)
1553 if (ssl_type == SSL_STARTTLS) {
1556 ok = imap_cmd_starttls(sock);
1557 if (ok != IMAP_SUCCESS) {
1558 log_warning(_("Can't start TLS session.\n"));
1562 if (!ssl_init_socket_with_method(sock, SSL_METHOD_TLSv1)) {
1572 static GList *imap_parse_namespace_str(gchar *str)
1577 IMAPNameSpace *namespace;
1578 GList *ns_list = NULL;
1580 while (*p != '\0') {
1581 /* parse ("#foo" "/") */
1583 while (*p && *p != '(') p++;
1584 if (*p == '\0') break;
1587 while (*p && *p != '"') p++;
1588 if (*p == '\0') break;
1592 while (*p && *p != '"') p++;
1593 if (*p == '\0') break;
1597 while (*p && isspace(*p)) p++;
1598 if (*p == '\0') break;
1599 if (strncmp(p, "NIL", 3) == 0)
1601 else if (*p == '"') {
1604 while (*p && *p != '"') p++;
1605 if (*p == '\0') break;
1610 while (*p && *p != ')') p++;
1611 if (*p == '\0') break;
1614 namespace = g_new(IMAPNameSpace, 1);
1615 namespace->name = g_strdup(name);
1616 namespace->separator = separator ? separator[0] : '\0';
1617 ns_list = g_list_append(ns_list, namespace);
1623 static void imap_parse_namespace(IMAPSession *session, IMAPFolder *folder)
1628 g_return_if_fail(session != NULL);
1629 g_return_if_fail(folder != NULL);
1631 if (folder->ns_personal != NULL ||
1632 folder->ns_others != NULL ||
1633 folder->ns_shared != NULL)
1636 if (!imap_has_capability(session, "NAMESPACE")) {
1637 imap_get_namespace_by_list(session, folder);
1641 if (imap_cmd_namespace(SESSION(session)->sock, &ns_str)
1643 log_warning(_("can't get namespace\n"));
1647 str_array = strsplit_parenthesis(ns_str, '(', ')', 3);
1648 if (str_array == NULL) {
1650 imap_get_namespace_by_list(session, folder);
1654 folder->ns_personal = imap_parse_namespace_str(str_array[0]);
1655 if (str_array[0] && str_array[1])
1656 folder->ns_others = imap_parse_namespace_str(str_array[1]);
1657 if (str_array[0] && str_array[1] && str_array[2])
1658 folder->ns_shared = imap_parse_namespace_str(str_array[2]);
1659 g_strfreev(str_array);
1663 static void imap_get_namespace_by_list(IMAPSession *session, IMAPFolder *folder)
1665 GSList *item_list, *cur;
1666 gchar separator = '\0';
1667 IMAPNameSpace *namespace;
1669 g_return_if_fail(session != NULL);
1670 g_return_if_fail(folder != NULL);
1672 if (folder->ns_personal != NULL ||
1673 folder->ns_others != NULL ||
1674 folder->ns_shared != NULL)
1677 imap_cmd_gen_send(SESSION(session)->sock, "LIST \"\" \"\"");
1678 item_list = imap_parse_list(NULL, session, "", &separator);
1679 for (cur = item_list; cur != NULL; cur = cur->next)
1680 folder_item_destroy(FOLDER_ITEM(cur->data));
1681 g_slist_free(item_list);
1683 namespace = g_new(IMAPNameSpace, 1);
1684 namespace->name = g_strdup("");
1685 namespace->separator = separator;
1686 folder->ns_personal = g_list_append(NULL, namespace);
1689 static IMAPNameSpace *imap_find_namespace_from_list(GList *ns_list,
1692 IMAPNameSpace *namespace = NULL;
1693 gchar *tmp_path, *name;
1695 if (!path) path = "";
1697 Xstrcat_a(tmp_path, path, "/", return NULL);
1699 for (; ns_list != NULL; ns_list = ns_list->next) {
1700 IMAPNameSpace *tmp_ns = ns_list->data;
1702 Xstrdup_a(name, tmp_ns->name, return namespace);
1703 if (tmp_ns->separator && tmp_ns->separator != '/')
1704 subst_char(name, tmp_ns->separator, '/');
1705 if (strncmp(tmp_path, name, strlen(name)) == 0)
1712 static IMAPNameSpace *imap_find_namespace(IMAPFolder *folder,
1715 IMAPNameSpace *namespace;
1717 g_return_val_if_fail(folder != NULL, NULL);
1719 namespace = imap_find_namespace_from_list(folder->ns_personal, path);
1720 if (namespace) return namespace;
1721 namespace = imap_find_namespace_from_list(folder->ns_others, path);
1722 if (namespace) return namespace;
1723 namespace = imap_find_namespace_from_list(folder->ns_shared, path);
1724 if (namespace) return namespace;
1729 static gchar imap_get_path_separator(IMAPFolder *folder, const gchar *path)
1731 IMAPNameSpace *namespace;
1732 gchar separator = '/';
1734 namespace = imap_find_namespace(folder, path);
1735 if (namespace && namespace->separator)
1736 separator = namespace->separator;
1741 static gchar *imap_get_real_path(IMAPFolder *folder, const gchar *path)
1746 g_return_val_if_fail(folder != NULL, NULL);
1747 g_return_val_if_fail(path != NULL, NULL);
1749 real_path = imap_locale_to_modified_utf7(path);
1750 separator = imap_get_path_separator(folder, path);
1751 imap_path_separator_subst(real_path, separator);
1756 static gchar *imap_parse_atom(SockInfo *sock, gchar *src,
1757 gchar *dest, gint dest_len, GString *str)
1759 gchar *cur_pos = src;
1762 g_return_val_if_fail(str != NULL, cur_pos);
1764 /* read the next line if the current response buffer is empty */
1765 while (isspace(*cur_pos)) cur_pos++;
1766 while (*cur_pos == '\0') {
1767 if ((nextline = sock_getline(sock)) == NULL)
1769 g_string_assign(str, nextline);
1771 strretchomp(nextline);
1772 /* log_print("IMAP4< %s\n", nextline); */
1773 debug_print("IMAP4< %s\n", nextline);
1776 while (isspace(*cur_pos)) cur_pos++;
1779 if (!strncmp(cur_pos, "NIL", 3)) {
1782 } else if (*cur_pos == '\"') {
1785 p = get_quoted(cur_pos, '\"', dest, dest_len);
1786 cur_pos = p ? p : cur_pos + 2;
1787 } else if (*cur_pos == '{') {
1792 cur_pos = strchr_cpy(cur_pos + 1, '}', buf, sizeof(buf));
1795 g_string_truncate(str, 0);
1799 if ((nextline = sock_getline(sock)) == NULL)
1801 line_len += strlen(nextline);
1802 g_string_append(str, nextline);
1804 strretchomp(nextline);
1805 /* log_print("IMAP4< %s\n", nextline); */
1806 debug_print("IMAP4< %s\n", nextline);
1808 } while (line_len < len);
1810 memcpy(dest, cur_pos, MIN(len, dest_len - 1));
1811 dest[MIN(len, dest_len - 1)] = '\0';
1818 static gchar *imap_get_header(SockInfo *sock, gchar *cur_pos, gchar **headers,
1828 g_return_val_if_fail(str != NULL, cur_pos);
1830 while (isspace(*cur_pos)) cur_pos++;
1832 g_return_val_if_fail(*cur_pos == '{', cur_pos);
1834 cur_pos = strchr_cpy(cur_pos + 1, '}', buf, sizeof(buf));
1837 g_string_truncate(str, 0);
1841 if ((nextline = sock_getline(sock)) == NULL)
1843 block_len += strlen(nextline);
1844 g_string_append(str, nextline);
1846 strretchomp(nextline);
1847 /* debug_print("IMAP4< %s\n", nextline); */
1849 } while (block_len < len);
1851 debug_print("IMAP4< [contents of BODY.PEEK[HEADER.FIELDS (...)]]\n");
1853 *headers = g_strndup(cur_pos, len);
1856 while (isspace(*cur_pos)) cur_pos++;
1857 while (*cur_pos == '\0') {
1858 if ((nextline = sock_getline(sock)) == NULL)
1860 g_string_assign(str, nextline);
1862 strretchomp(nextline);
1863 debug_print("IMAP4< %s\n", nextline);
1866 while (isspace(*cur_pos)) cur_pos++;
1872 static MsgFlags imap_parse_flags(const gchar *flag_str)
1874 const gchar *p = flag_str;
1875 MsgFlags flags = {0, 0};
1877 flags.perm_flags = MSG_UNREAD;
1879 while ((p = strchr(p, '\\')) != NULL) {
1882 if (g_strncasecmp(p, "Recent", 6) == 0 && MSG_IS_UNREAD(flags)) {
1883 MSG_SET_PERM_FLAGS(flags, MSG_NEW);
1884 } else if (g_strncasecmp(p, "Seen", 4) == 0) {
1885 MSG_UNSET_PERM_FLAGS(flags, MSG_NEW|MSG_UNREAD);
1886 } else if (g_strncasecmp(p, "Deleted", 7) == 0) {
1887 MSG_SET_PERM_FLAGS(flags, MSG_DELETED);
1888 } else if (g_strncasecmp(p, "Flagged", 7) == 0) {
1889 MSG_SET_PERM_FLAGS(flags, MSG_MARKED);
1890 } else if (g_strncasecmp(p, "Answered", 8) == 0) {
1891 MSG_SET_PERM_FLAGS(flags, MSG_REPLIED);
1898 static MsgInfo *imap_parse_envelope(SockInfo *sock, FolderItem *item,
1901 gchar buf[IMAPBUFSIZE];
1902 MsgInfo *msginfo = NULL;
1907 MsgFlags flags = {0, 0}, imap_flags = {0, 0};
1909 g_return_val_if_fail(line_str != NULL, NULL);
1910 g_return_val_if_fail(line_str->str[0] == '*' &&
1911 line_str->str[1] == ' ', NULL);
1913 MSG_SET_TMP_FLAGS(flags, MSG_IMAP);
1914 if (item->stype == F_QUEUE) {
1915 MSG_SET_TMP_FLAGS(flags, MSG_QUEUED);
1916 } else if (item->stype == F_DRAFT) {
1917 MSG_SET_TMP_FLAGS(flags, MSG_DRAFT);
1920 cur_pos = line_str->str + 2;
1922 #define PARSE_ONE_ELEMENT(ch) \
1924 cur_pos = strchr_cpy(cur_pos, ch, buf, sizeof(buf)); \
1925 if (cur_pos == NULL) { \
1926 g_warning("cur_pos == NULL\n"); \
1927 procmsg_msginfo_free(msginfo); \
1932 PARSE_ONE_ELEMENT(' ');
1935 PARSE_ONE_ELEMENT(' ');
1936 g_return_val_if_fail(!strcmp(buf, "FETCH"), NULL);
1938 g_return_val_if_fail(*cur_pos == '(', NULL);
1941 while (*cur_pos != '\0' && *cur_pos != ')') {
1942 while (*cur_pos == ' ') cur_pos++;
1944 if (!strncmp(cur_pos, "UID ", 4)) {
1946 uid = strtoul(cur_pos, &cur_pos, 10);
1947 } else if (!strncmp(cur_pos, "FLAGS ", 6)) {
1949 if (*cur_pos != '(') {
1950 g_warning("*cur_pos != '('\n");
1951 procmsg_msginfo_free(msginfo);
1955 PARSE_ONE_ELEMENT(')');
1956 imap_flags = imap_parse_flags(buf);
1957 } else if (!strncmp(cur_pos, "RFC822.SIZE ", 12)) {
1959 size = strtol(cur_pos, &cur_pos, 10);
1960 } else if (!strncmp(cur_pos, "BODY[HEADER.FIELDS ", 19)) {
1964 if (*cur_pos != '(') {
1965 g_warning("*cur_pos != '('\n");
1966 procmsg_msginfo_free(msginfo);
1970 PARSE_ONE_ELEMENT(')');
1971 if (*cur_pos != ']') {
1972 g_warning("*cur_pos != ']'\n");
1973 procmsg_msginfo_free(msginfo);
1978 cur_pos = imap_get_header(sock, cur_pos, &headers,
1980 msginfo = procheader_parse_str(headers, flags, FALSE, FALSE);
1983 g_warning("invalid FETCH response: %s\n", cur_pos);
1989 msginfo->msgnum = uid;
1990 msginfo->size = size;
1991 msginfo->flags.tmp_flags |= imap_flags.tmp_flags;
1992 msginfo->flags.perm_flags = imap_flags.perm_flags;
1998 static gint imap_set_message_flags(IMAPSession *session,
1999 MsgNumberList *numlist,
2006 buf = g_string_new(is_set ? "+FLAGS (" : "-FLAGS (");
2008 if (IMAP_IS_SEEN(flags)) g_string_append(buf, "\\Seen ");
2009 if (IMAP_IS_ANSWERED(flags)) g_string_append(buf, "\\Answered ");
2010 if (IMAP_IS_FLAGGED(flags)) g_string_append(buf, "\\Flagged ");
2011 if (IMAP_IS_DELETED(flags)) g_string_append(buf, "\\Deleted ");
2012 if (IMAP_IS_DRAFT(flags)) g_string_append(buf, "\\Draft");
2014 if (buf->str[buf->len - 1] == ' ')
2015 g_string_truncate(buf, buf->len - 1);
2017 g_string_append_c(buf, ')');
2019 ok = imap_cmd_store(SESSION(session)->sock, numberlist_to_imapset(numlist),
2021 g_string_free(buf, TRUE);
2026 static gint imap_select(IMAPSession *session, IMAPFolder *folder,
2028 gint *exists, gint *recent, gint *unseen,
2029 guint32 *uid_validity)
2033 gint exists_, recent_, unseen_, uid_validity_;
2035 if (!exists || !recent || !unseen || !uid_validity) {
2036 if (session->mbox && strcmp(session->mbox, path) == 0)
2037 return IMAP_SUCCESS;
2041 uid_validity = &uid_validity_;
2044 g_free(session->mbox);
2045 session->mbox = NULL;
2047 real_path = imap_get_real_path(folder, path);
2048 ok = imap_cmd_select(SESSION(session)->sock, real_path,
2049 exists, recent, unseen, uid_validity);
2050 if (ok != IMAP_SUCCESS)
2051 log_warning(_("can't select folder: %s\n"), real_path);
2053 session->mbox = g_strdup(path);
2059 #define THROW(err) { ok = err; goto catch; }
2061 static gint imap_status(IMAPSession *session, IMAPFolder *folder,
2063 gint *messages, gint *recent,
2064 guint32 *uid_next, guint32 *uid_validity,
2073 *messages = *recent = *uid_next = *uid_validity = *unseen = 0;
2075 argbuf = g_ptr_array_new();
2077 real_path = imap_get_real_path(folder, path);
2078 QUOTE_IF_REQUIRED(real_path_, real_path);
2079 imap_cmd_gen_send(SESSION(session)->sock, "STATUS %s "
2080 "(MESSAGES RECENT UIDNEXT UIDVALIDITY UNSEEN)",
2083 ok = imap_cmd_ok(SESSION(session)->sock, argbuf);
2084 if (ok != IMAP_SUCCESS) THROW(ok);
2086 str = search_array_str(argbuf, "STATUS");
2087 if (!str) THROW(IMAP_ERROR);
2089 str = strchr(str, '(');
2090 if (!str) THROW(IMAP_ERROR);
2092 while (*str != '\0' && *str != ')') {
2093 while (*str == ' ') str++;
2095 if (!strncmp(str, "MESSAGES ", 9)) {
2097 *messages = strtol(str, &str, 10);
2098 } else if (!strncmp(str, "RECENT ", 7)) {
2100 *recent = strtol(str, &str, 10);
2101 } else if (!strncmp(str, "UIDNEXT ", 8)) {
2103 *uid_next = strtoul(str, &str, 10);
2104 } else if (!strncmp(str, "UIDVALIDITY ", 12)) {
2106 *uid_validity = strtoul(str, &str, 10);
2107 } else if (!strncmp(str, "UNSEEN ", 7)) {
2109 *unseen = strtol(str, &str, 10);
2111 g_warning("invalid STATUS response: %s\n", str);
2118 ptr_array_free_strings(argbuf);
2119 g_ptr_array_free(argbuf, TRUE);
2127 /* low-level IMAP4rev1 commands */
2129 static gint imap_cmd_login(SockInfo *sock,
2130 const gchar *user, const gchar *pass)
2132 gchar *user_, *pass_;
2135 QUOTE_IF_REQUIRED(user_, user);
2136 QUOTE_IF_REQUIRED(pass_, pass);
2137 imap_cmd_gen_send(sock, "LOGIN %s %s", user_, pass_);
2139 ok = imap_cmd_ok(sock, NULL);
2140 if (ok != IMAP_SUCCESS)
2141 log_warning(_("IMAP4 login failed.\n"));
2146 static gint imap_cmd_logout(SockInfo *sock)
2148 imap_cmd_gen_send(sock, "LOGOUT");
2149 return imap_cmd_ok(sock, NULL);
2152 /* Send a NOOP, and examine the server's response to see whether this
2153 * connection is pre-authenticated or not. */
2154 static gint imap_greeting(SockInfo *sock, gboolean *is_preauth)
2159 imap_cmd_gen_send(sock, "NOOP");
2160 argbuf = g_ptr_array_new(); /* will hold messages sent back */
2161 r = imap_cmd_ok(sock, argbuf);
2162 *is_preauth = search_array_str(argbuf, "PREAUTH") != NULL;
2164 ptr_array_free_strings(argbuf);
2165 g_ptr_array_free(argbuf, TRUE);
2170 static void imap_get_capability(Session *session)
2175 if (IMAP_SESSION(session)->capability != NULL)
2178 imap_cmd_gen_send(session->sock, "CAPABILITY");
2180 argbuf = g_ptr_array_new();
2182 if (imap_cmd_ok(session->sock, argbuf) != IMAP_SUCCESS ||
2183 ((capstr = search_array_str(argbuf, "CAPABILITY ")) == NULL)) {
2184 ptr_array_free_strings(argbuf);
2185 g_ptr_array_free(argbuf, TRUE);
2189 capstr += strlen("CAPABILITY ");
2191 IMAP_SESSION(session)->capability = g_strsplit(capstr, " ", 0);
2193 ptr_array_free_strings(argbuf);
2194 g_ptr_array_free(argbuf, TRUE);
2197 static gboolean imap_has_capability(IMAPSession *session, const gchar *cap)
2201 for (p = session->capability; *p != NULL; ++p)
2202 if (g_strcasecmp(*p, cap) == 0)
2208 static const IMAPSet numberlist_to_imapset(MsgNumberList *list)
2210 static GString *imapset = NULL;
2211 MsgNumberList *numlist, *elem;
2212 guint first, last, next;
2214 if (imapset == NULL)
2215 imapset = g_string_sized_new(256);
2217 g_string_truncate(imapset, 0);
2219 numlist = g_slist_copy(list);
2220 numlist = g_slist_sort(numlist, g_int_compare);
2222 first = GPOINTER_TO_INT(numlist->data);
2224 for(elem = g_slist_next(numlist); elem != NULL; elem = g_slist_next(elem)) {
2225 next = GPOINTER_TO_INT(elem->data);
2226 if(next != (last + 1)) {
2227 if (imapset->len > 0)
2228 g_string_append(imapset, ",");
2230 g_string_sprintfa(imapset, "%d", first);
2232 g_string_sprintfa(imapset, "%d:%d", first, last);
2238 if (imapset->len > 0)
2239 g_string_append(imapset, ",");
2241 g_string_sprintfa(imapset, "%d", first);
2243 g_string_sprintfa(imapset, "%d:%d", first, last);
2245 g_slist_free(numlist);
2247 return imapset->str;
2250 static gint imap_cmd_noop(SockInfo *sock)
2252 imap_cmd_gen_send(sock, "NOOP");
2253 return imap_cmd_ok(sock, NULL);
2256 static gint imap_cmd_starttls(SockInfo *sock)
2258 imap_cmd_gen_send(sock, "STARTTLS");
2259 return imap_cmd_ok(sock, NULL);
2262 #define THROW(err) { ok = err; goto catch; }
2264 static gint imap_cmd_namespace(SockInfo *sock, gchar **ns_str)
2270 argbuf = g_ptr_array_new();
2272 imap_cmd_gen_send(sock, "NAMESPACE");
2273 if ((ok = imap_cmd_ok(sock, argbuf)) != IMAP_SUCCESS) THROW(ok);
2275 str = search_array_str(argbuf, "NAMESPACE");
2276 if (!str) THROW(IMAP_ERROR);
2278 *ns_str = g_strdup(str);
2281 ptr_array_free_strings(argbuf);
2282 g_ptr_array_free(argbuf, TRUE);
2289 static gint imap_cmd_list(SockInfo *sock, const gchar *ref,
2290 const gchar *mailbox, GPtrArray *argbuf)
2292 gchar *ref_, *mailbox_;
2294 if (!ref) ref = "\"\"";
2295 if (!mailbox) mailbox = "\"\"";
2297 QUOTE_IF_REQUIRED(ref_, ref);
2298 QUOTE_IF_REQUIRED(mailbox_, mailbox);
2299 imap_cmd_gen_send(sock, "LIST %s %s", ref_, mailbox_);
2301 return imap_cmd_ok(sock, argbuf);
2304 #define THROW goto catch
2306 static gint imap_cmd_do_select(SockInfo *sock, const gchar *folder,
2308 gint *exists, gint *recent, gint *unseen,
2309 guint32 *uid_validity)
2317 *exists = *recent = *unseen = *uid_validity = 0;
2318 argbuf = g_ptr_array_new();
2321 select_cmd = "EXAMINE";
2323 select_cmd = "SELECT";
2325 QUOTE_IF_REQUIRED(folder_, folder);
2326 imap_cmd_gen_send(sock, "%s %s", select_cmd, folder_);
2328 if ((ok = imap_cmd_ok(sock, argbuf)) != IMAP_SUCCESS) THROW;
2330 resp_str = search_array_contain_str(argbuf, "EXISTS");
2332 if (sscanf(resp_str,"%d EXISTS", exists) != 1) {
2333 g_warning("imap_cmd_select(): invalid EXISTS line.\n");
2338 resp_str = search_array_contain_str(argbuf, "RECENT");
2340 if (sscanf(resp_str, "%d RECENT", recent) != 1) {
2341 g_warning("imap_cmd_select(): invalid RECENT line.\n");
2346 resp_str = search_array_contain_str(argbuf, "UIDVALIDITY");
2348 if (sscanf(resp_str, "OK [UIDVALIDITY %u] ", uid_validity)
2350 g_warning("imap_cmd_select(): invalid UIDVALIDITY line.\n");
2355 resp_str = search_array_contain_str(argbuf, "UNSEEN");
2357 if (sscanf(resp_str, "OK [UNSEEN %d] ", unseen) != 1) {
2358 g_warning("imap_cmd_select(): invalid UNSEEN line.\n");
2364 ptr_array_free_strings(argbuf);
2365 g_ptr_array_free(argbuf, TRUE);
2370 static gint imap_cmd_select(SockInfo *sock, const gchar *folder,
2371 gint *exists, gint *recent, gint *unseen,
2372 guint32 *uid_validity)
2374 return imap_cmd_do_select(sock, folder, FALSE,
2375 exists, recent, unseen, uid_validity);
2378 static gint imap_cmd_examine(SockInfo *sock, const gchar *folder,
2379 gint *exists, gint *recent, gint *unseen,
2380 guint32 *uid_validity)
2382 return imap_cmd_do_select(sock, folder, TRUE,
2383 exists, recent, unseen, uid_validity);
2388 static gint imap_cmd_create(SockInfo *sock, const gchar *folder)
2392 QUOTE_IF_REQUIRED(folder_, folder);
2393 imap_cmd_gen_send(sock, "CREATE %s", folder_);
2395 return imap_cmd_ok(sock, NULL);
2398 static gint imap_cmd_rename(SockInfo *sock, const gchar *old_folder,
2399 const gchar *new_folder)
2401 gchar *old_folder_, *new_folder_;
2403 QUOTE_IF_REQUIRED(old_folder_, old_folder);
2404 QUOTE_IF_REQUIRED(new_folder_, new_folder);
2405 imap_cmd_gen_send(sock, "RENAME %s %s", old_folder_, new_folder_);
2407 return imap_cmd_ok(sock, NULL);
2410 static gint imap_cmd_delete(SockInfo *sock, const gchar *folder)
2414 QUOTE_IF_REQUIRED(folder_, folder);
2415 imap_cmd_gen_send(sock, "DELETE %s", folder_);
2417 return imap_cmd_ok(sock, NULL);
2420 static gint imap_cmd_search(SockInfo *sock, const gchar *criteria, GSList **list)
2426 g_return_val_if_fail(criteria != NULL, IMAP_ERROR);
2427 g_return_val_if_fail(list != NULL, IMAP_ERROR);
2431 argbuf = g_ptr_array_new();
2432 imap_cmd_gen_send(sock, "UID SEARCH %s", criteria);
2434 ok = imap_cmd_ok(sock, argbuf);
2435 if (ok != IMAP_SUCCESS) {
2436 ptr_array_free_strings(argbuf);
2437 g_ptr_array_free(argbuf, TRUE);
2441 if ((uidlist = search_array_str(argbuf, "SEARCH ")) != NULL) {
2442 gchar **strlist, **p;
2444 strlist = g_strsplit(uidlist + 7, " ", 0);
2445 for (p = strlist; *p != NULL; ++p) {
2448 if (sscanf(*p, "%d", &msgnum) == 1)
2449 *list = g_slist_append(*list, GINT_TO_POINTER(msgnum));
2451 g_strfreev(strlist);
2453 ptr_array_free_strings(argbuf);
2454 g_ptr_array_free(argbuf, TRUE);
2456 return IMAP_SUCCESS;
2459 static gint imap_cmd_fetch(SockInfo *sock, guint32 uid, const gchar *filename)
2467 g_return_val_if_fail(filename != NULL, IMAP_ERROR);
2469 imap_cmd_gen_send(sock, "UID FETCH %d BODY.PEEK[]", uid);
2471 while ((ok = imap_cmd_gen_recv(sock, &buf))
2473 if (buf[0] != '*' || buf[1] != ' ') {
2477 if (strstr(buf, "FETCH") != NULL)
2480 if (ok != IMAP_SUCCESS)
2483 cur_pos = strchr(buf, '{');
2484 if (cur_pos == NULL) {
2488 cur_pos = strchr_cpy(cur_pos + 1, '}', size_str, sizeof(size_str));
2489 if (cur_pos == NULL) {
2493 size_num = atol(size_str);
2495 if (*cur_pos != '\0') {
2500 if (recv_bytes_write_to_file(sock, size_num, filename) != 0) {
2505 if (imap_cmd_gen_recv(sock, &buf) != IMAP_SUCCESS) {
2510 if (buf[0] == '\0' || buf[strlen(buf) - 1] != ')') {
2516 ok = imap_cmd_ok(sock, NULL);
2521 static gint imap_cmd_append(IMAPSession *session, const gchar *destfolder,
2522 const gchar *file, gint32 *new_uid)
2527 gchar buf[BUFFSIZE], *imapbuf;
2532 g_return_val_if_fail(file != NULL, IMAP_ERROR);
2534 size = get_file_size_as_crlf(file);
2535 if ((fp = fopen(file, "rb")) == NULL) {
2536 FILE_OP_ERROR(file, "fopen");
2539 QUOTE_IF_REQUIRED(destfolder_, destfolder);
2540 imap_cmd_gen_send(SESSION(session)->sock, "APPEND %s (\\Seen) {%d}", destfolder_, size);
2542 ok = imap_cmd_gen_recv(SESSION(session)->sock, &imapbuf);
2543 if (ok != IMAP_SUCCESS || imapbuf[0] != '+' || imapbuf[1] != ' ') {
2544 log_warning(_("can't append %s to %s\n"), file, destfolder_);
2551 log_print("IMAP4> %s\n", _("(sending file...)"));
2553 while (fgets(buf, sizeof(buf), fp) != NULL) {
2555 if (sock_puts(SESSION(session)->sock, buf) < 0) {
2562 FILE_OP_ERROR(file, "fgets");
2567 sock_puts(SESSION(session)->sock, "");
2571 reply = g_ptr_array_new();
2574 ok = imap_cmd_ok(SESSION(session)->sock, reply);
2575 if (ok != IMAP_SUCCESS)
2576 log_warning(_("can't append message to %s\n"), destfolder_);
2578 (new_uid != NULL) &&
2579 (imap_has_capability(session, "UIDPLUS") && reply->len > 0) &&
2580 ((okmsginfo = g_ptr_array_index(reply, reply->len - 1)) != NULL) &&
2581 (sscanf(okmsginfo, "%*u OK [APPENDUID %*u %u]", &newuid) == 1)) {
2585 ptr_array_free_strings(reply);
2586 g_ptr_array_free(reply, TRUE);
2591 static gint imap_cmd_copy(IMAPSession * session,
2593 const gchar * destfolder, gint32 * new_uid)
2596 gint32 olduid, newuid;
2601 g_return_val_if_fail(destfolder != NULL, IMAP_ERROR);
2602 g_return_val_if_fail(session != NULL, IMAP_ERROR);
2603 g_return_val_if_fail(new_uid != NULL, IMAP_ERROR);
2605 QUOTE_IF_REQUIRED(destfolder_, destfolder);
2606 imap_cmd_gen_send(SESSION(session)->sock, "UID COPY %d %s", msgnum, destfolder_);
2608 reply = g_ptr_array_new();
2611 ok = imap_cmd_ok(SESSION(session)->sock, reply);
2612 if (ok != IMAP_SUCCESS)
2613 log_warning(_("can't copy %d to %s\n"), msgnum, destfolder_);
2614 else if (imap_has_capability(session, "UIDPLUS") && reply->len > 0)
2615 if ((okmsginfo = g_ptr_array_index(reply, reply->len - 1)) != NULL &&
2616 sscanf(okmsginfo, "%*u OK [COPYUID %*u %u %u]", &olduid, &newuid) == 2 &&
2620 ptr_array_free_strings(reply);
2621 g_ptr_array_free(reply, TRUE);
2625 gint imap_cmd_envelope(SockInfo *sock, IMAPSet set)
2627 static GString *header_fields = NULL;
2629 if (header_fields == NULL) {
2630 const HeaderEntry *headers, *elem;
2632 headers = procheader_get_headernames(FALSE);
2633 header_fields = g_string_new("");
2635 for (elem = headers; elem->name != NULL; ++elem) {
2636 gint namelen = strlen(elem->name);
2638 /* Header fields ending with space are not rfc822 headers */
2639 if (elem->name[namelen - 1] == ' ')
2642 /* strip : at the of header field */
2643 if(elem->name[namelen - 1] == ':')
2649 g_string_sprintfa(header_fields, "%s%.*s",
2650 header_fields->str[0] != '\0' ? " " : "",
2651 namelen, elem->name);
2656 (sock, "UID FETCH %s (UID FLAGS RFC822.SIZE BODY.PEEK[HEADER.FIELDS (%s)])",
2657 set, header_fields->str);
2659 return IMAP_SUCCESS;
2662 static gint imap_cmd_store(SockInfo *sock, IMAPSet set,
2667 imap_cmd_gen_send(sock, "UID STORE %s %s",
2670 if ((ok = imap_cmd_ok(sock, NULL)) != IMAP_SUCCESS) {
2671 log_warning(_("error while imap command: STORE %s %s\n"),
2676 return IMAP_SUCCESS;
2679 static gint imap_cmd_expunge(SockInfo *sock)
2683 imap_cmd_gen_send(sock, "EXPUNGE");
2684 if ((ok = imap_cmd_ok(sock, NULL)) != IMAP_SUCCESS) {
2685 log_warning(_("error while imap command: EXPUNGE\n"));
2689 return IMAP_SUCCESS;
2692 static gint imap_cmd_ok(SockInfo *sock, GPtrArray *argbuf)
2697 gchar cmd_status[IMAPBUFSIZE];
2699 while ((ok = imap_cmd_gen_recv(sock, &buf))
2701 if (buf[0] == '*' && buf[1] == ' ') {
2703 g_ptr_array_add(argbuf, g_strdup(buf + 2));
2707 if (sscanf(buf, "%d %s", &cmd_num, cmd_status) < 2) {
2710 } else if (cmd_num == imap_cmd_count &&
2711 !strcmp(cmd_status, "OK")) {
2713 g_ptr_array_add(argbuf, g_strdup(buf));
2715 return IMAP_SUCCESS;
2726 static void imap_cmd_gen_send(SockInfo *sock, const gchar *format, ...)
2728 gchar buf[IMAPBUFSIZE];
2729 gchar tmp[IMAPBUFSIZE];
2733 va_start(args, format);
2734 g_vsnprintf(tmp, sizeof(tmp), format, args);
2739 g_snprintf(buf, sizeof(buf), "%d %s\r\n", imap_cmd_count, tmp);
2740 if (!strncasecmp(tmp, "LOGIN ", 6) && (p = strchr(tmp + 6, ' '))) {
2742 log_print("IMAP4> %d %s ********\n", imap_cmd_count, tmp);
2744 log_print("IMAP4> %d %s\n", imap_cmd_count, tmp);
2746 sock_write_all(sock, buf, strlen(buf));
2749 static gint imap_cmd_gen_recv(SockInfo *sock, gchar **buf)
2751 if ((*buf = sock_getline(sock)) == NULL)
2756 log_print("IMAP4< %s\n", *buf);
2758 return IMAP_SUCCESS;
2762 /* misc utility functions */
2764 static gchar *strchr_cpy(const gchar *src, gchar ch, gchar *dest, gint len)
2769 tmp = strchr(src, ch);
2773 memcpy(dest, src, MIN(tmp - src, len - 1));
2774 dest[MIN(tmp - src, len - 1)] = '\0';
2779 static gchar *get_quoted(const gchar *src, gchar ch, gchar *dest, gint len)
2781 const gchar *p = src;
2784 g_return_val_if_fail(*p == ch, NULL);
2789 while (*p != '\0' && *p != ch) {
2791 if (*p == '\\' && *(p + 1) != '\0')
2800 return (gchar *)(*p == ch ? p + 1 : p);
2803 static gchar *search_array_contain_str(GPtrArray *array, const gchar *str)
2807 for (i = 0; i < array->len; i++) {
2810 tmp = g_ptr_array_index(array, i);
2811 if (strstr(tmp, str) != NULL)
2818 static gchar *search_array_str(GPtrArray *array, const gchar *str)
2825 for (i = 0; i < array->len; i++) {
2828 tmp = g_ptr_array_index(array, i);
2829 if (!strncmp(tmp, str, len))
2836 static void imap_path_separator_subst(gchar *str, gchar separator)
2839 gboolean in_escape = FALSE;
2841 if (!separator || separator == '/') return;
2843 for (p = str; *p != '\0'; p++) {
2844 if (*p == '/' && !in_escape)
2846 else if (*p == '&' && *(p + 1) != '-' && !in_escape)
2848 else if (*p == '-' && in_escape)
2853 static gchar *imap_modified_utf7_to_locale(const gchar *mutf7_str)
2856 return g_strdup(mutf7_str);
2858 static iconv_t cd = (iconv_t)-1;
2859 static gboolean iconv_ok = TRUE;
2862 size_t norm_utf7_len;
2864 gchar *to_str, *to_p;
2866 gboolean in_escape = FALSE;
2868 if (!iconv_ok) return g_strdup(mutf7_str);
2870 if (cd == (iconv_t)-1) {
2871 cd = iconv_open(conv_get_current_charset_str(), "UTF-7");
2872 if (cd == (iconv_t)-1) {
2873 g_warning("iconv cannot convert UTF-7 to %s\n",
2874 conv_get_current_charset_str());
2876 return g_strdup(mutf7_str);
2880 norm_utf7 = g_string_new(NULL);
2882 for (p = mutf7_str; *p != '\0'; p++) {
2883 /* replace: '&' -> '+',
2885 escaped ',' -> '/' */
2886 if (!in_escape && *p == '&') {
2887 if (*(p + 1) != '-') {
2888 g_string_append_c(norm_utf7, '+');
2891 g_string_append_c(norm_utf7, '&');
2894 } else if (in_escape && *p == ',') {
2895 g_string_append_c(norm_utf7, '/');
2896 } else if (in_escape && *p == '-') {
2897 g_string_append_c(norm_utf7, '-');
2900 g_string_append_c(norm_utf7, *p);
2904 norm_utf7_p = norm_utf7->str;
2905 norm_utf7_len = norm_utf7->len;
2906 to_len = strlen(mutf7_str) * 5;
2907 to_p = to_str = g_malloc(to_len + 1);
2909 if (iconv(cd, (ICONV_CONST gchar **)&norm_utf7_p, &norm_utf7_len,
2910 &to_p, &to_len) == -1) {
2911 g_warning(_("iconv cannot convert UTF-7 to %s\n"),
2912 conv_get_current_charset_str());
2913 g_string_free(norm_utf7, TRUE);
2915 return g_strdup(mutf7_str);
2918 /* second iconv() call for flushing */
2919 iconv(cd, NULL, NULL, &to_p, &to_len);
2920 g_string_free(norm_utf7, TRUE);
2924 #endif /* !HAVE_ICONV */
2927 static gchar *imap_locale_to_modified_utf7(const gchar *from)
2930 return g_strdup(from);
2932 static iconv_t cd = (iconv_t)-1;
2933 static gboolean iconv_ok = TRUE;
2934 gchar *norm_utf7, *norm_utf7_p;
2935 size_t from_len, norm_utf7_len;
2937 gchar *from_tmp, *to, *p;
2938 gboolean in_escape = FALSE;
2940 if (!iconv_ok) return g_strdup(from);
2942 if (cd == (iconv_t)-1) {
2943 cd = iconv_open("UTF-7", conv_get_current_charset_str());
2944 if (cd == (iconv_t)-1) {
2945 g_warning("iconv cannot convert %s to UTF-7\n",
2946 conv_get_current_charset_str());
2948 return g_strdup(from);
2952 Xstrdup_a(from_tmp, from, return g_strdup(from));
2953 from_len = strlen(from);
2954 norm_utf7_len = from_len * 5;
2955 Xalloca(norm_utf7, norm_utf7_len + 1, return g_strdup(from));
2956 norm_utf7_p = norm_utf7;
2958 #define IS_PRINT(ch) (isprint(ch) && isascii(ch))
2960 while (from_len > 0) {
2961 if (IS_PRINT(*from_tmp)) {
2962 /* printable ascii char */
2963 *norm_utf7_p = *from_tmp;
2970 /* unprintable char: convert to UTF-7 */
2972 !IS_PRINT(from_tmp[mblen]) && mblen < from_len;
2976 if (iconv(cd, (ICONV_CONST gchar **)&from_tmp, &mblen,
2977 &norm_utf7_p, &norm_utf7_len) == -1) {
2978 g_warning("iconv cannot convert %s to UTF-7\n",
2979 conv_get_current_charset_str());
2980 return g_strdup(from);
2983 /* second iconv() call for flushing */
2984 iconv(cd, NULL, NULL, &norm_utf7_p, &norm_utf7_len);
2990 *norm_utf7_p = '\0';
2991 to_str = g_string_new(NULL);
2992 for (p = norm_utf7; p < norm_utf7_p; p++) {
2993 /* replace: '&' -> "&-",
2995 escaped '/' -> ',' */
2996 if (!in_escape && *p == '&') {
2997 g_string_append(to_str, "&-");
2998 } else if (!in_escape && *p == '+') {
2999 g_string_append_c(to_str, '&');
3001 } else if (in_escape && *p == '/') {
3002 g_string_append_c(to_str, ',');
3003 } else if (in_escape && *p == '-') {
3005 g_string_append_c(to_str, '-');
3007 g_string_append_c(to_str, *p);
3013 g_string_append_c(to_str, '-');
3017 g_string_free(to_str, FALSE);
3020 #endif /* !HAVE_ICONV */
3023 static gboolean imap_rename_folder_func(GNode *node, gpointer data)
3025 FolderItem *item = node->data;
3026 gchar **paths = data;
3027 const gchar *oldpath = paths[0];
3028 const gchar *newpath = paths[1];
3030 gchar *new_itempath;
3033 oldpathlen = strlen(oldpath);
3034 if (strncmp(oldpath, item->path, oldpathlen) != 0) {
3035 g_warning("path doesn't match: %s, %s\n", oldpath, item->path);
3039 base = item->path + oldpathlen;
3040 while (*base == G_DIR_SEPARATOR) base++;
3042 new_itempath = g_strdup(newpath);
3044 new_itempath = g_strconcat(newpath, G_DIR_SEPARATOR_S, base,
3047 item->path = new_itempath;
3052 static gint get_list_of_uids(Folder *folder, IMAPFolderItem *item, GSList **msgnum_list)
3054 gint ok, nummsgs = 0, lastuid_old;
3055 IMAPSession *session;
3056 GSList *uidlist, *elem;
3059 session = imap_session_get(folder);
3060 g_return_val_if_fail(session != NULL, -1);
3062 ok = imap_select(session, IMAP_FOLDER(folder), item->item.path,
3063 NULL, NULL, NULL, NULL);
3064 if (ok != IMAP_SUCCESS)
3067 cmd_buf = g_strdup_printf("UID %d:*", item->lastuid + 1);
3068 ok = imap_cmd_search(SESSION(session)->sock, cmd_buf, &uidlist);
3071 if (ok == IMAP_SOCKET) {
3072 session_destroy((Session *)session);
3073 ((RemoteFolder *)folder)->session = NULL;
3077 if (ok != IMAP_SUCCESS) {
3081 argbuf = g_ptr_array_new();
3083 cmd_buf = g_strdup_printf("UID FETCH %d:* (UID)", item->lastuid + 1);
3084 imap_cmd_gen_send(SESSION(session)->sock, cmd_buf);
3086 ok = imap_cmd_ok(SESSION(session)->sock, argbuf);
3087 if (ok != IMAP_SUCCESS) {
3088 ptr_array_free_strings(argbuf);
3089 g_ptr_array_free(argbuf, TRUE);
3093 for(i = 0; i < argbuf->len; i++) {
3096 if((ret = sscanf(g_ptr_array_index(argbuf, i),
3097 "%*d FETCH (UID %d)", &msgnum)) == 1)
3098 uidlist = g_slist_prepend(uidlist, GINT_TO_POINTER(msgnum));
3100 ptr_array_free_strings(argbuf);
3101 g_ptr_array_free(argbuf, TRUE);
3104 lastuid_old = item->lastuid;
3105 *msgnum_list = g_slist_copy(item->uid_list);
3106 nummsgs = g_slist_length(*msgnum_list);
3107 debug_print("Got %d uids from cache\n", g_slist_length(item->uid_list));
3109 for (elem = uidlist; elem != NULL; elem = g_slist_next(elem)) {
3112 msgnum = GPOINTER_TO_INT(elem->data);
3113 if (msgnum > lastuid_old) {
3114 *msgnum_list = g_slist_prepend(*msgnum_list, GINT_TO_POINTER(msgnum));
3115 item->uid_list = g_slist_prepend(item->uid_list, GINT_TO_POINTER(msgnum));
3118 if(msgnum > item->lastuid)
3119 item->lastuid = msgnum;
3122 g_slist_free(uidlist);
3127 gint imap_get_num_list(Folder *folder, FolderItem *_item, GSList **msgnum_list)
3129 IMAPFolderItem *item = (IMAPFolderItem *)_item;
3130 IMAPSession *session;
3131 gint ok, nummsgs = 0, exists, recent, unseen, uid_val, uid_next;
3135 g_return_val_if_fail(folder != NULL, -1);
3136 g_return_val_if_fail(item != NULL, -1);
3137 g_return_val_if_fail(item->item.path != NULL, -1);
3138 g_return_val_if_fail(FOLDER_CLASS(folder) == &imap_class, -1);
3139 g_return_val_if_fail(folder->account != NULL, -1);
3141 session = imap_session_get(folder);
3142 g_return_val_if_fail(session != NULL, -1);
3144 ok = imap_status(session, IMAP_FOLDER(folder), item->item.path,
3145 &exists, &recent, &uid_next, &uid_val, &unseen);
3146 if (ok != IMAP_SUCCESS)
3149 /* If old uid_next matches new uid_next we can be sure no message
3150 was added to the folder */
3151 if (uid_next == item->uid_next) {
3152 nummsgs = g_slist_length(item->uid_list);
3154 /* If number of messages is still the same we
3155 know our caches message numbers are still valid,
3156 otherwise if the number of messages has decrease
3157 we discard our cache to start a new scan to find
3158 out which numbers have been removed */
3159 if (exists == nummsgs) {
3160 *msgnum_list = g_slist_copy(item->uid_list);
3162 } else if (exists < nummsgs) {
3163 debug_print("Freeing imap uid cache");
3165 g_slist_free(item->uid_list);
3166 item->uid_list = NULL;
3169 item->uid_next = uid_next;
3172 *msgnum_list = NULL;
3176 nummsgs = get_list_of_uids(folder, item, &uidlist);
3178 if (nummsgs != exists) {
3179 /* Cache contains more messages then folder, we have cached
3180 an old UID of a message that was removed and new messages
3181 have been added too, otherwise the uid_next check would
3183 debug_print("Freeing imap uid cache");
3185 g_slist_free(item->uid_list);
3186 item->uid_list = NULL;
3188 g_slist_free(*msgnum_list);
3190 nummsgs = get_list_of_uids(folder, item, &uidlist);
3193 *msgnum_list = uidlist;
3195 dir = folder_item_get_path((FolderItem *)item);
3196 debug_print("removing old messages from %s\n", dir);
3197 remove_numbered_files_not_in_list(dir, *msgnum_list);
3203 static MsgInfo *imap_parse_msg(const gchar *file, FolderItem *item)
3208 flags.perm_flags = MSG_NEW|MSG_UNREAD;
3209 flags.tmp_flags = 0;
3211 g_return_val_if_fail(item != NULL, NULL);
3212 g_return_val_if_fail(file != NULL, NULL);
3214 if (item->stype == F_QUEUE) {
3215 MSG_SET_TMP_FLAGS(flags, MSG_QUEUED);
3216 } else if (item->stype == F_DRAFT) {
3217 MSG_SET_TMP_FLAGS(flags, MSG_DRAFT);
3220 msginfo = procheader_parse_file(file, flags, FALSE, FALSE);
3221 if (!msginfo) return NULL;
3223 msginfo->folder = item;
3228 GSList *imap_get_msginfos(Folder *folder, FolderItem *item, GSList *msgnum_list)
3230 IMAPSession *session;
3231 MsgInfoList *ret = NULL;
3234 g_return_val_if_fail(folder != NULL, NULL);
3235 g_return_val_if_fail(item != NULL, NULL);
3236 g_return_val_if_fail(msgnum_list != NULL, NULL);
3238 session = imap_session_get(folder);
3239 g_return_val_if_fail(session != NULL, NULL);
3241 ok = imap_select(session, IMAP_FOLDER(folder), item->path,
3242 NULL, NULL, NULL, NULL);
3243 if (ok != IMAP_SUCCESS)
3246 if (!(item->stype == F_QUEUE || item->stype == F_DRAFT)) {
3247 ret = g_slist_concat(ret,
3248 imap_get_uncached_messages(
3249 session, item, msgnum_list));
3251 MsgNumberList *sorted_list, *elem;
3252 gint startnum, lastnum;
3254 sorted_list = g_slist_sort(g_slist_copy(msgnum_list), g_int_compare);
3256 startnum = lastnum = GPOINTER_TO_INT(sorted_list->data);
3258 for (elem = sorted_list;; elem = g_slist_next(elem)) {
3262 num = GPOINTER_TO_INT(elem->data);
3264 if (num > lastnum + 1 || elem == NULL) {
3266 for (i = startnum; i <= lastnum; ++i) {
3269 file = imap_fetch_msg(folder, item, i);
3271 MsgInfo *msginfo = imap_parse_msg(file, item);
3272 if (msginfo != NULL) {
3273 msginfo->msgnum = i;
3274 ret = g_slist_append(ret, msginfo);
3288 g_slist_free(sorted_list);
3294 MsgInfo *imap_get_msginfo(Folder *folder, FolderItem *item, gint uid)
3296 MsgInfo *msginfo = NULL;
3297 MsgInfoList *msginfolist;
3298 MsgNumberList numlist;
3300 numlist.next = NULL;
3301 numlist.data = GINT_TO_POINTER(uid);
3303 msginfolist = imap_get_msginfos(folder, item, &numlist);
3304 if (msginfolist != NULL) {
3305 msginfo = msginfolist->data;
3306 g_slist_free(msginfolist);
3312 gboolean imap_check_msgnum_validity(Folder *folder, FolderItem *_item)
3314 IMAPSession *session;
3315 IMAPFolderItem *item = (IMAPFolderItem *)_item;
3316 gint ok, exists = 0, recent = 0, unseen = 0;
3317 guint32 uid_next, uid_validity = 0;
3319 g_return_val_if_fail(folder != NULL, FALSE);
3320 g_return_val_if_fail(item != NULL, FALSE);
3321 g_return_val_if_fail(item->item.folder != NULL, FALSE);
3322 g_return_val_if_fail(FOLDER_CLASS(item->item.folder) == &imap_class, FALSE);
3324 session = imap_session_get(folder);
3325 g_return_val_if_fail(session != NULL, FALSE);
3327 ok = imap_status(session, IMAP_FOLDER(folder), item->item.path,
3328 &exists, &recent, &uid_next, &uid_validity, &unseen);
3329 if (ok != IMAP_SUCCESS)
3332 if(item->item.mtime == uid_validity)
3335 debug_print("Freeing imap uid cache");
3337 g_slist_free(item->uid_list);
3338 item->uid_list = NULL;
3340 item->item.mtime = uid_validity;
3342 imap_delete_all_cached_messages((FolderItem *)item);
3347 void imap_change_flags(Folder *folder, FolderItem *item, MsgInfo *msginfo, MsgPermFlags newflags)
3349 IMAPSession *session;
3350 IMAPFlags flags_set = 0, flags_unset = 0;
3351 gint ok = IMAP_SUCCESS;
3352 MsgNumberList numlist;
3354 g_return_if_fail(folder != NULL);
3355 g_return_if_fail(folder->klass == &imap_class);
3356 g_return_if_fail(item != NULL);
3357 g_return_if_fail(item->folder == folder);
3358 g_return_if_fail(msginfo != NULL);
3359 g_return_if_fail(msginfo->folder == item);
3361 session = imap_session_get(folder);
3362 if (!session) return;
3364 if ((ok = imap_select(session, IMAP_FOLDER(folder), msginfo->folder->path,
3365 NULL, NULL, NULL, NULL)) != IMAP_SUCCESS)
3368 if (!MSG_IS_MARKED(msginfo->flags) && (newflags & MSG_MARKED))
3369 flags_set |= IMAP_FLAG_FLAGGED;
3370 if ( MSG_IS_MARKED(msginfo->flags) && !(newflags & MSG_MARKED))
3371 flags_unset |= IMAP_FLAG_FLAGGED;
3373 if (!MSG_IS_UNREAD(msginfo->flags) && (newflags & MSG_UNREAD))
3374 flags_unset |= IMAP_FLAG_SEEN;
3375 if ( MSG_IS_UNREAD(msginfo->flags) && !(newflags & MSG_UNREAD))
3376 flags_set |= IMAP_FLAG_SEEN;
3378 if (!MSG_IS_REPLIED(msginfo->flags) && (newflags & MSG_REPLIED))
3379 flags_set |= IMAP_FLAG_ANSWERED;
3380 if ( MSG_IS_REPLIED(msginfo->flags) && !(newflags & MSG_REPLIED))
3381 flags_set |= IMAP_FLAG_ANSWERED;
3383 numlist.next = NULL;
3384 numlist.data = GINT_TO_POINTER(msginfo->msgnum);
3387 ok = imap_set_message_flags(session, &numlist, flags_set, TRUE);
3388 if (ok != IMAP_SUCCESS) return;
3392 ok = imap_set_message_flags(session, &numlist, flags_unset, FALSE);
3393 if (ok != IMAP_SUCCESS) return;
3396 msginfo->flags.perm_flags = newflags;