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"
57 #include "remotefolder.h"
59 typedef struct _IMAPFolder IMAPFolder;
60 typedef struct _IMAPSession IMAPSession;
61 typedef struct _IMAPNameSpace IMAPNameSpace;
62 typedef struct _IMAPFolderItem IMAPFolderItem;
64 #include "prefs_account.h"
66 #define IMAP_FOLDER(obj) ((IMAPFolder *)obj)
67 #define IMAP_FOLDER_ITEM(obj) ((IMAPFolderItem *)obj)
68 #define IMAP_SESSION(obj) ((IMAPSession *)obj)
74 /* list of IMAPNameSpace */
84 gboolean authenticated;
93 time_t last_access_time;
94 gboolean folder_content_changed;
104 #define IMAP_SUCCESS 0
105 #define IMAP_SOCKET 2
106 #define IMAP_AUTHFAIL 3
107 #define IMAP_PROTOCOL 4
108 #define IMAP_SYNTAX 5
112 #define IMAPBUFSIZE 8192
116 IMAP_FLAG_SEEN = 1 << 0,
117 IMAP_FLAG_ANSWERED = 1 << 1,
118 IMAP_FLAG_FLAGGED = 1 << 2,
119 IMAP_FLAG_DELETED = 1 << 3,
120 IMAP_FLAG_DRAFT = 1 << 4
123 #define IMAP_IS_SEEN(flags) ((flags & IMAP_FLAG_SEEN) != 0)
124 #define IMAP_IS_ANSWERED(flags) ((flags & IMAP_FLAG_ANSWERED) != 0)
125 #define IMAP_IS_FLAGGED(flags) ((flags & IMAP_FLAG_FLAGGED) != 0)
126 #define IMAP_IS_DELETED(flags) ((flags & IMAP_FLAG_DELETED) != 0)
127 #define IMAP_IS_DRAFT(flags) ((flags & IMAP_FLAG_DRAFT) != 0)
130 #define IMAP4_PORT 143
132 #define IMAPS_PORT 993
135 #define IMAP_CMD_LIMIT 1000
137 #define QUOTE_IF_REQUIRED(out, str) \
139 if (*str != '"' && strpbrk(str, " \t(){}%*") != NULL) { \
143 len = strlen(str) + 3; \
144 Xalloca(__tmp, len, return IMAP_ERROR); \
145 g_snprintf(__tmp, len, "\"%s\"", str); \
148 Xstrdup_a(out, str, return IMAP_ERROR); \
152 typedef gchar * IMAPSet;
154 struct _IMAPFolderItem
163 static void imap_folder_init (Folder *folder,
167 static Folder *imap_folder_new (const gchar *name,
169 static void imap_folder_destroy (Folder *folder);
171 static IMAPSession *imap_session_new (const PrefsAccount *account);
172 static void imap_session_authenticate(IMAPSession *session,
173 const PrefsAccount *account);
174 static void imap_session_destroy (Session *session);
176 static gchar *imap_fetch_msg (Folder *folder,
179 static gint imap_add_msg (Folder *folder,
183 static gint imap_add_msgs (Folder *folder,
186 GRelation *relation);
188 static gint imap_copy_msg (Folder *folder,
191 static gint imap_copy_msgs (Folder *folder,
193 MsgInfoList *msglist,
194 GRelation *relation);
196 static gint imap_remove_msg (Folder *folder,
199 static gint imap_remove_all_msg (Folder *folder,
202 static gboolean imap_is_msg_changed (Folder *folder,
206 static gint imap_close (Folder *folder,
209 static gint imap_scan_tree (Folder *folder);
211 static gint imap_create_tree (Folder *folder);
213 static FolderItem *imap_create_folder (Folder *folder,
216 static gint imap_rename_folder (Folder *folder,
219 static gint imap_remove_folder (Folder *folder,
222 static FolderItem *imap_folder_item_new (Folder *folder);
223 static void imap_folder_item_destroy (Folder *folder,
226 static IMAPSession *imap_session_get (Folder *folder);
228 static gint imap_greeting (IMAPSession *session);
229 static gint imap_auth (IMAPSession *session,
234 static gint imap_scan_tree_recursive (IMAPSession *session,
236 static GSList *imap_parse_list (IMAPFolder *folder,
237 IMAPSession *session,
238 const gchar *real_path,
241 static void imap_create_missing_folders (Folder *folder);
242 static FolderItem *imap_create_special_folder
244 SpecialFolderItemType stype,
247 static gint imap_do_copy_msgs (Folder *folder,
249 MsgInfoList *msglist,
250 GRelation *relation);
252 static void imap_delete_all_cached_messages (FolderItem *item);
255 static SockInfo *imap_open (const gchar *server,
259 static SockInfo *imap_open (const gchar *server,
264 static SockInfo *imap_open_tunnel(const gchar *server,
265 const gchar *tunnelcmd,
268 static SockInfo *imap_open_tunnel(const gchar *server,
269 const gchar *tunnelcmd);
273 static SockInfo *imap_init_sock(SockInfo *sock, SSLType ssl_type);
275 static SockInfo *imap_init_sock(SockInfo *sock);
278 static gchar *imap_get_flag_str (IMAPFlags flags);
279 static gint imap_set_message_flags (IMAPSession *session,
280 MsgNumberList *numlist,
283 static gint imap_select (IMAPSession *session,
289 guint32 *uid_validity);
290 static gint imap_status (IMAPSession *session,
296 guint32 *uid_validity,
299 static void imap_parse_namespace (IMAPSession *session,
301 static void imap_get_namespace_by_list (IMAPSession *session,
303 static IMAPNameSpace *imap_find_namespace (IMAPFolder *folder,
305 static gchar imap_get_path_separator (IMAPFolder *folder,
307 static gchar *imap_get_real_path (IMAPFolder *folder,
310 static gchar *imap_parse_atom (SockInfo *sock,
315 static MsgFlags imap_parse_flags (const gchar *flag_str);
316 static MsgInfo *imap_parse_envelope (SockInfo *sock,
320 static gboolean imap_has_capability (IMAPSession *session,
322 static void imap_free_capabilities (IMAPSession *session);
324 /* low-level IMAP4rev1 commands */
325 static gint imap_cmd_authenticate
326 (IMAPSession *session,
330 static gint imap_cmd_login (IMAPSession *session,
333 static gint imap_cmd_logout (IMAPSession *session);
334 static gint imap_cmd_noop (IMAPSession *session);
335 static gint imap_cmd_starttls (IMAPSession *session);
336 static gint imap_cmd_namespace (IMAPSession *session,
338 static gint imap_cmd_list (IMAPSession *session,
340 const gchar *mailbox,
342 static gint imap_cmd_do_select (IMAPSession *session,
348 guint32 *uid_validity);
349 static gint imap_cmd_select (IMAPSession *session,
354 guint32 *uid_validity);
355 static gint imap_cmd_examine (IMAPSession *session,
360 guint32 *uid_validity);
361 static gint imap_cmd_create (IMAPSession *sock,
362 const gchar *folder);
363 static gint imap_cmd_rename (IMAPSession *sock,
364 const gchar *oldfolder,
365 const gchar *newfolder);
366 static gint imap_cmd_delete (IMAPSession *session,
367 const gchar *folder);
368 static gint imap_cmd_envelope (IMAPSession *session,
370 static gint imap_cmd_fetch (IMAPSession *sock,
372 const gchar *filename);
373 static gint imap_cmd_append (IMAPSession *session,
374 const gchar *destfolder,
378 static gint imap_cmd_copy (IMAPSession *session,
379 const gchar *seq_set,
380 const gchar *destfolder,
381 GRelation *uid_mapping);
382 static gint imap_cmd_store (IMAPSession *session,
385 static gint imap_cmd_expunge (IMAPSession *session,
387 static gint imap_cmd_close (IMAPSession *session);
389 static gint imap_cmd_ok (IMAPSession *session,
391 static void imap_gen_send (IMAPSession *session,
392 const gchar *format, ...);
393 static gint imap_gen_recv (IMAPSession *session,
396 /* misc utility functions */
397 static gchar *strchr_cpy (const gchar *src,
401 static gchar *get_quoted (const gchar *src,
405 static gchar *search_array_contain_str (GPtrArray *array,
407 static gchar *search_array_str (GPtrArray *array,
409 static void imap_path_separator_subst (gchar *str,
412 static gchar *imap_modified_utf7_to_locale (const gchar *mutf7_str);
413 static gchar *imap_locale_to_modified_utf7 (const gchar *from);
415 static GSList *imap_get_seq_set_from_numlist (MsgNumberList *msglist);
416 static GSList *imap_get_seq_set_from_msglist (MsgInfoList *msglist);
417 static void imap_seq_set_free (GSList *seq_list);
419 static gboolean imap_rename_folder_func (GNode *node,
421 static gint imap_get_num_list (Folder *folder,
424 gboolean *old_uids_valid);
425 static GSList *imap_get_msginfos (Folder *folder,
427 GSList *msgnum_list);
428 static MsgInfo *imap_get_msginfo (Folder *folder,
431 static gboolean imap_scan_required (Folder *folder,
433 static void imap_change_flags (Folder *folder,
436 MsgPermFlags newflags);
437 static gchar *imap_folder_get_path (Folder *folder);
438 static gchar *imap_item_get_path (Folder *folder,
441 static FolderClass imap_class =
447 /* Folder functions */
455 /* FolderItem functions */
456 imap_folder_item_new,
457 imap_folder_item_destroy,
471 /* Message functions */
485 FolderClass *imap_get_class(void)
490 static Folder *imap_folder_new(const gchar *name, const gchar *path)
494 folder = (Folder *)g_new0(IMAPFolder, 1);
495 folder->klass = &imap_class;
496 imap_folder_init(folder, name, path);
501 static void imap_folder_destroy(Folder *folder)
505 dir = imap_folder_get_path(folder);
506 if (is_dir_exist(dir))
507 remove_dir_recursive(dir);
510 folder_remote_folder_destroy(REMOTE_FOLDER(folder));
513 static void imap_folder_init(Folder *folder, const gchar *name,
516 folder_remote_folder_init((Folder *)folder, name, path);
519 static FolderItem *imap_folder_item_new(Folder *folder)
521 IMAPFolderItem *item;
523 item = g_new0(IMAPFolderItem, 1);
526 item->uid_list = NULL;
528 return (FolderItem *)item;
531 static void imap_folder_item_destroy(Folder *folder, FolderItem *_item)
533 IMAPFolderItem *item = (IMAPFolderItem *)_item;
535 g_return_if_fail(item != NULL);
536 g_slist_free(item->uid_list);
541 static gboolean imap_reset_uid_lists_func(GNode *node, gpointer data)
543 IMAPFolderItem *item = (IMAPFolderItem *)node->data;
547 g_slist_free(item->uid_list);
548 item->uid_list = NULL;
553 static void imap_reset_uid_lists(Folder *folder)
555 if(folder->node == NULL)
558 /* Destroy all uid lists and rest last uid */
559 g_node_traverse(folder->node, G_IN_ORDER, G_TRAVERSE_ALL, -1, imap_reset_uid_lists_func, NULL);
562 /* Send CAPABILITY, and examine the server's response to see whether this
563 * connection is pre-authenticated or not and build a list of CAPABILITIES. */
564 static gint imap_greeting(IMAPSession *session)
569 imap_gen_send(session, "CAPABILITY");
571 argbuf = g_ptr_array_new();
573 if (imap_cmd_ok(session, argbuf) != IMAP_SUCCESS ||
574 ((capstr = search_array_str(argbuf, "CAPABILITY ")) == NULL)) {
575 ptr_array_free_strings(argbuf);
576 g_ptr_array_free(argbuf, TRUE);
580 session->authenticated = search_array_str(argbuf, "PREAUTH") != NULL;
582 capstr += strlen("CAPABILITY ");
584 IMAP_SESSION(session)->capability = g_strsplit(capstr, " ", 0);
586 ptr_array_free_strings(argbuf);
587 g_ptr_array_free(argbuf, TRUE);
589 if (imap_has_capability(session, "UIDPLUS"))
590 session->uidplus = TRUE;
595 static gint imap_auth(IMAPSession *session, const gchar *user, const gchar *pass,
600 if (type == 0 || type == IMAP_AUTH_LOGIN)
601 ok = imap_cmd_login(session, user, pass);
603 ok = imap_cmd_authenticate(session, user, pass, type);
605 if (ok == IMAP_SUCCESS)
606 session->authenticated = TRUE;
611 static IMAPSession *imap_session_get(Folder *folder)
613 RemoteFolder *rfolder = REMOTE_FOLDER(folder);
614 IMAPSession *session = NULL;
616 g_return_val_if_fail(folder != NULL, NULL);
617 g_return_val_if_fail(FOLDER_CLASS(folder) == &imap_class, NULL);
618 g_return_val_if_fail(folder->account != NULL, NULL);
620 /* Make sure we have a session */
621 if (rfolder->session != NULL) {
622 session = IMAP_SESSION(rfolder->session);
624 imap_reset_uid_lists(folder);
625 session = imap_session_new(folder->account);
630 if (SESSION(session)->sock->state == CONN_DISCONNECTED) {
631 debug_print("IMAP server disconnected\n");
632 session_destroy(SESSION(session));
633 imap_reset_uid_lists(folder);
634 session = imap_session_new(folder->account);
637 /* Make sure session is authenticated */
638 if (!IMAP_SESSION(session)->authenticated)
639 imap_session_authenticate(IMAP_SESSION(session), folder->account);
640 if (!IMAP_SESSION(session)->authenticated) {
641 session_destroy(SESSION(session));
642 rfolder->session = NULL;
646 /* Make sure we have parsed the IMAP namespace */
647 imap_parse_namespace(IMAP_SESSION(session),
648 IMAP_FOLDER(folder));
650 /* I think the point of this code is to avoid sending a
651 * keepalive if we've used the session recently and therefore
652 * think it's still alive. Unfortunately, most of the code
653 * does not yet check for errors on the socket, and so if the
654 * connection drops we don't notice until the timeout expires.
655 * A better solution than sending a NOOP every time would be
656 * for every command to be prepared to retry until it is
657 * successfully sent. -- mbp */
658 if (time(NULL) - session->last_access_time > SESSION_TIMEOUT) {
659 /* verify that the session is still alive */
660 if (imap_cmd_noop(session) != IMAP_SUCCESS) {
661 /* Check if this is the first try to establish a
662 connection, if yes we don't try to reconnect */
663 if (rfolder->session == NULL) {
664 log_warning(_("Connecting %s failed"),
665 folder->account->recv_server);
666 session_destroy(SESSION(session));
669 log_warning(_("IMAP4 connection to %s has been"
670 " disconnected. Reconnecting...\n"),
671 folder->account->recv_server);
672 session_destroy(SESSION(session));
673 /* Clear folders session to make imap_session_get create
674 a new session, because of rfolder->session == NULL
675 it will not try to reconnect again and so avoid an
677 rfolder->session = NULL;
678 session = imap_session_get(folder);
683 rfolder->session = SESSION(session);
685 session->last_access_time = time(NULL);
687 return IMAP_SESSION(session);
690 static IMAPSession *imap_session_new(const PrefsAccount *account)
692 IMAPSession *session;
697 /* FIXME: IMAP over SSL only... */
700 port = account->set_imapport ? account->imapport
701 : account->ssl_imap == SSL_TUNNEL ? IMAPS_PORT : IMAP4_PORT;
702 ssl_type = account->ssl_imap;
704 port = account->set_imapport ? account->imapport
708 if (account->set_tunnelcmd) {
709 log_message(_("creating tunneled IMAP4 connection\n"));
711 if ((imap_sock = imap_open_tunnel(account->recv_server,
715 if ((imap_sock = imap_open_tunnel(account->recv_server,
716 account->tunnelcmd)) == NULL)
720 g_return_val_if_fail(account->recv_server != NULL, NULL);
722 log_message(_("creating IMAP4 connection to %s:%d ...\n"),
723 account->recv_server, port);
726 if ((imap_sock = imap_open(account->recv_server, port,
729 if ((imap_sock = imap_open(account->recv_server, port)) == NULL)
734 session = g_new0(IMAPSession, 1);
735 session_init(SESSION(session));
736 SESSION(session)->type = SESSION_IMAP;
737 SESSION(session)->server = g_strdup(account->recv_server);
738 SESSION(session)->sock = imap_sock;
740 SESSION(session)->destroy = imap_session_destroy;
742 session->capability = NULL;
744 session->authenticated = FALSE;
745 session->mbox = NULL;
746 session->cmd_count = 0;
748 /* Only need to log in if the connection was not PREAUTH */
749 if (imap_greeting(session) != IMAP_SUCCESS) {
750 session_destroy(SESSION(session));
755 if (account->ssl_imap == SSL_STARTTLS &&
756 imap_has_capability(session, "STARTTLS")) {
759 ok = imap_cmd_starttls(session);
760 if (ok != IMAP_SUCCESS) {
761 log_warning(_("Can't start TLS session.\n"));
762 session_destroy(SESSION(session));
765 if (!ssl_init_socket_with_method(SESSION(session)->sock,
767 session_destroy(SESSION(session));
771 imap_free_capabilities(session);
772 session->authenticated = FALSE;
773 session->uidplus = FALSE;
774 session->cmd_count = 1;
776 if (imap_greeting(session) != IMAP_SUCCESS) {
777 session_destroy(SESSION(session));
782 log_message("IMAP connection is %s-authenticated\n",
783 (session->authenticated) ? "pre" : "un");
788 static void imap_session_authenticate(IMAPSession *session,
789 const PrefsAccount *account)
793 g_return_if_fail(account->userid != NULL);
795 pass = account->passwd;
798 tmp_pass = input_dialog_query_password(account->recv_server, account->userid);
801 Xstrdup_a(pass, tmp_pass, {g_free(tmp_pass); return;});
805 if (imap_auth(session, account->userid, pass, account->imap_auth_type) != IMAP_SUCCESS) {
806 imap_cmd_logout(session);
810 session->authenticated = TRUE;
813 static void imap_session_destroy(Session *session)
815 imap_free_capabilities(IMAP_SESSION(session));
816 g_free(IMAP_SESSION(session)->mbox);
817 sock_close(session->sock);
818 session->sock = NULL;
821 static gchar *imap_fetch_msg(Folder *folder, FolderItem *item, gint uid)
823 gchar *path, *filename;
824 IMAPSession *session;
827 g_return_val_if_fail(folder != NULL, NULL);
828 g_return_val_if_fail(item != NULL, NULL);
830 path = folder_item_get_path(item);
831 if (!is_dir_exist(path))
833 filename = g_strconcat(path, G_DIR_SEPARATOR_S, itos(uid), NULL);
836 if (is_file_exist(filename)) {
837 debug_print("message %d has been already cached.\n", uid);
841 session = imap_session_get(folder);
847 ok = imap_select(session, IMAP_FOLDER(folder), item->path,
848 NULL, NULL, NULL, NULL);
849 if (ok != IMAP_SUCCESS) {
850 g_warning("can't select mailbox %s\n", item->path);
855 debug_print("getting message %d...\n", uid);
856 ok = imap_cmd_fetch(session, (guint32)uid, filename);
858 if (ok != IMAP_SUCCESS) {
859 g_warning("can't fetch message %d\n", uid);
867 static gint imap_add_msg(Folder *folder, FolderItem *dest,
868 const gchar *file, MsgFlags *flags)
872 MsgFileInfo fileinfo;
874 g_return_val_if_fail(file != NULL, -1);
876 fileinfo.msginfo = NULL;
877 fileinfo.file = (gchar *)file;
878 fileinfo.flags = flags;
879 file_list.data = &fileinfo;
880 file_list.next = NULL;
882 ret = imap_add_msgs(folder, dest, &file_list, NULL);
886 static gint imap_add_msgs(Folder *folder, FolderItem *dest, GSList *file_list,
890 IMAPSession *session;
891 guint32 last_uid = 0;
893 MsgFileInfo *fileinfo;
896 g_return_val_if_fail(folder != NULL, -1);
897 g_return_val_if_fail(dest != NULL, -1);
898 g_return_val_if_fail(file_list != NULL, -1);
900 session = imap_session_get(folder);
901 if (!session) return -1;
903 destdir = imap_get_real_path(IMAP_FOLDER(folder), dest->path);
905 for (cur = file_list; cur != NULL; cur = cur->next) {
906 IMAPFlags iflags = 0;
909 fileinfo = (MsgFileInfo *)cur->data;
911 if (fileinfo->flags) {
912 if (MSG_IS_MARKED(*fileinfo->flags))
913 iflags |= IMAP_FLAG_FLAGGED;
914 if (MSG_IS_REPLIED(*fileinfo->flags))
915 iflags |= IMAP_FLAG_ANSWERED;
916 if (!MSG_IS_UNREAD(*fileinfo->flags))
917 iflags |= IMAP_FLAG_SEEN;
920 if (dest->stype == F_OUTBOX ||
921 dest->stype == F_QUEUE ||
922 dest->stype == F_DRAFT ||
923 dest->stype == F_TRASH)
924 iflags |= IMAP_FLAG_SEEN;
926 ok = imap_cmd_append(session, destdir, fileinfo->file, iflags,
929 if (ok != IMAP_SUCCESS) {
930 g_warning("can't append message %s\n", fileinfo->file);
935 if (relation != NULL)
936 g_relation_insert(relation, fileinfo->msginfo != NULL ?
937 (gpointer) fileinfo->msginfo : (gpointer) fileinfo,
938 GINT_TO_POINTER(dest->last_num + 1));
939 if (last_uid < new_uid)
948 static gint imap_do_copy_msgs(Folder *folder, FolderItem *dest,
949 MsgInfoList *msglist, GRelation *relation)
953 GSList *seq_list, *cur;
955 IMAPSession *session;
956 gint ok = IMAP_SUCCESS;
957 GRelation *uid_mapping;
960 g_return_val_if_fail(folder != NULL, -1);
961 g_return_val_if_fail(dest != NULL, -1);
962 g_return_val_if_fail(msglist != NULL, -1);
964 session = imap_session_get(folder);
965 if (!session) return -1;
967 msginfo = (MsgInfo *)msglist->data;
969 src = msginfo->folder;
971 g_warning("the src folder is identical to the dest.\n");
975 ok = imap_select(session, IMAP_FOLDER(folder), msginfo->folder->path,
976 NULL, NULL, NULL, NULL);
977 if (ok != IMAP_SUCCESS)
980 destdir = imap_get_real_path(IMAP_FOLDER(folder), dest->path);
981 seq_list = imap_get_seq_set_from_msglist(msglist);
982 uid_mapping = g_relation_new(2);
983 g_relation_index(uid_mapping, 0, g_direct_hash, g_direct_equal);
985 for (cur = seq_list; cur != NULL; cur = g_slist_next(cur)) {
986 gchar *seq_set = (gchar *)cur->data;
988 debug_print("Copying message %s%c[%s] to %s ...\n",
989 src->path, G_DIR_SEPARATOR,
992 ok = imap_cmd_copy(session, seq_set, destdir, uid_mapping);
993 if (ok != IMAP_SUCCESS) {
994 g_relation_destroy(uid_mapping);
995 imap_seq_set_free(seq_list);
1000 for (cur = msglist; cur != NULL; cur = g_slist_next(cur)) {
1001 MsgInfo *msginfo = (MsgInfo *)cur->data;
1004 tuples = g_relation_select(uid_mapping,
1005 GINT_TO_POINTER(msginfo->msgnum),
1007 if (tuples->len > 0) {
1008 gint num = GPOINTER_TO_INT(g_tuples_index(tuples, 0, 1));
1009 g_relation_insert(relation, msginfo,
1010 GPOINTER_TO_INT(num));
1014 g_relation_insert(relation, msginfo,
1015 GPOINTER_TO_INT(0));
1016 g_tuples_destroy(tuples);
1019 imap_seq_set_free(seq_list);
1023 if (ok == IMAP_SUCCESS)
1029 static gint imap_copy_msg(Folder *folder, FolderItem *dest, MsgInfo *msginfo)
1033 g_return_val_if_fail(msginfo != NULL, -1);
1035 msglist.data = msginfo;
1036 msglist.next = NULL;
1038 return imap_copy_msgs(folder, dest, &msglist, NULL);
1041 static gint imap_copy_msgs(Folder *folder, FolderItem *dest,
1042 MsgInfoList *msglist, GRelation *relation)
1048 g_return_val_if_fail(folder != NULL, -1);
1049 g_return_val_if_fail(dest != NULL, -1);
1050 g_return_val_if_fail(msglist != NULL, -1);
1052 msginfo = (MsgInfo *)msglist->data;
1053 g_return_val_if_fail(msginfo->folder != NULL, -1);
1055 if (folder == msginfo->folder->folder)
1056 return imap_do_copy_msgs(folder, dest, msglist, relation);
1058 file_list = procmsg_get_message_file_list(msglist);
1059 g_return_val_if_fail(file_list != NULL, -1);
1061 ret = imap_add_msgs(folder, dest, file_list, relation);
1063 procmsg_message_file_list_free(file_list);
1068 static gint imap_remove_msg(Folder *folder, FolderItem *item, gint uid)
1071 IMAPSession *session;
1073 MsgNumberList numlist;
1075 g_return_val_if_fail(folder != NULL, -1);
1076 g_return_val_if_fail(FOLDER_CLASS(folder) == &imap_class, -1);
1077 g_return_val_if_fail(item != NULL, -1);
1079 session = imap_session_get(folder);
1080 if (!session) return -1;
1082 ok = imap_select(session, IMAP_FOLDER(folder), item->path,
1083 NULL, NULL, NULL, NULL);
1084 if (ok != IMAP_SUCCESS)
1087 numlist.next = NULL;
1088 numlist.data = GINT_TO_POINTER(uid);
1090 ok = imap_set_message_flags
1091 (IMAP_SESSION(REMOTE_FOLDER(folder)->session),
1092 &numlist, IMAP_FLAG_DELETED, TRUE);
1093 if (ok != IMAP_SUCCESS) {
1094 log_warning(_("can't set deleted flags: %d\n"), uid);
1098 if (!session->uidplus) {
1099 ok = imap_cmd_expunge(session, NULL);
1103 uidstr = g_strdup_printf("%u", uid);
1104 ok = imap_cmd_expunge(session, uidstr);
1107 if (ok != IMAP_SUCCESS) {
1108 log_warning(_("can't expunge\n"));
1112 IMAP_FOLDER_ITEM(item)->uid_list = g_slist_remove(
1113 IMAP_FOLDER_ITEM(item)->uid_list, numlist.data);
1114 dir = folder_item_get_path(item);
1115 if (is_dir_exist(dir))
1116 remove_numbered_files(dir, uid, uid);
1119 return IMAP_SUCCESS;
1122 static gint imap_remove_all_msg(Folder *folder, FolderItem *item)
1125 IMAPSession *session;
1128 g_return_val_if_fail(folder != NULL, -1);
1129 g_return_val_if_fail(item != NULL, -1);
1131 session = imap_session_get(folder);
1132 if (!session) return -1;
1134 ok = imap_select(session, IMAP_FOLDER(folder), item->path,
1135 NULL, NULL, NULL, NULL);
1136 if (ok != IMAP_SUCCESS)
1139 imap_gen_send(session, "STORE 1:* +FLAGS.SILENT (\\Deleted)");
1140 ok = imap_cmd_ok(session, NULL);
1141 if (ok != IMAP_SUCCESS) {
1142 log_warning(_("can't set deleted flags: 1:*\n"));
1146 ok = imap_cmd_expunge(session, NULL);
1147 if (ok != IMAP_SUCCESS) {
1148 log_warning(_("can't expunge\n"));
1152 dir = folder_item_get_path(item);
1153 if (is_dir_exist(dir))
1154 remove_all_numbered_files(dir);
1157 return IMAP_SUCCESS;
1160 static gboolean imap_is_msg_changed(Folder *folder, FolderItem *item,
1163 /* TODO: properly implement this method */
1167 static gint imap_close(Folder *folder, FolderItem *item)
1170 IMAPSession *session;
1172 g_return_val_if_fail(folder != NULL, -1);
1173 g_return_val_if_fail(item != NULL, -1);
1174 g_return_val_if_fail(item->path != NULL, -1);
1176 session = imap_session_get(folder);
1177 if (!session) return -1;
1179 if (session->mbox) {
1180 if (strcmp2(session->mbox, item->path) != 0) return -1;
1182 ok = imap_cmd_close(session);
1183 if (ok != IMAP_SUCCESS)
1184 log_warning(_("can't close folder\n"));
1186 g_free(session->mbox);
1187 session->mbox = NULL;
1195 static gint imap_scan_tree(Folder *folder)
1197 FolderItem *item = NULL;
1198 IMAPSession *session;
1199 gchar *root_folder = NULL;
1201 g_return_val_if_fail(folder != NULL, -1);
1202 g_return_val_if_fail(folder->account != NULL, -1);
1204 session = imap_session_get(folder);
1206 if (!folder->node) {
1207 folder_tree_destroy(folder);
1208 item = folder_item_new(folder, folder->name, NULL);
1209 item->folder = folder;
1210 folder->node = item->node = g_node_new(item);
1215 if (folder->account->imap_dir && *folder->account->imap_dir) {
1220 Xstrdup_a(root_folder, folder->account->imap_dir, return -1);
1221 extract_quote(root_folder, '"');
1222 subst_char(root_folder,
1223 imap_get_path_separator(IMAP_FOLDER(folder),
1226 strtailchomp(root_folder, '/');
1227 real_path = imap_get_real_path
1228 (IMAP_FOLDER(folder), root_folder);
1229 debug_print("IMAP root directory: %s\n", real_path);
1231 /* check if root directory exist */
1232 argbuf = g_ptr_array_new();
1233 ok = imap_cmd_list(session, NULL, real_path, argbuf);
1234 if (ok != IMAP_SUCCESS ||
1235 search_array_str(argbuf, "LIST ") == NULL) {
1236 log_warning(_("root folder %s does not exist\n"), real_path);
1237 g_ptr_array_free(argbuf, TRUE);
1241 g_ptr_array_free(argbuf, TRUE);
1246 item = FOLDER_ITEM(folder->node->data);
1247 if (!item || ((item->path || root_folder) &&
1248 strcmp2(item->path, root_folder) != 0)) {
1249 folder_tree_destroy(folder);
1250 item = folder_item_new(folder, folder->name, root_folder);
1251 item->folder = folder;
1252 folder->node = item->node = g_node_new(item);
1255 imap_scan_tree_recursive(session, FOLDER_ITEM(folder->node->data));
1256 imap_create_missing_folders(folder);
1261 static gint imap_scan_tree_recursive(IMAPSession *session, FolderItem *item)
1264 IMAPFolder *imapfolder;
1265 FolderItem *new_item;
1266 GSList *item_list, *cur;
1269 gchar *wildcard_path;
1273 g_return_val_if_fail(item != NULL, -1);
1274 g_return_val_if_fail(item->folder != NULL, -1);
1275 g_return_val_if_fail(item->no_sub == FALSE, -1);
1277 folder = item->folder;
1278 imapfolder = IMAP_FOLDER(folder);
1280 separator = imap_get_path_separator(imapfolder, item->path);
1282 if (folder->ui_func)
1283 folder->ui_func(folder, item, folder->ui_func_data);
1286 wildcard[0] = separator;
1289 real_path = imap_get_real_path(imapfolder, item->path);
1293 real_path = g_strdup("");
1296 Xstrcat_a(wildcard_path, real_path, wildcard,
1297 {g_free(real_path); return IMAP_ERROR;});
1298 QUOTE_IF_REQUIRED(wildcard_path, wildcard_path);
1300 imap_gen_send(session, "LIST \"\" %s",
1303 strtailchomp(real_path, separator);
1304 item_list = imap_parse_list(imapfolder, session, real_path, NULL);
1307 node = item->node->children;
1308 while (node != NULL) {
1309 FolderItem *old_item = FOLDER_ITEM(node->data);
1310 GNode *next = node->next;
1313 for (cur = item_list; cur != NULL; cur = cur->next) {
1314 FolderItem *cur_item = FOLDER_ITEM(cur->data);
1315 if (!strcmp2(old_item->path, cur_item->path)) {
1316 new_item = cur_item;
1321 debug_print("folder '%s' not found. removing...\n",
1323 folder_item_remove(old_item);
1325 old_item->no_sub = new_item->no_sub;
1326 old_item->no_select = new_item->no_select;
1327 if (old_item->no_sub == TRUE && node->children) {
1328 debug_print("folder '%s' doesn't have "
1329 "subfolders. removing...\n",
1331 folder_item_remove_children(old_item);
1338 for (cur = item_list; cur != NULL; cur = cur->next) {
1339 FolderItem *cur_item = FOLDER_ITEM(cur->data);
1341 for (node = item->node->children; node != NULL;
1342 node = node->next) {
1343 if (!strcmp2(FOLDER_ITEM(node->data)->path,
1345 new_item = FOLDER_ITEM(node->data);
1346 folder_item_destroy(cur_item);
1352 new_item = cur_item;
1353 debug_print("new folder '%s' found.\n", new_item->path);
1354 folder_item_append(item, new_item);
1357 if (!strcmp(new_item->path, "INBOX")) {
1358 new_item->stype = F_INBOX;
1359 folder->inbox = new_item;
1360 } else if (!item->parent || item->stype == F_INBOX) {
1363 base = g_basename(new_item->path);
1365 if (!folder->outbox && !strcasecmp(base, "Sent")) {
1366 new_item->stype = F_OUTBOX;
1367 folder->outbox = new_item;
1368 } else if (!folder->draft && !strcasecmp(base, "Drafts")) {
1369 new_item->stype = F_DRAFT;
1370 folder->draft = new_item;
1371 } else if (!folder->queue && !strcasecmp(base, "Queue")) {
1372 new_item->stype = F_QUEUE;
1373 folder->queue = new_item;
1374 } else if (!folder->trash && !strcasecmp(base, "Trash")) {
1375 new_item->stype = F_TRASH;
1376 folder->trash = new_item;
1380 if (new_item->no_sub == FALSE)
1381 imap_scan_tree_recursive(session, new_item);
1384 g_slist_free(item_list);
1386 return IMAP_SUCCESS;
1389 static GSList *imap_parse_list(IMAPFolder *folder, IMAPSession *session,
1390 const gchar *real_path, gchar *separator)
1392 gchar buf[IMAPBUFSIZE];
1394 gchar separator_str[16];
1397 gchar *loc_name, *loc_path;
1398 GSList *item_list = NULL;
1400 FolderItem *new_item;
1402 debug_print("getting list of %s ...\n",
1403 *real_path ? real_path : "\"\"");
1405 str = g_string_new(NULL);
1408 if (sock_gets(SESSION(session)->sock, buf, sizeof(buf)) <= 0) {
1409 log_warning(_("error occurred while getting LIST.\n"));
1413 if (buf[0] != '*' || buf[1] != ' ') {
1414 log_print("IMAP4< %s\n", buf);
1415 if (sscanf(buf, "%*d %16s", buf) < 1 ||
1416 strcmp(buf, "OK") != 0)
1417 log_warning(_("error occurred while getting LIST.\n"));
1421 debug_print("IMAP4< %s\n", buf);
1423 g_string_assign(str, buf);
1425 if (strncmp(p, "LIST ", 5) != 0) continue;
1428 if (*p != '(') continue;
1430 p = strchr_cpy(p, ')', flags, sizeof(flags));
1432 while (*p == ' ') p++;
1434 p = strchr_cpy(p, ' ', separator_str, sizeof(separator_str));
1436 extract_quote(separator_str, '"');
1437 if (!strcmp(separator_str, "NIL"))
1438 separator_str[0] = '\0';
1440 *separator = separator_str[0];
1443 while (*p == ' ') p++;
1444 if (*p == '{' || *p == '"')
1445 p = imap_parse_atom(SESSION(session)->sock, p,
1446 buf, sizeof(buf), str);
1448 strncpy2(buf, p, sizeof(buf));
1449 strtailchomp(buf, separator_str[0]);
1450 if (buf[0] == '\0') continue;
1451 if (!strcmp(buf, real_path)) continue;
1453 if (separator_str[0] != '\0')
1454 subst_char(buf, separator_str[0], '/');
1455 name = g_basename(buf);
1456 if (name[0] == '.') continue;
1458 loc_name = imap_modified_utf7_to_locale(name);
1459 loc_path = imap_modified_utf7_to_locale(buf);
1460 new_item = folder_item_new(FOLDER(folder), loc_name, loc_path);
1461 if (strcasestr(flags, "\\Noinferiors") != NULL)
1462 new_item->no_sub = TRUE;
1463 if (strcmp(buf, "INBOX") != 0 &&
1464 strcasestr(flags, "\\Noselect") != NULL)
1465 new_item->no_select = TRUE;
1467 item_list = g_slist_append(item_list, new_item);
1469 debug_print("folder '%s' found.\n", loc_path);
1474 g_string_free(str, TRUE);
1479 static gint imap_create_tree(Folder *folder)
1481 g_return_val_if_fail(folder != NULL, -1);
1482 g_return_val_if_fail(folder->node != NULL, -1);
1483 g_return_val_if_fail(folder->node->data != NULL, -1);
1484 g_return_val_if_fail(folder->account != NULL, -1);
1486 imap_scan_tree(folder);
1487 imap_create_missing_folders(folder);
1492 static void imap_create_missing_folders(Folder *folder)
1494 g_return_if_fail(folder != NULL);
1497 folder->inbox = imap_create_special_folder
1498 (folder, F_INBOX, "INBOX");
1500 if (!folder->outbox)
1501 folder->outbox = imap_create_special_folder
1502 (folder, F_OUTBOX, "Sent");
1504 folder->draft = imap_create_special_folder
1505 (folder, F_DRAFT, "Drafts");
1507 folder->queue = imap_create_special_folder
1508 (folder, F_QUEUE, "Queue");
1511 folder->trash = imap_create_special_folder
1512 (folder, F_TRASH, "Trash");
1515 static FolderItem *imap_create_special_folder(Folder *folder,
1516 SpecialFolderItemType stype,
1520 FolderItem *new_item;
1522 g_return_val_if_fail(folder != NULL, NULL);
1523 g_return_val_if_fail(folder->node != NULL, NULL);
1524 g_return_val_if_fail(folder->node->data != NULL, NULL);
1525 g_return_val_if_fail(folder->account != NULL, NULL);
1526 g_return_val_if_fail(name != NULL, NULL);
1528 item = FOLDER_ITEM(folder->node->data);
1529 new_item = imap_create_folder(folder, item, name);
1532 g_warning("Can't create '%s'\n", name);
1533 if (!folder->inbox) return NULL;
1535 new_item = imap_create_folder(folder, folder->inbox, name);
1537 g_warning("Can't create '%s' under INBOX\n", name);
1539 new_item->stype = stype;
1541 new_item->stype = stype;
1546 static gchar *imap_folder_get_path(Folder *folder)
1550 g_return_val_if_fail(folder != NULL, NULL);
1551 g_return_val_if_fail(folder->account != NULL, NULL);
1553 folder_path = g_strconcat(get_imap_cache_dir(),
1555 folder->account->recv_server,
1557 folder->account->userid,
1563 static gchar *imap_item_get_path(Folder *folder, FolderItem *item)
1565 gchar *folder_path, *path;
1567 g_return_val_if_fail(folder != NULL, NULL);
1568 g_return_val_if_fail(item != NULL, NULL);
1569 folder_path = imap_folder_get_path(folder);
1571 g_return_val_if_fail(folder_path != NULL, NULL);
1572 if (folder_path[0] == G_DIR_SEPARATOR) {
1574 path = g_strconcat(folder_path, G_DIR_SEPARATOR_S,
1577 path = g_strdup(folder_path);
1580 path = g_strconcat(get_home_dir(), G_DIR_SEPARATOR_S,
1581 folder_path, G_DIR_SEPARATOR_S,
1584 path = g_strconcat(get_home_dir(), G_DIR_SEPARATOR_S,
1587 g_free(folder_path);
1592 static FolderItem *imap_create_folder(Folder *folder, FolderItem *parent,
1595 gchar *dirpath, *imap_path;
1596 IMAPSession *session;
1597 FolderItem *new_item;
1603 g_return_val_if_fail(folder != NULL, NULL);
1604 g_return_val_if_fail(folder->account != NULL, NULL);
1605 g_return_val_if_fail(parent != NULL, NULL);
1606 g_return_val_if_fail(name != NULL, NULL);
1608 session = imap_session_get(folder);
1609 if (!session) return NULL;
1611 if (!parent->parent && strcmp(name, "INBOX") == 0)
1612 dirpath = g_strdup(name);
1613 else if (parent->path)
1614 dirpath = g_strconcat(parent->path, "/", name, NULL);
1615 else if ((p = strchr(name, '/')) != NULL && *(p + 1) != '\0')
1616 dirpath = g_strdup(name);
1617 else if (folder->account->imap_dir && *folder->account->imap_dir) {
1620 Xstrdup_a(imap_dir, folder->account->imap_dir, return NULL);
1621 strtailchomp(imap_dir, '/');
1622 dirpath = g_strconcat(imap_dir, "/", name, NULL);
1624 dirpath = g_strdup(name);
1626 /* keep trailing directory separator to create a folder that contains
1628 imap_path = imap_locale_to_modified_utf7(dirpath);
1629 strtailchomp(dirpath, '/');
1630 Xstrdup_a(new_name, name, {g_free(dirpath); return NULL;});
1631 strtailchomp(new_name, '/');
1632 separator = imap_get_path_separator(IMAP_FOLDER(folder), imap_path);
1633 imap_path_separator_subst(imap_path, separator);
1634 subst_char(new_name, '/', separator);
1636 if (strcmp(name, "INBOX") != 0) {
1639 gboolean exist = FALSE;
1641 argbuf = g_ptr_array_new();
1642 ok = imap_cmd_list(session, NULL, imap_path,
1644 if (ok != IMAP_SUCCESS) {
1645 log_warning(_("can't create mailbox: LIST failed\n"));
1648 ptr_array_free_strings(argbuf);
1649 g_ptr_array_free(argbuf, TRUE);
1653 for (i = 0; i < argbuf->len; i++) {
1655 str = g_ptr_array_index(argbuf, i);
1656 if (!strncmp(str, "LIST ", 5)) {
1661 ptr_array_free_strings(argbuf);
1662 g_ptr_array_free(argbuf, TRUE);
1665 ok = imap_cmd_create(session, imap_path);
1666 if (ok != IMAP_SUCCESS) {
1667 log_warning(_("can't create mailbox\n"));
1675 new_item = folder_item_new(folder, new_name, dirpath);
1676 folder_item_append(parent, new_item);
1680 dirpath = folder_item_get_path(new_item);
1681 if (!is_dir_exist(dirpath))
1682 make_dir_hier(dirpath);
1688 static gint imap_rename_folder(Folder *folder, FolderItem *item,
1693 gchar *real_oldpath;
1694 gchar *real_newpath;
1696 gchar *old_cache_dir;
1697 gchar *new_cache_dir;
1698 IMAPSession *session;
1701 gint exists, recent, unseen;
1702 guint32 uid_validity;
1704 g_return_val_if_fail(folder != NULL, -1);
1705 g_return_val_if_fail(item != NULL, -1);
1706 g_return_val_if_fail(item->path != NULL, -1);
1707 g_return_val_if_fail(name != NULL, -1);
1709 session = imap_session_get(folder);
1710 if (!session) return -1;
1712 real_oldpath = imap_get_real_path(IMAP_FOLDER(folder), item->path);
1714 g_free(session->mbox);
1715 session->mbox = NULL;
1716 ok = imap_cmd_examine(session, "INBOX",
1717 &exists, &recent, &unseen, &uid_validity);
1718 if (ok != IMAP_SUCCESS) {
1719 g_free(real_oldpath);
1723 separator = imap_get_path_separator(IMAP_FOLDER(folder), item->path);
1724 if (strchr(item->path, G_DIR_SEPARATOR)) {
1725 dirpath = g_dirname(item->path);
1726 newpath = g_strconcat(dirpath, G_DIR_SEPARATOR_S, name, NULL);
1729 newpath = g_strdup(name);
1731 real_newpath = imap_locale_to_modified_utf7(newpath);
1732 imap_path_separator_subst(real_newpath, separator);
1734 ok = imap_cmd_rename(session, real_oldpath, real_newpath);
1735 if (ok != IMAP_SUCCESS) {
1736 log_warning(_("can't rename mailbox: %s to %s\n"),
1737 real_oldpath, real_newpath);
1738 g_free(real_oldpath);
1740 g_free(real_newpath);
1745 item->name = g_strdup(name);
1747 old_cache_dir = folder_item_get_path(item);
1749 paths[0] = g_strdup(item->path);
1751 g_node_traverse(item->node, G_PRE_ORDER, G_TRAVERSE_ALL, -1,
1752 imap_rename_folder_func, paths);
1754 if (is_dir_exist(old_cache_dir)) {
1755 new_cache_dir = folder_item_get_path(item);
1756 if (rename(old_cache_dir, new_cache_dir) < 0) {
1757 FILE_OP_ERROR(old_cache_dir, "rename");
1759 g_free(new_cache_dir);
1762 g_free(old_cache_dir);
1765 g_free(real_oldpath);
1766 g_free(real_newpath);
1771 static gint imap_remove_folder(Folder *folder, FolderItem *item)
1774 IMAPSession *session;
1777 gint exists, recent, unseen;
1778 guint32 uid_validity;
1780 g_return_val_if_fail(folder != NULL, -1);
1781 g_return_val_if_fail(item != NULL, -1);
1782 g_return_val_if_fail(item->path != NULL, -1);
1784 session = imap_session_get(folder);
1785 if (!session) return -1;
1787 path = imap_get_real_path(IMAP_FOLDER(folder), item->path);
1789 ok = imap_cmd_examine(session, "INBOX",
1790 &exists, &recent, &unseen, &uid_validity);
1791 if (ok != IMAP_SUCCESS) {
1796 ok = imap_cmd_delete(session, path);
1797 if (ok != IMAP_SUCCESS) {
1798 log_warning(_("can't delete mailbox\n"));
1804 cache_dir = folder_item_get_path(item);
1805 if (is_dir_exist(cache_dir) && remove_dir_recursive(cache_dir) < 0)
1806 g_warning("can't remove directory '%s'\n", cache_dir);
1808 folder_item_remove(item);
1813 static GSList *imap_get_uncached_messages(IMAPSession *session,
1815 MsgNumberList *numlist)
1818 GSList *newlist = NULL;
1819 GSList *llast = NULL;
1822 GSList *seq_list, *cur;
1825 g_return_val_if_fail(session != NULL, NULL);
1826 g_return_val_if_fail(item != NULL, NULL);
1827 g_return_val_if_fail(item->folder != NULL, NULL);
1828 g_return_val_if_fail(FOLDER_CLASS(item->folder) == &imap_class, NULL);
1830 seq_list = imap_get_seq_set_from_numlist(numlist);
1831 for (cur = seq_list; cur != NULL; cur = g_slist_next(cur)) {
1832 imapset = cur->data;
1834 if (imap_cmd_envelope(session, imapset)
1836 log_warning(_("can't get envelope\n"));
1840 str = g_string_new(NULL);
1843 if ((tmp = sock_getline(SESSION(session)->sock)) == NULL) {
1844 log_warning(_("error occurred while getting envelope.\n"));
1845 g_string_free(str, TRUE);
1849 if (tmp[0] != '*' || tmp[1] != ' ') {
1850 log_print("IMAP4< %s\n", tmp);
1854 if (strstr(tmp, "FETCH") == NULL) {
1855 log_print("IMAP4< %s\n", tmp);
1859 log_print("IMAP4< %s\n", tmp);
1860 g_string_assign(str, tmp);
1863 msginfo = imap_parse_envelope
1864 (SESSION(session)->sock, item, str);
1866 log_warning(_("can't parse envelope: %s\n"), str->str);
1869 if (item->stype == F_QUEUE) {
1870 MSG_SET_TMP_FLAGS(msginfo->flags, MSG_QUEUED);
1871 } else if (item->stype == F_DRAFT) {
1872 MSG_SET_TMP_FLAGS(msginfo->flags, MSG_DRAFT);
1875 msginfo->folder = item;
1878 llast = newlist = g_slist_append(newlist, msginfo);
1880 llast = g_slist_append(llast, msginfo);
1881 llast = llast->next;
1885 g_string_free(str, TRUE);
1887 imap_seq_set_free(seq_list);
1892 static void imap_delete_all_cached_messages(FolderItem *item)
1896 g_return_if_fail(item != NULL);
1897 g_return_if_fail(item->folder != NULL);
1898 g_return_if_fail(FOLDER_CLASS(item->folder) == &imap_class);
1900 debug_print("Deleting all cached messages...\n");
1902 dir = folder_item_get_path(item);
1903 if (is_dir_exist(dir))
1904 remove_all_numbered_files(dir);
1907 debug_print("done.\n");
1911 static SockInfo *imap_open_tunnel(const gchar *server,
1912 const gchar *tunnelcmd,
1915 static SockInfo *imap_open_tunnel(const gchar *server,
1916 const gchar *tunnelcmd)
1921 if ((sock = sock_connect_cmd(server, tunnelcmd)) == NULL) {
1922 log_warning(_("Can't establish IMAP4 session with: %s\n"),
1927 return imap_init_sock(sock, ssl_type);
1929 return imap_init_sock(sock);
1935 static SockInfo *imap_open(const gchar *server, gushort port,
1938 static SockInfo *imap_open(const gchar *server, gushort port)
1943 if ((sock = sock_connect(server, port)) == NULL) {
1944 log_warning(_("Can't connect to IMAP4 server: %s:%d\n"),
1950 if (ssl_type == SSL_TUNNEL && !ssl_init_socket(sock)) {
1951 log_warning(_("Can't establish IMAP4 session with: %s:%d\n"),
1961 static SockInfo *imap_init_sock(SockInfo *sock, SSLType ssl_type)
1963 static SockInfo *imap_init_sock(SockInfo *sock)
1970 static GList *imap_parse_namespace_str(gchar *str)
1975 IMAPNameSpace *namespace;
1976 GList *ns_list = NULL;
1978 while (*p != '\0') {
1979 /* parse ("#foo" "/") */
1981 while (*p && *p != '(') p++;
1982 if (*p == '\0') break;
1985 while (*p && *p != '"') p++;
1986 if (*p == '\0') break;
1990 while (*p && *p != '"') p++;
1991 if (*p == '\0') break;
1995 while (*p && isspace(*p)) p++;
1996 if (*p == '\0') break;
1997 if (strncmp(p, "NIL", 3) == 0)
1999 else if (*p == '"') {
2002 while (*p && *p != '"') p++;
2003 if (*p == '\0') break;
2008 while (*p && *p != ')') p++;
2009 if (*p == '\0') break;
2012 namespace = g_new(IMAPNameSpace, 1);
2013 namespace->name = g_strdup(name);
2014 namespace->separator = separator ? separator[0] : '\0';
2015 ns_list = g_list_append(ns_list, namespace);
2021 static void imap_parse_namespace(IMAPSession *session, IMAPFolder *folder)
2026 g_return_if_fail(session != NULL);
2027 g_return_if_fail(folder != NULL);
2029 if (folder->ns_personal != NULL ||
2030 folder->ns_others != NULL ||
2031 folder->ns_shared != NULL)
2034 if (!imap_has_capability(session, "NAMESPACE")) {
2035 imap_get_namespace_by_list(session, folder);
2039 if (imap_cmd_namespace(session, &ns_str)
2041 log_warning(_("can't get namespace\n"));
2045 str_array = strsplit_parenthesis(ns_str, '(', ')', 3);
2046 if (str_array == NULL) {
2048 imap_get_namespace_by_list(session, folder);
2052 folder->ns_personal = imap_parse_namespace_str(str_array[0]);
2053 if (str_array[0] && str_array[1])
2054 folder->ns_others = imap_parse_namespace_str(str_array[1]);
2055 if (str_array[0] && str_array[1] && str_array[2])
2056 folder->ns_shared = imap_parse_namespace_str(str_array[2]);
2057 g_strfreev(str_array);
2061 static void imap_get_namespace_by_list(IMAPSession *session, IMAPFolder *folder)
2063 GSList *item_list, *cur;
2064 gchar separator = '\0';
2065 IMAPNameSpace *namespace;
2067 g_return_if_fail(session != NULL);
2068 g_return_if_fail(folder != NULL);
2070 if (folder->ns_personal != NULL ||
2071 folder->ns_others != NULL ||
2072 folder->ns_shared != NULL)
2075 imap_gen_send(session, "LIST \"\" \"\"");
2076 item_list = imap_parse_list(folder, session, "", &separator);
2077 for (cur = item_list; cur != NULL; cur = cur->next)
2078 folder_item_destroy(FOLDER_ITEM(cur->data));
2079 g_slist_free(item_list);
2081 namespace = g_new(IMAPNameSpace, 1);
2082 namespace->name = g_strdup("");
2083 namespace->separator = separator;
2084 folder->ns_personal = g_list_append(NULL, namespace);
2087 static IMAPNameSpace *imap_find_namespace_from_list(GList *ns_list,
2090 IMAPNameSpace *namespace = NULL;
2091 gchar *tmp_path, *name;
2093 if (!path) path = "";
2095 for (; ns_list != NULL; ns_list = ns_list->next) {
2096 IMAPNameSpace *tmp_ns = ns_list->data;
2098 Xstrcat_a(tmp_path, path, "/", return namespace);
2099 Xstrdup_a(name, tmp_ns->name, return namespace);
2100 if (tmp_ns->separator && tmp_ns->separator != '/') {
2101 subst_char(tmp_path, tmp_ns->separator, '/');
2102 subst_char(name, tmp_ns->separator, '/');
2104 if (strncmp(tmp_path, name, strlen(name)) == 0)
2111 static IMAPNameSpace *imap_find_namespace(IMAPFolder *folder,
2114 IMAPNameSpace *namespace;
2116 g_return_val_if_fail(folder != NULL, NULL);
2118 namespace = imap_find_namespace_from_list(folder->ns_personal, path);
2119 if (namespace) return namespace;
2120 namespace = imap_find_namespace_from_list(folder->ns_others, path);
2121 if (namespace) return namespace;
2122 namespace = imap_find_namespace_from_list(folder->ns_shared, path);
2123 if (namespace) return namespace;
2128 static gchar imap_get_path_separator(IMAPFolder *folder, const gchar *path)
2130 IMAPNameSpace *namespace;
2131 gchar separator = '/';
2133 namespace = imap_find_namespace(folder, path);
2134 if (namespace && namespace->separator)
2135 separator = namespace->separator;
2140 static gchar *imap_get_real_path(IMAPFolder *folder, const gchar *path)
2145 g_return_val_if_fail(folder != NULL, NULL);
2146 g_return_val_if_fail(path != NULL, NULL);
2148 real_path = imap_locale_to_modified_utf7(path);
2149 separator = imap_get_path_separator(folder, path);
2150 imap_path_separator_subst(real_path, separator);
2155 static gchar *imap_parse_atom(SockInfo *sock, gchar *src,
2156 gchar *dest, gint dest_len, GString *str)
2158 gchar *cur_pos = src;
2161 g_return_val_if_fail(str != NULL, cur_pos);
2163 /* read the next line if the current response buffer is empty */
2164 while (isspace(*cur_pos)) cur_pos++;
2165 while (*cur_pos == '\0') {
2166 if ((nextline = sock_getline(sock)) == NULL)
2168 g_string_assign(str, nextline);
2170 strretchomp(nextline);
2171 /* log_print("IMAP4< %s\n", nextline); */
2172 debug_print("IMAP4< %s\n", nextline);
2175 while (isspace(*cur_pos)) cur_pos++;
2178 if (!strncmp(cur_pos, "NIL", 3)) {
2181 } else if (*cur_pos == '\"') {
2184 p = get_quoted(cur_pos, '\"', dest, dest_len);
2185 cur_pos = p ? p : cur_pos + 2;
2186 } else if (*cur_pos == '{') {
2191 cur_pos = strchr_cpy(cur_pos + 1, '}', buf, sizeof(buf));
2193 g_return_val_if_fail(len >= 0, cur_pos);
2195 g_string_truncate(str, 0);
2199 if ((nextline = sock_getline(sock)) == NULL)
2201 line_len += strlen(nextline);
2202 g_string_append(str, nextline);
2204 strretchomp(nextline);
2205 /* log_print("IMAP4< %s\n", nextline); */
2206 debug_print("IMAP4< %s\n", nextline);
2208 } while (line_len < len);
2210 memcpy(dest, cur_pos, MIN(len, dest_len - 1));
2211 dest[MIN(len, dest_len - 1)] = '\0';
2218 static gchar *imap_get_header(SockInfo *sock, gchar *cur_pos, gchar **headers,
2228 g_return_val_if_fail(str != NULL, cur_pos);
2230 while (isspace(*cur_pos)) cur_pos++;
2232 g_return_val_if_fail(*cur_pos == '{', cur_pos);
2234 cur_pos = strchr_cpy(cur_pos + 1, '}', buf, sizeof(buf));
2236 g_return_val_if_fail(len >= 0, cur_pos);
2238 g_string_truncate(str, 0);
2242 if ((nextline = sock_getline(sock)) == NULL)
2244 block_len += strlen(nextline);
2245 g_string_append(str, nextline);
2247 strretchomp(nextline);
2248 /* debug_print("IMAP4< %s\n", nextline); */
2250 } while (block_len < len);
2252 debug_print("IMAP4< [contents of BODY.PEEK[HEADER.FIELDS (...)]]\n");
2254 *headers = g_strndup(cur_pos, len);
2257 while (isspace(*cur_pos)) cur_pos++;
2258 while (*cur_pos == '\0') {
2259 if ((nextline = sock_getline(sock)) == NULL)
2261 g_string_assign(str, nextline);
2263 strretchomp(nextline);
2264 debug_print("IMAP4< %s\n", nextline);
2267 while (isspace(*cur_pos)) cur_pos++;
2273 static MsgFlags imap_parse_flags(const gchar *flag_str)
2275 const gchar *p = flag_str;
2276 MsgFlags flags = {0, 0};
2278 flags.perm_flags = MSG_UNREAD;
2280 while ((p = strchr(p, '\\')) != NULL) {
2283 if (g_strncasecmp(p, "Recent", 6) == 0 && MSG_IS_UNREAD(flags)) {
2284 MSG_SET_PERM_FLAGS(flags, MSG_NEW);
2285 } else if (g_strncasecmp(p, "Seen", 4) == 0) {
2286 MSG_UNSET_PERM_FLAGS(flags, MSG_NEW|MSG_UNREAD);
2287 } else if (g_strncasecmp(p, "Deleted", 7) == 0) {
2288 MSG_SET_PERM_FLAGS(flags, MSG_DELETED);
2289 } else if (g_strncasecmp(p, "Flagged", 7) == 0) {
2290 MSG_SET_PERM_FLAGS(flags, MSG_MARKED);
2291 } else if (g_strncasecmp(p, "Answered", 8) == 0) {
2292 MSG_SET_PERM_FLAGS(flags, MSG_REPLIED);
2299 static MsgInfo *imap_parse_envelope(SockInfo *sock, FolderItem *item,
2302 gchar buf[IMAPBUFSIZE];
2303 MsgInfo *msginfo = NULL;
2308 MsgFlags flags = {0, 0}, imap_flags = {0, 0};
2310 g_return_val_if_fail(line_str != NULL, NULL);
2311 g_return_val_if_fail(line_str->str[0] == '*' &&
2312 line_str->str[1] == ' ', NULL);
2314 MSG_SET_TMP_FLAGS(flags, MSG_IMAP);
2315 if (item->stype == F_QUEUE) {
2316 MSG_SET_TMP_FLAGS(flags, MSG_QUEUED);
2317 } else if (item->stype == F_DRAFT) {
2318 MSG_SET_TMP_FLAGS(flags, MSG_DRAFT);
2321 cur_pos = line_str->str + 2;
2323 #define PARSE_ONE_ELEMENT(ch) \
2325 cur_pos = strchr_cpy(cur_pos, ch, buf, sizeof(buf)); \
2326 if (cur_pos == NULL) { \
2327 g_warning("cur_pos == NULL\n"); \
2328 procmsg_msginfo_free(msginfo); \
2333 PARSE_ONE_ELEMENT(' ');
2336 PARSE_ONE_ELEMENT(' ');
2337 g_return_val_if_fail(!strcmp(buf, "FETCH"), NULL);
2339 g_return_val_if_fail(*cur_pos == '(', NULL);
2342 while (*cur_pos != '\0' && *cur_pos != ')') {
2343 while (*cur_pos == ' ') cur_pos++;
2345 if (!strncmp(cur_pos, "UID ", 4)) {
2347 uid = strtoul(cur_pos, &cur_pos, 10);
2348 } else if (!strncmp(cur_pos, "FLAGS ", 6)) {
2350 if (*cur_pos != '(') {
2351 g_warning("*cur_pos != '('\n");
2352 procmsg_msginfo_free(msginfo);
2356 PARSE_ONE_ELEMENT(')');
2357 imap_flags = imap_parse_flags(buf);
2358 } else if (!strncmp(cur_pos, "RFC822.SIZE ", 12)) {
2360 size = strtol(cur_pos, &cur_pos, 10);
2361 } else if (!strncmp(cur_pos, "BODY[HEADER.FIELDS ", 19)) {
2365 if (*cur_pos != '(') {
2366 g_warning("*cur_pos != '('\n");
2367 procmsg_msginfo_free(msginfo);
2371 PARSE_ONE_ELEMENT(')');
2372 if (*cur_pos != ']') {
2373 g_warning("*cur_pos != ']'\n");
2374 procmsg_msginfo_free(msginfo);
2379 cur_pos = imap_get_header(sock, cur_pos, &headers,
2381 msginfo = procheader_parse_str(headers, flags, FALSE, FALSE);
2384 g_warning("invalid FETCH response: %s\n", cur_pos);
2390 msginfo->msgnum = uid;
2391 msginfo->size = size;
2392 msginfo->flags.tmp_flags |= imap_flags.tmp_flags;
2393 msginfo->flags.perm_flags = imap_flags.perm_flags;
2399 static gchar *imap_get_flag_str(IMAPFlags flags)
2404 str = g_string_new(NULL);
2406 if (IMAP_IS_SEEN(flags)) g_string_append(str, "\\Seen ");
2407 if (IMAP_IS_ANSWERED(flags)) g_string_append(str, "\\Answered ");
2408 if (IMAP_IS_FLAGGED(flags)) g_string_append(str, "\\Flagged ");
2409 if (IMAP_IS_DELETED(flags)) g_string_append(str, "\\Deleted ");
2410 if (IMAP_IS_DRAFT(flags)) g_string_append(str, "\\Draft");
2412 if (str->len > 0 && str->str[str->len - 1] == ' ')
2413 g_string_truncate(str, str->len - 1);
2416 g_string_free(str, FALSE);
2421 static gint imap_set_message_flags(IMAPSession *session,
2422 MsgNumberList *numlist,
2429 GSList *seq_list, *cur;
2432 flag_str = imap_get_flag_str(flags);
2433 cmd = g_strconcat(is_set ? "+FLAGS.SILENT (" : "-FLAGS.SILENT (",
2434 flag_str, ")", NULL);
2437 seq_list = imap_get_seq_set_from_numlist(numlist);
2438 for (cur = seq_list; cur != NULL; cur = g_slist_next(cur)) {
2439 imapset = cur->data;
2441 ok = imap_cmd_store(session, imapset, cmd);
2443 imap_seq_set_free(seq_list);
2449 static gint imap_select(IMAPSession *session, IMAPFolder *folder,
2451 gint *exists, gint *recent, gint *unseen,
2452 guint32 *uid_validity)
2456 gint exists_, recent_, unseen_, uid_validity_;
2458 if (!exists || !recent || !unseen || !uid_validity) {
2459 if (session->mbox && strcmp(session->mbox, path) == 0)
2460 return IMAP_SUCCESS;
2464 uid_validity = &uid_validity_;
2467 g_free(session->mbox);
2468 session->mbox = NULL;
2470 real_path = imap_get_real_path(folder, path);
2471 ok = imap_cmd_select(session, real_path,
2472 exists, recent, unseen, uid_validity);
2473 if (ok != IMAP_SUCCESS)
2474 log_warning(_("can't select folder: %s\n"), real_path);
2476 session->mbox = g_strdup(path);
2477 session->folder_content_changed = FALSE;
2484 #define THROW(err) { ok = err; goto catch; }
2486 static gint imap_status(IMAPSession *session, IMAPFolder *folder,
2488 gint *messages, gint *recent,
2489 guint32 *uid_next, guint32 *uid_validity,
2495 GPtrArray *argbuf = NULL;
2498 if (messages && recent && uid_next && uid_validity && unseen) {
2499 *messages = *recent = *uid_next = *uid_validity = *unseen = 0;
2500 argbuf = g_ptr_array_new();
2503 real_path = imap_get_real_path(folder, path);
2504 QUOTE_IF_REQUIRED(real_path_, real_path);
2505 imap_gen_send(session, "STATUS %s "
2506 "(MESSAGES RECENT UIDNEXT UIDVALIDITY UNSEEN)",
2509 ok = imap_cmd_ok(session, argbuf);
2510 if (ok != IMAP_SUCCESS || !argbuf) THROW(ok);
2512 str = search_array_str(argbuf, "STATUS");
2513 if (!str) THROW(IMAP_ERROR);
2515 str = strchr(str, '(');
2516 if (!str) THROW(IMAP_ERROR);
2518 while (*str != '\0' && *str != ')') {
2519 while (*str == ' ') str++;
2521 if (!strncmp(str, "MESSAGES ", 9)) {
2523 *messages = strtol(str, &str, 10);
2524 } else if (!strncmp(str, "RECENT ", 7)) {
2526 *recent = strtol(str, &str, 10);
2527 } else if (!strncmp(str, "UIDNEXT ", 8)) {
2529 *uid_next = strtoul(str, &str, 10);
2530 } else if (!strncmp(str, "UIDVALIDITY ", 12)) {
2532 *uid_validity = strtoul(str, &str, 10);
2533 } else if (!strncmp(str, "UNSEEN ", 7)) {
2535 *unseen = strtol(str, &str, 10);
2537 g_warning("invalid STATUS response: %s\n", str);
2545 ptr_array_free_strings(argbuf);
2546 g_ptr_array_free(argbuf, TRUE);
2554 static gboolean imap_has_capability(IMAPSession *session, const gchar *cap)
2558 for (p = session->capability; *p != NULL; ++p) {
2559 if (!g_strcasecmp(*p, cap))
2566 static void imap_free_capabilities(IMAPSession *session)
2568 g_strfreev(session->capability);
2569 session->capability = NULL;
2572 /* low-level IMAP4rev1 commands */
2574 static gint imap_cmd_authenticate(IMAPSession *session, const gchar *user,
2575 const gchar *pass, IMAPAuthType type)
2582 gchar hexdigest[33];
2586 auth_type = "CRAM-MD5";
2588 imap_gen_send(session, "AUTHENTICATE %s", auth_type);
2589 ok = imap_gen_recv(session, &buf);
2590 if (ok != IMAP_SUCCESS || buf[0] != '+' || buf[1] != ' ') {
2595 challenge = g_malloc(strlen(buf + 2) + 1);
2596 challenge_len = base64_decode(challenge, buf + 2, -1);
2597 challenge[challenge_len] = '\0';
2599 log_print("IMAP< [Decoded: %s]\n", challenge);
2601 md5_hex_hmac(hexdigest, challenge, challenge_len, pass, strlen(pass));
2604 response = g_strdup_printf("%s %s", user, hexdigest);
2605 log_print("IMAP> [Encoded: %s]\n", response);
2606 response64 = g_malloc((strlen(response) + 3) * 2 + 1);
2607 base64_encode(response64, response, strlen(response));
2610 log_print("IMAP> %s\n", response64);
2611 sock_puts(SESSION(session)->sock, response64);
2612 ok = imap_cmd_ok(session, NULL);
2613 if (ok != IMAP_SUCCESS)
2614 log_warning(_("IMAP4 authentication failed.\n"));
2619 static gint imap_cmd_login(IMAPSession *session,
2620 const gchar *user, const gchar *pass)
2622 gchar *user_, *pass_;
2625 QUOTE_IF_REQUIRED(user_, user);
2626 QUOTE_IF_REQUIRED(pass_, pass);
2627 imap_gen_send(session, "LOGIN %s %s", user_, pass_);
2629 ok = imap_cmd_ok(session, NULL);
2630 if (ok != IMAP_SUCCESS)
2631 log_warning(_("IMAP4 login failed.\n"));
2636 static gint imap_cmd_logout(IMAPSession *session)
2638 imap_gen_send(session, "LOGOUT");
2639 return imap_cmd_ok(session, NULL);
2642 static gint imap_cmd_noop(IMAPSession *session)
2644 imap_gen_send(session, "NOOP");
2645 return imap_cmd_ok(session, NULL);
2648 static gint imap_cmd_starttls(IMAPSession *session)
2650 imap_gen_send(session, "STARTTLS");
2651 return imap_cmd_ok(session, NULL);
2654 #define THROW(err) { ok = err; goto catch; }
2656 static gint imap_cmd_namespace(IMAPSession *session, gchar **ns_str)
2662 argbuf = g_ptr_array_new();
2664 imap_gen_send(session, "NAMESPACE");
2665 if ((ok = imap_cmd_ok(session, argbuf)) != IMAP_SUCCESS) THROW(ok);
2667 str = search_array_str(argbuf, "NAMESPACE");
2668 if (!str) THROW(IMAP_ERROR);
2670 *ns_str = g_strdup(str);
2673 ptr_array_free_strings(argbuf);
2674 g_ptr_array_free(argbuf, TRUE);
2681 static gint imap_cmd_list(IMAPSession *session, const gchar *ref,
2682 const gchar *mailbox, GPtrArray *argbuf)
2684 gchar *ref_, *mailbox_;
2686 if (!ref) ref = "\"\"";
2687 if (!mailbox) mailbox = "\"\"";
2689 QUOTE_IF_REQUIRED(ref_, ref);
2690 QUOTE_IF_REQUIRED(mailbox_, mailbox);
2691 imap_gen_send(session, "LIST %s %s", ref_, mailbox_);
2693 return imap_cmd_ok(session, argbuf);
2696 #define THROW goto catch
2698 static gint imap_cmd_do_select(IMAPSession *session, const gchar *folder,
2700 gint *exists, gint *recent, gint *unseen,
2701 guint32 *uid_validity)
2709 *exists = *recent = *unseen = *uid_validity = 0;
2710 argbuf = g_ptr_array_new();
2713 select_cmd = "EXAMINE";
2715 select_cmd = "SELECT";
2717 QUOTE_IF_REQUIRED(folder_, folder);
2718 imap_gen_send(session, "%s %s", select_cmd, folder_);
2720 if ((ok = imap_cmd_ok(session, argbuf)) != IMAP_SUCCESS) THROW;
2722 resp_str = search_array_contain_str(argbuf, "EXISTS");
2724 if (sscanf(resp_str,"%d EXISTS", exists) != 1) {
2725 g_warning("imap_cmd_select(): invalid EXISTS line.\n");
2730 resp_str = search_array_contain_str(argbuf, "RECENT");
2732 if (sscanf(resp_str, "%d RECENT", recent) != 1) {
2733 g_warning("imap_cmd_select(): invalid RECENT line.\n");
2738 resp_str = search_array_contain_str(argbuf, "UIDVALIDITY");
2740 if (sscanf(resp_str, "OK [UIDVALIDITY %u] ", uid_validity)
2742 g_warning("imap_cmd_select(): invalid UIDVALIDITY line.\n");
2747 resp_str = search_array_contain_str(argbuf, "UNSEEN");
2749 if (sscanf(resp_str, "OK [UNSEEN %d] ", unseen) != 1) {
2750 g_warning("imap_cmd_select(): invalid UNSEEN line.\n");
2756 ptr_array_free_strings(argbuf);
2757 g_ptr_array_free(argbuf, TRUE);
2762 static gint imap_cmd_select(IMAPSession *session, const gchar *folder,
2763 gint *exists, gint *recent, gint *unseen,
2764 guint32 *uid_validity)
2766 return imap_cmd_do_select(session, folder, FALSE,
2767 exists, recent, unseen, uid_validity);
2770 static gint imap_cmd_examine(IMAPSession *session, const gchar *folder,
2771 gint *exists, gint *recent, gint *unseen,
2772 guint32 *uid_validity)
2774 return imap_cmd_do_select(session, folder, TRUE,
2775 exists, recent, unseen, uid_validity);
2780 static gint imap_cmd_create(IMAPSession *session, const gchar *folder)
2784 QUOTE_IF_REQUIRED(folder_, folder);
2785 imap_gen_send(session, "CREATE %s", folder_);
2787 return imap_cmd_ok(session, NULL);
2790 static gint imap_cmd_rename(IMAPSession *session, const gchar *old_folder,
2791 const gchar *new_folder)
2793 gchar *old_folder_, *new_folder_;
2795 QUOTE_IF_REQUIRED(old_folder_, old_folder);
2796 QUOTE_IF_REQUIRED(new_folder_, new_folder);
2797 imap_gen_send(session, "RENAME %s %s", old_folder_, new_folder_);
2799 return imap_cmd_ok(session, NULL);
2802 static gint imap_cmd_delete(IMAPSession *session, const gchar *folder)
2806 QUOTE_IF_REQUIRED(folder_, folder);
2807 imap_gen_send(session, "DELETE %s", folder_);
2809 return imap_cmd_ok(session, NULL);
2812 static gint imap_cmd_search(IMAPSession *session, const gchar *criteria,
2819 g_return_val_if_fail(criteria != NULL, IMAP_ERROR);
2820 g_return_val_if_fail(list != NULL, IMAP_ERROR);
2824 argbuf = g_ptr_array_new();
2825 imap_gen_send(session, "UID SEARCH %s", criteria);
2827 ok = imap_cmd_ok(session, argbuf);
2828 if (ok != IMAP_SUCCESS) {
2829 ptr_array_free_strings(argbuf);
2830 g_ptr_array_free(argbuf, TRUE);
2834 if ((uidlist = search_array_str(argbuf, "SEARCH ")) != NULL) {
2835 gchar **strlist, **p;
2837 strlist = g_strsplit(uidlist + 7, " ", 0);
2838 for (p = strlist; *p != NULL; ++p) {
2841 if (sscanf(*p, "%d", &msgnum) == 1)
2842 *list = g_slist_append(*list, GINT_TO_POINTER(msgnum));
2844 g_strfreev(strlist);
2846 ptr_array_free_strings(argbuf);
2847 g_ptr_array_free(argbuf, TRUE);
2849 return IMAP_SUCCESS;
2852 static gint imap_cmd_fetch(IMAPSession *session, guint32 uid,
2853 const gchar *filename)
2861 g_return_val_if_fail(filename != NULL, IMAP_ERROR);
2863 imap_gen_send(session, "UID FETCH %d BODY.PEEK[]", uid);
2865 while ((ok = imap_gen_recv(session, &buf)) == IMAP_SUCCESS) {
2866 if (buf[0] != '*' || buf[1] != ' ') {
2870 if (strstr(buf, "FETCH") != NULL) break;
2873 if (ok != IMAP_SUCCESS) {
2878 #define RETURN_ERROR_IF_FAIL(cond) \
2881 return IMAP_ERROR; \
2884 cur_pos = strchr(buf, '{');
2885 RETURN_ERROR_IF_FAIL(cur_pos != NULL);
2886 cur_pos = strchr_cpy(cur_pos + 1, '}', size_str, sizeof(size_str));
2887 RETURN_ERROR_IF_FAIL(cur_pos != NULL);
2888 size_num = atol(size_str);
2889 RETURN_ERROR_IF_FAIL(size_num >= 0);
2891 RETURN_ERROR_IF_FAIL(*cur_pos == '\0');
2893 #undef RETURN_ERROR_IF_FAIL
2897 if (recv_bytes_write_to_file(SESSION(session)->sock,
2898 size_num, filename) != 0)
2901 if (imap_gen_recv(session, &buf) != IMAP_SUCCESS) {
2906 if (buf[0] == '\0' || buf[strlen(buf) - 1] != ')') {
2912 ok = imap_cmd_ok(session, NULL);
2917 static gint imap_cmd_append(IMAPSession *session, const gchar *destfolder,
2918 const gchar *file, IMAPFlags flags,
2927 gchar buf[BUFFSIZE];
2932 g_return_val_if_fail(file != NULL, IMAP_ERROR);
2934 size = get_file_size_as_crlf(file);
2935 if ((fp = fopen(file, "rb")) == NULL) {
2936 FILE_OP_ERROR(file, "fopen");
2939 QUOTE_IF_REQUIRED(destfolder_, destfolder);
2940 flag_str = imap_get_flag_str(flags);
2941 imap_gen_send(session, "APPEND %s (%s) {%d}",
2942 destfolder_, flag_str, size);
2945 ok = imap_gen_recv(session, &ret);
2946 if (ok != IMAP_SUCCESS || ret[0] != '+' || ret[1] != ' ') {
2947 log_warning(_("can't append %s to %s\n"), file, destfolder_);
2954 log_print("IMAP4> %s\n", _("(sending file...)"));
2956 while (fgets(buf, sizeof(buf), fp) != NULL) {
2958 if (sock_puts(SESSION(session)->sock, buf) < 0) {
2965 FILE_OP_ERROR(file, "fgets");
2970 sock_puts(SESSION(session)->sock, "");
2974 if (new_uid != NULL)
2977 if (new_uid != NULL && session->uidplus) {
2978 argbuf = g_ptr_array_new();
2980 ok = imap_cmd_ok(session, argbuf);
2981 if ((ok == IMAP_SUCCESS) && (argbuf->len > 0)) {
2982 resp_str = g_ptr_array_index(argbuf, argbuf->len - 1);
2984 sscanf(resp_str, "%*u OK [APPENDUID %*u %u]",
2986 *new_uid = new_uid_;
2990 ptr_array_free_strings(argbuf);
2991 g_ptr_array_free(argbuf, TRUE);
2993 ok = imap_cmd_ok(session, NULL);
2995 if (ok != IMAP_SUCCESS)
2996 log_warning(_("can't append message to %s\n"),
3002 static MsgNumberList *imapset_to_numlist(IMAPSet imapset)
3004 gchar **ranges, **range;
3006 MsgNumberList *uids = NULL;
3008 ranges = g_strsplit(imapset, ",", 0);
3009 for (range = ranges; *range != NULL; range++) {
3010 printf("%s\n", *range);
3011 if(sscanf(*range, "%u:%u", &low, &high) == 1)
3012 uids = g_slist_prepend(uids, GINT_TO_POINTER(low));
3015 for (i = low; i <= high; i++)
3016 uids = g_slist_prepend(uids, GINT_TO_POINTER(i));
3019 uids = g_slist_reverse(uids);
3025 static gint imap_cmd_copy(IMAPSession *session, const gchar *seq_set,
3026 const gchar *destfolder, GRelation *uid_mapping)
3031 g_return_val_if_fail(session != NULL, IMAP_ERROR);
3032 g_return_val_if_fail(seq_set != NULL, IMAP_ERROR);
3033 g_return_val_if_fail(destfolder != NULL, IMAP_ERROR);
3035 QUOTE_IF_REQUIRED(destfolder_, destfolder);
3036 imap_gen_send(session, "UID COPY %s %s", seq_set, destfolder_);
3038 if (uid_mapping != NULL && session->uidplus) {
3040 gchar *resp_str = NULL, *olduids_str, *newuids_str;
3041 MsgNumberList *olduids, *old_cur, *newuids, *new_cur;
3043 reply = g_ptr_array_new();
3044 ok = imap_cmd_ok(session, reply);
3045 if ((ok == IMAP_SUCCESS) && (reply->len > 0)) {
3046 resp_str = g_ptr_array_index(reply, reply->len - 1);
3048 olduids_str = g_new0(gchar, strlen(resp_str));
3049 newuids_str = g_new0(gchar, strlen(resp_str));
3050 if (sscanf(resp_str, "%*s OK [COPYUID %*u %[0-9,:] %[0-9,:]]",
3051 olduids_str, newuids_str) == 2) {
3052 olduids = imapset_to_numlist(olduids_str);
3053 newuids = imapset_to_numlist(newuids_str);
3057 while(old_cur != NULL && new_cur != NULL) {
3058 g_relation_insert(uid_mapping,
3059 GPOINTER_TO_INT(old_cur->data),
3060 GPOINTER_TO_INT(new_cur->data));
3061 old_cur = g_slist_next(old_cur);
3062 new_cur = g_slist_next(new_cur);
3065 g_slist_free(olduids);
3066 g_slist_free(newuids);
3068 g_free(olduids_str);
3069 g_free(newuids_str);
3072 ptr_array_free_strings(reply);
3073 g_ptr_array_free(reply, TRUE);
3075 ok = imap_cmd_ok(session, NULL);
3077 if (ok != IMAP_SUCCESS)
3078 log_warning(_("can't copy %s to %s\n"), seq_set, destfolder_);
3083 gint imap_cmd_envelope(IMAPSession *session, IMAPSet set)
3085 static GString *header_fields = NULL;
3087 if (header_fields == NULL) {
3088 const HeaderEntry *headers, *elem;
3090 headers = procheader_get_headernames(FALSE);
3091 header_fields = g_string_new("");
3093 for (elem = headers; elem->name != NULL; ++elem) {
3094 gint namelen = strlen(elem->name);
3096 /* Header fields ending with space are not rfc822 headers */
3097 if (elem->name[namelen - 1] == ' ')
3100 /* strip : at the of header field */
3101 if(elem->name[namelen - 1] == ':')
3107 g_string_sprintfa(header_fields, "%s%.*s",
3108 header_fields->str[0] != '\0' ? " " : "",
3109 namelen, elem->name);
3114 (session, "UID FETCH %s (UID FLAGS RFC822.SIZE BODY.PEEK[HEADER.FIELDS (%s)])",
3115 set, header_fields->str);
3117 return IMAP_SUCCESS;
3120 static gint imap_cmd_store(IMAPSession *session, IMAPSet seq_set,
3125 imap_gen_send(session, "UID STORE %s %s", seq_set, sub_cmd);
3127 if ((ok = imap_cmd_ok(session, NULL)) != IMAP_SUCCESS) {
3128 log_warning(_("error while imap command: STORE %s %s\n"),
3133 return IMAP_SUCCESS;
3136 static gint imap_cmd_expunge(IMAPSession *session, IMAPSet seq_set)
3140 if (seq_set && session->uidplus)
3141 imap_gen_send(session, "UID EXPUNGE %s", seq_set);
3143 imap_gen_send(session, "EXPUNGE");
3144 if ((ok = imap_cmd_ok(session, NULL)) != IMAP_SUCCESS) {
3145 log_warning(_("error while imap command: EXPUNGE\n"));
3149 return IMAP_SUCCESS;
3152 static gint imap_cmd_close(IMAPSession *session)
3156 imap_gen_send(session, "CLOSE");
3157 if ((ok = imap_cmd_ok(session, NULL)) != IMAP_SUCCESS)
3158 log_warning(_("error while imap command: CLOSE\n"));
3163 static gint imap_cmd_ok(IMAPSession *session, GPtrArray *argbuf)
3165 gint ok = IMAP_SUCCESS;
3170 while ((ok = imap_gen_recv(session, &buf))
3172 // make sure data is long enough for any substring of buf
3173 data = alloca(strlen(buf) + 1);
3175 // untagged line read
3176 if (buf[0] == '*' && buf[1] == ' ') {
3179 g_ptr_array_add(argbuf, g_strdup(buf + 2));
3181 if (sscanf(buf + 2, "%d %s", &num, data) >= 2) {
3182 if (!strcmp(data, "EXISTS")) {
3183 session->exists = num;
3184 session->folder_content_changed = TRUE;
3187 if(!strcmp(data, "EXPUNGE")) {
3189 session->folder_content_changed = TRUE;
3192 // tagged line with correct tag and OK response found
3193 } else if ((sscanf(buf, "%d %s", &cmd_num, data) >= 2) &&
3194 (cmd_num == session->cmd_count) &&
3195 !strcmp(data, "OK")) {
3197 g_ptr_array_add(argbuf, g_strdup(buf));
3211 static void imap_gen_send(IMAPSession *session, const gchar *format, ...)
3218 va_start(args, format);
3219 tmp = g_strdup_vprintf(format, args);
3222 session->cmd_count++;
3224 buf = g_strdup_printf("%d %s\r\n", session->cmd_count, tmp);
3225 if (!strncasecmp(tmp, "LOGIN ", 6) && (p = strchr(tmp + 6, ' '))) {
3227 log_print("IMAP4> %d %s ********\n", session->cmd_count, tmp);
3229 log_print("IMAP4> %d %s\n", session->cmd_count, tmp);
3231 sock_write_all(SESSION(session)->sock, buf, strlen(buf));
3236 static gint imap_gen_recv(IMAPSession *session, gchar **ret)
3238 if ((*ret = sock_getline(SESSION(session)->sock)) == NULL)
3243 log_print("IMAP4< %s\n", *ret);
3245 return IMAP_SUCCESS;
3249 /* misc utility functions */
3251 static gchar *strchr_cpy(const gchar *src, gchar ch, gchar *dest, gint len)
3256 tmp = strchr(src, ch);
3260 memcpy(dest, src, MIN(tmp - src, len - 1));
3261 dest[MIN(tmp - src, len - 1)] = '\0';
3266 static gchar *get_quoted(const gchar *src, gchar ch, gchar *dest, gint len)
3268 const gchar *p = src;
3271 g_return_val_if_fail(*p == ch, NULL);
3276 while (*p != '\0' && *p != ch) {
3278 if (*p == '\\' && *(p + 1) != '\0')
3287 return (gchar *)(*p == ch ? p + 1 : p);
3290 static gchar *search_array_contain_str(GPtrArray *array, const gchar *str)
3294 for (i = 0; i < array->len; i++) {
3297 tmp = g_ptr_array_index(array, i);
3298 if (strstr(tmp, str) != NULL)
3305 static gchar *search_array_str(GPtrArray *array, const gchar *str)
3312 for (i = 0; i < array->len; i++) {
3315 tmp = g_ptr_array_index(array, i);
3316 if (!strncmp(tmp, str, len))
3323 static void imap_path_separator_subst(gchar *str, gchar separator)
3326 gboolean in_escape = FALSE;
3328 if (!separator || separator == '/') return;
3330 for (p = str; *p != '\0'; p++) {
3331 if (*p == '/' && !in_escape)
3333 else if (*p == '&' && *(p + 1) != '-' && !in_escape)
3335 else if (*p == '-' && in_escape)
3340 static gchar *imap_modified_utf7_to_locale(const gchar *mutf7_str)
3343 const gchar *from_p;
3346 to = g_malloc(strlen(mutf7_str) + 1);
3349 for (from_p = mutf7_str; *from_p != '\0'; from_p++) {
3350 if (*from_p == '&' && *(from_p + 1) == '-') {
3360 static iconv_t cd = (iconv_t)-1;
3361 static gboolean iconv_ok = TRUE;
3364 size_t norm_utf7_len;
3366 gchar *to_str, *to_p;
3368 gboolean in_escape = FALSE;
3370 if (!iconv_ok) return g_strdup(mutf7_str);
3372 if (cd == (iconv_t)-1) {
3373 cd = iconv_open(conv_get_current_charset_str(), "UTF-7");
3374 if (cd == (iconv_t)-1) {
3375 g_warning("iconv cannot convert UTF-7 to %s\n",
3376 conv_get_current_charset_str());
3378 return g_strdup(mutf7_str);
3382 norm_utf7 = g_string_new(NULL);
3384 for (p = mutf7_str; *p != '\0'; p++) {
3385 /* replace: '&' -> '+',
3387 escaped ',' -> '/' */
3388 if (!in_escape && *p == '&') {
3389 if (*(p + 1) != '-') {
3390 g_string_append_c(norm_utf7, '+');
3393 g_string_append_c(norm_utf7, '&');
3396 } else if (in_escape && *p == ',') {
3397 g_string_append_c(norm_utf7, '/');
3398 } else if (in_escape && *p == '-') {
3399 g_string_append_c(norm_utf7, '-');
3402 g_string_append_c(norm_utf7, *p);
3406 norm_utf7_p = norm_utf7->str;
3407 norm_utf7_len = norm_utf7->len;
3408 to_len = strlen(mutf7_str) * 5;
3409 to_p = to_str = g_malloc(to_len + 1);
3411 if (iconv(cd, (ICONV_CONST gchar **)&norm_utf7_p, &norm_utf7_len,
3412 &to_p, &to_len) == -1) {
3413 g_warning(_("iconv cannot convert UTF-7 to %s\n"),
3414 conv_get_current_charset_str());
3415 g_string_free(norm_utf7, TRUE);
3417 return g_strdup(mutf7_str);
3420 /* second iconv() call for flushing */
3421 iconv(cd, NULL, NULL, &to_p, &to_len);
3422 g_string_free(norm_utf7, TRUE);
3426 #endif /* !HAVE_ICONV */
3429 static gchar *imap_locale_to_modified_utf7(const gchar *from)
3432 const gchar *from_p;
3435 to = g_malloc(strlen(from) * 2 + 1);
3438 for (from_p = from; *from_p != '\0'; from_p++) {
3439 if (*from_p == '&') {
3449 static iconv_t cd = (iconv_t)-1;
3450 static gboolean iconv_ok = TRUE;
3451 gchar *norm_utf7, *norm_utf7_p;
3452 size_t from_len, norm_utf7_len;
3454 gchar *from_tmp, *to, *p;
3455 gboolean in_escape = FALSE;
3457 if (!iconv_ok) return g_strdup(from);
3459 if (cd == (iconv_t)-1) {
3460 cd = iconv_open("UTF-7", conv_get_current_charset_str());
3461 if (cd == (iconv_t)-1) {
3462 g_warning("iconv cannot convert %s to UTF-7\n",
3463 conv_get_current_charset_str());
3465 return g_strdup(from);
3469 Xstrdup_a(from_tmp, from, return g_strdup(from));
3470 from_len = strlen(from);
3471 norm_utf7_len = from_len * 5;
3472 Xalloca(norm_utf7, norm_utf7_len + 1, return g_strdup(from));
3473 norm_utf7_p = norm_utf7;
3475 #define IS_PRINT(ch) (isprint(ch) && isascii(ch))
3477 while (from_len > 0) {
3478 if (*from_tmp == '+') {
3479 *norm_utf7_p++ = '+';
3480 *norm_utf7_p++ = '-';
3484 } else if (IS_PRINT(*from_tmp)) {
3485 /* printable ascii char */
3486 *norm_utf7_p = *from_tmp;
3492 size_t mb_len = 0, conv_len = 0;
3494 /* unprintable char: convert to UTF-7 */
3496 while (!IS_PRINT(*p) && conv_len < from_len) {
3497 mb_len = mblen(p, MB_LEN_MAX);
3499 g_warning("wrong multibyte sequence\n");
3500 return g_strdup(from);
3506 from_len -= conv_len;
3507 if (iconv(cd, (ICONV_CONST gchar **)&from_tmp,
3509 &norm_utf7_p, &norm_utf7_len) == -1) {
3510 g_warning("iconv cannot convert %s to UTF-7\n",
3511 conv_get_current_charset_str());
3512 return g_strdup(from);
3515 /* second iconv() call for flushing */
3516 iconv(cd, NULL, NULL, &norm_utf7_p, &norm_utf7_len);
3522 *norm_utf7_p = '\0';
3523 to_str = g_string_new(NULL);
3524 for (p = norm_utf7; p < norm_utf7_p; p++) {
3525 /* replace: '&' -> "&-",
3528 BASE64 '/' -> ',' */
3529 if (!in_escape && *p == '&') {
3530 g_string_append(to_str, "&-");
3531 } else if (!in_escape && *p == '+') {
3532 if (*(p + 1) == '-') {
3533 g_string_append_c(to_str, '+');
3536 g_string_append_c(to_str, '&');
3539 } else if (in_escape && *p == '/') {
3540 g_string_append_c(to_str, ',');
3541 } else if (in_escape && *p == '-') {
3542 g_string_append_c(to_str, '-');
3545 g_string_append_c(to_str, *p);
3551 g_string_append_c(to_str, '-');
3555 g_string_free(to_str, FALSE);
3558 #endif /* !HAVE_ICONV */
3561 static GSList *imap_get_seq_set_from_numlist(MsgNumberList *numlist)
3564 GSList *sorted_list, *cur;
3565 guint first, last, next;
3567 GSList *ret_list = NULL;
3569 if (numlist == NULL)
3572 str = g_string_sized_new(256);
3574 sorted_list = g_slist_copy(numlist);
3575 sorted_list = g_slist_sort(sorted_list, g_int_compare);
3577 first = GPOINTER_TO_INT(sorted_list->data);
3579 for (cur = sorted_list; cur != NULL; cur = g_slist_next(cur)) {
3580 last = GPOINTER_TO_INT(cur->data);
3582 next = GPOINTER_TO_INT(cur->next->data);
3586 if (last + 1 != next || next == 0) {
3588 g_string_append_c(str, ',');
3590 g_string_sprintfa(str, "%u", first);
3592 g_string_sprintfa(str, "%u:%u", first, last);
3596 if (str->len > IMAP_CMD_LIMIT) {
3597 ret_str = g_strdup(str->str);
3598 ret_list = g_slist_append(ret_list, ret_str);
3599 g_string_truncate(str, 0);
3605 ret_str = g_strdup(str->str);
3606 ret_list = g_slist_append(ret_list, ret_str);
3609 g_slist_free(sorted_list);
3610 g_string_free(str, TRUE);
3615 static GSList *imap_get_seq_set_from_msglist(MsgInfoList *msglist)
3617 MsgNumberList *numlist = NULL;
3621 for (cur = msglist; cur != NULL; cur = g_slist_next(cur)) {
3622 MsgInfo *msginfo = (MsgInfo *) cur->data;
3624 numlist = g_slist_append(numlist, GINT_TO_POINTER(msginfo->msgnum));
3626 seq_list = imap_get_seq_set_from_numlist(numlist);
3627 g_slist_free(numlist);
3632 static void imap_seq_set_free(GSList *seq_list)
3634 slist_free_strings(seq_list);
3635 g_slist_free(seq_list);
3639 static gboolean imap_rename_folder_func(GNode *node, gpointer data)
3641 FolderItem *item = node->data;
3642 gchar **paths = data;
3643 const gchar *oldpath = paths[0];
3644 const gchar *newpath = paths[1];
3646 gchar *new_itempath;
3649 oldpathlen = strlen(oldpath);
3650 if (strncmp(oldpath, item->path, oldpathlen) != 0) {
3651 g_warning("path doesn't match: %s, %s\n", oldpath, item->path);
3655 base = item->path + oldpathlen;
3656 while (*base == G_DIR_SEPARATOR) base++;
3658 new_itempath = g_strdup(newpath);
3660 new_itempath = g_strconcat(newpath, G_DIR_SEPARATOR_S, base,
3663 item->path = new_itempath;
3668 static gint get_list_of_uids(Folder *folder, IMAPFolderItem *item, GSList **msgnum_list)
3670 gint ok, nummsgs = 0, lastuid_old;
3671 IMAPSession *session;
3672 GSList *uidlist, *elem;
3675 session = imap_session_get(folder);
3676 g_return_val_if_fail(session != NULL, -1);
3678 ok = imap_select(session, IMAP_FOLDER(folder), item->item.path,
3679 NULL, NULL, NULL, NULL);
3680 if (ok != IMAP_SUCCESS)
3683 cmd_buf = g_strdup_printf("UID %d:*", item->lastuid + 1);
3684 ok = imap_cmd_search(session, cmd_buf, &uidlist);
3687 if (ok == IMAP_SOCKET) {
3688 session_destroy((Session *)session);
3689 ((RemoteFolder *)folder)->session = NULL;
3693 if (ok != IMAP_SUCCESS) {
3697 argbuf = g_ptr_array_new();
3699 cmd_buf = g_strdup_printf("UID FETCH %d:* (UID)", item->lastuid + 1);
3700 imap_gen_send(session, cmd_buf);
3702 ok = imap_cmd_ok(session, argbuf);
3703 if (ok != IMAP_SUCCESS) {
3704 ptr_array_free_strings(argbuf);
3705 g_ptr_array_free(argbuf, TRUE);
3709 for(i = 0; i < argbuf->len; i++) {
3712 if((ret = sscanf(g_ptr_array_index(argbuf, i),
3713 "%*d FETCH (UID %d)", &msgnum)) == 1)
3714 uidlist = g_slist_prepend(uidlist, GINT_TO_POINTER(msgnum));
3716 ptr_array_free_strings(argbuf);
3717 g_ptr_array_free(argbuf, TRUE);
3720 lastuid_old = item->lastuid;
3721 *msgnum_list = g_slist_copy(item->uid_list);
3722 nummsgs = g_slist_length(*msgnum_list);
3723 debug_print("Got %d uids from cache\n", g_slist_length(item->uid_list));
3725 for (elem = uidlist; elem != NULL; elem = g_slist_next(elem)) {
3728 msgnum = GPOINTER_TO_INT(elem->data);
3729 if (msgnum > lastuid_old) {
3730 *msgnum_list = g_slist_prepend(*msgnum_list, GINT_TO_POINTER(msgnum));
3731 item->uid_list = g_slist_prepend(item->uid_list, GINT_TO_POINTER(msgnum));
3734 if(msgnum > item->lastuid)
3735 item->lastuid = msgnum;
3738 g_slist_free(uidlist);
3743 gint imap_get_num_list(Folder *folder, FolderItem *_item, GSList **msgnum_list, gboolean *old_uids_valid)
3745 IMAPFolderItem *item = (IMAPFolderItem *)_item;
3746 IMAPSession *session;
3747 gint ok, nummsgs = 0, exists, recent, uid_val, uid_next, unseen;
3750 gboolean selected_folder;
3752 g_return_val_if_fail(folder != NULL, -1);
3753 g_return_val_if_fail(item != NULL, -1);
3754 g_return_val_if_fail(item->item.path != NULL, -1);
3755 g_return_val_if_fail(FOLDER_CLASS(folder) == &imap_class, -1);
3756 g_return_val_if_fail(folder->account != NULL, -1);
3758 session = imap_session_get(folder);
3759 g_return_val_if_fail(session != NULL, -1);
3761 selected_folder = (session->mbox != NULL) &&
3762 (!strcmp(session->mbox, item->item.path));
3763 if (selected_folder) {
3764 ok = imap_cmd_noop(session);
3765 if (ok != IMAP_SUCCESS)
3767 exists = session->exists;
3769 *old_uids_valid = TRUE;
3771 ok = imap_status(session, IMAP_FOLDER(folder), item->item.path,
3772 &exists, &recent, &uid_next, &uid_val, &unseen);
3773 if (ok != IMAP_SUCCESS)
3776 if(item->item.mtime == uid_val)
3777 *old_uids_valid = TRUE;
3779 *old_uids_valid = FALSE;
3781 debug_print("Freeing imap uid cache\n");
3783 g_slist_free(item->uid_list);
3784 item->uid_list = NULL;
3786 item->item.mtime = uid_val;
3788 imap_delete_all_cached_messages((FolderItem *)item);
3792 if (!selected_folder)
3793 item->uid_next = uid_next;
3795 /* If old uid_next matches new uid_next we can be sure no message
3796 was added to the folder */
3797 if (( selected_folder && !session->folder_content_changed) ||
3798 (!selected_folder && uid_next == item->uid_next)) {
3799 nummsgs = g_slist_length(item->uid_list);
3801 /* If number of messages is still the same we
3802 know our caches message numbers are still valid,
3803 otherwise if the number of messages has decrease
3804 we discard our cache to start a new scan to find
3805 out which numbers have been removed */
3806 if (exists == nummsgs) {
3807 *msgnum_list = g_slist_copy(item->uid_list);
3809 } else if (exists < nummsgs) {
3810 debug_print("Freeing imap uid cache");
3812 g_slist_free(item->uid_list);
3813 item->uid_list = NULL;
3818 *msgnum_list = NULL;
3822 nummsgs = get_list_of_uids(folder, item, &uidlist);
3824 if (nummsgs != exists) {
3825 /* Cache contains more messages then folder, we have cached
3826 an old UID of a message that was removed and new messages
3827 have been added too, otherwise the uid_next check would
3829 debug_print("Freeing imap uid cache");
3831 g_slist_free(item->uid_list);
3832 item->uid_list = NULL;
3834 g_slist_free(*msgnum_list);
3836 nummsgs = get_list_of_uids(folder, item, &uidlist);
3839 *msgnum_list = uidlist;
3841 dir = folder_item_get_path((FolderItem *)item);
3842 debug_print("removing old messages from %s\n", dir);
3843 remove_numbered_files_not_in_list(dir, *msgnum_list);
3849 static MsgInfo *imap_parse_msg(const gchar *file, FolderItem *item)
3854 flags.perm_flags = MSG_NEW|MSG_UNREAD;
3855 flags.tmp_flags = 0;
3857 g_return_val_if_fail(item != NULL, NULL);
3858 g_return_val_if_fail(file != NULL, NULL);
3860 if (item->stype == F_QUEUE) {
3861 MSG_SET_TMP_FLAGS(flags, MSG_QUEUED);
3862 } else if (item->stype == F_DRAFT) {
3863 MSG_SET_TMP_FLAGS(flags, MSG_DRAFT);
3866 msginfo = procheader_parse_file(file, flags, FALSE, FALSE);
3867 if (!msginfo) return NULL;
3869 msginfo->folder = item;
3874 GSList *imap_get_msginfos(Folder *folder, FolderItem *item, GSList *msgnum_list)
3876 IMAPSession *session;
3877 MsgInfoList *ret = NULL;
3880 g_return_val_if_fail(folder != NULL, NULL);
3881 g_return_val_if_fail(item != NULL, NULL);
3882 g_return_val_if_fail(msgnum_list != NULL, NULL);
3884 session = imap_session_get(folder);
3885 g_return_val_if_fail(session != NULL, NULL);
3887 ok = imap_select(session, IMAP_FOLDER(folder), item->path,
3888 NULL, NULL, NULL, NULL);
3889 if (ok != IMAP_SUCCESS)
3892 if (!(item->stype == F_QUEUE || item->stype == F_DRAFT)) {
3893 ret = g_slist_concat(ret,
3894 imap_get_uncached_messages(
3895 session, item, msgnum_list));
3897 MsgNumberList *sorted_list, *elem;
3898 gint startnum, lastnum;
3900 sorted_list = g_slist_sort(g_slist_copy(msgnum_list), g_int_compare);
3902 startnum = lastnum = GPOINTER_TO_INT(sorted_list->data);
3904 for (elem = sorted_list;; elem = g_slist_next(elem)) {
3908 num = GPOINTER_TO_INT(elem->data);
3910 if (num > lastnum + 1 || elem == NULL) {
3912 for (i = startnum; i <= lastnum; ++i) {
3915 file = imap_fetch_msg(folder, item, i);
3917 MsgInfo *msginfo = imap_parse_msg(file, item);
3918 if (msginfo != NULL) {
3919 msginfo->msgnum = i;
3920 ret = g_slist_append(ret, msginfo);
3934 g_slist_free(sorted_list);
3940 MsgInfo *imap_get_msginfo(Folder *folder, FolderItem *item, gint uid)
3942 MsgInfo *msginfo = NULL;
3943 MsgInfoList *msginfolist;
3944 MsgNumberList numlist;
3946 numlist.next = NULL;
3947 numlist.data = GINT_TO_POINTER(uid);
3949 msginfolist = imap_get_msginfos(folder, item, &numlist);
3950 if (msginfolist != NULL) {
3951 msginfo = msginfolist->data;
3952 g_slist_free(msginfolist);
3958 gboolean imap_scan_required(Folder *folder, FolderItem *_item)
3960 IMAPSession *session;
3961 IMAPFolderItem *item = (IMAPFolderItem *)_item;
3962 gint ok, exists = 0, recent = 0, unseen = 0;
3963 guint32 uid_next, uid_val = 0;
3964 gboolean selected_folder;
3966 g_return_val_if_fail(folder != NULL, FALSE);
3967 g_return_val_if_fail(item != NULL, FALSE);
3968 g_return_val_if_fail(item->item.folder != NULL, FALSE);
3969 g_return_val_if_fail(FOLDER_CLASS(item->item.folder) == &imap_class, FALSE);
3971 if (item->item.path == NULL)
3974 session = imap_session_get(folder);
3975 g_return_val_if_fail(session != NULL, FALSE);
3977 selected_folder = (session->mbox != NULL) &&
3978 (!strcmp(session->mbox, item->item.path));
3979 if (selected_folder) {
3980 ok = imap_cmd_noop(session);
3981 if (ok != IMAP_SUCCESS)
3984 if (session->folder_content_changed)
3987 ok = imap_status(session, IMAP_FOLDER(folder), item->item.path,
3988 &exists, &recent, &uid_next, &uid_val, &unseen);
3989 if (ok != IMAP_SUCCESS)
3992 if ((uid_next != item->uid_next) || (exists < item->item.total_msgs))
3999 void imap_change_flags(Folder *folder, FolderItem *item, MsgInfo *msginfo, MsgPermFlags newflags)
4001 IMAPSession *session;
4002 IMAPFlags flags_set = 0, flags_unset = 0;
4003 gint ok = IMAP_SUCCESS;
4004 MsgNumberList numlist;
4006 g_return_if_fail(folder != NULL);
4007 g_return_if_fail(folder->klass == &imap_class);
4008 g_return_if_fail(item != NULL);
4009 g_return_if_fail(item->folder == folder);
4010 g_return_if_fail(msginfo != NULL);
4011 g_return_if_fail(msginfo->folder == item);
4013 session = imap_session_get(folder);
4014 if (!session) return;
4016 if ((ok = imap_select(session, IMAP_FOLDER(folder), msginfo->folder->path,
4017 NULL, NULL, NULL, NULL)) != IMAP_SUCCESS)
4020 if (!MSG_IS_MARKED(msginfo->flags) && (newflags & MSG_MARKED))
4021 flags_set |= IMAP_FLAG_FLAGGED;
4022 if ( MSG_IS_MARKED(msginfo->flags) && !(newflags & MSG_MARKED))
4023 flags_unset |= IMAP_FLAG_FLAGGED;
4025 if (!MSG_IS_UNREAD(msginfo->flags) && (newflags & MSG_UNREAD))
4026 flags_unset |= IMAP_FLAG_SEEN;
4027 if ( MSG_IS_UNREAD(msginfo->flags) && !(newflags & MSG_UNREAD))
4028 flags_set |= IMAP_FLAG_SEEN;
4030 if (!MSG_IS_REPLIED(msginfo->flags) && (newflags & MSG_REPLIED))
4031 flags_set |= IMAP_FLAG_ANSWERED;
4032 if ( MSG_IS_REPLIED(msginfo->flags) && !(newflags & MSG_REPLIED))
4033 flags_set |= IMAP_FLAG_ANSWERED;
4035 numlist.next = NULL;
4036 numlist.data = GINT_TO_POINTER(msginfo->msgnum);
4039 ok = imap_set_message_flags(session, &numlist, flags_set, TRUE);
4040 if (ok != IMAP_SUCCESS) return;
4044 ok = imap_set_message_flags(session, &numlist, flags_unset, FALSE);
4045 if (ok != IMAP_SUCCESS) return;
4048 msginfo->flags.perm_flags = newflags;