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"
55 #include "inputdialog.h"
58 typedef struct _IMAPFolder IMAPFolder;
59 typedef struct _IMAPSession IMAPSession;
60 typedef struct _IMAPNameSpace IMAPNameSpace;
61 typedef struct _IMAPFolderItem IMAPFolderItem;
63 #include "prefs_account.h"
65 #define IMAP_FOLDER(obj) ((IMAPFolder *)obj)
66 #define IMAP_SESSION(obj) ((IMAPSession *)obj)
72 /* list of IMAPNameSpace */
84 time_t last_access_time;
85 gboolean authenticated;
87 gboolean folder_content_changed;
97 #define IMAP_SUCCESS 0
99 #define IMAP_AUTHFAIL 3
100 #define IMAP_PROTOCOL 4
101 #define IMAP_SYNTAX 5
105 #define IMAPBUFSIZE 8192
106 #define IMAPCMDLIMIT 1000
110 IMAP_FLAG_SEEN = 1 << 0,
111 IMAP_FLAG_ANSWERED = 1 << 1,
112 IMAP_FLAG_FLAGGED = 1 << 2,
113 IMAP_FLAG_DELETED = 1 << 3,
114 IMAP_FLAG_DRAFT = 1 << 4
117 #define IMAP_IS_SEEN(flags) ((flags & IMAP_FLAG_SEEN) != 0)
118 #define IMAP_IS_ANSWERED(flags) ((flags & IMAP_FLAG_ANSWERED) != 0)
119 #define IMAP_IS_FLAGGED(flags) ((flags & IMAP_FLAG_FLAGGED) != 0)
120 #define IMAP_IS_DELETED(flags) ((flags & IMAP_FLAG_DELETED) != 0)
121 #define IMAP_IS_DRAFT(flags) ((flags & IMAP_FLAG_DRAFT) != 0)
124 #define IMAP4_PORT 143
126 #define IMAPS_PORT 993
129 #define QUOTE_IF_REQUIRED(out, str) \
131 if (*str != '"' && strpbrk(str, " \t(){}%*") != NULL) { \
135 len = strlen(str) + 3; \
136 Xalloca(__tmp, len, return IMAP_ERROR); \
137 g_snprintf(__tmp, len, "\"%s\"", str); \
140 Xstrdup_a(out, str, return IMAP_ERROR); \
144 typedef gchar * IMAPSet;
146 struct _IMAPFolderItem
155 static Folder *imap_folder_new(const gchar * name, const gchar * path);
156 static void imap_folder_destroy(Folder * folder);
158 static IMAPSession *imap_session_new(const PrefsAccount * account);
159 static void imap_session_authenticate(IMAPSession * session,
160 const PrefsAccount * account);
161 static void imap_session_destroy(Session * session);
163 static gchar *imap_fetch_msg(Folder * folder, FolderItem * item, gint uid);
164 static gint imap_add_msg(Folder * folder,
166 const gchar * file, MsgFlags * flags);
167 static gint imap_add_msgs(Folder * folder, FolderItem * dest,
169 GRelation *relation);
171 static gint imap_copy_msg(Folder * folder,
172 FolderItem * dest, MsgInfo * msginfo);
174 static gint imap_remove_msg(Folder * folder, FolderItem * item, gint uid);
175 static gint imap_remove_all_msg(Folder * folder, FolderItem * item);
177 static gboolean imap_is_msg_changed(Folder * folder,
178 FolderItem * item, MsgInfo * msginfo);
180 static gint imap_close(Folder * folder, FolderItem * item);
182 static void imap_scan_tree(Folder * folder);
184 static gint imap_create_tree(Folder * folder);
186 static FolderItem *imap_create_folder(Folder * folder,
189 static gint imap_rename_folder(Folder * folder,
190 FolderItem * item, const gchar * name);
191 static gint imap_remove_folder(Folder * folder, FolderItem * item);
194 static void imap_folder_init (Folder *folder,
198 static FolderItem *imap_folder_item_new (Folder *folder);
199 static void imap_folder_item_destroy (Folder *folder,
202 static IMAPSession *imap_session_get (Folder *folder);
204 static gint imap_auth (IMAPSession *session,
209 static gint imap_scan_tree_recursive (IMAPSession *session,
211 static GSList *imap_parse_list (IMAPFolder *folder,
212 IMAPSession *session,
213 const gchar *real_path,
216 static void imap_create_missing_folders (Folder *folder);
217 static FolderItem *imap_create_special_folder
219 SpecialFolderItemType stype,
222 static gint imap_do_copy (Folder *folder,
225 gboolean remove_source);
227 static void imap_delete_all_cached_messages (FolderItem *item);
230 static SockInfo *imap_open (const gchar *server,
234 static SockInfo *imap_open (const gchar *server,
239 static SockInfo *imap_open_tunnel(const gchar *server,
240 const gchar *tunnelcmd,
243 static SockInfo *imap_open_tunnel(const gchar *server,
244 const gchar *tunnelcmd);
248 static SockInfo *imap_init_sock(SockInfo *sock, SSLType ssl_type);
250 static SockInfo *imap_init_sock(SockInfo *sock);
253 static gchar *imap_get_flag_str (IMAPFlags flags);
254 static gint imap_set_message_flags (IMAPSession *session,
255 MsgNumberList *numlist,
258 static gint imap_select (IMAPSession *session,
264 guint32 *uid_validity);
265 static gint imap_status (IMAPSession *session,
271 guint32 *uid_validity,
274 static void imap_parse_namespace (IMAPSession *session,
276 static void imap_get_namespace_by_list (IMAPSession *session,
278 static IMAPNameSpace *imap_find_namespace (IMAPFolder *folder,
280 static gchar imap_get_path_separator (IMAPFolder *folder,
282 static gchar *imap_get_real_path (IMAPFolder *folder,
285 static gchar *imap_parse_atom (SockInfo *sock,
290 static MsgFlags imap_parse_flags (const gchar *flag_str);
291 static MsgInfo *imap_parse_envelope (SockInfo *sock,
294 static gint imap_greeting (IMAPSession *session);
295 static gboolean imap_has_capability (IMAPSession *session,
297 void imap_free_capabilities (IMAPSession *session);
298 static const IMAPSet numberlist_to_imapset
299 (MsgNumberList *list);
301 /* low-level IMAP4rev1 commands */
302 static gint imap_cmd_authenticate
303 (IMAPSession *session,
307 static gint imap_cmd_login (IMAPSession *sock,
310 static gint imap_cmd_logout (IMAPSession *sock);
311 static gint imap_cmd_noop (IMAPSession *sock);
312 static gint imap_cmd_starttls (IMAPSession *sock);
313 static gint imap_cmd_namespace (IMAPSession *sock,
315 static gint imap_cmd_list (IMAPSession *session,
317 const gchar *mailbox,
319 static gint imap_cmd_do_select (IMAPSession *sock,
325 guint32 *uid_validity);
326 static gint imap_cmd_select (IMAPSession *sock,
331 guint32 *uid_validity);
332 static gint imap_cmd_examine (IMAPSession *sock,
337 guint32 *uid_validity);
338 static gint imap_cmd_create (IMAPSession *sock,
339 const gchar *folder);
340 static gint imap_cmd_rename (IMAPSession *sock,
341 const gchar *oldfolder,
342 const gchar *newfolder);
343 static gint imap_cmd_delete (IMAPSession *sock,
344 const gchar *folder);
345 static gint imap_cmd_envelope (IMAPSession *sock,
347 static gint imap_cmd_fetch (IMAPSession *sock,
349 const gchar *filename);
350 static gint imap_cmd_append (IMAPSession *session,
351 const gchar *destfolder,
355 static gint imap_cmd_copy (IMAPSession *session,
357 const gchar *destfolder,
359 static gint imap_cmd_store (IMAPSession *sock,
362 static gint imap_cmd_expunge (IMAPSession *sock);
363 static gint imap_cmd_close (IMAPSession *session);
365 static gint imap_cmd_ok (IMAPSession *session,
367 static void imap_gen_send (IMAPSession *sock,
368 const gchar *format, ...);
369 static gint imap_gen_recv (IMAPSession *sock,
372 /* misc utility functions */
373 static gchar *strchr_cpy (const gchar *src,
377 static gchar *get_quoted (const gchar *src,
381 static gchar *search_array_contain_str (GPtrArray *array,
383 static gchar *search_array_str (GPtrArray *array,
385 static void imap_path_separator_subst (gchar *str,
388 static gchar *imap_modified_utf7_to_locale (const gchar *mutf7_str);
389 static gchar *imap_locale_to_modified_utf7 (const gchar *from);
391 static gboolean imap_rename_folder_func (GNode *node,
393 static gint imap_get_num_list (Folder *folder,
396 static GSList *imap_get_msginfos (Folder *folder,
398 GSList *msgnum_list);
399 static MsgInfo *imap_get_msginfo (Folder *folder,
402 static gboolean imap_check_msgnum_validity (Folder *folder,
404 static void imap_change_flags (Folder *folder,
407 MsgPermFlags newflags);
408 static gchar *imap_folder_get_path (Folder *folder);
409 static gchar *imap_item_get_path (Folder *folder,
412 FolderClass imap_class =
418 /* Folder functions */
424 /* FolderItem functions */
425 imap_folder_item_new,
426 imap_folder_item_destroy,
436 imap_check_msgnum_validity,
438 /* Message functions */
452 FolderClass *imap_get_class(void)
457 Folder *imap_folder_new(const gchar *name, const gchar *path)
461 folder = (Folder *)g_new0(IMAPFolder, 1);
462 folder->klass = &imap_class;
463 imap_folder_init(folder, name, path);
468 void imap_folder_destroy(Folder *folder)
472 dir = imap_folder_get_path(folder);
473 if (is_dir_exist(dir))
474 remove_dir_recursive(dir);
477 folder_remote_folder_destroy(REMOTE_FOLDER(folder));
480 static void imap_folder_init(Folder *folder, const gchar *name,
483 folder_remote_folder_init((Folder *)folder, name, path);
486 static FolderItem *imap_folder_item_new(Folder *folder)
488 IMAPFolderItem *item;
490 item = g_new0(IMAPFolderItem, 1);
493 item->uid_list = NULL;
495 return (FolderItem *)item;
498 static void imap_folder_item_destroy(Folder *folder, FolderItem *_item)
500 IMAPFolderItem *item = (IMAPFolderItem *)_item;
502 g_return_if_fail(item != NULL);
503 g_slist_free(item->uid_list);
508 static gboolean imap_reset_uid_lists_func(GNode *node, gpointer data)
510 IMAPFolderItem *item = (IMAPFolderItem *)node->data;
514 g_slist_free(item->uid_list);
515 item->uid_list = NULL;
520 static void imap_reset_uid_lists(Folder *folder)
522 if(folder->node == NULL)
525 /* Destroy all uid lists and rest last uid */
526 g_node_traverse(folder->node, G_IN_ORDER, G_TRAVERSE_ALL, -1, imap_reset_uid_lists_func, NULL);
529 static gint imap_auth(IMAPSession *session, const gchar *user, const gchar *pass,
532 if (type == 0 || type == IMAP_AUTH_LOGIN)
533 return imap_cmd_login(session, user, pass);
535 return imap_cmd_authenticate(session, user, pass, type);
538 static IMAPSession *imap_session_get(Folder *folder)
540 RemoteFolder *rfolder = REMOTE_FOLDER(folder);
541 IMAPSession *session = NULL;
543 g_return_val_if_fail(folder != NULL, NULL);
544 g_return_val_if_fail(FOLDER_CLASS(folder) == &imap_class, NULL);
545 g_return_val_if_fail(folder->account != NULL, NULL);
547 /* Make sure we have a session */
548 if (rfolder->session != NULL) {
549 session = IMAP_SESSION(rfolder->session);
551 imap_reset_uid_lists(folder);
552 session = imap_session_new(folder->account);
557 if (SESSION(session)->sock->state == CONN_DISCONNECTED) {
558 debug_print("IMAP server disconnected\n");
559 session_destroy(SESSION(session));
560 imap_reset_uid_lists(folder);
561 session = imap_session_new(folder->account);
564 /* Make sure session is authenticated */
565 if (!IMAP_SESSION(session)->authenticated)
566 imap_session_authenticate(IMAP_SESSION(session), folder->account);
567 if (!IMAP_SESSION(session)->authenticated) {
568 session_destroy(SESSION(session));
569 rfolder->session = NULL;
573 /* Make sure we have parsed the IMAP namespace */
574 imap_parse_namespace(IMAP_SESSION(session),
575 IMAP_FOLDER(folder));
577 /* I think the point of this code is to avoid sending a
578 * keepalive if we've used the session recently and therefore
579 * think it's still alive. Unfortunately, most of the code
580 * does not yet check for errors on the socket, and so if the
581 * connection drops we don't notice until the timeout expires.
582 * A better solution than sending a NOOP every time would be
583 * for every command to be prepared to retry until it is
584 * successfully sent. -- mbp */
585 if (time(NULL) - session->last_access_time > SESSION_TIMEOUT) {
586 /* verify that the session is still alive */
587 if (imap_cmd_noop(session) != IMAP_SUCCESS) {
588 /* Check if this is the first try to establish a
589 connection, if yes we don't try to reconnect */
590 if (rfolder->session == NULL) {
591 log_warning(_("Connecting %s failed"),
592 folder->account->recv_server);
593 session_destroy(SESSION(session));
596 log_warning(_("IMAP4 connection to %s has been"
597 " disconnected. Reconnecting...\n"),
598 folder->account->recv_server);
599 session_destroy(SESSION(session));
600 /* Clear folders session to make imap_session_get create
601 a new session, because of rfolder->session == NULL
602 it will not try to reconnect again and so avoid an
604 rfolder->session = NULL;
605 session = imap_session_get(folder);
610 rfolder->session = SESSION(session);
612 session->last_access_time = time(NULL);
614 return IMAP_SESSION(session);
617 IMAPSession *imap_session_new(const PrefsAccount *account)
619 IMAPSession *session;
624 /* FIXME: IMAP over SSL only... */
627 port = account->set_imapport ? account->imapport
628 : account->ssl_imap == SSL_TUNNEL ? IMAPS_PORT : IMAP4_PORT;
629 ssl_type = account->ssl_imap;
631 port = account->set_imapport ? account->imapport
635 if (account->set_tunnelcmd) {
636 log_message(_("creating tunneled IMAP4 connection\n"));
638 if ((imap_sock = imap_open_tunnel(account->recv_server,
642 if ((imap_sock = imap_open_tunnel(account->recv_server,
643 account->tunnelcmd)) == NULL)
647 g_return_val_if_fail(account->recv_server != NULL, NULL);
649 log_message(_("creating IMAP4 connection to %s:%d ...\n"),
650 account->recv_server, port);
653 if ((imap_sock = imap_open(account->recv_server, port,
656 if ((imap_sock = imap_open(account->recv_server, port)) == NULL)
661 session = g_new0(IMAPSession, 1);
662 session_init(SESSION(session));
663 SESSION(session)->type = SESSION_IMAP;
664 SESSION(session)->server = g_strdup(account->recv_server);
665 SESSION(session)->sock = imap_sock;
667 SESSION(session)->destroy = imap_session_destroy;
669 session->capability = NULL;
671 session->mbox = NULL;
672 session->authenticated = FALSE;
673 session->cmd_count = 0;
675 /* Only need to log in if the connection was not PREAUTH */
676 if (imap_greeting(session) != IMAP_SUCCESS) {
677 session_destroy(SESSION(session));
682 if (account->ssl_imap == SSL_STARTTLS && imap_has_capability(session, "STARTTLS")) {
685 ok = imap_cmd_starttls(session);
686 if (ok != IMAP_SUCCESS) {
687 log_warning(_("Can't start TLS session.\n"));
688 session_destroy(SESSION(session));
691 if (!ssl_init_socket_with_method(SESSION(session)->sock, SSL_METHOD_TLSv1)) {
692 session_destroy(SESSION(session));
696 imap_free_capabilities(session);
697 session->authenticated = FALSE;
698 session->cmd_count = 1;
700 if (imap_greeting(session) != IMAP_SUCCESS) {
701 session_destroy(SESSION(session));
706 log_message("IMAP connection is %s-authenticated\n",
707 (session->authenticated) ? "pre" : "un");
712 void imap_session_authenticate(IMAPSession *session, const PrefsAccount *account)
716 g_return_if_fail(account->userid != NULL);
718 pass = account->passwd;
721 tmp_pass = input_dialog_query_password(account->recv_server, account->userid);
724 Xstrdup_a(pass, tmp_pass, {g_free(tmp_pass); return;});
728 if (imap_auth(session, account->userid, pass, account->imap_auth_type) != IMAP_SUCCESS) {
729 imap_cmd_logout(session);
733 session->authenticated = TRUE;
736 void imap_session_destroy(Session *session)
738 sock_close(session->sock);
739 session->sock = NULL;
741 g_free(IMAP_SESSION(session)->mbox);
742 imap_free_capabilities(IMAP_SESSION(session));
745 gchar *imap_fetch_msg(Folder *folder, FolderItem *item, gint uid)
747 gchar *path, *filename;
748 IMAPSession *session;
751 g_return_val_if_fail(folder != NULL, NULL);
752 g_return_val_if_fail(item != NULL, NULL);
754 path = folder_item_get_path(item);
755 if (!is_dir_exist(path))
757 filename = g_strconcat(path, G_DIR_SEPARATOR_S, itos(uid), NULL);
760 if (is_file_exist(filename)) {
761 debug_print("message %d has been already cached.\n", uid);
765 session = imap_session_get(folder);
771 ok = imap_select(session, IMAP_FOLDER(folder), item->path,
772 NULL, NULL, NULL, NULL);
773 if (ok != IMAP_SUCCESS) {
774 g_warning("can't select mailbox %s\n", item->path);
779 debug_print("getting message %d...\n", uid);
780 ok = imap_cmd_fetch(session, (guint32)uid, filename);
782 if (ok != IMAP_SUCCESS) {
783 g_warning("can't fetch message %d\n", uid);
791 gint imap_add_msg(Folder *folder, FolderItem *dest, const gchar *file, MsgFlags *flags)
795 MsgFileInfo fileinfo;
797 g_return_val_if_fail(file != NULL, -1);
799 fileinfo.file = (gchar *)file;
800 fileinfo.flags = flags;
801 file_list.data = &fileinfo;
802 file_list.next = NULL;
804 ret = imap_add_msgs(folder, dest, &file_list, NULL);
808 gint imap_add_msgs(Folder *folder, FolderItem *dest, GSList *file_list,
812 IMAPSession *session;
813 guint32 last_uid = 0;
815 MsgFileInfo *fileinfo;
818 g_return_val_if_fail(folder != NULL, -1);
819 g_return_val_if_fail(dest != NULL, -1);
820 g_return_val_if_fail(file_list != NULL, -1);
822 session = imap_session_get(folder);
823 if (!session) return -1;
825 destdir = imap_get_real_path(IMAP_FOLDER(folder), dest->path);
827 for (cur = file_list; cur != NULL; cur = cur->next) {
828 IMAPFlags iflags = 0;
830 fileinfo = (MsgFileInfo *)cur->data;
832 if (fileinfo->flags) {
833 if (MSG_IS_MARKED(*fileinfo->flags))
834 iflags |= IMAP_FLAG_FLAGGED;
835 if (MSG_IS_REPLIED(*fileinfo->flags))
836 iflags |= IMAP_FLAG_ANSWERED;
837 if (!MSG_IS_UNREAD(*fileinfo->flags))
838 iflags |= IMAP_FLAG_SEEN;
841 if (dest->stype == F_OUTBOX ||
842 dest->stype == F_QUEUE ||
843 dest->stype == F_DRAFT ||
844 dest->stype == F_TRASH)
845 iflags |= IMAP_FLAG_SEEN;
847 ok = imap_cmd_append(session, destdir, fileinfo->file, iflags, &newnum);
849 if (ok != IMAP_SUCCESS) {
850 g_warning("can't append message %s\n", fileinfo->file);
855 if (relation != NULL)
856 g_relation_insert(relation, fileinfo, GINT_TO_POINTER(newnum));
857 if (newnum > last_uid)
866 static gint imap_do_copy(Folder *folder, FolderItem *dest, MsgInfo *msginfo,
867 gboolean remove_source)
870 IMAPSession *session;
874 g_return_val_if_fail(folder != NULL, -1);
875 g_return_val_if_fail(FOLDER_CLASS(folder) == &imap_class, -1);
876 g_return_val_if_fail(dest != NULL, -1);
877 g_return_val_if_fail(msginfo != NULL, -1);
879 session = imap_session_get(folder);
880 if (!session) return -1;
882 if (msginfo->folder == dest) {
883 g_warning("the src folder is identical to the dest.\n");
887 destdir = imap_get_real_path(IMAP_FOLDER(folder), dest->path);
889 /* ensure source folder selected */
890 ok = imap_select(session, IMAP_FOLDER(folder), msginfo->folder->path,
891 NULL, NULL, NULL, NULL);
892 if (ok != IMAP_SUCCESS)
896 debug_print("Moving message %s%c%d to %s ...\n",
897 msginfo->folder->path, G_DIR_SEPARATOR,
898 msginfo->msgnum, destdir);
900 debug_print("Copying message %s%c%d to %s ...\n",
901 msginfo->folder->path, G_DIR_SEPARATOR,
902 msginfo->msgnum, destdir);
904 ok = imap_cmd_copy(session, msginfo->msgnum, destdir, &newuid);
906 if (ok == IMAP_SUCCESS && remove_source) {
907 MsgNumberList numlist;
910 numlist.data = GINT_TO_POINTER(msginfo->msgnum);
912 imap_set_message_flags(session, &numlist,
913 IMAP_FLAG_DELETED, TRUE);
914 ok = imap_cmd_expunge(session);
919 if (ok == IMAP_SUCCESS)
925 gint imap_copy_msg(Folder *folder, FolderItem *dest, MsgInfo *msginfo)
930 g_return_val_if_fail(folder != NULL, -1);
931 g_return_val_if_fail(dest != NULL, -1);
932 g_return_val_if_fail(msginfo != NULL, -1);
933 g_return_val_if_fail(msginfo->folder != NULL, -1);
935 if (folder == msginfo->folder->folder)
936 return imap_do_copy(folder, dest, msginfo, FALSE);
938 srcfile = procmsg_get_message_file(msginfo);
939 if (!srcfile) return -1;
941 ret = imap_add_msg(folder, dest, srcfile, NULL);
948 gint imap_remove_msg(Folder *folder, FolderItem *item, gint uid)
951 IMAPSession *session;
953 MsgNumberList numlist;
955 g_return_val_if_fail(folder != NULL, -1);
956 g_return_val_if_fail(FOLDER_CLASS(folder) == &imap_class, -1);
957 g_return_val_if_fail(item != NULL, -1);
959 session = imap_session_get(folder);
960 if (!session) return -1;
962 ok = imap_select(session, IMAP_FOLDER(folder), item->path,
963 NULL, NULL, NULL, NULL);
964 if (ok != IMAP_SUCCESS)
968 numlist.data = GINT_TO_POINTER(uid);
970 ok = imap_set_message_flags
971 (IMAP_SESSION(REMOTE_FOLDER(folder)->session),
972 &numlist, IMAP_FLAG_DELETED, TRUE);
973 if (ok != IMAP_SUCCESS) {
974 log_warning(_("can't set deleted flags: %d\n"), uid);
978 ok = imap_cmd_expunge(session);
979 if (ok != IMAP_SUCCESS) {
980 log_warning(_("can't expunge\n"));
984 dir = folder_item_get_path(item);
985 if (is_dir_exist(dir))
986 remove_numbered_files(dir, uid, uid);
992 gint imap_remove_all_msg(Folder *folder, FolderItem *item)
994 gint exists, recent, unseen;
995 guint32 uid_validity;
997 IMAPSession *session;
1000 g_return_val_if_fail(folder != NULL, -1);
1001 g_return_val_if_fail(item != NULL, -1);
1003 session = imap_session_get(folder);
1004 if (!session) return -1;
1006 ok = imap_select(session, IMAP_FOLDER(folder), item->path,
1007 &exists, &recent, &unseen, &uid_validity);
1008 if (ok != IMAP_SUCCESS)
1011 return IMAP_SUCCESS;
1013 imap_gen_send(session,
1014 "STORE 1:%d +FLAGS (\\Deleted)", exists);
1015 ok = imap_cmd_ok(session, NULL);
1016 if (ok != IMAP_SUCCESS) {
1017 log_warning(_("can't set deleted flags: 1:%d\n"), exists);
1021 ok = imap_cmd_expunge(session);
1022 if (ok != IMAP_SUCCESS) {
1023 log_warning(_("can't expunge\n"));
1027 dir = folder_item_get_path(item);
1028 if (is_dir_exist(dir))
1029 remove_all_numbered_files(dir);
1032 return IMAP_SUCCESS;
1035 gboolean imap_is_msg_changed(Folder *folder, FolderItem *item, MsgInfo *msginfo)
1037 /* TODO: properly implement this method */
1041 gint imap_close(Folder *folder, FolderItem *item)
1044 IMAPSession *session;
1046 g_return_val_if_fail(folder != NULL, -1);
1048 session = imap_session_get(folder);
1049 if (!session) return -1;
1051 if (session->mbox) {
1052 ok = imap_cmd_close(session);
1053 if (ok != IMAP_SUCCESS)
1054 log_warning(_("can't close folder\n"));
1056 g_free(session->mbox);
1057 session->mbox = NULL;
1065 void imap_scan_tree(Folder *folder)
1068 IMAPSession *session;
1069 gchar *root_folder = NULL;
1071 g_return_if_fail(folder != NULL);
1072 g_return_if_fail(folder->account != NULL);
1074 session = imap_session_get(folder);
1076 if (!folder->node) {
1077 folder_tree_destroy(folder);
1078 item = folder_item_new(folder, folder->name, NULL);
1079 item->folder = folder;
1080 folder->node = g_node_new(item);
1085 if (folder->account->imap_dir && *folder->account->imap_dir) {
1086 Xstrdup_a(root_folder, folder->account->imap_dir, return);
1087 strtailchomp(root_folder, '/');
1088 debug_print("IMAP root directory: %s\n", root_folder);
1091 item = folder_item_new(folder, folder->name, root_folder);
1092 item->folder = folder;
1093 item->no_select = TRUE;
1094 folder->node = g_node_new(item);
1096 imap_scan_tree_recursive(session, item);
1098 imap_create_missing_folders(folder);
1101 static gint imap_scan_tree_recursive(IMAPSession *session, FolderItem *item)
1104 IMAPFolder *imapfolder;
1105 FolderItem *new_item;
1106 GSList *item_list, *cur;
1108 gchar *wildcard_path;
1112 g_return_val_if_fail(item != NULL, -1);
1113 g_return_val_if_fail(item->folder != NULL, -1);
1114 g_return_val_if_fail(item->no_sub == FALSE, -1);
1116 folder = FOLDER(item->folder);
1117 imapfolder = IMAP_FOLDER(folder);
1119 separator = imap_get_path_separator(imapfolder, item->path);
1121 if (item->folder->ui_func)
1122 item->folder->ui_func(folder, item, folder->ui_func_data);
1125 wildcard[0] = separator;
1128 real_path = imap_get_real_path(imapfolder, item->path);
1132 real_path = g_strdup("");
1135 Xstrcat_a(wildcard_path, real_path, wildcard,
1136 {g_free(real_path); return IMAP_ERROR;});
1137 QUOTE_IF_REQUIRED(wildcard_path, wildcard_path);
1139 imap_gen_send(session, "LIST \"\" %s",
1142 strtailchomp(real_path, separator);
1143 item_list = imap_parse_list(imapfolder, session, real_path, NULL);
1146 for (cur = item_list; cur != NULL; cur = cur->next) {
1147 new_item = cur->data;
1148 if (!strcmp(new_item->path, "INBOX")) {
1149 if (!folder->inbox) {
1150 new_item->stype = F_INBOX;
1151 item->folder->inbox = new_item;
1153 folder_item_destroy(new_item);
1156 } else if (!item->parent || item->stype == F_INBOX) {
1159 base = g_basename(new_item->path);
1161 if (!folder->outbox && !strcasecmp(base, "Sent")) {
1162 new_item->stype = F_OUTBOX;
1163 folder->outbox = new_item;
1164 } else if (!folder->draft && !strcasecmp(base, "Drafts")) {
1165 new_item->stype = F_DRAFT;
1166 folder->draft = new_item;
1167 } else if (!folder->queue && !strcasecmp(base, "Queue")) {
1168 new_item->stype = F_QUEUE;
1169 folder->queue = new_item;
1170 } else if (!folder->trash && !strcasecmp(base, "Trash")) {
1171 new_item->stype = F_TRASH;
1172 folder->trash = new_item;
1175 folder_item_append(item, new_item);
1176 if (new_item->no_sub == FALSE)
1177 imap_scan_tree_recursive(session, new_item);
1180 return IMAP_SUCCESS;
1183 static GSList *imap_parse_list(IMAPFolder *folder, IMAPSession *session,
1184 const gchar *real_path, gchar *separator)
1186 gchar buf[IMAPBUFSIZE];
1188 gchar separator_str[16];
1191 gchar *loc_name, *loc_path;
1192 GSList *item_list = NULL;
1194 FolderItem *new_item;
1196 debug_print("getting list of %s ...\n",
1197 *real_path ? real_path : "\"\"");
1199 str = g_string_new(NULL);
1202 if (sock_gets(SESSION(session)->sock, buf, sizeof(buf)) <= 0) {
1203 log_warning(_("error occurred while getting LIST.\n"));
1207 if (buf[0] != '*' || buf[1] != ' ') {
1208 log_print("IMAP4< %s\n", buf);
1211 debug_print("IMAP4< %s\n", buf);
1213 g_string_assign(str, buf);
1215 if (strncmp(p, "LIST ", 5) != 0) continue;
1218 if (*p != '(') continue;
1220 p = strchr_cpy(p, ')', flags, sizeof(flags));
1222 while (*p == ' ') p++;
1224 p = strchr_cpy(p, ' ', separator_str, sizeof(separator_str));
1226 extract_quote(separator_str, '"');
1227 if (!strcmp(separator_str, "NIL"))
1228 separator_str[0] = '\0';
1230 *separator = separator_str[0];
1233 while (*p == ' ') p++;
1234 if (*p == '{' || *p == '"')
1235 p = imap_parse_atom(SESSION(session)->sock, p,
1236 buf, sizeof(buf), str);
1238 strncpy2(buf, p, sizeof(buf));
1239 strtailchomp(buf, separator_str[0]);
1240 if (buf[0] == '\0') continue;
1241 if (!strcmp(buf, real_path)) continue;
1243 if (separator_str[0] != '\0')
1244 subst_char(buf, separator_str[0], '/');
1245 name = g_basename(buf);
1246 if (name[0] == '.') continue;
1248 loc_name = imap_modified_utf7_to_locale(name);
1249 loc_path = imap_modified_utf7_to_locale(buf);
1250 new_item = folder_item_new(FOLDER(folder), loc_name, loc_path);
1251 if (strcasestr(flags, "\\Noinferiors") != NULL)
1252 new_item->no_sub = TRUE;
1253 if (strcmp(buf, "INBOX") != 0 &&
1254 strcasestr(flags, "\\Noselect") != NULL)
1255 new_item->no_select = TRUE;
1257 item_list = g_slist_append(item_list, new_item);
1259 debug_print("folder %s has been added.\n", loc_path);
1264 g_string_free(str, TRUE);
1269 gint imap_create_tree(Folder *folder)
1271 g_return_val_if_fail(folder != NULL, -1);
1272 g_return_val_if_fail(folder->node != NULL, -1);
1273 g_return_val_if_fail(folder->node->data != NULL, -1);
1274 g_return_val_if_fail(folder->account != NULL, -1);
1276 imap_scan_tree(folder);
1277 imap_create_missing_folders(folder);
1282 static void imap_create_missing_folders(Folder *folder)
1284 g_return_if_fail(folder != NULL);
1287 folder->inbox = imap_create_special_folder
1288 (folder, F_INBOX, "INBOX");
1290 if (!folder->outbox)
1291 folder->outbox = imap_create_special_folder
1292 (folder, F_OUTBOX, "Sent");
1294 folder->draft = imap_create_special_folder
1295 (folder, F_DRAFT, "Drafts");
1297 folder->queue = imap_create_special_folder
1298 (folder, F_QUEUE, "Queue");
1301 folder->trash = imap_create_special_folder
1302 (folder, F_TRASH, "Trash");
1305 static FolderItem *imap_create_special_folder(Folder *folder,
1306 SpecialFolderItemType stype,
1310 FolderItem *new_item;
1312 g_return_val_if_fail(folder != NULL, NULL);
1313 g_return_val_if_fail(folder->node != NULL, NULL);
1314 g_return_val_if_fail(folder->node->data != NULL, NULL);
1315 g_return_val_if_fail(folder->account != NULL, NULL);
1316 g_return_val_if_fail(name != NULL, NULL);
1318 item = FOLDER_ITEM(folder->node->data);
1319 new_item = imap_create_folder(folder, item, name);
1322 g_warning("Can't create '%s'\n", name);
1323 if (!folder->inbox) return NULL;
1325 new_item = imap_create_folder(folder, folder->inbox, name);
1327 g_warning("Can't create '%s' under INBOX\n", name);
1329 new_item->stype = stype;
1331 new_item->stype = stype;
1336 static gchar *imap_folder_get_path(Folder *folder)
1340 g_return_val_if_fail(folder != NULL, NULL);
1341 g_return_val_if_fail(folder->account != NULL, NULL);
1343 folder_path = g_strconcat(get_imap_cache_dir(),
1345 folder->account->recv_server,
1347 folder->account->userid,
1353 static gchar *imap_item_get_path(Folder *folder, FolderItem *item)
1355 gchar *folder_path, *path;
1357 g_return_val_if_fail(folder != NULL, NULL);
1358 g_return_val_if_fail(item != NULL, NULL);
1359 folder_path = imap_folder_get_path(folder);
1361 g_return_val_if_fail(folder_path != NULL, NULL);
1362 if (folder_path[0] == G_DIR_SEPARATOR) {
1364 path = g_strconcat(folder_path, G_DIR_SEPARATOR_S,
1367 path = g_strdup(folder_path);
1370 path = g_strconcat(get_home_dir(), G_DIR_SEPARATOR_S,
1371 folder_path, G_DIR_SEPARATOR_S,
1374 path = g_strconcat(get_home_dir(), G_DIR_SEPARATOR_S,
1377 g_free(folder_path);
1382 FolderItem *imap_create_folder(Folder *folder, FolderItem *parent,
1385 gchar *dirpath, *imap_path;
1386 IMAPSession *session;
1387 FolderItem *new_item;
1393 g_return_val_if_fail(folder != NULL, NULL);
1394 g_return_val_if_fail(folder->account != NULL, NULL);
1395 g_return_val_if_fail(parent != NULL, NULL);
1396 g_return_val_if_fail(name != NULL, NULL);
1398 session = imap_session_get(folder);
1399 if (!session) return NULL;
1401 if (!parent->parent && strcmp(name, "INBOX") == 0)
1402 dirpath = g_strdup(name);
1403 else if (parent->path)
1404 dirpath = g_strconcat(parent->path, "/", name, NULL);
1405 else if ((p = strchr(name, '/')) != NULL && *(p + 1) != '\0')
1406 dirpath = g_strdup(name);
1407 else if (folder->account->imap_dir && *folder->account->imap_dir) {
1410 Xstrdup_a(imap_dir, folder->account->imap_dir, return NULL);
1411 strtailchomp(imap_dir, '/');
1412 dirpath = g_strconcat(imap_dir, "/", name, NULL);
1414 dirpath = g_strdup(name);
1416 /* keep trailing directory separator to create a folder that contains
1418 imap_path = imap_locale_to_modified_utf7(dirpath);
1419 strtailchomp(dirpath, '/');
1420 Xstrdup_a(new_name, name, {g_free(dirpath); return NULL;});
1421 strtailchomp(new_name, '/');
1422 separator = imap_get_path_separator(IMAP_FOLDER(folder), imap_path);
1423 imap_path_separator_subst(imap_path, separator);
1424 subst_char(new_name, '/', separator);
1426 if (strcmp(name, "INBOX") != 0) {
1429 gboolean exist = FALSE;
1431 argbuf = g_ptr_array_new();
1432 ok = imap_cmd_list(session, NULL, imap_path,
1434 if (ok != IMAP_SUCCESS) {
1435 log_warning(_("can't create mailbox: LIST failed\n"));
1438 ptr_array_free_strings(argbuf);
1439 g_ptr_array_free(argbuf, TRUE);
1443 for (i = 0; i < argbuf->len; i++) {
1445 str = g_ptr_array_index(argbuf, i);
1446 if (!strncmp(str, "LIST ", 5)) {
1451 ptr_array_free_strings(argbuf);
1452 g_ptr_array_free(argbuf, TRUE);
1455 ok = imap_cmd_create(session, imap_path);
1456 if (ok != IMAP_SUCCESS) {
1457 log_warning(_("can't create mailbox\n"));
1465 new_item = folder_item_new(folder, new_name, dirpath);
1466 folder_item_append(parent, new_item);
1470 dirpath = folder_item_get_path(new_item);
1471 if (!is_dir_exist(dirpath))
1472 make_dir_hier(dirpath);
1478 gint imap_rename_folder(Folder *folder, FolderItem *item, const gchar *name)
1482 gchar *real_oldpath;
1483 gchar *real_newpath;
1486 gchar *old_cache_dir;
1487 gchar *new_cache_dir;
1488 IMAPSession *session;
1491 gint exists, recent, unseen;
1492 guint32 uid_validity;
1494 g_return_val_if_fail(folder != NULL, -1);
1495 g_return_val_if_fail(item != NULL, -1);
1496 g_return_val_if_fail(item->path != NULL, -1);
1497 g_return_val_if_fail(name != NULL, -1);
1499 session = imap_session_get(folder);
1500 if (!session) return -1;
1502 real_oldpath = imap_get_real_path(IMAP_FOLDER(folder), item->path);
1504 g_free(session->mbox);
1505 session->mbox = NULL;
1506 ok = imap_cmd_examine(session, "INBOX",
1507 &exists, &recent, &unseen, &uid_validity);
1508 if (ok != IMAP_SUCCESS) {
1509 g_free(real_oldpath);
1513 separator = imap_get_path_separator(IMAP_FOLDER(folder), item->path);
1514 if (strchr(item->path, G_DIR_SEPARATOR)) {
1515 dirpath = g_dirname(item->path);
1516 newpath = g_strconcat(dirpath, G_DIR_SEPARATOR_S, name, NULL);
1519 newpath = g_strdup(name);
1521 real_newpath = imap_locale_to_modified_utf7(newpath);
1522 imap_path_separator_subst(real_newpath, separator);
1524 ok = imap_cmd_rename(session, real_oldpath, real_newpath);
1525 if (ok != IMAP_SUCCESS) {
1526 log_warning(_("can't rename mailbox: %s to %s\n"),
1527 real_oldpath, real_newpath);
1528 g_free(real_oldpath);
1530 g_free(real_newpath);
1535 item->name = g_strdup(name);
1537 old_cache_dir = folder_item_get_path(item);
1539 node = g_node_find(item->folder->node, G_PRE_ORDER, G_TRAVERSE_ALL,
1541 paths[0] = g_strdup(item->path);
1543 g_node_traverse(node, G_PRE_ORDER, G_TRAVERSE_ALL, -1,
1544 imap_rename_folder_func, paths);
1546 if (is_dir_exist(old_cache_dir)) {
1547 new_cache_dir = folder_item_get_path(item);
1548 if (rename(old_cache_dir, new_cache_dir) < 0) {
1549 FILE_OP_ERROR(old_cache_dir, "rename");
1551 g_free(new_cache_dir);
1554 g_free(old_cache_dir);
1557 g_free(real_oldpath);
1558 g_free(real_newpath);
1563 gint imap_remove_folder(Folder *folder, FolderItem *item)
1566 IMAPSession *session;
1569 gint exists, recent, unseen;
1570 guint32 uid_validity;
1572 g_return_val_if_fail(folder != NULL, -1);
1573 g_return_val_if_fail(item != NULL, -1);
1574 g_return_val_if_fail(item->path != NULL, -1);
1576 session = imap_session_get(folder);
1577 if (!session) return -1;
1579 path = imap_get_real_path(IMAP_FOLDER(folder), item->path);
1581 ok = imap_cmd_examine(session, "INBOX",
1582 &exists, &recent, &unseen, &uid_validity);
1583 if (ok != IMAP_SUCCESS) {
1588 ok = imap_cmd_delete(session, path);
1589 if (ok != IMAP_SUCCESS) {
1590 log_warning(_("can't delete mailbox\n"));
1596 cache_dir = folder_item_get_path(item);
1597 if (is_dir_exist(cache_dir) && remove_dir_recursive(cache_dir) < 0)
1598 g_warning("can't remove directory '%s'\n", cache_dir);
1600 folder_item_remove(item);
1605 static GSList *imap_get_uncached_messages(IMAPSession *session,
1607 MsgNumberList *numlist)
1610 GSList *newlist = NULL;
1611 GSList *llast = NULL;
1616 g_return_val_if_fail(session != NULL, NULL);
1617 g_return_val_if_fail(item != NULL, NULL);
1618 g_return_val_if_fail(item->folder != NULL, NULL);
1619 g_return_val_if_fail(FOLDER_CLASS(item->folder) == &imap_class, NULL);
1621 imapset = numberlist_to_imapset(numlist);
1622 while (imapset != NULL) {
1623 if (imap_cmd_envelope(session, imapset)
1625 log_warning(_("can't get envelope\n"));
1629 str = g_string_new(NULL);
1632 if ((tmp = sock_getline(SESSION(session)->sock)) == NULL) {
1633 log_warning(_("error occurred while getting envelope.\n"));
1634 g_string_free(str, TRUE);
1638 if (tmp[0] != '*' || tmp[1] != ' ') {
1639 log_print("IMAP4< %s\n", tmp);
1643 if (strstr(tmp, "FETCH") == NULL) {
1644 log_print("IMAP4< %s\n", tmp);
1648 log_print("IMAP4< %s\n", tmp);
1649 g_string_assign(str, tmp);
1652 msginfo = imap_parse_envelope
1653 (SESSION(session)->sock, item, str);
1655 log_warning(_("can't parse envelope: %s\n"), str->str);
1658 if (item->stype == F_QUEUE) {
1659 MSG_SET_TMP_FLAGS(msginfo->flags, MSG_QUEUED);
1660 } else if (item->stype == F_DRAFT) {
1661 MSG_SET_TMP_FLAGS(msginfo->flags, MSG_DRAFT);
1664 msginfo->folder = item;
1667 llast = newlist = g_slist_append(newlist, msginfo);
1669 llast = g_slist_append(llast, msginfo);
1670 llast = llast->next;
1674 g_string_free(str, TRUE);
1675 imapset = numberlist_to_imapset(NULL);
1681 static void imap_delete_all_cached_messages(FolderItem *item)
1685 g_return_if_fail(item != NULL);
1686 g_return_if_fail(item->folder != NULL);
1687 g_return_if_fail(FOLDER_CLASS(item->folder) == &imap_class);
1689 debug_print("Deleting all cached messages...\n");
1691 dir = folder_item_get_path(item);
1692 if (is_dir_exist(dir))
1693 remove_all_numbered_files(dir);
1696 debug_print("done.\n");
1700 static SockInfo *imap_open_tunnel(const gchar *server,
1701 const gchar *tunnelcmd,
1704 static SockInfo *imap_open_tunnel(const gchar *server,
1705 const gchar *tunnelcmd)
1710 if ((sock = sock_connect_cmd(server, tunnelcmd)) == NULL) {
1711 log_warning(_("Can't establish IMAP4 session with: %s\n"),
1716 return imap_init_sock(sock, ssl_type);
1718 return imap_init_sock(sock);
1724 static SockInfo *imap_open(const gchar *server, gushort port,
1727 static SockInfo *imap_open(const gchar *server, gushort port)
1732 if ((sock = sock_connect(server, port)) == NULL) {
1733 log_warning(_("Can't connect to IMAP4 server: %s:%d\n"),
1739 if (ssl_type == SSL_TUNNEL && !ssl_init_socket(sock)) {
1740 log_warning(_("Can't establish IMAP4 session with: %s:%d\n"),
1750 static SockInfo *imap_init_sock(SockInfo *sock, SSLType ssl_type)
1752 static SockInfo *imap_init_sock(SockInfo *sock)
1759 static GList *imap_parse_namespace_str(gchar *str)
1764 IMAPNameSpace *namespace;
1765 GList *ns_list = NULL;
1767 while (*p != '\0') {
1768 /* parse ("#foo" "/") */
1770 while (*p && *p != '(') p++;
1771 if (*p == '\0') break;
1774 while (*p && *p != '"') p++;
1775 if (*p == '\0') break;
1779 while (*p && *p != '"') p++;
1780 if (*p == '\0') break;
1784 while (*p && isspace(*p)) p++;
1785 if (*p == '\0') break;
1786 if (strncmp(p, "NIL", 3) == 0)
1788 else if (*p == '"') {
1791 while (*p && *p != '"') p++;
1792 if (*p == '\0') break;
1797 while (*p && *p != ')') p++;
1798 if (*p == '\0') break;
1801 namespace = g_new(IMAPNameSpace, 1);
1802 namespace->name = g_strdup(name);
1803 namespace->separator = separator ? separator[0] : '\0';
1804 ns_list = g_list_append(ns_list, namespace);
1810 static void imap_parse_namespace(IMAPSession *session, IMAPFolder *folder)
1815 g_return_if_fail(session != NULL);
1816 g_return_if_fail(folder != NULL);
1818 if (folder->ns_personal != NULL ||
1819 folder->ns_others != NULL ||
1820 folder->ns_shared != NULL)
1823 if (!imap_has_capability(session, "NAMESPACE")) {
1824 imap_get_namespace_by_list(session, folder);
1828 if (imap_cmd_namespace(session, &ns_str)
1830 log_warning(_("can't get namespace\n"));
1834 str_array = strsplit_parenthesis(ns_str, '(', ')', 3);
1835 if (str_array == NULL) {
1837 imap_get_namespace_by_list(session, folder);
1841 folder->ns_personal = imap_parse_namespace_str(str_array[0]);
1842 if (str_array[0] && str_array[1])
1843 folder->ns_others = imap_parse_namespace_str(str_array[1]);
1844 if (str_array[0] && str_array[1] && str_array[2])
1845 folder->ns_shared = imap_parse_namespace_str(str_array[2]);
1846 g_strfreev(str_array);
1850 static void imap_get_namespace_by_list(IMAPSession *session, IMAPFolder *folder)
1852 GSList *item_list, *cur;
1853 gchar separator = '\0';
1854 IMAPNameSpace *namespace;
1856 g_return_if_fail(session != NULL);
1857 g_return_if_fail(folder != NULL);
1859 if (folder->ns_personal != NULL ||
1860 folder->ns_others != NULL ||
1861 folder->ns_shared != NULL)
1864 imap_gen_send(session, "LIST \"\" \"\"");
1865 item_list = imap_parse_list(folder, session, "", &separator);
1866 for (cur = item_list; cur != NULL; cur = cur->next)
1867 folder_item_destroy(FOLDER_ITEM(cur->data));
1868 g_slist_free(item_list);
1870 namespace = g_new(IMAPNameSpace, 1);
1871 namespace->name = g_strdup("");
1872 namespace->separator = separator;
1873 folder->ns_personal = g_list_append(NULL, namespace);
1876 static IMAPNameSpace *imap_find_namespace_from_list(GList *ns_list,
1879 IMAPNameSpace *namespace = NULL;
1880 gchar *tmp_path, *name;
1882 if (!path) path = "";
1884 Xstrcat_a(tmp_path, path, "/", return NULL);
1886 for (; ns_list != NULL; ns_list = ns_list->next) {
1887 IMAPNameSpace *tmp_ns = ns_list->data;
1889 Xstrdup_a(name, tmp_ns->name, return namespace);
1890 if (tmp_ns->separator && tmp_ns->separator != '/')
1891 subst_char(name, tmp_ns->separator, '/');
1892 if (strncmp(tmp_path, name, strlen(name)) == 0)
1899 static IMAPNameSpace *imap_find_namespace(IMAPFolder *folder,
1902 IMAPNameSpace *namespace;
1904 g_return_val_if_fail(folder != NULL, NULL);
1906 namespace = imap_find_namespace_from_list(folder->ns_personal, path);
1907 if (namespace) return namespace;
1908 namespace = imap_find_namespace_from_list(folder->ns_others, path);
1909 if (namespace) return namespace;
1910 namespace = imap_find_namespace_from_list(folder->ns_shared, path);
1911 if (namespace) return namespace;
1916 static gchar imap_get_path_separator(IMAPFolder *folder, const gchar *path)
1918 IMAPNameSpace *namespace;
1919 gchar separator = '/';
1921 namespace = imap_find_namespace(folder, path);
1922 if (namespace && namespace->separator)
1923 separator = namespace->separator;
1928 static gchar *imap_get_real_path(IMAPFolder *folder, const gchar *path)
1933 g_return_val_if_fail(folder != NULL, NULL);
1934 g_return_val_if_fail(path != NULL, NULL);
1936 real_path = imap_locale_to_modified_utf7(path);
1937 separator = imap_get_path_separator(folder, path);
1938 imap_path_separator_subst(real_path, separator);
1943 static gchar *imap_parse_atom(SockInfo *sock, gchar *src,
1944 gchar *dest, gint dest_len, GString *str)
1946 gchar *cur_pos = src;
1949 g_return_val_if_fail(str != NULL, cur_pos);
1951 /* read the next line if the current response buffer is empty */
1952 while (isspace(*cur_pos)) cur_pos++;
1953 while (*cur_pos == '\0') {
1954 if ((nextline = sock_getline(sock)) == NULL)
1956 g_string_assign(str, nextline);
1958 strretchomp(nextline);
1959 /* log_print("IMAP4< %s\n", nextline); */
1960 debug_print("IMAP4< %s\n", nextline);
1963 while (isspace(*cur_pos)) cur_pos++;
1966 if (!strncmp(cur_pos, "NIL", 3)) {
1969 } else if (*cur_pos == '\"') {
1972 p = get_quoted(cur_pos, '\"', dest, dest_len);
1973 cur_pos = p ? p : cur_pos + 2;
1974 } else if (*cur_pos == '{') {
1979 cur_pos = strchr_cpy(cur_pos + 1, '}', buf, sizeof(buf));
1981 g_return_val_if_fail(len > 0, cur_pos);
1983 g_string_truncate(str, 0);
1987 if ((nextline = sock_getline(sock)) == NULL)
1989 line_len += strlen(nextline);
1990 g_string_append(str, nextline);
1992 strretchomp(nextline);
1993 /* log_print("IMAP4< %s\n", nextline); */
1994 debug_print("IMAP4< %s\n", nextline);
1996 } while (line_len < len);
1998 memcpy(dest, cur_pos, MIN(len, dest_len - 1));
1999 dest[MIN(len, dest_len - 1)] = '\0';
2006 static gchar *imap_get_header(SockInfo *sock, gchar *cur_pos, gchar **headers,
2016 g_return_val_if_fail(str != NULL, cur_pos);
2018 while (isspace(*cur_pos)) cur_pos++;
2020 g_return_val_if_fail(*cur_pos == '{', cur_pos);
2022 cur_pos = strchr_cpy(cur_pos + 1, '}', buf, sizeof(buf));
2024 g_return_val_if_fail(len > 0, cur_pos);
2026 g_string_truncate(str, 0);
2030 if ((nextline = sock_getline(sock)) == NULL)
2032 block_len += strlen(nextline);
2033 g_string_append(str, nextline);
2035 strretchomp(nextline);
2036 /* debug_print("IMAP4< %s\n", nextline); */
2038 } while (block_len < len);
2040 debug_print("IMAP4< [contents of BODY.PEEK[HEADER.FIELDS (...)]]\n");
2042 *headers = g_strndup(cur_pos, len);
2045 while (isspace(*cur_pos)) cur_pos++;
2046 while (*cur_pos == '\0') {
2047 if ((nextline = sock_getline(sock)) == NULL)
2049 g_string_assign(str, nextline);
2051 strretchomp(nextline);
2052 debug_print("IMAP4< %s\n", nextline);
2055 while (isspace(*cur_pos)) cur_pos++;
2061 static MsgFlags imap_parse_flags(const gchar *flag_str)
2063 const gchar *p = flag_str;
2064 MsgFlags flags = {0, 0};
2066 flags.perm_flags = MSG_UNREAD;
2068 while ((p = strchr(p, '\\')) != NULL) {
2071 if (g_strncasecmp(p, "Recent", 6) == 0 && MSG_IS_UNREAD(flags)) {
2072 MSG_SET_PERM_FLAGS(flags, MSG_NEW);
2073 } else if (g_strncasecmp(p, "Seen", 4) == 0) {
2074 MSG_UNSET_PERM_FLAGS(flags, MSG_NEW|MSG_UNREAD);
2075 } else if (g_strncasecmp(p, "Deleted", 7) == 0) {
2076 MSG_SET_PERM_FLAGS(flags, MSG_DELETED);
2077 } else if (g_strncasecmp(p, "Flagged", 7) == 0) {
2078 MSG_SET_PERM_FLAGS(flags, MSG_MARKED);
2079 } else if (g_strncasecmp(p, "Answered", 8) == 0) {
2080 MSG_SET_PERM_FLAGS(flags, MSG_REPLIED);
2087 static MsgInfo *imap_parse_envelope(SockInfo *sock, FolderItem *item,
2090 gchar buf[IMAPBUFSIZE];
2091 MsgInfo *msginfo = NULL;
2096 MsgFlags flags = {0, 0}, imap_flags = {0, 0};
2098 g_return_val_if_fail(line_str != NULL, NULL);
2099 g_return_val_if_fail(line_str->str[0] == '*' &&
2100 line_str->str[1] == ' ', NULL);
2102 MSG_SET_TMP_FLAGS(flags, MSG_IMAP);
2103 if (item->stype == F_QUEUE) {
2104 MSG_SET_TMP_FLAGS(flags, MSG_QUEUED);
2105 } else if (item->stype == F_DRAFT) {
2106 MSG_SET_TMP_FLAGS(flags, MSG_DRAFT);
2109 cur_pos = line_str->str + 2;
2111 #define PARSE_ONE_ELEMENT(ch) \
2113 cur_pos = strchr_cpy(cur_pos, ch, buf, sizeof(buf)); \
2114 if (cur_pos == NULL) { \
2115 g_warning("cur_pos == NULL\n"); \
2116 procmsg_msginfo_free(msginfo); \
2121 PARSE_ONE_ELEMENT(' ');
2124 PARSE_ONE_ELEMENT(' ');
2125 g_return_val_if_fail(!strcmp(buf, "FETCH"), NULL);
2127 g_return_val_if_fail(*cur_pos == '(', NULL);
2130 while (*cur_pos != '\0' && *cur_pos != ')') {
2131 while (*cur_pos == ' ') cur_pos++;
2133 if (!strncmp(cur_pos, "UID ", 4)) {
2135 uid = strtoul(cur_pos, &cur_pos, 10);
2136 } else if (!strncmp(cur_pos, "FLAGS ", 6)) {
2138 if (*cur_pos != '(') {
2139 g_warning("*cur_pos != '('\n");
2140 procmsg_msginfo_free(msginfo);
2144 PARSE_ONE_ELEMENT(')');
2145 imap_flags = imap_parse_flags(buf);
2146 } else if (!strncmp(cur_pos, "RFC822.SIZE ", 12)) {
2148 size = strtol(cur_pos, &cur_pos, 10);
2149 } else if (!strncmp(cur_pos, "BODY[HEADER.FIELDS ", 19)) {
2153 if (*cur_pos != '(') {
2154 g_warning("*cur_pos != '('\n");
2155 procmsg_msginfo_free(msginfo);
2159 PARSE_ONE_ELEMENT(')');
2160 if (*cur_pos != ']') {
2161 g_warning("*cur_pos != ']'\n");
2162 procmsg_msginfo_free(msginfo);
2167 cur_pos = imap_get_header(sock, cur_pos, &headers,
2169 msginfo = procheader_parse_str(headers, flags, FALSE, FALSE);
2172 g_warning("invalid FETCH response: %s\n", cur_pos);
2178 msginfo->msgnum = uid;
2179 msginfo->size = size;
2180 msginfo->flags.tmp_flags |= imap_flags.tmp_flags;
2181 msginfo->flags.perm_flags = imap_flags.perm_flags;
2187 static gchar *imap_get_flag_str(IMAPFlags flags)
2192 str = g_string_new(NULL);
2194 if (IMAP_IS_SEEN(flags)) g_string_append(str, "\\Seen ");
2195 if (IMAP_IS_ANSWERED(flags)) g_string_append(str, "\\Answered ");
2196 if (IMAP_IS_FLAGGED(flags)) g_string_append(str, "\\Flagged ");
2197 if (IMAP_IS_DELETED(flags)) g_string_append(str, "\\Deleted ");
2198 if (IMAP_IS_DRAFT(flags)) g_string_append(str, "\\Draft");
2200 if (str->len > 0 && str->str[str->len - 1] == ' ')
2201 g_string_truncate(str, str->len - 1);
2204 g_string_free(str, FALSE);
2209 static gint imap_set_message_flags(IMAPSession *session,
2210 MsgNumberList *numlist,
2219 flag_str = imap_get_flag_str(flags);
2220 cmd = g_strconcat(is_set ? "+FLAGS.SILENT (" : "-FLAGS.SILENT (",
2221 flag_str, ")", NULL);
2224 imapset = numberlist_to_imapset(numlist);
2225 while (imapset != NULL) {
2226 ok = imap_cmd_store(session, imapset, cmd);
2227 imapset = numberlist_to_imapset(NULL);
2234 static gint imap_select(IMAPSession *session, IMAPFolder *folder,
2236 gint *exists, gint *recent, gint *unseen,
2237 guint32 *uid_validity)
2241 gint exists_, recent_, unseen_, uid_validity_;
2243 if (!exists || !recent || !unseen || !uid_validity) {
2244 if (session->mbox && strcmp(session->mbox, path) == 0)
2245 return IMAP_SUCCESS;
2249 uid_validity = &uid_validity_;
2252 g_free(session->mbox);
2253 session->mbox = NULL;
2255 real_path = imap_get_real_path(folder, path);
2256 ok = imap_cmd_select(session, real_path,
2257 exists, recent, unseen, uid_validity);
2258 if (ok != IMAP_SUCCESS)
2259 log_warning(_("can't select folder: %s\n"), real_path);
2261 session->mbox = g_strdup(path);
2262 session->folder_content_changed = FALSE;
2269 #define THROW(err) { ok = err; goto catch; }
2271 static gint imap_status(IMAPSession *session, IMAPFolder *folder,
2273 gint *messages, gint *recent,
2274 guint32 *uid_next, guint32 *uid_validity,
2283 *messages = *recent = *uid_next = *uid_validity = *unseen = 0;
2285 argbuf = g_ptr_array_new();
2287 real_path = imap_get_real_path(folder, path);
2288 QUOTE_IF_REQUIRED(real_path_, real_path);
2289 imap_gen_send(session, "STATUS %s "
2290 "(MESSAGES RECENT UIDNEXT UIDVALIDITY UNSEEN)",
2293 ok = imap_cmd_ok(session, argbuf);
2294 if (ok != IMAP_SUCCESS) THROW(ok);
2296 str = search_array_str(argbuf, "STATUS");
2297 if (!str) THROW(IMAP_ERROR);
2299 str = strchr(str, '(');
2300 if (!str) THROW(IMAP_ERROR);
2302 while (*str != '\0' && *str != ')') {
2303 while (*str == ' ') str++;
2305 if (!strncmp(str, "MESSAGES ", 9)) {
2307 *messages = strtol(str, &str, 10);
2308 } else if (!strncmp(str, "RECENT ", 7)) {
2310 *recent = strtol(str, &str, 10);
2311 } else if (!strncmp(str, "UIDNEXT ", 8)) {
2313 *uid_next = strtoul(str, &str, 10);
2314 } else if (!strncmp(str, "UIDVALIDITY ", 12)) {
2316 *uid_validity = strtoul(str, &str, 10);
2317 } else if (!strncmp(str, "UNSEEN ", 7)) {
2319 *unseen = strtol(str, &str, 10);
2321 g_warning("invalid STATUS response: %s\n", str);
2328 ptr_array_free_strings(argbuf);
2329 g_ptr_array_free(argbuf, TRUE);
2337 /* low-level IMAP4rev1 commands */
2339 static gint imap_cmd_authenticate(IMAPSession *session, const gchar *user,
2340 const gchar *pass, IMAPAuthType type)
2347 gchar hexdigest[33];
2351 auth_type = "CRAM-MD5";
2353 imap_gen_send(session, "AUTHENTICATE %s", auth_type);
2354 ok = imap_gen_recv(session, &buf);
2355 if (ok != IMAP_SUCCESS || buf[0] != '+' || buf[1] != ' ') {
2360 challenge = g_malloc(strlen(buf + 2) + 1);
2361 challenge_len = base64_decode(challenge, buf + 2, -1);
2362 challenge[challenge_len] = '\0';
2364 log_print("IMAP< [Decoded: %s]\n", challenge);
2366 md5_hex_hmac(hexdigest, challenge, challenge_len, pass, strlen(pass));
2369 response = g_strdup_printf("%s %s", user, hexdigest);
2370 log_print("IMAP> [Encoded: %s]\n", response);
2371 response64 = g_malloc((strlen(response) + 3) * 2 + 1);
2372 base64_encode(response64, response, strlen(response));
2375 log_print("IMAP> %s\n", response64);
2376 sock_puts(SESSION(session)->sock, response64);
2377 ok = imap_cmd_ok(session, NULL);
2378 if (ok != IMAP_SUCCESS)
2379 log_warning(_("IMAP4 authentication failed.\n"));
2384 static gint imap_cmd_login(IMAPSession *session,
2385 const gchar *user, const gchar *pass)
2387 gchar *user_, *pass_;
2390 QUOTE_IF_REQUIRED(user_, user);
2391 QUOTE_IF_REQUIRED(pass_, pass);
2392 imap_gen_send(session, "LOGIN %s %s", user_, pass_);
2394 ok = imap_cmd_ok(session, NULL);
2395 if (ok != IMAP_SUCCESS)
2396 log_warning(_("IMAP4 login failed.\n"));
2401 static gint imap_cmd_logout(IMAPSession *session)
2403 imap_gen_send(session, "LOGOUT");
2404 return imap_cmd_ok(session, NULL);
2407 /* Send CAPABILITY, and examine the server's response to see whether this
2408 * connection is pre-authenticated or not and build a list of CAPABILITIES. */
2409 static gint imap_greeting(IMAPSession *session)
2414 imap_gen_send(session, "CAPABILITY");
2416 argbuf = g_ptr_array_new();
2418 if (imap_cmd_ok(session, argbuf) != IMAP_SUCCESS ||
2419 ((capstr = search_array_str(argbuf, "CAPABILITY ")) == NULL)) {
2420 ptr_array_free_strings(argbuf);
2421 g_ptr_array_free(argbuf, TRUE);
2425 session->authenticated = search_array_str(argbuf, "PREAUTH") != NULL;
2427 capstr += strlen("CAPABILITY ");
2429 IMAP_SESSION(session)->capability = g_strsplit(capstr, " ", 0);
2431 ptr_array_free_strings(argbuf);
2432 g_ptr_array_free(argbuf, TRUE);
2437 static gboolean imap_has_capability(IMAPSession *session, const gchar *cap)
2441 for (p = session->capability; *p != NULL; ++p)
2442 if (g_strcasecmp(*p, cap) == 0)
2448 void imap_free_capabilities(IMAPSession *session)
2450 g_strfreev(session->capability);
2451 session->capability = NULL;
2454 static const IMAPSet numberlist_to_imapset(MsgNumberList *list)
2456 static GString *imapset = NULL;
2457 static MsgNumberList *numlist, *elem;
2458 guint first, last, next;
2460 if (imapset == NULL)
2461 imapset = g_string_sized_new(256);
2463 g_string_truncate(imapset, 0);
2466 g_slist_free(numlist);
2467 numlist = g_slist_copy(list);
2468 numlist = g_slist_sort(numlist, g_int_compare);
2469 } else if (numlist == NULL) {
2473 first = GPOINTER_TO_INT(numlist->data);
2475 for(elem = g_slist_next(numlist); elem != NULL; elem = g_slist_next(elem)) {
2476 next = GPOINTER_TO_INT(elem->data);
2478 if(next != (last + 1)) {
2479 if (imapset->len > 0)
2480 g_string_append(imapset, ",");
2482 g_string_sprintfa(imapset, "%d", first);
2484 g_string_sprintfa(imapset, "%d:%d", first, last);
2486 if (imapset->len > IMAPCMDLIMIT) {
2496 if (imapset->len > 0)
2497 g_string_append(imapset, ",");
2499 g_string_sprintfa(imapset, "%d", first);
2501 g_string_sprintfa(imapset, "%d:%d", first, last);
2503 g_slist_free(numlist);
2506 MsgNumberList *remaining;
2508 remaining = elem->next;
2509 remaining = g_slist_prepend(remaining, elem->data);
2511 g_slist_free(numlist);
2512 numlist = remaining;
2515 return imapset->str;
2518 static gint imap_cmd_noop(IMAPSession *session)
2520 imap_gen_send(session, "NOOP");
2521 return imap_cmd_ok(session, NULL);
2524 static gint imap_cmd_starttls(IMAPSession *session)
2526 imap_gen_send(session, "STARTTLS");
2527 return imap_cmd_ok(session, NULL);
2530 #define THROW(err) { ok = err; goto catch; }
2532 static gint imap_cmd_namespace(IMAPSession *session, gchar **ns_str)
2538 argbuf = g_ptr_array_new();
2540 imap_gen_send(session, "NAMESPACE");
2541 if ((ok = imap_cmd_ok(session, argbuf)) != IMAP_SUCCESS) THROW(ok);
2543 str = search_array_str(argbuf, "NAMESPACE");
2544 if (!str) THROW(IMAP_ERROR);
2546 *ns_str = g_strdup(str);
2549 ptr_array_free_strings(argbuf);
2550 g_ptr_array_free(argbuf, TRUE);
2557 static gint imap_cmd_list(IMAPSession *session, const gchar *ref,
2558 const gchar *mailbox, GPtrArray *argbuf)
2560 gchar *ref_, *mailbox_;
2562 if (!ref) ref = "\"\"";
2563 if (!mailbox) mailbox = "\"\"";
2565 QUOTE_IF_REQUIRED(ref_, ref);
2566 QUOTE_IF_REQUIRED(mailbox_, mailbox);
2567 imap_gen_send(session, "LIST %s %s", ref_, mailbox_);
2569 return imap_cmd_ok(session, argbuf);
2572 #define THROW goto catch
2574 static gint imap_cmd_do_select(IMAPSession *session, const gchar *folder,
2576 gint *exists, gint *recent, gint *unseen,
2577 guint32 *uid_validity)
2585 *exists = *recent = *unseen = *uid_validity = 0;
2586 argbuf = g_ptr_array_new();
2589 select_cmd = "EXAMINE";
2591 select_cmd = "SELECT";
2593 QUOTE_IF_REQUIRED(folder_, folder);
2594 imap_gen_send(session, "%s %s", select_cmd, folder_);
2596 if ((ok = imap_cmd_ok(session, argbuf)) != IMAP_SUCCESS) THROW;
2598 resp_str = search_array_contain_str(argbuf, "EXISTS");
2600 if (sscanf(resp_str,"%d EXISTS", exists) != 1) {
2601 g_warning("imap_cmd_select(): invalid EXISTS line.\n");
2606 resp_str = search_array_contain_str(argbuf, "RECENT");
2608 if (sscanf(resp_str, "%d RECENT", recent) != 1) {
2609 g_warning("imap_cmd_select(): invalid RECENT line.\n");
2614 resp_str = search_array_contain_str(argbuf, "UIDVALIDITY");
2616 if (sscanf(resp_str, "OK [UIDVALIDITY %u] ", uid_validity)
2618 g_warning("imap_cmd_select(): invalid UIDVALIDITY line.\n");
2623 resp_str = search_array_contain_str(argbuf, "UNSEEN");
2625 if (sscanf(resp_str, "OK [UNSEEN %d] ", unseen) != 1) {
2626 g_warning("imap_cmd_select(): invalid UNSEEN line.\n");
2632 ptr_array_free_strings(argbuf);
2633 g_ptr_array_free(argbuf, TRUE);
2638 static gint imap_cmd_select(IMAPSession *session, const gchar *folder,
2639 gint *exists, gint *recent, gint *unseen,
2640 guint32 *uid_validity)
2642 return imap_cmd_do_select(session, folder, FALSE,
2643 exists, recent, unseen, uid_validity);
2646 static gint imap_cmd_examine(IMAPSession *session, const gchar *folder,
2647 gint *exists, gint *recent, gint *unseen,
2648 guint32 *uid_validity)
2650 return imap_cmd_do_select(session, folder, TRUE,
2651 exists, recent, unseen, uid_validity);
2656 static gint imap_cmd_create(IMAPSession *session, const gchar *folder)
2660 QUOTE_IF_REQUIRED(folder_, folder);
2661 imap_gen_send(session, "CREATE %s", folder_);
2663 return imap_cmd_ok(session, NULL);
2666 static gint imap_cmd_rename(IMAPSession *session, const gchar *old_folder,
2667 const gchar *new_folder)
2669 gchar *old_folder_, *new_folder_;
2671 QUOTE_IF_REQUIRED(old_folder_, old_folder);
2672 QUOTE_IF_REQUIRED(new_folder_, new_folder);
2673 imap_gen_send(session, "RENAME %s %s", old_folder_, new_folder_);
2675 return imap_cmd_ok(session, NULL);
2678 static gint imap_cmd_delete(IMAPSession *session, const gchar *folder)
2682 QUOTE_IF_REQUIRED(folder_, folder);
2683 imap_gen_send(session, "DELETE %s", folder_);
2685 return imap_cmd_ok(session, NULL);
2688 static gint imap_cmd_search(IMAPSession *session, const gchar *criteria, GSList **list)
2694 g_return_val_if_fail(criteria != NULL, IMAP_ERROR);
2695 g_return_val_if_fail(list != NULL, IMAP_ERROR);
2699 argbuf = g_ptr_array_new();
2700 imap_gen_send(session, "UID SEARCH %s", criteria);
2702 ok = imap_cmd_ok(session, argbuf);
2703 if (ok != IMAP_SUCCESS) {
2704 ptr_array_free_strings(argbuf);
2705 g_ptr_array_free(argbuf, TRUE);
2709 if ((uidlist = search_array_str(argbuf, "SEARCH ")) != NULL) {
2710 gchar **strlist, **p;
2712 strlist = g_strsplit(uidlist + 7, " ", 0);
2713 for (p = strlist; *p != NULL; ++p) {
2716 if (sscanf(*p, "%d", &msgnum) == 1)
2717 *list = g_slist_append(*list, GINT_TO_POINTER(msgnum));
2719 g_strfreev(strlist);
2721 ptr_array_free_strings(argbuf);
2722 g_ptr_array_free(argbuf, TRUE);
2724 return IMAP_SUCCESS;
2727 static gint imap_cmd_fetch(IMAPSession *session, guint32 uid, const gchar *filename)
2735 g_return_val_if_fail(filename != NULL, IMAP_ERROR);
2737 imap_gen_send(session, "UID FETCH %d BODY.PEEK[]", uid);
2739 while ((ok = imap_gen_recv(session, &buf)) == IMAP_SUCCESS) {
2740 if (buf[0] != '*' || buf[1] != ' ') {
2744 if (strstr(buf, "FETCH") != NULL) break;
2747 if (ok != IMAP_SUCCESS) {
2752 #define RETURN_ERROR_IF_FAIL(cond) \
2755 return IMAP_ERROR; \
2758 cur_pos = strchr(buf, '{');
2759 RETURN_ERROR_IF_FAIL(cur_pos != NULL);
2760 cur_pos = strchr_cpy(cur_pos + 1, '}', size_str, sizeof(size_str));
2761 RETURN_ERROR_IF_FAIL(cur_pos != NULL);
2762 size_num = atol(size_str);
2763 RETURN_ERROR_IF_FAIL(size_num > 0);
2765 RETURN_ERROR_IF_FAIL(*cur_pos == '\0');
2767 #undef RETURN_ERROR_IF_FAIL
2771 if (recv_bytes_write_to_file(SESSION(session)->sock, size_num, filename) != 0)
2774 if (imap_gen_recv(session, &buf) != IMAP_SUCCESS) {
2779 if (buf[0] == '\0' || buf[strlen(buf) - 1] != ')') {
2785 ok = imap_cmd_ok(session, NULL);
2790 static gint imap_cmd_append(IMAPSession *session, const gchar *destfolder,
2791 const gchar *file, IMAPFlags flags, gint32 *new_uid)
2798 gchar buf[BUFFSIZE];
2803 g_return_val_if_fail(file != NULL, IMAP_ERROR);
2805 size = get_file_size_as_crlf(file);
2806 if ((fp = fopen(file, "rb")) == NULL) {
2807 FILE_OP_ERROR(file, "fopen");
2810 QUOTE_IF_REQUIRED(destfolder_, destfolder);
2811 flag_str = imap_get_flag_str(flags);
2812 imap_gen_send(session, "APPEND %s (%s) {%d}",
2813 destfolder_, flag_str, size);
2816 ok = imap_gen_recv(session, &ret);
2817 if (ok != IMAP_SUCCESS || ret[0] != '+' || ret[1] != ' ') {
2818 log_warning(_("can't append %s to %s\n"), file, destfolder_);
2825 log_print("IMAP4> %s\n", _("(sending file...)"));
2827 while (fgets(buf, sizeof(buf), fp) != NULL) {
2829 if (sock_puts(SESSION(session)->sock, buf) < 0) {
2836 FILE_OP_ERROR(file, "fgets");
2841 sock_puts(SESSION(session)->sock, "");
2845 reply = g_ptr_array_new();
2848 ok = imap_cmd_ok(session, reply);
2849 if (ok != IMAP_SUCCESS)
2850 log_warning(_("can't append message to %s\n"), destfolder_);
2852 (new_uid != NULL) &&
2853 (imap_has_capability(session, "UIDPLUS") && reply->len > 0) &&
2854 ((okmsginfo = g_ptr_array_index(reply, reply->len - 1)) != NULL) &&
2855 (sscanf(okmsginfo, "%*u OK [APPENDUID %*u %u]", &newuid) == 1)) {
2859 ptr_array_free_strings(reply);
2860 g_ptr_array_free(reply, TRUE);
2865 static gint imap_cmd_copy(IMAPSession * session,
2867 const gchar * destfolder, gint32 * new_uid)
2870 gint32 olduid, newuid;
2875 g_return_val_if_fail(destfolder != NULL, IMAP_ERROR);
2876 g_return_val_if_fail(session != NULL, IMAP_ERROR);
2877 g_return_val_if_fail(new_uid != NULL, IMAP_ERROR);
2879 QUOTE_IF_REQUIRED(destfolder_, destfolder);
2880 imap_gen_send(session, "UID COPY %d %s", msgnum, destfolder_);
2882 reply = g_ptr_array_new();
2885 ok = imap_cmd_ok(session, reply);
2886 if (ok != IMAP_SUCCESS)
2887 log_warning(_("can't copy %d to %s\n"), msgnum, destfolder_);
2888 else if (imap_has_capability(session, "UIDPLUS") && reply->len > 0)
2889 if ((okmsginfo = g_ptr_array_index(reply, reply->len - 1)) != NULL &&
2890 sscanf(okmsginfo, "%*u OK [COPYUID %*u %u %u]", &olduid, &newuid) == 2 &&
2894 ptr_array_free_strings(reply);
2895 g_ptr_array_free(reply, TRUE);
2899 gint imap_cmd_envelope(IMAPSession *session, IMAPSet set)
2901 static GString *header_fields = NULL;
2903 if (header_fields == NULL) {
2904 const HeaderEntry *headers, *elem;
2906 headers = procheader_get_headernames(FALSE);
2907 header_fields = g_string_new("");
2909 for (elem = headers; elem->name != NULL; ++elem) {
2910 gint namelen = strlen(elem->name);
2912 /* Header fields ending with space are not rfc822 headers */
2913 if (elem->name[namelen - 1] == ' ')
2916 /* strip : at the of header field */
2917 if(elem->name[namelen - 1] == ':')
2923 g_string_sprintfa(header_fields, "%s%.*s",
2924 header_fields->str[0] != '\0' ? " " : "",
2925 namelen, elem->name);
2930 (session, "UID FETCH %s (UID FLAGS RFC822.SIZE BODY.PEEK[HEADER.FIELDS (%s)])",
2931 set, header_fields->str);
2933 return IMAP_SUCCESS;
2936 static gint imap_cmd_store(IMAPSession *session, IMAPSet set,
2941 imap_gen_send(session, "UID STORE %s %s",
2944 if ((ok = imap_cmd_ok(session, NULL)) != IMAP_SUCCESS) {
2945 log_warning(_("error while imap command: STORE %s %s\n"),
2950 return IMAP_SUCCESS;
2953 static gint imap_cmd_expunge(IMAPSession *session)
2957 imap_gen_send(session, "EXPUNGE");
2958 if ((ok = imap_cmd_ok(session, NULL)) != IMAP_SUCCESS) {
2959 log_warning(_("error while imap command: EXPUNGE\n"));
2963 return IMAP_SUCCESS;
2966 static gint imap_cmd_close(IMAPSession *session)
2970 imap_gen_send(session, "CLOSE");
2971 if ((ok = imap_cmd_ok(session, NULL)) != IMAP_SUCCESS)
2972 log_warning(_("error while imap command: CLOSE\n"));
2977 static gint imap_cmd_ok(IMAPSession *session, GPtrArray *argbuf)
2979 gint ok = IMAP_SUCCESS;
2984 while ((ok = imap_gen_recv(session, &buf))
2986 // make sure data is long enough for any substring of buf
2987 data = alloca(strlen(buf) + 1);
2989 // untagged line read
2990 if (buf[0] == '*' && buf[1] == ' ') {
2993 g_ptr_array_add(argbuf, g_strdup(buf + 2));
2995 if (sscanf(buf + 2, "%d %s", &num, data) >= 2) {
2996 if (!strcmp(data, "EXISTS")) {
2997 session->exists = num;
2998 session->folder_content_changed = TRUE;
3001 if(!strcmp(data, "EXPUNGE")) {
3003 session->folder_content_changed = TRUE;
3006 // tagged line with correct tag and OK response found
3007 } else if ((sscanf(buf, "%d %s", &cmd_num, data) >= 2) &&
3008 (cmd_num == session->cmd_count) &&
3009 !strcmp(data, "OK")) {
3011 g_ptr_array_add(argbuf, g_strdup(buf));
3025 static void imap_gen_send(IMAPSession *session, const gchar *format, ...)
3032 va_start(args, format);
3033 tmp = g_strdup_vprintf(format, args);
3036 session->cmd_count++;
3038 buf = g_strdup_printf("%d %s\r\n", session->cmd_count, tmp);
3039 if (!strncasecmp(tmp, "LOGIN ", 6) && (p = strchr(tmp + 6, ' '))) {
3041 log_print("IMAP4> %d %s ********\n", session->cmd_count, tmp);
3043 log_print("IMAP4> %d %s\n", session->cmd_count, tmp);
3045 sock_write_all(SESSION(session)->sock, buf, strlen(buf));
3050 static gint imap_gen_recv(IMAPSession *session, gchar **ret)
3052 if ((*ret = sock_getline(SESSION(session)->sock)) == NULL)
3057 log_print("IMAP4< %s\n", *ret);
3059 return IMAP_SUCCESS;
3063 /* misc utility functions */
3065 static gchar *strchr_cpy(const gchar *src, gchar ch, gchar *dest, gint len)
3070 tmp = strchr(src, ch);
3074 memcpy(dest, src, MIN(tmp - src, len - 1));
3075 dest[MIN(tmp - src, len - 1)] = '\0';
3080 static gchar *get_quoted(const gchar *src, gchar ch, gchar *dest, gint len)
3082 const gchar *p = src;
3085 g_return_val_if_fail(*p == ch, NULL);
3090 while (*p != '\0' && *p != ch) {
3092 if (*p == '\\' && *(p + 1) != '\0')
3101 return (gchar *)(*p == ch ? p + 1 : p);
3104 static gchar *search_array_contain_str(GPtrArray *array, const gchar *str)
3108 for (i = 0; i < array->len; i++) {
3111 tmp = g_ptr_array_index(array, i);
3112 if (strstr(tmp, str) != NULL)
3119 static gchar *search_array_str(GPtrArray *array, const gchar *str)
3126 for (i = 0; i < array->len; i++) {
3129 tmp = g_ptr_array_index(array, i);
3130 if (!strncmp(tmp, str, len))
3137 static void imap_path_separator_subst(gchar *str, gchar separator)
3140 gboolean in_escape = FALSE;
3142 if (!separator || separator == '/') return;
3144 for (p = str; *p != '\0'; p++) {
3145 if (*p == '/' && !in_escape)
3147 else if (*p == '&' && *(p + 1) != '-' && !in_escape)
3149 else if (*p == '-' && in_escape)
3154 static gchar *imap_modified_utf7_to_locale(const gchar *mutf7_str)
3157 const gchar *from_p;
3160 to = g_malloc(strlen(mutf7_str) + 1);
3163 for (from_p = mutf7_str; *from_p != '\0'; from_p++) {
3164 if (*from_p == '&' && *(from_p + 1) == '-') {
3174 static iconv_t cd = (iconv_t)-1;
3175 static gboolean iconv_ok = TRUE;
3178 size_t norm_utf7_len;
3180 gchar *to_str, *to_p;
3182 gboolean in_escape = FALSE;
3184 if (!iconv_ok) return g_strdup(mutf7_str);
3186 if (cd == (iconv_t)-1) {
3187 cd = iconv_open(conv_get_current_charset_str(), "UTF-7");
3188 if (cd == (iconv_t)-1) {
3189 g_warning("iconv cannot convert UTF-7 to %s\n",
3190 conv_get_current_charset_str());
3192 return g_strdup(mutf7_str);
3196 norm_utf7 = g_string_new(NULL);
3198 for (p = mutf7_str; *p != '\0'; p++) {
3199 /* replace: '&' -> '+',
3201 escaped ',' -> '/' */
3202 if (!in_escape && *p == '&') {
3203 if (*(p + 1) != '-') {
3204 g_string_append_c(norm_utf7, '+');
3207 g_string_append_c(norm_utf7, '&');
3210 } else if (in_escape && *p == ',') {
3211 g_string_append_c(norm_utf7, '/');
3212 } else if (in_escape && *p == '-') {
3213 g_string_append_c(norm_utf7, '-');
3216 g_string_append_c(norm_utf7, *p);
3220 norm_utf7_p = norm_utf7->str;
3221 norm_utf7_len = norm_utf7->len;
3222 to_len = strlen(mutf7_str) * 5;
3223 to_p = to_str = g_malloc(to_len + 1);
3225 if (iconv(cd, (ICONV_CONST gchar **)&norm_utf7_p, &norm_utf7_len,
3226 &to_p, &to_len) == -1) {
3227 g_warning(_("iconv cannot convert UTF-7 to %s\n"),
3228 conv_get_current_charset_str());
3229 g_string_free(norm_utf7, TRUE);
3231 return g_strdup(mutf7_str);
3234 /* second iconv() call for flushing */
3235 iconv(cd, NULL, NULL, &to_p, &to_len);
3236 g_string_free(norm_utf7, TRUE);
3240 #endif /* !HAVE_ICONV */
3243 static gchar *imap_locale_to_modified_utf7(const gchar *from)
3246 const gchar *from_p;
3249 to = g_malloc(strlen(from) * 2 + 1);
3252 for (from_p = from; *from_p != '\0'; from_p++) {
3253 if (*from_p == '&') {
3263 static iconv_t cd = (iconv_t)-1;
3264 static gboolean iconv_ok = TRUE;
3265 gchar *norm_utf7, *norm_utf7_p;
3266 size_t from_len, norm_utf7_len;
3268 gchar *from_tmp, *to, *p;
3269 gboolean in_escape = FALSE;
3271 if (!iconv_ok) return g_strdup(from);
3273 if (cd == (iconv_t)-1) {
3274 cd = iconv_open("UTF-7", conv_get_current_charset_str());
3275 if (cd == (iconv_t)-1) {
3276 g_warning("iconv cannot convert %s to UTF-7\n",
3277 conv_get_current_charset_str());
3279 return g_strdup(from);
3283 Xstrdup_a(from_tmp, from, return g_strdup(from));
3284 from_len = strlen(from);
3285 norm_utf7_len = from_len * 5;
3286 Xalloca(norm_utf7, norm_utf7_len + 1, return g_strdup(from));
3287 norm_utf7_p = norm_utf7;
3289 #define IS_PRINT(ch) (isprint(ch) && isascii(ch))
3291 while (from_len > 0) {
3292 if (*from_tmp == '+') {
3293 *norm_utf7_p++ = '+';
3294 *norm_utf7_p++ = '-';
3298 } else if (IS_PRINT(*from_tmp)) {
3299 /* printable ascii char */
3300 *norm_utf7_p = *from_tmp;
3306 size_t mb_len = 0, conv_len = 0;
3308 /* unprintable char: convert to UTF-7 */
3310 while (!IS_PRINT(*p) && conv_len < from_len) {
3311 mb_len = mblen(p, MB_LEN_MAX);
3313 g_warning("wrong multibyte sequence\n");
3314 return g_strdup(from);
3320 from_len -= conv_len;
3321 if (iconv(cd, (ICONV_CONST gchar **)&from_tmp,
3323 &norm_utf7_p, &norm_utf7_len) == -1) {
3324 g_warning("iconv cannot convert %s to UTF-7\n",
3325 conv_get_current_charset_str());
3326 return g_strdup(from);
3329 /* second iconv() call for flushing */
3330 iconv(cd, NULL, NULL, &norm_utf7_p, &norm_utf7_len);
3336 *norm_utf7_p = '\0';
3337 to_str = g_string_new(NULL);
3338 for (p = norm_utf7; p < norm_utf7_p; p++) {
3339 /* replace: '&' -> "&-",
3342 BASE64 '/' -> ',' */
3343 if (!in_escape && *p == '&') {
3344 g_string_append(to_str, "&-");
3345 } else if (!in_escape && *p == '+') {
3346 if (*(p + 1) == '-') {
3347 g_string_append_c(to_str, '+');
3350 g_string_append_c(to_str, '&');
3353 } else if (in_escape && *p == '/') {
3354 g_string_append_c(to_str, ',');
3355 } else if (in_escape && *p == '-') {
3356 g_string_append_c(to_str, '-');
3359 g_string_append_c(to_str, *p);
3365 g_string_append_c(to_str, '-');
3369 g_string_free(to_str, FALSE);
3372 #endif /* !HAVE_ICONV */
3375 static gboolean imap_rename_folder_func(GNode *node, gpointer data)
3377 FolderItem *item = node->data;
3378 gchar **paths = data;
3379 const gchar *oldpath = paths[0];
3380 const gchar *newpath = paths[1];
3382 gchar *new_itempath;
3385 oldpathlen = strlen(oldpath);
3386 if (strncmp(oldpath, item->path, oldpathlen) != 0) {
3387 g_warning("path doesn't match: %s, %s\n", oldpath, item->path);
3391 base = item->path + oldpathlen;
3392 while (*base == G_DIR_SEPARATOR) base++;
3394 new_itempath = g_strdup(newpath);
3396 new_itempath = g_strconcat(newpath, G_DIR_SEPARATOR_S, base,
3399 item->path = new_itempath;
3404 static gint get_list_of_uids(Folder *folder, IMAPFolderItem *item, GSList **msgnum_list)
3406 gint ok, nummsgs = 0, lastuid_old;
3407 IMAPSession *session;
3408 GSList *uidlist, *elem;
3411 session = imap_session_get(folder);
3412 g_return_val_if_fail(session != NULL, -1);
3414 ok = imap_select(session, IMAP_FOLDER(folder), item->item.path,
3415 NULL, NULL, NULL, NULL);
3416 if (ok != IMAP_SUCCESS)
3419 cmd_buf = g_strdup_printf("UID %d:*", item->lastuid + 1);
3420 ok = imap_cmd_search(session, cmd_buf, &uidlist);
3423 if (ok == IMAP_SOCKET) {
3424 session_destroy((Session *)session);
3425 ((RemoteFolder *)folder)->session = NULL;
3429 if (ok != IMAP_SUCCESS) {
3433 argbuf = g_ptr_array_new();
3435 cmd_buf = g_strdup_printf("UID FETCH %d:* (UID)", item->lastuid + 1);
3436 imap_gen_send(session, cmd_buf);
3438 ok = imap_cmd_ok(session, argbuf);
3439 if (ok != IMAP_SUCCESS) {
3440 ptr_array_free_strings(argbuf);
3441 g_ptr_array_free(argbuf, TRUE);
3445 for(i = 0; i < argbuf->len; i++) {
3448 if((ret = sscanf(g_ptr_array_index(argbuf, i),
3449 "%*d FETCH (UID %d)", &msgnum)) == 1)
3450 uidlist = g_slist_prepend(uidlist, GINT_TO_POINTER(msgnum));
3452 ptr_array_free_strings(argbuf);
3453 g_ptr_array_free(argbuf, TRUE);
3456 lastuid_old = item->lastuid;
3457 *msgnum_list = g_slist_copy(item->uid_list);
3458 nummsgs = g_slist_length(*msgnum_list);
3459 debug_print("Got %d uids from cache\n", g_slist_length(item->uid_list));
3461 for (elem = uidlist; elem != NULL; elem = g_slist_next(elem)) {
3464 msgnum = GPOINTER_TO_INT(elem->data);
3465 if (msgnum > lastuid_old) {
3466 *msgnum_list = g_slist_prepend(*msgnum_list, GINT_TO_POINTER(msgnum));
3467 item->uid_list = g_slist_prepend(item->uid_list, GINT_TO_POINTER(msgnum));
3470 if(msgnum > item->lastuid)
3471 item->lastuid = msgnum;
3474 g_slist_free(uidlist);
3479 gint imap_get_num_list(Folder *folder, FolderItem *_item, GSList **msgnum_list)
3481 IMAPFolderItem *item = (IMAPFolderItem *)_item;
3482 IMAPSession *session;
3483 gint ok, nummsgs = 0, exists, recent, unseen, uid_val, uid_next;
3486 gboolean selected_folder;
3488 g_return_val_if_fail(folder != NULL, -1);
3489 g_return_val_if_fail(item != NULL, -1);
3490 g_return_val_if_fail(item->item.path != NULL, -1);
3491 g_return_val_if_fail(FOLDER_CLASS(folder) == &imap_class, -1);
3492 g_return_val_if_fail(folder->account != NULL, -1);
3494 session = imap_session_get(folder);
3495 g_return_val_if_fail(session != NULL, -1);
3497 selected_folder = (session->mbox != NULL) &&
3498 (!strcmp(session->mbox, item->item.path));
3499 if (selected_folder) {
3500 ok = imap_cmd_noop(session);
3501 if (ok != IMAP_SUCCESS)
3503 exists = session->exists;
3505 ok = imap_status(session, IMAP_FOLDER(folder), item->item.path,
3506 &exists, &recent, &uid_next, &uid_val, &unseen);
3507 if (ok != IMAP_SUCCESS)
3511 /* If old uid_next matches new uid_next we can be sure no message
3512 was added to the folder */
3513 if (( selected_folder && !session->folder_content_changed) ||
3514 (!selected_folder && uid_next == item->uid_next)) {
3515 nummsgs = g_slist_length(item->uid_list);
3517 /* If number of messages is still the same we
3518 know our caches message numbers are still valid,
3519 otherwise if the number of messages has decrease
3520 we discard our cache to start a new scan to find
3521 out which numbers have been removed */
3522 if (exists == nummsgs) {
3523 *msgnum_list = g_slist_copy(item->uid_list);
3525 } else if (exists < nummsgs) {
3526 debug_print("Freeing imap uid cache");
3528 g_slist_free(item->uid_list);
3529 item->uid_list = NULL;
3532 if (!selected_folder)