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.
49 #include "procheader.h"
50 #include "prefs_account.h"
53 #include "inputdialog.h"
56 typedef struct _IMAPFolder IMAPFolder;
57 typedef struct _IMAPSession IMAPSession;
58 typedef struct _IMAPNameSpace IMAPNameSpace;
59 typedef struct _IMAPFolderItem IMAPFolderItem;
61 #include "prefs_account.h"
63 #define IMAP_FOLDER(obj) ((IMAPFolder *)obj)
64 #define IMAP_SESSION(obj) ((IMAPSession *)obj)
70 /* list of IMAPNameSpace */
82 time_t last_access_time;
83 gboolean authenticated;
93 #define IMAP_SUCCESS 0
95 #define IMAP_AUTHFAIL 3
96 #define IMAP_PROTOCOL 4
101 #define IMAPBUFSIZE 8192
105 IMAP_FLAG_SEEN = 1 << 0,
106 IMAP_FLAG_ANSWERED = 1 << 1,
107 IMAP_FLAG_FLAGGED = 1 << 2,
108 IMAP_FLAG_DELETED = 1 << 3,
109 IMAP_FLAG_DRAFT = 1 << 4
112 #define IMAP_IS_SEEN(flags) ((flags & IMAP_FLAG_SEEN) != 0)
113 #define IMAP_IS_ANSWERED(flags) ((flags & IMAP_FLAG_ANSWERED) != 0)
114 #define IMAP_IS_FLAGGED(flags) ((flags & IMAP_FLAG_FLAGGED) != 0)
115 #define IMAP_IS_DELETED(flags) ((flags & IMAP_FLAG_DELETED) != 0)
116 #define IMAP_IS_DRAFT(flags) ((flags & IMAP_FLAG_DRAFT) != 0)
119 #define IMAP4_PORT 143
121 #define IMAPS_PORT 993
124 #define QUOTE_IF_REQUIRED(out, str) \
126 if (*str != '"' && strpbrk(str, " \t(){}%*") != NULL) { \
130 len = strlen(str) + 3; \
131 Xalloca(__tmp, len, return IMAP_ERROR); \
132 g_snprintf(__tmp, len, "\"%s\"", str); \
135 Xstrdup_a(out, str, return IMAP_ERROR); \
139 typedef gchar * IMAPSet;
141 struct _IMAPFolderItem
150 static Folder *imap_folder_new(const gchar * name, const gchar * path);
151 static void imap_folder_destroy(Folder * folder);
153 static IMAPSession *imap_session_new(const PrefsAccount * account);
154 static void imap_session_authenticate(IMAPSession * session,
155 const PrefsAccount * account);
156 static void imap_session_destroy(Session * session);
158 static gchar *imap_fetch_msg(Folder * folder, FolderItem * item, gint uid);
159 static gint imap_add_msg(Folder * folder,
161 const gchar * file, gboolean remove_source);
163 static gint imap_copy_msg(Folder * folder,
164 FolderItem * dest, MsgInfo * msginfo);
166 static gint imap_remove_msg(Folder * folder, FolderItem * item, gint uid);
167 static gint imap_remove_all_msg(Folder * folder, FolderItem * item);
169 static gboolean imap_is_msg_changed(Folder * folder,
170 FolderItem * item, MsgInfo * msginfo);
172 static gint imap_scan_folder(Folder * folder, FolderItem * item);
173 static void imap_scan_tree(Folder * folder);
175 static gint imap_create_tree(Folder * folder);
177 static FolderItem *imap_create_folder(Folder * folder,
180 static gint imap_rename_folder(Folder * folder,
181 FolderItem * item, const gchar * name);
182 static gint imap_remove_folder(Folder * folder, FolderItem * item);
185 static void imap_folder_init (Folder *folder,
189 static FolderItem *imap_folder_item_new (Folder *folder);
190 static void imap_folder_item_destroy (Folder *folder,
193 static IMAPSession *imap_session_get (Folder *folder);
195 static gint imap_scan_tree_recursive (IMAPSession *session,
197 static GSList *imap_parse_list (Folder *folder,
198 IMAPSession *session,
199 const gchar *real_path,
202 static void imap_create_missing_folders (Folder *folder);
203 static FolderItem *imap_create_special_folder
205 SpecialFolderItemType stype,
208 static gint imap_do_copy (Folder *folder,
211 gboolean remove_source);
213 static void imap_delete_all_cached_messages (FolderItem *item);
216 static SockInfo *imap_open (const gchar *server,
220 static SockInfo *imap_open (const gchar *server,
225 static SockInfo *imap_open_tunnel(const gchar *server,
226 const gchar *tunnelcmd,
229 static SockInfo *imap_open_tunnel(const gchar *server,
230 const gchar *tunnelcmd);
234 static SockInfo *imap_init_sock(SockInfo *sock, SSLType ssl_type);
236 static SockInfo *imap_init_sock(SockInfo *sock);
239 static gint imap_set_message_flags (IMAPSession *session,
240 MsgNumberList *numlist,
243 static gint imap_select (IMAPSession *session,
249 guint32 *uid_validity);
250 static gint imap_status (IMAPSession *session,
256 guint32 *uid_validity,
259 static void imap_parse_namespace (IMAPSession *session,
261 static void imap_get_namespace_by_list (IMAPSession *session,
263 static IMAPNameSpace *imap_find_namespace (IMAPFolder *folder,
265 static gchar imap_get_path_separator (IMAPFolder *folder,
267 static gchar *imap_get_real_path (IMAPFolder *folder,
270 static gchar *imap_parse_atom (SockInfo *sock,
275 static MsgFlags imap_parse_flags (const gchar *flag_str);
276 static MsgInfo *imap_parse_envelope (SockInfo *sock,
279 static gint imap_greeting (IMAPSession *session);
280 static gboolean imap_has_capability (IMAPSession *session,
282 void imap_free_capabilities (IMAPSession *session);
283 static const IMAPSet numberlist_to_imapset(MsgNumberList *list);
285 /* low-level IMAP4rev1 commands */
286 static gint imap_cmd_login (IMAPSession *sock,
289 static gint imap_cmd_logout (IMAPSession *sock);
290 static gint imap_cmd_noop (IMAPSession *sock);
291 static gint imap_cmd_starttls (IMAPSession *sock);
292 static gint imap_cmd_namespace (IMAPSession *sock,
294 static gint imap_cmd_list (IMAPSession *session,
296 const gchar *mailbox,
298 static gint imap_cmd_do_select (IMAPSession *sock,
304 guint32 *uid_validity);
305 static gint imap_cmd_select (IMAPSession *sock,
310 guint32 *uid_validity);
311 static gint imap_cmd_examine (IMAPSession *sock,
316 guint32 *uid_validity);
317 static gint imap_cmd_create (IMAPSession *sock,
318 const gchar *folder);
319 static gint imap_cmd_rename (IMAPSession *sock,
320 const gchar *oldfolder,
321 const gchar *newfolder);
322 static gint imap_cmd_delete (IMAPSession *sock,
323 const gchar *folder);
324 static gint imap_cmd_envelope (IMAPSession *sock,
326 static gint imap_cmd_fetch (IMAPSession *sock,
328 const gchar *filename);
329 static gint imap_cmd_append (IMAPSession *session,
330 const gchar *destfolder,
333 static gint imap_cmd_copy (IMAPSession *session,
335 const gchar *destfolder,
337 static gint imap_cmd_store (IMAPSession *sock,
340 static gint imap_cmd_expunge (IMAPSession *sock);
342 static gint imap_cmd_ok (IMAPSession *session,
344 static void imap_gen_send (IMAPSession *sock,
345 const gchar *format, ...);
346 static gint imap_gen_recv (IMAPSession *sock,
349 /* misc utility functions */
350 static gchar *strchr_cpy (const gchar *src,
354 static gchar *get_quoted (const gchar *src,
358 static gchar *search_array_contain_str (GPtrArray *array,
360 static gchar *search_array_str (GPtrArray *array,
362 static void imap_path_separator_subst (gchar *str,
365 static gchar *imap_modified_utf7_to_locale (const gchar *mutf7_str);
366 static gchar *imap_locale_to_modified_utf7 (const gchar *from);
368 static gboolean imap_rename_folder_func (GNode *node,
370 static gint imap_get_num_list (Folder *folder,
373 static GSList *imap_get_msginfos (Folder *folder,
375 GSList *msgnum_list);
376 static MsgInfo *imap_get_msginfo (Folder *folder,
379 static gboolean imap_check_msgnum_validity (Folder *folder,
381 static void imap_change_flags (Folder *folder,
384 MsgPermFlags newflags);
386 FolderClass imap_class =
392 /* Folder functions */
398 /* FolderItem functions */
399 imap_folder_item_new,
400 imap_folder_item_destroy,
408 imap_check_msgnum_validity,
410 /* Message functions */
422 FolderClass *imap_get_class(void)
427 Folder *imap_folder_new(const gchar *name, const gchar *path)
431 folder = (Folder *)g_new0(IMAPFolder, 1);
432 folder->klass = &imap_class;
433 imap_folder_init(folder, name, path);
438 void imap_folder_destroy(Folder *folder)
442 dir = folder_get_path(folder);
443 if (is_dir_exist(dir))
444 remove_dir_recursive(dir);
447 folder_remote_folder_destroy(REMOTE_FOLDER(folder));
450 static void imap_folder_init(Folder *folder, const gchar *name,
453 folder_remote_folder_init((Folder *)folder, name, path);
456 static FolderItem *imap_folder_item_new(Folder *folder)
458 IMAPFolderItem *item;
460 item = g_new0(IMAPFolderItem, 1);
463 item->uid_list = NULL;
465 return (FolderItem *)item;
468 static void imap_folder_item_destroy(Folder *folder, FolderItem *_item)
470 IMAPFolderItem *item = (IMAPFolderItem *)_item;
472 g_return_if_fail(item != NULL);
473 g_slist_free(item->uid_list);
478 static gboolean imap_reset_uid_lists_func(GNode *node, gpointer data)
480 IMAPFolderItem *item = (IMAPFolderItem *)node->data;
484 g_slist_free(item->uid_list);
485 item->uid_list = NULL;
490 static void imap_reset_uid_lists(Folder *folder)
492 if(folder->node == NULL)
495 /* Destroy all uid lists and rest last uid */
496 g_node_traverse(folder->node, G_IN_ORDER, G_TRAVERSE_ALL, -1, imap_reset_uid_lists_func, NULL);
499 static IMAPSession *imap_session_get(Folder *folder)
501 RemoteFolder *rfolder = REMOTE_FOLDER(folder);
502 IMAPSession *session = NULL;
505 g_return_val_if_fail(folder != NULL, NULL);
506 g_return_val_if_fail(FOLDER_CLASS(folder) == &imap_class, NULL);
507 g_return_val_if_fail(folder->account != NULL, NULL);
510 port = folder->account->set_imapport ? folder->account->imapport
511 : folder->account->ssl_imap == SSL_TUNNEL
512 ? IMAPS_PORT : IMAP4_PORT;
514 port = folder->account->set_imapport ? folder->account->imapport
518 /* Make sure we have a session */
519 if (rfolder->session != NULL) {
520 session = IMAP_SESSION(rfolder->session);
522 imap_reset_uid_lists(folder);
523 session = imap_session_new(folder->account);
528 if (SESSION(session)->sock->state == CONN_DISCONNECTED) {
529 debug_print("IMAP server disconnected\n");
530 session_destroy(SESSION(session));
531 imap_reset_uid_lists(folder);
532 session = imap_session_new(folder->account);
535 /* Make sure session is authenticated */
536 if (!IMAP_SESSION(session)->authenticated)
537 imap_session_authenticate(IMAP_SESSION(session), folder->account);
538 if (!IMAP_SESSION(session)->authenticated) {
539 session_destroy(SESSION(session));
540 rfolder->session = NULL;
544 /* Make sure we have parsed the IMAP namespace */
545 imap_parse_namespace(IMAP_SESSION(session),
546 IMAP_FOLDER(folder));
548 /* I think the point of this code is to avoid sending a
549 * keepalive if we've used the session recently and therefore
550 * think it's still alive. Unfortunately, most of the code
551 * does not yet check for errors on the socket, and so if the
552 * connection drops we don't notice until the timeout expires.
553 * A better solution than sending a NOOP every time would be
554 * for every command to be prepared to retry until it is
555 * successfully sent. -- mbp */
556 if (time(NULL) - session->last_access_time > SESSION_TIMEOUT) {
557 /* verify that the session is still alive */
558 if (imap_cmd_noop(session) != IMAP_SUCCESS) {
559 /* Check if this is the first try to establish a
560 connection, if yes we don't try to reconnect */
561 if (rfolder->session == NULL) {
562 log_warning(_("Connecting %s:%d failed"),
563 folder->account->recv_server, port);
564 session_destroy(SESSION(session));
567 log_warning(_("IMAP4 connection to %s:%d has been"
568 " disconnected. Reconnecting...\n"),
569 folder->account->recv_server, port);
570 session_destroy(SESSION(session));
571 /* Clear folders session to make imap_session_get create
572 a new session, because of rfolder->session == NULL
573 it will not try to reconnect again and so avoid an
575 rfolder->session = NULL;
576 session = imap_session_get(folder);
581 rfolder->session = SESSION(session);
583 session->last_access_time = time(NULL);
585 return IMAP_SESSION(session);
588 IMAPSession *imap_session_new(const PrefsAccount *account)
590 IMAPSession *session;
596 /* FIXME: IMAP over SSL only... */
599 port = account->set_imapport ? account->imapport
600 : account->ssl_imap == SSL_TUNNEL ? IMAPS_PORT : IMAP4_PORT;
601 ssl_type = account->ssl_imap;
603 port = account->set_imapport ? account->imapport
607 if (account->set_tunnelcmd) {
608 log_message(_("creating tunneled IMAP4 connection\n"));
610 if ((imap_sock = imap_open_tunnel(account->recv_server,
614 if ((imap_sock = imap_open_tunnel(account->recv_server,
615 account->tunnelcmd)) == NULL)
619 g_return_val_if_fail(account->recv_server != NULL, NULL);
621 log_message(_("creating IMAP4 connection to %s:%d ...\n"),
622 account->recv_server, port);
625 if ((imap_sock = imap_open(account->recv_server, port,
628 if ((imap_sock = imap_open(account->recv_server, port)) == NULL)
633 session = g_new0(IMAPSession, 1);
634 session_init(SESSION(session));
635 SESSION(session)->type = SESSION_IMAP;
636 SESSION(session)->server = g_strdup(account->recv_server);
637 SESSION(session)->sock = imap_sock;
639 SESSION(session)->destroy = imap_session_destroy;
641 session->capability = NULL;
643 session->mbox = NULL;
644 session->authenticated = is_preauth;
645 session->cmd_count = 0;
647 /* Only need to log in if the connection was not PREAUTH */
648 if (imap_greeting(session) != IMAP_SUCCESS) {
649 session_destroy(SESSION(session));
654 if (account->ssl_imap == SSL_STARTTLS && imap_has_capability(session, "STARTTLS")) {
657 ok = imap_cmd_starttls(session);
658 if (ok != IMAP_SUCCESS) {
659 log_warning(_("Can't start TLS session.\n"));
660 session_destroy(SESSION(session));
663 if (!ssl_init_socket_with_method(SESSION(session)->sock, SSL_METHOD_TLSv1)) {
664 session_destroy(SESSION(session));
668 imap_free_capabilities(session);
669 session->authenticated = is_preauth;
670 session->cmd_count = 1;
672 if (imap_greeting(session) != IMAP_SUCCESS) {
673 session_destroy(SESSION(session));
678 log_message("IMAP connection is %s-authenticated\n",
679 (is_preauth) ? "pre" : "un");
684 void imap_session_authenticate(IMAPSession *session, const PrefsAccount *account)
688 g_return_if_fail(account->userid != NULL);
690 pass = account->passwd;
693 tmp_pass = input_dialog_query_password(account->recv_server, account->userid);
696 Xstrdup_a(pass, tmp_pass, {g_free(tmp_pass); return;});
700 if (imap_cmd_login(session, account->userid, pass) != IMAP_SUCCESS) {
701 imap_cmd_logout(session);
705 session->authenticated = TRUE;
708 void imap_session_destroy(Session *session)
710 sock_close(session->sock);
711 session->sock = NULL;
713 g_free(IMAP_SESSION(session)->mbox);
714 imap_free_capabilities(IMAP_SESSION(session));
717 gchar *imap_fetch_msg(Folder *folder, FolderItem *item, gint uid)
719 gchar *path, *filename;
720 IMAPSession *session;
723 g_return_val_if_fail(folder != NULL, NULL);
724 g_return_val_if_fail(item != NULL, NULL);
726 path = folder_item_get_path(item);
727 if (!is_dir_exist(path))
729 filename = g_strconcat(path, G_DIR_SEPARATOR_S, itos(uid), NULL);
732 if (is_file_exist(filename)) {
733 debug_print("message %d has been already cached.\n", uid);
737 session = imap_session_get(folder);
743 ok = imap_select(session, IMAP_FOLDER(folder), item->path,
744 NULL, NULL, NULL, NULL);
745 if (ok != IMAP_SUCCESS) {
746 g_warning("can't select mailbox %s\n", item->path);
751 debug_print("getting message %d...\n", uid);
752 ok = imap_cmd_fetch(session, (guint32)uid, filename);
754 if (ok != IMAP_SUCCESS) {
755 g_warning("can't fetch message %d\n", uid);
763 gint imap_add_msg(Folder *folder, FolderItem *dest, const gchar *file,
764 gboolean remove_source)
767 IMAPSession *session;
770 g_return_val_if_fail(folder != NULL, -1);
771 g_return_val_if_fail(dest != NULL, -1);
772 g_return_val_if_fail(file != NULL, -1);
774 session = imap_session_get(folder);
775 if (!session) return -1;
777 destdir = imap_get_real_path(IMAP_FOLDER(folder), dest->path);
778 ok = imap_cmd_append(session, destdir, file, &newuid);
781 if (ok != IMAP_SUCCESS) {
782 g_warning("can't append message %s\n", file);
787 if (unlink(file) < 0)
788 FILE_OP_ERROR(file, "unlink");
794 static gint imap_do_copy(Folder *folder, FolderItem *dest, MsgInfo *msginfo,
795 gboolean remove_source)
798 IMAPSession *session;
802 g_return_val_if_fail(folder != NULL, -1);
803 g_return_val_if_fail(FOLDER_CLASS(folder) == &imap_class, -1);
804 g_return_val_if_fail(dest != NULL, -1);
805 g_return_val_if_fail(msginfo != NULL, -1);
807 session = imap_session_get(folder);
808 if (!session) return -1;
810 if (msginfo->folder == dest) {
811 g_warning("the src folder is identical to the dest.\n");
815 destdir = imap_get_real_path(IMAP_FOLDER(folder), dest->path);
817 /* ensure source folder selected */
818 ok = imap_select(session, IMAP_FOLDER(folder), msginfo->folder->path,
819 NULL, NULL, NULL, NULL);
820 if (ok != IMAP_SUCCESS)
824 debug_print("Moving message %s%c%d to %s ...\n",
825 msginfo->folder->path, G_DIR_SEPARATOR,
826 msginfo->msgnum, destdir);
828 debug_print("Copying message %s%c%d to %s ...\n",
829 msginfo->folder->path, G_DIR_SEPARATOR,
830 msginfo->msgnum, destdir);
832 ok = imap_cmd_copy(session, msginfo->msgnum, destdir, &newuid);
834 if (ok == IMAP_SUCCESS && remove_source) {
835 MsgNumberList numlist;
838 numlist.data = GINT_TO_POINTER(msginfo->msgnum);
840 imap_set_message_flags(session, &numlist,
841 IMAP_FLAG_DELETED, TRUE);
842 ok = imap_cmd_expunge(session);
847 if (ok == IMAP_SUCCESS)
853 gint imap_copy_msg(Folder *folder, FolderItem *dest, MsgInfo *msginfo)
858 g_return_val_if_fail(folder != NULL, -1);
859 g_return_val_if_fail(dest != NULL, -1);
860 g_return_val_if_fail(msginfo != NULL, -1);
861 g_return_val_if_fail(msginfo->folder != NULL, -1);
863 if (folder == msginfo->folder->folder)
864 return imap_do_copy(folder, dest, msginfo, FALSE);
866 srcfile = procmsg_get_message_file(msginfo);
867 if (!srcfile) return -1;
869 ret = imap_add_msg(folder, dest, srcfile, FALSE);
876 gint imap_remove_msg(Folder *folder, FolderItem *item, gint uid)
879 IMAPSession *session;
881 MsgNumberList numlist;
883 g_return_val_if_fail(folder != NULL, -1);
884 g_return_val_if_fail(FOLDER_CLASS(folder) == &imap_class, -1);
885 g_return_val_if_fail(item != NULL, -1);
887 session = imap_session_get(folder);
888 if (!session) return -1;
890 ok = imap_select(session, IMAP_FOLDER(folder), item->path,
891 NULL, NULL, NULL, NULL);
892 if (ok != IMAP_SUCCESS)
896 numlist.data = GINT_TO_POINTER(uid);
898 ok = imap_set_message_flags
899 (IMAP_SESSION(REMOTE_FOLDER(folder)->session),
900 &numlist, IMAP_FLAG_DELETED, TRUE);
901 if (ok != IMAP_SUCCESS) {
902 log_warning(_("can't set deleted flags: %d\n"), uid);
906 ok = imap_cmd_expunge(session);
907 if (ok != IMAP_SUCCESS) {
908 log_warning(_("can't expunge\n"));
912 dir = folder_item_get_path(item);
913 if (is_dir_exist(dir))
914 remove_numbered_files(dir, uid, uid);
920 gint imap_remove_all_msg(Folder *folder, FolderItem *item)
922 gint exists, recent, unseen;
923 guint32 uid_validity;
925 IMAPSession *session;
928 g_return_val_if_fail(folder != NULL, -1);
929 g_return_val_if_fail(item != NULL, -1);
931 session = imap_session_get(folder);
932 if (!session) return -1;
934 ok = imap_select(session, IMAP_FOLDER(folder), item->path,
935 &exists, &recent, &unseen, &uid_validity);
936 if (ok != IMAP_SUCCESS)
941 imap_gen_send(session,
942 "STORE 1:%d +FLAGS (\\Deleted)", exists);
943 ok = imap_cmd_ok(session, NULL);
944 if (ok != IMAP_SUCCESS) {
945 log_warning(_("can't set deleted flags: 1:%d\n"), exists);
949 ok = imap_cmd_expunge(session);
950 if (ok != IMAP_SUCCESS) {
951 log_warning(_("can't expunge\n"));
955 dir = folder_item_get_path(item);
956 if (is_dir_exist(dir))
957 remove_all_numbered_files(dir);
963 gboolean imap_is_msg_changed(Folder *folder, FolderItem *item, MsgInfo *msginfo)
965 /* TODO: properly implement this method */
969 gint imap_scan_folder(Folder *folder, FolderItem *item)
971 IMAPSession *session;
972 gint messages, recent, unseen;
973 guint32 uid_next, uid_validity;
976 g_return_val_if_fail(folder != NULL, -1);
977 g_return_val_if_fail(item != NULL, -1);
979 session = imap_session_get(folder);
980 if (!session) return -1;
982 ok = imap_status(session, IMAP_FOLDER(folder), item->path,
983 &messages, &recent, &uid_next, &uid_validity, &unseen);
984 if (ok != IMAP_SUCCESS) return -1;
986 item->new_msgs = unseen > 0 ? recent : 0;
987 item->unread_msgs = unseen;
988 item->total_msgs = messages;
989 item->last_num = (messages > 0 && uid_next > 0) ? uid_next - 1 : 0;
990 /* item->mtime = uid_validity; */
995 void imap_scan_tree(Folder *folder)
998 IMAPSession *session;
999 gchar *root_folder = NULL;
1001 g_return_if_fail(folder != NULL);
1002 g_return_if_fail(folder->account != NULL);
1004 session = imap_session_get(folder);
1006 if (!folder->node) {
1007 folder_tree_destroy(folder);
1008 item = folder_item_new(folder, folder->name, NULL);
1009 item->folder = folder;
1010 folder->node = g_node_new(item);
1015 if (folder->account->imap_dir && *folder->account->imap_dir) {
1016 Xstrdup_a(root_folder, folder->account->imap_dir, return);
1017 strtailchomp(root_folder, '/');
1018 debug_print("IMAP root directory: %s\n", root_folder);
1021 item = folder_item_new(folder, folder->name, root_folder);
1022 item->folder = folder;
1023 item->no_select = TRUE;
1024 folder->node = g_node_new(item);
1026 imap_scan_tree_recursive(session, item);
1028 imap_create_missing_folders(folder);
1031 static gint imap_scan_tree_recursive(IMAPSession *session, FolderItem *item)
1034 IMAPFolder *imapfolder;
1035 FolderItem *new_item;
1036 GSList *item_list, *cur;
1038 gchar *wildcard_path;
1042 g_return_val_if_fail(item != NULL, -1);
1043 g_return_val_if_fail(item->folder != NULL, -1);
1044 g_return_val_if_fail(item->no_sub == FALSE, -1);
1046 folder = FOLDER(item->folder);
1047 imapfolder = IMAP_FOLDER(folder);
1049 separator = imap_get_path_separator(imapfolder, item->path);
1051 if (item->folder->ui_func)
1052 item->folder->ui_func(folder, item, folder->ui_func_data);
1055 wildcard[0] = separator;
1058 real_path = imap_get_real_path(imapfolder, item->path);
1062 real_path = g_strdup("");
1065 Xstrcat_a(wildcard_path, real_path, wildcard,
1066 {g_free(real_path); return IMAP_ERROR;});
1067 QUOTE_IF_REQUIRED(wildcard_path, wildcard_path);
1069 imap_gen_send(session, "LIST \"\" %s",
1072 strtailchomp(real_path, separator);
1073 item_list = imap_parse_list(folder, session, real_path, NULL);
1076 for (cur = item_list; cur != NULL; cur = cur->next) {
1077 new_item = cur->data;
1078 if (!strcmp(new_item->path, "INBOX")) {
1079 if (!folder->inbox) {
1080 new_item->stype = F_INBOX;
1081 item->folder->inbox = new_item;
1083 folder_item_destroy(new_item);
1086 } else if (!item->parent || item->stype == F_INBOX) {
1089 base = g_basename(new_item->path);
1091 if (!folder->outbox && !strcasecmp(base, "Sent")) {
1092 new_item->stype = F_OUTBOX;
1093 folder->outbox = new_item;
1094 } else if (!folder->draft && !strcasecmp(base, "Drafts")) {
1095 new_item->stype = F_DRAFT;
1096 folder->draft = new_item;
1097 } else if (!folder->queue && !strcasecmp(base, "Queue")) {
1098 new_item->stype = F_QUEUE;
1099 folder->queue = new_item;
1100 } else if (!folder->trash && !strcasecmp(base, "Trash")) {
1101 new_item->stype = F_TRASH;
1102 folder->trash = new_item;
1105 folder_item_append(item, new_item);
1106 if (new_item->no_select == FALSE)
1107 imap_scan_folder(folder, new_item);
1108 if (new_item->no_sub == FALSE)
1109 imap_scan_tree_recursive(session, new_item);
1112 return IMAP_SUCCESS;
1115 static GSList *imap_parse_list(Folder *folder, IMAPSession *session,
1116 const gchar *real_path, gchar *separator)
1118 gchar buf[IMAPBUFSIZE];
1120 gchar separator_str[16];
1123 gchar *loc_name, *loc_path;
1124 GSList *item_list = NULL;
1126 FolderItem *new_item;
1128 debug_print("getting list of %s ...\n",
1129 *real_path ? real_path : "\"\"");
1131 str = g_string_new(NULL);
1134 if (sock_gets(SESSION(session)->sock, buf, sizeof(buf)) <= 0) {
1135 log_warning(_("error occurred while getting LIST.\n"));
1139 if (buf[0] != '*' || buf[1] != ' ') {
1140 log_print("IMAP4< %s\n", buf);
1143 debug_print("IMAP4< %s\n", buf);
1145 g_string_assign(str, buf);
1147 if (strncmp(p, "LIST ", 5) != 0) continue;
1150 if (*p != '(') continue;
1152 p = strchr_cpy(p, ')', flags, sizeof(flags));
1154 while (*p == ' ') p++;
1156 p = strchr_cpy(p, ' ', separator_str, sizeof(separator_str));
1158 extract_quote(separator_str, '"');
1159 if (!strcmp(separator_str, "NIL"))
1160 separator_str[0] = '\0';
1162 *separator = separator_str[0];
1165 while (*p == ' ') p++;
1166 if (*p == '{' || *p == '"')
1167 p = imap_parse_atom(SESSION(session)->sock, p,
1168 buf, sizeof(buf), str);
1170 strncpy2(buf, p, sizeof(buf));
1171 strtailchomp(buf, separator_str[0]);
1172 if (buf[0] == '\0') continue;
1173 if (!strcmp(buf, real_path)) continue;
1175 if (separator_str[0] != '\0')
1176 subst_char(buf, separator_str[0], '/');
1177 name = g_basename(buf);
1178 if (name[0] == '.') continue;
1180 loc_name = imap_modified_utf7_to_locale(name);
1181 loc_path = imap_modified_utf7_to_locale(buf);
1182 new_item = folder_item_new(folder, loc_name, loc_path);
1183 if (strcasestr(flags, "\\Noinferiors") != NULL)
1184 new_item->no_sub = TRUE;
1185 if (strcmp(buf, "INBOX") != 0 &&
1186 strcasestr(flags, "\\Noselect") != NULL)
1187 new_item->no_select = TRUE;
1189 item_list = g_slist_append(item_list, new_item);
1191 debug_print("folder %s has been added.\n", loc_path);
1196 g_string_free(str, TRUE);
1201 gint imap_create_tree(Folder *folder)
1203 g_return_val_if_fail(folder != NULL, -1);
1204 g_return_val_if_fail(folder->node != NULL, -1);
1205 g_return_val_if_fail(folder->node->data != NULL, -1);
1206 g_return_val_if_fail(folder->account != NULL, -1);
1208 imap_scan_tree(folder);
1209 imap_create_missing_folders(folder);
1214 static void imap_create_missing_folders(Folder *folder)
1216 g_return_if_fail(folder != NULL);
1219 folder->inbox = imap_create_special_folder
1220 (folder, F_INBOX, "INBOX");
1222 if (!folder->outbox)
1223 folder->outbox = imap_create_special_folder
1224 (folder, F_OUTBOX, "Sent");
1226 folder->draft = imap_create_special_folder
1227 (folder, F_DRAFT, "Drafts");
1229 folder->queue = imap_create_special_folder
1230 (folder, F_QUEUE, "Queue");
1233 folder->trash = imap_create_special_folder
1234 (folder, F_TRASH, "Trash");
1237 static FolderItem *imap_create_special_folder(Folder *folder,
1238 SpecialFolderItemType stype,
1242 FolderItem *new_item;
1244 g_return_val_if_fail(folder != NULL, NULL);
1245 g_return_val_if_fail(folder->node != NULL, NULL);
1246 g_return_val_if_fail(folder->node->data != NULL, NULL);
1247 g_return_val_if_fail(folder->account != NULL, NULL);
1248 g_return_val_if_fail(name != NULL, NULL);
1250 item = FOLDER_ITEM(folder->node->data);
1251 new_item = imap_create_folder(folder, item, name);
1254 g_warning("Can't create '%s'\n", name);
1255 if (!folder->inbox) return NULL;
1257 new_item = imap_create_folder(folder, folder->inbox, name);
1259 g_warning("Can't create '%s' under INBOX\n", name);
1261 new_item->stype = stype;
1263 new_item->stype = stype;
1268 FolderItem *imap_create_folder(Folder *folder, FolderItem *parent,
1271 gchar *dirpath, *imap_path;
1272 IMAPSession *session;
1273 FolderItem *new_item;
1279 g_return_val_if_fail(folder != NULL, NULL);
1280 g_return_val_if_fail(folder->account != NULL, NULL);
1281 g_return_val_if_fail(parent != NULL, NULL);
1282 g_return_val_if_fail(name != NULL, NULL);
1284 session = imap_session_get(folder);
1285 if (!session) return NULL;
1287 if (!parent->parent && strcmp(name, "INBOX") == 0)
1288 dirpath = g_strdup(name);
1289 else if (parent->path)
1290 dirpath = g_strconcat(parent->path, "/", name, NULL);
1291 else if ((p = strchr(name, '/')) != NULL && *(p + 1) != '\0')
1292 dirpath = g_strdup(name);
1293 else if (folder->account->imap_dir && *folder->account->imap_dir) {
1296 Xstrdup_a(imap_dir, folder->account->imap_dir, return NULL);
1297 strtailchomp(imap_dir, '/');
1298 dirpath = g_strconcat(imap_dir, "/", name, NULL);
1300 dirpath = g_strdup(name);
1302 /* keep trailing directory separator to create a folder that contains
1304 imap_path = imap_locale_to_modified_utf7(dirpath);
1305 strtailchomp(dirpath, '/');
1306 Xstrdup_a(new_name, name, {g_free(dirpath); return NULL;});
1307 strtailchomp(new_name, '/');
1308 separator = imap_get_path_separator(IMAP_FOLDER(folder), imap_path);
1309 imap_path_separator_subst(imap_path, separator);
1310 subst_char(new_name, '/', separator);
1312 if (strcmp(name, "INBOX") != 0) {
1315 gboolean exist = FALSE;
1317 argbuf = g_ptr_array_new();
1318 ok = imap_cmd_list(session, NULL, imap_path,
1320 if (ok != IMAP_SUCCESS) {
1321 log_warning(_("can't create mailbox: LIST failed\n"));
1324 ptr_array_free_strings(argbuf);
1325 g_ptr_array_free(argbuf, TRUE);
1329 for (i = 0; i < argbuf->len; i++) {
1331 str = g_ptr_array_index(argbuf, i);
1332 if (!strncmp(str, "LIST ", 5)) {
1337 ptr_array_free_strings(argbuf);
1338 g_ptr_array_free(argbuf, TRUE);
1341 ok = imap_cmd_create(session, imap_path);
1342 if (ok != IMAP_SUCCESS) {
1343 log_warning(_("can't create mailbox\n"));
1351 new_item = folder_item_new(folder, new_name, dirpath);
1352 folder_item_append(parent, new_item);
1356 dirpath = folder_item_get_path(new_item);
1357 if (!is_dir_exist(dirpath))
1358 make_dir_hier(dirpath);
1364 gint imap_rename_folder(Folder *folder, FolderItem *item, const gchar *name)
1368 gchar *real_oldpath;
1369 gchar *real_newpath;
1372 gchar *old_cache_dir;
1373 gchar *new_cache_dir;
1374 IMAPSession *session;
1377 gint exists, recent, unseen;
1378 guint32 uid_validity;
1380 g_return_val_if_fail(folder != NULL, -1);
1381 g_return_val_if_fail(item != NULL, -1);
1382 g_return_val_if_fail(item->path != NULL, -1);
1383 g_return_val_if_fail(name != NULL, -1);
1385 session = imap_session_get(folder);
1386 if (!session) return -1;
1388 real_oldpath = imap_get_real_path(IMAP_FOLDER(folder), item->path);
1390 g_free(session->mbox);
1391 session->mbox = NULL;
1392 ok = imap_cmd_examine(session, "INBOX",
1393 &exists, &recent, &unseen, &uid_validity);
1394 if (ok != IMAP_SUCCESS) {
1395 g_free(real_oldpath);
1399 separator = imap_get_path_separator(IMAP_FOLDER(folder), item->path);
1400 if (strchr(item->path, G_DIR_SEPARATOR)) {
1401 dirpath = g_dirname(item->path);
1402 newpath = g_strconcat(dirpath, G_DIR_SEPARATOR_S, name, NULL);
1405 newpath = g_strdup(name);
1407 real_newpath = imap_locale_to_modified_utf7(newpath);
1408 imap_path_separator_subst(real_newpath, separator);
1410 ok = imap_cmd_rename(session, real_oldpath, real_newpath);
1411 if (ok != IMAP_SUCCESS) {
1412 log_warning(_("can't rename mailbox: %s to %s\n"),
1413 real_oldpath, real_newpath);
1414 g_free(real_oldpath);
1416 g_free(real_newpath);
1421 item->name = g_strdup(name);
1423 old_cache_dir = folder_item_get_path(item);
1425 node = g_node_find(item->folder->node, G_PRE_ORDER, G_TRAVERSE_ALL,
1427 paths[0] = g_strdup(item->path);
1429 g_node_traverse(node, G_PRE_ORDER, G_TRAVERSE_ALL, -1,
1430 imap_rename_folder_func, paths);
1432 if (is_dir_exist(old_cache_dir)) {
1433 new_cache_dir = folder_item_get_path(item);
1434 if (rename(old_cache_dir, new_cache_dir) < 0) {
1435 FILE_OP_ERROR(old_cache_dir, "rename");
1437 g_free(new_cache_dir);
1440 g_free(old_cache_dir);
1443 g_free(real_oldpath);
1444 g_free(real_newpath);
1449 gint imap_remove_folder(Folder *folder, FolderItem *item)
1452 IMAPSession *session;
1455 gint exists, recent, unseen;
1456 guint32 uid_validity;
1458 g_return_val_if_fail(folder != NULL, -1);
1459 g_return_val_if_fail(item != NULL, -1);
1460 g_return_val_if_fail(item->path != NULL, -1);
1462 session = imap_session_get(folder);
1463 if (!session) return -1;
1465 path = imap_get_real_path(IMAP_FOLDER(folder), item->path);
1467 ok = imap_cmd_examine(session, "INBOX",
1468 &exists, &recent, &unseen, &uid_validity);
1469 if (ok != IMAP_SUCCESS) {
1474 ok = imap_cmd_delete(session, path);
1475 if (ok != IMAP_SUCCESS) {
1476 log_warning(_("can't delete mailbox\n"));
1482 cache_dir = folder_item_get_path(item);
1483 if (is_dir_exist(cache_dir) && remove_dir_recursive(cache_dir) < 0)
1484 g_warning("can't remove directory '%s'\n", cache_dir);
1486 folder_item_remove(item);
1491 static GSList *imap_get_uncached_messages(IMAPSession *session,
1493 MsgNumberList *numlist)
1496 GSList *newlist = NULL;
1497 GSList *llast = NULL;
1501 g_return_val_if_fail(session != NULL, NULL);
1502 g_return_val_if_fail(item != NULL, NULL);
1503 g_return_val_if_fail(item->folder != NULL, NULL);
1504 g_return_val_if_fail(FOLDER_CLASS(item->folder) == &imap_class, NULL);
1506 if (imap_cmd_envelope(session, numberlist_to_imapset(numlist))
1508 log_warning(_("can't get envelope\n"));
1512 str = g_string_new(NULL);
1515 if ((tmp = sock_getline(SESSION(session)->sock)) == NULL) {
1516 log_warning(_("error occurred while getting envelope.\n"));
1517 g_string_free(str, TRUE);
1521 if (tmp[0] != '*' || tmp[1] != ' ') {
1522 log_print("IMAP4< %s\n", tmp);
1526 if (strstr(tmp, "FETCH") == NULL) {
1527 log_print("IMAP4< %s\n", tmp);
1531 log_print("IMAP4< %s\n", tmp);
1532 g_string_assign(str, tmp);
1535 msginfo = imap_parse_envelope
1536 (SESSION(session)->sock, item, str);
1538 log_warning(_("can't parse envelope: %s\n"), str->str);
1541 if (item->stype == F_QUEUE) {
1542 MSG_SET_TMP_FLAGS(msginfo->flags, MSG_QUEUED);
1543 } else if (item->stype == F_DRAFT) {
1544 MSG_SET_TMP_FLAGS(msginfo->flags, MSG_DRAFT);
1547 msginfo->folder = item;
1550 llast = newlist = g_slist_append(newlist, msginfo);
1552 llast = g_slist_append(llast, msginfo);
1553 llast = llast->next;
1557 g_string_free(str, TRUE);
1562 static void imap_delete_all_cached_messages(FolderItem *item)
1566 g_return_if_fail(item != NULL);
1567 g_return_if_fail(item->folder != NULL);
1568 g_return_if_fail(FOLDER_CLASS(item->folder) == &imap_class);
1570 debug_print("Deleting all cached messages...\n");
1572 dir = folder_item_get_path(item);
1573 if (is_dir_exist(dir))
1574 remove_all_numbered_files(dir);
1577 debug_print("done.\n");
1581 static SockInfo *imap_open_tunnel(const gchar *server,
1582 const gchar *tunnelcmd,
1585 static SockInfo *imap_open_tunnel(const gchar *server,
1586 const gchar *tunnelcmd)
1591 if ((sock = sock_connect_cmd(server, tunnelcmd)) == NULL) {
1592 log_warning(_("Can't establish IMAP4 session with: %s\n"),
1597 return imap_init_sock(sock, ssl_type);
1599 return imap_init_sock(sock);
1605 static SockInfo *imap_open(const gchar *server, gushort port,
1608 static SockInfo *imap_open(const gchar *server, gushort port)
1613 if ((sock = sock_connect(server, port)) == NULL) {
1614 log_warning(_("Can't connect to IMAP4 server: %s:%d\n"),
1620 if (ssl_type == SSL_TUNNEL && !ssl_init_socket(sock)) {
1621 log_warning(_("Can't establish IMAP4 session with: %s:%d\n"),
1631 static SockInfo *imap_init_sock(SockInfo *sock, SSLType ssl_type)
1633 static SockInfo *imap_init_sock(SockInfo *sock)
1640 static GList *imap_parse_namespace_str(gchar *str)
1645 IMAPNameSpace *namespace;
1646 GList *ns_list = NULL;
1648 while (*p != '\0') {
1649 /* parse ("#foo" "/") */
1651 while (*p && *p != '(') p++;
1652 if (*p == '\0') break;
1655 while (*p && *p != '"') p++;
1656 if (*p == '\0') break;
1660 while (*p && *p != '"') p++;
1661 if (*p == '\0') break;
1665 while (*p && isspace(*p)) p++;
1666 if (*p == '\0') break;
1667 if (strncmp(p, "NIL", 3) == 0)
1669 else if (*p == '"') {
1672 while (*p && *p != '"') p++;
1673 if (*p == '\0') break;
1678 while (*p && *p != ')') p++;
1679 if (*p == '\0') break;
1682 namespace = g_new(IMAPNameSpace, 1);
1683 namespace->name = g_strdup(name);
1684 namespace->separator = separator ? separator[0] : '\0';
1685 ns_list = g_list_append(ns_list, namespace);
1691 static void imap_parse_namespace(IMAPSession *session, IMAPFolder *folder)
1696 g_return_if_fail(session != NULL);
1697 g_return_if_fail(folder != NULL);
1699 if (folder->ns_personal != NULL ||
1700 folder->ns_others != NULL ||
1701 folder->ns_shared != NULL)
1704 if (!imap_has_capability(session, "NAMESPACE")) {
1705 imap_get_namespace_by_list(session, folder);
1709 if (imap_cmd_namespace(session, &ns_str)
1711 log_warning(_("can't get namespace\n"));
1715 str_array = strsplit_parenthesis(ns_str, '(', ')', 3);
1716 if (str_array == NULL) {
1718 imap_get_namespace_by_list(session, folder);
1722 folder->ns_personal = imap_parse_namespace_str(str_array[0]);
1723 if (str_array[0] && str_array[1])
1724 folder->ns_others = imap_parse_namespace_str(str_array[1]);
1725 if (str_array[0] && str_array[1] && str_array[2])
1726 folder->ns_shared = imap_parse_namespace_str(str_array[2]);
1727 g_strfreev(str_array);
1731 static void imap_get_namespace_by_list(IMAPSession *session, IMAPFolder *folder)
1733 GSList *item_list, *cur;
1734 gchar separator = '\0';
1735 IMAPNameSpace *namespace;
1737 g_return_if_fail(session != NULL);
1738 g_return_if_fail(folder != NULL);
1740 if (folder->ns_personal != NULL ||
1741 folder->ns_others != NULL ||
1742 folder->ns_shared != NULL)
1745 imap_gen_send(session, "LIST \"\" \"\"");
1746 item_list = imap_parse_list(NULL, session, "", &separator);
1747 for (cur = item_list; cur != NULL; cur = cur->next)
1748 folder_item_destroy(FOLDER_ITEM(cur->data));
1749 g_slist_free(item_list);
1751 namespace = g_new(IMAPNameSpace, 1);
1752 namespace->name = g_strdup("");
1753 namespace->separator = separator;
1754 folder->ns_personal = g_list_append(NULL, namespace);
1757 static IMAPNameSpace *imap_find_namespace_from_list(GList *ns_list,
1760 IMAPNameSpace *namespace = NULL;
1761 gchar *tmp_path, *name;
1763 if (!path) path = "";
1765 Xstrcat_a(tmp_path, path, "/", return NULL);
1767 for (; ns_list != NULL; ns_list = ns_list->next) {
1768 IMAPNameSpace *tmp_ns = ns_list->data;
1770 Xstrdup_a(name, tmp_ns->name, return namespace);
1771 if (tmp_ns->separator && tmp_ns->separator != '/')
1772 subst_char(name, tmp_ns->separator, '/');
1773 if (strncmp(tmp_path, name, strlen(name)) == 0)
1780 static IMAPNameSpace *imap_find_namespace(IMAPFolder *folder,
1783 IMAPNameSpace *namespace;
1785 g_return_val_if_fail(folder != NULL, NULL);
1787 namespace = imap_find_namespace_from_list(folder->ns_personal, path);
1788 if (namespace) return namespace;
1789 namespace = imap_find_namespace_from_list(folder->ns_others, path);
1790 if (namespace) return namespace;
1791 namespace = imap_find_namespace_from_list(folder->ns_shared, path);
1792 if (namespace) return namespace;
1797 static gchar imap_get_path_separator(IMAPFolder *folder, const gchar *path)
1799 IMAPNameSpace *namespace;
1800 gchar separator = '/';
1802 namespace = imap_find_namespace(folder, path);
1803 if (namespace && namespace->separator)
1804 separator = namespace->separator;
1809 static gchar *imap_get_real_path(IMAPFolder *folder, const gchar *path)
1814 g_return_val_if_fail(folder != NULL, NULL);
1815 g_return_val_if_fail(path != NULL, NULL);
1817 real_path = imap_locale_to_modified_utf7(path);
1818 separator = imap_get_path_separator(folder, path);
1819 imap_path_separator_subst(real_path, separator);
1824 static gchar *imap_parse_atom(SockInfo *sock, gchar *src,
1825 gchar *dest, gint dest_len, GString *str)
1827 gchar *cur_pos = src;
1830 g_return_val_if_fail(str != NULL, cur_pos);
1832 /* read the next line if the current response buffer is empty */
1833 while (isspace(*cur_pos)) cur_pos++;
1834 while (*cur_pos == '\0') {
1835 if ((nextline = sock_getline(sock)) == NULL)
1837 g_string_assign(str, nextline);
1839 strretchomp(nextline);
1840 /* log_print("IMAP4< %s\n", nextline); */
1841 debug_print("IMAP4< %s\n", nextline);
1844 while (isspace(*cur_pos)) cur_pos++;
1847 if (!strncmp(cur_pos, "NIL", 3)) {
1850 } else if (*cur_pos == '\"') {
1853 p = get_quoted(cur_pos, '\"', dest, dest_len);
1854 cur_pos = p ? p : cur_pos + 2;
1855 } else if (*cur_pos == '{') {
1860 cur_pos = strchr_cpy(cur_pos + 1, '}', buf, sizeof(buf));
1863 g_string_truncate(str, 0);
1867 if ((nextline = sock_getline(sock)) == NULL)
1869 line_len += strlen(nextline);
1870 g_string_append(str, nextline);
1872 strretchomp(nextline);
1873 /* log_print("IMAP4< %s\n", nextline); */
1874 debug_print("IMAP4< %s\n", nextline);
1876 } while (line_len < len);
1878 memcpy(dest, cur_pos, MIN(len, dest_len - 1));
1879 dest[MIN(len, dest_len - 1)] = '\0';
1886 static gchar *imap_get_header(SockInfo *sock, gchar *cur_pos, gchar **headers,
1896 g_return_val_if_fail(str != NULL, cur_pos);
1898 while (isspace(*cur_pos)) cur_pos++;
1900 g_return_val_if_fail(*cur_pos == '{', cur_pos);
1902 cur_pos = strchr_cpy(cur_pos + 1, '}', buf, sizeof(buf));
1905 g_string_truncate(str, 0);
1909 if ((nextline = sock_getline(sock)) == NULL)
1911 block_len += strlen(nextline);
1912 g_string_append(str, nextline);
1914 strretchomp(nextline);
1915 /* debug_print("IMAP4< %s\n", nextline); */
1917 } while (block_len < len);
1919 debug_print("IMAP4< [contents of BODY.PEEK[HEADER.FIELDS (...)]]\n");
1921 *headers = g_strndup(cur_pos, len);
1924 while (isspace(*cur_pos)) cur_pos++;
1925 while (*cur_pos == '\0') {
1926 if ((nextline = sock_getline(sock)) == NULL)
1928 g_string_assign(str, nextline);
1930 strretchomp(nextline);
1931 debug_print("IMAP4< %s\n", nextline);
1934 while (isspace(*cur_pos)) cur_pos++;
1940 static MsgFlags imap_parse_flags(const gchar *flag_str)
1942 const gchar *p = flag_str;
1943 MsgFlags flags = {0, 0};
1945 flags.perm_flags = MSG_UNREAD;
1947 while ((p = strchr(p, '\\')) != NULL) {
1950 if (g_strncasecmp(p, "Recent", 6) == 0 && MSG_IS_UNREAD(flags)) {
1951 MSG_SET_PERM_FLAGS(flags, MSG_NEW);
1952 } else if (g_strncasecmp(p, "Seen", 4) == 0) {
1953 MSG_UNSET_PERM_FLAGS(flags, MSG_NEW|MSG_UNREAD);
1954 } else if (g_strncasecmp(p, "Deleted", 7) == 0) {
1955 MSG_SET_PERM_FLAGS(flags, MSG_DELETED);
1956 } else if (g_strncasecmp(p, "Flagged", 7) == 0) {
1957 MSG_SET_PERM_FLAGS(flags, MSG_MARKED);
1958 } else if (g_strncasecmp(p, "Answered", 8) == 0) {
1959 MSG_SET_PERM_FLAGS(flags, MSG_REPLIED);
1966 static MsgInfo *imap_parse_envelope(SockInfo *sock, FolderItem *item,
1969 gchar buf[IMAPBUFSIZE];
1970 MsgInfo *msginfo = NULL;
1975 MsgFlags flags = {0, 0}, imap_flags = {0, 0};
1977 g_return_val_if_fail(line_str != NULL, NULL);
1978 g_return_val_if_fail(line_str->str[0] == '*' &&
1979 line_str->str[1] == ' ', NULL);
1981 MSG_SET_TMP_FLAGS(flags, MSG_IMAP);
1982 if (item->stype == F_QUEUE) {
1983 MSG_SET_TMP_FLAGS(flags, MSG_QUEUED);
1984 } else if (item->stype == F_DRAFT) {
1985 MSG_SET_TMP_FLAGS(flags, MSG_DRAFT);
1988 cur_pos = line_str->str + 2;
1990 #define PARSE_ONE_ELEMENT(ch) \
1992 cur_pos = strchr_cpy(cur_pos, ch, buf, sizeof(buf)); \
1993 if (cur_pos == NULL) { \
1994 g_warning("cur_pos == NULL\n"); \
1995 procmsg_msginfo_free(msginfo); \
2000 PARSE_ONE_ELEMENT(' ');
2003 PARSE_ONE_ELEMENT(' ');
2004 g_return_val_if_fail(!strcmp(buf, "FETCH"), NULL);
2006 g_return_val_if_fail(*cur_pos == '(', NULL);
2009 while (*cur_pos != '\0' && *cur_pos != ')') {
2010 while (*cur_pos == ' ') cur_pos++;
2012 if (!strncmp(cur_pos, "UID ", 4)) {
2014 uid = strtoul(cur_pos, &cur_pos, 10);
2015 } else if (!strncmp(cur_pos, "FLAGS ", 6)) {
2017 if (*cur_pos != '(') {
2018 g_warning("*cur_pos != '('\n");
2019 procmsg_msginfo_free(msginfo);
2023 PARSE_ONE_ELEMENT(')');
2024 imap_flags = imap_parse_flags(buf);
2025 } else if (!strncmp(cur_pos, "RFC822.SIZE ", 12)) {
2027 size = strtol(cur_pos, &cur_pos, 10);
2028 } else if (!strncmp(cur_pos, "BODY[HEADER.FIELDS ", 19)) {
2032 if (*cur_pos != '(') {
2033 g_warning("*cur_pos != '('\n");
2034 procmsg_msginfo_free(msginfo);
2038 PARSE_ONE_ELEMENT(')');
2039 if (*cur_pos != ']') {
2040 g_warning("*cur_pos != ']'\n");
2041 procmsg_msginfo_free(msginfo);
2046 cur_pos = imap_get_header(sock, cur_pos, &headers,
2048 msginfo = procheader_parse_str(headers, flags, FALSE, FALSE);
2051 g_warning("invalid FETCH response: %s\n", cur_pos);
2057 msginfo->msgnum = uid;
2058 msginfo->size = size;
2059 msginfo->flags.tmp_flags |= imap_flags.tmp_flags;
2060 msginfo->flags.perm_flags = imap_flags.perm_flags;
2066 static gint imap_set_message_flags(IMAPSession *session,
2067 MsgNumberList *numlist,
2074 buf = g_string_new(is_set ? "+FLAGS (" : "-FLAGS (");
2076 if (IMAP_IS_SEEN(flags)) g_string_append(buf, "\\Seen ");
2077 if (IMAP_IS_ANSWERED(flags)) g_string_append(buf, "\\Answered ");
2078 if (IMAP_IS_FLAGGED(flags)) g_string_append(buf, "\\Flagged ");
2079 if (IMAP_IS_DELETED(flags)) g_string_append(buf, "\\Deleted ");
2080 if (IMAP_IS_DRAFT(flags)) g_string_append(buf, "\\Draft");
2082 if (buf->str[buf->len - 1] == ' ')
2083 g_string_truncate(buf, buf->len - 1);
2085 g_string_append_c(buf, ')');
2087 ok = imap_cmd_store(session, numberlist_to_imapset(numlist),
2089 g_string_free(buf, TRUE);
2094 static gint imap_select(IMAPSession *session, IMAPFolder *folder,
2096 gint *exists, gint *recent, gint *unseen,
2097 guint32 *uid_validity)
2101 gint exists_, recent_, unseen_, uid_validity_;
2103 if (!exists || !recent || !unseen || !uid_validity) {
2104 if (session->mbox && strcmp(session->mbox, path) == 0)
2105 return IMAP_SUCCESS;
2109 uid_validity = &uid_validity_;
2112 g_free(session->mbox);
2113 session->mbox = NULL;
2115 real_path = imap_get_real_path(folder, path);
2116 ok = imap_cmd_select(session, real_path,
2117 exists, recent, unseen, uid_validity);
2118 if (ok != IMAP_SUCCESS)
2119 log_warning(_("can't select folder: %s\n"), real_path);
2121 session->mbox = g_strdup(path);
2127 #define THROW(err) { ok = err; goto catch; }
2129 static gint imap_status(IMAPSession *session, IMAPFolder *folder,
2131 gint *messages, gint *recent,
2132 guint32 *uid_next, guint32 *uid_validity,
2141 *messages = *recent = *uid_next = *uid_validity = *unseen = 0;
2143 argbuf = g_ptr_array_new();
2145 real_path = imap_get_real_path(folder, path);
2146 QUOTE_IF_REQUIRED(real_path_, real_path);
2147 imap_gen_send(session, "STATUS %s "
2148 "(MESSAGES RECENT UIDNEXT UIDVALIDITY UNSEEN)",
2151 ok = imap_cmd_ok(session, argbuf);
2152 if (ok != IMAP_SUCCESS) THROW(ok);
2154 str = search_array_str(argbuf, "STATUS");
2155 if (!str) THROW(IMAP_ERROR);
2157 str = strchr(str, '(');
2158 if (!str) THROW(IMAP_ERROR);
2160 while (*str != '\0' && *str != ')') {
2161 while (*str == ' ') str++;
2163 if (!strncmp(str, "MESSAGES ", 9)) {
2165 *messages = strtol(str, &str, 10);
2166 } else if (!strncmp(str, "RECENT ", 7)) {
2168 *recent = strtol(str, &str, 10);
2169 } else if (!strncmp(str, "UIDNEXT ", 8)) {
2171 *uid_next = strtoul(str, &str, 10);
2172 } else if (!strncmp(str, "UIDVALIDITY ", 12)) {
2174 *uid_validity = strtoul(str, &str, 10);
2175 } else if (!strncmp(str, "UNSEEN ", 7)) {
2177 *unseen = strtol(str, &str, 10);
2179 g_warning("invalid STATUS response: %s\n", str);
2186 ptr_array_free_strings(argbuf);
2187 g_ptr_array_free(argbuf, TRUE);
2195 /* low-level IMAP4rev1 commands */
2197 static gint imap_cmd_login(IMAPSession *session,
2198 const gchar *user, const gchar *pass)
2200 gchar *user_, *pass_;
2203 QUOTE_IF_REQUIRED(user_, user);
2204 QUOTE_IF_REQUIRED(pass_, pass);
2205 imap_gen_send(session, "LOGIN %s %s", user_, pass_);
2207 ok = imap_cmd_ok(session, NULL);
2208 if (ok != IMAP_SUCCESS)
2209 log_warning(_("IMAP4 login failed.\n"));
2214 static gint imap_cmd_logout(IMAPSession *session)
2216 imap_gen_send(session, "LOGOUT");
2217 return imap_cmd_ok(session, NULL);
2220 /* Send CAPABILITY, and examine the server's response to see whether this
2221 * connection is pre-authenticated or not and build a list of CAPABILITIES. */
2222 static gint imap_greeting(IMAPSession *session)
2227 imap_gen_send(session, "CAPABILITY");
2229 argbuf = g_ptr_array_new();
2231 if (imap_cmd_ok(session, argbuf) != IMAP_SUCCESS ||
2232 ((capstr = search_array_str(argbuf, "CAPABILITY ")) == NULL)) {
2233 ptr_array_free_strings(argbuf);
2234 g_ptr_array_free(argbuf, TRUE);
2238 session->authenticated = search_array_str(argbuf, "PREAUTH") != NULL;
2240 capstr += strlen("CAPABILITY ");
2242 IMAP_SESSION(session)->capability = g_strsplit(capstr, " ", 0);
2244 ptr_array_free_strings(argbuf);
2245 g_ptr_array_free(argbuf, TRUE);
2250 static gboolean imap_has_capability(IMAPSession *session, const gchar *cap)
2254 for (p = session->capability; *p != NULL; ++p)
2255 if (g_strcasecmp(*p, cap) == 0)
2261 void imap_free_capabilities(IMAPSession *session)
2263 g_strfreev(session->capability);
2264 session->capability = NULL;
2267 static const IMAPSet numberlist_to_imapset(MsgNumberList *list)
2269 static GString *imapset = NULL;
2270 MsgNumberList *numlist, *elem;
2271 guint first, last, next;
2273 if (imapset == NULL)
2274 imapset = g_string_sized_new(256);
2276 g_string_truncate(imapset, 0);
2278 numlist = g_slist_copy(list);
2279 numlist = g_slist_sort(numlist, g_int_compare);
2281 first = GPOINTER_TO_INT(numlist->data);
2283 for(elem = g_slist_next(numlist); elem != NULL; elem = g_slist_next(elem)) {
2284 next = GPOINTER_TO_INT(elem->data);
2285 if(next != (last + 1)) {
2286 if (imapset->len > 0)
2287 g_string_append(imapset, ",");
2289 g_string_sprintfa(imapset, "%d", first);
2291 g_string_sprintfa(imapset, "%d:%d", first, last);
2297 if (imapset->len > 0)
2298 g_string_append(imapset, ",");
2300 g_string_sprintfa(imapset, "%d", first);
2302 g_string_sprintfa(imapset, "%d:%d", first, last);
2304 g_slist_free(numlist);
2306 return imapset->str;
2309 static gint imap_cmd_noop(IMAPSession *session)
2311 imap_gen_send(session, "NOOP");
2312 return imap_cmd_ok(session, NULL);
2315 static gint imap_cmd_starttls(IMAPSession *session)
2317 imap_gen_send(session, "STARTTLS");
2318 return imap_cmd_ok(session, NULL);
2321 #define THROW(err) { ok = err; goto catch; }
2323 static gint imap_cmd_namespace(IMAPSession *session, gchar **ns_str)
2329 argbuf = g_ptr_array_new();
2331 imap_gen_send(session, "NAMESPACE");
2332 if ((ok = imap_cmd_ok(session, argbuf)) != IMAP_SUCCESS) THROW(ok);
2334 str = search_array_str(argbuf, "NAMESPACE");
2335 if (!str) THROW(IMAP_ERROR);
2337 *ns_str = g_strdup(str);
2340 ptr_array_free_strings(argbuf);
2341 g_ptr_array_free(argbuf, TRUE);
2348 static gint imap_cmd_list(IMAPSession *session, const gchar *ref,
2349 const gchar *mailbox, GPtrArray *argbuf)
2351 gchar *ref_, *mailbox_;
2353 if (!ref) ref = "\"\"";
2354 if (!mailbox) mailbox = "\"\"";
2356 QUOTE_IF_REQUIRED(ref_, ref);
2357 QUOTE_IF_REQUIRED(mailbox_, mailbox);
2358 imap_gen_send(session, "LIST %s %s", ref_, mailbox_);
2360 return imap_cmd_ok(session, argbuf);
2363 #define THROW goto catch
2365 static gint imap_cmd_do_select(IMAPSession *session, const gchar *folder,
2367 gint *exists, gint *recent, gint *unseen,
2368 guint32 *uid_validity)
2376 *exists = *recent = *unseen = *uid_validity = 0;
2377 argbuf = g_ptr_array_new();
2380 select_cmd = "EXAMINE";
2382 select_cmd = "SELECT";
2384 QUOTE_IF_REQUIRED(folder_, folder);
2385 imap_gen_send(session, "%s %s", select_cmd, folder_);
2387 if ((ok = imap_cmd_ok(session, argbuf)) != IMAP_SUCCESS) THROW;
2389 resp_str = search_array_contain_str(argbuf, "EXISTS");
2391 if (sscanf(resp_str,"%d EXISTS", exists) != 1) {
2392 g_warning("imap_cmd_select(): invalid EXISTS line.\n");
2397 resp_str = search_array_contain_str(argbuf, "RECENT");
2399 if (sscanf(resp_str, "%d RECENT", recent) != 1) {
2400 g_warning("imap_cmd_select(): invalid RECENT line.\n");
2405 resp_str = search_array_contain_str(argbuf, "UIDVALIDITY");
2407 if (sscanf(resp_str, "OK [UIDVALIDITY %u] ", uid_validity)
2409 g_warning("imap_cmd_select(): invalid UIDVALIDITY line.\n");
2414 resp_str = search_array_contain_str(argbuf, "UNSEEN");
2416 if (sscanf(resp_str, "OK [UNSEEN %d] ", unseen) != 1) {
2417 g_warning("imap_cmd_select(): invalid UNSEEN line.\n");
2423 ptr_array_free_strings(argbuf);
2424 g_ptr_array_free(argbuf, TRUE);
2429 static gint imap_cmd_select(IMAPSession *session, const gchar *folder,
2430 gint *exists, gint *recent, gint *unseen,
2431 guint32 *uid_validity)
2433 return imap_cmd_do_select(session, folder, FALSE,
2434 exists, recent, unseen, uid_validity);
2437 static gint imap_cmd_examine(IMAPSession *session, const gchar *folder,
2438 gint *exists, gint *recent, gint *unseen,
2439 guint32 *uid_validity)
2441 return imap_cmd_do_select(session, folder, TRUE,
2442 exists, recent, unseen, uid_validity);
2447 static gint imap_cmd_create(IMAPSession *session, const gchar *folder)
2451 QUOTE_IF_REQUIRED(folder_, folder);
2452 imap_gen_send(session, "CREATE %s", folder_);
2454 return imap_cmd_ok(session, NULL);
2457 static gint imap_cmd_rename(IMAPSession *session, const gchar *old_folder,
2458 const gchar *new_folder)
2460 gchar *old_folder_, *new_folder_;
2462 QUOTE_IF_REQUIRED(old_folder_, old_folder);
2463 QUOTE_IF_REQUIRED(new_folder_, new_folder);
2464 imap_gen_send(session, "RENAME %s %s", old_folder_, new_folder_);
2466 return imap_cmd_ok(session, NULL);
2469 static gint imap_cmd_delete(IMAPSession *session, const gchar *folder)
2473 QUOTE_IF_REQUIRED(folder_, folder);
2474 imap_gen_send(session, "DELETE %s", folder_);
2476 return imap_cmd_ok(session, NULL);
2479 static gint imap_cmd_search(IMAPSession *session, const gchar *criteria, GSList **list)
2485 g_return_val_if_fail(criteria != NULL, IMAP_ERROR);
2486 g_return_val_if_fail(list != NULL, IMAP_ERROR);
2490 argbuf = g_ptr_array_new();
2491 imap_gen_send(session, "UID SEARCH %s", criteria);
2493 ok = imap_cmd_ok(session, argbuf);
2494 if (ok != IMAP_SUCCESS) {
2495 ptr_array_free_strings(argbuf);
2496 g_ptr_array_free(argbuf, TRUE);
2500 if ((uidlist = search_array_str(argbuf, "SEARCH ")) != NULL) {
2501 gchar **strlist, **p;
2503 strlist = g_strsplit(uidlist + 7, " ", 0);
2504 for (p = strlist; *p != NULL; ++p) {
2507 if (sscanf(*p, "%d", &msgnum) == 1)
2508 *list = g_slist_append(*list, GINT_TO_POINTER(msgnum));
2510 g_strfreev(strlist);
2512 ptr_array_free_strings(argbuf);
2513 g_ptr_array_free(argbuf, TRUE);
2515 return IMAP_SUCCESS;
2518 static gint imap_cmd_fetch(IMAPSession *session, guint32 uid, const gchar *filename)
2526 g_return_val_if_fail(filename != NULL, IMAP_ERROR);
2528 imap_gen_send(session, "UID FETCH %d BODY.PEEK[]", uid);
2530 while ((ok = imap_gen_recv(session, &buf))
2532 if (buf[0] != '*' || buf[1] != ' ') {
2536 if (strstr(buf, "FETCH") != NULL)
2539 if (ok != IMAP_SUCCESS)
2542 cur_pos = strchr(buf, '{');
2543 if (cur_pos == NULL) {
2547 cur_pos = strchr_cpy(cur_pos + 1, '}', size_str, sizeof(size_str));
2548 if (cur_pos == NULL) {
2552 size_num = atol(size_str);
2554 if (*cur_pos != '\0') {
2559 if (recv_bytes_write_to_file(SESSION(session)->sock, size_num, filename) != 0) {
2564 if (imap_gen_recv(session, &buf) != IMAP_SUCCESS) {
2569 if (buf[0] == '\0' || buf[strlen(buf) - 1] != ')') {
2575 ok = imap_cmd_ok(session, NULL);
2580 static gint imap_cmd_append(IMAPSession *session, const gchar *destfolder,
2581 const gchar *file, gint32 *new_uid)
2586 gchar buf[BUFFSIZE], *imapbuf;
2591 g_return_val_if_fail(file != NULL, IMAP_ERROR);
2593 size = get_file_size_as_crlf(file);
2594 if ((fp = fopen(file, "rb")) == NULL) {
2595 FILE_OP_ERROR(file, "fopen");
2598 QUOTE_IF_REQUIRED(destfolder_, destfolder);
2599 imap_gen_send(session, "APPEND %s (\\Seen) {%d}", destfolder_, size);
2601 ok = imap_gen_recv(session, &imapbuf);
2602 if (ok != IMAP_SUCCESS || imapbuf[0] != '+' || imapbuf[1] != ' ') {
2603 log_warning(_("can't append %s to %s\n"), file, destfolder_);
2610 log_print("IMAP4> %s\n", _("(sending file...)"));
2612 while (fgets(buf, sizeof(buf), fp) != NULL) {
2614 if (sock_puts(SESSION(session)->sock, buf) < 0) {
2621 FILE_OP_ERROR(file, "fgets");
2626 sock_puts(SESSION(session)->sock, "");
2630 reply = g_ptr_array_new();
2633 ok = imap_cmd_ok(session, reply);
2634 if (ok != IMAP_SUCCESS)
2635 log_warning(_("can't append message to %s\n"), destfolder_);
2637 (new_uid != NULL) &&
2638 (imap_has_capability(session, "UIDPLUS") && reply->len > 0) &&
2639 ((okmsginfo = g_ptr_array_index(reply, reply->len - 1)) != NULL) &&
2640 (sscanf(okmsginfo, "%*u OK [APPENDUID %*u %u]", &newuid) == 1)) {
2644 ptr_array_free_strings(reply);
2645 g_ptr_array_free(reply, TRUE);
2650 static gint imap_cmd_copy(IMAPSession * session,
2652 const gchar * destfolder, gint32 * new_uid)
2655 gint32 olduid, newuid;
2660 g_return_val_if_fail(destfolder != NULL, IMAP_ERROR);
2661 g_return_val_if_fail(session != NULL, IMAP_ERROR);
2662 g_return_val_if_fail(new_uid != NULL, IMAP_ERROR);
2664 QUOTE_IF_REQUIRED(destfolder_, destfolder);
2665 imap_gen_send(session, "UID COPY %d %s", msgnum, destfolder_);
2667 reply = g_ptr_array_new();
2670 ok = imap_cmd_ok(session, reply);
2671 if (ok != IMAP_SUCCESS)
2672 log_warning(_("can't copy %d to %s\n"), msgnum, destfolder_);
2673 else if (imap_has_capability(session, "UIDPLUS") && reply->len > 0)
2674 if ((okmsginfo = g_ptr_array_index(reply, reply->len - 1)) != NULL &&
2675 sscanf(okmsginfo, "%*u OK [COPYUID %*u %u %u]", &olduid, &newuid) == 2 &&
2679 ptr_array_free_strings(reply);
2680 g_ptr_array_free(reply, TRUE);
2684 gint imap_cmd_envelope(IMAPSession *session, IMAPSet set)
2686 static GString *header_fields = NULL;
2688 if (header_fields == NULL) {
2689 const HeaderEntry *headers, *elem;
2691 headers = procheader_get_headernames(FALSE);
2692 header_fields = g_string_new("");
2694 for (elem = headers; elem->name != NULL; ++elem) {
2695 gint namelen = strlen(elem->name);
2697 /* Header fields ending with space are not rfc822 headers */
2698 if (elem->name[namelen - 1] == ' ')
2701 /* strip : at the of header field */
2702 if(elem->name[namelen - 1] == ':')
2708 g_string_sprintfa(header_fields, "%s%.*s",
2709 header_fields->str[0] != '\0' ? " " : "",
2710 namelen, elem->name);
2715 (session, "UID FETCH %s (UID FLAGS RFC822.SIZE BODY.PEEK[HEADER.FIELDS (%s)])",
2716 set, header_fields->str);
2718 return IMAP_SUCCESS;
2721 static gint imap_cmd_store(IMAPSession *session, IMAPSet set,
2726 imap_gen_send(session, "UID STORE %s %s",
2729 if ((ok = imap_cmd_ok(session, NULL)) != IMAP_SUCCESS) {
2730 log_warning(_("error while imap command: STORE %s %s\n"),
2735 return IMAP_SUCCESS;
2738 static gint imap_cmd_expunge(IMAPSession *session)
2742 imap_gen_send(session, "EXPUNGE");
2743 if ((ok = imap_cmd_ok(session, NULL)) != IMAP_SUCCESS) {
2744 log_warning(_("error while imap command: EXPUNGE\n"));
2748 return IMAP_SUCCESS;
2751 static gint imap_cmd_ok(IMAPSession *session, GPtrArray *argbuf)
2756 gchar cmd_status[IMAPBUFSIZE];
2758 while ((ok = imap_gen_recv(session, &buf))
2760 if (buf[0] == '*' && buf[1] == ' ') {
2762 g_ptr_array_add(argbuf, g_strdup(buf + 2));
2766 if (sscanf(buf, "%d %s", &cmd_num, cmd_status) < 2) {
2769 } else if (cmd_num == session->cmd_count &&
2770 !strcmp(cmd_status, "OK")) {
2772 g_ptr_array_add(argbuf, g_strdup(buf));
2774 return IMAP_SUCCESS;
2785 static void imap_gen_send(IMAPSession *session, const gchar *format, ...)
2787 gchar buf[IMAPBUFSIZE];
2788 gchar tmp[IMAPBUFSIZE];
2792 va_start(args, format);
2793 g_vsnprintf(tmp, sizeof(tmp), format, args);
2796 session->cmd_count++;
2798 g_snprintf(buf, sizeof(buf), "%d %s\r\n", session->cmd_count, tmp);
2799 if (!strncasecmp(tmp, "LOGIN ", 6) && (p = strchr(tmp + 6, ' '))) {
2801 log_print("IMAP4> %d %s ********\n", session->cmd_count, tmp);
2803 log_print("IMAP4> %d %s\n", session->cmd_count, tmp);
2805 sock_write_all(SESSION(session)->sock, buf, strlen(buf));
2808 static gint imap_gen_recv(IMAPSession *session, gchar **buf)
2810 if ((*buf = sock_getline(SESSION(session)->sock)) == NULL)
2815 log_print("IMAP4< %s\n", *buf);
2817 return IMAP_SUCCESS;
2821 /* misc utility functions */
2823 static gchar *strchr_cpy(const gchar *src, gchar ch, gchar *dest, gint len)
2828 tmp = strchr(src, ch);
2832 memcpy(dest, src, MIN(tmp - src, len - 1));
2833 dest[MIN(tmp - src, len - 1)] = '\0';
2838 static gchar *get_quoted(const gchar *src, gchar ch, gchar *dest, gint len)
2840 const gchar *p = src;
2843 g_return_val_if_fail(*p == ch, NULL);
2848 while (*p != '\0' && *p != ch) {
2850 if (*p == '\\' && *(p + 1) != '\0')
2859 return (gchar *)(*p == ch ? p + 1 : p);
2862 static gchar *search_array_contain_str(GPtrArray *array, const gchar *str)
2866 for (i = 0; i < array->len; i++) {
2869 tmp = g_ptr_array_index(array, i);
2870 if (strstr(tmp, str) != NULL)
2877 static gchar *search_array_str(GPtrArray *array, const gchar *str)
2884 for (i = 0; i < array->len; i++) {
2887 tmp = g_ptr_array_index(array, i);
2888 if (!strncmp(tmp, str, len))
2895 static void imap_path_separator_subst(gchar *str, gchar separator)
2898 gboolean in_escape = FALSE;
2900 if (!separator || separator == '/') return;
2902 for (p = str; *p != '\0'; p++) {
2903 if (*p == '/' && !in_escape)
2905 else if (*p == '&' && *(p + 1) != '-' && !in_escape)
2907 else if (*p == '-' && in_escape)
2912 static gchar *imap_modified_utf7_to_locale(const gchar *mutf7_str)
2915 return g_strdup(mutf7_str);
2917 static iconv_t cd = (iconv_t)-1;
2918 static gboolean iconv_ok = TRUE;
2921 size_t norm_utf7_len;
2923 gchar *to_str, *to_p;
2925 gboolean in_escape = FALSE;
2927 if (!iconv_ok) return g_strdup(mutf7_str);
2929 if (cd == (iconv_t)-1) {
2930 cd = iconv_open(conv_get_current_charset_str(), "UTF-7");
2931 if (cd == (iconv_t)-1) {
2932 g_warning("iconv cannot convert UTF-7 to %s\n",
2933 conv_get_current_charset_str());
2935 return g_strdup(mutf7_str);
2939 norm_utf7 = g_string_new(NULL);
2941 for (p = mutf7_str; *p != '\0'; p++) {
2942 /* replace: '&' -> '+',
2944 escaped ',' -> '/' */
2945 if (!in_escape && *p == '&') {
2946 if (*(p + 1) != '-') {
2947 g_string_append_c(norm_utf7, '+');
2950 g_string_append_c(norm_utf7, '&');
2953 } else if (in_escape && *p == ',') {
2954 g_string_append_c(norm_utf7, '/');
2955 } else if (in_escape && *p == '-') {
2956 g_string_append_c(norm_utf7, '-');
2959 g_string_append_c(norm_utf7, *p);
2963 norm_utf7_p = norm_utf7->str;
2964 norm_utf7_len = norm_utf7->len;
2965 to_len = strlen(mutf7_str) * 5;
2966 to_p = to_str = g_malloc(to_len + 1);
2968 if (iconv(cd, (ICONV_CONST gchar **)&norm_utf7_p, &norm_utf7_len,
2969 &to_p, &to_len) == -1) {
2970 g_warning(_("iconv cannot convert UTF-7 to %s\n"),
2971 conv_get_current_charset_str());
2972 g_string_free(norm_utf7, TRUE);
2974 return g_strdup(mutf7_str);
2977 /* second iconv() call for flushing */
2978 iconv(cd, NULL, NULL, &to_p, &to_len);
2979 g_string_free(norm_utf7, TRUE);
2983 #endif /* !HAVE_ICONV */
2986 static gchar *imap_locale_to_modified_utf7(const gchar *from)
2989 return g_strdup(from);
2991 static iconv_t cd = (iconv_t)-1;
2992 static gboolean iconv_ok = TRUE;
2993 gchar *norm_utf7, *norm_utf7_p;
2994 size_t from_len, norm_utf7_len;
2996 gchar *from_tmp, *to, *p;
2997 gboolean in_escape = FALSE;
2999 if (!iconv_ok) return g_strdup(from);
3001 if (cd == (iconv_t)-1) {
3002 cd = iconv_open("UTF-7", conv_get_current_charset_str());
3003 if (cd == (iconv_t)-1) {
3004 g_warning("iconv cannot convert %s to UTF-7\n",
3005 conv_get_current_charset_str());
3007 return g_strdup(from);
3011 Xstrdup_a(from_tmp, from, return g_strdup(from));
3012 from_len = strlen(from);
3013 norm_utf7_len = from_len * 5;
3014 Xalloca(norm_utf7, norm_utf7_len + 1, return g_strdup(from));
3015 norm_utf7_p = norm_utf7;
3017 #define IS_PRINT(ch) (isprint(ch) && isascii(ch))
3019 while (from_len > 0) {
3020 if (IS_PRINT(*from_tmp)) {
3021 /* printable ascii char */
3022 *norm_utf7_p = *from_tmp;
3029 /* unprintable char: convert to UTF-7 */
3031 !IS_PRINT(from_tmp[mblen]) && mblen < from_len;
3035 if (iconv(cd, (ICONV_CONST gchar **)&from_tmp, &mblen,
3036 &norm_utf7_p, &norm_utf7_len) == -1) {
3037 g_warning("iconv cannot convert %s to UTF-7\n",
3038 conv_get_current_charset_str());
3039 return g_strdup(from);
3042 /* second iconv() call for flushing */
3043 iconv(cd, NULL, NULL, &norm_utf7_p, &norm_utf7_len);
3049 *norm_utf7_p = '\0';
3050 to_str = g_string_new(NULL);
3051 for (p = norm_utf7; p < norm_utf7_p; p++) {
3052 /* replace: '&' -> "&-",
3054 escaped '/' -> ',' */
3055 if (!in_escape && *p == '&') {
3056 g_string_append(to_str, "&-");
3057 } else if (!in_escape && *p == '+') {
3058 g_string_append_c(to_str, '&');
3060 } else if (in_escape && *p == '/') {
3061 g_string_append_c(to_str, ',');
3062 } else if (in_escape && *p == '-') {
3064 g_string_append_c(to_str, '-');
3066 g_string_append_c(to_str, *p);
3072 g_string_append_c(to_str, '-');
3076 g_string_free(to_str, FALSE);
3079 #endif /* !HAVE_ICONV */
3082 static gboolean imap_rename_folder_func(GNode *node, gpointer data)
3084 FolderItem *item = node->data;
3085 gchar **paths = data;
3086 const gchar *oldpath = paths[0];
3087 const gchar *newpath = paths[1];
3089 gchar *new_itempath;
3092 oldpathlen = strlen(oldpath);
3093 if (strncmp(oldpath, item->path, oldpathlen) != 0) {
3094 g_warning("path doesn't match: %s, %s\n", oldpath, item->path);
3098 base = item->path + oldpathlen;
3099 while (*base == G_DIR_SEPARATOR) base++;
3101 new_itempath = g_strdup(newpath);
3103 new_itempath = g_strconcat(newpath, G_DIR_SEPARATOR_S, base,
3106 item->path = new_itempath;
3111 static gint get_list_of_uids(Folder *folder, IMAPFolderItem *item, GSList **msgnum_list)
3113 gint ok, nummsgs = 0, lastuid_old;
3114 IMAPSession *session;
3115 GSList *uidlist, *elem;
3118 session = imap_session_get(folder);
3119 g_return_val_if_fail(session != NULL, -1);
3121 ok = imap_select(session, IMAP_FOLDER(folder), item->item.path,
3122 NULL, NULL, NULL, NULL);
3123 if (ok != IMAP_SUCCESS)
3126 cmd_buf = g_strdup_printf("UID %d:*", item->lastuid + 1);
3127 ok = imap_cmd_search(session, cmd_buf, &uidlist);
3130 if (ok == IMAP_SOCKET) {
3131 session_destroy((Session *)session);
3132 ((RemoteFolder *)folder)->session = NULL;
3136 if (ok != IMAP_SUCCESS) {
3140 argbuf = g_ptr_array_new();
3142 cmd_buf = g_strdup_printf("UID FETCH %d:* (UID)", item->lastuid + 1);
3143 imap_gen_send(session, cmd_buf);
3145 ok = imap_cmd_ok(session, argbuf);
3146 if (ok != IMAP_SUCCESS) {
3147 ptr_array_free_strings(argbuf);
3148 g_ptr_array_free(argbuf, TRUE);
3152 for(i = 0; i < argbuf->len; i++) {
3155 if((ret = sscanf(g_ptr_array_index(argbuf, i),
3156 "%*d FETCH (UID %d)", &msgnum)) == 1)
3157 uidlist = g_slist_prepend(uidlist, GINT_TO_POINTER(msgnum));
3159 ptr_array_free_strings(argbuf);
3160 g_ptr_array_free(argbuf, TRUE);
3163 lastuid_old = item->lastuid;
3164 *msgnum_list = g_slist_copy(item->uid_list);
3165 nummsgs = g_slist_length(*msgnum_list);
3166 debug_print("Got %d uids from cache\n", g_slist_length(item->uid_list));
3168 for (elem = uidlist; elem != NULL; elem = g_slist_next(elem)) {
3171 msgnum = GPOINTER_TO_INT(elem->data);
3172 if (msgnum > lastuid_old) {
3173 *msgnum_list = g_slist_prepend(*msgnum_list, GINT_TO_POINTER(msgnum));
3174 item->uid_list = g_slist_prepend(item->uid_list, GINT_TO_POINTER(msgnum));
3177 if(msgnum > item->lastuid)
3178 item->lastuid = msgnum;
3181 g_slist_free(uidlist);
3186 gint imap_get_num_list(Folder *folder, FolderItem *_item, GSList **msgnum_list)
3188 IMAPFolderItem *item = (IMAPFolderItem *)_item;
3189 IMAPSession *session;
3190 gint ok, nummsgs = 0, exists, recent, unseen, uid_val, uid_next;
3194 g_return_val_if_fail(folder != NULL, -1);
3195 g_return_val_if_fail(item != NULL, -1);
3196 g_return_val_if_fail(item->item.path != NULL, -1);
3197 g_return_val_if_fail(FOLDER_CLASS(folder) == &imap_class, -1);
3198 g_return_val_if_fail(folder->account != NULL, -1);
3200 session = imap_session_get(folder);
3201 g_return_val_if_fail(session != NULL, -1);
3203 ok = imap_status(session, IMAP_FOLDER(folder), item->item.path,
3204 &exists, &recent, &uid_next, &uid_val, &unseen);
3205 if (ok != IMAP_SUCCESS)
3208 /* If old uid_next matches new uid_next we can be sure no message
3209 was added to the folder */
3210 if (uid_next == item->uid_next) {
3211 nummsgs = g_slist_length(item->uid_list);
3213 /* If number of messages is still the same we
3214 know our caches message numbers are still valid,
3215 otherwise if the number of messages has decrease
3216 we discard our cache to start a new scan to find
3217 out which numbers have been removed */
3218 if (exists == nummsgs) {
3219 *msgnum_list = g_slist_copy(item->uid_list);
3221 } else if (exists < nummsgs) {
3222 debug_print("Freeing imap uid cache");
3224 g_slist_free(item->uid_list);
3225 item->uid_list = NULL;
3228 item->uid_next = uid_next;
3231 *msgnum_list = NULL;
3235 nummsgs = get_list_of_uids(folder, item, &uidlist);
3237 if (nummsgs != exists) {
3238 /* Cache contains more messages then folder, we have cached
3239 an old UID of a message that was removed and new messages
3240 have been added too, otherwise the uid_next check would
3242 debug_print("Freeing imap uid cache");
3244 g_slist_free(item->uid_list);
3245 item->uid_list = NULL;
3247 g_slist_free(*msgnum_list);
3249 nummsgs = get_list_of_uids(folder, item, &uidlist);
3252 *msgnum_list = uidlist;
3254 dir = folder_item_get_path((FolderItem *)item);
3255 debug_print("removing old messages from %s\n", dir);
3256 remove_numbered_files_not_in_list(dir, *msgnum_list);
3262 static MsgInfo *imap_parse_msg(const gchar *file, FolderItem *item)
3267 flags.perm_flags = MSG_NEW|MSG_UNREAD;
3268 flags.tmp_flags = 0;
3270 g_return_val_if_fail(item != NULL, NULL);
3271 g_return_val_if_fail(file != NULL, NULL);
3273 if (item->stype == F_QUEUE) {
3274 MSG_SET_TMP_FLAGS(flags, MSG_QUEUED);
3275 } else if (item->stype == F_DRAFT) {
3276 MSG_SET_TMP_FLAGS(flags, MSG_DRAFT);
3279 msginfo = procheader_parse_file(file, flags, FALSE, FALSE);
3280 if (!msginfo) return NULL;
3282 msginfo->folder = item;
3287 GSList *imap_get_msginfos(Folder *folder, FolderItem *item, GSList *msgnum_list)
3289 IMAPSession *session;
3290 MsgInfoList *ret = NULL;
3293 g_return_val_if_fail(folder != NULL, NULL);
3294 g_return_val_if_fail(item != NULL, NULL);
3295 g_return_val_if_fail(msgnum_list != NULL, NULL);
3297 session = imap_session_get(folder);
3298 g_return_val_if_fail(session != NULL, NULL);
3300 ok = imap_select(session, IMAP_FOLDER(folder), item->path,
3301 NULL, NULL, NULL, NULL);
3302 if (ok != IMAP_SUCCESS)
3305 if (!(item->stype == F_QUEUE || item->stype == F_DRAFT)) {
3306 ret = g_slist_concat(ret,
3307 imap_get_uncached_messages(
3308 session, item, msgnum_list));
3310 MsgNumberList *sorted_list, *elem;
3311 gint startnum, lastnum;
3313 sorted_list = g_slist_sort(g_slist_copy(msgnum_list), g_int_compare);
3315 startnum = lastnum = GPOINTER_TO_INT(sorted_list->data);
3317 for (elem = sorted_list;; elem = g_slist_next(elem)) {
3321 num = GPOINTER_TO_INT(elem->data);
3323 if (num > lastnum + 1 || elem == NULL) {
3325 for (i = startnum; i <= lastnum; ++i) {
3328 file = imap_fetch_msg(folder, item, i);
3330 MsgInfo *msginfo = imap_parse_msg(file, item);
3331 if (msginfo != NULL) {
3332 msginfo->msgnum = i;
3333 ret = g_slist_append(ret, msginfo);
3347 g_slist_free(sorted_list);
3353 MsgInfo *imap_get_msginfo(Folder *folder, FolderItem *item, gint uid)
3355 MsgInfo *msginfo = NULL;
3356 MsgInfoList *msginfolist;
3357 MsgNumberList numlist;
3359 numlist.next = NULL;
3360 numlist.data = GINT_TO_POINTER(uid);
3362 msginfolist = imap_get_msginfos(folder, item, &numlist);
3363 if (msginfolist != NULL) {
3364 msginfo = msginfolist->data;
3365 g_slist_free(msginfolist);
3371 gboolean imap_check_msgnum_validity(Folder *folder, FolderItem *_item)
3373 IMAPSession *session;
3374 IMAPFolderItem *item = (IMAPFolderItem *)_item;
3375 gint ok, exists = 0, recent = 0, unseen = 0;
3376 guint32 uid_next, uid_validity = 0;
3378 g_return_val_if_fail(folder != NULL, FALSE);
3379 g_return_val_if_fail(item != NULL, FALSE);
3380 g_return_val_if_fail(item->item.folder != NULL, FALSE);
3381 g_return_val_if_fail(FOLDER_CLASS(item->item.folder) == &imap_class, FALSE);
3383 session = imap_session_get(folder);
3384 g_return_val_if_fail(session != NULL, FALSE);
3386 ok = imap_status(session, IMAP_FOLDER(folder), item->item.path,
3387 &exists, &recent, &uid_next, &uid_validity, &unseen);
3388 if (ok != IMAP_SUCCESS)
3391 if(item->item.mtime == uid_validity)
3394 debug_print("Freeing imap uid cache");
3396 g_slist_free(item->uid_list);
3397 item->uid_list = NULL;
3399 item->item.mtime = uid_validity;
3401 imap_delete_all_cached_messages((FolderItem *)item);
3406 void imap_change_flags(Folder *folder, FolderItem *item, MsgInfo *msginfo, MsgPermFlags newflags)
3408 IMAPSession *session;
3409 IMAPFlags flags_set = 0, flags_unset = 0;
3410 gint ok = IMAP_SUCCESS;
3411 MsgNumberList numlist;
3413 g_return_if_fail(folder != NULL);
3414 g_return_if_fail(folder->klass == &imap_class);
3415 g_return_if_fail(item != NULL);
3416 g_return_if_fail(item->folder == folder);
3417 g_return_if_fail(msginfo != NULL);
3418 g_return_if_fail(msginfo->folder == item);
3420 session = imap_session_get(folder);
3421 if (!session) return;
3423 if ((ok = imap_select(session, IMAP_FOLDER(folder), msginfo->folder->path,
3424 NULL, NULL, NULL, NULL)) != IMAP_SUCCESS)
3427 if (!MSG_IS_MARKED(msginfo->flags) && (newflags & MSG_MARKED))
3428 flags_set |= IMAP_FLAG_FLAGGED;
3429 if ( MSG_IS_MARKED(msginfo->flags) && !(newflags & MSG_MARKED))
3430 flags_unset |= IMAP_FLAG_FLAGGED;
3432 if (!MSG_IS_UNREAD(msginfo->flags) && (newflags & MSG_UNREAD))
3433 flags_unset |= IMAP_FLAG_SEEN;
3434 if ( MSG_IS_UNREAD(msginfo->flags) && !(newflags & MSG_UNREAD))
3435 flags_set |= IMAP_FLAG_SEEN;
3437 if (!MSG_IS_REPLIED(msginfo->flags) && (newflags & MSG_REPLIED))
3438 flags_set |= IMAP_FLAG_ANSWERED;
3439 if ( MSG_IS_REPLIED(msginfo->flags) && !(newflags & MSG_REPLIED))
3440 flags_set |= IMAP_FLAG_ANSWERED;
3442 numlist.next = NULL;
3443 numlist.data = GINT_TO_POINTER(msginfo->msgnum);
3446 ok = imap_set_message_flags(session, &numlist, flags_set, TRUE);
3447 if (ok != IMAP_SUCCESS) return;
3451 ok = imap_set_message_flags(session, &numlist, flags_unset, FALSE);
3452 if (ok != IMAP_SUCCESS) return;
3455 msginfo->flags.perm_flags = newflags;