2 * Sylpheed -- a GTK+ based, lightweight, and fast e-mail client
3 * Copyright (C) 1999-2004 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 "prefs_common.h"
56 #include "inputdialog.h"
58 #include "remotefolder.h"
59 #include "alertpanel.h"
61 typedef struct _IMAPFolder IMAPFolder;
62 typedef struct _IMAPSession IMAPSession;
63 typedef struct _IMAPNameSpace IMAPNameSpace;
64 typedef struct _IMAPFolderItem IMAPFolderItem;
66 #include "prefs_account.h"
68 #define IMAP_FOLDER(obj) ((IMAPFolder *)obj)
69 #define IMAP_FOLDER_ITEM(obj) ((IMAPFolderItem *)obj)
70 #define IMAP_SESSION(obj) ((IMAPSession *)obj)
76 /* list of IMAPNameSpace */
86 gboolean authenticated;
95 time_t last_access_time;
96 gboolean folder_content_changed;
100 struct _IMAPNameSpace
106 #define IMAP_SUCCESS 0
107 #define IMAP_SOCKET 2
108 #define IMAP_AUTHFAIL 3
109 #define IMAP_PROTOCOL 4
110 #define IMAP_SYNTAX 5
114 #define IMAPBUFSIZE 8192
118 IMAP_FLAG_SEEN = 1 << 0,
119 IMAP_FLAG_ANSWERED = 1 << 1,
120 IMAP_FLAG_FLAGGED = 1 << 2,
121 IMAP_FLAG_DELETED = 1 << 3,
122 IMAP_FLAG_DRAFT = 1 << 4
125 #define IMAP_IS_SEEN(flags) ((flags & IMAP_FLAG_SEEN) != 0)
126 #define IMAP_IS_ANSWERED(flags) ((flags & IMAP_FLAG_ANSWERED) != 0)
127 #define IMAP_IS_FLAGGED(flags) ((flags & IMAP_FLAG_FLAGGED) != 0)
128 #define IMAP_IS_DELETED(flags) ((flags & IMAP_FLAG_DELETED) != 0)
129 #define IMAP_IS_DRAFT(flags) ((flags & IMAP_FLAG_DRAFT) != 0)
132 #define IMAP4_PORT 143
134 #define IMAPS_PORT 993
137 #define IMAP_CMD_LIMIT 1000
139 #define QUOTE_IF_REQUIRED(out, str) \
141 if (*str != '"' && strpbrk(str, " \t(){}%*") != NULL) { \
145 len = strlen(str) + 3; \
146 Xalloca(__tmp, len, return IMAP_ERROR); \
147 g_snprintf(__tmp, len, "\"%s\"", str); \
150 Xstrdup_a(out, str, return IMAP_ERROR); \
154 typedef gchar * IMAPSet;
156 struct _IMAPFolderItem
165 static void imap_folder_init (Folder *folder,
169 static Folder *imap_folder_new (const gchar *name,
171 static void imap_folder_destroy (Folder *folder);
173 static IMAPSession *imap_session_new (const PrefsAccount *account);
174 static void imap_session_authenticate(IMAPSession *session,
175 const PrefsAccount *account);
176 static void imap_session_destroy (Session *session);
178 static gchar *imap_fetch_msg (Folder *folder,
181 static gint imap_add_msg (Folder *folder,
185 static gint imap_add_msgs (Folder *folder,
188 GRelation *relation);
190 static gint imap_copy_msg (Folder *folder,
193 static gint imap_copy_msgs (Folder *folder,
195 MsgInfoList *msglist,
196 GRelation *relation);
198 static gint imap_remove_msg (Folder *folder,
201 static gint imap_remove_all_msg (Folder *folder,
204 static gboolean imap_is_msg_changed (Folder *folder,
208 static gint imap_close (Folder *folder,
211 static gint imap_scan_tree (Folder *folder);
213 static gint imap_create_tree (Folder *folder);
215 static FolderItem *imap_create_folder (Folder *folder,
218 static gint imap_rename_folder (Folder *folder,
221 static gint imap_remove_folder (Folder *folder,
224 static FolderItem *imap_folder_item_new (Folder *folder);
225 static void imap_folder_item_destroy (Folder *folder,
228 static IMAPSession *imap_session_get (Folder *folder);
230 static gint imap_greeting (IMAPSession *session);
231 static gint imap_auth (IMAPSession *session,
236 static gint imap_scan_tree_recursive (IMAPSession *session,
238 static GSList *imap_parse_list (IMAPFolder *folder,
239 IMAPSession *session,
240 const gchar *real_path,
243 static void imap_create_missing_folders (Folder *folder);
244 static FolderItem *imap_create_special_folder
246 SpecialFolderItemType stype,
249 static gint imap_do_copy_msgs (Folder *folder,
251 MsgInfoList *msglist,
252 GRelation *relation);
254 static void imap_delete_all_cached_messages (FolderItem *item);
257 static SockInfo *imap_open (const gchar *server,
261 static SockInfo *imap_open (const gchar *server,
266 static SockInfo *imap_open_tunnel(const gchar *server,
267 const gchar *tunnelcmd,
270 static SockInfo *imap_open_tunnel(const gchar *server,
271 const gchar *tunnelcmd);
275 static SockInfo *imap_init_sock(SockInfo *sock, SSLType ssl_type);
277 static SockInfo *imap_init_sock(SockInfo *sock);
280 static gchar *imap_get_flag_str (IMAPFlags flags);
281 static gint imap_set_message_flags (IMAPSession *session,
282 MsgNumberList *numlist,
285 static gint imap_select (IMAPSession *session,
291 guint32 *uid_validity);
292 static gint imap_status (IMAPSession *session,
298 guint32 *uid_validity,
301 static void imap_parse_namespace (IMAPSession *session,
303 static void imap_get_namespace_by_list (IMAPSession *session,
305 static IMAPNameSpace *imap_find_namespace (IMAPFolder *folder,
307 static gchar imap_get_path_separator (IMAPFolder *folder,
309 static gchar *imap_get_real_path (IMAPFolder *folder,
312 static gchar *imap_parse_atom (SockInfo *sock,
317 static MsgFlags imap_parse_flags (const gchar *flag_str);
318 static MsgInfo *imap_parse_envelope (SockInfo *sock,
322 static gboolean imap_has_capability (IMAPSession *session,
324 static void imap_free_capabilities (IMAPSession *session);
326 /* low-level IMAP4rev1 commands */
327 static gint imap_cmd_authenticate
328 (IMAPSession *session,
332 static gint imap_cmd_login (IMAPSession *session,
335 static gint imap_cmd_logout (IMAPSession *session);
336 static gint imap_cmd_noop (IMAPSession *session);
337 static gint imap_cmd_starttls (IMAPSession *session);
338 static gint imap_cmd_namespace (IMAPSession *session,
340 static gint imap_cmd_list (IMAPSession *session,
342 const gchar *mailbox,
344 static gint imap_cmd_do_select (IMAPSession *session,
350 guint32 *uid_validity);
351 static gint imap_cmd_select (IMAPSession *session,
356 guint32 *uid_validity);
357 static gint imap_cmd_examine (IMAPSession *session,
362 guint32 *uid_validity);
363 static gint imap_cmd_create (IMAPSession *sock,
364 const gchar *folder);
365 static gint imap_cmd_rename (IMAPSession *sock,
366 const gchar *oldfolder,
367 const gchar *newfolder);
368 static gint imap_cmd_delete (IMAPSession *session,
369 const gchar *folder);
370 static gint imap_cmd_envelope (IMAPSession *session,
372 static gint imap_cmd_fetch (IMAPSession *sock,
374 const gchar *filename);
375 static gint imap_cmd_append (IMAPSession *session,
376 const gchar *destfolder,
380 static gint imap_cmd_copy (IMAPSession *session,
381 const gchar *seq_set,
382 const gchar *destfolder,
383 GRelation *uid_mapping);
384 static gint imap_cmd_store (IMAPSession *session,
387 static gint imap_cmd_expunge (IMAPSession *session,
389 static gint imap_cmd_close (IMAPSession *session);
391 static gint imap_cmd_ok (IMAPSession *session,
393 static void imap_gen_send (IMAPSession *session,
394 const gchar *format, ...);
395 static gint imap_gen_recv (IMAPSession *session,
398 /* misc utility functions */
399 static gchar *strchr_cpy (const gchar *src,
403 static gchar *get_quoted (const gchar *src,
407 static gchar *search_array_contain_str (GPtrArray *array,
409 static gchar *search_array_str (GPtrArray *array,
411 static void imap_path_separator_subst (gchar *str,
414 static gchar *imap_utf8_to_modified_utf7 (const gchar *from);
415 static gchar *imap_modified_utf7_to_utf8 (const gchar *mutf7_str);
417 static GSList *imap_get_seq_set_from_numlist (MsgNumberList *msglist);
418 static GSList *imap_get_seq_set_from_msglist (MsgInfoList *msglist);
419 static void imap_seq_set_free (GSList *seq_list);
421 static gboolean imap_rename_folder_func (GNode *node,
423 static gint imap_get_num_list (Folder *folder,
426 gboolean *old_uids_valid);
427 static GSList *imap_get_msginfos (Folder *folder,
429 GSList *msgnum_list);
430 static MsgInfo *imap_get_msginfo (Folder *folder,
433 static gboolean imap_scan_required (Folder *folder,
435 static void imap_change_flags (Folder *folder,
438 MsgPermFlags newflags);
439 static gchar *imap_folder_get_path (Folder *folder);
440 static gchar *imap_item_get_path (Folder *folder,
443 static FolderClass imap_class =
449 /* Folder functions */
457 /* FolderItem functions */
458 imap_folder_item_new,
459 imap_folder_item_destroy,
473 /* Message functions */
487 FolderClass *imap_get_class(void)
492 static Folder *imap_folder_new(const gchar *name, const gchar *path)
496 folder = (Folder *)g_new0(IMAPFolder, 1);
497 folder->klass = &imap_class;
498 imap_folder_init(folder, name, path);
503 static void imap_folder_destroy(Folder *folder)
507 dir = imap_folder_get_path(folder);
508 if (is_dir_exist(dir))
509 remove_dir_recursive(dir);
512 folder_remote_folder_destroy(REMOTE_FOLDER(folder));
515 static void imap_folder_init(Folder *folder, const gchar *name,
518 folder_remote_folder_init((Folder *)folder, name, path);
521 static FolderItem *imap_folder_item_new(Folder *folder)
523 IMAPFolderItem *item;
525 item = g_new0(IMAPFolderItem, 1);
528 item->uid_list = NULL;
530 return (FolderItem *)item;
533 static void imap_folder_item_destroy(Folder *folder, FolderItem *_item)
535 IMAPFolderItem *item = (IMAPFolderItem *)_item;
537 g_return_if_fail(item != NULL);
538 g_slist_free(item->uid_list);
543 static gboolean imap_reset_uid_lists_func(GNode *node, gpointer data)
545 IMAPFolderItem *item = (IMAPFolderItem *)node->data;
549 g_slist_free(item->uid_list);
550 item->uid_list = NULL;
555 static void imap_reset_uid_lists(Folder *folder)
557 if(folder->node == NULL)
560 /* Destroy all uid lists and rest last uid */
561 g_node_traverse(folder->node, G_IN_ORDER, G_TRAVERSE_ALL, -1, imap_reset_uid_lists_func, NULL);
564 /* Send CAPABILITY, and examine the server's response to see whether this
565 * connection is pre-authenticated or not and build a list of CAPABILITIES. */
566 static gint imap_greeting(IMAPSession *session)
571 imap_gen_send(session, "CAPABILITY");
573 argbuf = g_ptr_array_new();
575 if (imap_cmd_ok(session, argbuf) != IMAP_SUCCESS ||
576 ((capstr = search_array_str(argbuf, "CAPABILITY ")) == NULL)) {
577 ptr_array_free_strings(argbuf);
578 g_ptr_array_free(argbuf, TRUE);
582 session->authenticated = search_array_str(argbuf, "PREAUTH") != NULL;
584 capstr += strlen("CAPABILITY ");
586 IMAP_SESSION(session)->capability = g_strsplit(capstr, " ", 0);
588 ptr_array_free_strings(argbuf);
589 g_ptr_array_free(argbuf, TRUE);
591 if (imap_has_capability(session, "UIDPLUS"))
592 session->uidplus = TRUE;
597 static gint imap_auth(IMAPSession *session, const gchar *user, const gchar *pass,
602 if (type == 0 || type == IMAP_AUTH_LOGIN)
603 ok = imap_cmd_login(session, user, pass);
605 ok = imap_cmd_authenticate(session, user, pass, type);
607 if (ok == IMAP_SUCCESS)
608 session->authenticated = TRUE;
613 static IMAPSession *imap_session_get(Folder *folder)
615 RemoteFolder *rfolder = REMOTE_FOLDER(folder);
616 IMAPSession *session = NULL;
618 g_return_val_if_fail(folder != NULL, NULL);
619 g_return_val_if_fail(FOLDER_CLASS(folder) == &imap_class, NULL);
620 g_return_val_if_fail(folder->account != NULL, NULL);
622 if (prefs_common.work_offline)
625 /* Make sure we have a session */
626 if (rfolder->session != NULL) {
627 session = IMAP_SESSION(rfolder->session);
629 imap_reset_uid_lists(folder);
630 session = imap_session_new(folder->account);
635 if (SESSION(session)->sock->state == CONN_DISCONNECTED) {
636 debug_print("IMAP server disconnected\n");
637 session_destroy(SESSION(session));
638 imap_reset_uid_lists(folder);
639 session = imap_session_new(folder->account);
642 /* Make sure session is authenticated */
643 if (!IMAP_SESSION(session)->authenticated)
644 imap_session_authenticate(IMAP_SESSION(session), folder->account);
645 if (!IMAP_SESSION(session)->authenticated) {
646 session_destroy(SESSION(session));
647 rfolder->session = NULL;
651 /* Make sure we have parsed the IMAP namespace */
652 imap_parse_namespace(IMAP_SESSION(session),
653 IMAP_FOLDER(folder));
655 /* I think the point of this code is to avoid sending a
656 * keepalive if we've used the session recently and therefore
657 * think it's still alive. Unfortunately, most of the code
658 * does not yet check for errors on the socket, and so if the
659 * connection drops we don't notice until the timeout expires.
660 * A better solution than sending a NOOP every time would be
661 * for every command to be prepared to retry until it is
662 * successfully sent. -- mbp */
663 if (time(NULL) - session->last_access_time > SESSION_TIMEOUT) {
664 /* verify that the session is still alive */
665 if (imap_cmd_noop(session) != IMAP_SUCCESS) {
666 /* Check if this is the first try to establish a
667 connection, if yes we don't try to reconnect */
668 if (rfolder->session == NULL) {
669 log_warning(_("Connecting %s failed"),
670 folder->account->recv_server);
671 session_destroy(SESSION(session));
674 log_warning(_("IMAP4 connection to %s has been"
675 " disconnected. Reconnecting...\n"),
676 folder->account->recv_server);
677 session_destroy(SESSION(session));
678 /* Clear folders session to make imap_session_get create
679 a new session, because of rfolder->session == NULL
680 it will not try to reconnect again and so avoid an
682 rfolder->session = NULL;
683 session = imap_session_get(folder);
688 rfolder->session = SESSION(session);
690 session->last_access_time = time(NULL);
692 return IMAP_SESSION(session);
695 static IMAPSession *imap_session_new(const PrefsAccount *account)
697 IMAPSession *session;
702 /* FIXME: IMAP over SSL only... */
705 port = account->set_imapport ? account->imapport
706 : account->ssl_imap == SSL_TUNNEL ? IMAPS_PORT : IMAP4_PORT;
707 ssl_type = account->ssl_imap;
709 port = account->set_imapport ? account->imapport
713 if (account->set_tunnelcmd) {
714 log_message(_("creating tunneled IMAP4 connection\n"));
716 if ((imap_sock = imap_open_tunnel(account->recv_server,
720 if ((imap_sock = imap_open_tunnel(account->recv_server,
721 account->tunnelcmd)) == NULL)
725 g_return_val_if_fail(account->recv_server != NULL, NULL);
727 log_message(_("creating IMAP4 connection to %s:%d ...\n"),
728 account->recv_server, port);
731 if ((imap_sock = imap_open(account->recv_server, port,
734 if ((imap_sock = imap_open(account->recv_server, port)) == NULL)
739 session = g_new0(IMAPSession, 1);
740 session_init(SESSION(session));
741 SESSION(session)->type = SESSION_IMAP;
742 SESSION(session)->server = g_strdup(account->recv_server);
743 SESSION(session)->sock = imap_sock;
745 SESSION(session)->destroy = imap_session_destroy;
747 session->capability = NULL;
749 session->authenticated = FALSE;
750 session->mbox = NULL;
751 session->cmd_count = 0;
753 /* Only need to log in if the connection was not PREAUTH */
754 if (imap_greeting(session) != IMAP_SUCCESS) {
755 session_destroy(SESSION(session));
760 if (account->ssl_imap == SSL_STARTTLS &&
761 imap_has_capability(session, "STARTTLS")) {
764 ok = imap_cmd_starttls(session);
765 if (ok != IMAP_SUCCESS) {
766 log_warning(_("Can't start TLS session.\n"));
767 session_destroy(SESSION(session));
770 if (!ssl_init_socket_with_method(SESSION(session)->sock,
772 session_destroy(SESSION(session));
776 imap_free_capabilities(session);
777 session->authenticated = FALSE;
778 session->uidplus = FALSE;
779 session->cmd_count = 1;
781 if (imap_greeting(session) != IMAP_SUCCESS) {
782 session_destroy(SESSION(session));
787 log_message("IMAP connection is %s-authenticated\n",
788 (session->authenticated) ? "pre" : "un");
793 static void imap_session_authenticate(IMAPSession *session,
794 const PrefsAccount *account)
798 g_return_if_fail(account->userid != NULL);
800 pass = account->passwd;
803 tmp_pass = input_dialog_query_password(account->recv_server, account->userid);
806 Xstrdup_a(pass, tmp_pass, {g_free(tmp_pass); return;});
810 if (imap_auth(session, account->userid, pass, account->imap_auth_type) != IMAP_SUCCESS) {
811 imap_cmd_logout(session);
815 session->authenticated = TRUE;
818 static void imap_session_destroy(Session *session)
820 imap_free_capabilities(IMAP_SESSION(session));
821 g_free(IMAP_SESSION(session)->mbox);
822 sock_close(session->sock);
823 session->sock = NULL;
826 static gchar *imap_fetch_msg(Folder *folder, FolderItem *item, gint uid)
828 gchar *path, *filename;
829 IMAPSession *session;
832 g_return_val_if_fail(folder != NULL, NULL);
833 g_return_val_if_fail(item != NULL, NULL);
835 path = folder_item_get_path(item);
836 if (!is_dir_exist(path))
838 filename = g_strconcat(path, G_DIR_SEPARATOR_S, itos(uid), NULL);
841 if (is_file_exist(filename)) {
842 debug_print("message %d has been already cached.\n", uid);
846 session = imap_session_get(folder);
852 ok = imap_select(session, IMAP_FOLDER(folder), item->path,
853 NULL, NULL, NULL, NULL);
854 if (ok != IMAP_SUCCESS) {
855 g_warning("can't select mailbox %s\n", item->path);
860 debug_print("getting message %d...\n", uid);
861 ok = imap_cmd_fetch(session, (guint32)uid, filename);
863 if (ok != IMAP_SUCCESS) {
864 g_warning("can't fetch message %d\n", uid);
872 static gint imap_add_msg(Folder *folder, FolderItem *dest,
873 const gchar *file, MsgFlags *flags)
877 MsgFileInfo fileinfo;
879 g_return_val_if_fail(file != NULL, -1);
881 fileinfo.msginfo = NULL;
882 fileinfo.file = (gchar *)file;
883 fileinfo.flags = flags;
884 file_list.data = &fileinfo;
885 file_list.next = NULL;
887 ret = imap_add_msgs(folder, dest, &file_list, NULL);
891 static gint imap_add_msgs(Folder *folder, FolderItem *dest, GSList *file_list,
895 IMAPSession *session;
896 guint32 last_uid = 0;
898 MsgFileInfo *fileinfo;
901 g_return_val_if_fail(folder != NULL, -1);
902 g_return_val_if_fail(dest != NULL, -1);
903 g_return_val_if_fail(file_list != NULL, -1);
905 session = imap_session_get(folder);
906 if (!session) return -1;
908 destdir = imap_get_real_path(IMAP_FOLDER(folder), dest->path);
910 for (cur = file_list; cur != NULL; cur = cur->next) {
911 IMAPFlags iflags = 0;
914 fileinfo = (MsgFileInfo *)cur->data;
916 if (fileinfo->flags) {
917 if (MSG_IS_MARKED(*fileinfo->flags))
918 iflags |= IMAP_FLAG_FLAGGED;
919 if (MSG_IS_REPLIED(*fileinfo->flags))
920 iflags |= IMAP_FLAG_ANSWERED;
921 if (!MSG_IS_UNREAD(*fileinfo->flags))
922 iflags |= IMAP_FLAG_SEEN;
925 if (dest->stype == F_OUTBOX ||
926 dest->stype == F_QUEUE ||
927 dest->stype == F_DRAFT ||
928 dest->stype == F_TRASH)
929 iflags |= IMAP_FLAG_SEEN;
931 ok = imap_cmd_append(session, destdir, fileinfo->file, iflags,
934 if (ok != IMAP_SUCCESS) {
935 g_warning("can't append message %s\n", fileinfo->file);
940 if (relation != NULL)
941 g_relation_insert(relation, fileinfo->msginfo != NULL ?
942 (gpointer) fileinfo->msginfo : (gpointer) fileinfo,
943 GINT_TO_POINTER(dest->last_num + 1));
944 if (last_uid < new_uid)
953 static gint imap_do_copy_msgs(Folder *folder, FolderItem *dest,
954 MsgInfoList *msglist, GRelation *relation)
958 GSList *seq_list, *cur;
960 IMAPSession *session;
961 gint ok = IMAP_SUCCESS;
962 GRelation *uid_mapping;
965 g_return_val_if_fail(folder != NULL, -1);
966 g_return_val_if_fail(dest != NULL, -1);
967 g_return_val_if_fail(msglist != NULL, -1);
969 session = imap_session_get(folder);
970 if (!session) return -1;
972 msginfo = (MsgInfo *)msglist->data;
974 src = msginfo->folder;
976 g_warning("the src folder is identical to the dest.\n");
980 ok = imap_select(session, IMAP_FOLDER(folder), msginfo->folder->path,
981 NULL, NULL, NULL, NULL);
982 if (ok != IMAP_SUCCESS)
985 destdir = imap_get_real_path(IMAP_FOLDER(folder), dest->path);
986 seq_list = imap_get_seq_set_from_msglist(msglist);
987 uid_mapping = g_relation_new(2);
988 g_relation_index(uid_mapping, 0, g_direct_hash, g_direct_equal);
990 for (cur = seq_list; cur != NULL; cur = g_slist_next(cur)) {
991 gchar *seq_set = (gchar *)cur->data;
993 debug_print("Copying message %s%c[%s] to %s ...\n",
994 src->path, G_DIR_SEPARATOR,
997 ok = imap_cmd_copy(session, seq_set, destdir, uid_mapping);
998 if (ok != IMAP_SUCCESS) {
999 g_relation_destroy(uid_mapping);
1000 imap_seq_set_free(seq_list);
1005 for (cur = msglist; cur != NULL; cur = g_slist_next(cur)) {
1006 MsgInfo *msginfo = (MsgInfo *)cur->data;
1009 tuples = g_relation_select(uid_mapping,
1010 GINT_TO_POINTER(msginfo->msgnum),
1012 if (tuples->len > 0) {
1013 gint num = GPOINTER_TO_INT(g_tuples_index(tuples, 0, 1));
1014 g_relation_insert(relation, msginfo,
1015 GPOINTER_TO_INT(num));
1019 g_relation_insert(relation, msginfo,
1020 GPOINTER_TO_INT(0));
1021 g_tuples_destroy(tuples);
1024 g_relation_destroy(uid_mapping);
1025 imap_seq_set_free(seq_list);
1029 if (ok == IMAP_SUCCESS)
1035 static gint imap_copy_msg(Folder *folder, FolderItem *dest, MsgInfo *msginfo)
1039 g_return_val_if_fail(msginfo != NULL, -1);
1041 msglist.data = msginfo;
1042 msglist.next = NULL;
1044 return imap_copy_msgs(folder, dest, &msglist, NULL);
1047 static gint imap_copy_msgs(Folder *folder, FolderItem *dest,
1048 MsgInfoList *msglist, GRelation *relation)
1054 g_return_val_if_fail(folder != NULL, -1);
1055 g_return_val_if_fail(dest != NULL, -1);
1056 g_return_val_if_fail(msglist != NULL, -1);
1058 msginfo = (MsgInfo *)msglist->data;
1059 g_return_val_if_fail(msginfo->folder != NULL, -1);
1061 if (folder == msginfo->folder->folder)
1062 return imap_do_copy_msgs(folder, dest, msglist, relation);
1064 file_list = procmsg_get_message_file_list(msglist);
1065 g_return_val_if_fail(file_list != NULL, -1);
1067 ret = imap_add_msgs(folder, dest, file_list, relation);
1069 procmsg_message_file_list_free(file_list);
1074 static gint imap_remove_msg(Folder *folder, FolderItem *item, gint uid)
1077 IMAPSession *session;
1079 MsgNumberList numlist;
1081 g_return_val_if_fail(folder != NULL, -1);
1082 g_return_val_if_fail(FOLDER_CLASS(folder) == &imap_class, -1);
1083 g_return_val_if_fail(item != NULL, -1);
1085 session = imap_session_get(folder);
1086 if (!session) return -1;
1088 ok = imap_select(session, IMAP_FOLDER(folder), item->path,
1089 NULL, NULL, NULL, NULL);
1090 if (ok != IMAP_SUCCESS)
1093 numlist.next = NULL;
1094 numlist.data = GINT_TO_POINTER(uid);
1096 ok = imap_set_message_flags
1097 (IMAP_SESSION(REMOTE_FOLDER(folder)->session),
1098 &numlist, IMAP_FLAG_DELETED, TRUE);
1099 if (ok != IMAP_SUCCESS) {
1100 log_warning(_("can't set deleted flags: %d\n"), uid);
1104 if (!session->uidplus) {
1105 ok = imap_cmd_expunge(session, NULL);
1109 uidstr = g_strdup_printf("%u", uid);
1110 ok = imap_cmd_expunge(session, uidstr);
1113 if (ok != IMAP_SUCCESS) {
1114 log_warning(_("can't expunge\n"));
1118 IMAP_FOLDER_ITEM(item)->uid_list = g_slist_remove(
1119 IMAP_FOLDER_ITEM(item)->uid_list, numlist.data);
1120 dir = folder_item_get_path(item);
1121 if (is_dir_exist(dir))
1122 remove_numbered_files(dir, uid, uid);
1125 return IMAP_SUCCESS;
1128 static gint imap_remove_all_msg(Folder *folder, FolderItem *item)
1131 IMAPSession *session;
1134 g_return_val_if_fail(folder != NULL, -1);
1135 g_return_val_if_fail(item != NULL, -1);
1137 session = imap_session_get(folder);
1138 if (!session) return -1;
1140 ok = imap_select(session, IMAP_FOLDER(folder), item->path,
1141 NULL, NULL, NULL, NULL);
1142 if (ok != IMAP_SUCCESS)
1145 imap_gen_send(session, "STORE 1:* +FLAGS.SILENT (\\Deleted)");
1146 ok = imap_cmd_ok(session, NULL);
1147 if (ok != IMAP_SUCCESS) {
1148 log_warning(_("can't set deleted flags: 1:*\n"));
1152 ok = imap_cmd_expunge(session, NULL);
1153 if (ok != IMAP_SUCCESS) {
1154 log_warning(_("can't expunge\n"));
1158 dir = folder_item_get_path(item);
1159 if (is_dir_exist(dir))
1160 remove_all_numbered_files(dir);
1163 return IMAP_SUCCESS;
1166 static gboolean imap_is_msg_changed(Folder *folder, FolderItem *item,
1169 /* TODO: properly implement this method */
1173 static gint imap_close(Folder *folder, FolderItem *item)
1176 IMAPSession *session;
1178 g_return_val_if_fail(folder != NULL, -1);
1179 g_return_val_if_fail(item != NULL, -1);
1180 g_return_val_if_fail(item->path != NULL, -1);
1182 session = imap_session_get(folder);
1183 if (!session) return -1;
1185 if (session->mbox) {
1186 if (strcmp2(session->mbox, item->path) != 0) return -1;
1188 ok = imap_cmd_close(session);
1189 if (ok != IMAP_SUCCESS)
1190 log_warning(_("can't close folder\n"));
1192 g_free(session->mbox);
1193 session->mbox = NULL;
1201 static gint imap_scan_tree(Folder *folder)
1203 FolderItem *item = NULL;
1204 IMAPSession *session;
1205 gchar *root_folder = NULL;
1207 g_return_val_if_fail(folder != NULL, -1);
1208 g_return_val_if_fail(folder->account != NULL, -1);
1210 session = imap_session_get(folder);
1212 if (!folder->node) {
1213 folder_tree_destroy(folder);
1214 item = folder_item_new(folder, folder->name, NULL);
1215 item->folder = folder;
1216 folder->node = item->node = g_node_new(item);
1221 if (folder->account->imap_dir && *folder->account->imap_dir) {
1226 Xstrdup_a(root_folder, folder->account->imap_dir, return -1);
1227 extract_quote(root_folder, '"');
1228 subst_char(root_folder,
1229 imap_get_path_separator(IMAP_FOLDER(folder),
1232 strtailchomp(root_folder, '/');
1233 real_path = imap_get_real_path
1234 (IMAP_FOLDER(folder), root_folder);
1235 debug_print("IMAP root directory: %s\n", real_path);
1237 /* check if root directory exist */
1238 argbuf = g_ptr_array_new();
1239 ok = imap_cmd_list(session, NULL, real_path, argbuf);
1240 if (ok != IMAP_SUCCESS ||
1241 search_array_str(argbuf, "LIST ") == NULL) {
1242 log_warning(_("root folder %s does not exist\n"), real_path);
1243 g_ptr_array_free(argbuf, TRUE);
1246 if (!folder->node) {
1247 item = folder_item_new(folder, folder->name, NULL);
1248 item->folder = folder;
1249 folder->node = item->node = g_node_new(item);
1253 g_ptr_array_free(argbuf, TRUE);
1258 item = FOLDER_ITEM(folder->node->data);
1259 if (!item || ((item->path || root_folder) &&
1260 strcmp2(item->path, root_folder) != 0)) {
1261 folder_tree_destroy(folder);
1262 item = folder_item_new(folder, folder->name, root_folder);
1263 item->folder = folder;
1264 folder->node = item->node = g_node_new(item);
1267 imap_scan_tree_recursive(session, FOLDER_ITEM(folder->node->data));
1268 imap_create_missing_folders(folder);
1273 static gint imap_scan_tree_recursive(IMAPSession *session, FolderItem *item)
1276 IMAPFolder *imapfolder;
1277 FolderItem *new_item;
1278 GSList *item_list, *cur;
1281 gchar *wildcard_path;
1285 g_return_val_if_fail(item != NULL, -1);
1286 g_return_val_if_fail(item->folder != NULL, -1);
1287 g_return_val_if_fail(item->no_sub == FALSE, -1);
1289 folder = item->folder;
1290 imapfolder = IMAP_FOLDER(folder);
1292 separator = imap_get_path_separator(imapfolder, item->path);
1294 if (folder->ui_func)
1295 folder->ui_func(folder, item, folder->ui_func_data);
1298 wildcard[0] = separator;
1301 real_path = imap_get_real_path(imapfolder, item->path);
1305 real_path = g_strdup("");
1308 Xstrcat_a(wildcard_path, real_path, wildcard,
1309 {g_free(real_path); return IMAP_ERROR;});
1310 QUOTE_IF_REQUIRED(wildcard_path, wildcard_path);
1312 imap_gen_send(session, "LIST \"\" %s",
1315 strtailchomp(real_path, separator);
1316 item_list = imap_parse_list(imapfolder, session, real_path, NULL);
1319 node = item->node->children;
1320 while (node != NULL) {
1321 FolderItem *old_item = FOLDER_ITEM(node->data);
1322 GNode *next = node->next;
1325 for (cur = item_list; cur != NULL; cur = cur->next) {
1326 FolderItem *cur_item = FOLDER_ITEM(cur->data);
1327 if (!strcmp2(old_item->path, cur_item->path)) {
1328 new_item = cur_item;
1333 debug_print("folder '%s' not found. removing...\n",
1335 folder_item_remove(old_item);
1337 old_item->no_sub = new_item->no_sub;
1338 old_item->no_select = new_item->no_select;
1339 if (old_item->no_sub == TRUE && node->children) {
1340 debug_print("folder '%s' doesn't have "
1341 "subfolders. removing...\n",
1343 folder_item_remove_children(old_item);
1350 for (cur = item_list; cur != NULL; cur = cur->next) {
1351 FolderItem *cur_item = FOLDER_ITEM(cur->data);
1353 for (node = item->node->children; node != NULL;
1354 node = node->next) {
1355 if (!strcmp2(FOLDER_ITEM(node->data)->path,
1357 new_item = FOLDER_ITEM(node->data);
1358 folder_item_destroy(cur_item);
1364 new_item = cur_item;
1365 debug_print("new folder '%s' found.\n", new_item->path);
1366 folder_item_append(item, new_item);
1369 if (!strcmp(new_item->path, "INBOX")) {
1370 new_item->stype = F_INBOX;
1371 folder->inbox = new_item;
1372 } else if (!folder_item_parent(item) || item->stype == F_INBOX) {
1375 base = g_basename(new_item->path);
1377 if (!folder->outbox && !strcasecmp(base, "Sent")) {
1378 new_item->stype = F_OUTBOX;
1379 folder->outbox = new_item;
1380 } else if (!folder->draft && !strcasecmp(base, "Drafts")) {
1381 new_item->stype = F_DRAFT;
1382 folder->draft = new_item;
1383 } else if (!folder->queue && !strcasecmp(base, "Queue")) {
1384 new_item->stype = F_QUEUE;
1385 folder->queue = new_item;
1386 } else if (!folder->trash && !strcasecmp(base, "Trash")) {
1387 new_item->stype = F_TRASH;
1388 folder->trash = new_item;
1392 if (new_item->no_sub == FALSE)
1393 imap_scan_tree_recursive(session, new_item);
1396 g_slist_free(item_list);
1398 return IMAP_SUCCESS;
1401 static GSList *imap_parse_list(IMAPFolder *folder, IMAPSession *session,
1402 const gchar *real_path, gchar *separator)
1404 gchar buf[IMAPBUFSIZE];
1406 gchar separator_str[16];
1409 gchar *loc_name, *loc_path;
1410 GSList *item_list = NULL;
1412 FolderItem *new_item;
1414 debug_print("getting list of %s ...\n",
1415 *real_path ? real_path : "\"\"");
1417 str = g_string_new(NULL);
1420 if (sock_gets(SESSION(session)->sock, buf, sizeof(buf)) <= 0) {
1421 log_warning(_("error occurred while getting LIST.\n"));
1425 if (buf[0] != '*' || buf[1] != ' ') {
1426 log_print("IMAP4< %s\n", buf);
1427 if (sscanf(buf, "%*d %16s", buf) < 1 ||
1428 strcmp(buf, "OK") != 0)
1429 log_warning(_("error occurred while getting LIST.\n"));
1433 debug_print("IMAP4< %s\n", buf);
1435 g_string_assign(str, buf);
1437 if (strncmp(p, "LIST ", 5) != 0) continue;
1440 if (*p != '(') continue;
1442 p = strchr_cpy(p, ')', flags, sizeof(flags));
1444 while (*p == ' ') p++;
1446 p = strchr_cpy(p, ' ', separator_str, sizeof(separator_str));
1448 extract_quote(separator_str, '"');
1449 if (!strcmp(separator_str, "NIL"))
1450 separator_str[0] = '\0';
1452 *separator = separator_str[0];
1455 while (*p == ' ') p++;
1456 if (*p == '{' || *p == '"')
1457 p = imap_parse_atom(SESSION(session)->sock, p,
1458 buf, sizeof(buf), str);
1460 strncpy2(buf, p, sizeof(buf));
1461 strtailchomp(buf, separator_str[0]);
1462 if (buf[0] == '\0') continue;
1463 if (!strcmp(buf, real_path)) continue;
1465 if (separator_str[0] != '\0')
1466 subst_char(buf, separator_str[0], '/');
1467 name = g_basename(buf);
1468 if (name[0] == '.') continue;
1470 loc_name = imap_modified_utf7_to_utf8(name);
1471 loc_path = imap_modified_utf7_to_utf8(buf);
1472 new_item = folder_item_new(FOLDER(folder), loc_name, loc_path);
1473 if (strcasestr(flags, "\\Noinferiors") != NULL)
1474 new_item->no_sub = TRUE;
1475 if (strcmp(buf, "INBOX") != 0 &&
1476 strcasestr(flags, "\\Noselect") != NULL)
1477 new_item->no_select = TRUE;
1479 item_list = g_slist_append(item_list, new_item);
1481 debug_print("folder '%s' found.\n", loc_path);
1486 g_string_free(str, TRUE);
1491 static gint imap_create_tree(Folder *folder)
1493 g_return_val_if_fail(folder != NULL, -1);
1494 g_return_val_if_fail(folder->node != NULL, -1);
1495 g_return_val_if_fail(folder->node->data != NULL, -1);
1496 g_return_val_if_fail(folder->account != NULL, -1);
1498 imap_scan_tree(folder);
1499 imap_create_missing_folders(folder);
1504 static void imap_create_missing_folders(Folder *folder)
1506 g_return_if_fail(folder != NULL);
1509 folder->inbox = imap_create_special_folder
1510 (folder, F_INBOX, "INBOX");
1512 if (!folder->outbox)
1513 folder->outbox = imap_create_special_folder
1514 (folder, F_OUTBOX, "Sent");
1516 folder->draft = imap_create_special_folder
1517 (folder, F_DRAFT, "Drafts");
1519 folder->queue = imap_create_special_folder
1520 (folder, F_QUEUE, "Queue");
1523 folder->trash = imap_create_special_folder
1524 (folder, F_TRASH, "Trash");
1527 static FolderItem *imap_create_special_folder(Folder *folder,
1528 SpecialFolderItemType stype,
1532 FolderItem *new_item;
1534 g_return_val_if_fail(folder != NULL, NULL);
1535 g_return_val_if_fail(folder->node != NULL, NULL);
1536 g_return_val_if_fail(folder->node->data != NULL, NULL);
1537 g_return_val_if_fail(folder->account != NULL, NULL);
1538 g_return_val_if_fail(name != NULL, NULL);
1540 item = FOLDER_ITEM(folder->node->data);
1541 new_item = imap_create_folder(folder, item, name);
1544 g_warning("Can't create '%s'\n", name);
1545 if (!folder->inbox) return NULL;
1547 new_item = imap_create_folder(folder, folder->inbox, name);
1549 g_warning("Can't create '%s' under INBOX\n", name);
1551 new_item->stype = stype;
1553 new_item->stype = stype;
1558 static gchar *imap_folder_get_path(Folder *folder)
1562 g_return_val_if_fail(folder != NULL, NULL);
1563 g_return_val_if_fail(folder->account != NULL, NULL);
1565 folder_path = g_strconcat(get_imap_cache_dir(),
1567 folder->account->recv_server,
1569 folder->account->userid,
1575 static gchar *imap_item_get_path(Folder *folder, FolderItem *item)
1577 gchar *folder_path, *path;
1579 g_return_val_if_fail(folder != NULL, NULL);
1580 g_return_val_if_fail(item != NULL, NULL);
1581 folder_path = imap_folder_get_path(folder);
1583 g_return_val_if_fail(folder_path != NULL, NULL);
1584 if (folder_path[0] == G_DIR_SEPARATOR) {
1586 path = g_strconcat(folder_path, G_DIR_SEPARATOR_S,
1589 path = g_strdup(folder_path);
1592 path = g_strconcat(get_home_dir(), G_DIR_SEPARATOR_S,
1593 folder_path, G_DIR_SEPARATOR_S,
1596 path = g_strconcat(get_home_dir(), G_DIR_SEPARATOR_S,
1599 g_free(folder_path);
1604 static FolderItem *imap_create_folder(Folder *folder, FolderItem *parent,
1607 gchar *dirpath, *imap_path;
1608 IMAPSession *session;
1609 FolderItem *new_item;
1615 g_return_val_if_fail(folder != NULL, NULL);
1616 g_return_val_if_fail(folder->account != NULL, NULL);
1617 g_return_val_if_fail(parent != NULL, NULL);
1618 g_return_val_if_fail(name != NULL, NULL);
1620 session = imap_session_get(folder);
1621 if (!session) return NULL;
1623 if (!folder_item_parent(parent) && strcmp(name, "INBOX") == 0)
1624 dirpath = g_strdup(name);
1625 else if (parent->path)
1626 dirpath = g_strconcat(parent->path, "/", name, NULL);
1627 else if ((p = strchr(name, '/')) != NULL && *(p + 1) != '\0')
1628 dirpath = g_strdup(name);
1629 else if (folder->account->imap_dir && *folder->account->imap_dir) {
1632 Xstrdup_a(imap_dir, folder->account->imap_dir, return NULL);
1633 strtailchomp(imap_dir, '/');
1634 dirpath = g_strconcat(imap_dir, "/", name, NULL);
1636 dirpath = g_strdup(name);
1638 /* keep trailing directory separator to create a folder that contains
1640 imap_path = imap_utf8_to_modified_utf7(dirpath);
1641 strtailchomp(dirpath, '/');
1642 Xstrdup_a(new_name, name, {g_free(dirpath); return NULL;});
1643 strtailchomp(new_name, '/');
1644 separator = imap_get_path_separator(IMAP_FOLDER(folder), imap_path);
1645 imap_path_separator_subst(imap_path, separator);
1646 subst_char(new_name, '/', separator);
1648 if (strcmp(name, "INBOX") != 0) {
1651 gboolean exist = FALSE;
1653 argbuf = g_ptr_array_new();
1654 ok = imap_cmd_list(session, NULL, imap_path,
1656 if (ok != IMAP_SUCCESS) {
1657 log_warning(_("can't create mailbox: LIST failed\n"));
1660 ptr_array_free_strings(argbuf);
1661 g_ptr_array_free(argbuf, TRUE);
1665 for (i = 0; i < argbuf->len; i++) {
1667 str = g_ptr_array_index(argbuf, i);
1668 if (!strncmp(str, "LIST ", 5)) {
1673 ptr_array_free_strings(argbuf);
1674 g_ptr_array_free(argbuf, TRUE);
1677 ok = imap_cmd_create(session, imap_path);
1678 if (ok != IMAP_SUCCESS) {
1679 log_warning(_("can't create mailbox\n"));
1687 new_item = folder_item_new(folder, new_name, dirpath);
1688 folder_item_append(parent, new_item);
1692 dirpath = folder_item_get_path(new_item);
1693 if (!is_dir_exist(dirpath))
1694 make_dir_hier(dirpath);
1700 static gint imap_rename_folder(Folder *folder, FolderItem *item,
1705 gchar *real_oldpath;
1706 gchar *real_newpath;
1708 gchar *old_cache_dir;
1709 gchar *new_cache_dir;
1710 IMAPSession *session;
1713 gint exists, recent, unseen;
1714 guint32 uid_validity;
1716 g_return_val_if_fail(folder != NULL, -1);
1717 g_return_val_if_fail(item != NULL, -1);
1718 g_return_val_if_fail(item->path != NULL, -1);
1719 g_return_val_if_fail(name != NULL, -1);
1721 session = imap_session_get(folder);
1722 if (!session) return -1;
1724 real_oldpath = imap_get_real_path(IMAP_FOLDER(folder), item->path);
1726 g_free(session->mbox);
1727 session->mbox = NULL;
1728 ok = imap_cmd_examine(session, "INBOX",
1729 &exists, &recent, &unseen, &uid_validity);
1730 if (ok != IMAP_SUCCESS) {
1731 g_free(real_oldpath);
1735 separator = imap_get_path_separator(IMAP_FOLDER(folder), item->path);
1736 if (strchr(item->path, G_DIR_SEPARATOR)) {
1737 dirpath = g_dirname(item->path);
1738 newpath = g_strconcat(dirpath, G_DIR_SEPARATOR_S, name, NULL);
1741 newpath = g_strdup(name);
1743 real_newpath = imap_utf8_to_modified_utf7(newpath);
1744 imap_path_separator_subst(real_newpath, separator);
1746 ok = imap_cmd_rename(session, real_oldpath, real_newpath);
1747 if (ok != IMAP_SUCCESS) {
1748 log_warning(_("can't rename mailbox: %s to %s\n"),
1749 real_oldpath, real_newpath);
1750 g_free(real_oldpath);
1752 g_free(real_newpath);
1757 item->name = g_strdup(name);
1759 old_cache_dir = folder_item_get_path(item);
1761 paths[0] = g_strdup(item->path);
1763 g_node_traverse(item->node, G_PRE_ORDER, G_TRAVERSE_ALL, -1,
1764 imap_rename_folder_func, paths);
1766 if (is_dir_exist(old_cache_dir)) {
1767 new_cache_dir = folder_item_get_path(item);
1768 if (rename(old_cache_dir, new_cache_dir) < 0) {
1769 FILE_OP_ERROR(old_cache_dir, "rename");
1771 g_free(new_cache_dir);
1774 g_free(old_cache_dir);
1777 g_free(real_oldpath);
1778 g_free(real_newpath);
1783 static gint imap_remove_folder(Folder *folder, FolderItem *item)
1786 IMAPSession *session;
1789 gint exists, recent, unseen;
1790 guint32 uid_validity;
1792 g_return_val_if_fail(folder != NULL, -1);
1793 g_return_val_if_fail(item != NULL, -1);
1794 g_return_val_if_fail(item->path != NULL, -1);
1796 session = imap_session_get(folder);
1797 if (!session) return -1;
1799 path = imap_get_real_path(IMAP_FOLDER(folder), item->path);
1801 ok = imap_cmd_examine(session, "INBOX",
1802 &exists, &recent, &unseen, &uid_validity);
1803 if (ok != IMAP_SUCCESS) {
1808 ok = imap_cmd_delete(session, path);
1809 if (ok != IMAP_SUCCESS) {
1810 log_warning(_("can't delete mailbox\n"));
1816 cache_dir = folder_item_get_path(item);
1817 if (is_dir_exist(cache_dir) && remove_dir_recursive(cache_dir) < 0)
1818 g_warning("can't remove directory '%s'\n", cache_dir);
1820 folder_item_remove(item);
1825 static GSList *imap_get_uncached_messages(IMAPSession *session,
1827 MsgNumberList *numlist)
1830 GSList *newlist = NULL;
1831 GSList *llast = NULL;
1834 GSList *seq_list, *cur;
1837 g_return_val_if_fail(session != NULL, NULL);
1838 g_return_val_if_fail(item != NULL, NULL);
1839 g_return_val_if_fail(item->folder != NULL, NULL);
1840 g_return_val_if_fail(FOLDER_CLASS(item->folder) == &imap_class, NULL);
1842 seq_list = imap_get_seq_set_from_numlist(numlist);
1843 for (cur = seq_list; cur != NULL; cur = g_slist_next(cur)) {
1844 imapset = cur->data;
1846 if (imap_cmd_envelope(session, imapset)
1848 log_warning(_("can't get envelope\n"));
1852 str = g_string_new(NULL);
1855 if ((tmp = sock_getline(SESSION(session)->sock)) == NULL) {
1856 log_warning(_("error occurred while getting envelope.\n"));
1857 g_string_free(str, TRUE);
1861 if (tmp[0] != '*' || tmp[1] != ' ') {
1862 log_print("IMAP4< %s\n", tmp);
1866 if (strstr(tmp, "FETCH") == NULL) {
1867 log_print("IMAP4< %s\n", tmp);
1871 log_print("IMAP4< %s\n", tmp);
1872 g_string_assign(str, tmp);
1875 msginfo = imap_parse_envelope
1876 (SESSION(session)->sock, item, str);
1878 log_warning(_("can't parse envelope: %s\n"), str->str);
1881 if (item->stype == F_QUEUE) {
1882 MSG_SET_TMP_FLAGS(msginfo->flags, MSG_QUEUED);
1883 } else if (item->stype == F_DRAFT) {
1884 MSG_SET_TMP_FLAGS(msginfo->flags, MSG_DRAFT);
1887 msginfo->folder = item;
1890 llast = newlist = g_slist_append(newlist, msginfo);
1892 llast = g_slist_append(llast, msginfo);
1893 llast = llast->next;
1897 g_string_free(str, TRUE);
1899 imap_seq_set_free(seq_list);
1904 static void imap_delete_all_cached_messages(FolderItem *item)
1908 g_return_if_fail(item != NULL);
1909 g_return_if_fail(item->folder != NULL);
1910 g_return_if_fail(FOLDER_CLASS(item->folder) == &imap_class);
1912 debug_print("Deleting all cached messages...\n");
1914 dir = folder_item_get_path(item);
1915 if (is_dir_exist(dir))
1916 remove_all_numbered_files(dir);
1919 debug_print("done.\n");
1923 static SockInfo *imap_open_tunnel(const gchar *server,
1924 const gchar *tunnelcmd,
1927 static SockInfo *imap_open_tunnel(const gchar *server,
1928 const gchar *tunnelcmd)
1933 if ((sock = sock_connect_cmd(server, tunnelcmd)) == NULL) {
1934 log_warning(_("Can't establish IMAP4 session with: %s\n"),
1939 return imap_init_sock(sock, ssl_type);
1941 return imap_init_sock(sock);
1947 static SockInfo *imap_open(const gchar *server, gushort port,
1950 static SockInfo *imap_open(const gchar *server, gushort port)
1955 if ((sock = sock_connect(server, port)) == NULL) {
1956 log_warning(_("Can't connect to IMAP4 server: %s:%d\n"),
1962 if (ssl_type == SSL_TUNNEL && !ssl_init_socket(sock)) {
1963 log_warning(_("Can't establish IMAP4 session with: %s:%d\n"),
1973 static SockInfo *imap_init_sock(SockInfo *sock, SSLType ssl_type)
1975 static SockInfo *imap_init_sock(SockInfo *sock)
1982 static GList *imap_parse_namespace_str(gchar *str)
1987 IMAPNameSpace *namespace;
1988 GList *ns_list = NULL;
1990 while (*p != '\0') {
1991 /* parse ("#foo" "/") */
1993 while (*p && *p != '(') p++;
1994 if (*p == '\0') break;
1997 while (*p && *p != '"') p++;
1998 if (*p == '\0') break;
2002 while (*p && *p != '"') p++;
2003 if (*p == '\0') break;
2007 while (*p && isspace(*p)) p++;
2008 if (*p == '\0') break;
2009 if (strncmp(p, "NIL", 3) == 0)
2011 else if (*p == '"') {
2014 while (*p && *p != '"') p++;
2015 if (*p == '\0') break;
2020 while (*p && *p != ')') p++;
2021 if (*p == '\0') break;
2024 namespace = g_new(IMAPNameSpace, 1);
2025 namespace->name = g_strdup(name);
2026 namespace->separator = separator ? separator[0] : '\0';
2027 ns_list = g_list_append(ns_list, namespace);
2033 static void imap_parse_namespace(IMAPSession *session, IMAPFolder *folder)
2038 g_return_if_fail(session != NULL);
2039 g_return_if_fail(folder != NULL);
2041 if (folder->ns_personal != NULL ||
2042 folder->ns_others != NULL ||
2043 folder->ns_shared != NULL)
2046 if (!imap_has_capability(session, "NAMESPACE")) {
2047 imap_get_namespace_by_list(session, folder);
2051 if (imap_cmd_namespace(session, &ns_str)
2053 log_warning(_("can't get namespace\n"));
2057 str_array = strsplit_parenthesis(ns_str, '(', ')', 3);
2058 if (str_array == NULL) {
2060 imap_get_namespace_by_list(session, folder);
2064 folder->ns_personal = imap_parse_namespace_str(str_array[0]);
2065 if (str_array[0] && str_array[1])
2066 folder->ns_others = imap_parse_namespace_str(str_array[1]);
2067 if (str_array[0] && str_array[1] && str_array[2])
2068 folder->ns_shared = imap_parse_namespace_str(str_array[2]);
2069 g_strfreev(str_array);
2073 static void imap_get_namespace_by_list(IMAPSession *session, IMAPFolder *folder)
2075 GSList *item_list, *cur;
2076 gchar separator = '\0';
2077 IMAPNameSpace *namespace;
2079 g_return_if_fail(session != NULL);
2080 g_return_if_fail(folder != NULL);
2082 if (folder->ns_personal != NULL ||
2083 folder->ns_others != NULL ||
2084 folder->ns_shared != NULL)
2087 imap_gen_send(session, "LIST \"\" \"\"");
2088 item_list = imap_parse_list(folder, session, "", &separator);
2089 for (cur = item_list; cur != NULL; cur = cur->next)
2090 folder_item_destroy(FOLDER_ITEM(cur->data));
2091 g_slist_free(item_list);
2093 namespace = g_new(IMAPNameSpace, 1);
2094 namespace->name = g_strdup("");
2095 namespace->separator = separator;
2096 folder->ns_personal = g_list_append(NULL, namespace);
2099 static IMAPNameSpace *imap_find_namespace_from_list(GList *ns_list,
2102 IMAPNameSpace *namespace = NULL;
2103 gchar *tmp_path, *name;
2105 if (!path) path = "";
2107 for (; ns_list != NULL; ns_list = ns_list->next) {
2108 IMAPNameSpace *tmp_ns = ns_list->data;
2110 Xstrcat_a(tmp_path, path, "/", return namespace);
2111 Xstrdup_a(name, tmp_ns->name, return namespace);
2112 if (tmp_ns->separator && tmp_ns->separator != '/') {
2113 subst_char(tmp_path, tmp_ns->separator, '/');
2114 subst_char(name, tmp_ns->separator, '/');
2116 if (strncmp(tmp_path, name, strlen(name)) == 0)
2123 static IMAPNameSpace *imap_find_namespace(IMAPFolder *folder,
2126 IMAPNameSpace *namespace;
2128 g_return_val_if_fail(folder != NULL, NULL);
2130 namespace = imap_find_namespace_from_list(folder->ns_personal, path);
2131 if (namespace) return namespace;
2132 namespace = imap_find_namespace_from_list(folder->ns_others, path);
2133 if (namespace) return namespace;
2134 namespace = imap_find_namespace_from_list(folder->ns_shared, path);
2135 if (namespace) return namespace;
2140 static gchar imap_get_path_separator(IMAPFolder *folder, const gchar *path)
2142 IMAPNameSpace *namespace;
2143 gchar separator = '/';
2145 namespace = imap_find_namespace(folder, path);
2146 if (namespace && namespace->separator)
2147 separator = namespace->separator;
2152 static gchar *imap_get_real_path(IMAPFolder *folder, const gchar *path)
2157 g_return_val_if_fail(folder != NULL, NULL);
2158 g_return_val_if_fail(path != NULL, NULL);
2160 real_path = imap_utf8_to_modified_utf7(path);
2161 separator = imap_get_path_separator(folder, path);
2162 imap_path_separator_subst(real_path, separator);
2167 static gchar *imap_parse_atom(SockInfo *sock, gchar *src,
2168 gchar *dest, gint dest_len, GString *str)
2170 gchar *cur_pos = src;
2173 g_return_val_if_fail(str != NULL, cur_pos);
2175 /* read the next line if the current response buffer is empty */
2176 while (isspace(*(guchar *)cur_pos)) cur_pos++;
2177 while (*cur_pos == '\0') {
2178 if ((nextline = sock_getline(sock)) == NULL)
2180 g_string_assign(str, nextline);
2182 strretchomp(nextline);
2183 /* log_print("IMAP4< %s\n", nextline); */
2184 debug_print("IMAP4< %s\n", nextline);
2187 while (isspace(*(guchar *)cur_pos)) cur_pos++;
2190 if (!strncmp(cur_pos, "NIL", 3)) {
2193 } else if (*cur_pos == '\"') {
2196 p = get_quoted(cur_pos, '\"', dest, dest_len);
2197 cur_pos = p ? p : cur_pos + 2;
2198 } else if (*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 line_len += strlen(nextline);
2214 g_string_append(str, nextline);
2216 strretchomp(nextline);
2217 /* log_print("IMAP4< %s\n", nextline); */
2218 debug_print("IMAP4< %s\n", nextline);
2220 } while (line_len < len);
2222 memcpy(dest, cur_pos, MIN(len, dest_len - 1));
2223 dest[MIN(len, dest_len - 1)] = '\0';
2230 static gchar *imap_get_header(SockInfo *sock, gchar *cur_pos, gchar **headers,
2240 g_return_val_if_fail(str != NULL, cur_pos);
2242 while (isspace(*(guchar *)cur_pos)) cur_pos++;
2244 g_return_val_if_fail(*cur_pos == '{', cur_pos);
2246 cur_pos = strchr_cpy(cur_pos + 1, '}', buf, sizeof(buf));
2248 g_return_val_if_fail(len >= 0, cur_pos);
2250 g_string_truncate(str, 0);
2254 if ((nextline = sock_getline(sock)) == NULL)
2256 block_len += strlen(nextline);
2257 g_string_append(str, nextline);
2259 strretchomp(nextline);
2260 /* debug_print("IMAP4< %s\n", nextline); */
2262 } while (block_len < len);
2264 debug_print("IMAP4< [contents of BODY.PEEK[HEADER.FIELDS (...)]]\n");
2266 *headers = g_strndup(cur_pos, len);
2269 while (isspace(*(guchar *)cur_pos)) cur_pos++;
2270 while (*cur_pos == '\0') {
2271 if ((nextline = sock_getline(sock)) == NULL)
2273 g_string_assign(str, nextline);
2275 strretchomp(nextline);
2276 debug_print("IMAP4< %s\n", nextline);
2279 while (isspace(*(guchar *)cur_pos)) cur_pos++;
2285 static MsgFlags imap_parse_flags(const gchar *flag_str)
2287 const gchar *p = flag_str;
2288 MsgFlags flags = {0, 0};
2290 flags.perm_flags = MSG_UNREAD;
2292 while ((p = strchr(p, '\\')) != NULL) {
2295 if (g_strncasecmp(p, "Recent", 6) == 0 && MSG_IS_UNREAD(flags)) {
2296 MSG_SET_PERM_FLAGS(flags, MSG_NEW);
2297 } else if (g_strncasecmp(p, "Seen", 4) == 0) {
2298 MSG_UNSET_PERM_FLAGS(flags, MSG_NEW|MSG_UNREAD);
2299 } else if (g_strncasecmp(p, "Deleted", 7) == 0) {
2300 MSG_SET_PERM_FLAGS(flags, MSG_DELETED);
2301 } else if (g_strncasecmp(p, "Flagged", 7) == 0) {
2302 MSG_SET_PERM_FLAGS(flags, MSG_MARKED);
2303 } else if (g_strncasecmp(p, "Answered", 8) == 0) {
2304 MSG_SET_PERM_FLAGS(flags, MSG_REPLIED);
2311 static MsgInfo *imap_parse_envelope(SockInfo *sock, FolderItem *item,
2314 gchar buf[IMAPBUFSIZE];
2315 MsgInfo *msginfo = NULL;
2320 MsgFlags flags = {0, 0}, imap_flags = {0, 0};
2322 g_return_val_if_fail(line_str != NULL, NULL);
2323 g_return_val_if_fail(line_str->str[0] == '*' &&
2324 line_str->str[1] == ' ', NULL);
2326 MSG_SET_TMP_FLAGS(flags, MSG_IMAP);
2327 if (item->stype == F_QUEUE) {
2328 MSG_SET_TMP_FLAGS(flags, MSG_QUEUED);
2329 } else if (item->stype == F_DRAFT) {
2330 MSG_SET_TMP_FLAGS(flags, MSG_DRAFT);
2333 cur_pos = line_str->str + 2;
2335 #define PARSE_ONE_ELEMENT(ch) \
2337 cur_pos = strchr_cpy(cur_pos, ch, buf, sizeof(buf)); \
2338 if (cur_pos == NULL) { \
2339 g_warning("cur_pos == NULL\n"); \
2340 procmsg_msginfo_free(msginfo); \
2345 PARSE_ONE_ELEMENT(' ');
2348 PARSE_ONE_ELEMENT(' ');
2349 g_return_val_if_fail(!strcmp(buf, "FETCH"), NULL);
2351 g_return_val_if_fail(*cur_pos == '(', NULL);
2354 while (*cur_pos != '\0' && *cur_pos != ')') {
2355 while (*cur_pos == ' ') cur_pos++;
2357 if (!strncmp(cur_pos, "UID ", 4)) {
2359 uid = strtoul(cur_pos, &cur_pos, 10);
2360 } else if (!strncmp(cur_pos, "FLAGS ", 6)) {
2362 if (*cur_pos != '(') {
2363 g_warning("*cur_pos != '('\n");
2364 procmsg_msginfo_free(msginfo);
2368 PARSE_ONE_ELEMENT(')');
2369 imap_flags = imap_parse_flags(buf);
2370 } else if (!strncmp(cur_pos, "RFC822.SIZE ", 12)) {
2372 size = strtol(cur_pos, &cur_pos, 10);
2373 } else if (!strncmp(cur_pos, "BODY[HEADER.FIELDS ", 19)) {
2377 if (*cur_pos != '(') {
2378 g_warning("*cur_pos != '('\n");
2379 procmsg_msginfo_free(msginfo);
2383 PARSE_ONE_ELEMENT(')');
2384 if (*cur_pos != ']') {
2385 g_warning("*cur_pos != ']'\n");
2386 procmsg_msginfo_free(msginfo);
2391 cur_pos = imap_get_header(sock, cur_pos, &headers,
2393 msginfo = procheader_parse_str(headers, flags, FALSE, FALSE);
2396 g_warning("invalid FETCH response: %s\n", cur_pos);
2402 msginfo->msgnum = uid;
2403 msginfo->size = size;
2404 msginfo->flags.tmp_flags |= imap_flags.tmp_flags;
2405 msginfo->flags.perm_flags = imap_flags.perm_flags;
2411 static gchar *imap_get_flag_str(IMAPFlags flags)
2416 str = g_string_new(NULL);
2418 if (IMAP_IS_SEEN(flags)) g_string_append(str, "\\Seen ");
2419 if (IMAP_IS_ANSWERED(flags)) g_string_append(str, "\\Answered ");
2420 if (IMAP_IS_FLAGGED(flags)) g_string_append(str, "\\Flagged ");
2421 if (IMAP_IS_DELETED(flags)) g_string_append(str, "\\Deleted ");
2422 if (IMAP_IS_DRAFT(flags)) g_string_append(str, "\\Draft");
2424 if (str->len > 0 && str->str[str->len - 1] == ' ')
2425 g_string_truncate(str, str->len - 1);
2428 g_string_free(str, FALSE);
2433 static gint imap_set_message_flags(IMAPSession *session,
2434 MsgNumberList *numlist,
2441 GSList *seq_list, *cur;
2444 flag_str = imap_get_flag_str(flags);
2445 cmd = g_strconcat(is_set ? "+FLAGS.SILENT (" : "-FLAGS.SILENT (",
2446 flag_str, ")", NULL);
2449 seq_list = imap_get_seq_set_from_numlist(numlist);
2450 for (cur = seq_list; cur != NULL; cur = g_slist_next(cur)) {
2451 imapset = cur->data;
2453 ok = imap_cmd_store(session, imapset, cmd);
2455 imap_seq_set_free(seq_list);
2461 static gint imap_select(IMAPSession *session, IMAPFolder *folder,
2463 gint *exists, gint *recent, gint *unseen,
2464 guint32 *uid_validity)
2468 gint exists_, recent_, unseen_, uid_validity_;
2470 if (!exists || !recent || !unseen || !uid_validity) {
2471 if (session->mbox && strcmp(session->mbox, path) == 0)
2472 return IMAP_SUCCESS;
2476 uid_validity = &uid_validity_;
2479 g_free(session->mbox);
2480 session->mbox = NULL;
2482 real_path = imap_get_real_path(folder, path);
2483 ok = imap_cmd_select(session, real_path,
2484 exists, recent, unseen, uid_validity);
2485 if (ok != IMAP_SUCCESS)
2486 log_warning(_("can't select folder: %s\n"), real_path);
2488 session->mbox = g_strdup(path);
2489 session->folder_content_changed = FALSE;
2496 #define THROW(err) { ok = err; goto catch; }
2498 static gint imap_status(IMAPSession *session, IMAPFolder *folder,
2500 gint *messages, gint *recent,
2501 guint32 *uid_next, guint32 *uid_validity,
2507 GPtrArray *argbuf = NULL;
2510 if (messages && recent && uid_next && uid_validity && unseen) {
2511 *messages = *recent = *uid_next = *uid_validity = *unseen = 0;
2512 argbuf = g_ptr_array_new();
2515 real_path = imap_get_real_path(folder, path);
2516 QUOTE_IF_REQUIRED(real_path_, real_path);
2517 imap_gen_send(session, "STATUS %s "
2518 "(MESSAGES RECENT UIDNEXT UIDVALIDITY UNSEEN)",
2521 ok = imap_cmd_ok(session, argbuf);
2522 if (ok != IMAP_SUCCESS || !argbuf) THROW(ok);
2524 str = search_array_str(argbuf, "STATUS");
2525 if (!str) THROW(IMAP_ERROR);
2527 str = strchr(str, '(');
2528 if (!str) THROW(IMAP_ERROR);
2530 while (*str != '\0' && *str != ')') {
2531 while (*str == ' ') str++;
2533 if (!strncmp(str, "MESSAGES ", 9)) {
2535 *messages = strtol(str, &str, 10);
2536 } else if (!strncmp(str, "RECENT ", 7)) {
2538 *recent = strtol(str, &str, 10);
2539 } else if (!strncmp(str, "UIDNEXT ", 8)) {
2541 *uid_next = strtoul(str, &str, 10);
2542 } else if (!strncmp(str, "UIDVALIDITY ", 12)) {
2544 *uid_validity = strtoul(str, &str, 10);
2545 } else if (!strncmp(str, "UNSEEN ", 7)) {
2547 *unseen = strtol(str, &str, 10);
2549 g_warning("invalid STATUS response: %s\n", str);
2557 ptr_array_free_strings(argbuf);
2558 g_ptr_array_free(argbuf, TRUE);
2566 static gboolean imap_has_capability(IMAPSession *session, const gchar *cap)
2570 for (p = session->capability; *p != NULL; ++p) {
2571 if (!g_strcasecmp(*p, cap))
2578 static void imap_free_capabilities(IMAPSession *session)
2580 g_strfreev(session->capability);
2581 session->capability = NULL;
2584 /* low-level IMAP4rev1 commands */
2586 static gint imap_cmd_authenticate(IMAPSession *session, const gchar *user,
2587 const gchar *pass, IMAPAuthType type)
2594 gchar hexdigest[33];
2598 auth_type = "CRAM-MD5";
2600 imap_gen_send(session, "AUTHENTICATE %s", auth_type);
2601 ok = imap_gen_recv(session, &buf);
2602 if (ok != IMAP_SUCCESS || buf[0] != '+' || buf[1] != ' ') {
2607 challenge = g_malloc(strlen(buf + 2) + 1);
2608 challenge_len = base64_decode(challenge, buf + 2, -1);
2609 challenge[challenge_len] = '\0';
2611 log_print("IMAP< [Decoded: %s]\n", challenge);
2613 md5_hex_hmac(hexdigest, challenge, challenge_len, pass, strlen(pass));
2616 response = g_strdup_printf("%s %s", user, hexdigest);
2617 log_print("IMAP> [Encoded: %s]\n", response);
2618 response64 = g_malloc((strlen(response) + 3) * 2 + 1);
2619 base64_encode(response64, response, strlen(response));
2622 log_print("IMAP> %s\n", response64);
2623 sock_puts(SESSION(session)->sock, response64);
2624 ok = imap_cmd_ok(session, NULL);
2625 if (ok != IMAP_SUCCESS)
2626 log_warning(_("IMAP4 authentication failed.\n"));
2631 static gint imap_cmd_login(IMAPSession *session,
2632 const gchar *user, const gchar *pass)
2634 gchar *user_, *pass_;
2637 QUOTE_IF_REQUIRED(user_, user);
2638 QUOTE_IF_REQUIRED(pass_, pass);
2639 imap_gen_send(session, "LOGIN %s %s", user_, pass_);
2641 ok = imap_cmd_ok(session, NULL);
2642 if (ok != IMAP_SUCCESS)
2643 log_warning(_("IMAP4 login failed.\n"));
2648 static gint imap_cmd_logout(IMAPSession *session)
2650 imap_gen_send(session, "LOGOUT");
2651 return imap_cmd_ok(session, NULL);
2654 static gint imap_cmd_noop(IMAPSession *session)
2656 imap_gen_send(session, "NOOP");
2657 return imap_cmd_ok(session, NULL);
2660 static gint imap_cmd_starttls(IMAPSession *session)
2662 imap_gen_send(session, "STARTTLS");
2663 return imap_cmd_ok(session, NULL);
2666 #define THROW(err) { ok = err; goto catch; }
2668 static gint imap_cmd_namespace(IMAPSession *session, gchar **ns_str)
2674 argbuf = g_ptr_array_new();
2676 imap_gen_send(session, "NAMESPACE");
2677 if ((ok = imap_cmd_ok(session, argbuf)) != IMAP_SUCCESS) THROW(ok);
2679 str = search_array_str(argbuf, "NAMESPACE");
2680 if (!str) THROW(IMAP_ERROR);
2682 *ns_str = g_strdup(str);
2685 ptr_array_free_strings(argbuf);
2686 g_ptr_array_free(argbuf, TRUE);
2693 static gint imap_cmd_list(IMAPSession *session, const gchar *ref,
2694 const gchar *mailbox, GPtrArray *argbuf)
2696 gchar *ref_, *mailbox_;
2698 if (!ref) ref = "\"\"";
2699 if (!mailbox) mailbox = "\"\"";
2701 QUOTE_IF_REQUIRED(ref_, ref);
2702 QUOTE_IF_REQUIRED(mailbox_, mailbox);
2703 imap_gen_send(session, "LIST %s %s", ref_, mailbox_);
2705 return imap_cmd_ok(session, argbuf);
2708 #define THROW goto catch
2710 static gint imap_cmd_do_select(IMAPSession *session, const gchar *folder,
2712 gint *exists, gint *recent, gint *unseen,
2713 guint32 *uid_validity)
2721 *exists = *recent = *unseen = *uid_validity = 0;
2722 argbuf = g_ptr_array_new();
2725 select_cmd = "EXAMINE";
2727 select_cmd = "SELECT";
2729 QUOTE_IF_REQUIRED(folder_, folder);
2730 imap_gen_send(session, "%s %s", select_cmd, folder_);
2732 if ((ok = imap_cmd_ok(session, argbuf)) != IMAP_SUCCESS) THROW;
2734 resp_str = search_array_contain_str(argbuf, "EXISTS");
2736 if (sscanf(resp_str,"%d EXISTS", exists) != 1) {
2737 g_warning("imap_cmd_select(): invalid EXISTS line.\n");
2742 resp_str = search_array_contain_str(argbuf, "RECENT");
2744 if (sscanf(resp_str, "%d RECENT", recent) != 1) {
2745 g_warning("imap_cmd_select(): invalid RECENT line.\n");
2750 resp_str = search_array_contain_str(argbuf, "UIDVALIDITY");
2752 if (sscanf(resp_str, "OK [UIDVALIDITY %u] ", uid_validity)
2754 g_warning("imap_cmd_select(): invalid UIDVALIDITY line.\n");
2759 resp_str = search_array_contain_str(argbuf, "UNSEEN");
2761 if (sscanf(resp_str, "OK [UNSEEN %d] ", unseen) != 1) {
2762 g_warning("imap_cmd_select(): invalid UNSEEN line.\n");
2768 ptr_array_free_strings(argbuf);
2769 g_ptr_array_free(argbuf, TRUE);
2774 static gint imap_cmd_select(IMAPSession *session, const gchar *folder,
2775 gint *exists, gint *recent, gint *unseen,
2776 guint32 *uid_validity)
2778 return imap_cmd_do_select(session, folder, FALSE,
2779 exists, recent, unseen, uid_validity);
2782 static gint imap_cmd_examine(IMAPSession *session, const gchar *folder,
2783 gint *exists, gint *recent, gint *unseen,
2784 guint32 *uid_validity)
2786 return imap_cmd_do_select(session, folder, TRUE,
2787 exists, recent, unseen, uid_validity);
2792 static gint imap_cmd_create(IMAPSession *session, const gchar *folder)
2796 QUOTE_IF_REQUIRED(folder_, folder);
2797 imap_gen_send(session, "CREATE %s", folder_);
2799 return imap_cmd_ok(session, NULL);
2802 static gint imap_cmd_rename(IMAPSession *session, const gchar *old_folder,
2803 const gchar *new_folder)
2805 gchar *old_folder_, *new_folder_;
2807 QUOTE_IF_REQUIRED(old_folder_, old_folder);
2808 QUOTE_IF_REQUIRED(new_folder_, new_folder);
2809 imap_gen_send(session, "RENAME %s %s", old_folder_, new_folder_);
2811 return imap_cmd_ok(session, NULL);
2814 static gint imap_cmd_delete(IMAPSession *session, const gchar *folder)
2818 QUOTE_IF_REQUIRED(folder_, folder);
2819 imap_gen_send(session, "DELETE %s", folder_);
2821 return imap_cmd_ok(session, NULL);
2824 static gint imap_cmd_search(IMAPSession *session, const gchar *criteria,
2831 g_return_val_if_fail(criteria != NULL, IMAP_ERROR);
2832 g_return_val_if_fail(list != NULL, IMAP_ERROR);
2836 argbuf = g_ptr_array_new();
2837 imap_gen_send(session, "UID SEARCH %s", criteria);
2839 ok = imap_cmd_ok(session, argbuf);
2840 if (ok != IMAP_SUCCESS) {
2841 ptr_array_free_strings(argbuf);
2842 g_ptr_array_free(argbuf, TRUE);
2846 if ((uidlist = search_array_str(argbuf, "SEARCH ")) != NULL) {
2847 gchar **strlist, **p;
2849 strlist = g_strsplit(uidlist + 7, " ", 0);
2850 for (p = strlist; *p != NULL; ++p) {
2853 if (sscanf(*p, "%d", &msgnum) == 1)
2854 *list = g_slist_append(*list, GINT_TO_POINTER(msgnum));
2856 g_strfreev(strlist);
2858 ptr_array_free_strings(argbuf);
2859 g_ptr_array_free(argbuf, TRUE);
2861 return IMAP_SUCCESS;
2864 static gint imap_cmd_fetch(IMAPSession *session, guint32 uid,
2865 const gchar *filename)
2873 g_return_val_if_fail(filename != NULL, IMAP_ERROR);
2875 imap_gen_send(session, "UID FETCH %d BODY.PEEK[]", uid);
2877 while ((ok = imap_gen_recv(session, &buf)) == IMAP_SUCCESS) {
2878 if (buf[0] != '*' || buf[1] != ' ') {
2882 if (strstr(buf, "FETCH") != NULL) break;
2885 if (ok != IMAP_SUCCESS) {
2890 #define RETURN_ERROR_IF_FAIL(cond) \
2893 return IMAP_ERROR; \
2896 cur_pos = strchr(buf, '{');
2897 RETURN_ERROR_IF_FAIL(cur_pos != NULL);
2898 cur_pos = strchr_cpy(cur_pos + 1, '}', size_str, sizeof(size_str));
2899 RETURN_ERROR_IF_FAIL(cur_pos != NULL);
2900 size_num = atol(size_str);
2901 RETURN_ERROR_IF_FAIL(size_num >= 0);
2903 RETURN_ERROR_IF_FAIL(*cur_pos == '\0');
2905 #undef RETURN_ERROR_IF_FAIL
2909 if (recv_bytes_write_to_file(SESSION(session)->sock,
2910 size_num, filename) != 0)
2913 if (imap_gen_recv(session, &buf) != IMAP_SUCCESS) {
2918 if (buf[0] == '\0' || buf[strlen(buf) - 1] != ')') {
2924 ok = imap_cmd_ok(session, NULL);
2929 static gint imap_cmd_append(IMAPSession *session, const gchar *destfolder,
2930 const gchar *file, IMAPFlags flags,
2939 gchar buf[BUFFSIZE];
2944 g_return_val_if_fail(file != NULL, IMAP_ERROR);
2946 size = get_file_size_as_crlf(file);
2947 if ((fp = fopen(file, "rb")) == NULL) {
2948 FILE_OP_ERROR(file, "fopen");
2951 QUOTE_IF_REQUIRED(destfolder_, destfolder);
2952 flag_str = imap_get_flag_str(flags);
2953 imap_gen_send(session, "APPEND %s (%s) {%d}",
2954 destfolder_, flag_str, size);
2957 ok = imap_gen_recv(session, &ret);
2958 if (ok != IMAP_SUCCESS || ret[0] != '+' || ret[1] != ' ') {
2959 log_warning(_("can't append %s to %s\n"), file, destfolder_);
2966 log_print("IMAP4> %s\n", _("(sending file...)"));
2968 while (fgets(buf, sizeof(buf), fp) != NULL) {
2970 if (sock_puts(SESSION(session)->sock, buf) < 0) {
2977 FILE_OP_ERROR(file, "fgets");
2982 sock_puts(SESSION(session)->sock, "");
2986 if (new_uid != NULL)
2989 if (new_uid != NULL && session->uidplus) {
2990 argbuf = g_ptr_array_new();
2992 ok = imap_cmd_ok(session, argbuf);
2993 if ((ok == IMAP_SUCCESS) && (argbuf->len > 0)) {
2994 resp_str = g_ptr_array_index(argbuf, argbuf->len - 1);
2996 sscanf(resp_str, "%*u OK [APPENDUID %*u %u]",
2998 *new_uid = new_uid_;
3002 ptr_array_free_strings(argbuf);
3003 g_ptr_array_free(argbuf, TRUE);
3005 ok = imap_cmd_ok(session, NULL);
3007 if (ok != IMAP_SUCCESS)
3008 log_warning(_("can't append message to %s\n"),
3014 static MsgNumberList *imapset_to_numlist(IMAPSet imapset)
3016 gchar **ranges, **range;
3018 MsgNumberList *uids = NULL;
3020 ranges = g_strsplit(imapset, ",", 0);
3021 for (range = ranges; *range != NULL; range++) {
3022 printf("%s\n", *range);
3023 if(sscanf(*range, "%u:%u", &low, &high) == 1)
3024 uids = g_slist_prepend(uids, GINT_TO_POINTER(low));
3027 for (i = low; i <= high; i++)
3028 uids = g_slist_prepend(uids, GINT_TO_POINTER(i));
3031 uids = g_slist_reverse(uids);
3037 static gint imap_cmd_copy(IMAPSession *session, const gchar *seq_set,
3038 const gchar *destfolder, GRelation *uid_mapping)
3043 g_return_val_if_fail(session != NULL, IMAP_ERROR);
3044 g_return_val_if_fail(seq_set != NULL, IMAP_ERROR);
3045 g_return_val_if_fail(destfolder != NULL, IMAP_ERROR);
3047 QUOTE_IF_REQUIRED(destfolder_, destfolder);
3048 imap_gen_send(session, "UID COPY %s %s", seq_set, destfolder_);
3050 if (uid_mapping != NULL && session->uidplus) {
3052 gchar *resp_str = NULL, *olduids_str, *newuids_str;
3053 MsgNumberList *olduids, *old_cur, *newuids, *new_cur;
3055 reply = g_ptr_array_new();
3056 ok = imap_cmd_ok(session, reply);
3057 if ((ok == IMAP_SUCCESS) && (reply->len > 0)) {
3058 resp_str = g_ptr_array_index(reply, reply->len - 1);
3060 olduids_str = g_new0(gchar, strlen(resp_str));
3061 newuids_str = g_new0(gchar, strlen(resp_str));
3062 if (sscanf(resp_str, "%*s OK [COPYUID %*u %[0-9,:] %[0-9,:]]",
3063 olduids_str, newuids_str) == 2) {
3064 olduids = imapset_to_numlist(olduids_str);
3065 newuids = imapset_to_numlist(newuids_str);
3069 while(old_cur != NULL && new_cur != NULL) {
3070 g_relation_insert(uid_mapping,
3071 GPOINTER_TO_INT(old_cur->data),
3072 GPOINTER_TO_INT(new_cur->data));
3073 old_cur = g_slist_next(old_cur);
3074 new_cur = g_slist_next(new_cur);
3077 g_slist_free(olduids);
3078 g_slist_free(newuids);
3080 g_free(olduids_str);
3081 g_free(newuids_str);
3084 ptr_array_free_strings(reply);
3085 g_ptr_array_free(reply, TRUE);
3087 ok = imap_cmd_ok(session, NULL);
3089 if (ok != IMAP_SUCCESS)
3090 log_warning(_("can't copy %s to %s\n"), seq_set, destfolder_);
3095 gint imap_cmd_envelope(IMAPSession *session, IMAPSet set)
3097 static GString *header_fields = NULL;
3099 if (header_fields == NULL) {
3100 const HeaderEntry *headers, *elem;
3102 headers = procheader_get_headernames(FALSE);
3103 header_fields = g_string_new("");
3105 for (elem = headers; elem->name != NULL; ++elem) {
3106 gint namelen = strlen(elem->name);
3108 /* Header fields ending with space are not rfc822 headers */
3109 if (elem->name[namelen - 1] == ' ')
3112 /* strip : at the of header field */
3113 if(elem->name[namelen - 1] == ':')
3119 g_string_sprintfa(header_fields, "%s%.*s",
3120 header_fields->str[0] != '\0' ? " " : "",
3121 namelen, elem->name);
3126 (session, "UID FETCH %s (UID FLAGS RFC822.SIZE BODY.PEEK[HEADER.FIELDS (%s)])",
3127 set, header_fields->str);
3129 return IMAP_SUCCESS;
3132 static gint imap_cmd_store(IMAPSession *session, IMAPSet seq_set,
3137 imap_gen_send(session, "UID STORE %s %s", seq_set, sub_cmd);
3139 if ((ok = imap_cmd_ok(session, NULL)) != IMAP_SUCCESS) {
3140 log_warning(_("error while imap command: STORE %s %s\n"),
3145 return IMAP_SUCCESS;
3148 static gint imap_cmd_expunge(IMAPSession *session, IMAPSet seq_set)
3152 if (seq_set && session->uidplus)
3153 imap_gen_send(session, "UID EXPUNGE %s", seq_set);
3155 imap_gen_send(session, "EXPUNGE");
3156 if ((ok = imap_cmd_ok(session, NULL)) != IMAP_SUCCESS) {
3157 log_warning(_("error while imap command: EXPUNGE\n"));
3161 return IMAP_SUCCESS;
3164 static gint imap_cmd_close(IMAPSession *session)
3168 imap_gen_send(session, "CLOSE");
3169 if ((ok = imap_cmd_ok(session, NULL)) != IMAP_SUCCESS)
3170 log_warning(_("error while imap command: CLOSE\n"));
3175 static gint imap_cmd_ok(IMAPSession *session, GPtrArray *argbuf)
3177 gint ok = IMAP_SUCCESS;
3182 while ((ok = imap_gen_recv(session, &buf))
3184 // make sure data is long enough for any substring of buf
3185 data = alloca(strlen(buf) + 1);
3187 // untagged line read
3188 if (buf[0] == '*' && buf[1] == ' ') {
3191 g_ptr_array_add(argbuf, g_strdup(buf + 2));
3193 if (sscanf(buf + 2, "%d %s", &num, data) >= 2) {
3194 if (!strcmp(data, "EXISTS")) {
3195 session->exists = num;
3196 session->folder_content_changed = TRUE;
3199 if(!strcmp(data, "EXPUNGE")) {
3201 session->folder_content_changed = TRUE;
3204 // tagged line with correct tag and OK response found
3205 } else if ((sscanf(buf, "%d %s", &cmd_num, data) >= 2) &&
3206 (cmd_num == session->cmd_count) &&
3207 !strcmp(data, "OK")) {
3209 g_ptr_array_add(argbuf, g_strdup(buf));
3223 static void imap_gen_send(IMAPSession *session, const gchar *format, ...)
3230 va_start(args, format);
3231 tmp = g_strdup_vprintf(format, args);
3234 session->cmd_count++;
3236 buf = g_strdup_printf("%d %s\r\n", session->cmd_count, tmp);
3237 if (!strncasecmp(tmp, "LOGIN ", 6) && (p = strchr(tmp + 6, ' '))) {
3239 log_print("IMAP4> %d %s ********\n", session->cmd_count, tmp);
3241 log_print("IMAP4> %d %s\n", session->cmd_count, tmp);
3243 sock_write_all(SESSION(session)->sock, buf, strlen(buf));
3248 static gint imap_gen_recv(IMAPSession *session, gchar **ret)
3250 if ((*ret = sock_getline(SESSION(session)->sock)) == NULL)
3255 log_print("IMAP4< %s\n", *ret);
3257 return IMAP_SUCCESS;
3261 /* misc utility functions */
3263 static gchar *strchr_cpy(const gchar *src, gchar ch, gchar *dest, gint len)
3268 tmp = strchr(src, ch);
3272 memcpy(dest, src, MIN(tmp - src, len - 1));
3273 dest[MIN(tmp - src, len - 1)] = '\0';
3278 static gchar *get_quoted(const gchar *src, gchar ch, gchar *dest, gint len)
3280 const gchar *p = src;
3283 g_return_val_if_fail(*p == ch, NULL);
3288 while (*p != '\0' && *p != ch) {
3290 if (*p == '\\' && *(p + 1) != '\0')
3299 return (gchar *)(*p == ch ? p + 1 : p);
3302 static gchar *search_array_contain_str(GPtrArray *array, const gchar *str)
3306 for (i = 0; i < array->len; i++) {
3309 tmp = g_ptr_array_index(array, i);
3310 if (strstr(tmp, str) != NULL)
3317 static gchar *search_array_str(GPtrArray *array, const gchar *str)
3324 for (i = 0; i < array->len; i++) {
3327 tmp = g_ptr_array_index(array, i);
3328 if (!strncmp(tmp, str, len))
3335 static void imap_path_separator_subst(gchar *str, gchar separator)
3338 gboolean in_escape = FALSE;
3340 if (!separator || separator == '/') return;
3342 for (p = str; *p != '\0'; p++) {
3343 if (*p == '/' && !in_escape)
3345 else if (*p == '&' && *(p + 1) != '-' && !in_escape)
3347 else if (*p == '-' && in_escape)
3352 static gchar *imap_modified_utf7_to_utf8(const gchar *mutf7_str)
3355 const gchar *from_p;
3358 to = g_malloc(strlen(mutf7_str) + 1);
3361 for (from_p = mutf7_str; *from_p != '\0'; from_p++) {
3362 if (*from_p == '&' && *(from_p + 1) == '-') {
3372 static iconv_t cd = (iconv_t)-1;
3373 static gboolean iconv_ok = TRUE;
3376 size_t norm_utf7_len;
3378 gchar *to_str, *to_p;
3380 gboolean in_escape = FALSE;
3382 if (!iconv_ok) return g_strdup(mutf7_str);
3384 if (cd == (iconv_t)-1) {
3385 cd = iconv_open(conv_get_current_charset_str(), CS_UTF_8);
3386 if (cd == (iconv_t)-1) {
3387 g_warning(_("iconv cannot convert UTF-7 to UTF-8\n"));
3389 return g_strdup(mutf7_str);
3393 norm_utf7 = g_string_new(NULL);
3395 for (p = mutf7_str; *p != '\0'; p++) {
3396 /* replace: '&' -> '+',
3398 escaped ',' -> '/' */
3399 if (!in_escape && *p == '&') {
3400 if (*(p + 1) != '-') {
3401 g_string_append_c(norm_utf7, '+');
3404 g_string_append_c(norm_utf7, '&');
3407 } else if (in_escape && *p == ',') {
3408 g_string_append_c(norm_utf7, '/');
3409 } else if (in_escape && *p == '-') {
3410 g_string_append_c(norm_utf7, '-');
3413 g_string_append_c(norm_utf7, *p);
3417 norm_utf7_p = norm_utf7->str;
3418 norm_utf7_len = norm_utf7->len;
3419 to_len = strlen(mutf7_str) * 5;
3420 to_p = to_str = g_malloc(to_len + 1);
3422 if (iconv(cd, (ICONV_CONST gchar **)&norm_utf7_p, &norm_utf7_len,
3423 &to_p, &to_len) == -1) {
3424 g_warning(_("iconv cannot convert UTF-7 to UTF-8\n"));
3425 g_string_free(norm_utf7, TRUE);
3427 return g_strdup(mutf7_str);
3430 /* second iconv() call for flushing */
3431 iconv(cd, NULL, NULL, &to_p, &to_len);
3432 g_string_free(norm_utf7, TRUE);
3436 #endif /* !HAVE_ICONV */
3439 static gchar *imap_utf8_to_modified_utf7(const gchar *from)
3442 const gchar *from_p;
3445 to = g_malloc(strlen(from) * 2 + 1);
3448 for (from_p = from; *from_p != '\0'; from_p++) {
3449 if (*from_p == '&') {
3459 static iconv_t cd = (iconv_t)-1;
3460 static gboolean iconv_ok = TRUE;
3461 gchar *norm_utf7, *norm_utf7_p;
3462 size_t from_len, norm_utf7_len;
3464 gchar *from_tmp, *to, *p;
3465 gboolean in_escape = FALSE;
3467 if (!iconv_ok) return g_strdup(from);
3469 if (cd == (iconv_t)-1) {
3470 cd = iconv_open("UTF-7", CS_UTF_8);
3471 if (cd == (iconv_t)-1) {
3472 g_warning("iconv cannot convert UTF-8 to UTF-7\n");
3474 return g_strdup(from);
3478 Xstrdup_a(from_tmp, from, return g_strdup(from));
3479 from_len = strlen(from);
3480 norm_utf7_len = from_len * 5;
3481 Xalloca(norm_utf7, norm_utf7_len + 1, return g_strdup(from));
3482 norm_utf7_p = norm_utf7;
3484 #define IS_PRINT(ch) (isprint(ch) && isascii(ch))
3486 while (from_len > 0) {
3487 if (*from_tmp == '+') {
3488 *norm_utf7_p++ = '+';
3489 *norm_utf7_p++ = '-';
3493 } else if (IS_PRINT(*(guchar *)from_tmp)) {
3494 /* printable ascii char */
3495 *norm_utf7_p = *from_tmp;
3501 size_t mb_len = 0, conv_len = 0;
3503 /* unprintable char: convert to UTF-7 */
3505 while (!IS_PRINT(*(guchar *)p) && conv_len < from_len) {
3506 mb_len = mblen(p, MB_LEN_MAX);
3508 g_warning("wrong multibyte sequence\n");
3509 return g_strdup(from);
3515 from_len -= conv_len;
3516 if (iconv(cd, (ICONV_CONST gchar **)&from_tmp,
3518 &norm_utf7_p, &norm_utf7_len) == -1) {
3519 g_warning(_("iconv cannot convert UTF-8 to UTF-7\n"));
3520 return g_strdup(from);
3523 /* second iconv() call for flushing */
3524 iconv(cd, NULL, NULL, &norm_utf7_p, &norm_utf7_len);
3530 *norm_utf7_p = '\0';
3531 to_str = g_string_new(NULL);
3532 for (p = norm_utf7; p < norm_utf7_p; p++) {
3533 /* replace: '&' -> "&-",
3536 BASE64 '/' -> ',' */
3537 if (!in_escape && *p == '&') {
3538 g_string_append(to_str, "&-");
3539 } else if (!in_escape && *p == '+') {
3540 if (*(p + 1) == '-') {
3541 g_string_append_c(to_str, '+');
3544 g_string_append_c(to_str, '&');
3547 } else if (in_escape && *p == '/') {
3548 g_string_append_c(to_str, ',');
3549 } else if (in_escape && *p == '-') {
3550 g_string_append_c(to_str, '-');
3553 g_string_append_c(to_str, *p);
3559 g_string_append_c(to_str, '-');
3563 g_string_free(to_str, FALSE);
3566 #endif /* !HAVE_ICONV */
3569 static GSList *imap_get_seq_set_from_numlist(MsgNumberList *numlist)
3572 GSList *sorted_list, *cur;
3573 guint first, last, next;
3575 GSList *ret_list = NULL;
3577 if (numlist == NULL)
3580 str = g_string_sized_new(256);
3582 sorted_list = g_slist_copy(numlist);
3583 sorted_list = g_slist_sort(sorted_list, g_int_compare);
3585 first = GPOINTER_TO_INT(sorted_list->data);
3587 for (cur = sorted_list; cur != NULL; cur = g_slist_next(cur)) {
3588 last = GPOINTER_TO_INT(cur->data);
3590 next = GPOINTER_TO_INT(cur->next->data);
3594 if (last + 1 != next || next == 0) {
3596 g_string_append_c(str, ',');
3598 g_string_sprintfa(str, "%u", first);
3600 g_string_sprintfa(str, "%u:%u", first, last);
3604 if (str->len > IMAP_CMD_LIMIT) {
3605 ret_str = g_strdup(str->str);
3606 ret_list = g_slist_append(ret_list, ret_str);
3607 g_string_truncate(str, 0);
3613 ret_str = g_strdup(str->str);
3614 ret_list = g_slist_append(ret_list, ret_str);
3617 g_slist_free(sorted_list);
3618 g_string_free(str, TRUE);
3623 static GSList *imap_get_seq_set_from_msglist(MsgInfoList *msglist)
3625 MsgNumberList *numlist = NULL;
3629 for (cur = msglist; cur != NULL; cur = g_slist_next(cur)) {
3630 MsgInfo *msginfo = (MsgInfo *) cur->data;
3632 numlist = g_slist_append(numlist, GINT_TO_POINTER(msginfo->msgnum));
3634 seq_list = imap_get_seq_set_from_numlist(numlist);
3635 g_slist_free(numlist);
3640 static void imap_seq_set_free(GSList *seq_list)
3642 slist_free_strings(seq_list);
3643 g_slist_free(seq_list);
3647 static gboolean imap_rename_folder_func(GNode *node, gpointer data)
3649 FolderItem *item = node->data;
3650 gchar **paths = data;
3651 const gchar *oldpath = paths[0];
3652 const gchar *newpath = paths[1];
3654 gchar *new_itempath;
3657 oldpathlen = strlen(oldpath);
3658 if (strncmp(oldpath, item->path, oldpathlen) != 0) {
3659 g_warning("path doesn't match: %s, %s\n", oldpath, item->path);
3663 base = item->path + oldpathlen;
3664 while (*base == G_DIR_SEPARATOR) base++;
3666 new_itempath = g_strdup(newpath);
3668 new_itempath = g_strconcat(newpath, G_DIR_SEPARATOR_S, base,
3671 item->path = new_itempath;
3676 static gint get_list_of_uids(Folder *folder, IMAPFolderItem *item, GSList **msgnum_list)
3678 gint ok, nummsgs = 0, lastuid_old;
3679 IMAPSession *session;
3680 GSList *uidlist, *elem;
3683 session = imap_session_get(folder);
3684 g_return_val_if_fail(session != NULL, -1);
3686 ok = imap_select(session, IMAP_FOLDER(folder), item->item.path,
3687 NULL, NULL, NULL, NULL);
3688 if (ok != IMAP_SUCCESS)
3691 cmd_buf = g_strdup_printf("UID %d:*", item->lastuid + 1);
3692 ok = imap_cmd_search(session, cmd_buf, &uidlist);
3695 if (ok == IMAP_SOCKET) {
3696 session_destroy((Session *)session);
3697 ((RemoteFolder *)folder)->session = NULL;
3701 if (ok != IMAP_SUCCESS) {
3705 argbuf = g_ptr_array_new();
3707 cmd_buf = g_strdup_printf("UID FETCH %d:* (UID)", item->lastuid + 1);
3708 imap_gen_send(session, cmd_buf);
3710 ok = imap_cmd_ok(session, argbuf);
3711 if (ok != IMAP_SUCCESS) {
3712 ptr_array_free_strings(argbuf);
3713 g_ptr_array_free(argbuf, TRUE);
3717 for(i = 0; i < argbuf->len; i++) {
3720 if((ret = sscanf(g_ptr_array_index(argbuf, i),
3721 "%*d FETCH (UID %d)", &msgnum)) == 1)
3722 uidlist = g_slist_prepend(uidlist, GINT_TO_POINTER(msgnum));
3724 ptr_array_free_strings(argbuf);
3725 g_ptr_array_free(argbuf, TRUE);
3728 lastuid_old = item->lastuid;
3729 *msgnum_list = g_slist_copy(item->uid_list);
3730 nummsgs = g_slist_length(*msgnum_list);
3731 debug_print("Got %d uids from cache\n", g_slist_length(item->uid_list));
3733 for (elem = uidlist; elem != NULL; elem = g_slist_next(elem)) {
3736 msgnum = GPOINTER_TO_INT(elem->data);
3737 if (msgnum > lastuid_old) {
3738 *msgnum_list = g_slist_prepend(*msgnum_list, GINT_TO_POINTER(msgnum));
3739 item->uid_list = g_slist_prepend(item->uid_list, GINT_TO_POINTER(msgnum));
3742 if(msgnum > item->lastuid)
3743 item->lastuid = msgnum;
3746 g_slist_free(uidlist);
3751 gint imap_get_num_list(Folder *folder, FolderItem *_item, GSList **msgnum_list, gboolean *old_uids_valid)
3753 IMAPFolderItem *item = (IMAPFolderItem *)_item;
3754 IMAPSession *session;
3755 gint ok, nummsgs = 0, exists, recent, uid_val, uid_next, unseen;
3758 gboolean selected_folder;
3760 g_return_val_if_fail(folder != NULL, -1);
3761 g_return_val_if_fail(item != NULL, -1);
3762 g_return_val_if_fail(item->item.path != NULL, -1);
3763 g_return_val_if_fail(FOLDER_CLASS(folder) == &imap_class, -1);
3764 g_return_val_if_fail(folder->account != NULL, -1);
3766 session = imap_session_get(folder);
3767 g_return_val_if_fail(session != NULL, -1);
3769 selected_folder = (session->mbox != NULL) &&
3770 (!strcmp(session->mbox, item->item.path));
3771 if (selected_folder) {
3772 ok = imap_cmd_noop(session);
3773 if (ok != IMAP_SUCCESS)
3775 exists = session->exists;
3777 *old_uids_valid = TRUE;
3779 ok = imap_status(session, IMAP_FOLDER(folder), item->item.path,
3780 &exists, &recent, &uid_next, &uid_val, &unseen);
3781 if (ok != IMAP_SUCCESS)
3784 if(item->item.mtime == uid_val)
3785 *old_uids_valid = TRUE;
3787 *old_uids_valid = FALSE;
3789 debug_print("Freeing imap uid cache\n");
3791 g_slist_free(item->uid_list);
3792 item->uid_list = NULL;
3794 item->item.mtime = uid_val;
3796 imap_delete_all_cached_messages((FolderItem *)item);
3800 if (!selected_folder)
3801 item->uid_next = uid_next;
3803 /* If old uid_next matches new uid_next we can be sure no message
3804 was added to the folder */
3805 if (( selected_folder && !session->folder_content_changed) ||
3806 (!selected_folder && uid_next == item->uid_next)) {
3807 nummsgs = g_slist_length(item->uid_list);
3809 /* If number of messages is still the same we
3810 know our caches message numbers are still valid,
3811 otherwise if the number of messages has decrease
3812 we discard our cache to start a new scan to find
3813 out which numbers have been removed */
3814 if (exists == nummsgs) {
3815 *msgnum_list = g_slist_copy(item->uid_list);
3817 } else if (exists < nummsgs) {
3818 debug_print("Freeing imap uid cache");
3820 g_slist_free(item->uid_list);
3821 item->uid_list = NULL;
3826 *msgnum_list = NULL;
3830 nummsgs = get_list_of_uids(folder, item, &uidlist);
3832 if (nummsgs != exists) {
3833 /* Cache contains more messages then folder, we have cached
3834 an old UID of a message that was removed and new messages
3835 have been added too, otherwise the uid_next check would
3837 debug_print("Freeing imap uid cache");
3839 g_slist_free(item->uid_list);
3840 item->uid_list = NULL;
3842 g_slist_free(*msgnum_list);
3844 nummsgs = get_list_of_uids(folder, item, &uidlist);
3847 *msgnum_list = uidlist;
3849 dir = folder_item_get_path((FolderItem *)item);
3850 debug_print("removing old messages from %s\n", dir);
3851 remove_numbered_files_not_in_list(dir, *msgnum_list);
3857 static MsgInfo *imap_parse_msg(const gchar *file, FolderItem *item)
3862 flags.perm_flags = MSG_NEW|MSG_UNREAD;
3863 flags.tmp_flags = 0;
3865 g_return_val_if_fail(item != NULL, NULL);
3866 g_return_val_if_fail(file != NULL, NULL);
3868 if (item->stype == F_QUEUE) {
3869 MSG_SET_TMP_FLAGS(flags, MSG_QUEUED);
3870 } else if (item->stype == F_DRAFT) {
3871 MSG_SET_TMP_FLAGS(flags, MSG_DRAFT);
3874 msginfo = procheader_parse_file(file, flags, FALSE, FALSE);
3875 if (!msginfo) return NULL;
3877 msginfo->folder = item;
3882 GSList *imap_get_msginfos(Folder *folder, FolderItem *item, GSList *msgnum_list)
3884 IMAPSession *session;
3885 MsgInfoList *ret = NULL;
3888 g_return_val_if_fail(folder != NULL, NULL);
3889 g_return_val_if_fail(item != NULL, NULL);
3890 g_return_val_if_fail(msgnum_list != NULL, NULL);
3892 session = imap_session_get(folder);
3893 g_return_val_if_fail(session != NULL, NULL);
3895 ok = imap_select(session, IMAP_FOLDER(folder), item->path,
3896 NULL, NULL, NULL, NULL);
3897 if (ok != IMAP_SUCCESS)
3900 if (!(item->stype == F_QUEUE || item->stype == F_DRAFT)) {
3901 ret = g_slist_concat(ret,
3902 imap_get_uncached_messages(
3903 session, item, msgnum_list));
3905 MsgNumberList *sorted_list, *elem;
3906 gint startnum, lastnum;
3908 sorted_list = g_slist_sort(g_slist_copy(msgnum_list), g_int_compare);
3910 startnum = lastnum = GPOINTER_TO_INT(sorted_list->data);
3912 for (elem = sorted_list;; elem = g_slist_next(elem)) {
3916 num = GPOINTER_TO_INT(elem->data);
3918 if (num > lastnum + 1 || elem == NULL) {
3920 for (i = startnum; i <= lastnum; ++i) {
3923 file = imap_fetch_msg(folder, item, i);
3925 MsgInfo *msginfo = imap_parse_msg(file, item);
3926 if (msginfo != NULL) {
3927 msginfo->msgnum = i;
3928 ret = g_slist_append(ret, msginfo);
3942 g_slist_free(sorted_list);
3948 MsgInfo *imap_get_msginfo(Folder *folder, FolderItem *item, gint uid)
3950 MsgInfo *msginfo = NULL;
3951 MsgInfoList *msginfolist;
3952 MsgNumberList numlist;
3954 numlist.next = NULL;
3955 numlist.data = GINT_TO_POINTER(uid);
3957 msginfolist = imap_get_msginfos(folder, item, &numlist);
3958 if (msginfolist != NULL) {
3959 msginfo = msginfolist->data;
3960 g_slist_free(msginfolist);
3966 gboolean imap_scan_required(Folder *folder, FolderItem *_item)
3968 IMAPSession *session;
3969 IMAPFolderItem *item = (IMAPFolderItem *)_item;
3970 gint ok, exists = 0, recent = 0, unseen = 0;
3971 guint32 uid_next, uid_val = 0;
3972 gboolean selected_folder;
3974 g_return_val_if_fail(folder != NULL, FALSE);
3975 g_return_val_if_fail(item != NULL, FALSE);
3976 g_return_val_if_fail(item->item.folder != NULL, FALSE);
3977 g_return_val_if_fail(FOLDER_CLASS(item->item.folder) == &imap_class, FALSE);
3979 if (item->item.path == NULL)
3982 session = imap_session_get(folder);
3983 g_return_val_if_fail(session != NULL, FALSE);
3985 selected_folder = (session->mbox != NULL) &&
3986 (!strcmp(session->mbox, item->item.path));
3987 if (selected_folder) {
3988 ok = imap_cmd_noop(session);
3989 if (ok != IMAP_SUCCESS)
3992 if (session->folder_content_changed)
3995 ok = imap_status(session, IMAP_FOLDER(folder), item->item.path,
3996 &exists, &recent, &uid_next, &uid_val, &unseen);
3997 if (ok != IMAP_SUCCESS)
4000 if ((uid_next != item->uid_next) || (exists < item->item.total_msgs))
4007 void imap_change_flags(Folder *folder, FolderItem *item, MsgInfo *msginfo, MsgPermFlags newflags)
4009 IMAPSession *session;
4010 IMAPFlags flags_set = 0, flags_unset = 0;
4011 gint ok = IMAP_SUCCESS;
4012 MsgNumberList numlist;
4014 g_return_if_fail(folder != NULL);
4015 g_return_if_fail(folder->klass == &imap_class);
4016 g_return_if_fail(item != NULL);
4017 g_return_if_fail(item->folder == folder);
4018 g_return_if_fail(msginfo != NULL);
4019 g_return_if_fail(msginfo->folder == item);
4021 session = imap_session_get(folder);
4022 if (!session) return;
4024 if ((ok = imap_select(session, IMAP_FOLDER(folder), msginfo->folder->path,
4025 NULL, NULL, NULL, NULL)) != IMAP_SUCCESS)
4028 if (!MSG_IS_MARKED(msginfo->flags) && (newflags & MSG_MARKED))
4029 flags_set |= IMAP_FLAG_FLAGGED;
4030 if ( MSG_IS_MARKED(msginfo->flags) && !(newflags & MSG_MARKED))
4031 flags_unset |= IMAP_FLAG_FLAGGED;
4033 if (!MSG_IS_UNREAD(msginfo->flags) && (newflags & MSG_UNREAD))
4034 flags_unset |= IMAP_FLAG_SEEN;
4035 if ( MSG_IS_UNREAD(msginfo->flags) && !(newflags & MSG_UNREAD))
4036 flags_set |= IMAP_FLAG_SEEN;
4038 if (!MSG_IS_REPLIED(msginfo->flags) && (newflags & MSG_REPLIED))
4039 flags_set |= IMAP_FLAG_ANSWERED;
4040 if ( MSG_IS_REPLIED(msginfo->flags) && !(newflags & MSG_REPLIED))
4041 flags_set |= IMAP_FLAG_ANSWERED;
4043 numlist.next = NULL;
4044 numlist.data = GINT_TO_POINTER(msginfo->msgnum);
4047 ok = imap_set_message_flags(session, &numlist, flags_set, TRUE);
4048 if (ok != IMAP_SUCCESS) return;
4052 ok = imap_set_message_flags(session, &numlist, flags_unset, FALSE);
4053 if (ok != IMAP_SUCCESS) return;
4056 msginfo->flags.perm_flags = newflags;