2 * Sylpheed -- a GTK+ based, lightweight, and fast e-mail client
3 * Copyright (C) 1999-2003 Hiroyuki Yamamoto
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation; either version 2 of the License, or
8 * (at your option) any later version.
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
15 * You should have received a copy of the GNU General Public License
16 * along with this program; if not, write to the Free Software
17 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
49 #include "procheader.h"
50 #include "prefs_account.h"
55 #include "inputdialog.h"
58 typedef struct _IMAPFolder IMAPFolder;
59 typedef struct _IMAPSession IMAPSession;
60 typedef struct _IMAPNameSpace IMAPNameSpace;
61 typedef struct _IMAPFolderItem IMAPFolderItem;
63 #include "prefs_account.h"
65 #define IMAP_FOLDER(obj) ((IMAPFolder *)obj)
66 #define IMAP_FOLDER_ITEM(obj) ((IMAPFolderItem *)obj)
67 #define IMAP_SESSION(obj) ((IMAPSession *)obj)
73 /* list of IMAPNameSpace */
83 gboolean authenticated;
92 time_t last_access_time;
93 gboolean folder_content_changed;
103 #define IMAP_SUCCESS 0
104 #define IMAP_SOCKET 2
105 #define IMAP_AUTHFAIL 3
106 #define IMAP_PROTOCOL 4
107 #define IMAP_SYNTAX 5
111 #define IMAPBUFSIZE 8192
115 IMAP_FLAG_SEEN = 1 << 0,
116 IMAP_FLAG_ANSWERED = 1 << 1,
117 IMAP_FLAG_FLAGGED = 1 << 2,
118 IMAP_FLAG_DELETED = 1 << 3,
119 IMAP_FLAG_DRAFT = 1 << 4
122 #define IMAP_IS_SEEN(flags) ((flags & IMAP_FLAG_SEEN) != 0)
123 #define IMAP_IS_ANSWERED(flags) ((flags & IMAP_FLAG_ANSWERED) != 0)
124 #define IMAP_IS_FLAGGED(flags) ((flags & IMAP_FLAG_FLAGGED) != 0)
125 #define IMAP_IS_DELETED(flags) ((flags & IMAP_FLAG_DELETED) != 0)
126 #define IMAP_IS_DRAFT(flags) ((flags & IMAP_FLAG_DRAFT) != 0)
129 #define IMAP4_PORT 143
131 #define IMAPS_PORT 993
134 #define IMAP_CMD_LIMIT 1000
136 #define QUOTE_IF_REQUIRED(out, str) \
138 if (*str != '"' && strpbrk(str, " \t(){}%*") != NULL) { \
142 len = strlen(str) + 3; \
143 Xalloca(__tmp, len, return IMAP_ERROR); \
144 g_snprintf(__tmp, len, "\"%s\"", str); \
147 Xstrdup_a(out, str, return IMAP_ERROR); \
151 typedef gchar * IMAPSet;
153 struct _IMAPFolderItem
162 static Folder *imap_folder_new(const gchar * name, const gchar * path);
163 static void imap_folder_destroy(Folder * folder);
165 static IMAPSession *imap_session_new(const PrefsAccount * account);
166 static void imap_session_authenticate(IMAPSession * session,
167 const PrefsAccount * account);
168 static void imap_session_destroy(Session * session);
170 static gchar *imap_fetch_msg(Folder * folder, FolderItem * item, gint uid);
171 static gint imap_add_msg(Folder * folder,
173 const gchar * file, MsgFlags * flags);
174 static gint imap_add_msgs(Folder * folder, FolderItem * dest,
176 GRelation *relation);
178 static gint imap_copy_msg(Folder * folder,
179 FolderItem * dest, MsgInfo * msginfo);
180 static gint imap_copy_msgs(Folder *folder, FolderItem *dest,
181 MsgInfoList *msglist, GRelation *relation);
183 static gint imap_remove_msg(Folder * folder, FolderItem * item, gint uid);
184 static gint imap_remove_all_msg(Folder * folder, FolderItem * item);
186 static gboolean imap_is_msg_changed(Folder * folder,
187 FolderItem * item, MsgInfo * msginfo);
189 static gint imap_close(Folder * folder, FolderItem * item);
191 static gint imap_scan_tree(Folder * folder);
193 static gint imap_create_tree(Folder * folder);
195 static FolderItem *imap_create_folder(Folder * folder,
198 static gint imap_rename_folder(Folder * folder,
199 FolderItem * item, const gchar * name);
200 static gint imap_remove_folder(Folder * folder, FolderItem * item);
203 static void imap_folder_init (Folder *folder,
207 static FolderItem *imap_folder_item_new (Folder *folder);
208 static void imap_folder_item_destroy (Folder *folder,
211 static IMAPSession *imap_session_get (Folder *folder);
213 static gint imap_greeting (IMAPSession *session);
214 static gint imap_auth (IMAPSession *session,
219 static gint imap_scan_tree_recursive (IMAPSession *session,
221 static GSList *imap_parse_list (IMAPFolder *folder,
222 IMAPSession *session,
223 const gchar *real_path,
226 static void imap_create_missing_folders (Folder *folder);
227 static FolderItem *imap_create_special_folder
229 SpecialFolderItemType stype,
232 static gint imap_do_copy_msgs (Folder *folder,
234 MsgInfoList *msglist,
235 GRelation *relation);
237 static void imap_delete_all_cached_messages (FolderItem *item);
240 static SockInfo *imap_open (const gchar *server,
244 static SockInfo *imap_open (const gchar *server,
249 static SockInfo *imap_open_tunnel(const gchar *server,
250 const gchar *tunnelcmd,
253 static SockInfo *imap_open_tunnel(const gchar *server,
254 const gchar *tunnelcmd);
258 static SockInfo *imap_init_sock(SockInfo *sock, SSLType ssl_type);
260 static SockInfo *imap_init_sock(SockInfo *sock);
263 static gchar *imap_get_flag_str (IMAPFlags flags);
264 static gint imap_set_message_flags (IMAPSession *session,
265 MsgNumberList *numlist,
268 static gint imap_select (IMAPSession *session,
274 guint32 *uid_validity);
275 static gint imap_status (IMAPSession *session,
281 guint32 *uid_validity,
284 static void imap_parse_namespace (IMAPSession *session,
286 static void imap_get_namespace_by_list (IMAPSession *session,
288 static IMAPNameSpace *imap_find_namespace (IMAPFolder *folder,
290 static gchar imap_get_path_separator (IMAPFolder *folder,
292 static gchar *imap_get_real_path (IMAPFolder *folder,
295 static gchar *imap_parse_atom (SockInfo *sock,
300 static MsgFlags imap_parse_flags (const gchar *flag_str);
301 static MsgInfo *imap_parse_envelope (SockInfo *sock,
305 static gboolean imap_has_capability (IMAPSession *session,
307 static void imap_free_capabilities (IMAPSession *session);
309 /* low-level IMAP4rev1 commands */
310 static gint imap_cmd_authenticate
311 (IMAPSession *session,
315 static gint imap_cmd_login (IMAPSession *session,
318 static gint imap_cmd_logout (IMAPSession *session);
319 static gint imap_cmd_noop (IMAPSession *session);
320 static gint imap_cmd_starttls (IMAPSession *session);
321 static gint imap_cmd_namespace (IMAPSession *session,
323 static gint imap_cmd_list (IMAPSession *session,
325 const gchar *mailbox,
327 static gint imap_cmd_do_select (IMAPSession *session,
333 guint32 *uid_validity);
334 static gint imap_cmd_select (IMAPSession *session,
339 guint32 *uid_validity);
340 static gint imap_cmd_examine (IMAPSession *session,
345 guint32 *uid_validity);
346 static gint imap_cmd_create (IMAPSession *sock,
347 const gchar *folder);
348 static gint imap_cmd_rename (IMAPSession *sock,
349 const gchar *oldfolder,
350 const gchar *newfolder);
351 static gint imap_cmd_delete (IMAPSession *session,
352 const gchar *folder);
353 static gint imap_cmd_envelope (IMAPSession *session,
355 static gint imap_cmd_fetch (IMAPSession *sock,
357 const gchar *filename);
358 static gint imap_cmd_append (IMAPSession *session,
359 const gchar *destfolder,
363 static gint imap_cmd_copy (IMAPSession *session,
364 const gchar *seq_set,
365 const gchar *destfolder,
366 GRelation *uid_mapping);
367 static gint imap_cmd_store (IMAPSession *session,
370 static gint imap_cmd_expunge (IMAPSession *session,
372 static gint imap_cmd_close (IMAPSession *session);
374 static gint imap_cmd_ok (IMAPSession *session,
376 static void imap_gen_send (IMAPSession *session,
377 const gchar *format, ...);
378 static gint imap_gen_recv (IMAPSession *session,
381 /* misc utility functions */
382 static gchar *strchr_cpy (const gchar *src,
386 static gchar *get_quoted (const gchar *src,
390 static gchar *search_array_contain_str (GPtrArray *array,
392 static gchar *search_array_str (GPtrArray *array,
394 static void imap_path_separator_subst (gchar *str,
397 static gchar *imap_modified_utf7_to_locale (const gchar *mutf7_str);
398 static gchar *imap_locale_to_modified_utf7 (const gchar *from);
400 static GSList *imap_get_seq_set_from_numlist (MsgNumberList *msglist);
401 static GSList *imap_get_seq_set_from_msglist (MsgInfoList *msglist);
402 static void imap_seq_set_free (GSList *seq_list);
404 static gboolean imap_rename_folder_func (GNode *node,
406 static gint imap_get_num_list (Folder *folder,
409 gboolean *old_uids_valid);
410 static GSList *imap_get_msginfos (Folder *folder,
412 GSList *msgnum_list);
413 static MsgInfo *imap_get_msginfo (Folder *folder,
416 static gboolean imap_scan_required (Folder *folder,
418 static void imap_change_flags (Folder *folder,
421 MsgPermFlags newflags);
422 static gchar *imap_folder_get_path (Folder *folder);
423 static gchar *imap_item_get_path (Folder *folder,
426 FolderClass imap_class =
432 /* Folder functions */
438 /* FolderItem functions */
439 imap_folder_item_new,
440 imap_folder_item_destroy,
452 /* Message functions */
466 FolderClass *imap_get_class(void)
471 Folder *imap_folder_new(const gchar *name, const gchar *path)
475 folder = (Folder *)g_new0(IMAPFolder, 1);
476 folder->klass = &imap_class;
477 imap_folder_init(folder, name, path);
482 void imap_folder_destroy(Folder *folder)
486 dir = imap_folder_get_path(folder);
487 if (is_dir_exist(dir))
488 remove_dir_recursive(dir);
491 folder_remote_folder_destroy(REMOTE_FOLDER(folder));
494 static void imap_folder_init(Folder *folder, const gchar *name,
497 folder_remote_folder_init((Folder *)folder, name, path);
500 static FolderItem *imap_folder_item_new(Folder *folder)
502 IMAPFolderItem *item;
504 item = g_new0(IMAPFolderItem, 1);
507 item->uid_list = NULL;
509 return (FolderItem *)item;
512 static void imap_folder_item_destroy(Folder *folder, FolderItem *_item)
514 IMAPFolderItem *item = (IMAPFolderItem *)_item;
516 g_return_if_fail(item != NULL);
517 g_slist_free(item->uid_list);
522 static gboolean imap_reset_uid_lists_func(GNode *node, gpointer data)
524 IMAPFolderItem *item = (IMAPFolderItem *)node->data;
528 g_slist_free(item->uid_list);
529 item->uid_list = NULL;
534 static void imap_reset_uid_lists(Folder *folder)
536 if(folder->node == NULL)
539 /* Destroy all uid lists and rest last uid */
540 g_node_traverse(folder->node, G_IN_ORDER, G_TRAVERSE_ALL, -1, imap_reset_uid_lists_func, NULL);
543 /* Send CAPABILITY, and examine the server's response to see whether this
544 * connection is pre-authenticated or not and build a list of CAPABILITIES. */
545 static gint imap_greeting(IMAPSession *session)
550 imap_gen_send(session, "CAPABILITY");
552 argbuf = g_ptr_array_new();
554 if (imap_cmd_ok(session, argbuf) != IMAP_SUCCESS ||
555 ((capstr = search_array_str(argbuf, "CAPABILITY ")) == NULL)) {
556 ptr_array_free_strings(argbuf);
557 g_ptr_array_free(argbuf, TRUE);
561 session->authenticated = search_array_str(argbuf, "PREAUTH") != NULL;
563 capstr += strlen("CAPABILITY ");
565 IMAP_SESSION(session)->capability = g_strsplit(capstr, " ", 0);
567 ptr_array_free_strings(argbuf);
568 g_ptr_array_free(argbuf, TRUE);
570 if (imap_has_capability(session, "UIDPLUS"))
571 session->uidplus = TRUE;
576 static gint imap_auth(IMAPSession *session, const gchar *user, const gchar *pass,
581 if (type == 0 || type == IMAP_AUTH_LOGIN)
582 ok = imap_cmd_login(session, user, pass);
584 ok = imap_cmd_authenticate(session, user, pass, type);
586 if (ok == IMAP_SUCCESS)
587 session->authenticated = TRUE;
592 static IMAPSession *imap_session_get(Folder *folder)
594 RemoteFolder *rfolder = REMOTE_FOLDER(folder);
595 IMAPSession *session = NULL;
597 g_return_val_if_fail(folder != NULL, NULL);
598 g_return_val_if_fail(FOLDER_CLASS(folder) == &imap_class, NULL);
599 g_return_val_if_fail(folder->account != NULL, NULL);
601 /* Make sure we have a session */
602 if (rfolder->session != NULL) {
603 session = IMAP_SESSION(rfolder->session);
605 imap_reset_uid_lists(folder);
606 session = imap_session_new(folder->account);
611 if (SESSION(session)->sock->state == CONN_DISCONNECTED) {
612 debug_print("IMAP server disconnected\n");
613 session_destroy(SESSION(session));
614 imap_reset_uid_lists(folder);
615 session = imap_session_new(folder->account);
618 /* Make sure session is authenticated */
619 if (!IMAP_SESSION(session)->authenticated)
620 imap_session_authenticate(IMAP_SESSION(session), folder->account);
621 if (!IMAP_SESSION(session)->authenticated) {
622 session_destroy(SESSION(session));
623 rfolder->session = NULL;
627 /* Make sure we have parsed the IMAP namespace */
628 imap_parse_namespace(IMAP_SESSION(session),
629 IMAP_FOLDER(folder));
631 /* I think the point of this code is to avoid sending a
632 * keepalive if we've used the session recently and therefore
633 * think it's still alive. Unfortunately, most of the code
634 * does not yet check for errors on the socket, and so if the
635 * connection drops we don't notice until the timeout expires.
636 * A better solution than sending a NOOP every time would be
637 * for every command to be prepared to retry until it is
638 * successfully sent. -- mbp */
639 if (time(NULL) - session->last_access_time > SESSION_TIMEOUT) {
640 /* verify that the session is still alive */
641 if (imap_cmd_noop(session) != IMAP_SUCCESS) {
642 /* Check if this is the first try to establish a
643 connection, if yes we don't try to reconnect */
644 if (rfolder->session == NULL) {
645 log_warning(_("Connecting %s failed"),
646 folder->account->recv_server);
647 session_destroy(SESSION(session));
650 log_warning(_("IMAP4 connection to %s has been"
651 " disconnected. Reconnecting...\n"),
652 folder->account->recv_server);
653 session_destroy(SESSION(session));
654 /* Clear folders session to make imap_session_get create
655 a new session, because of rfolder->session == NULL
656 it will not try to reconnect again and so avoid an
658 rfolder->session = NULL;
659 session = imap_session_get(folder);
664 rfolder->session = SESSION(session);
666 session->last_access_time = time(NULL);
668 return IMAP_SESSION(session);
671 IMAPSession *imap_session_new(const PrefsAccount *account)
673 IMAPSession *session;
678 /* FIXME: IMAP over SSL only... */
681 port = account->set_imapport ? account->imapport
682 : account->ssl_imap == SSL_TUNNEL ? IMAPS_PORT : IMAP4_PORT;
683 ssl_type = account->ssl_imap;
685 port = account->set_imapport ? account->imapport
689 if (account->set_tunnelcmd) {
690 log_message(_("creating tunneled IMAP4 connection\n"));
692 if ((imap_sock = imap_open_tunnel(account->recv_server,
696 if ((imap_sock = imap_open_tunnel(account->recv_server,
697 account->tunnelcmd)) == NULL)
701 g_return_val_if_fail(account->recv_server != NULL, NULL);
703 log_message(_("creating IMAP4 connection to %s:%d ...\n"),
704 account->recv_server, port);
707 if ((imap_sock = imap_open(account->recv_server, port,
710 if ((imap_sock = imap_open(account->recv_server, port)) == NULL)
715 session = g_new0(IMAPSession, 1);
716 session_init(SESSION(session));
717 SESSION(session)->type = SESSION_IMAP;
718 SESSION(session)->server = g_strdup(account->recv_server);
719 SESSION(session)->sock = imap_sock;
721 SESSION(session)->destroy = imap_session_destroy;
723 session->capability = NULL;
725 session->authenticated = FALSE;
726 session->mbox = NULL;
727 session->cmd_count = 0;
729 /* Only need to log in if the connection was not PREAUTH */
730 if (imap_greeting(session) != IMAP_SUCCESS) {
731 session_destroy(SESSION(session));
736 if (account->ssl_imap == SSL_STARTTLS &&
737 imap_has_capability(session, "STARTTLS")) {
740 ok = imap_cmd_starttls(session);
741 if (ok != IMAP_SUCCESS) {
742 log_warning(_("Can't start TLS session.\n"));
743 session_destroy(SESSION(session));
746 if (!ssl_init_socket_with_method(SESSION(session)->sock, SSL_METHOD_TLSv1)) {
747 session_destroy(SESSION(session));
751 imap_free_capabilities(session);
752 session->authenticated = FALSE;
753 session->uidplus = FALSE;
754 session->cmd_count = 1;
756 if (imap_greeting(session) != IMAP_SUCCESS) {
757 session_destroy(SESSION(session));
762 log_message("IMAP connection is %s-authenticated\n",
763 (session->authenticated) ? "pre" : "un");
768 void imap_session_authenticate(IMAPSession *session, const PrefsAccount *account)
772 g_return_if_fail(account->userid != NULL);
774 pass = account->passwd;
777 tmp_pass = input_dialog_query_password(account->recv_server, account->userid);
780 Xstrdup_a(pass, tmp_pass, {g_free(tmp_pass); return;});
784 if (imap_auth(session, account->userid, pass, account->imap_auth_type) != IMAP_SUCCESS) {
785 imap_cmd_logout(session);
789 session->authenticated = TRUE;
792 void imap_session_destroy(Session *session)
794 imap_free_capabilities(IMAP_SESSION(session));
795 g_free(IMAP_SESSION(session)->mbox);
796 sock_close(session->sock);
797 session->sock = NULL;
800 gchar *imap_fetch_msg(Folder *folder, FolderItem *item, gint uid)
802 gchar *path, *filename;
803 IMAPSession *session;
806 g_return_val_if_fail(folder != NULL, NULL);
807 g_return_val_if_fail(item != NULL, NULL);
809 path = folder_item_get_path(item);
810 if (!is_dir_exist(path))
812 filename = g_strconcat(path, G_DIR_SEPARATOR_S, itos(uid), NULL);
815 if (is_file_exist(filename)) {
816 debug_print("message %d has been already cached.\n", uid);
820 session = imap_session_get(folder);
826 ok = imap_select(session, IMAP_FOLDER(folder), item->path,
827 NULL, NULL, NULL, NULL);
828 if (ok != IMAP_SUCCESS) {
829 g_warning("can't select mailbox %s\n", item->path);
834 debug_print("getting message %d...\n", uid);
835 ok = imap_cmd_fetch(session, (guint32)uid, filename);
837 if (ok != IMAP_SUCCESS) {
838 g_warning("can't fetch message %d\n", uid);
846 gint imap_add_msg(Folder *folder, FolderItem *dest, const gchar *file, MsgFlags *flags)
850 MsgFileInfo fileinfo;
852 g_return_val_if_fail(file != NULL, -1);
854 fileinfo.msginfo = NULL;
855 fileinfo.file = (gchar *)file;
856 fileinfo.flags = flags;
857 file_list.data = &fileinfo;
858 file_list.next = NULL;
860 ret = imap_add_msgs(folder, dest, &file_list, NULL);
864 gint imap_add_msgs(Folder *folder, FolderItem *dest, GSList *file_list,
868 IMAPSession *session;
869 guint32 last_uid = 0;
871 MsgFileInfo *fileinfo;
874 g_return_val_if_fail(folder != NULL, -1);
875 g_return_val_if_fail(dest != NULL, -1);
876 g_return_val_if_fail(file_list != NULL, -1);
878 session = imap_session_get(folder);
879 if (!session) return -1;
881 destdir = imap_get_real_path(IMAP_FOLDER(folder), dest->path);
883 for (cur = file_list; cur != NULL; cur = cur->next) {
884 IMAPFlags iflags = 0;
887 fileinfo = (MsgFileInfo *)cur->data;
889 if (fileinfo->flags) {
890 if (MSG_IS_MARKED(*fileinfo->flags))
891 iflags |= IMAP_FLAG_FLAGGED;
892 if (MSG_IS_REPLIED(*fileinfo->flags))
893 iflags |= IMAP_FLAG_ANSWERED;
894 if (!MSG_IS_UNREAD(*fileinfo->flags))
895 iflags |= IMAP_FLAG_SEEN;
898 if (dest->stype == F_OUTBOX ||
899 dest->stype == F_QUEUE ||
900 dest->stype == F_DRAFT ||
901 dest->stype == F_TRASH)
902 iflags |= IMAP_FLAG_SEEN;
904 ok = imap_cmd_append(session, destdir, fileinfo->file, iflags,
907 if (ok != IMAP_SUCCESS) {
908 g_warning("can't append message %s\n", fileinfo->file);
913 if (relation != NULL)
914 g_relation_insert(relation, fileinfo->msginfo != NULL ?
915 (gpointer) fileinfo->msginfo : (gpointer) fileinfo,
916 GINT_TO_POINTER(dest->last_num + 1));
917 if (last_uid < new_uid)
926 static gint imap_do_copy_msgs(Folder *folder, FolderItem *dest,
927 MsgInfoList *msglist, GRelation *relation)
931 GSList *seq_list, *cur;
933 IMAPSession *session;
934 gint ok = IMAP_SUCCESS;
935 GRelation *uid_mapping;
938 g_return_val_if_fail(folder != NULL, -1);
939 g_return_val_if_fail(dest != NULL, -1);
940 g_return_val_if_fail(msglist != NULL, -1);
942 session = imap_session_get(folder);
943 if (!session) return -1;
945 msginfo = (MsgInfo *)msglist->data;
947 src = msginfo->folder;
949 g_warning("the src folder is identical to the dest.\n");
953 ok = imap_select(session, IMAP_FOLDER(folder), msginfo->folder->path,
954 NULL, NULL, NULL, NULL);
955 if (ok != IMAP_SUCCESS)
958 destdir = imap_get_real_path(IMAP_FOLDER(folder), dest->path);
959 seq_list = imap_get_seq_set_from_msglist(msglist);
960 uid_mapping = g_relation_new(2);
961 g_relation_index(uid_mapping, 0, g_direct_hash, g_direct_equal);
963 for (cur = seq_list; cur != NULL; cur = g_slist_next(cur)) {
964 gchar *seq_set = (gchar *)cur->data;
966 debug_print("Copying message %s%c[%s] to %s ...\n",
967 src->path, G_DIR_SEPARATOR,
970 ok = imap_cmd_copy(session, seq_set, destdir, uid_mapping);
971 if (ok != IMAP_SUCCESS) {
972 g_relation_destroy(uid_mapping);
973 imap_seq_set_free(seq_list);
978 for (cur = msglist; cur != NULL; cur = g_slist_next(cur)) {
979 MsgInfo *msginfo = (MsgInfo *)cur->data;
982 tuples = g_relation_select(uid_mapping,
983 GINT_TO_POINTER(msginfo->msgnum),
985 if (tuples->len > 0) {
986 gint num = GPOINTER_TO_INT(g_tuples_index(tuples, 0, 1));
987 g_relation_insert(relation, msginfo,
988 GPOINTER_TO_INT(num));
992 g_relation_insert(relation, msginfo,
994 g_tuples_destroy(tuples);
997 imap_seq_set_free(seq_list);
1001 if (ok == IMAP_SUCCESS)
1007 gint imap_copy_msg(Folder *folder, FolderItem *dest, MsgInfo *msginfo)
1011 g_return_val_if_fail(msginfo != NULL, -1);
1013 msglist.data = msginfo;
1014 msglist.next = NULL;
1016 return imap_copy_msgs(folder, dest, &msglist, NULL);
1019 gint imap_copy_msgs(Folder *folder, FolderItem *dest,
1020 MsgInfoList *msglist, GRelation *relation)
1026 g_return_val_if_fail(folder != NULL, -1);
1027 g_return_val_if_fail(dest != NULL, -1);
1028 g_return_val_if_fail(msglist != NULL, -1);
1030 msginfo = (MsgInfo *)msglist->data;
1031 g_return_val_if_fail(msginfo->folder != NULL, -1);
1033 if (folder == msginfo->folder->folder)
1034 return imap_do_copy_msgs(folder, dest, msglist, relation);
1036 file_list = procmsg_get_message_file_list(msglist);
1037 g_return_val_if_fail(file_list != NULL, -1);
1039 ret = imap_add_msgs(folder, dest, file_list, relation);
1041 procmsg_message_file_list_free(file_list);
1046 gint imap_remove_msg(Folder *folder, FolderItem *item, gint uid)
1049 IMAPSession *session;
1051 MsgNumberList numlist;
1053 g_return_val_if_fail(folder != NULL, -1);
1054 g_return_val_if_fail(FOLDER_CLASS(folder) == &imap_class, -1);
1055 g_return_val_if_fail(item != NULL, -1);
1057 session = imap_session_get(folder);
1058 if (!session) return -1;
1060 ok = imap_select(session, IMAP_FOLDER(folder), item->path,
1061 NULL, NULL, NULL, NULL);
1062 if (ok != IMAP_SUCCESS)
1065 numlist.next = NULL;
1066 numlist.data = GINT_TO_POINTER(uid);
1068 ok = imap_set_message_flags
1069 (IMAP_SESSION(REMOTE_FOLDER(folder)->session),
1070 &numlist, IMAP_FLAG_DELETED, TRUE);
1071 if (ok != IMAP_SUCCESS) {
1072 log_warning(_("can't set deleted flags: %d\n"), uid);
1076 if (!session->uidplus) {
1077 ok = imap_cmd_expunge(session, NULL);
1081 uidstr = g_strdup_printf("%u", uid);
1082 ok = imap_cmd_expunge(session, uidstr);
1085 if (ok != IMAP_SUCCESS) {
1086 log_warning(_("can't expunge\n"));
1090 IMAP_FOLDER_ITEM(item)->uid_list = g_slist_remove(
1091 IMAP_FOLDER_ITEM(item)->uid_list, numlist.data);
1092 dir = folder_item_get_path(item);
1093 if (is_dir_exist(dir))
1094 remove_numbered_files(dir, uid, uid);
1097 return IMAP_SUCCESS;
1100 gint imap_remove_all_msg(Folder *folder, FolderItem *item)
1103 IMAPSession *session;
1106 g_return_val_if_fail(folder != NULL, -1);
1107 g_return_val_if_fail(item != NULL, -1);
1109 session = imap_session_get(folder);
1110 if (!session) return -1;
1112 ok = imap_select(session, IMAP_FOLDER(folder), item->path,
1113 NULL, NULL, NULL, NULL);
1114 if (ok != IMAP_SUCCESS)
1117 imap_gen_send(session, "STORE 1:* +FLAGS.SILENT (\\Deleted)");
1118 ok = imap_cmd_ok(session, NULL);
1119 if (ok != IMAP_SUCCESS) {
1120 log_warning(_("can't set deleted flags: 1:*\n"));
1124 ok = imap_cmd_expunge(session, NULL);
1125 if (ok != IMAP_SUCCESS) {
1126 log_warning(_("can't expunge\n"));
1130 dir = folder_item_get_path(item);
1131 if (is_dir_exist(dir))
1132 remove_all_numbered_files(dir);
1135 return IMAP_SUCCESS;
1138 gboolean imap_is_msg_changed(Folder *folder, FolderItem *item, MsgInfo *msginfo)
1140 /* TODO: properly implement this method */
1144 gint imap_close(Folder *folder, FolderItem *item)
1147 IMAPSession *session;
1149 g_return_val_if_fail(folder != NULL, -1);
1150 g_return_val_if_fail(item != NULL, -1);
1151 g_return_val_if_fail(item->path != NULL, -1);
1153 session = imap_session_get(folder);
1154 if (!session) return -1;
1156 if (session->mbox) {
1157 if (strcmp2(session->mbox, item->path) != 0) return -1;
1159 ok = imap_cmd_close(session);
1160 if (ok != IMAP_SUCCESS)
1161 log_warning(_("can't close folder\n"));
1163 g_free(session->mbox);
1164 session->mbox = NULL;
1172 gint imap_scan_tree(Folder *folder)
1174 FolderItem *item = NULL;
1175 IMAPSession *session;
1176 gchar *root_folder = NULL;
1178 g_return_val_if_fail(folder != NULL, -1);
1179 g_return_val_if_fail(folder->account != NULL, -1);
1181 session = imap_session_get(folder);
1183 if (!folder->node) {
1184 folder_tree_destroy(folder);
1185 item = folder_item_new(folder, folder->name, NULL);
1186 item->folder = folder;
1187 folder->node = item->node = g_node_new(item);
1192 if (folder->account->imap_dir && *folder->account->imap_dir) {
1195 Xstrdup_a(root_folder, folder->account->imap_dir, return -1);
1196 strtailchomp(root_folder, '/');
1197 extract_quote(root_folder, '"');
1198 real_path = imap_get_real_path
1199 (IMAP_FOLDER(folder), root_folder);
1200 debug_print("IMAP root directory: %s\n", real_path);
1201 if (imap_status(session, IMAP_FOLDER(folder), root_folder,
1202 NULL, NULL, NULL, NULL, NULL)
1204 if (imap_cmd_create(session, real_path)
1206 log_warning(_("can't create root folder %s\n"),
1216 item = FOLDER_ITEM(folder->node->data);
1217 if (!item || ((item->path || root_folder) &&
1218 strcmp2(item->path, root_folder) != 0)) {
1219 folder_tree_destroy(folder);
1220 item = folder_item_new(folder, folder->name, root_folder);
1221 item->folder = folder;
1222 folder->node = item->node = g_node_new(item);
1225 imap_scan_tree_recursive(session, FOLDER_ITEM(folder->node->data));
1226 imap_create_missing_folders(folder);
1231 static gint imap_scan_tree_recursive(IMAPSession *session, FolderItem *item)
1234 IMAPFolder *imapfolder;
1235 FolderItem *new_item;
1236 GSList *item_list, *cur;
1239 gchar *wildcard_path;
1243 g_return_val_if_fail(item != NULL, -1);
1244 g_return_val_if_fail(item->folder != NULL, -1);
1245 g_return_val_if_fail(item->no_sub == FALSE, -1);
1247 folder = item->folder;
1248 imapfolder = IMAP_FOLDER(folder);
1250 separator = imap_get_path_separator(imapfolder, item->path);
1252 if (folder->ui_func)
1253 folder->ui_func(folder, item, folder->ui_func_data);
1256 wildcard[0] = separator;
1259 real_path = imap_get_real_path(imapfolder, item->path);
1263 real_path = g_strdup("");
1266 Xstrcat_a(wildcard_path, real_path, wildcard,
1267 {g_free(real_path); return IMAP_ERROR;});
1268 QUOTE_IF_REQUIRED(wildcard_path, wildcard_path);
1270 imap_gen_send(session, "LIST \"\" %s",
1273 strtailchomp(real_path, separator);
1274 item_list = imap_parse_list(imapfolder, session, real_path, NULL);
1277 node = item->node->children;
1278 while (node != NULL) {
1279 FolderItem *old_item = FOLDER_ITEM(node->data);
1280 GNode *next = node->next;
1283 for (cur = item_list; cur != NULL; cur = cur->next) {
1284 FolderItem *cur_item = FOLDER_ITEM(cur->data);
1285 if (!strcmp2(old_item->path, cur_item->path)) {
1286 new_item = cur_item;
1291 debug_print("folder '%s' not found. removing...\n",
1293 folder_item_remove(old_item);
1295 old_item->no_sub = new_item->no_sub;
1296 old_item->no_select = new_item->no_select;
1297 if (old_item->no_sub == TRUE && node->children) {
1298 debug_print("folder '%s' doesn't have "
1299 "subfolders. removing...\n",
1301 folder_item_remove_children(old_item);
1308 for (cur = item_list; cur != NULL; cur = cur->next) {
1309 FolderItem *cur_item = FOLDER_ITEM(cur->data);
1311 for (node = item->node->children; node != NULL;
1312 node = node->next) {
1313 if (!strcmp2(FOLDER_ITEM(node->data)->path,
1315 new_item = FOLDER_ITEM(node->data);
1316 folder_item_destroy(cur_item);
1322 new_item = cur_item;
1323 debug_print("new folder '%s' found.\n", new_item->path);
1324 folder_item_append(item, new_item);
1327 if (!strcmp(new_item->path, "INBOX")) {
1328 new_item->stype = F_INBOX;
1329 folder->inbox = new_item;
1330 } else if (!item->parent || item->stype == F_INBOX) {
1333 base = g_basename(new_item->path);
1335 if (!folder->outbox && !strcasecmp(base, "Sent")) {
1336 new_item->stype = F_OUTBOX;
1337 folder->outbox = new_item;
1338 } else if (!folder->draft && !strcasecmp(base, "Drafts")) {
1339 new_item->stype = F_DRAFT;
1340 folder->draft = new_item;
1341 } else if (!folder->queue && !strcasecmp(base, "Queue")) {
1342 new_item->stype = F_QUEUE;
1343 folder->queue = new_item;
1344 } else if (!folder->trash && !strcasecmp(base, "Trash")) {
1345 new_item->stype = F_TRASH;
1346 folder->trash = new_item;
1350 if (new_item->no_sub == FALSE)
1351 imap_scan_tree_recursive(session, new_item);
1354 g_slist_free(item_list);
1356 return IMAP_SUCCESS;
1359 static GSList *imap_parse_list(IMAPFolder *folder, IMAPSession *session,
1360 const gchar *real_path, gchar *separator)
1362 gchar buf[IMAPBUFSIZE];
1364 gchar separator_str[16];
1367 gchar *loc_name, *loc_path;
1368 GSList *item_list = NULL;
1370 FolderItem *new_item;
1372 debug_print("getting list of %s ...\n",
1373 *real_path ? real_path : "\"\"");
1375 str = g_string_new(NULL);
1378 if (sock_gets(SESSION(session)->sock, buf, sizeof(buf)) <= 0) {
1379 log_warning(_("error occurred while getting LIST.\n"));
1383 if (buf[0] != '*' || buf[1] != ' ') {
1384 log_print("IMAP4< %s\n", buf);
1385 if (sscanf(buf, "%*d %16s", buf) < 1 ||
1386 strcmp(buf, "OK") != 0)
1387 log_warning(_("error occurred while getting LIST.\n"));
1391 debug_print("IMAP4< %s\n", buf);
1393 g_string_assign(str, buf);
1395 if (strncmp(p, "LIST ", 5) != 0) continue;
1398 if (*p != '(') continue;
1400 p = strchr_cpy(p, ')', flags, sizeof(flags));
1402 while (*p == ' ') p++;
1404 p = strchr_cpy(p, ' ', separator_str, sizeof(separator_str));
1406 extract_quote(separator_str, '"');
1407 if (!strcmp(separator_str, "NIL"))
1408 separator_str[0] = '\0';
1410 *separator = separator_str[0];
1413 while (*p == ' ') p++;
1414 if (*p == '{' || *p == '"')
1415 p = imap_parse_atom(SESSION(session)->sock, p,
1416 buf, sizeof(buf), str);
1418 strncpy2(buf, p, sizeof(buf));
1419 strtailchomp(buf, separator_str[0]);
1420 if (buf[0] == '\0') continue;
1421 if (!strcmp(buf, real_path)) continue;
1423 if (separator_str[0] != '\0')
1424 subst_char(buf, separator_str[0], '/');
1425 name = g_basename(buf);
1426 if (name[0] == '.') continue;
1428 loc_name = imap_modified_utf7_to_locale(name);
1429 loc_path = imap_modified_utf7_to_locale(buf);
1430 new_item = folder_item_new(FOLDER(folder), loc_name, loc_path);
1431 if (strcasestr(flags, "\\Noinferiors") != NULL)
1432 new_item->no_sub = TRUE;
1433 if (strcmp(buf, "INBOX") != 0 &&
1434 strcasestr(flags, "\\Noselect") != NULL)
1435 new_item->no_select = TRUE;
1437 item_list = g_slist_append(item_list, new_item);
1439 debug_print("folder '%s' found.\n", loc_path);
1444 g_string_free(str, TRUE);
1449 gint imap_create_tree(Folder *folder)
1451 g_return_val_if_fail(folder != NULL, -1);
1452 g_return_val_if_fail(folder->node != NULL, -1);
1453 g_return_val_if_fail(folder->node->data != NULL, -1);
1454 g_return_val_if_fail(folder->account != NULL, -1);
1456 imap_scan_tree(folder);
1457 imap_create_missing_folders(folder);
1462 static void imap_create_missing_folders(Folder *folder)
1464 g_return_if_fail(folder != NULL);
1467 folder->inbox = imap_create_special_folder
1468 (folder, F_INBOX, "INBOX");
1470 if (!folder->outbox)
1471 folder->outbox = imap_create_special_folder
1472 (folder, F_OUTBOX, "Sent");
1474 folder->draft = imap_create_special_folder
1475 (folder, F_DRAFT, "Drafts");
1477 folder->queue = imap_create_special_folder
1478 (folder, F_QUEUE, "Queue");
1481 folder->trash = imap_create_special_folder
1482 (folder, F_TRASH, "Trash");
1485 static FolderItem *imap_create_special_folder(Folder *folder,
1486 SpecialFolderItemType stype,
1490 FolderItem *new_item;
1492 g_return_val_if_fail(folder != NULL, NULL);
1493 g_return_val_if_fail(folder->node != NULL, NULL);
1494 g_return_val_if_fail(folder->node->data != NULL, NULL);
1495 g_return_val_if_fail(folder->account != NULL, NULL);
1496 g_return_val_if_fail(name != NULL, NULL);
1498 item = FOLDER_ITEM(folder->node->data);
1499 new_item = imap_create_folder(folder, item, name);
1502 g_warning("Can't create '%s'\n", name);
1503 if (!folder->inbox) return NULL;
1505 new_item = imap_create_folder(folder, folder->inbox, name);
1507 g_warning("Can't create '%s' under INBOX\n", name);
1509 new_item->stype = stype;
1511 new_item->stype = stype;
1516 static gchar *imap_folder_get_path(Folder *folder)
1520 g_return_val_if_fail(folder != NULL, NULL);
1521 g_return_val_if_fail(folder->account != NULL, NULL);
1523 folder_path = g_strconcat(get_imap_cache_dir(),
1525 folder->account->recv_server,
1527 folder->account->userid,
1533 static gchar *imap_item_get_path(Folder *folder, FolderItem *item)
1535 gchar *folder_path, *path;
1537 g_return_val_if_fail(folder != NULL, NULL);
1538 g_return_val_if_fail(item != NULL, NULL);
1539 folder_path = imap_folder_get_path(folder);
1541 g_return_val_if_fail(folder_path != NULL, NULL);
1542 if (folder_path[0] == G_DIR_SEPARATOR) {
1544 path = g_strconcat(folder_path, G_DIR_SEPARATOR_S,
1547 path = g_strdup(folder_path);
1550 path = g_strconcat(get_home_dir(), G_DIR_SEPARATOR_S,
1551 folder_path, G_DIR_SEPARATOR_S,
1554 path = g_strconcat(get_home_dir(), G_DIR_SEPARATOR_S,
1557 g_free(folder_path);
1562 FolderItem *imap_create_folder(Folder *folder, FolderItem *parent,
1565 gchar *dirpath, *imap_path;
1566 IMAPSession *session;
1567 FolderItem *new_item;
1573 g_return_val_if_fail(folder != NULL, NULL);
1574 g_return_val_if_fail(folder->account != NULL, NULL);
1575 g_return_val_if_fail(parent != NULL, NULL);
1576 g_return_val_if_fail(name != NULL, NULL);
1578 session = imap_session_get(folder);
1579 if (!session) return NULL;
1581 if (!parent->parent && strcmp(name, "INBOX") == 0)
1582 dirpath = g_strdup(name);
1583 else if (parent->path)
1584 dirpath = g_strconcat(parent->path, "/", name, NULL);
1585 else if ((p = strchr(name, '/')) != NULL && *(p + 1) != '\0')
1586 dirpath = g_strdup(name);
1587 else if (folder->account->imap_dir && *folder->account->imap_dir) {
1590 Xstrdup_a(imap_dir, folder->account->imap_dir, return NULL);
1591 strtailchomp(imap_dir, '/');
1592 dirpath = g_strconcat(imap_dir, "/", name, NULL);
1594 dirpath = g_strdup(name);
1596 /* keep trailing directory separator to create a folder that contains
1598 imap_path = imap_locale_to_modified_utf7(dirpath);
1599 strtailchomp(dirpath, '/');
1600 Xstrdup_a(new_name, name, {g_free(dirpath); return NULL;});
1601 strtailchomp(new_name, '/');
1602 separator = imap_get_path_separator(IMAP_FOLDER(folder), imap_path);
1603 imap_path_separator_subst(imap_path, separator);
1604 subst_char(new_name, '/', separator);
1606 if (strcmp(name, "INBOX") != 0) {
1609 gboolean exist = FALSE;
1611 argbuf = g_ptr_array_new();
1612 ok = imap_cmd_list(session, NULL, imap_path,
1614 if (ok != IMAP_SUCCESS) {
1615 log_warning(_("can't create mailbox: LIST failed\n"));
1618 ptr_array_free_strings(argbuf);
1619 g_ptr_array_free(argbuf, TRUE);
1623 for (i = 0; i < argbuf->len; i++) {
1625 str = g_ptr_array_index(argbuf, i);
1626 if (!strncmp(str, "LIST ", 5)) {
1631 ptr_array_free_strings(argbuf);
1632 g_ptr_array_free(argbuf, TRUE);
1635 ok = imap_cmd_create(session, imap_path);
1636 if (ok != IMAP_SUCCESS) {
1637 log_warning(_("can't create mailbox\n"));
1645 new_item = folder_item_new(folder, new_name, dirpath);
1646 folder_item_append(parent, new_item);
1650 dirpath = folder_item_get_path(new_item);
1651 if (!is_dir_exist(dirpath))
1652 make_dir_hier(dirpath);
1658 gint imap_rename_folder(Folder *folder, FolderItem *item, const gchar *name)
1662 gchar *real_oldpath;
1663 gchar *real_newpath;
1665 gchar *old_cache_dir;
1666 gchar *new_cache_dir;
1667 IMAPSession *session;
1670 gint exists, recent, unseen;
1671 guint32 uid_validity;
1673 g_return_val_if_fail(folder != NULL, -1);
1674 g_return_val_if_fail(item != NULL, -1);
1675 g_return_val_if_fail(item->path != NULL, -1);
1676 g_return_val_if_fail(name != NULL, -1);
1678 session = imap_session_get(folder);
1679 if (!session) return -1;
1681 real_oldpath = imap_get_real_path(IMAP_FOLDER(folder), item->path);
1683 g_free(session->mbox);
1684 session->mbox = NULL;
1685 ok = imap_cmd_examine(session, "INBOX",
1686 &exists, &recent, &unseen, &uid_validity);
1687 if (ok != IMAP_SUCCESS) {
1688 g_free(real_oldpath);
1692 separator = imap_get_path_separator(IMAP_FOLDER(folder), item->path);
1693 if (strchr(item->path, G_DIR_SEPARATOR)) {
1694 dirpath = g_dirname(item->path);
1695 newpath = g_strconcat(dirpath, G_DIR_SEPARATOR_S, name, NULL);
1698 newpath = g_strdup(name);
1700 real_newpath = imap_locale_to_modified_utf7(newpath);
1701 imap_path_separator_subst(real_newpath, separator);
1703 ok = imap_cmd_rename(session, real_oldpath, real_newpath);
1704 if (ok != IMAP_SUCCESS) {
1705 log_warning(_("can't rename mailbox: %s to %s\n"),
1706 real_oldpath, real_newpath);
1707 g_free(real_oldpath);
1709 g_free(real_newpath);
1714 item->name = g_strdup(name);
1716 old_cache_dir = folder_item_get_path(item);
1718 paths[0] = g_strdup(item->path);
1720 g_node_traverse(item->node, G_PRE_ORDER, G_TRAVERSE_ALL, -1,
1721 imap_rename_folder_func, paths);
1723 if (is_dir_exist(old_cache_dir)) {
1724 new_cache_dir = folder_item_get_path(item);
1725 if (rename(old_cache_dir, new_cache_dir) < 0) {
1726 FILE_OP_ERROR(old_cache_dir, "rename");
1728 g_free(new_cache_dir);
1731 g_free(old_cache_dir);
1734 g_free(real_oldpath);
1735 g_free(real_newpath);
1740 gint imap_remove_folder(Folder *folder, FolderItem *item)
1743 IMAPSession *session;
1746 gint exists, recent, unseen;
1747 guint32 uid_validity;
1749 g_return_val_if_fail(folder != NULL, -1);
1750 g_return_val_if_fail(item != NULL, -1);
1751 g_return_val_if_fail(item->path != NULL, -1);
1753 session = imap_session_get(folder);
1754 if (!session) return -1;
1756 path = imap_get_real_path(IMAP_FOLDER(folder), item->path);
1758 ok = imap_cmd_examine(session, "INBOX",
1759 &exists, &recent, &unseen, &uid_validity);
1760 if (ok != IMAP_SUCCESS) {
1765 ok = imap_cmd_delete(session, path);
1766 if (ok != IMAP_SUCCESS) {
1767 log_warning(_("can't delete mailbox\n"));
1773 cache_dir = folder_item_get_path(item);
1774 if (is_dir_exist(cache_dir) && remove_dir_recursive(cache_dir) < 0)
1775 g_warning("can't remove directory '%s'\n", cache_dir);
1777 folder_item_remove(item);
1782 static GSList *imap_get_uncached_messages(IMAPSession *session,
1784 MsgNumberList *numlist)
1787 GSList *newlist = NULL;
1788 GSList *llast = NULL;
1791 GSList *seq_list, *cur;
1794 g_return_val_if_fail(session != NULL, NULL);
1795 g_return_val_if_fail(item != NULL, NULL);
1796 g_return_val_if_fail(item->folder != NULL, NULL);
1797 g_return_val_if_fail(FOLDER_CLASS(item->folder) == &imap_class, NULL);
1799 seq_list = imap_get_seq_set_from_numlist(numlist);
1800 for (cur = seq_list; cur != NULL; cur = g_slist_next(cur)) {
1801 imapset = cur->data;
1803 if (imap_cmd_envelope(session, imapset)
1805 log_warning(_("can't get envelope\n"));
1809 str = g_string_new(NULL);
1812 if ((tmp = sock_getline(SESSION(session)->sock)) == NULL) {
1813 log_warning(_("error occurred while getting envelope.\n"));
1814 g_string_free(str, TRUE);
1818 if (tmp[0] != '*' || tmp[1] != ' ') {
1819 log_print("IMAP4< %s\n", tmp);
1823 if (strstr(tmp, "FETCH") == NULL) {
1824 log_print("IMAP4< %s\n", tmp);
1828 log_print("IMAP4< %s\n", tmp);
1829 g_string_assign(str, tmp);
1832 msginfo = imap_parse_envelope
1833 (SESSION(session)->sock, item, str);
1835 log_warning(_("can't parse envelope: %s\n"), str->str);
1838 if (item->stype == F_QUEUE) {
1839 MSG_SET_TMP_FLAGS(msginfo->flags, MSG_QUEUED);
1840 } else if (item->stype == F_DRAFT) {
1841 MSG_SET_TMP_FLAGS(msginfo->flags, MSG_DRAFT);
1844 msginfo->folder = item;
1847 llast = newlist = g_slist_append(newlist, msginfo);
1849 llast = g_slist_append(llast, msginfo);
1850 llast = llast->next;
1854 g_string_free(str, TRUE);
1856 imap_seq_set_free(seq_list);
1861 static void imap_delete_all_cached_messages(FolderItem *item)
1865 g_return_if_fail(item != NULL);
1866 g_return_if_fail(item->folder != NULL);
1867 g_return_if_fail(FOLDER_CLASS(item->folder) == &imap_class);
1869 debug_print("Deleting all cached messages...\n");
1871 dir = folder_item_get_path(item);
1872 if (is_dir_exist(dir))
1873 remove_all_numbered_files(dir);
1876 debug_print("done.\n");
1880 static SockInfo *imap_open_tunnel(const gchar *server,
1881 const gchar *tunnelcmd,
1884 static SockInfo *imap_open_tunnel(const gchar *server,
1885 const gchar *tunnelcmd)
1890 if ((sock = sock_connect_cmd(server, tunnelcmd)) == NULL) {
1891 log_warning(_("Can't establish IMAP4 session with: %s\n"),
1896 return imap_init_sock(sock, ssl_type);
1898 return imap_init_sock(sock);
1904 static SockInfo *imap_open(const gchar *server, gushort port,
1907 static SockInfo *imap_open(const gchar *server, gushort port)
1912 if ((sock = sock_connect(server, port)) == NULL) {
1913 log_warning(_("Can't connect to IMAP4 server: %s:%d\n"),
1919 if (ssl_type == SSL_TUNNEL && !ssl_init_socket(sock)) {
1920 log_warning(_("Can't establish IMAP4 session with: %s:%d\n"),
1930 static SockInfo *imap_init_sock(SockInfo *sock, SSLType ssl_type)
1932 static SockInfo *imap_init_sock(SockInfo *sock)
1939 static GList *imap_parse_namespace_str(gchar *str)
1944 IMAPNameSpace *namespace;
1945 GList *ns_list = NULL;
1947 while (*p != '\0') {
1948 /* parse ("#foo" "/") */
1950 while (*p && *p != '(') p++;
1951 if (*p == '\0') break;
1954 while (*p && *p != '"') p++;
1955 if (*p == '\0') break;
1959 while (*p && *p != '"') p++;
1960 if (*p == '\0') break;
1964 while (*p && isspace(*p)) p++;
1965 if (*p == '\0') break;
1966 if (strncmp(p, "NIL", 3) == 0)
1968 else if (*p == '"') {
1971 while (*p && *p != '"') p++;
1972 if (*p == '\0') break;
1977 while (*p && *p != ')') p++;
1978 if (*p == '\0') break;
1981 namespace = g_new(IMAPNameSpace, 1);
1982 namespace->name = g_strdup(name);
1983 namespace->separator = separator ? separator[0] : '\0';
1984 ns_list = g_list_append(ns_list, namespace);
1990 static void imap_parse_namespace(IMAPSession *session, IMAPFolder *folder)
1995 g_return_if_fail(session != NULL);
1996 g_return_if_fail(folder != NULL);
1998 if (folder->ns_personal != NULL ||
1999 folder->ns_others != NULL ||
2000 folder->ns_shared != NULL)
2003 if (!imap_has_capability(session, "NAMESPACE")) {
2004 imap_get_namespace_by_list(session, folder);
2008 if (imap_cmd_namespace(session, &ns_str)
2010 log_warning(_("can't get namespace\n"));
2014 str_array = strsplit_parenthesis(ns_str, '(', ')', 3);
2015 if (str_array == NULL) {
2017 imap_get_namespace_by_list(session, folder);
2021 folder->ns_personal = imap_parse_namespace_str(str_array[0]);
2022 if (str_array[0] && str_array[1])
2023 folder->ns_others = imap_parse_namespace_str(str_array[1]);
2024 if (str_array[0] && str_array[1] && str_array[2])
2025 folder->ns_shared = imap_parse_namespace_str(str_array[2]);
2026 g_strfreev(str_array);
2030 static void imap_get_namespace_by_list(IMAPSession *session, IMAPFolder *folder)
2032 GSList *item_list, *cur;
2033 gchar separator = '\0';
2034 IMAPNameSpace *namespace;
2036 g_return_if_fail(session != NULL);
2037 g_return_if_fail(folder != NULL);
2039 if (folder->ns_personal != NULL ||
2040 folder->ns_others != NULL ||
2041 folder->ns_shared != NULL)
2044 imap_gen_send(session, "LIST \"\" \"\"");
2045 item_list = imap_parse_list(folder, session, "", &separator);
2046 for (cur = item_list; cur != NULL; cur = cur->next)
2047 folder_item_destroy(FOLDER_ITEM(cur->data));
2048 g_slist_free(item_list);
2050 namespace = g_new(IMAPNameSpace, 1);
2051 namespace->name = g_strdup("");
2052 namespace->separator = separator;
2053 folder->ns_personal = g_list_append(NULL, namespace);
2056 static IMAPNameSpace *imap_find_namespace_from_list(GList *ns_list,
2059 IMAPNameSpace *namespace = NULL;
2060 gchar *tmp_path, *name;
2062 if (!path) path = "";
2064 for (; ns_list != NULL; ns_list = ns_list->next) {
2065 IMAPNameSpace *tmp_ns = ns_list->data;
2067 Xstrcat_a(tmp_path, path, "/", return namespace);
2068 Xstrdup_a(name, tmp_ns->name, return namespace);
2069 if (tmp_ns->separator && tmp_ns->separator != '/') {
2070 subst_char(tmp_path, tmp_ns->separator, '/');
2071 subst_char(name, tmp_ns->separator, '/');
2073 if (strncmp(tmp_path, name, strlen(name)) == 0)
2080 static IMAPNameSpace *imap_find_namespace(IMAPFolder *folder,
2083 IMAPNameSpace *namespace;
2085 g_return_val_if_fail(folder != NULL, NULL);
2087 namespace = imap_find_namespace_from_list(folder->ns_personal, path);
2088 if (namespace) return namespace;
2089 namespace = imap_find_namespace_from_list(folder->ns_others, path);
2090 if (namespace) return namespace;
2091 namespace = imap_find_namespace_from_list(folder->ns_shared, path);
2092 if (namespace) return namespace;
2097 static gchar imap_get_path_separator(IMAPFolder *folder, const gchar *path)
2099 IMAPNameSpace *namespace;
2100 gchar separator = '/';
2102 namespace = imap_find_namespace(folder, path);
2103 if (namespace && namespace->separator)
2104 separator = namespace->separator;
2109 static gchar *imap_get_real_path(IMAPFolder *folder, const gchar *path)
2114 g_return_val_if_fail(folder != NULL, NULL);
2115 g_return_val_if_fail(path != NULL, NULL);
2117 real_path = imap_locale_to_modified_utf7(path);
2118 separator = imap_get_path_separator(folder, path);
2119 imap_path_separator_subst(real_path, separator);
2124 static gchar *imap_parse_atom(SockInfo *sock, gchar *src,
2125 gchar *dest, gint dest_len, GString *str)
2127 gchar *cur_pos = src;
2130 g_return_val_if_fail(str != NULL, cur_pos);
2132 /* read the next line if the current response buffer is empty */
2133 while (isspace(*cur_pos)) cur_pos++;
2134 while (*cur_pos == '\0') {
2135 if ((nextline = sock_getline(sock)) == NULL)
2137 g_string_assign(str, nextline);
2139 strretchomp(nextline);
2140 /* log_print("IMAP4< %s\n", nextline); */
2141 debug_print("IMAP4< %s\n", nextline);
2144 while (isspace(*cur_pos)) cur_pos++;
2147 if (!strncmp(cur_pos, "NIL", 3)) {
2150 } else if (*cur_pos == '\"') {
2153 p = get_quoted(cur_pos, '\"', dest, dest_len);
2154 cur_pos = p ? p : cur_pos + 2;
2155 } else if (*cur_pos == '{') {
2160 cur_pos = strchr_cpy(cur_pos + 1, '}', buf, sizeof(buf));
2162 g_return_val_if_fail(len >= 0, cur_pos);
2164 g_string_truncate(str, 0);
2168 if ((nextline = sock_getline(sock)) == NULL)
2170 line_len += strlen(nextline);
2171 g_string_append(str, nextline);
2173 strretchomp(nextline);
2174 /* log_print("IMAP4< %s\n", nextline); */
2175 debug_print("IMAP4< %s\n", nextline);
2177 } while (line_len < len);
2179 memcpy(dest, cur_pos, MIN(len, dest_len - 1));
2180 dest[MIN(len, dest_len - 1)] = '\0';
2187 static gchar *imap_get_header(SockInfo *sock, gchar *cur_pos, gchar **headers,
2197 g_return_val_if_fail(str != NULL, cur_pos);
2199 while (isspace(*cur_pos)) cur_pos++;
2201 g_return_val_if_fail(*cur_pos == '{', cur_pos);
2203 cur_pos = strchr_cpy(cur_pos + 1, '}', buf, sizeof(buf));
2205 g_return_val_if_fail(len >= 0, cur_pos);
2207 g_string_truncate(str, 0);
2211 if ((nextline = sock_getline(sock)) == NULL)
2213 block_len += strlen(nextline);
2214 g_string_append(str, nextline);
2216 strretchomp(nextline);
2217 /* debug_print("IMAP4< %s\n", nextline); */
2219 } while (block_len < len);
2221 debug_print("IMAP4< [contents of BODY.PEEK[HEADER.FIELDS (...)]]\n");
2223 *headers = g_strndup(cur_pos, len);
2226 while (isspace(*cur_pos)) cur_pos++;
2227 while (*cur_pos == '\0') {
2228 if ((nextline = sock_getline(sock)) == NULL)
2230 g_string_assign(str, nextline);
2232 strretchomp(nextline);
2233 debug_print("IMAP4< %s\n", nextline);
2236 while (isspace(*cur_pos)) cur_pos++;
2242 static MsgFlags imap_parse_flags(const gchar *flag_str)
2244 const gchar *p = flag_str;
2245 MsgFlags flags = {0, 0};
2247 flags.perm_flags = MSG_UNREAD;
2249 while ((p = strchr(p, '\\')) != NULL) {
2252 if (g_strncasecmp(p, "Recent", 6) == 0 && MSG_IS_UNREAD(flags)) {
2253 MSG_SET_PERM_FLAGS(flags, MSG_NEW);
2254 } else if (g_strncasecmp(p, "Seen", 4) == 0) {
2255 MSG_UNSET_PERM_FLAGS(flags, MSG_NEW|MSG_UNREAD);
2256 } else if (g_strncasecmp(p, "Deleted", 7) == 0) {
2257 MSG_SET_PERM_FLAGS(flags, MSG_DELETED);
2258 } else if (g_strncasecmp(p, "Flagged", 7) == 0) {
2259 MSG_SET_PERM_FLAGS(flags, MSG_MARKED);
2260 } else if (g_strncasecmp(p, "Answered", 8) == 0) {
2261 MSG_SET_PERM_FLAGS(flags, MSG_REPLIED);
2268 static MsgInfo *imap_parse_envelope(SockInfo *sock, FolderItem *item,
2271 gchar buf[IMAPBUFSIZE];
2272 MsgInfo *msginfo = NULL;
2277 MsgFlags flags = {0, 0}, imap_flags = {0, 0};
2279 g_return_val_if_fail(line_str != NULL, NULL);
2280 g_return_val_if_fail(line_str->str[0] == '*' &&
2281 line_str->str[1] == ' ', NULL);
2283 MSG_SET_TMP_FLAGS(flags, MSG_IMAP);
2284 if (item->stype == F_QUEUE) {
2285 MSG_SET_TMP_FLAGS(flags, MSG_QUEUED);
2286 } else if (item->stype == F_DRAFT) {
2287 MSG_SET_TMP_FLAGS(flags, MSG_DRAFT);
2290 cur_pos = line_str->str + 2;
2292 #define PARSE_ONE_ELEMENT(ch) \
2294 cur_pos = strchr_cpy(cur_pos, ch, buf, sizeof(buf)); \
2295 if (cur_pos == NULL) { \
2296 g_warning("cur_pos == NULL\n"); \
2297 procmsg_msginfo_free(msginfo); \
2302 PARSE_ONE_ELEMENT(' ');
2305 PARSE_ONE_ELEMENT(' ');
2306 g_return_val_if_fail(!strcmp(buf, "FETCH"), NULL);
2308 g_return_val_if_fail(*cur_pos == '(', NULL);
2311 while (*cur_pos != '\0' && *cur_pos != ')') {
2312 while (*cur_pos == ' ') cur_pos++;
2314 if (!strncmp(cur_pos, "UID ", 4)) {
2316 uid = strtoul(cur_pos, &cur_pos, 10);
2317 } else if (!strncmp(cur_pos, "FLAGS ", 6)) {
2319 if (*cur_pos != '(') {
2320 g_warning("*cur_pos != '('\n");
2321 procmsg_msginfo_free(msginfo);
2325 PARSE_ONE_ELEMENT(')');
2326 imap_flags = imap_parse_flags(buf);
2327 } else if (!strncmp(cur_pos, "RFC822.SIZE ", 12)) {
2329 size = strtol(cur_pos, &cur_pos, 10);
2330 } else if (!strncmp(cur_pos, "BODY[HEADER.FIELDS ", 19)) {
2334 if (*cur_pos != '(') {
2335 g_warning("*cur_pos != '('\n");
2336 procmsg_msginfo_free(msginfo);
2340 PARSE_ONE_ELEMENT(')');
2341 if (*cur_pos != ']') {
2342 g_warning("*cur_pos != ']'\n");
2343 procmsg_msginfo_free(msginfo);
2348 cur_pos = imap_get_header(sock, cur_pos, &headers,
2350 msginfo = procheader_parse_str(headers, flags, FALSE, FALSE);
2353 g_warning("invalid FETCH response: %s\n", cur_pos);
2359 msginfo->msgnum = uid;
2360 msginfo->size = size;
2361 msginfo->flags.tmp_flags |= imap_flags.tmp_flags;
2362 msginfo->flags.perm_flags = imap_flags.perm_flags;
2368 static gchar *imap_get_flag_str(IMAPFlags flags)
2373 str = g_string_new(NULL);
2375 if (IMAP_IS_SEEN(flags)) g_string_append(str, "\\Seen ");
2376 if (IMAP_IS_ANSWERED(flags)) g_string_append(str, "\\Answered ");
2377 if (IMAP_IS_FLAGGED(flags)) g_string_append(str, "\\Flagged ");
2378 if (IMAP_IS_DELETED(flags)) g_string_append(str, "\\Deleted ");
2379 if (IMAP_IS_DRAFT(flags)) g_string_append(str, "\\Draft");
2381 if (str->len > 0 && str->str[str->len - 1] == ' ')
2382 g_string_truncate(str, str->len - 1);
2385 g_string_free(str, FALSE);
2390 static gint imap_set_message_flags(IMAPSession *session,
2391 MsgNumberList *numlist,
2398 GSList *seq_list, *cur;
2401 flag_str = imap_get_flag_str(flags);
2402 cmd = g_strconcat(is_set ? "+FLAGS.SILENT (" : "-FLAGS.SILENT (",
2403 flag_str, ")", NULL);
2406 seq_list = imap_get_seq_set_from_numlist(numlist);
2407 for (cur = seq_list; cur != NULL; cur = g_slist_next(cur)) {
2408 imapset = cur->data;
2410 ok = imap_cmd_store(session, imapset, cmd);
2412 imap_seq_set_free(seq_list);
2418 static gint imap_select(IMAPSession *session, IMAPFolder *folder,
2420 gint *exists, gint *recent, gint *unseen,
2421 guint32 *uid_validity)
2425 gint exists_, recent_, unseen_, uid_validity_;
2427 if (!exists || !recent || !unseen || !uid_validity) {
2428 if (session->mbox && strcmp(session->mbox, path) == 0)
2429 return IMAP_SUCCESS;
2433 uid_validity = &uid_validity_;
2436 g_free(session->mbox);
2437 session->mbox = NULL;
2439 real_path = imap_get_real_path(folder, path);
2440 ok = imap_cmd_select(session, real_path,
2441 exists, recent, unseen, uid_validity);
2442 if (ok != IMAP_SUCCESS)
2443 log_warning(_("can't select folder: %s\n"), real_path);
2445 session->mbox = g_strdup(path);
2446 session->folder_content_changed = FALSE;
2453 #define THROW(err) { ok = err; goto catch; }
2455 static gint imap_status(IMAPSession *session, IMAPFolder *folder,
2457 gint *messages, gint *recent,
2458 guint32 *uid_next, guint32 *uid_validity,
2464 GPtrArray *argbuf = NULL;
2467 if (messages && recent && uid_next && uid_validity && unseen) {
2468 *messages = *recent = *uid_next = *uid_validity = *unseen = 0;
2469 argbuf = g_ptr_array_new();
2472 real_path = imap_get_real_path(folder, path);
2473 QUOTE_IF_REQUIRED(real_path_, real_path);
2474 imap_gen_send(session, "STATUS %s "
2475 "(MESSAGES RECENT UIDNEXT UIDVALIDITY UNSEEN)",
2478 ok = imap_cmd_ok(session, argbuf);
2479 if (ok != IMAP_SUCCESS || !argbuf) THROW(ok);
2481 str = search_array_str(argbuf, "STATUS");
2482 if (!str) THROW(IMAP_ERROR);
2484 str = strchr(str, '(');
2485 if (!str) THROW(IMAP_ERROR);
2487 while (*str != '\0' && *str != ')') {
2488 while (*str == ' ') str++;
2490 if (!strncmp(str, "MESSAGES ", 9)) {
2492 *messages = strtol(str, &str, 10);
2493 } else if (!strncmp(str, "RECENT ", 7)) {
2495 *recent = strtol(str, &str, 10);
2496 } else if (!strncmp(str, "UIDNEXT ", 8)) {
2498 *uid_next = strtoul(str, &str, 10);
2499 } else if (!strncmp(str, "UIDVALIDITY ", 12)) {
2501 *uid_validity = strtoul(str, &str, 10);
2502 } else if (!strncmp(str, "UNSEEN ", 7)) {
2504 *unseen = strtol(str, &str, 10);
2506 g_warning("invalid STATUS response: %s\n", str);
2514 ptr_array_free_strings(argbuf);
2515 g_ptr_array_free(argbuf, TRUE);
2523 static gboolean imap_has_capability(IMAPSession *session, const gchar *cap)
2527 for (p = session->capability; *p != NULL; ++p) {
2528 if (!g_strcasecmp(*p, cap))
2535 void imap_free_capabilities(IMAPSession *session)
2537 g_strfreev(session->capability);
2538 session->capability = NULL;
2541 /* low-level IMAP4rev1 commands */
2543 static gint imap_cmd_authenticate(IMAPSession *session, const gchar *user,
2544 const gchar *pass, IMAPAuthType type)
2551 gchar hexdigest[33];
2555 auth_type = "CRAM-MD5";
2557 imap_gen_send(session, "AUTHENTICATE %s", auth_type);
2558 ok = imap_gen_recv(session, &buf);
2559 if (ok != IMAP_SUCCESS || buf[0] != '+' || buf[1] != ' ') {
2564 challenge = g_malloc(strlen(buf + 2) + 1);
2565 challenge_len = base64_decode(challenge, buf + 2, -1);
2566 challenge[challenge_len] = '\0';
2568 log_print("IMAP< [Decoded: %s]\n", challenge);
2570 md5_hex_hmac(hexdigest, challenge, challenge_len, pass, strlen(pass));
2573 response = g_strdup_printf("%s %s", user, hexdigest);
2574 log_print("IMAP> [Encoded: %s]\n", response);
2575 response64 = g_malloc((strlen(response) + 3) * 2 + 1);
2576 base64_encode(response64, response, strlen(response));
2579 log_print("IMAP> %s\n", response64);
2580 sock_puts(SESSION(session)->sock, response64);
2581 ok = imap_cmd_ok(session, NULL);
2582 if (ok != IMAP_SUCCESS)
2583 log_warning(_("IMAP4 authentication failed.\n"));
2588 static gint imap_cmd_login(IMAPSession *session,
2589 const gchar *user, const gchar *pass)
2591 gchar *user_, *pass_;
2594 QUOTE_IF_REQUIRED(user_, user);
2595 QUOTE_IF_REQUIRED(pass_, pass);
2596 imap_gen_send(session, "LOGIN %s %s", user_, pass_);
2598 ok = imap_cmd_ok(session, NULL);
2599 if (ok != IMAP_SUCCESS)
2600 log_warning(_("IMAP4 login failed.\n"));
2605 static gint imap_cmd_logout(IMAPSession *session)
2607 imap_gen_send(session, "LOGOUT");
2608 return imap_cmd_ok(session, NULL);
2611 static gint imap_cmd_noop(IMAPSession *session)
2613 imap_gen_send(session, "NOOP");
2614 return imap_cmd_ok(session, NULL);
2617 static gint imap_cmd_starttls(IMAPSession *session)
2619 imap_gen_send(session, "STARTTLS");
2620 return imap_cmd_ok(session, NULL);
2623 #define THROW(err) { ok = err; goto catch; }
2625 static gint imap_cmd_namespace(IMAPSession *session, gchar **ns_str)
2631 argbuf = g_ptr_array_new();
2633 imap_gen_send(session, "NAMESPACE");
2634 if ((ok = imap_cmd_ok(session, argbuf)) != IMAP_SUCCESS) THROW(ok);
2636 str = search_array_str(argbuf, "NAMESPACE");
2637 if (!str) THROW(IMAP_ERROR);
2639 *ns_str = g_strdup(str);
2642 ptr_array_free_strings(argbuf);
2643 g_ptr_array_free(argbuf, TRUE);
2650 static gint imap_cmd_list(IMAPSession *session, const gchar *ref,
2651 const gchar *mailbox, GPtrArray *argbuf)
2653 gchar *ref_, *mailbox_;
2655 if (!ref) ref = "\"\"";
2656 if (!mailbox) mailbox = "\"\"";
2658 QUOTE_IF_REQUIRED(ref_, ref);
2659 QUOTE_IF_REQUIRED(mailbox_, mailbox);
2660 imap_gen_send(session, "LIST %s %s", ref_, mailbox_);
2662 return imap_cmd_ok(session, argbuf);
2665 #define THROW goto catch
2667 static gint imap_cmd_do_select(IMAPSession *session, const gchar *folder,
2669 gint *exists, gint *recent, gint *unseen,
2670 guint32 *uid_validity)
2678 *exists = *recent = *unseen = *uid_validity = 0;
2679 argbuf = g_ptr_array_new();
2682 select_cmd = "EXAMINE";
2684 select_cmd = "SELECT";
2686 QUOTE_IF_REQUIRED(folder_, folder);
2687 imap_gen_send(session, "%s %s", select_cmd, folder_);
2689 if ((ok = imap_cmd_ok(session, argbuf)) != IMAP_SUCCESS) THROW;
2691 resp_str = search_array_contain_str(argbuf, "EXISTS");
2693 if (sscanf(resp_str,"%d EXISTS", exists) != 1) {
2694 g_warning("imap_cmd_select(): invalid EXISTS line.\n");
2699 resp_str = search_array_contain_str(argbuf, "RECENT");
2701 if (sscanf(resp_str, "%d RECENT", recent) != 1) {
2702 g_warning("imap_cmd_select(): invalid RECENT line.\n");
2707 resp_str = search_array_contain_str(argbuf, "UIDVALIDITY");
2709 if (sscanf(resp_str, "OK [UIDVALIDITY %u] ", uid_validity)
2711 g_warning("imap_cmd_select(): invalid UIDVALIDITY line.\n");
2716 resp_str = search_array_contain_str(argbuf, "UNSEEN");
2718 if (sscanf(resp_str, "OK [UNSEEN %d] ", unseen) != 1) {
2719 g_warning("imap_cmd_select(): invalid UNSEEN line.\n");
2725 ptr_array_free_strings(argbuf);
2726 g_ptr_array_free(argbuf, TRUE);
2731 static gint imap_cmd_select(IMAPSession *session, const gchar *folder,
2732 gint *exists, gint *recent, gint *unseen,
2733 guint32 *uid_validity)
2735 return imap_cmd_do_select(session, folder, FALSE,
2736 exists, recent, unseen, uid_validity);
2739 static gint imap_cmd_examine(IMAPSession *session, const gchar *folder,
2740 gint *exists, gint *recent, gint *unseen,
2741 guint32 *uid_validity)
2743 return imap_cmd_do_select(session, folder, TRUE,
2744 exists, recent, unseen, uid_validity);
2749 static gint imap_cmd_create(IMAPSession *session, const gchar *folder)
2753 QUOTE_IF_REQUIRED(folder_, folder);
2754 imap_gen_send(session, "CREATE %s", folder_);
2756 return imap_cmd_ok(session, NULL);
2759 static gint imap_cmd_rename(IMAPSession *session, const gchar *old_folder,
2760 const gchar *new_folder)
2762 gchar *old_folder_, *new_folder_;
2764 QUOTE_IF_REQUIRED(old_folder_, old_folder);
2765 QUOTE_IF_REQUIRED(new_folder_, new_folder);
2766 imap_gen_send(session, "RENAME %s %s", old_folder_, new_folder_);
2768 return imap_cmd_ok(session, NULL);
2771 static gint imap_cmd_delete(IMAPSession *session, const gchar *folder)
2775 QUOTE_IF_REQUIRED(folder_, folder);
2776 imap_gen_send(session, "DELETE %s", folder_);
2778 return imap_cmd_ok(session, NULL);
2781 static gint imap_cmd_search(IMAPSession *session, const gchar *criteria, GSList **list)
2787 g_return_val_if_fail(criteria != NULL, IMAP_ERROR);
2788 g_return_val_if_fail(list != NULL, IMAP_ERROR);
2792 argbuf = g_ptr_array_new();
2793 imap_gen_send(session, "UID SEARCH %s", criteria);
2795 ok = imap_cmd_ok(session, argbuf);
2796 if (ok != IMAP_SUCCESS) {
2797 ptr_array_free_strings(argbuf);
2798 g_ptr_array_free(argbuf, TRUE);
2802 if ((uidlist = search_array_str(argbuf, "SEARCH ")) != NULL) {
2803 gchar **strlist, **p;
2805 strlist = g_strsplit(uidlist + 7, " ", 0);
2806 for (p = strlist; *p != NULL; ++p) {
2809 if (sscanf(*p, "%d", &msgnum) == 1)
2810 *list = g_slist_append(*list, GINT_TO_POINTER(msgnum));
2812 g_strfreev(strlist);
2814 ptr_array_free_strings(argbuf);
2815 g_ptr_array_free(argbuf, TRUE);
2817 return IMAP_SUCCESS;
2820 static gint imap_cmd_fetch(IMAPSession *session, guint32 uid, const gchar *filename)
2828 g_return_val_if_fail(filename != NULL, IMAP_ERROR);
2830 imap_gen_send(session, "UID FETCH %d BODY.PEEK[]", uid);
2832 while ((ok = imap_gen_recv(session, &buf)) == IMAP_SUCCESS) {
2833 if (buf[0] != '*' || buf[1] != ' ') {
2837 if (strstr(buf, "FETCH") != NULL) break;
2840 if (ok != IMAP_SUCCESS) {
2845 #define RETURN_ERROR_IF_FAIL(cond) \
2848 return IMAP_ERROR; \
2851 cur_pos = strchr(buf, '{');
2852 RETURN_ERROR_IF_FAIL(cur_pos != NULL);
2853 cur_pos = strchr_cpy(cur_pos + 1, '}', size_str, sizeof(size_str));
2854 RETURN_ERROR_IF_FAIL(cur_pos != NULL);
2855 size_num = atol(size_str);
2856 RETURN_ERROR_IF_FAIL(size_num >= 0);
2858 RETURN_ERROR_IF_FAIL(*cur_pos == '\0');
2860 #undef RETURN_ERROR_IF_FAIL
2864 if (recv_bytes_write_to_file(SESSION(session)->sock, size_num, filename) != 0)
2867 if (imap_gen_recv(session, &buf) != IMAP_SUCCESS) {
2872 if (buf[0] == '\0' || buf[strlen(buf) - 1] != ')') {
2878 ok = imap_cmd_ok(session, NULL);
2883 static gint imap_cmd_append(IMAPSession *session, const gchar *destfolder,
2884 const gchar *file, IMAPFlags flags, guint32 *new_uid)
2892 gchar buf[BUFFSIZE];
2897 g_return_val_if_fail(file != NULL, IMAP_ERROR);
2899 size = get_file_size_as_crlf(file);
2900 if ((fp = fopen(file, "rb")) == NULL) {
2901 FILE_OP_ERROR(file, "fopen");
2904 QUOTE_IF_REQUIRED(destfolder_, destfolder);
2905 flag_str = imap_get_flag_str(flags);
2906 imap_gen_send(session, "APPEND %s (%s) {%d}",
2907 destfolder_, flag_str, size);
2910 ok = imap_gen_recv(session, &ret);
2911 if (ok != IMAP_SUCCESS || ret[0] != '+' || ret[1] != ' ') {
2912 log_warning(_("can't append %s to %s\n"), file, destfolder_);
2919 log_print("IMAP4> %s\n", _("(sending file...)"));
2921 while (fgets(buf, sizeof(buf), fp) != NULL) {
2923 if (sock_puts(SESSION(session)->sock, buf) < 0) {
2930 FILE_OP_ERROR(file, "fgets");
2935 sock_puts(SESSION(session)->sock, "");
2939 if (new_uid != NULL)
2942 if (new_uid != NULL && session->uidplus) {
2943 argbuf = g_ptr_array_new();
2945 ok = imap_cmd_ok(session, argbuf);
2946 if ((ok == IMAP_SUCCESS) && (argbuf->len > 0)) {
2947 resp_str = g_ptr_array_index(argbuf, argbuf->len - 1);
2949 sscanf(resp_str, "%*u OK [APPENDUID %*u %u]",
2951 *new_uid = new_uid_;
2955 ptr_array_free_strings(argbuf);
2956 g_ptr_array_free(argbuf, TRUE);
2958 ok = imap_cmd_ok(session, NULL);
2960 if (ok != IMAP_SUCCESS)
2961 log_warning(_("can't append message to %s\n"),
2967 static MsgNumberList *imapset_to_numlist(IMAPSet imapset)
2969 gchar **ranges, **range;
2971 MsgNumberList *uids = NULL;
2973 ranges = g_strsplit(imapset, ",", 0);
2974 for (range = ranges; *range != NULL; range++) {
2975 printf("%s\n", *range);
2976 if(sscanf(*range, "%u:%u", &low, &high) == 1)
2977 uids = g_slist_prepend(uids, GINT_TO_POINTER(low));
2980 for (i = low; i <= high; i++)
2981 uids = g_slist_prepend(uids, GINT_TO_POINTER(i));
2984 uids = g_slist_reverse(uids);
2990 static gint imap_cmd_copy(IMAPSession *session, const gchar *seq_set,
2991 const gchar *destfolder, GRelation *uid_mapping)
2996 g_return_val_if_fail(session != NULL, IMAP_ERROR);
2997 g_return_val_if_fail(seq_set != NULL, IMAP_ERROR);
2998 g_return_val_if_fail(destfolder != NULL, IMAP_ERROR);
3000 QUOTE_IF_REQUIRED(destfolder_, destfolder);
3001 imap_gen_send(session, "UID COPY %s %s", seq_set, destfolder_);
3003 if (uid_mapping != NULL && session->uidplus) {
3005 gchar *resp_str = NULL, *olduids_str, *newuids_str;
3006 MsgNumberList *olduids, *old_cur, *newuids, *new_cur;
3008 reply = g_ptr_array_new();
3009 ok = imap_cmd_ok(session, reply);
3010 if ((ok == IMAP_SUCCESS) && (reply->len > 0)) {
3011 resp_str = g_ptr_array_index(reply, reply->len - 1);
3013 olduids_str = g_new0(gchar, strlen(resp_str));
3014 newuids_str = g_new0(gchar, strlen(resp_str));
3015 if (sscanf(resp_str, "%*s OK [COPYUID %*u %[0-9,:] %[0-9,:]]",
3016 olduids_str, newuids_str) == 2) {
3017 olduids = imapset_to_numlist(olduids_str);
3018 newuids = imapset_to_numlist(newuids_str);
3022 while(old_cur != NULL && new_cur != NULL) {
3023 g_relation_insert(uid_mapping,
3024 GPOINTER_TO_INT(old_cur->data),
3025 GPOINTER_TO_INT(new_cur->data));
3026 old_cur = g_slist_next(old_cur);
3027 new_cur = g_slist_next(new_cur);
3030 g_slist_free(olduids);
3031 g_slist_free(newuids);
3033 g_free(olduids_str);
3034 g_free(newuids_str);
3037 ptr_array_free_strings(reply);
3038 g_ptr_array_free(reply, TRUE);
3040 ok = imap_cmd_ok(session, NULL);
3042 if (ok != IMAP_SUCCESS)
3043 log_warning(_("can't copy %s to %s\n"), seq_set, destfolder_);
3048 gint imap_cmd_envelope(IMAPSession *session, IMAPSet set)
3050 static GString *header_fields = NULL;
3052 if (header_fields == NULL) {
3053 const HeaderEntry *headers, *elem;
3055 headers = procheader_get_headernames(FALSE);
3056 header_fields = g_string_new("");
3058 for (elem = headers; elem->name != NULL; ++elem) {
3059 gint namelen = strlen(elem->name);
3061 /* Header fields ending with space are not rfc822 headers */
3062 if (elem->name[namelen - 1] == ' ')
3065 /* strip : at the of header field */
3066 if(elem->name[namelen - 1] == ':')
3072 g_string_sprintfa(header_fields, "%s%.*s",
3073 header_fields->str[0] != '\0' ? " " : "",
3074 namelen, elem->name);
3079 (session, "UID FETCH %s (UID FLAGS RFC822.SIZE BODY.PEEK[HEADER.FIELDS (%s)])",
3080 set, header_fields->str);
3082 return IMAP_SUCCESS;
3085 static gint imap_cmd_store(IMAPSession *session, IMAPSet seq_set,
3090 imap_gen_send(session, "UID STORE %s %s", seq_set, sub_cmd);
3092 if ((ok = imap_cmd_ok(session, NULL)) != IMAP_SUCCESS) {
3093 log_warning(_("error while imap command: STORE %s %s\n"),
3098 return IMAP_SUCCESS;
3101 static gint imap_cmd_expunge(IMAPSession *session, IMAPSet seq_set)
3105 if (seq_set && session->uidplus)
3106 imap_gen_send(session, "UID EXPUNGE %s", seq_set);
3108 imap_gen_send(session, "EXPUNGE");
3109 if ((ok = imap_cmd_ok(session, NULL)) != IMAP_SUCCESS) {
3110 log_warning(_("error while imap command: EXPUNGE\n"));
3114 return IMAP_SUCCESS;
3117 static gint imap_cmd_close(IMAPSession *session)
3121 imap_gen_send(session, "CLOSE");
3122 if ((ok = imap_cmd_ok(session, NULL)) != IMAP_SUCCESS)
3123 log_warning(_("error while imap command: CLOSE\n"));
3128 static gint imap_cmd_ok(IMAPSession *session, GPtrArray *argbuf)
3130 gint ok = IMAP_SUCCESS;
3135 while ((ok = imap_gen_recv(session, &buf))
3137 // make sure data is long enough for any substring of buf
3138 data = alloca(strlen(buf) + 1);
3140 // untagged line read
3141 if (buf[0] == '*' && buf[1] == ' ') {
3144 g_ptr_array_add(argbuf, g_strdup(buf + 2));
3146 if (sscanf(buf + 2, "%d %s", &num, data) >= 2) {
3147 if (!strcmp(data, "EXISTS")) {
3148 session->exists = num;
3149 session->folder_content_changed = TRUE;
3152 if(!strcmp(data, "EXPUNGE")) {
3154 session->folder_content_changed = TRUE;
3157 // tagged line with correct tag and OK response found
3158 } else if ((sscanf(buf, "%d %s", &cmd_num, data) >= 2) &&
3159 (cmd_num == session->cmd_count) &&
3160 !strcmp(data, "OK")) {
3162 g_ptr_array_add(argbuf, g_strdup(buf));
3176 static void imap_gen_send(IMAPSession *session, const gchar *format, ...)
3183 va_start(args, format);
3184 tmp = g_strdup_vprintf(format, args);
3187 session->cmd_count++;
3189 buf = g_strdup_printf("%d %s\r\n", session->cmd_count, tmp);
3190 if (!strncasecmp(tmp, "LOGIN ", 6) && (p = strchr(tmp + 6, ' '))) {
3192 log_print("IMAP4> %d %s ********\n", session->cmd_count, tmp);
3194 log_print("IMAP4> %d %s\n", session->cmd_count, tmp);
3196 sock_write_all(SESSION(session)->sock, buf, strlen(buf));
3201 static gint imap_gen_recv(IMAPSession *session, gchar **ret)
3203 if ((*ret = sock_getline(SESSION(session)->sock)) == NULL)
3208 log_print("IMAP4< %s\n", *ret);
3210 return IMAP_SUCCESS;
3214 /* misc utility functions */
3216 static gchar *strchr_cpy(const gchar *src, gchar ch, gchar *dest, gint len)
3221 tmp = strchr(src, ch);
3225 memcpy(dest, src, MIN(tmp - src, len - 1));
3226 dest[MIN(tmp - src, len - 1)] = '\0';
3231 static gchar *get_quoted(const gchar *src, gchar ch, gchar *dest, gint len)
3233 const gchar *p = src;
3236 g_return_val_if_fail(*p == ch, NULL);
3241 while (*p != '\0' && *p != ch) {
3243 if (*p == '\\' && *(p + 1) != '\0')
3252 return (gchar *)(*p == ch ? p + 1 : p);
3255 static gchar *search_array_contain_str(GPtrArray *array, const gchar *str)
3259 for (i = 0; i < array->len; i++) {
3262 tmp = g_ptr_array_index(array, i);
3263 if (strstr(tmp, str) != NULL)
3270 static gchar *search_array_str(GPtrArray *array, const gchar *str)
3277 for (i = 0; i < array->len; i++) {
3280 tmp = g_ptr_array_index(array, i);
3281 if (!strncmp(tmp, str, len))
3288 static void imap_path_separator_subst(gchar *str, gchar separator)
3291 gboolean in_escape = FALSE;
3293 if (!separator || separator == '/') return;
3295 for (p = str; *p != '\0'; p++) {
3296 if (*p == '/' && !in_escape)
3298 else if (*p == '&' && *(p + 1) != '-' && !in_escape)
3300 else if (*p == '-' && in_escape)
3305 static gchar *imap_modified_utf7_to_locale(const gchar *mutf7_str)
3308 const gchar *from_p;
3311 to = g_malloc(strlen(mutf7_str) + 1);
3314 for (from_p = mutf7_str; *from_p != '\0'; from_p++) {
3315 if (*from_p == '&' && *(from_p + 1) == '-') {
3325 static iconv_t cd = (iconv_t)-1;
3326 static gboolean iconv_ok = TRUE;
3329 size_t norm_utf7_len;
3331 gchar *to_str, *to_p;
3333 gboolean in_escape = FALSE;
3335 if (!iconv_ok) return g_strdup(mutf7_str);
3337 if (cd == (iconv_t)-1) {
3338 cd = iconv_open(conv_get_current_charset_str(), "UTF-7");
3339 if (cd == (iconv_t)-1) {
3340 g_warning("iconv cannot convert UTF-7 to %s\n",
3341 conv_get_current_charset_str());
3343 return g_strdup(mutf7_str);
3347 norm_utf7 = g_string_new(NULL);
3349 for (p = mutf7_str; *p != '\0'; p++) {
3350 /* replace: '&' -> '+',
3352 escaped ',' -> '/' */
3353 if (!in_escape && *p == '&') {
3354 if (*(p + 1) != '-') {
3355 g_string_append_c(norm_utf7, '+');
3358 g_string_append_c(norm_utf7, '&');
3361 } else if (in_escape && *p == ',') {
3362 g_string_append_c(norm_utf7, '/');
3363 } else if (in_escape && *p == '-') {
3364 g_string_append_c(norm_utf7, '-');
3367 g_string_append_c(norm_utf7, *p);
3371 norm_utf7_p = norm_utf7->str;
3372 norm_utf7_len = norm_utf7->len;
3373 to_len = strlen(mutf7_str) * 5;
3374 to_p = to_str = g_malloc(to_len + 1);
3376 if (iconv(cd, (ICONV_CONST gchar **)&norm_utf7_p, &norm_utf7_len,
3377 &to_p, &to_len) == -1) {
3378 g_warning(_("iconv cannot convert UTF-7 to %s\n"),
3379 conv_get_current_charset_str());
3380 g_string_free(norm_utf7, TRUE);
3382 return g_strdup(mutf7_str);
3385 /* second iconv() call for flushing */
3386 iconv(cd, NULL, NULL, &to_p, &to_len);
3387 g_string_free(norm_utf7, TRUE);
3391 #endif /* !HAVE_ICONV */
3394 static gchar *imap_locale_to_modified_utf7(const gchar *from)
3397 const gchar *from_p;
3400 to = g_malloc(strlen(from) * 2 + 1);
3403 for (from_p = from; *from_p != '\0'; from_p++) {
3404 if (*from_p == '&') {
3414 static iconv_t cd = (iconv_t)-1;
3415 static gboolean iconv_ok = TRUE;
3416 gchar *norm_utf7, *norm_utf7_p;
3417 size_t from_len, norm_utf7_len;
3419 gchar *from_tmp, *to, *p;
3420 gboolean in_escape = FALSE;
3422 if (!iconv_ok) return g_strdup(from);
3424 if (cd == (iconv_t)-1) {
3425 cd = iconv_open("UTF-7", conv_get_current_charset_str());
3426 if (cd == (iconv_t)-1) {
3427 g_warning("iconv cannot convert %s to UTF-7\n",
3428 conv_get_current_charset_str());
3430 return g_strdup(from);
3434 Xstrdup_a(from_tmp, from, return g_strdup(from));
3435 from_len = strlen(from);
3436 norm_utf7_len = from_len * 5;
3437 Xalloca(norm_utf7, norm_utf7_len + 1, return g_strdup(from));
3438 norm_utf7_p = norm_utf7;
3440 #define IS_PRINT(ch) (isprint(ch) && isascii(ch))
3442 while (from_len > 0) {
3443 if (*from_tmp == '+') {
3444 *norm_utf7_p++ = '+';
3445 *norm_utf7_p++ = '-';
3449 } else if (IS_PRINT(*from_tmp)) {
3450 /* printable ascii char */
3451 *norm_utf7_p = *from_tmp;
3457 size_t mb_len = 0, conv_len = 0;
3459 /* unprintable char: convert to UTF-7 */
3461 while (!IS_PRINT(*p) && conv_len < from_len) {
3462 mb_len = mblen(p, MB_LEN_MAX);
3464 g_warning("wrong multibyte sequence\n");
3465 return g_strdup(from);
3471 from_len -= conv_len;
3472 if (iconv(cd, (ICONV_CONST gchar **)&from_tmp,
3474 &norm_utf7_p, &norm_utf7_len) == -1) {
3475 g_warning("iconv cannot convert %s to UTF-7\n",
3476 conv_get_current_charset_str());
3477 return g_strdup(from);
3480 /* second iconv() call for flushing */
3481 iconv(cd, NULL, NULL, &norm_utf7_p, &norm_utf7_len);
3487 *norm_utf7_p = '\0';
3488 to_str = g_string_new(NULL);
3489 for (p = norm_utf7; p < norm_utf7_p; p++) {
3490 /* replace: '&' -> "&-",
3493 BASE64 '/' -> ',' */
3494 if (!in_escape && *p == '&') {
3495 g_string_append(to_str, "&-");
3496 } else if (!in_escape && *p == '+') {
3497 if (*(p + 1) == '-') {
3498 g_string_append_c(to_str, '+');
3501 g_string_append_c(to_str, '&');
3504 } else if (in_escape && *p == '/') {
3505 g_string_append_c(to_str, ',');
3506 } else if (in_escape && *p == '-') {
3507 g_string_append_c(to_str, '-');
3510 g_string_append_c(to_str, *p);
3516 g_string_append_c(to_str, '-');
3520 g_string_free(to_str, FALSE);
3523 #endif /* !HAVE_ICONV */
3526 static GSList *imap_get_seq_set_from_numlist(MsgNumberList *numlist)
3529 GSList *sorted_list, *cur;
3530 guint first, last, next;
3532 GSList *ret_list = NULL;
3534 if (numlist == NULL)
3537 str = g_string_sized_new(256);
3539 sorted_list = g_slist_copy(numlist);
3540 sorted_list = g_slist_sort(sorted_list, g_int_compare);
3542 first = GPOINTER_TO_INT(sorted_list->data);
3544 for (cur = sorted_list; cur != NULL; cur = g_slist_next(cur)) {
3545 last = GPOINTER_TO_INT(cur->data);
3547 next = GPOINTER_TO_INT(cur->next->data);
3551 if (last + 1 != next || next == 0) {
3553 g_string_append_c(str, ',');
3555 g_string_sprintfa(str, "%u", first);
3557 g_string_sprintfa(str, "%u:%u", first, last);
3561 if (str->len > IMAP_CMD_LIMIT) {
3562 ret_str = g_strdup(str->str);
3563 ret_list = g_slist_append(ret_list, ret_str);
3564 g_string_truncate(str, 0);
3570 ret_str = g_strdup(str->str);
3571 ret_list = g_slist_append(ret_list, ret_str);
3574 g_slist_free(sorted_list);
3575 g_string_free(str, TRUE);
3580 static GSList *imap_get_seq_set_from_msglist(MsgInfoList *msglist)
3582 MsgNumberList *numlist = NULL;
3586 for (cur = msglist; cur != NULL; cur = g_slist_next(cur)) {
3587 MsgInfo *msginfo = (MsgInfo *) cur->data;
3589 numlist = g_slist_append(numlist, GINT_TO_POINTER(msginfo->msgnum));
3591 seq_list = imap_get_seq_set_from_numlist(numlist);
3592 g_slist_free(numlist);
3597 static void imap_seq_set_free(GSList *seq_list)
3599 slist_free_strings(seq_list);
3600 g_slist_free(seq_list);
3604 static gboolean imap_rename_folder_func(GNode *node, gpointer data)
3606 FolderItem *item = node->data;
3607 gchar **paths = data;
3608 const gchar *oldpath = paths[0];
3609 const gchar *newpath = paths[1];
3611 gchar *new_itempath;
3614 oldpathlen = strlen(oldpath);
3615 if (strncmp(oldpath, item->path, oldpathlen) != 0) {
3616 g_warning("path doesn't match: %s, %s\n", oldpath, item->path);
3620 base = item->path + oldpathlen;
3621 while (*base == G_DIR_SEPARATOR) base++;
3623 new_itempath = g_strdup(newpath);
3625 new_itempath = g_strconcat(newpath, G_DIR_SEPARATOR_S, base,
3628 item->path = new_itempath;
3633 static gint get_list_of_uids(Folder *folder, IMAPFolderItem *item, GSList **msgnum_list)
3635 gint ok, nummsgs = 0, lastuid_old;
3636 IMAPSession *session;
3637 GSList *uidlist, *elem;
3640 session = imap_session_get(folder);
3641 g_return_val_if_fail(session != NULL, -1);
3643 ok = imap_select(session, IMAP_FOLDER(folder), item->item.path,
3644 NULL, NULL, NULL, NULL);
3645 if (ok != IMAP_SUCCESS)
3648 cmd_buf = g_strdup_printf("UID %d:*", item->lastuid + 1);
3649 ok = imap_cmd_search(session, cmd_buf, &uidlist);
3652 if (ok == IMAP_SOCKET) {
3653 session_destroy((Session *)session);
3654 ((RemoteFolder *)folder)->session = NULL;
3658 if (ok != IMAP_SUCCESS) {
3662 argbuf = g_ptr_array_new();
3664 cmd_buf = g_strdup_printf("UID FETCH %d:* (UID)", item->lastuid + 1);
3665 imap_gen_send(session, cmd_buf);
3667 ok = imap_cmd_ok(session, argbuf);
3668 if (ok != IMAP_SUCCESS) {
3669 ptr_array_free_strings(argbuf);
3670 g_ptr_array_free(argbuf, TRUE);
3674 for(i = 0; i < argbuf->len; i++) {
3677 if((ret = sscanf(g_ptr_array_index(argbuf, i),
3678 "%*d FETCH (UID %d)", &msgnum)) == 1)
3679 uidlist = g_slist_prepend(uidlist, GINT_TO_POINTER(msgnum));
3681 ptr_array_free_strings(argbuf);
3682 g_ptr_array_free(argbuf, TRUE);
3685 lastuid_old = item->lastuid;
3686 *msgnum_list = g_slist_copy(item->uid_list);
3687 nummsgs = g_slist_length(*msgnum_list);
3688 debug_print("Got %d uids from cache\n", g_slist_length(item->uid_list));
3690 for (elem = uidlist; elem != NULL; elem = g_slist_next(elem)) {
3693 msgnum = GPOINTER_TO_INT(elem->data);
3694 if (msgnum > lastuid_old) {
3695 *msgnum_list = g_slist_prepend(*msgnum_list, GINT_TO_POINTER(msgnum));
3696 item->uid_list = g_slist_prepend(item->uid_list, GINT_TO_POINTER(msgnum));
3699 if(msgnum > item->lastuid)
3700 item->lastuid = msgnum;
3703 g_slist_free(uidlist);
3708 gint imap_get_num_list(Folder *folder, FolderItem *_item, GSList **msgnum_list, gboolean *old_uids_valid)
3710 IMAPFolderItem *item = (IMAPFolderItem *)_item;
3711 IMAPSession *session;
3712 gint ok, nummsgs = 0, exists, recent, uid_val, uid_next, unseen;
3715 gboolean selected_folder;
3717 g_return_val_if_fail(folder != NULL, -1);
3718 g_return_val_if_fail(item != NULL, -1);
3719 g_return_val_if_fail(item->item.path != NULL, -1);
3720 g_return_val_if_fail(FOLDER_CLASS(folder) == &imap_class, -1);
3721 g_return_val_if_fail(folder->account != NULL, -1);
3723 session = imap_session_get(folder);
3724 g_return_val_if_fail(session != NULL, -1);
3726 selected_folder = (session->mbox != NULL) &&
3727 (!strcmp(session->mbox, item->item.path));
3728 if (selected_folder) {
3729 ok = imap_cmd_noop(session);
3730 if (ok != IMAP_SUCCESS)
3732 exists = session->exists;
3734 *old_uids_valid = TRUE;
3736 ok = imap_status(session, IMAP_FOLDER(folder), item->item.path,
3737 &exists, &recent, &uid_next, &uid_val, &unseen);
3738 if (ok != IMAP_SUCCESS)
3741 if(item->item.mtime == uid_val)
3742 *old_uids_valid = TRUE;
3744 *old_uids_valid = FALSE;
3746 debug_print("Freeing imap uid cache\n");
3748 g_slist_free(item->uid_list);
3749 item->uid_list = NULL;
3751 item->item.mtime = uid_val;
3753 imap_delete_all_cached_messages((FolderItem *)item);
3757 if (!selected_folder)
3758 item->uid_next = uid_next;
3760 /* If old uid_next matches new uid_next we can be sure no message
3761 was added to the folder */
3762 if (( selected_folder && !session->folder_content_changed) ||
3763 (!selected_folder && uid_next == item->uid_next)) {
3764 nummsgs = g_slist_length(item->uid_list);
3766 /* If number of messages is still the same we
3767 know our caches message numbers are still valid,
3768 otherwise if the number of messages has decrease
3769 we discard our cache to start a new scan to find
3770 out which numbers have been removed */
3771 if (exists == nummsgs) {
3772 *msgnum_list = g_slist_copy(item->uid_list);
3774 } else if (exists < nummsgs) {
3775 debug_print("Freeing imap uid cache");
3777 g_slist_free(item->uid_list);
3778 item->uid_list = NULL;
3783 *msgnum_list = NULL;
3787 nummsgs = get_list_of_uids(folder, item, &uidlist);
3789 if (nummsgs != exists) {
3790 /* Cache contains more messages then folder, we have cached
3791 an old UID of a message that was removed and new messages
3792 have been added too, otherwise the uid_next check would
3794 debug_print("Freeing imap uid cache");
3796 g_slist_free(item->uid_list);
3797 item->uid_list = NULL;
3799 g_slist_free(*msgnum_list);
3801 nummsgs = get_list_of_uids(folder, item, &uidlist);
3804 *msgnum_list = uidlist;
3806 dir = folder_item_get_path((FolderItem *)item);
3807 debug_print("removing old messages from %s\n", dir);
3808 remove_numbered_files_not_in_list(dir, *msgnum_list);
3814 static MsgInfo *imap_parse_msg(const gchar *file, FolderItem *item)
3819 flags.perm_flags = MSG_NEW|MSG_UNREAD;
3820 flags.tmp_flags = 0;
3822 g_return_val_if_fail(item != NULL, NULL);
3823 g_return_val_if_fail(file != NULL, NULL);
3825 if (item->stype == F_QUEUE) {
3826 MSG_SET_TMP_FLAGS(flags, MSG_QUEUED);
3827 } else if (item->stype == F_DRAFT) {
3828 MSG_SET_TMP_FLAGS(flags, MSG_DRAFT);
3831 msginfo = procheader_parse_file(file, flags, FALSE, FALSE);
3832 if (!msginfo) return NULL;
3834 msginfo->folder = item;
3839 GSList *imap_get_msginfos(Folder *folder, FolderItem *item, GSList *msgnum_list)
3841 IMAPSession *session;
3842 MsgInfoList *ret = NULL;
3845 g_return_val_if_fail(folder != NULL, NULL);
3846 g_return_val_if_fail(item != NULL, NULL);
3847 g_return_val_if_fail(msgnum_list != NULL, NULL);
3849 session = imap_session_get(folder);
3850 g_return_val_if_fail(session != NULL, NULL);
3852 ok = imap_select(session, IMAP_FOLDER(folder), item->path,
3853 NULL, NULL, NULL, NULL);
3854 if (ok != IMAP_SUCCESS)
3857 if (!(item->stype == F_QUEUE || item->stype == F_DRAFT)) {
3858 ret = g_slist_concat(ret,
3859 imap_get_uncached_messages(
3860 session, item, msgnum_list));
3862 MsgNumberList *sorted_list, *elem;
3863 gint startnum, lastnum;
3865 sorted_list = g_slist_sort(g_slist_copy(msgnum_list), g_int_compare);
3867 startnum = lastnum = GPOINTER_TO_INT(sorted_list->data);
3869 for (elem = sorted_list;; elem = g_slist_next(elem)) {
3873 num = GPOINTER_TO_INT(elem->data);
3875 if (num > lastnum + 1 || elem == NULL) {
3877 for (i = startnum; i <= lastnum; ++i) {
3880 file = imap_fetch_msg(folder, item, i);
3882 MsgInfo *msginfo = imap_parse_msg(file, item);
3883 if (msginfo != NULL) {
3884 msginfo->msgnum = i;
3885 ret = g_slist_append(ret, msginfo);
3899 g_slist_free(sorted_list);
3905 MsgInfo *imap_get_msginfo(Folder *folder, FolderItem *item, gint uid)
3907 MsgInfo *msginfo = NULL;
3908 MsgInfoList *msginfolist;
3909 MsgNumberList numlist;
3911 numlist.next = NULL;
3912 numlist.data = GINT_TO_POINTER(uid);
3914 msginfolist = imap_get_msginfos(folder, item, &numlist);
3915 if (msginfolist != NULL) {
3916 msginfo = msginfolist->data;
3917 g_slist_free(msginfolist);
3923 gboolean imap_scan_required(Folder *folder, FolderItem *_item)
3925 IMAPSession *session;
3926 IMAPFolderItem *item = (IMAPFolderItem *)_item;
3927 gint ok, exists = 0, recent = 0, unseen = 0;
3928 guint32 uid_next, uid_val = 0;
3929 gboolean selected_folder;
3931 g_return_val_if_fail(folder != NULL, FALSE);
3932 g_return_val_if_fail(item != NULL, FALSE);
3933 g_return_val_if_fail(item->item.folder != NULL, FALSE);
3934 g_return_val_if_fail(FOLDER_CLASS(item->item.folder) == &imap_class, FALSE);
3936 if (item->item.path == NULL)
3939 session = imap_session_get(folder);
3940 g_return_val_if_fail(session != NULL, FALSE);
3942 selected_folder = (session->mbox != NULL) &&
3943 (!strcmp(session->mbox, item->item.path));
3944 if (selected_folder) {
3945 ok = imap_cmd_noop(session);
3946 if (ok != IMAP_SUCCESS)
3949 if (session->folder_content_changed)
3952 ok = imap_status(session, IMAP_FOLDER(folder), item->item.path,
3953 &exists, &recent, &uid_next, &uid_val, &unseen);
3954 if (ok != IMAP_SUCCESS)
3957 if ((uid_next != item->uid_next) || (exists < item->item.total_msgs))
3964 void imap_change_flags(Folder *folder, FolderItem *item, MsgInfo *msginfo, MsgPermFlags newflags)
3966 IMAPSession *session;
3967 IMAPFlags flags_set = 0, flags_unset = 0;
3968 gint ok = IMAP_SUCCESS;
3969 MsgNumberList numlist;
3971 g_return_if_fail(folder != NULL);
3972 g_return_if_fail(folder->klass == &imap_class);
3973 g_return_if_fail(item != NULL);
3974 g_return_if_fail(item->folder == folder);
3975 g_return_if_fail(msginfo != NULL);
3976 g_return_if_fail(msginfo->folder == item);
3978 session = imap_session_get(folder);
3979 if (!session) return;
3981 if ((ok = imap_select(session, IMAP_FOLDER(folder), msginfo->folder->path,
3982 NULL, NULL, NULL, NULL)) != IMAP_SUCCESS)
3985 if (!MSG_IS_MARKED(msginfo->flags) && (newflags & MSG_MARKED))
3986 flags_set |= IMAP_FLAG_FLAGGED;
3987 if ( MSG_IS_MARKED(msginfo->flags) && !(newflags & MSG_MARKED))
3988 flags_unset |= IMAP_FLAG_FLAGGED;
3990 if (!MSG_IS_UNREAD(msginfo->flags) && (newflags & MSG_UNREAD))
3991 flags_unset |= IMAP_FLAG_SEEN;
3992 if ( MSG_IS_UNREAD(msginfo->flags) && !(newflags & MSG_UNREAD))
3993 flags_set |= IMAP_FLAG_SEEN;
3995 if (!MSG_IS_REPLIED(msginfo->flags) && (newflags & MSG_REPLIED))
3996 flags_set |= IMAP_FLAG_ANSWERED;
3997 if ( MSG_IS_REPLIED(msginfo->flags) && !(newflags & MSG_REPLIED))
3998 flags_set |= IMAP_FLAG_ANSWERED;
4000 numlist.next = NULL;
4001 numlist.data = GINT_TO_POINTER(msginfo->msgnum);
4004 ok = imap_set_message_flags(session, &numlist, flags_set, TRUE);
4005 if (ok != IMAP_SUCCESS) return;
4009 ok = imap_set_message_flags(session, &numlist, flags_unset, FALSE);
4010 if (ok != IMAP_SUCCESS) return;
4013 msginfo->flags.perm_flags = newflags;