2 * Sylpheed -- a GTK+ based, lightweight, and fast e-mail client
3 * Copyright (C) 1999-2003 Hiroyuki Yamamoto
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation; either version 2 of the License, or
8 * (at your option) any later version.
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
15 * You should have received a copy of the GNU General Public License
16 * along with this program; if not, write to the Free Software
17 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
44 #include "procheader.h"
46 #include "prefs_account.h"
49 #include "inputdialog.h"
52 #define IMAP4_PORT 143
54 #define IMAPS_PORT 993
57 #define QUOTE_IF_REQUIRED(out, str) \
59 if (*str != '"' && strpbrk(str, " \t(){}%*") != NULL) { \
63 len = strlen(str) + 3; \
64 Xalloca(__tmp, len, return IMAP_ERROR); \
65 g_snprintf(__tmp, len, "\"%s\"", str); \
68 Xstrdup_a(out, str, return IMAP_ERROR); \
72 struct _IMAPFolderItem
81 static GList *session_list = NULL;
83 static gint imap_cmd_count = 0;
85 static void imap_folder_init (Folder *folder,
89 static FolderItem *imap_folder_item_new (Folder *folder);
90 static void imap_folder_item_destroy (Folder *folder,
93 static IMAPSession *imap_session_get (Folder *folder);
95 static gint imap_scan_tree_recursive (IMAPSession *session,
97 static GSList *imap_parse_list (Folder *folder,
99 const gchar *real_path,
102 static void imap_create_missing_folders (Folder *folder);
103 static FolderItem *imap_create_special_folder
105 SpecialFolderItemType stype,
108 static gint imap_do_copy (Folder *folder,
111 gboolean remove_source);
113 static gint imap_do_copy_msgs_with_dest (Folder *folder,
116 gboolean remove_source);
119 static GSList *imap_get_uncached_messages (IMAPSession *session,
123 static void imap_delete_all_cached_messages (FolderItem *item);
126 static SockInfo *imap_open (const gchar *server,
130 static SockInfo *imap_open (const gchar *server,
135 static SockInfo *imap_open_tunnel(const gchar *server,
136 const gchar *tunnelcmd,
139 static SockInfo *imap_open_tunnel(const gchar *server,
140 const gchar *tunnelcmd);
144 static SockInfo *imap_init_sock(SockInfo *sock, SSLType ssl_type);
146 static SockInfo *imap_init_sock(SockInfo *sock);
149 static gint imap_set_message_flags (IMAPSession *session,
154 static gint imap_select (IMAPSession *session,
160 guint32 *uid_validity);
161 static gint imap_status (IMAPSession *session,
167 guint32 *uid_validity,
170 static void imap_parse_namespace (IMAPSession *session,
172 static void imap_get_namespace_by_list (IMAPSession *session,
174 static IMAPNameSpace *imap_find_namespace (IMAPFolder *folder,
176 static gchar imap_get_path_separator (IMAPFolder *folder,
178 static gchar *imap_get_real_path (IMAPFolder *folder,
181 static gchar *imap_parse_atom (SockInfo *sock,
186 static MsgFlags imap_parse_flags (const gchar *flag_str);
187 static MsgInfo *imap_parse_envelope (SockInfo *sock,
190 static gint imap_greeting (SockInfo *sock,
191 gboolean *is_preauth);
192 static void imap_get_capability (Session *session);
193 static gboolean imap_has_capability (IMAPSession *session,
196 /* low-level IMAP4rev1 commands */
197 static gint imap_cmd_login (SockInfo *sock,
200 static gint imap_cmd_logout (SockInfo *sock);
201 static gint imap_cmd_noop (SockInfo *sock);
202 static gint imap_cmd_starttls (SockInfo *sock);
203 static gint imap_cmd_namespace (SockInfo *sock,
205 static gint imap_cmd_list (SockInfo *sock,
207 const gchar *mailbox,
209 static gint imap_cmd_do_select (SockInfo *sock,
215 guint32 *uid_validity);
216 static gint imap_cmd_select (SockInfo *sock,
221 guint32 *uid_validity);
222 static gint imap_cmd_examine (SockInfo *sock,
227 guint32 *uid_validity);
228 static gint imap_cmd_create (SockInfo *sock,
229 const gchar *folder);
230 static gint imap_cmd_rename (SockInfo *sock,
231 const gchar *oldfolder,
232 const gchar *newfolder);
233 static gint imap_cmd_delete (SockInfo *sock,
234 const gchar *folder);
235 static gint imap_cmd_envelope (SockInfo *sock,
239 static gint imap_cmd_search (SockInfo *sock,
242 static gint imap_cmd_fetch (SockInfo *sock,
244 const gchar *filename);
245 static gint imap_cmd_append (SockInfo *sock,
246 const gchar *destfolder,
248 static gint imap_cmd_copy (IMAPSession *session,
250 const gchar *destfolder,
252 static gint imap_cmd_store (SockInfo *sock,
256 static gint imap_cmd_expunge (SockInfo *sock);
258 static gint imap_cmd_ok (SockInfo *sock,
260 static void imap_cmd_gen_send (SockInfo *sock,
261 const gchar *format, ...);
262 static gint imap_cmd_gen_recv (SockInfo *sock,
265 /* misc utility functions */
266 static gchar *strchr_cpy (const gchar *src,
270 static gchar *get_quoted (const gchar *src,
274 static gchar *search_array_contain_str (GPtrArray *array,
276 static gchar *search_array_str (GPtrArray *array,
278 static void imap_path_separator_subst (gchar *str,
281 static gchar *imap_modified_utf7_to_locale (const gchar *mutf7_str);
282 static gchar *imap_locale_to_modified_utf7 (const gchar *from);
284 static gboolean imap_rename_folder_func (GNode *node,
286 gint imap_get_num_list (Folder *folder,
289 GSList *imap_get_msginfos (Folder *folder,
291 GSList *msgnum_list);
292 MsgInfo *imap_get_msginfo (Folder *folder,
295 gboolean imap_check_msgnum_validity (Folder *folder,
298 FolderClass imap_class =
304 /* Folder functions */
310 /* FolderItem functions */
311 imap_folder_item_new,
312 imap_folder_item_destroy,
320 imap_check_msgnum_validity,
322 /* Message functions */
338 FolderClass *imap_get_class()
343 Folder *imap_folder_new(const gchar *name, const gchar *path)
347 folder = (Folder *)g_new0(IMAPFolder, 1);
348 folder->class = &imap_class;
349 imap_folder_init(folder, name, path);
354 void imap_folder_destroy(Folder *folder)
358 dir = folder_get_path(folder);
359 if (is_dir_exist(dir))
360 remove_dir_recursive(dir);
363 folder_remote_folder_destroy(REMOTE_FOLDER(folder));
366 static void imap_folder_init(Folder *folder, const gchar *name,
369 folder_remote_folder_init((Folder *)folder, name, path);
372 static FolderItem *imap_folder_item_new(Folder *folder)
374 IMAPFolderItem *item;
376 item = g_new0(IMAPFolderItem, 1);
379 item->uid_list = NULL;
381 return (FolderItem *)item;
384 static void imap_folder_item_destroy(Folder *folder, FolderItem *_item)
386 IMAPFolderItem *item = (IMAPFolderItem *)_item;
388 g_return_if_fail(item != NULL);
389 g_slist_free(item->uid_list);
394 static gboolean imap_reset_uid_lists_func(GNode *node, gpointer data)
396 IMAPFolderItem *item = (IMAPFolderItem *)node->data;
400 g_slist_free(item->uid_list);
401 item->uid_list = NULL;
406 static void imap_reset_uid_lists(Folder *folder)
408 if(folder->node == NULL)
411 /* Destroy all uid lists and rest last uid */
412 g_node_traverse(folder->node, G_IN_ORDER, G_TRAVERSE_ALL, -1, imap_reset_uid_lists_func, NULL);
415 static IMAPSession *imap_session_get(Folder *folder)
417 RemoteFolder *rfolder = REMOTE_FOLDER(folder);
418 Session *session = NULL;
421 g_return_val_if_fail(folder != NULL, NULL);
422 g_return_val_if_fail(FOLDER_CLASS(folder) == &imap_class, NULL);
423 g_return_val_if_fail(folder->account != NULL, NULL);
426 port = folder->account->set_imapport ? folder->account->imapport
427 : folder->account->ssl_imap == SSL_TUNNEL
428 ? IMAPS_PORT : IMAP4_PORT;
430 port = folder->account->set_imapport ? folder->account->imapport
434 /* Make sure we have a session */
435 if (rfolder->session != NULL) {
436 session = rfolder->session;
438 imap_reset_uid_lists(folder);
439 session = imap_session_new(folder->account);
445 imap_get_capability(session);
447 /* Make sure session is authenticated */
448 if (!IMAP_SESSION(session)->authenticated)
449 imap_session_authenticate(IMAP_SESSION(session), folder->account);
450 if (!IMAP_SESSION(session)->authenticated) {
451 session_destroy(session);
452 rfolder->session = NULL;
456 /* Make sure we have parsed the IMAP namespace */
457 imap_parse_namespace(IMAP_SESSION(session),
458 IMAP_FOLDER(folder));
460 /* I think the point of this code is to avoid sending a
461 * keepalive if we've used the session recently and therefore
462 * think it's still alive. Unfortunately, most of the code
463 * does not yet check for errors on the socket, and so if the
464 * connection drops we don't notice until the timeout expires.
465 * A better solution than sending a NOOP every time would be
466 * for every command to be prepared to retry until it is
467 * successfully sent. -- mbp */
468 if (time(NULL) - session->last_access_time > SESSION_TIMEOUT) {
469 /* verify that the session is still alive */
470 if (imap_cmd_noop(session->sock) != IMAP_SUCCESS) {
471 /* Check if this is the first try to establish a
472 connection, if yes we don't try to reconnect */
473 if (rfolder->session == NULL) {
474 log_warning(_("Connecting %s:%d failed"),
475 folder->account->recv_server, port);
476 session_destroy(session);
479 log_warning(_("IMAP4 connection to %s:%d has been"
480 " disconnected. Reconnecting...\n"),
481 folder->account->recv_server, port);
482 session_destroy(session);
483 /* Clear folders session to make imap_session_get create
484 a new session, because of rfolder->session == NULL
485 it will not try to reconnect again and so avoid an
487 rfolder->session = NULL;
488 session = SESSION(imap_session_get(folder));
493 rfolder->session = session;
495 session->last_access_time = time(NULL);
497 return IMAP_SESSION(session);
500 Session *imap_session_new(const PrefsAccount *account)
502 IMAPSession *session;
508 /* FIXME: IMAP over SSL only... */
511 port = account->set_imapport ? account->imapport
512 : account->ssl_imap == SSL_TUNNEL ? IMAPS_PORT : IMAP4_PORT;
513 ssl_type = account->ssl_imap;
515 port = account->set_imapport ? account->imapport
519 if (account->set_tunnelcmd) {
520 log_message(_("creating tunneled IMAP4 connection\n"));
522 if ((imap_sock = imap_open_tunnel(account->recv_server,
526 if ((imap_sock = imap_open_tunnel(account->recv_server,
527 account->tunnelcmd)) == NULL)
531 g_return_val_if_fail(account->recv_server != NULL, NULL);
533 log_message(_("creating IMAP4 connection to %s:%d ...\n"),
534 account->recv_server, port);
537 if ((imap_sock = imap_open(account->recv_server, port,
540 if ((imap_sock = imap_open(account->recv_server, port)) == NULL)
545 /* Only need to log in if the connection was not PREAUTH */
546 if (imap_greeting(imap_sock, &is_preauth) != IMAP_SUCCESS) {
547 sock_close(imap_sock);
550 log_message("IMAP connection is %s-authenticated\n",
551 (is_preauth) ? "pre" : "un");
553 session = g_new(IMAPSession, 1);
554 SESSION(session)->type = SESSION_IMAP;
555 SESSION(session)->server = g_strdup(account->recv_server);
556 SESSION(session)->sock = imap_sock;
557 SESSION(session)->connected = TRUE;
558 SESSION(session)->phase = SESSION_READY;
559 SESSION(session)->last_access_time = time(NULL);
560 SESSION(session)->data = NULL;
562 SESSION(session)->destroy = imap_session_destroy;
564 session->capability = NULL;
566 session->mbox = NULL;
567 session->authenticated = is_preauth;
569 session_list = g_list_append(session_list, session);
571 return SESSION(session);
574 void imap_session_authenticate(IMAPSession *session, const PrefsAccount *account)
578 g_return_if_fail(account->userid != NULL);
580 pass = account->passwd;
583 tmp_pass = input_dialog_query_password(account->recv_server, account->userid);
586 Xstrdup_a(pass, tmp_pass, {g_free(tmp_pass); return;});
590 if (imap_cmd_login(SESSION(session)->sock, account->userid, pass) != IMAP_SUCCESS) {
591 imap_cmd_logout(SESSION(session)->sock);
595 session->authenticated = TRUE;
598 void imap_session_destroy(Session *session)
600 sock_close(session->sock);
601 session->sock = NULL;
603 g_free(IMAP_SESSION(session)->mbox);
605 g_strfreev(IMAP_SESSION(session)->capability);
607 session_list = g_list_remove(session_list, session);
610 void imap_session_destroy_all(void)
612 while (session_list != NULL) {
613 IMAPSession *session = (IMAPSession *)session_list->data;
615 imap_cmd_logout(SESSION(session)->sock);
616 session_destroy(SESSION(session));
620 gchar *imap_fetch_msg(Folder *folder, FolderItem *item, gint uid)
622 gchar *path, *filename;
623 IMAPSession *session;
626 g_return_val_if_fail(folder != NULL, NULL);
627 g_return_val_if_fail(item != NULL, NULL);
629 path = folder_item_get_path(item);
630 if (!is_dir_exist(path))
632 filename = g_strconcat(path, G_DIR_SEPARATOR_S, itos(uid), NULL);
635 if (is_file_exist(filename)) {
636 debug_print("message %d has been already cached.\n", uid);
640 session = imap_session_get(folder);
646 ok = imap_select(session, IMAP_FOLDER(folder), item->path,
647 NULL, NULL, NULL, NULL);
648 if (ok != IMAP_SUCCESS) {
649 g_warning("can't select mailbox %s\n", item->path);
654 debug_print("getting message %d...\n", uid);
655 ok = imap_cmd_fetch(SESSION(session)->sock, (guint32)uid, filename);
657 if (ok != IMAP_SUCCESS) {
658 g_warning("can't fetch message %d\n", uid);
666 gint imap_add_msg(Folder *folder, FolderItem *dest, const gchar *file,
667 gboolean remove_source)
670 IMAPSession *session;
673 g_return_val_if_fail(folder != NULL, -1);
674 g_return_val_if_fail(dest != NULL, -1);
675 g_return_val_if_fail(file != NULL, -1);
677 session = imap_session_get(folder);
678 if (!session) return -1;
680 destdir = imap_get_real_path(IMAP_FOLDER(folder), dest->path);
681 ok = imap_cmd_append(SESSION(session)->sock, destdir, file);
684 if (ok != IMAP_SUCCESS) {
685 g_warning("can't append message %s\n", file);
690 if (unlink(file) < 0)
691 FILE_OP_ERROR(file, "unlink");
697 static gint imap_do_copy(Folder *folder, FolderItem *dest, MsgInfo *msginfo,
698 gboolean remove_source)
701 IMAPSession *session;
702 IMAPFlags iflags = 0;
703 gint messages, recent, unseen;
704 guint32 newuid = 0, uid_validity;
707 g_return_val_if_fail(folder != NULL, -1);
708 g_return_val_if_fail(FOLDER_CLASS(folder) == &imap_class, -1);
709 g_return_val_if_fail(dest != NULL, -1);
710 g_return_val_if_fail(msginfo != NULL, -1);
712 session = imap_session_get(folder);
713 if (!session) return -1;
715 if (msginfo->folder == dest) {
716 g_warning("the src folder is identical to the dest.\n");
720 /* if we don't have UIDPLUS we get UID-NEXT and hope that the
721 message really gets this uid */
722 if (!imap_has_capability(session, "UIDPLUS")) {
723 ok = imap_status(session, IMAP_FOLDER(folder), dest->path,
724 &messages, &recent, &newuid, &uid_validity, &unseen);
725 if (ok != IMAP_SUCCESS) {
726 g_warning("can't copy message\n");
731 destdir = imap_get_real_path(IMAP_FOLDER(folder), dest->path);
733 /* ensure source folder selected */
734 ok = imap_select(session, IMAP_FOLDER(folder), msginfo->folder->path,
735 NULL, NULL, NULL, NULL);
736 if (ok != IMAP_SUCCESS)
740 debug_print("Moving message %s%c%d to %s ...\n",
741 msginfo->folder->path, G_DIR_SEPARATOR,
742 msginfo->msgnum, destdir);
744 debug_print("Copying message %s%c%d to %s ...\n",
745 msginfo->folder->path, G_DIR_SEPARATOR,
746 msginfo->msgnum, destdir);
748 ok = imap_cmd_copy(session, msginfo->msgnum, destdir, &newuid);
750 if (ok == IMAP_SUCCESS && remove_source) {
751 imap_set_message_flags(session, msginfo->msgnum, msginfo->msgnum,
752 IMAP_FLAG_DELETED, TRUE);
753 ok = imap_cmd_expunge(SESSION(session)->sock);
756 /* get the dest folder to set the flags */
758 ok = imap_select(session, IMAP_FOLDER(folder), dest->path,
759 NULL, NULL, NULL, NULL);
760 if (ok != IMAP_SUCCESS) /* the folder disappeared? */
763 if (msginfo->flags.perm_flags & MSG_MARKED) iflags |= IMAP_FLAG_FLAGGED;
764 if (msginfo->flags.perm_flags & MSG_REPLIED) iflags |= IMAP_FLAG_ANSWERED;
766 if (imap_set_message_flags(session, newuid, newuid, iflags, TRUE)
773 if (ok == IMAP_SUCCESS)
780 static gint imap_do_copy_msgs_with_dest(Folder *folder, FolderItem *dest,
782 gboolean remove_source)
787 IMAPSession *session;
788 gint ok = IMAP_SUCCESS;
790 g_return_val_if_fail(folder != NULL, -1);
791 g_return_val_if_fail(dest != NULL, -1);
792 g_return_val_if_fail(msglist != NULL, -1);
794 session = imap_session_get(folder);
795 if (!session) return -1;
797 destdir = imap_get_real_path(IMAP_FOLDER(folder), dest->path);
799 for (cur = msglist; cur != NULL; cur = cur->next) {
800 msginfo = (MsgInfo *)cur->data;
802 if (msginfo->folder == dest) {
803 g_warning("the src folder is identical to the dest.\n");
807 /* ensure source folder selected */
808 ok = imap_select(session, IMAP_FOLDER(folder),
809 msginfo->folder->path, NULL, NULL, NULL, NULL);
812 debug_print("Moving message %s%c%d to %s ...\n",
813 msginfo->folder->path, G_DIR_SEPARATOR,
814 msginfo->msgnum, destdir);
816 debug_print("Copying message %s%c%d to %s ...\n",
817 msginfo->folder->path, G_DIR_SEPARATOR,
818 msginfo->msgnum, destdir);
820 ok = imap_cmd_copy(session, msginfo, destdir);
822 if (ok == IMAP_SUCCESS && remove_source) {
823 imap_set_message_flags
824 (session, msginfo->msgnum, msginfo->msgnum,
825 IMAP_FLAG_DELETED, TRUE);
830 ok = imap_cmd_expunge(SESSION(session)->sock);
834 if (ok == IMAP_SUCCESS)
841 gint imap_move_msg(Folder *folder, FolderItem *dest, MsgInfo *msginfo)
846 g_return_val_if_fail(folder != NULL, -1);
847 g_return_val_if_fail(dest != NULL, -1);
848 g_return_val_if_fail(msginfo != NULL, -1);
849 g_return_val_if_fail(msginfo->folder != NULL, -1);
851 if (folder == msginfo->folder->folder)
852 return imap_do_copy(folder, dest, msginfo, TRUE);
854 srcfile = procmsg_get_message_file(msginfo);
855 if (!srcfile) return -1;
857 ret = imap_add_msg(folder, dest, srcfile, FALSE);
861 if(folder_item_remove_msg(msginfo->folder, msginfo->msgnum)) {
870 gint imap_move_msgs_with_dest(Folder *folder, FolderItem *dest,
877 g_return_val_if_fail(folder != NULL, -1);
878 g_return_val_if_fail(dest != NULL, -1);
879 g_return_val_if_fail(msglist != NULL, -1);
881 msginfo = (MsgInfo *)msglist->data;
882 if (folder == msginfo->folder->folder)
883 return imap_do_copy_msgs_with_dest(folder, dest, msglist, TRUE);
885 for (cur = msglist; cur != NULL; cur = cur->next) {
886 msginfo = (MsgInfo *)cur->data;
887 ret = imap_move_msg(folder, dest, msginfo);
888 if (ret == -1) break;
895 gint imap_copy_msg(Folder *folder, FolderItem *dest, MsgInfo *msginfo)
900 g_return_val_if_fail(folder != NULL, -1);
901 g_return_val_if_fail(dest != NULL, -1);
902 g_return_val_if_fail(msginfo != NULL, -1);
903 g_return_val_if_fail(msginfo->folder != NULL, -1);
905 if (folder == msginfo->folder->folder)
906 return imap_do_copy(folder, dest, msginfo, FALSE);
908 srcfile = procmsg_get_message_file(msginfo);
909 if (!srcfile) return -1;
911 ret = imap_add_msg(folder, dest, srcfile, FALSE);
919 gint imap_copy_msgs_with_dest(Folder *folder, FolderItem *dest,
926 g_return_val_if_fail(folder != NULL, -1);
927 g_return_val_if_fail(dest != NULL, -1);
928 g_return_val_if_fail(msglist != NULL, -1);
930 msginfo = (MsgInfo *)msglist->data;
931 if (folder == msginfo->folder->folder)
932 return imap_do_copy_msgs_with_dest
933 (folder, dest, msglist, FALSE);
935 for (cur = msglist; cur != NULL; cur = cur->next) {
936 msginfo = (MsgInfo *)cur->data;
937 ret = imap_copy_msg(folder, dest, msginfo);
938 if (ret == -1) break;
945 gint imap_remove_msg(Folder *folder, FolderItem *item, gint uid)
948 IMAPSession *session;
951 g_return_val_if_fail(folder != NULL, -1);
952 g_return_val_if_fail(FOLDER_CLASS(folder) == &imap_class, -1);
953 g_return_val_if_fail(item != NULL, -1);
955 session = imap_session_get(folder);
956 if (!session) return -1;
958 ok = imap_select(session, IMAP_FOLDER(folder), item->path,
959 NULL, NULL, NULL, NULL);
960 if (ok != IMAP_SUCCESS)
963 ok = imap_set_message_flags
964 (IMAP_SESSION(REMOTE_FOLDER(folder)->session),
965 (guint32)uid, (guint32)uid, IMAP_FLAG_DELETED, TRUE);
966 if (ok != IMAP_SUCCESS) {
967 log_warning(_("can't set deleted flags: %d\n"), uid);
971 ok = imap_cmd_expunge(SESSION(session)->sock);
972 if (ok != IMAP_SUCCESS) {
973 log_warning(_("can't expunge\n"));
977 dir = folder_item_get_path(item);
978 if (is_dir_exist(dir))
979 remove_numbered_files(dir, uid, uid);
985 gint imap_remove_msgs(Folder *folder, FolderItem *item, GSList *msglist)
988 IMAPSession *session;
994 g_return_val_if_fail(folder != NULL, -1);
995 g_return_val_if_fail(FOLDER_CLASS(folder) == &imap_class, -1);
996 g_return_val_if_fail(item != NULL, -1);
997 g_return_val_if_fail(msglist != NULL, -1);
999 session = imap_session_get(folder);
1000 if (!session) return -1;
1002 ok = imap_select(session, IMAP_FOLDER(folder), item->path,
1003 NULL, NULL, NULL, NULL);
1004 if (ok != IMAP_SUCCESS)
1007 for (cur = msglist; cur != NULL; cur = cur->next) {
1008 msginfo = (MsgInfo *)cur->data;
1009 uid = msginfo->msgnum;
1010 ok = imap_set_message_flags
1011 (IMAP_SESSION(REMOTE_FOLDER(folder)->session),
1012 uid, uid, IMAP_FLAG_DELETED, TRUE);
1013 if (ok != IMAP_SUCCESS) {
1014 log_warning(_("can't set deleted flags: %d\n"), uid);
1019 ok = imap_cmd_expunge(SESSION(session)->sock);
1020 if (ok != IMAP_SUCCESS) {
1021 log_warning(_("can't expunge\n"));
1025 dir = folder_item_get_path(item);
1026 if (is_dir_exist(dir)) {
1027 for (cur = msglist; cur != NULL; cur = cur->next) {
1028 msginfo = (MsgInfo *)cur->data;
1029 uid = msginfo->msgnum;
1030 remove_numbered_files(dir, uid, uid);
1035 return IMAP_SUCCESS;
1038 gint imap_remove_all_msg(Folder *folder, FolderItem *item)
1040 gint exists, recent, unseen;
1041 guint32 uid_validity;
1043 IMAPSession *session;
1046 g_return_val_if_fail(folder != NULL, -1);
1047 g_return_val_if_fail(item != NULL, -1);
1049 session = imap_session_get(folder);
1050 if (!session) return -1;
1052 ok = imap_select(session, IMAP_FOLDER(folder), item->path,
1053 &exists, &recent, &unseen, &uid_validity);
1054 if (ok != IMAP_SUCCESS)
1057 return IMAP_SUCCESS;
1059 imap_cmd_gen_send(SESSION(session)->sock,
1060 "STORE 1:%d +FLAGS (\\Deleted)", exists);
1061 ok = imap_cmd_ok(SESSION(session)->sock, NULL);
1062 if (ok != IMAP_SUCCESS) {
1063 log_warning(_("can't set deleted flags: 1:%d\n"), exists);
1067 ok = imap_cmd_expunge(SESSION(session)->sock);
1068 if (ok != IMAP_SUCCESS) {
1069 log_warning(_("can't expunge\n"));
1073 dir = folder_item_get_path(item);
1074 if (is_dir_exist(dir))
1075 remove_all_numbered_files(dir);
1078 return IMAP_SUCCESS;
1081 gboolean imap_is_msg_changed(Folder *folder, FolderItem *item, MsgInfo *msginfo)
1083 /* TODO: properly implement this method */
1087 gint imap_scan_folder(Folder *folder, FolderItem *item)
1089 IMAPSession *session;
1090 gint messages, recent, unseen;
1091 guint32 uid_next, uid_validity;
1094 g_return_val_if_fail(folder != NULL, -1);
1095 g_return_val_if_fail(item != NULL, -1);
1097 session = imap_session_get(folder);
1098 if (!session) return -1;
1100 ok = imap_status(session, IMAP_FOLDER(folder), item->path,
1101 &messages, &recent, &uid_next, &uid_validity, &unseen);
1102 if (ok != IMAP_SUCCESS) return -1;
1104 item->new = unseen > 0 ? recent : 0;
1105 item->unread = unseen;
1106 item->total = messages;
1107 item->last_num = (messages > 0 && uid_next > 0) ? uid_next - 1 : 0;
1108 /* item->mtime = uid_validity; */
1113 void imap_scan_tree(Folder *folder)
1116 IMAPSession *session;
1117 gchar *root_folder = NULL;
1119 g_return_if_fail(folder != NULL);
1120 g_return_if_fail(folder->account != NULL);
1122 session = imap_session_get(folder);
1124 if (!folder->node) {
1125 folder_tree_destroy(folder);
1126 item = folder_item_new(folder, folder->name, NULL);
1127 item->folder = folder;
1128 folder->node = g_node_new(item);
1133 if (folder->account->imap_dir && *folder->account->imap_dir) {
1134 Xstrdup_a(root_folder, folder->account->imap_dir, return);
1135 strtailchomp(root_folder, '/');
1136 debug_print("IMAP root directory: %s\n", root_folder);
1139 item = folder_item_new(folder, folder->name, root_folder);
1140 item->folder = folder;
1141 item->no_select = TRUE;
1142 folder->node = g_node_new(item);
1144 imap_scan_tree_recursive(session, item);
1146 imap_create_missing_folders(folder);
1149 static gint imap_scan_tree_recursive(IMAPSession *session, FolderItem *item)
1152 IMAPFolder *imapfolder;
1153 FolderItem *new_item;
1154 GSList *item_list, *cur;
1156 gchar *wildcard_path;
1160 g_return_val_if_fail(item != NULL, -1);
1161 g_return_val_if_fail(item->folder != NULL, -1);
1162 g_return_val_if_fail(item->no_sub == FALSE, -1);
1164 folder = FOLDER(item->folder);
1165 imapfolder = IMAP_FOLDER(folder);
1167 separator = imap_get_path_separator(imapfolder, item->path);
1169 if (item->folder->ui_func)
1170 item->folder->ui_func(folder, item, folder->ui_func_data);
1173 wildcard[0] = separator;
1176 real_path = imap_get_real_path(imapfolder, item->path);
1180 real_path = g_strdup("");
1183 Xstrcat_a(wildcard_path, real_path, wildcard,
1184 {g_free(real_path); return IMAP_ERROR;});
1185 QUOTE_IF_REQUIRED(wildcard_path, wildcard_path);
1187 imap_cmd_gen_send(SESSION(session)->sock, "LIST \"\" %s",
1190 strtailchomp(real_path, separator);
1191 item_list = imap_parse_list(folder, session, real_path, NULL);
1194 for (cur = item_list; cur != NULL; cur = cur->next) {
1195 new_item = cur->data;
1196 if (!strcmp(new_item->path, "INBOX")) {
1197 if (!folder->inbox) {
1198 new_item->stype = F_INBOX;
1199 item->folder->inbox = new_item;
1201 folder_item_destroy(new_item);
1204 } else if (!item->parent || item->stype == F_INBOX) {
1207 base = g_basename(new_item->path);
1209 if (!folder->outbox && !strcasecmp(base, "Sent")) {
1210 new_item->stype = F_OUTBOX;
1211 folder->outbox = new_item;
1212 } else if (!folder->draft && !strcasecmp(base, "Drafts")) {
1213 new_item->stype = F_DRAFT;
1214 folder->draft = new_item;
1215 } else if (!folder->queue && !strcasecmp(base, "Queue")) {
1216 new_item->stype = F_QUEUE;
1217 folder->queue = new_item;
1218 } else if (!folder->trash && !strcasecmp(base, "Trash")) {
1219 new_item->stype = F_TRASH;
1220 folder->trash = new_item;
1223 folder_item_append(item, new_item);
1224 if (new_item->no_select == FALSE)
1225 imap_scan_folder(folder, new_item);
1226 if (new_item->no_sub == FALSE)
1227 imap_scan_tree_recursive(session, new_item);
1230 return IMAP_SUCCESS;
1233 static GSList *imap_parse_list(Folder *folder, IMAPSession *session,
1234 const gchar *real_path, gchar *separator)
1236 gchar buf[IMAPBUFSIZE];
1238 gchar separator_str[16];
1241 gchar *loc_name, *loc_path;
1242 GSList *item_list = NULL;
1244 FolderItem *new_item;
1246 debug_print("getting list of %s ...\n",
1247 *real_path ? real_path : "\"\"");
1249 str = g_string_new(NULL);
1252 if (sock_gets(SESSION(session)->sock, buf, sizeof(buf)) <= 0) {
1253 log_warning(_("error occured while getting LIST.\n"));
1257 if (buf[0] != '*' || buf[1] != ' ') {
1258 log_print("IMAP4< %s\n", buf);
1261 debug_print("IMAP4< %s\n", buf);
1263 g_string_assign(str, buf);
1265 if (strncmp(p, "LIST ", 5) != 0) continue;
1268 if (*p != '(') continue;
1270 p = strchr_cpy(p, ')', flags, sizeof(flags));
1272 while (*p == ' ') p++;
1274 p = strchr_cpy(p, ' ', separator_str, sizeof(separator_str));
1276 extract_quote(separator_str, '"');
1277 if (!strcmp(separator_str, "NIL"))
1278 separator_str[0] = '\0';
1280 *separator = separator_str[0];
1283 while (*p == ' ') p++;
1284 if (*p == '{' || *p == '"')
1285 p = imap_parse_atom(SESSION(session)->sock, p,
1286 buf, sizeof(buf), str);
1288 strncpy2(buf, p, sizeof(buf));
1289 strtailchomp(buf, separator_str[0]);
1290 if (buf[0] == '\0') continue;
1291 if (!strcmp(buf, real_path)) continue;
1293 if (separator_str[0] != '\0')
1294 subst_char(buf, separator_str[0], '/');
1295 name = g_basename(buf);
1296 if (name[0] == '.') continue;
1298 loc_name = imap_modified_utf7_to_locale(name);
1299 loc_path = imap_modified_utf7_to_locale(buf);
1300 new_item = folder_item_new(folder, loc_name, loc_path);
1301 if (strcasestr(flags, "\\Noinferiors") != NULL)
1302 new_item->no_sub = TRUE;
1303 if (strcmp(buf, "INBOX") != 0 &&
1304 strcasestr(flags, "\\Noselect") != NULL)
1305 new_item->no_select = TRUE;
1307 item_list = g_slist_append(item_list, new_item);
1309 debug_print("folder %s has been added.\n", loc_path);
1314 g_string_free(str, TRUE);
1319 gint imap_create_tree(Folder *folder)
1321 g_return_val_if_fail(folder != NULL, -1);
1322 g_return_val_if_fail(folder->node != NULL, -1);
1323 g_return_val_if_fail(folder->node->data != NULL, -1);
1324 g_return_val_if_fail(folder->account != NULL, -1);
1326 imap_scan_tree(folder);
1327 imap_create_missing_folders(folder);
1332 static void imap_create_missing_folders(Folder *folder)
1334 g_return_if_fail(folder != NULL);
1337 folder->inbox = imap_create_special_folder
1338 (folder, F_INBOX, "INBOX");
1340 if (!folder->outbox)
1341 folder->outbox = imap_create_special_folder
1342 (folder, F_OUTBOX, "Sent");
1344 folder->draft = imap_create_special_folder
1345 (folder, F_DRAFT, "Drafts");
1347 folder->queue = imap_create_special_folder
1348 (folder, F_QUEUE, "Queue");
1351 folder->trash = imap_create_special_folder
1352 (folder, F_TRASH, "Trash");
1355 static FolderItem *imap_create_special_folder(Folder *folder,
1356 SpecialFolderItemType stype,
1360 FolderItem *new_item;
1362 g_return_val_if_fail(folder != NULL, NULL);
1363 g_return_val_if_fail(folder->node != NULL, NULL);
1364 g_return_val_if_fail(folder->node->data != NULL, NULL);
1365 g_return_val_if_fail(folder->account != NULL, NULL);
1366 g_return_val_if_fail(name != NULL, NULL);
1368 item = FOLDER_ITEM(folder->node->data);
1369 new_item = imap_create_folder(folder, item, name);
1372 g_warning("Can't create '%s'\n", name);
1373 if (!folder->inbox) return NULL;
1375 new_item = imap_create_folder(folder, folder->inbox, name);
1377 g_warning("Can't create '%s' under INBOX\n", name);
1379 new_item->stype = stype;
1381 new_item->stype = stype;
1386 FolderItem *imap_create_folder(Folder *folder, FolderItem *parent,
1389 gchar *dirpath, *imap_path;
1390 IMAPSession *session;
1391 FolderItem *new_item;
1397 g_return_val_if_fail(folder != NULL, NULL);
1398 g_return_val_if_fail(folder->account != NULL, NULL);
1399 g_return_val_if_fail(parent != NULL, NULL);
1400 g_return_val_if_fail(name != NULL, NULL);
1402 session = imap_session_get(folder);
1403 if (!session) return NULL;
1405 if (!parent->parent && strcmp(name, "INBOX") == 0)
1406 dirpath = g_strdup(name);
1407 else if (parent->path)
1408 dirpath = g_strconcat(parent->path, "/", name, NULL);
1409 else if ((p = strchr(name, '/')) != NULL && *(p + 1) != '\0')
1410 dirpath = g_strdup(name);
1411 else if (folder->account->imap_dir && *folder->account->imap_dir) {
1414 Xstrdup_a(imap_dir, folder->account->imap_dir, return NULL);
1415 strtailchomp(imap_dir, '/');
1416 dirpath = g_strconcat(imap_dir, "/", name, NULL);
1418 dirpath = g_strdup(name);
1420 /* keep trailing directory separator to create a folder that contains
1422 imap_path = imap_locale_to_modified_utf7(dirpath);
1423 strtailchomp(dirpath, '/');
1424 Xstrdup_a(new_name, name, {g_free(dirpath); return NULL;});
1425 strtailchomp(new_name, '/');
1426 separator = imap_get_path_separator(IMAP_FOLDER(folder), imap_path);
1427 imap_path_separator_subst(imap_path, separator);
1428 subst_char(new_name, '/', separator);
1430 if (strcmp(name, "INBOX") != 0) {
1433 gboolean exist = FALSE;
1435 argbuf = g_ptr_array_new();
1436 ok = imap_cmd_list(SESSION(session)->sock, NULL, imap_path,
1438 if (ok != IMAP_SUCCESS) {
1439 log_warning(_("can't create mailbox: LIST failed\n"));
1442 ptr_array_free_strings(argbuf);
1443 g_ptr_array_free(argbuf, TRUE);
1447 for (i = 0; i < argbuf->len; i++) {
1449 str = g_ptr_array_index(argbuf, i);
1450 if (!strncmp(str, "LIST ", 5)) {
1455 ptr_array_free_strings(argbuf);
1456 g_ptr_array_free(argbuf, TRUE);
1459 ok = imap_cmd_create(SESSION(session)->sock, imap_path);
1460 if (ok != IMAP_SUCCESS) {
1461 log_warning(_("can't create mailbox\n"));
1469 new_item = folder_item_new(folder, new_name, dirpath);
1470 folder_item_append(parent, new_item);
1474 dirpath = folder_item_get_path(new_item);
1475 if (!is_dir_exist(dirpath))
1476 make_dir_hier(dirpath);
1482 gint imap_rename_folder(Folder *folder, FolderItem *item, const gchar *name)
1486 gchar *real_oldpath;
1487 gchar *real_newpath;
1490 gchar *old_cache_dir;
1491 gchar *new_cache_dir;
1492 IMAPSession *session;
1495 gint exists, recent, unseen;
1496 guint32 uid_validity;
1498 g_return_val_if_fail(folder != NULL, -1);
1499 g_return_val_if_fail(item != NULL, -1);
1500 g_return_val_if_fail(item->path != NULL, -1);
1501 g_return_val_if_fail(name != NULL, -1);
1503 session = imap_session_get(folder);
1504 if (!session) return -1;
1506 real_oldpath = imap_get_real_path(IMAP_FOLDER(folder), item->path);
1508 g_free(session->mbox);
1509 session->mbox = NULL;
1510 ok = imap_cmd_examine(SESSION(session)->sock, "INBOX",
1511 &exists, &recent, &unseen, &uid_validity);
1512 if (ok != IMAP_SUCCESS) {
1513 g_free(real_oldpath);
1517 separator = imap_get_path_separator(IMAP_FOLDER(folder), item->path);
1518 if (strchr(item->path, G_DIR_SEPARATOR)) {
1519 dirpath = g_dirname(item->path);
1520 newpath = g_strconcat(dirpath, G_DIR_SEPARATOR_S, name, NULL);
1523 newpath = g_strdup(name);
1525 real_newpath = imap_locale_to_modified_utf7(newpath);
1526 imap_path_separator_subst(real_newpath, separator);
1528 ok = imap_cmd_rename(SESSION(session)->sock, real_oldpath, real_newpath);
1529 if (ok != IMAP_SUCCESS) {
1530 log_warning(_("can't rename mailbox: %s to %s\n"),
1531 real_oldpath, real_newpath);
1532 g_free(real_oldpath);
1534 g_free(real_newpath);
1539 item->name = g_strdup(name);
1541 old_cache_dir = folder_item_get_path(item);
1543 node = g_node_find(item->folder->node, G_PRE_ORDER, G_TRAVERSE_ALL,
1545 paths[0] = g_strdup(item->path);
1547 g_node_traverse(node, G_PRE_ORDER, G_TRAVERSE_ALL, -1,
1548 imap_rename_folder_func, paths);
1550 if (is_dir_exist(old_cache_dir)) {
1551 new_cache_dir = folder_item_get_path(item);
1552 if (rename(old_cache_dir, new_cache_dir) < 0) {
1553 FILE_OP_ERROR(old_cache_dir, "rename");
1555 g_free(new_cache_dir);
1558 g_free(old_cache_dir);
1561 g_free(real_oldpath);
1562 g_free(real_newpath);
1567 gint imap_remove_folder(Folder *folder, FolderItem *item)
1570 IMAPSession *session;
1573 gint exists, recent, unseen;
1574 guint32 uid_validity;
1576 g_return_val_if_fail(folder != NULL, -1);
1577 g_return_val_if_fail(item != NULL, -1);
1578 g_return_val_if_fail(item->path != NULL, -1);
1580 session = imap_session_get(folder);
1581 if (!session) return -1;
1583 path = imap_get_real_path(IMAP_FOLDER(folder), item->path);
1585 ok = imap_cmd_examine(SESSION(session)->sock, "INBOX",
1586 &exists, &recent, &unseen, &uid_validity);
1587 if (ok != IMAP_SUCCESS) {
1592 ok = imap_cmd_delete(SESSION(session)->sock, path);
1593 if (ok != IMAP_SUCCESS) {
1594 log_warning(_("can't delete mailbox\n"));
1600 cache_dir = folder_item_get_path(item);
1601 if (is_dir_exist(cache_dir) && remove_dir_recursive(cache_dir) < 0)
1602 g_warning("can't remove directory '%s'\n", cache_dir);
1604 folder_item_remove(item);
1609 static GSList *imap_get_uncached_messages(IMAPSession *session,
1611 guint32 first_uid, guint32 last_uid)
1614 GSList *newlist = NULL;
1615 GSList *llast = NULL;
1619 g_return_val_if_fail(session != NULL, NULL);
1620 g_return_val_if_fail(item != NULL, NULL);
1621 g_return_val_if_fail(item->folder != NULL, NULL);
1622 g_return_val_if_fail(FOLDER_CLASS(item->folder) == &imap_class, NULL);
1623 g_return_val_if_fail(first_uid <= last_uid, NULL);
1625 if (imap_cmd_envelope(SESSION(session)->sock, first_uid, last_uid)
1627 log_warning(_("can't get envelope\n"));
1631 str = g_string_new(NULL);
1634 if ((tmp = sock_getline(SESSION(session)->sock)) == NULL) {
1635 log_warning(_("error occurred while getting envelope.\n"));
1636 g_string_free(str, TRUE);
1640 if (tmp[0] != '*' || tmp[1] != ' ') {
1641 log_print("IMAP4< %s\n", tmp);
1645 if (strstr(tmp, "FETCH") == NULL) {
1646 log_print("IMAP4< %s\n", tmp);
1650 log_print("IMAP4< %s\n", tmp);
1651 g_string_assign(str, tmp);
1654 msginfo = imap_parse_envelope
1655 (SESSION(session)->sock, item, str);
1657 log_warning(_("can't parse envelope: %s\n"), str->str);
1660 if (item->stype == F_QUEUE) {
1661 MSG_SET_TMP_FLAGS(msginfo->flags, MSG_QUEUED);
1662 } else if (item->stype == F_DRAFT) {
1663 MSG_SET_TMP_FLAGS(msginfo->flags, MSG_DRAFT);
1666 msginfo->folder = item;
1669 llast = newlist = g_slist_append(newlist, msginfo);
1671 llast = g_slist_append(llast, msginfo);
1672 llast = llast->next;
1676 g_string_free(str, TRUE);
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"),
1715 return imap_init_sock(sock, ssl_type);
1717 return imap_init_sock(sock);
1723 static SockInfo *imap_open(const gchar *server, gushort port,
1726 static SockInfo *imap_open(const gchar *server, gushort port)
1731 if ((sock = sock_connect(server, port)) == NULL) {
1732 log_warning(_("Can't connect to IMAP4 server: %s:%d\n"),
1738 if (ssl_type == SSL_TUNNEL && !ssl_init_socket(sock)) {
1739 log_warning(_("Can't establish IMAP4 session with: %s:%d\n"),
1744 return imap_init_sock(sock, ssl_type);
1746 return imap_init_sock(sock);
1751 static SockInfo *imap_init_sock(SockInfo *sock, SSLType ssl_type)
1753 static SockInfo *imap_init_sock(SockInfo *sock)
1758 if (ssl_type == SSL_STARTTLS) {
1761 ok = imap_cmd_starttls(sock);
1762 if (ok != IMAP_SUCCESS) {
1763 log_warning(_("Can't start TLS session.\n"));
1767 if (!ssl_init_socket_with_method(sock, SSL_METHOD_TLSv1)) {
1774 if (imap_cmd_noop(sock) != IMAP_SUCCESS) {
1775 log_warning(_("Can't establish IMAP4 session.\n"));
1783 static GList *imap_parse_namespace_str(gchar *str)
1788 IMAPNameSpace *namespace;
1789 GList *ns_list = NULL;
1791 while (*p != '\0') {
1792 /* parse ("#foo" "/") */
1794 while (*p && *p != '(') p++;
1795 if (*p == '\0') break;
1798 while (*p && *p != '"') p++;
1799 if (*p == '\0') break;
1803 while (*p && *p != '"') p++;
1804 if (*p == '\0') break;
1808 while (*p && isspace(*p)) p++;
1809 if (*p == '\0') break;
1810 if (strncmp(p, "NIL", 3) == 0)
1812 else if (*p == '"') {
1815 while (*p && *p != '"') p++;
1816 if (*p == '\0') break;
1821 while (*p && *p != ')') p++;
1822 if (*p == '\0') break;
1825 namespace = g_new(IMAPNameSpace, 1);
1826 namespace->name = g_strdup(name);
1827 namespace->separator = separator ? separator[0] : '\0';
1828 ns_list = g_list_append(ns_list, namespace);
1834 static void imap_parse_namespace(IMAPSession *session, IMAPFolder *folder)
1839 g_return_if_fail(session != NULL);
1840 g_return_if_fail(folder != NULL);
1842 if (folder->ns_personal != NULL ||
1843 folder->ns_others != NULL ||
1844 folder->ns_shared != NULL)
1847 if (!imap_has_capability(session, "NAMESPACE")) {
1848 imap_get_namespace_by_list(session, folder);
1852 if (imap_cmd_namespace(SESSION(session)->sock, &ns_str)
1854 log_warning(_("can't get namespace\n"));
1858 str_array = strsplit_parenthesis(ns_str, '(', ')', 3);
1859 if (str_array == NULL) {
1861 imap_get_namespace_by_list(session, folder);
1865 folder->ns_personal = imap_parse_namespace_str(str_array[0]);
1866 if (str_array[0] && str_array[1])
1867 folder->ns_others = imap_parse_namespace_str(str_array[1]);
1868 if (str_array[0] && str_array[1] && str_array[2])
1869 folder->ns_shared = imap_parse_namespace_str(str_array[2]);
1870 g_strfreev(str_array);
1874 static void imap_get_namespace_by_list(IMAPSession *session, IMAPFolder *folder)
1876 GSList *item_list, *cur;
1877 gchar separator = '\0';
1878 IMAPNameSpace *namespace;
1880 g_return_if_fail(session != NULL);
1881 g_return_if_fail(folder != NULL);
1883 if (folder->ns_personal != NULL ||
1884 folder->ns_others != NULL ||
1885 folder->ns_shared != NULL)
1888 imap_cmd_gen_send(SESSION(session)->sock, "LIST \"\" \"\"");
1889 item_list = imap_parse_list(NULL, session, "", &separator);
1890 for (cur = item_list; cur != NULL; cur = cur->next)
1891 folder_item_destroy(FOLDER_ITEM(cur->data));
1892 g_slist_free(item_list);
1894 namespace = g_new(IMAPNameSpace, 1);
1895 namespace->name = g_strdup("");
1896 namespace->separator = separator;
1897 folder->ns_personal = g_list_append(NULL, namespace);
1900 static IMAPNameSpace *imap_find_namespace_from_list(GList *ns_list,
1903 IMAPNameSpace *namespace = NULL;
1904 gchar *tmp_path, *name;
1906 if (!path) path = "";
1908 Xstrcat_a(tmp_path, path, "/", return NULL);
1910 for (; ns_list != NULL; ns_list = ns_list->next) {
1911 IMAPNameSpace *tmp_ns = ns_list->data;
1913 Xstrdup_a(name, tmp_ns->name, return namespace);
1914 if (tmp_ns->separator && tmp_ns->separator != '/')
1915 subst_char(name, tmp_ns->separator, '/');
1916 if (strncmp(tmp_path, name, strlen(name)) == 0)
1923 static IMAPNameSpace *imap_find_namespace(IMAPFolder *folder,
1926 IMAPNameSpace *namespace;
1928 g_return_val_if_fail(folder != NULL, NULL);
1930 namespace = imap_find_namespace_from_list(folder->ns_personal, path);
1931 if (namespace) return namespace;
1932 namespace = imap_find_namespace_from_list(folder->ns_others, path);
1933 if (namespace) return namespace;
1934 namespace = imap_find_namespace_from_list(folder->ns_shared, path);
1935 if (namespace) return namespace;
1940 static gchar imap_get_path_separator(IMAPFolder *folder, const gchar *path)
1942 IMAPNameSpace *namespace;
1943 gchar separator = '/';
1945 namespace = imap_find_namespace(folder, path);
1946 if (namespace && namespace->separator)
1947 separator = namespace->separator;
1952 static gchar *imap_get_real_path(IMAPFolder *folder, const gchar *path)
1957 g_return_val_if_fail(folder != NULL, NULL);
1958 g_return_val_if_fail(path != NULL, NULL);
1960 real_path = imap_locale_to_modified_utf7(path);
1961 separator = imap_get_path_separator(folder, path);
1962 imap_path_separator_subst(real_path, separator);
1967 static gchar *imap_parse_atom(SockInfo *sock, gchar *src,
1968 gchar *dest, gint dest_len, GString *str)
1970 gchar *cur_pos = src;
1973 g_return_val_if_fail(str != NULL, cur_pos);
1975 /* read the next line if the current response buffer is empty */
1976 while (isspace(*cur_pos)) cur_pos++;
1977 while (*cur_pos == '\0') {
1978 if ((nextline = sock_getline(sock)) == NULL)
1980 g_string_assign(str, nextline);
1982 strretchomp(nextline);
1983 /* log_print("IMAP4< %s\n", nextline); */
1984 debug_print("IMAP4< %s\n", nextline);
1987 while (isspace(*cur_pos)) cur_pos++;
1990 if (!strncmp(cur_pos, "NIL", 3)) {
1993 } else if (*cur_pos == '\"') {
1996 p = get_quoted(cur_pos, '\"', dest, dest_len);
1997 cur_pos = p ? p : cur_pos + 2;
1998 } else if (*cur_pos == '{') {
2003 cur_pos = strchr_cpy(cur_pos + 1, '}', buf, sizeof(buf));
2006 g_string_truncate(str, 0);
2010 if ((nextline = sock_getline(sock)) == NULL)
2012 line_len += strlen(nextline);
2013 g_string_append(str, nextline);
2015 strretchomp(nextline);
2016 /* log_print("IMAP4< %s\n", nextline); */
2017 debug_print("IMAP4< %s\n", nextline);
2019 } while (line_len < len);
2021 memcpy(dest, cur_pos, MIN(len, dest_len - 1));
2022 dest[MIN(len, dest_len - 1)] = '\0';
2029 static gchar *imap_get_header(SockInfo *sock, gchar *cur_pos, gchar **headers,
2039 g_return_val_if_fail(str != NULL, cur_pos);
2041 while (isspace(*cur_pos)) cur_pos++;
2043 g_return_val_if_fail(*cur_pos == '{', cur_pos);
2045 cur_pos = strchr_cpy(cur_pos + 1, '}', buf, sizeof(buf));
2048 g_string_truncate(str, 0);
2052 if ((nextline = sock_getline(sock)) == NULL)
2054 block_len += strlen(nextline);
2055 g_string_append(str, nextline);
2057 strretchomp(nextline);
2058 /* debug_print("IMAP4< %s\n", nextline); */
2060 } while (block_len < len);
2062 debug_print("IMAP4< [contents of BODY.PEEK[HEADER.FIELDS (...)]]\n");
2064 *headers = g_strndup(cur_pos, len);
2067 while (isspace(*cur_pos)) cur_pos++;
2068 while (*cur_pos == '\0') {
2069 if ((nextline = sock_getline(sock)) == NULL)
2071 g_string_assign(str, nextline);
2073 strretchomp(nextline);
2074 debug_print("IMAP4< %s\n", nextline);
2077 while (isspace(*cur_pos)) cur_pos++;
2083 static MsgFlags imap_parse_flags(const gchar *flag_str)
2085 const gchar *p = flag_str;
2086 MsgFlags flags = {0, 0};
2088 flags.perm_flags = MSG_UNREAD;
2090 while ((p = strchr(p, '\\')) != NULL) {
2093 if (g_strncasecmp(p, "Recent", 6) == 0 && MSG_IS_UNREAD(flags)) {
2094 MSG_SET_PERM_FLAGS(flags, MSG_NEW);
2095 } else if (g_strncasecmp(p, "Seen", 4) == 0) {
2096 MSG_UNSET_PERM_FLAGS(flags, MSG_NEW|MSG_UNREAD);
2097 } else if (g_strncasecmp(p, "Deleted", 7) == 0) {
2098 MSG_SET_PERM_FLAGS(flags, MSG_DELETED);
2099 } else if (g_strncasecmp(p, "Flagged", 7) == 0) {
2100 MSG_SET_PERM_FLAGS(flags, MSG_MARKED);
2101 } else if (g_strncasecmp(p, "Answered", 8) == 0) {
2102 MSG_SET_PERM_FLAGS(flags, MSG_REPLIED);
2109 static MsgInfo *imap_parse_envelope(SockInfo *sock, FolderItem *item,
2112 gchar buf[IMAPBUFSIZE];
2113 MsgInfo *msginfo = NULL;
2118 MsgFlags flags = {0, 0}, imap_flags = {0, 0};
2120 g_return_val_if_fail(line_str != NULL, NULL);
2121 g_return_val_if_fail(line_str->str[0] == '*' &&
2122 line_str->str[1] == ' ', NULL);
2124 MSG_SET_TMP_FLAGS(flags, MSG_IMAP);
2125 if (item->stype == F_QUEUE) {
2126 MSG_SET_TMP_FLAGS(flags, MSG_QUEUED);
2127 } else if (item->stype == F_DRAFT) {
2128 MSG_SET_TMP_FLAGS(flags, MSG_DRAFT);
2131 cur_pos = line_str->str + 2;
2133 #define PARSE_ONE_ELEMENT(ch) \
2135 cur_pos = strchr_cpy(cur_pos, ch, buf, sizeof(buf)); \
2136 if (cur_pos == NULL) { \
2137 g_warning("cur_pos == NULL\n"); \
2138 procmsg_msginfo_free(msginfo); \
2143 PARSE_ONE_ELEMENT(' ');
2146 PARSE_ONE_ELEMENT(' ');
2147 g_return_val_if_fail(!strcmp(buf, "FETCH"), NULL);
2149 g_return_val_if_fail(*cur_pos == '(', NULL);
2152 while (*cur_pos != '\0' && *cur_pos != ')') {
2153 while (*cur_pos == ' ') cur_pos++;
2155 if (!strncmp(cur_pos, "UID ", 4)) {
2157 uid = strtoul(cur_pos, &cur_pos, 10);
2158 } else if (!strncmp(cur_pos, "FLAGS ", 6)) {
2160 if (*cur_pos != '(') {
2161 g_warning("*cur_pos != '('\n");
2162 procmsg_msginfo_free(msginfo);
2166 PARSE_ONE_ELEMENT(')');
2167 imap_flags = imap_parse_flags(buf);
2168 } else if (!strncmp(cur_pos, "RFC822.SIZE ", 12)) {
2170 size = strtol(cur_pos, &cur_pos, 10);
2171 } else if (!strncmp(cur_pos, "BODY[HEADER.FIELDS ", 19)) {
2175 if (*cur_pos != '(') {
2176 g_warning("*cur_pos != '('\n");
2177 procmsg_msginfo_free(msginfo);
2181 PARSE_ONE_ELEMENT(')');
2182 if (*cur_pos != ']') {
2183 g_warning("*cur_pos != ']'\n");
2184 procmsg_msginfo_free(msginfo);
2189 cur_pos = imap_get_header(sock, cur_pos, &headers,
2191 msginfo = procheader_parse_str(headers, flags, FALSE, FALSE);
2194 g_warning("invalid FETCH response: %s\n", cur_pos);
2200 msginfo->msgnum = uid;
2201 msginfo->size = size;
2202 msginfo->flags.tmp_flags |= imap_flags.tmp_flags;
2203 msginfo->flags.perm_flags = imap_flags.perm_flags;
2209 gint imap_msg_set_perm_flags(MsgInfo *msginfo, MsgPermFlags flags)
2212 IMAPSession *session;
2213 IMAPFlags iflags = 0;
2214 gint ok = IMAP_SUCCESS;
2216 g_return_val_if_fail(msginfo != NULL, -1);
2217 g_return_val_if_fail(msginfo->folder != NULL, -1);
2218 g_return_val_if_fail(msginfo->folder->folder != NULL, -1);
2220 folder = msginfo->folder->folder;
2221 g_return_val_if_fail(FOLDER_CLASS(folder) == &imap_class, -1);
2223 session = imap_session_get(folder);
2224 if (!session) return -1;
2226 if ((ok = imap_select(session, IMAP_FOLDER(folder), msginfo->folder->path,
2227 NULL, NULL, NULL, NULL)) != IMAP_SUCCESS)
2230 if (flags & MSG_MARKED) iflags |= IMAP_FLAG_FLAGGED;
2231 if (flags & MSG_REPLIED) iflags |= IMAP_FLAG_ANSWERED;
2233 ok = imap_set_message_flags(session, msginfo->msgnum,
2234 msginfo->msgnum, iflags, TRUE);
2235 if (ok != IMAP_SUCCESS) return ok;
2238 if (flags & MSG_UNREAD)
2239 ok = imap_set_message_flags(session, msginfo->msgnum,
2240 msginfo->msgnum, IMAP_FLAG_SEEN,
2245 gint imap_msg_unset_perm_flags(MsgInfo *msginfo, MsgPermFlags flags)
2248 IMAPSession *session;
2249 IMAPFlags iflags = 0;
2250 gint ok = IMAP_SUCCESS;
2252 g_return_val_if_fail(msginfo != NULL, -1);
2253 g_return_val_if_fail(msginfo->folder != NULL, -1);
2254 g_return_val_if_fail(msginfo->folder->folder != NULL, -1);
2256 folder = msginfo->folder->folder;
2257 g_return_val_if_fail(FOLDER_CLASS(folder) == &imap_class, -1);
2259 session = imap_session_get(folder);
2260 if (!session) return -1;
2262 if (flags & MSG_MARKED) iflags |= IMAP_FLAG_FLAGGED;
2263 if (flags & MSG_REPLIED) iflags |= IMAP_FLAG_ANSWERED;
2265 ok = imap_set_message_flags(session, msginfo->msgnum,
2266 msginfo->msgnum, iflags, FALSE);
2267 if (ok != IMAP_SUCCESS) return ok;
2270 if (flags & MSG_UNREAD)
2271 ok = imap_set_message_flags(session, msginfo->msgnum,
2272 msginfo->msgnum, IMAP_FLAG_SEEN,
2277 static gint imap_set_message_flags(IMAPSession *session,
2286 buf = g_string_new(is_set ? "+FLAGS (" : "-FLAGS (");
2288 if (IMAP_IS_SEEN(flags)) g_string_append(buf, "\\Seen ");
2289 if (IMAP_IS_ANSWERED(flags)) g_string_append(buf, "\\Answered ");
2290 if (IMAP_IS_FLAGGED(flags)) g_string_append(buf, "\\Flagged ");
2291 if (IMAP_IS_DELETED(flags)) g_string_append(buf, "\\Deleted ");
2292 if (IMAP_IS_DRAFT(flags)) g_string_append(buf, "\\Draft");
2294 if (buf->str[buf->len - 1] == ' ')
2295 g_string_truncate(buf, buf->len - 1);
2297 g_string_append_c(buf, ')');
2299 ok = imap_cmd_store(SESSION(session)->sock, first_uid, last_uid,
2301 g_string_free(buf, TRUE);
2306 static gint imap_select(IMAPSession *session, IMAPFolder *folder,
2308 gint *exists, gint *recent, gint *unseen,
2309 guint32 *uid_validity)
2313 gint exists_, recent_, unseen_, uid_validity_;
2315 if (!exists || !recent || !unseen || !uid_validity) {
2316 if (session->mbox && strcmp(session->mbox, path) == 0)
2317 return IMAP_SUCCESS;
2321 uid_validity = &uid_validity_;
2324 g_free(session->mbox);
2325 session->mbox = NULL;
2327 real_path = imap_get_real_path(folder, path);
2328 ok = imap_cmd_select(SESSION(session)->sock, real_path,
2329 exists, recent, unseen, uid_validity);
2330 if (ok != IMAP_SUCCESS)
2331 log_warning(_("can't select folder: %s\n"), real_path);
2333 session->mbox = g_strdup(path);
2339 #define THROW(err) { ok = err; goto catch; }
2341 static gint imap_status(IMAPSession *session, IMAPFolder *folder,
2343 gint *messages, gint *recent,
2344 guint32 *uid_next, guint32 *uid_validity,
2353 *messages = *recent = *uid_next = *uid_validity = *unseen = 0;
2355 argbuf = g_ptr_array_new();
2357 real_path = imap_get_real_path(folder, path);
2358 QUOTE_IF_REQUIRED(real_path_, real_path);
2359 imap_cmd_gen_send(SESSION(session)->sock, "STATUS %s "
2360 "(MESSAGES RECENT UIDNEXT UIDVALIDITY UNSEEN)",
2363 ok = imap_cmd_ok(SESSION(session)->sock, argbuf);
2364 if (ok != IMAP_SUCCESS) THROW(ok);
2366 str = search_array_str(argbuf, "STATUS");
2367 if (!str) THROW(IMAP_ERROR);
2369 str = strchr(str, '(');
2370 if (!str) THROW(IMAP_ERROR);
2372 while (*str != '\0' && *str != ')') {
2373 while (*str == ' ') str++;
2375 if (!strncmp(str, "MESSAGES ", 9)) {
2377 *messages = strtol(str, &str, 10);
2378 } else if (!strncmp(str, "RECENT ", 7)) {
2380 *recent = strtol(str, &str, 10);
2381 } else if (!strncmp(str, "UIDNEXT ", 8)) {
2383 *uid_next = strtoul(str, &str, 10);
2384 } else if (!strncmp(str, "UIDVALIDITY ", 12)) {
2386 *uid_validity = strtoul(str, &str, 10);
2387 } else if (!strncmp(str, "UNSEEN ", 7)) {
2389 *unseen = strtol(str, &str, 10);
2391 g_warning("invalid STATUS response: %s\n", str);
2398 ptr_array_free_strings(argbuf);
2399 g_ptr_array_free(argbuf, TRUE);
2407 /* low-level IMAP4rev1 commands */
2409 static gint imap_cmd_login(SockInfo *sock,
2410 const gchar *user, const gchar *pass)
2412 gchar *user_, *pass_;
2415 QUOTE_IF_REQUIRED(user_, user);
2416 QUOTE_IF_REQUIRED(pass_, pass);
2417 imap_cmd_gen_send(sock, "LOGIN %s %s", user_, pass_);
2419 ok = imap_cmd_ok(sock, NULL);
2420 if (ok != IMAP_SUCCESS)
2421 log_warning(_("IMAP4 login failed.\n"));
2426 static gint imap_cmd_logout(SockInfo *sock)
2428 imap_cmd_gen_send(sock, "LOGOUT");
2429 return imap_cmd_ok(sock, NULL);
2432 /* Send a NOOP, and examine the server's response to see whether this
2433 * connection is pre-authenticated or not. */
2434 static gint imap_greeting(SockInfo *sock, gboolean *is_preauth)
2439 imap_cmd_gen_send(sock, "NOOP");
2440 argbuf = g_ptr_array_new(); /* will hold messages sent back */
2441 r = imap_cmd_ok(sock, argbuf);
2442 *is_preauth = search_array_str(argbuf, "PREAUTH") != NULL;
2444 ptr_array_free_strings(argbuf);
2445 g_ptr_array_free(argbuf, TRUE);
2450 static void imap_get_capability(Session *session)
2455 if (IMAP_SESSION(session)->capability != NULL)
2458 imap_cmd_gen_send(session->sock, "CAPABILITY");
2460 argbuf = g_ptr_array_new();
2462 if (imap_cmd_ok(session->sock, argbuf) != IMAP_SUCCESS ||
2463 ((capstr = search_array_str(argbuf, "CAPABILITY ")) == NULL)) {
2464 ptr_array_free_strings(argbuf);
2465 g_ptr_array_free(argbuf, TRUE);
2469 capstr += strlen("CAPABILITY ");
2471 IMAP_SESSION(session)->capability = g_strsplit(capstr, " ", 0);
2473 ptr_array_free_strings(argbuf);
2474 g_ptr_array_free(argbuf, TRUE);
2477 static gboolean imap_has_capability(IMAPSession *session, const gchar *cap)
2481 for (p = session->capability; *p != NULL; ++p)
2482 if (g_strcasecmp(*p, cap) == 0)
2488 static gint imap_cmd_noop(SockInfo *sock)
2490 imap_cmd_gen_send(sock, "NOOP");
2491 return imap_cmd_ok(sock, NULL);
2494 static gint imap_cmd_starttls(SockInfo *sock)
2496 imap_cmd_gen_send(sock, "STARTTLS");
2497 return imap_cmd_ok(sock, NULL);
2500 #define THROW(err) { ok = err; goto catch; }
2502 static gint imap_cmd_namespace(SockInfo *sock, gchar **ns_str)
2508 argbuf = g_ptr_array_new();
2510 imap_cmd_gen_send(sock, "NAMESPACE");
2511 if ((ok = imap_cmd_ok(sock, argbuf)) != IMAP_SUCCESS) THROW(ok);
2513 str = search_array_str(argbuf, "NAMESPACE");
2514 if (!str) THROW(IMAP_ERROR);
2516 *ns_str = g_strdup(str);
2519 ptr_array_free_strings(argbuf);
2520 g_ptr_array_free(argbuf, TRUE);
2527 static gint imap_cmd_list(SockInfo *sock, const gchar *ref,
2528 const gchar *mailbox, GPtrArray *argbuf)
2530 gchar *ref_, *mailbox_;
2532 if (!ref) ref = "\"\"";
2533 if (!mailbox) mailbox = "\"\"";
2535 QUOTE_IF_REQUIRED(ref_, ref);
2536 QUOTE_IF_REQUIRED(mailbox_, mailbox);
2537 imap_cmd_gen_send(sock, "LIST %s %s", ref_, mailbox_);
2539 return imap_cmd_ok(sock, argbuf);
2542 #define THROW goto catch
2544 static gint imap_cmd_do_select(SockInfo *sock, const gchar *folder,
2546 gint *exists, gint *recent, gint *unseen,
2547 guint32 *uid_validity)
2555 *exists = *recent = *unseen = *uid_validity = 0;
2556 argbuf = g_ptr_array_new();
2559 select_cmd = "EXAMINE";
2561 select_cmd = "SELECT";
2563 QUOTE_IF_REQUIRED(folder_, folder);
2564 imap_cmd_gen_send(sock, "%s %s", select_cmd, folder_);
2566 if ((ok = imap_cmd_ok(sock, argbuf)) != IMAP_SUCCESS) THROW;
2568 resp_str = search_array_contain_str(argbuf, "EXISTS");
2570 if (sscanf(resp_str,"%d EXISTS", exists) != 1) {
2571 g_warning("imap_cmd_select(): invalid EXISTS line.\n");
2576 resp_str = search_array_contain_str(argbuf, "RECENT");
2578 if (sscanf(resp_str, "%d RECENT", recent) != 1) {
2579 g_warning("imap_cmd_select(): invalid RECENT line.\n");
2584 resp_str = search_array_contain_str(argbuf, "UIDVALIDITY");
2586 if (sscanf(resp_str, "OK [UIDVALIDITY %u] ", uid_validity)
2588 g_warning("imap_cmd_select(): invalid UIDVALIDITY line.\n");
2593 resp_str = search_array_contain_str(argbuf, "UNSEEN");
2595 if (sscanf(resp_str, "OK [UNSEEN %d] ", unseen) != 1) {
2596 g_warning("imap_cmd_select(): invalid UNSEEN line.\n");
2602 ptr_array_free_strings(argbuf);
2603 g_ptr_array_free(argbuf, TRUE);
2608 static gint imap_cmd_select(SockInfo *sock, const gchar *folder,
2609 gint *exists, gint *recent, gint *unseen,
2610 guint32 *uid_validity)
2612 return imap_cmd_do_select(sock, folder, FALSE,
2613 exists, recent, unseen, uid_validity);
2616 static gint imap_cmd_examine(SockInfo *sock, const gchar *folder,
2617 gint *exists, gint *recent, gint *unseen,
2618 guint32 *uid_validity)
2620 return imap_cmd_do_select(sock, folder, TRUE,
2621 exists, recent, unseen, uid_validity);
2626 static gint imap_cmd_create(SockInfo *sock, const gchar *folder)
2630 QUOTE_IF_REQUIRED(folder_, folder);
2631 imap_cmd_gen_send(sock, "CREATE %s", folder_);
2633 return imap_cmd_ok(sock, NULL);
2636 static gint imap_cmd_rename(SockInfo *sock, const gchar *old_folder,
2637 const gchar *new_folder)
2639 gchar *old_folder_, *new_folder_;
2641 QUOTE_IF_REQUIRED(old_folder_, old_folder);
2642 QUOTE_IF_REQUIRED(new_folder_, new_folder);
2643 imap_cmd_gen_send(sock, "RENAME %s %s", old_folder_, new_folder_);
2645 return imap_cmd_ok(sock, NULL);
2648 static gint imap_cmd_delete(SockInfo *sock, const gchar *folder)
2652 QUOTE_IF_REQUIRED(folder_, folder);
2653 imap_cmd_gen_send(sock, "DELETE %s", folder_);
2655 return imap_cmd_ok(sock, NULL);
2658 static gint imap_cmd_search(SockInfo *sock, const gchar *criteria, GSList **list)
2664 g_return_val_if_fail(criteria != NULL, IMAP_ERROR);
2665 g_return_val_if_fail(list != NULL, IMAP_ERROR);
2669 argbuf = g_ptr_array_new();
2670 imap_cmd_gen_send(sock, "UID SEARCH %s", criteria);
2672 ok = imap_cmd_ok(sock, argbuf);
2673 if (ok != IMAP_SUCCESS) {
2674 ptr_array_free_strings(argbuf);
2675 g_ptr_array_free(argbuf, TRUE);
2679 if ((uidlist = search_array_str(argbuf, "SEARCH ")) != NULL) {
2680 gchar **strlist, **p;
2682 strlist = g_strsplit(uidlist + 7, " ", 0);
2683 for (p = strlist; *p != NULL; ++p) {
2686 if (sscanf(*p, "%d", &msgnum) == 1)
2687 *list = g_slist_append(*list, GINT_TO_POINTER(msgnum));
2689 g_strfreev(strlist);
2691 ptr_array_free_strings(argbuf);
2692 g_ptr_array_free(argbuf, TRUE);
2694 return IMAP_SUCCESS;
2697 static gint imap_cmd_fetch(SockInfo *sock, guint32 uid, const gchar *filename)
2705 g_return_val_if_fail(filename != NULL, IMAP_ERROR);
2707 imap_cmd_gen_send(sock, "UID FETCH %d BODY[]", uid);
2709 while ((ok = imap_cmd_gen_recv(sock, &buf))
2711 if (buf[0] != '*' || buf[1] != ' ') {
2715 if (strstr(buf, "FETCH") != NULL)
2718 if (ok != IMAP_SUCCESS)
2721 cur_pos = strchr(buf, '{');
2722 if (cur_pos == NULL) {
2726 cur_pos = strchr_cpy(cur_pos + 1, '}', size_str, sizeof(size_str));
2727 if (cur_pos == NULL) {
2731 size_num = atol(size_str);
2733 if (*cur_pos != '\0') {
2738 if (recv_bytes_write_to_file(sock, size_num, filename) != 0) {
2743 if (imap_cmd_gen_recv(sock, &buf) != IMAP_SUCCESS) {
2748 if (buf[0] == '\0' || buf[strlen(buf) - 1] != ')') {
2754 ok = imap_cmd_ok(sock, NULL);
2759 static gint imap_cmd_append(SockInfo *sock, const gchar *destfolder,
2765 gchar buf[BUFFSIZE], *imapbuf;
2768 g_return_val_if_fail(file != NULL, IMAP_ERROR);
2770 size = get_file_size_as_crlf(file);
2771 if ((fp = fopen(file, "rb")) == NULL) {
2772 FILE_OP_ERROR(file, "fopen");
2775 QUOTE_IF_REQUIRED(destfolder_, destfolder);
2776 imap_cmd_gen_send(sock, "APPEND %s (\\Seen) {%d}", destfolder_, size);
2778 ok = imap_cmd_gen_recv(sock, &imapbuf);
2779 if (ok != IMAP_SUCCESS || imapbuf[0] != '+' || imapbuf[1] != ' ') {
2780 log_warning(_("can't append %s to %s\n"), file, destfolder_);
2787 log_print("IMAP4> %s\n", _("(sending file...)"));
2789 while (fgets(buf, sizeof(buf), fp) != NULL) {
2791 if (sock_puts(sock, buf) < 0) {
2799 FILE_OP_ERROR(file, "fgets");
2805 sock_puts(sock, "");
2808 return imap_cmd_ok(sock, NULL);
2812 static void imap_uidplus_copy(IMAPSession *session, MsgInfo *msginfo,
2813 const gchar *destfolder, const gchar *okmsginfo)
2815 unsigned int olduid, newuid;
2816 IMAPFlags iflags = 0;
2818 if (okmsginfo == NULL)
2821 if (sscanf(okmsginfo, "%*u OK [COPYUID %*llu %u %u]", &olduid, &newuid) != 2)
2824 if (olduid != msginfo->msgnum) /* this should NEVER happen */
2827 if (imap_select(session, IMAP_FOLDER(msginfo->folder->folder), destfolder,
2828 NULL, NULL, NULL, NULL) != IMAP_SUCCESS)
2831 if (msginfo->flags.perm_flags & MSG_MARKED) iflags |= IMAP_FLAG_FLAGGED;
2832 if (msginfo->flags.perm_flags & MSG_REPLIED) iflags |= IMAP_FLAG_ANSWERED;
2834 imap_set_message_flags(session, newuid, newuid, iflags, TRUE);
2836 if (msginfo->flags.perm_flags & MSG_UNREAD)
2837 imap_set_message_flags(session, newuid, newuid, IMAP_FLAG_SEEN, FALSE);
2839 imap_select(session, IMAP_FOLDER(msginfo->folder->folder), msginfo->folder->path,
2840 NULL, NULL, NULL, NULL);
2844 static gint imap_cmd_copy(IMAPSession *session,
2845 gint32 msgnum, const gchar *destfolder, gint32 *new_uid)
2848 gint32 olduid, newuid;
2853 g_return_val_if_fail(destfolder != NULL, IMAP_ERROR);
2854 g_return_val_if_fail(session != NULL, IMAP_ERROR);
2855 g_return_val_if_fail(new_uid != NULL, IMAP_ERROR);
2857 QUOTE_IF_REQUIRED(destfolder_, destfolder);
2858 imap_cmd_gen_send(SESSION(session)->sock, "UID COPY %d %s", msgnum, destfolder_);
2860 reply = g_ptr_array_new();
2862 ok = imap_cmd_ok(SESSION(session)->sock, reply);
2863 if (ok != IMAP_SUCCESS)
2864 log_warning(_("can't copy %d to %s\n"), msgnum, destfolder_);
2865 else if (imap_has_capability(session, "UIDPLUS") && reply->len > 0)
2866 if ((okmsginfo = g_ptr_array_index(reply, reply->len - 1)) != NULL &&
2867 sscanf(okmsginfo, "%*u OK [COPYUID %*u %u %u]", &olduid, &newuid) == 2 &&
2871 ptr_array_free_strings(reply);
2872 g_ptr_array_free(reply, TRUE);
2876 gint imap_cmd_envelope(SockInfo *sock, guint32 first_uid, guint32 last_uid)
2878 static GString *header_fields = NULL;
2880 if (header_fields == NULL) {
2881 const HeaderEntry *headers, *elem;
2883 headers = procheader_get_headernames(FALSE);
2884 header_fields = g_string_new("");
2886 for (elem = headers; elem->name != NULL; ++elem) {
2887 gint namelen = strlen(elem->name);
2889 /* Header fields ending with space are not rfc822 headers */
2890 if (elem->name[namelen - 1] == ' ')
2893 /* strip : at the of header field */
2894 if(elem->name[namelen - 1] == ':')
2900 g_string_sprintfa(header_fields, "%s%.*s",
2901 header_fields->str[0] != '\0' ? " " : "",
2902 namelen, elem->name);
2907 (sock, "UID FETCH %d:%d (UID FLAGS RFC822.SIZE BODY.PEEK[HEADER.FIELDS (%s)])",
2908 first_uid, last_uid,
2909 header_fields->str);
2911 return IMAP_SUCCESS;
2914 static gint imap_cmd_store(SockInfo *sock, guint32 first_uid, guint32 last_uid,
2919 imap_cmd_gen_send(sock, "UID STORE %d:%d %s",
2920 first_uid, last_uid, sub_cmd);
2922 if ((ok = imap_cmd_ok(sock, NULL)) != IMAP_SUCCESS) {
2923 log_warning(_("error while imap command: STORE %d:%d %s\n"),
2924 first_uid, last_uid, sub_cmd);
2928 return IMAP_SUCCESS;
2931 static gint imap_cmd_expunge(SockInfo *sock)
2935 imap_cmd_gen_send(sock, "EXPUNGE");
2936 if ((ok = imap_cmd_ok(sock, NULL)) != IMAP_SUCCESS) {
2937 log_warning(_("error while imap command: EXPUNGE\n"));
2941 return IMAP_SUCCESS;
2944 static gint imap_cmd_ok(SockInfo *sock, GPtrArray *argbuf)
2949 gchar cmd_status[IMAPBUFSIZE];
2951 while ((ok = imap_cmd_gen_recv(sock, &buf))
2953 if (buf[0] == '*' && buf[1] == ' ') {
2955 g_ptr_array_add(argbuf, g_strdup(buf + 2));
2959 if (sscanf(buf, "%d %s", &cmd_num, cmd_status) < 2)
2961 else if (cmd_num == imap_cmd_count &&
2962 !strcmp(cmd_status, "OK")) {
2964 g_ptr_array_add(argbuf, g_strdup(buf));
2966 return IMAP_SUCCESS;
2977 static void imap_cmd_gen_send(SockInfo *sock, const gchar *format, ...)
2979 gchar buf[IMAPBUFSIZE];
2980 gchar tmp[IMAPBUFSIZE];
2984 va_start(args, format);
2985 g_vsnprintf(tmp, sizeof(tmp), format, args);
2990 g_snprintf(buf, sizeof(buf), "%d %s\r\n", imap_cmd_count, tmp);
2991 if (!strncasecmp(tmp, "LOGIN ", 6) && (p = strchr(tmp + 6, ' '))) {
2993 log_print("IMAP4> %d %s ********\n", imap_cmd_count, tmp);
2995 log_print("IMAP4> %d %s\n", imap_cmd_count, tmp);
2997 sock_write(sock, buf, strlen(buf));
3000 static gint imap_cmd_gen_recv(SockInfo *sock, gchar **buf)
3002 if ((*buf = sock_getline(sock)) == NULL)
3007 log_print("IMAP4< %s\n", *buf);
3009 return IMAP_SUCCESS;
3013 /* misc utility functions */
3015 static gchar *strchr_cpy(const gchar *src, gchar ch, gchar *dest, gint len)
3020 tmp = strchr(src, ch);
3024 memcpy(dest, src, MIN(tmp - src, len - 1));
3025 dest[MIN(tmp - src, len - 1)] = '\0';
3030 static gchar *get_quoted(const gchar *src, gchar ch, gchar *dest, gint len)
3032 const gchar *p = src;
3035 g_return_val_if_fail(*p == ch, NULL);
3040 while (*p != '\0' && *p != ch) {
3042 if (*p == '\\' && *(p + 1) != '\0')
3051 return (gchar *)(*p == ch ? p + 1 : p);
3054 static gchar *search_array_contain_str(GPtrArray *array, const gchar *str)
3058 for (i = 0; i < array->len; i++) {
3061 tmp = g_ptr_array_index(array, i);
3062 if (strstr(tmp, str) != NULL)
3069 static gchar *search_array_str(GPtrArray *array, const gchar *str)
3076 for (i = 0; i < array->len; i++) {
3079 tmp = g_ptr_array_index(array, i);
3080 if (!strncmp(tmp, str, len))
3087 static void imap_path_separator_subst(gchar *str, gchar separator)
3090 gboolean in_escape = FALSE;
3092 if (!separator || separator == '/') return;
3094 for (p = str; *p != '\0'; p++) {
3095 if (*p == '/' && !in_escape)
3097 else if (*p == '&' && *(p + 1) != '-' && !in_escape)
3099 else if (*p == '-' && in_escape)
3104 static gchar *imap_modified_utf7_to_locale(const gchar *mutf7_str)
3107 return g_strdup(mutf7_str);
3109 static iconv_t cd = (iconv_t)-1;
3110 static gboolean iconv_ok = TRUE;
3113 size_t norm_utf7_len;
3115 gchar *to_str, *to_p;
3117 gboolean in_escape = FALSE;
3119 if (!iconv_ok) return g_strdup(mutf7_str);
3121 if (cd == (iconv_t)-1) {
3122 cd = iconv_open(conv_get_current_charset_str(), "UTF-7");
3123 if (cd == (iconv_t)-1) {
3124 g_warning("iconv cannot convert UTF-7 to %s\n",
3125 conv_get_current_charset_str());
3127 return g_strdup(mutf7_str);
3131 norm_utf7 = g_string_new(NULL);
3133 for (p = mutf7_str; *p != '\0'; p++) {
3134 /* replace: '&' -> '+',
3136 escaped ',' -> '/' */
3137 if (!in_escape && *p == '&') {
3138 if (*(p + 1) != '-') {
3139 g_string_append_c(norm_utf7, '+');
3142 g_string_append_c(norm_utf7, '&');
3145 } else if (in_escape && *p == ',') {
3146 g_string_append_c(norm_utf7, '/');
3147 } else if (in_escape && *p == '-') {
3148 g_string_append_c(norm_utf7, '-');
3151 g_string_append_c(norm_utf7, *p);
3155 norm_utf7_p = norm_utf7->str;
3156 norm_utf7_len = norm_utf7->len;
3157 to_len = strlen(mutf7_str) * 5;
3158 to_p = to_str = g_malloc(to_len + 1);
3160 if (iconv(cd, (ICONV_CONST gchar **)&norm_utf7_p, &norm_utf7_len,
3161 &to_p, &to_len) == -1) {
3162 g_warning(_("iconv cannot convert UTF-7 to %s\n"),
3163 conv_get_current_charset_str());
3164 g_string_free(norm_utf7, TRUE);
3166 return g_strdup(mutf7_str);
3169 /* second iconv() call for flushing */
3170 iconv(cd, NULL, NULL, &to_p, &to_len);
3171 g_string_free(norm_utf7, TRUE);
3175 #endif /* !HAVE_ICONV */
3178 static gchar *imap_locale_to_modified_utf7(const gchar *from)
3181 return g_strdup(from);
3183 static iconv_t cd = (iconv_t)-1;
3184 static gboolean iconv_ok = TRUE;
3185 gchar *norm_utf7, *norm_utf7_p;
3186 size_t from_len, norm_utf7_len;
3188 gchar *from_tmp, *to, *p;
3189 gboolean in_escape = FALSE;
3191 if (!iconv_ok) return g_strdup(from);
3193 if (cd == (iconv_t)-1) {
3194 cd = iconv_open("UTF-7", conv_get_current_charset_str());
3195 if (cd == (iconv_t)-1) {
3196 g_warning("iconv cannot convert %s to UTF-7\n",
3197 conv_get_current_charset_str());
3199 return g_strdup(from);
3203 Xstrdup_a(from_tmp, from, return g_strdup(from));
3204 from_len = strlen(from);
3205 norm_utf7_len = from_len * 5;
3206 Xalloca(norm_utf7, norm_utf7_len + 1, return g_strdup(from));
3207 norm_utf7_p = norm_utf7;
3209 #define IS_PRINT(ch) (isprint(ch) && isascii(ch))
3211 while (from_len > 0) {
3212 if (IS_PRINT(*from_tmp)) {
3213 /* printable ascii char */
3214 *norm_utf7_p = *from_tmp;
3221 /* unprintable char: convert to UTF-7 */
3223 !IS_PRINT(from_tmp[mblen]) && mblen < from_len;
3227 if (iconv(cd, (ICONV_CONST gchar **)&from_tmp, &mblen,
3228 &norm_utf7_p, &norm_utf7_len) == -1) {
3229 g_warning("iconv cannot convert %s to UTF-7\n",
3230 conv_get_current_charset_str());
3231 return g_strdup(from);
3234 /* second iconv() call for flushing */
3235 iconv(cd, NULL, NULL, &norm_utf7_p, &norm_utf7_len);
3241 *norm_utf7_p = '\0';
3242 to_str = g_string_new(NULL);
3243 for (p = norm_utf7; p < norm_utf7_p; p++) {
3244 /* replace: '&' -> "&-",
3246 escaped '/' -> ',' */
3247 if (!in_escape && *p == '&') {
3248 g_string_append(to_str, "&-");
3249 } else if (!in_escape && *p == '+') {
3250 g_string_append_c(to_str, '&');
3252 } else if (in_escape && *p == '/') {
3253 g_string_append_c(to_str, ',');
3254 } else if (in_escape && *p == '-') {
3256 g_string_append_c(to_str, '-');
3258 g_string_append_c(to_str, *p);
3264 g_string_append_c(to_str, '-');
3268 g_string_free(to_str, FALSE);
3271 #endif /* !HAVE_ICONV */
3274 static gboolean imap_rename_folder_func(GNode *node, gpointer data)
3276 FolderItem *item = node->data;
3277 gchar **paths = data;
3278 const gchar *oldpath = paths[0];
3279 const gchar *newpath = paths[1];
3281 gchar *new_itempath;
3284 oldpathlen = strlen(oldpath);
3285 if (strncmp(oldpath, item->path, oldpathlen) != 0) {
3286 g_warning("path doesn't match: %s, %s\n", oldpath, item->path);
3290 base = item->path + oldpathlen;
3291 while (*base == G_DIR_SEPARATOR) base++;
3293 new_itempath = g_strdup(newpath);
3295 new_itempath = g_strconcat(newpath, G_DIR_SEPARATOR_S, base,
3298 item->path = new_itempath;
3303 gint imap_get_num_list(Folder *folder, FolderItem *_item, GSList **msgnum_list)
3305 IMAPFolderItem *item = (IMAPFolderItem *)_item;
3306 IMAPSession *session;
3307 gint ok, lastuid_old, nummsgs = 0, exists, recent, unseen, uid_val, uid_next;
3308 GSList *uidlist, *elem;
3309 gchar *dir, *cmd_buf;
3311 g_return_val_if_fail(folder != NULL, -1);
3312 g_return_val_if_fail(item != NULL, -1);
3313 g_return_val_if_fail(item->item.path != NULL, -1);
3314 g_return_val_if_fail(FOLDER_CLASS(folder) == &imap_class, -1);
3315 g_return_val_if_fail(folder->account != NULL, -1);
3317 session = imap_session_get(folder);
3318 g_return_val_if_fail(session != NULL, -1);
3320 ok = imap_status(session, IMAP_FOLDER(folder), item->item.path,
3321 &exists, &recent, &uid_next, &uid_val, &unseen);
3322 if (ok != IMAP_SUCCESS)
3325 /* If old uid_next matches new uid_next we can be sure no message
3326 was added to the folder */
3327 if (uid_next == item->uid_next) {
3328 nummsgs = g_slist_length(item->uid_list);
3330 /* If number of messages is still the same we
3331 know our caches message numbers are still valid,
3332 otherwise if the number of messages has decrease
3333 we discard our cache to start a new scan to find
3334 out which numbers have been removed */
3335 if (exists == nummsgs) {
3336 *msgnum_list = g_slist_copy(item->uid_list);
3338 } else if (exists < nummsgs) {
3339 debug_print("Freeing imap uid cache");
3341 g_slist_free(item->uid_list);
3342 item->uid_list = NULL;
3345 item->uid_next = uid_next;
3348 *msgnum_list = NULL;
3352 ok = imap_select(session, IMAP_FOLDER(folder), item->item.path,
3353 NULL, NULL, NULL, NULL);
3354 if (ok != IMAP_SUCCESS)
3357 cmd_buf = g_strdup_printf("UID %d:*", item->lastuid + 1);
3358 ok = imap_cmd_search(SESSION(session)->sock, cmd_buf, &uidlist);
3361 if (ok == IMAP_SOCKET) {
3362 session_destroy((Session *)session);
3363 ((RemoteFolder *)folder)->session = NULL;
3367 if (ok != IMAP_SUCCESS) {
3371 argbuf = g_ptr_array_new();
3373 cmd_buf = g_strdup_printf("UID FETCH %d:* (UID)", item->lastuid + 1);
3374 imap_cmd_gen_send(SESSION(session)->sock, cmd_buf);
3376 ok = imap_cmd_ok(SESSION(session)->sock, argbuf);
3377 if (ok != IMAP_SUCCESS) {
3378 ptr_array_free_strings(argbuf);
3379 g_ptr_array_free(argbuf, TRUE);
3383 for(i = 0; i < argbuf->len; i++) {
3386 if((ret = sscanf(g_ptr_array_index(argbuf, i),
3387 "%*d FETCH (UID %d)", &msgnum)) == 1)
3388 uidlist = g_slist_prepend(uidlist, GINT_TO_POINTER(msgnum));
3390 ptr_array_free_strings(argbuf);
3391 g_ptr_array_free(argbuf, TRUE);
3394 lastuid_old = item->lastuid;
3395 *msgnum_list = g_slist_copy(item->uid_list);
3396 nummsgs = g_slist_length(*msgnum_list);
3397 debug_print("Got %d uids from cache\n", g_slist_length(item->uid_list));
3399 for (elem = uidlist; elem != NULL; elem = g_slist_next(elem)) {
3402 msgnum = GPOINTER_TO_INT(elem->data);
3403 if (msgnum > lastuid_old) {
3404 *msgnum_list = g_slist_prepend(*msgnum_list, GINT_TO_POINTER(msgnum));
3405 item->uid_list = g_slist_prepend(item->uid_list, GINT_TO_POINTER(msgnum));
3408 if(msgnum > item->lastuid)
3409 item->lastuid = msgnum;
3412 g_slist_free(uidlist);
3414 if (nummsgs != exists) {
3415 /* Cache contains more messages then folder, we have cached
3416 an old UID of a message that was removed and new messages
3417 have been added too, otherwise the uid_next check would
3419 debug_print("Freeing imap uid cache");
3421 g_slist_free(item->uid_list);
3422 item->uid_list = NULL;
3424 g_slist_free(*msgnum_list);
3426 return imap_get_num_list(folder, _item, msgnum_list);
3429 dir = folder_item_get_path((FolderItem *)item);
3430 debug_print("removing old messages from %s\n", dir);
3431 remove_numbered_files_not_in_list(dir, *msgnum_list);
3437 static MsgInfo *imap_parse_msg(const gchar *file, FolderItem *item)
3442 flags.perm_flags = MSG_NEW|MSG_UNREAD;
3443 flags.tmp_flags = 0;
3445 g_return_val_if_fail(item != NULL, NULL);
3446 g_return_val_if_fail(file != NULL, NULL);
3448 if (item->stype == F_QUEUE) {
3449 MSG_SET_TMP_FLAGS(flags, MSG_QUEUED);
3450 } else if (item->stype == F_DRAFT) {
3451 MSG_SET_TMP_FLAGS(flags, MSG_DRAFT);
3454 msginfo = procheader_parse_file(file, flags, FALSE, FALSE);
3455 if (!msginfo) return NULL;
3457 msginfo->folder = item;
3462 static int compare_uint(gconstpointer a, gconstpointer b)
3467 GSList *imap_get_msginfos(Folder *folder, FolderItem *item, GSList *msgnum_list)
3469 IMAPSession *session;
3470 GSList *sorted_list, *elem, *ret = NULL;
3471 gint ok, startnum, lastnum;
3473 g_return_val_if_fail(folder != NULL, NULL);
3474 g_return_val_if_fail(item != NULL, NULL);
3475 g_return_val_if_fail(msgnum_list != NULL, NULL);
3477 session = imap_session_get(folder);
3478 g_return_val_if_fail(session != NULL, NULL);
3480 ok = imap_select(session, IMAP_FOLDER(folder), item->path,
3481 NULL, NULL, NULL, NULL);
3482 if (ok != IMAP_SUCCESS)
3485 sorted_list = g_slist_sort(g_slist_copy(msgnum_list), compare_uint);
3487 startnum = lastnum = GPOINTER_TO_INT(sorted_list->data);
3489 for (elem = sorted_list;; elem = g_slist_next(elem)) {
3493 num = GPOINTER_TO_INT(elem->data);
3495 if (num > lastnum + 1 || elem == NULL) {
3496 if (!(item->stype == F_QUEUE || item->stype == F_DRAFT)) {
3497 ret = g_slist_concat(ret,
3498 imap_get_uncached_messages(
3499 session, item, startnum, lastnum));
3502 for (i = startnum; i <= lastnum; ++i) {
3505 file = imap_fetch_msg(folder, item, i);
3507 MsgInfo *msginfo = imap_parse_msg(file, item);
3508 if (msginfo != NULL) {
3509 msginfo->msgnum = i;
3510 ret = g_slist_append(ret, msginfo);
3525 g_slist_free(sorted_list);
3530 MsgInfo *imap_get_msginfo(Folder *folder, FolderItem *item, gint uid)
3532 IMAPSession *session;
3533 MsgInfo *msginfo = NULL;
3536 g_return_val_if_fail(folder != NULL, NULL);
3537 g_return_val_if_fail(item != NULL, NULL);
3539 session = imap_session_get(folder);
3540 g_return_val_if_fail(session != NULL, NULL);
3542 ok = imap_select(session, IMAP_FOLDER(folder), item->path,
3543 NULL, NULL, NULL, NULL);
3544 if (ok != IMAP_SUCCESS)
3547 if (!(item->stype == F_QUEUE || item->stype == F_DRAFT)) {
3550 list = imap_get_uncached_messages(session, item, uid, uid);
3552 msginfo = (MsgInfo *)list->data;
3555 procmsg_msg_list_free(list);
3559 file = imap_fetch_msg(folder, item, uid);
3561 msginfo = imap_parse_msg(file, item);
3562 if (msginfo != NULL)
3563 msginfo->msgnum = uid;
3571 gboolean imap_check_msgnum_validity(Folder *folder, FolderItem *_item)
3573 IMAPSession *session;
3574 IMAPFolderItem *item = (IMAPFolderItem *)_item;
3575 gint ok, exists = 0, recent = 0, unseen = 0;
3576 guint32 uid_next, uid_validity = 0;
3578 g_return_val_if_fail(folder != NULL, FALSE);
3579 g_return_val_if_fail(item != NULL, FALSE);
3580 g_return_val_if_fail(item->item.folder != NULL, FALSE);
3581 g_return_val_if_fail(FOLDER_CLASS(item->item.folder) == &imap_class, FALSE);
3583 session = imap_session_get(folder);
3584 g_return_val_if_fail(session != NULL, FALSE);
3586 ok = imap_status(session, IMAP_FOLDER(folder), item->item.path,
3587 &exists, &recent, &uid_next, &uid_validity, &unseen);
3588 if (ok != IMAP_SUCCESS)
3591 if(item->item.mtime == uid_validity)
3594 debug_print("Freeing imap uid cache");
3596 g_slist_free(item->uid_list);
3597 item->uid_list = NULL;
3599 item->item.mtime = uid_validity;
3601 imap_delete_all_cached_messages((FolderItem *)item);