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 "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_modified_utf7_to_locale (const gchar *mutf7_str);
415 static gchar *imap_locale_to_modified_utf7 (const gchar *from);
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 imap_seq_set_free(seq_list);
1028 if (ok == IMAP_SUCCESS)
1034 static gint imap_copy_msg(Folder *folder, FolderItem *dest, MsgInfo *msginfo)
1038 g_return_val_if_fail(msginfo != NULL, -1);
1040 msglist.data = msginfo;
1041 msglist.next = NULL;
1043 return imap_copy_msgs(folder, dest, &msglist, NULL);
1046 static gint imap_copy_msgs(Folder *folder, FolderItem *dest,
1047 MsgInfoList *msglist, GRelation *relation)
1053 g_return_val_if_fail(folder != NULL, -1);
1054 g_return_val_if_fail(dest != NULL, -1);
1055 g_return_val_if_fail(msglist != NULL, -1);
1057 msginfo = (MsgInfo *)msglist->data;
1058 g_return_val_if_fail(msginfo->folder != NULL, -1);
1060 if (folder == msginfo->folder->folder)
1061 return imap_do_copy_msgs(folder, dest, msglist, relation);
1063 file_list = procmsg_get_message_file_list(msglist);
1064 g_return_val_if_fail(file_list != NULL, -1);
1066 ret = imap_add_msgs(folder, dest, file_list, relation);
1068 procmsg_message_file_list_free(file_list);
1073 static gint imap_remove_msg(Folder *folder, FolderItem *item, gint uid)
1076 IMAPSession *session;
1078 MsgNumberList numlist;
1080 g_return_val_if_fail(folder != NULL, -1);
1081 g_return_val_if_fail(FOLDER_CLASS(folder) == &imap_class, -1);
1082 g_return_val_if_fail(item != NULL, -1);
1084 session = imap_session_get(folder);
1085 if (!session) return -1;
1087 ok = imap_select(session, IMAP_FOLDER(folder), item->path,
1088 NULL, NULL, NULL, NULL);
1089 if (ok != IMAP_SUCCESS)
1092 numlist.next = NULL;
1093 numlist.data = GINT_TO_POINTER(uid);
1095 ok = imap_set_message_flags
1096 (IMAP_SESSION(REMOTE_FOLDER(folder)->session),
1097 &numlist, IMAP_FLAG_DELETED, TRUE);
1098 if (ok != IMAP_SUCCESS) {
1099 log_warning(_("can't set deleted flags: %d\n"), uid);
1103 if (!session->uidplus) {
1104 ok = imap_cmd_expunge(session, NULL);
1108 uidstr = g_strdup_printf("%u", uid);
1109 ok = imap_cmd_expunge(session, uidstr);
1112 if (ok != IMAP_SUCCESS) {
1113 log_warning(_("can't expunge\n"));
1117 IMAP_FOLDER_ITEM(item)->uid_list = g_slist_remove(
1118 IMAP_FOLDER_ITEM(item)->uid_list, numlist.data);
1119 dir = folder_item_get_path(item);
1120 if (is_dir_exist(dir))
1121 remove_numbered_files(dir, uid, uid);
1124 return IMAP_SUCCESS;
1127 static gint imap_remove_all_msg(Folder *folder, FolderItem *item)
1130 IMAPSession *session;
1133 g_return_val_if_fail(folder != NULL, -1);
1134 g_return_val_if_fail(item != NULL, -1);
1136 session = imap_session_get(folder);
1137 if (!session) return -1;
1139 ok = imap_select(session, IMAP_FOLDER(folder), item->path,
1140 NULL, NULL, NULL, NULL);
1141 if (ok != IMAP_SUCCESS)
1144 imap_gen_send(session, "STORE 1:* +FLAGS.SILENT (\\Deleted)");
1145 ok = imap_cmd_ok(session, NULL);
1146 if (ok != IMAP_SUCCESS) {
1147 log_warning(_("can't set deleted flags: 1:*\n"));
1151 ok = imap_cmd_expunge(session, NULL);
1152 if (ok != IMAP_SUCCESS) {
1153 log_warning(_("can't expunge\n"));
1157 dir = folder_item_get_path(item);
1158 if (is_dir_exist(dir))
1159 remove_all_numbered_files(dir);
1162 return IMAP_SUCCESS;
1165 static gboolean imap_is_msg_changed(Folder *folder, FolderItem *item,
1168 /* TODO: properly implement this method */
1172 static gint imap_close(Folder *folder, FolderItem *item)
1175 IMAPSession *session;
1177 g_return_val_if_fail(folder != NULL, -1);
1178 g_return_val_if_fail(item != NULL, -1);
1179 g_return_val_if_fail(item->path != NULL, -1);
1181 session = imap_session_get(folder);
1182 if (!session) return -1;
1184 if (session->mbox) {
1185 if (strcmp2(session->mbox, item->path) != 0) return -1;
1187 ok = imap_cmd_close(session);
1188 if (ok != IMAP_SUCCESS)
1189 log_warning(_("can't close folder\n"));
1191 g_free(session->mbox);
1192 session->mbox = NULL;
1200 static gint imap_scan_tree(Folder *folder)
1202 FolderItem *item = NULL;
1203 IMAPSession *session;
1204 gchar *root_folder = NULL;
1206 g_return_val_if_fail(folder != NULL, -1);
1207 g_return_val_if_fail(folder->account != NULL, -1);
1209 session = imap_session_get(folder);
1211 if (!folder->node) {
1212 folder_tree_destroy(folder);
1213 item = folder_item_new(folder, folder->name, NULL);
1214 item->folder = folder;
1215 folder->node = item->node = g_node_new(item);
1220 if (folder->account->imap_dir && *folder->account->imap_dir) {
1225 Xstrdup_a(root_folder, folder->account->imap_dir, return -1);
1226 extract_quote(root_folder, '"');
1227 subst_char(root_folder,
1228 imap_get_path_separator(IMAP_FOLDER(folder),
1231 strtailchomp(root_folder, '/');
1232 real_path = imap_get_real_path
1233 (IMAP_FOLDER(folder), root_folder);
1234 debug_print("IMAP root directory: %s\n", real_path);
1236 /* check if root directory exist */
1237 argbuf = g_ptr_array_new();
1238 ok = imap_cmd_list(session, NULL, real_path, argbuf);
1239 if (ok != IMAP_SUCCESS ||
1240 search_array_str(argbuf, "LIST ") == NULL) {
1241 log_warning(_("root folder %s does not exist\n"), real_path);
1242 g_ptr_array_free(argbuf, TRUE);
1246 g_ptr_array_free(argbuf, TRUE);
1251 item = FOLDER_ITEM(folder->node->data);
1252 if (!item || ((item->path || root_folder) &&
1253 strcmp2(item->path, root_folder) != 0)) {
1254 folder_tree_destroy(folder);
1255 item = folder_item_new(folder, folder->name, root_folder);
1256 item->folder = folder;
1257 folder->node = item->node = g_node_new(item);
1260 imap_scan_tree_recursive(session, FOLDER_ITEM(folder->node->data));
1261 imap_create_missing_folders(folder);
1266 static gint imap_scan_tree_recursive(IMAPSession *session, FolderItem *item)
1269 IMAPFolder *imapfolder;
1270 FolderItem *new_item;
1271 GSList *item_list, *cur;
1274 gchar *wildcard_path;
1278 g_return_val_if_fail(item != NULL, -1);
1279 g_return_val_if_fail(item->folder != NULL, -1);
1280 g_return_val_if_fail(item->no_sub == FALSE, -1);
1282 folder = item->folder;
1283 imapfolder = IMAP_FOLDER(folder);
1285 separator = imap_get_path_separator(imapfolder, item->path);
1287 if (folder->ui_func)
1288 folder->ui_func(folder, item, folder->ui_func_data);
1291 wildcard[0] = separator;
1294 real_path = imap_get_real_path(imapfolder, item->path);
1298 real_path = g_strdup("");
1301 Xstrcat_a(wildcard_path, real_path, wildcard,
1302 {g_free(real_path); return IMAP_ERROR;});
1303 QUOTE_IF_REQUIRED(wildcard_path, wildcard_path);
1305 imap_gen_send(session, "LIST \"\" %s",
1308 strtailchomp(real_path, separator);
1309 item_list = imap_parse_list(imapfolder, session, real_path, NULL);
1312 node = item->node->children;
1313 while (node != NULL) {
1314 FolderItem *old_item = FOLDER_ITEM(node->data);
1315 GNode *next = node->next;
1318 for (cur = item_list; cur != NULL; cur = cur->next) {
1319 FolderItem *cur_item = FOLDER_ITEM(cur->data);
1320 if (!strcmp2(old_item->path, cur_item->path)) {
1321 new_item = cur_item;
1326 debug_print("folder '%s' not found. removing...\n",
1328 folder_item_remove(old_item);
1330 old_item->no_sub = new_item->no_sub;
1331 old_item->no_select = new_item->no_select;
1332 if (old_item->no_sub == TRUE && node->children) {
1333 debug_print("folder '%s' doesn't have "
1334 "subfolders. removing...\n",
1336 folder_item_remove_children(old_item);
1343 for (cur = item_list; cur != NULL; cur = cur->next) {
1344 FolderItem *cur_item = FOLDER_ITEM(cur->data);
1346 for (node = item->node->children; node != NULL;
1347 node = node->next) {
1348 if (!strcmp2(FOLDER_ITEM(node->data)->path,
1350 new_item = FOLDER_ITEM(node->data);
1351 folder_item_destroy(cur_item);
1357 new_item = cur_item;
1358 debug_print("new folder '%s' found.\n", new_item->path);
1359 folder_item_append(item, new_item);
1362 if (!strcmp(new_item->path, "INBOX")) {
1363 new_item->stype = F_INBOX;
1364 folder->inbox = new_item;
1365 } else if (!item->parent || item->stype == F_INBOX) {
1368 base = g_basename(new_item->path);
1370 if (!folder->outbox && !strcasecmp(base, "Sent")) {
1371 new_item->stype = F_OUTBOX;
1372 folder->outbox = new_item;
1373 } else if (!folder->draft && !strcasecmp(base, "Drafts")) {
1374 new_item->stype = F_DRAFT;
1375 folder->draft = new_item;
1376 } else if (!folder->queue && !strcasecmp(base, "Queue")) {
1377 new_item->stype = F_QUEUE;
1378 folder->queue = new_item;
1379 } else if (!folder->trash && !strcasecmp(base, "Trash")) {
1380 new_item->stype = F_TRASH;
1381 folder->trash = new_item;
1385 if (new_item->no_sub == FALSE)
1386 imap_scan_tree_recursive(session, new_item);
1389 g_slist_free(item_list);
1391 return IMAP_SUCCESS;
1394 static GSList *imap_parse_list(IMAPFolder *folder, IMAPSession *session,
1395 const gchar *real_path, gchar *separator)
1397 gchar buf[IMAPBUFSIZE];
1399 gchar separator_str[16];
1402 gchar *loc_name, *loc_path;
1403 GSList *item_list = NULL;
1405 FolderItem *new_item;
1407 debug_print("getting list of %s ...\n",
1408 *real_path ? real_path : "\"\"");
1410 str = g_string_new(NULL);
1413 if (sock_gets(SESSION(session)->sock, buf, sizeof(buf)) <= 0) {
1414 log_warning(_("error occurred while getting LIST.\n"));
1418 if (buf[0] != '*' || buf[1] != ' ') {
1419 log_print("IMAP4< %s\n", buf);
1420 if (sscanf(buf, "%*d %16s", buf) < 1 ||
1421 strcmp(buf, "OK") != 0)
1422 log_warning(_("error occurred while getting LIST.\n"));
1426 debug_print("IMAP4< %s\n", buf);
1428 g_string_assign(str, buf);
1430 if (strncmp(p, "LIST ", 5) != 0) continue;
1433 if (*p != '(') continue;
1435 p = strchr_cpy(p, ')', flags, sizeof(flags));
1437 while (*p == ' ') p++;
1439 p = strchr_cpy(p, ' ', separator_str, sizeof(separator_str));
1441 extract_quote(separator_str, '"');
1442 if (!strcmp(separator_str, "NIL"))
1443 separator_str[0] = '\0';
1445 *separator = separator_str[0];
1448 while (*p == ' ') p++;
1449 if (*p == '{' || *p == '"')
1450 p = imap_parse_atom(SESSION(session)->sock, p,
1451 buf, sizeof(buf), str);
1453 strncpy2(buf, p, sizeof(buf));
1454 strtailchomp(buf, separator_str[0]);
1455 if (buf[0] == '\0') continue;
1456 if (!strcmp(buf, real_path)) continue;
1458 if (separator_str[0] != '\0')
1459 subst_char(buf, separator_str[0], '/');
1460 name = g_basename(buf);
1461 if (name[0] == '.') continue;
1463 loc_name = imap_modified_utf7_to_locale(name);
1464 loc_path = imap_modified_utf7_to_locale(buf);
1465 new_item = folder_item_new(FOLDER(folder), loc_name, loc_path);
1466 if (strcasestr(flags, "\\Noinferiors") != NULL)
1467 new_item->no_sub = TRUE;
1468 if (strcmp(buf, "INBOX") != 0 &&
1469 strcasestr(flags, "\\Noselect") != NULL)
1470 new_item->no_select = TRUE;
1472 item_list = g_slist_append(item_list, new_item);
1474 debug_print("folder '%s' found.\n", loc_path);
1479 g_string_free(str, TRUE);
1484 static gint imap_create_tree(Folder *folder)
1486 g_return_val_if_fail(folder != NULL, -1);
1487 g_return_val_if_fail(folder->node != NULL, -1);
1488 g_return_val_if_fail(folder->node->data != NULL, -1);
1489 g_return_val_if_fail(folder->account != NULL, -1);
1491 imap_scan_tree(folder);
1492 imap_create_missing_folders(folder);
1497 static void imap_create_missing_folders(Folder *folder)
1499 g_return_if_fail(folder != NULL);
1502 folder->inbox = imap_create_special_folder
1503 (folder, F_INBOX, "INBOX");
1505 if (!folder->outbox)
1506 folder->outbox = imap_create_special_folder
1507 (folder, F_OUTBOX, "Sent");
1509 folder->draft = imap_create_special_folder
1510 (folder, F_DRAFT, "Drafts");
1512 folder->queue = imap_create_special_folder
1513 (folder, F_QUEUE, "Queue");
1516 folder->trash = imap_create_special_folder
1517 (folder, F_TRASH, "Trash");
1520 static FolderItem *imap_create_special_folder(Folder *folder,
1521 SpecialFolderItemType stype,
1525 FolderItem *new_item;
1527 g_return_val_if_fail(folder != NULL, NULL);
1528 g_return_val_if_fail(folder->node != NULL, NULL);
1529 g_return_val_if_fail(folder->node->data != NULL, NULL);
1530 g_return_val_if_fail(folder->account != NULL, NULL);
1531 g_return_val_if_fail(name != NULL, NULL);
1533 item = FOLDER_ITEM(folder->node->data);
1534 new_item = imap_create_folder(folder, item, name);
1537 g_warning("Can't create '%s'\n", name);
1538 if (!folder->inbox) return NULL;
1540 new_item = imap_create_folder(folder, folder->inbox, name);
1542 g_warning("Can't create '%s' under INBOX\n", name);
1544 new_item->stype = stype;
1546 new_item->stype = stype;
1551 static gchar *imap_folder_get_path(Folder *folder)
1555 g_return_val_if_fail(folder != NULL, NULL);
1556 g_return_val_if_fail(folder->account != NULL, NULL);
1558 folder_path = g_strconcat(get_imap_cache_dir(),
1560 folder->account->recv_server,
1562 folder->account->userid,
1568 static gchar *imap_item_get_path(Folder *folder, FolderItem *item)
1570 gchar *folder_path, *path;
1572 g_return_val_if_fail(folder != NULL, NULL);
1573 g_return_val_if_fail(item != NULL, NULL);
1574 folder_path = imap_folder_get_path(folder);
1576 g_return_val_if_fail(folder_path != NULL, NULL);
1577 if (folder_path[0] == G_DIR_SEPARATOR) {
1579 path = g_strconcat(folder_path, G_DIR_SEPARATOR_S,
1582 path = g_strdup(folder_path);
1585 path = g_strconcat(get_home_dir(), G_DIR_SEPARATOR_S,
1586 folder_path, G_DIR_SEPARATOR_S,
1589 path = g_strconcat(get_home_dir(), G_DIR_SEPARATOR_S,
1592 g_free(folder_path);
1597 static FolderItem *imap_create_folder(Folder *folder, FolderItem *parent,
1600 gchar *dirpath, *imap_path;
1601 IMAPSession *session;
1602 FolderItem *new_item;
1608 g_return_val_if_fail(folder != NULL, NULL);
1609 g_return_val_if_fail(folder->account != NULL, NULL);
1610 g_return_val_if_fail(parent != NULL, NULL);
1611 g_return_val_if_fail(name != NULL, NULL);
1613 session = imap_session_get(folder);
1614 if (!session) return NULL;
1616 if (!parent->parent && strcmp(name, "INBOX") == 0)
1617 dirpath = g_strdup(name);
1618 else if (parent->path)
1619 dirpath = g_strconcat(parent->path, "/", name, NULL);
1620 else if ((p = strchr(name, '/')) != NULL && *(p + 1) != '\0')
1621 dirpath = g_strdup(name);
1622 else if (folder->account->imap_dir && *folder->account->imap_dir) {
1625 Xstrdup_a(imap_dir, folder->account->imap_dir, return NULL);
1626 strtailchomp(imap_dir, '/');
1627 dirpath = g_strconcat(imap_dir, "/", name, NULL);
1629 dirpath = g_strdup(name);
1631 /* keep trailing directory separator to create a folder that contains
1633 imap_path = imap_locale_to_modified_utf7(dirpath);
1634 strtailchomp(dirpath, '/');
1635 Xstrdup_a(new_name, name, {g_free(dirpath); return NULL;});
1636 strtailchomp(new_name, '/');
1637 separator = imap_get_path_separator(IMAP_FOLDER(folder), imap_path);
1638 imap_path_separator_subst(imap_path, separator);
1639 subst_char(new_name, '/', separator);
1641 if (strcmp(name, "INBOX") != 0) {
1644 gboolean exist = FALSE;
1646 argbuf = g_ptr_array_new();
1647 ok = imap_cmd_list(session, NULL, imap_path,
1649 if (ok != IMAP_SUCCESS) {
1650 log_warning(_("can't create mailbox: LIST failed\n"));
1653 ptr_array_free_strings(argbuf);
1654 g_ptr_array_free(argbuf, TRUE);
1658 for (i = 0; i < argbuf->len; i++) {
1660 str = g_ptr_array_index(argbuf, i);
1661 if (!strncmp(str, "LIST ", 5)) {
1666 ptr_array_free_strings(argbuf);
1667 g_ptr_array_free(argbuf, TRUE);
1670 ok = imap_cmd_create(session, imap_path);
1671 if (ok != IMAP_SUCCESS) {
1672 log_warning(_("can't create mailbox\n"));
1680 new_item = folder_item_new(folder, new_name, dirpath);
1681 folder_item_append(parent, new_item);
1685 dirpath = folder_item_get_path(new_item);
1686 if (!is_dir_exist(dirpath))
1687 make_dir_hier(dirpath);
1693 static gint imap_rename_folder(Folder *folder, FolderItem *item,
1698 gchar *real_oldpath;
1699 gchar *real_newpath;
1701 gchar *old_cache_dir;
1702 gchar *new_cache_dir;
1703 IMAPSession *session;
1706 gint exists, recent, unseen;
1707 guint32 uid_validity;
1709 g_return_val_if_fail(folder != NULL, -1);
1710 g_return_val_if_fail(item != NULL, -1);
1711 g_return_val_if_fail(item->path != NULL, -1);
1712 g_return_val_if_fail(name != NULL, -1);
1714 session = imap_session_get(folder);
1715 if (!session) return -1;
1717 real_oldpath = imap_get_real_path(IMAP_FOLDER(folder), item->path);
1719 g_free(session->mbox);
1720 session->mbox = NULL;
1721 ok = imap_cmd_examine(session, "INBOX",
1722 &exists, &recent, &unseen, &uid_validity);
1723 if (ok != IMAP_SUCCESS) {
1724 g_free(real_oldpath);
1728 separator = imap_get_path_separator(IMAP_FOLDER(folder), item->path);
1729 if (strchr(item->path, G_DIR_SEPARATOR)) {
1730 dirpath = g_dirname(item->path);
1731 newpath = g_strconcat(dirpath, G_DIR_SEPARATOR_S, name, NULL);
1734 newpath = g_strdup(name);
1736 real_newpath = imap_locale_to_modified_utf7(newpath);
1737 imap_path_separator_subst(real_newpath, separator);
1739 ok = imap_cmd_rename(session, real_oldpath, real_newpath);
1740 if (ok != IMAP_SUCCESS) {
1741 log_warning(_("can't rename mailbox: %s to %s\n"),
1742 real_oldpath, real_newpath);
1743 g_free(real_oldpath);
1745 g_free(real_newpath);
1750 item->name = g_strdup(name);
1752 old_cache_dir = folder_item_get_path(item);
1754 paths[0] = g_strdup(item->path);
1756 g_node_traverse(item->node, G_PRE_ORDER, G_TRAVERSE_ALL, -1,
1757 imap_rename_folder_func, paths);
1759 if (is_dir_exist(old_cache_dir)) {
1760 new_cache_dir = folder_item_get_path(item);
1761 if (rename(old_cache_dir, new_cache_dir) < 0) {
1762 FILE_OP_ERROR(old_cache_dir, "rename");
1764 g_free(new_cache_dir);
1767 g_free(old_cache_dir);
1770 g_free(real_oldpath);
1771 g_free(real_newpath);
1776 static gint imap_remove_folder(Folder *folder, FolderItem *item)
1779 IMAPSession *session;
1782 gint exists, recent, unseen;
1783 guint32 uid_validity;
1785 g_return_val_if_fail(folder != NULL, -1);
1786 g_return_val_if_fail(item != NULL, -1);
1787 g_return_val_if_fail(item->path != NULL, -1);
1789 session = imap_session_get(folder);
1790 if (!session) return -1;
1792 path = imap_get_real_path(IMAP_FOLDER(folder), item->path);
1794 ok = imap_cmd_examine(session, "INBOX",
1795 &exists, &recent, &unseen, &uid_validity);
1796 if (ok != IMAP_SUCCESS) {
1801 ok = imap_cmd_delete(session, path);
1802 if (ok != IMAP_SUCCESS) {
1803 log_warning(_("can't delete mailbox\n"));
1809 cache_dir = folder_item_get_path(item);
1810 if (is_dir_exist(cache_dir) && remove_dir_recursive(cache_dir) < 0)
1811 g_warning("can't remove directory '%s'\n", cache_dir);
1813 folder_item_remove(item);
1818 static GSList *imap_get_uncached_messages(IMAPSession *session,
1820 MsgNumberList *numlist)
1823 GSList *newlist = NULL;
1824 GSList *llast = NULL;
1827 GSList *seq_list, *cur;
1830 g_return_val_if_fail(session != NULL, NULL);
1831 g_return_val_if_fail(item != NULL, NULL);
1832 g_return_val_if_fail(item->folder != NULL, NULL);
1833 g_return_val_if_fail(FOLDER_CLASS(item->folder) == &imap_class, NULL);
1835 seq_list = imap_get_seq_set_from_numlist(numlist);
1836 for (cur = seq_list; cur != NULL; cur = g_slist_next(cur)) {
1837 imapset = cur->data;
1839 if (imap_cmd_envelope(session, imapset)
1841 log_warning(_("can't get envelope\n"));
1845 str = g_string_new(NULL);
1848 if ((tmp = sock_getline(SESSION(session)->sock)) == NULL) {
1849 log_warning(_("error occurred while getting envelope.\n"));
1850 g_string_free(str, TRUE);
1854 if (tmp[0] != '*' || tmp[1] != ' ') {
1855 log_print("IMAP4< %s\n", tmp);
1859 if (strstr(tmp, "FETCH") == NULL) {
1860 log_print("IMAP4< %s\n", tmp);
1864 log_print("IMAP4< %s\n", tmp);
1865 g_string_assign(str, tmp);
1868 msginfo = imap_parse_envelope
1869 (SESSION(session)->sock, item, str);
1871 log_warning(_("can't parse envelope: %s\n"), str->str);
1874 if (item->stype == F_QUEUE) {
1875 MSG_SET_TMP_FLAGS(msginfo->flags, MSG_QUEUED);
1876 } else if (item->stype == F_DRAFT) {
1877 MSG_SET_TMP_FLAGS(msginfo->flags, MSG_DRAFT);
1880 msginfo->folder = item;
1883 llast = newlist = g_slist_append(newlist, msginfo);
1885 llast = g_slist_append(llast, msginfo);
1886 llast = llast->next;
1890 g_string_free(str, TRUE);
1892 imap_seq_set_free(seq_list);
1897 static void imap_delete_all_cached_messages(FolderItem *item)
1901 g_return_if_fail(item != NULL);
1902 g_return_if_fail(item->folder != NULL);
1903 g_return_if_fail(FOLDER_CLASS(item->folder) == &imap_class);
1905 debug_print("Deleting all cached messages...\n");
1907 dir = folder_item_get_path(item);
1908 if (is_dir_exist(dir))
1909 remove_all_numbered_files(dir);
1912 debug_print("done.\n");
1916 static SockInfo *imap_open_tunnel(const gchar *server,
1917 const gchar *tunnelcmd,
1920 static SockInfo *imap_open_tunnel(const gchar *server,
1921 const gchar *tunnelcmd)
1926 if ((sock = sock_connect_cmd(server, tunnelcmd)) == NULL) {
1927 log_warning(_("Can't establish IMAP4 session with: %s\n"),
1932 return imap_init_sock(sock, ssl_type);
1934 return imap_init_sock(sock);
1940 static SockInfo *imap_open(const gchar *server, gushort port,
1943 static SockInfo *imap_open(const gchar *server, gushort port)
1948 if ((sock = sock_connect(server, port)) == NULL) {
1949 log_warning(_("Can't connect to IMAP4 server: %s:%d\n"),
1955 if (ssl_type == SSL_TUNNEL && !ssl_init_socket(sock)) {
1956 log_warning(_("Can't establish IMAP4 session with: %s:%d\n"),
1966 static SockInfo *imap_init_sock(SockInfo *sock, SSLType ssl_type)
1968 static SockInfo *imap_init_sock(SockInfo *sock)
1975 static GList *imap_parse_namespace_str(gchar *str)
1980 IMAPNameSpace *namespace;
1981 GList *ns_list = NULL;
1983 while (*p != '\0') {
1984 /* parse ("#foo" "/") */
1986 while (*p && *p != '(') p++;
1987 if (*p == '\0') break;
1990 while (*p && *p != '"') p++;
1991 if (*p == '\0') break;
1995 while (*p && *p != '"') p++;
1996 if (*p == '\0') break;
2000 while (*p && isspace(*p)) p++;
2001 if (*p == '\0') break;
2002 if (strncmp(p, "NIL", 3) == 0)
2004 else if (*p == '"') {
2007 while (*p && *p != '"') p++;
2008 if (*p == '\0') break;
2013 while (*p && *p != ')') p++;
2014 if (*p == '\0') break;
2017 namespace = g_new(IMAPNameSpace, 1);
2018 namespace->name = g_strdup(name);
2019 namespace->separator = separator ? separator[0] : '\0';
2020 ns_list = g_list_append(ns_list, namespace);
2026 static void imap_parse_namespace(IMAPSession *session, IMAPFolder *folder)
2031 g_return_if_fail(session != NULL);
2032 g_return_if_fail(folder != NULL);
2034 if (folder->ns_personal != NULL ||
2035 folder->ns_others != NULL ||
2036 folder->ns_shared != NULL)
2039 if (!imap_has_capability(session, "NAMESPACE")) {
2040 imap_get_namespace_by_list(session, folder);
2044 if (imap_cmd_namespace(session, &ns_str)
2046 log_warning(_("can't get namespace\n"));
2050 str_array = strsplit_parenthesis(ns_str, '(', ')', 3);
2051 if (str_array == NULL) {
2053 imap_get_namespace_by_list(session, folder);
2057 folder->ns_personal = imap_parse_namespace_str(str_array[0]);
2058 if (str_array[0] && str_array[1])
2059 folder->ns_others = imap_parse_namespace_str(str_array[1]);
2060 if (str_array[0] && str_array[1] && str_array[2])
2061 folder->ns_shared = imap_parse_namespace_str(str_array[2]);
2062 g_strfreev(str_array);
2066 static void imap_get_namespace_by_list(IMAPSession *session, IMAPFolder *folder)
2068 GSList *item_list, *cur;
2069 gchar separator = '\0';
2070 IMAPNameSpace *namespace;
2072 g_return_if_fail(session != NULL);
2073 g_return_if_fail(folder != NULL);
2075 if (folder->ns_personal != NULL ||
2076 folder->ns_others != NULL ||
2077 folder->ns_shared != NULL)
2080 imap_gen_send(session, "LIST \"\" \"\"");
2081 item_list = imap_parse_list(folder, session, "", &separator);
2082 for (cur = item_list; cur != NULL; cur = cur->next)
2083 folder_item_destroy(FOLDER_ITEM(cur->data));
2084 g_slist_free(item_list);
2086 namespace = g_new(IMAPNameSpace, 1);
2087 namespace->name = g_strdup("");
2088 namespace->separator = separator;
2089 folder->ns_personal = g_list_append(NULL, namespace);
2092 static IMAPNameSpace *imap_find_namespace_from_list(GList *ns_list,
2095 IMAPNameSpace *namespace = NULL;
2096 gchar *tmp_path, *name;
2098 if (!path) path = "";
2100 for (; ns_list != NULL; ns_list = ns_list->next) {
2101 IMAPNameSpace *tmp_ns = ns_list->data;
2103 Xstrcat_a(tmp_path, path, "/", return namespace);
2104 Xstrdup_a(name, tmp_ns->name, return namespace);
2105 if (tmp_ns->separator && tmp_ns->separator != '/') {
2106 subst_char(tmp_path, tmp_ns->separator, '/');
2107 subst_char(name, tmp_ns->separator, '/');
2109 if (strncmp(tmp_path, name, strlen(name)) == 0)
2116 static IMAPNameSpace *imap_find_namespace(IMAPFolder *folder,
2119 IMAPNameSpace *namespace;
2121 g_return_val_if_fail(folder != NULL, NULL);
2123 namespace = imap_find_namespace_from_list(folder->ns_personal, path);
2124 if (namespace) return namespace;
2125 namespace = imap_find_namespace_from_list(folder->ns_others, path);
2126 if (namespace) return namespace;
2127 namespace = imap_find_namespace_from_list(folder->ns_shared, path);
2128 if (namespace) return namespace;
2133 static gchar imap_get_path_separator(IMAPFolder *folder, const gchar *path)
2135 IMAPNameSpace *namespace;
2136 gchar separator = '/';
2138 namespace = imap_find_namespace(folder, path);
2139 if (namespace && namespace->separator)
2140 separator = namespace->separator;
2145 static gchar *imap_get_real_path(IMAPFolder *folder, const gchar *path)
2150 g_return_val_if_fail(folder != NULL, NULL);
2151 g_return_val_if_fail(path != NULL, NULL);
2153 real_path = imap_locale_to_modified_utf7(path);
2154 separator = imap_get_path_separator(folder, path);
2155 imap_path_separator_subst(real_path, separator);
2160 static gchar *imap_parse_atom(SockInfo *sock, gchar *src,
2161 gchar *dest, gint dest_len, GString *str)
2163 gchar *cur_pos = src;
2166 g_return_val_if_fail(str != NULL, cur_pos);
2168 /* read the next line if the current response buffer is empty */
2169 while (isspace(*cur_pos)) cur_pos++;
2170 while (*cur_pos == '\0') {
2171 if ((nextline = sock_getline(sock)) == NULL)
2173 g_string_assign(str, nextline);
2175 strretchomp(nextline);
2176 /* log_print("IMAP4< %s\n", nextline); */
2177 debug_print("IMAP4< %s\n", nextline);
2180 while (isspace(*cur_pos)) cur_pos++;
2183 if (!strncmp(cur_pos, "NIL", 3)) {
2186 } else if (*cur_pos == '\"') {
2189 p = get_quoted(cur_pos, '\"', dest, dest_len);
2190 cur_pos = p ? p : cur_pos + 2;
2191 } else if (*cur_pos == '{') {
2196 cur_pos = strchr_cpy(cur_pos + 1, '}', buf, sizeof(buf));
2198 g_return_val_if_fail(len >= 0, cur_pos);
2200 g_string_truncate(str, 0);
2204 if ((nextline = sock_getline(sock)) == NULL)
2206 line_len += strlen(nextline);
2207 g_string_append(str, nextline);
2209 strretchomp(nextline);
2210 /* log_print("IMAP4< %s\n", nextline); */
2211 debug_print("IMAP4< %s\n", nextline);
2213 } while (line_len < len);
2215 memcpy(dest, cur_pos, MIN(len, dest_len - 1));
2216 dest[MIN(len, dest_len - 1)] = '\0';
2223 static gchar *imap_get_header(SockInfo *sock, gchar *cur_pos, gchar **headers,
2233 g_return_val_if_fail(str != NULL, cur_pos);
2235 while (isspace(*cur_pos)) cur_pos++;
2237 g_return_val_if_fail(*cur_pos == '{', cur_pos);
2239 cur_pos = strchr_cpy(cur_pos + 1, '}', buf, sizeof(buf));
2241 g_return_val_if_fail(len >= 0, cur_pos);
2243 g_string_truncate(str, 0);
2247 if ((nextline = sock_getline(sock)) == NULL)
2249 block_len += strlen(nextline);
2250 g_string_append(str, nextline);
2252 strretchomp(nextline);
2253 /* debug_print("IMAP4< %s\n", nextline); */
2255 } while (block_len < len);
2257 debug_print("IMAP4< [contents of BODY.PEEK[HEADER.FIELDS (...)]]\n");
2259 *headers = g_strndup(cur_pos, len);
2262 while (isspace(*cur_pos)) cur_pos++;
2263 while (*cur_pos == '\0') {
2264 if ((nextline = sock_getline(sock)) == NULL)
2266 g_string_assign(str, nextline);
2268 strretchomp(nextline);
2269 debug_print("IMAP4< %s\n", nextline);
2272 while (isspace(*cur_pos)) cur_pos++;
2278 static MsgFlags imap_parse_flags(const gchar *flag_str)
2280 const gchar *p = flag_str;
2281 MsgFlags flags = {0, 0};
2283 flags.perm_flags = MSG_UNREAD;
2285 while ((p = strchr(p, '\\')) != NULL) {
2288 if (g_strncasecmp(p, "Recent", 6) == 0 && MSG_IS_UNREAD(flags)) {
2289 MSG_SET_PERM_FLAGS(flags, MSG_NEW);
2290 } else if (g_strncasecmp(p, "Seen", 4) == 0) {
2291 MSG_UNSET_PERM_FLAGS(flags, MSG_NEW|MSG_UNREAD);
2292 } else if (g_strncasecmp(p, "Deleted", 7) == 0) {
2293 MSG_SET_PERM_FLAGS(flags, MSG_DELETED);
2294 } else if (g_strncasecmp(p, "Flagged", 7) == 0) {
2295 MSG_SET_PERM_FLAGS(flags, MSG_MARKED);
2296 } else if (g_strncasecmp(p, "Answered", 8) == 0) {
2297 MSG_SET_PERM_FLAGS(flags, MSG_REPLIED);
2304 static MsgInfo *imap_parse_envelope(SockInfo *sock, FolderItem *item,
2307 gchar buf[IMAPBUFSIZE];
2308 MsgInfo *msginfo = NULL;
2313 MsgFlags flags = {0, 0}, imap_flags = {0, 0};
2315 g_return_val_if_fail(line_str != NULL, NULL);
2316 g_return_val_if_fail(line_str->str[0] == '*' &&
2317 line_str->str[1] == ' ', NULL);
2319 MSG_SET_TMP_FLAGS(flags, MSG_IMAP);
2320 if (item->stype == F_QUEUE) {
2321 MSG_SET_TMP_FLAGS(flags, MSG_QUEUED);
2322 } else if (item->stype == F_DRAFT) {
2323 MSG_SET_TMP_FLAGS(flags, MSG_DRAFT);
2326 cur_pos = line_str->str + 2;
2328 #define PARSE_ONE_ELEMENT(ch) \
2330 cur_pos = strchr_cpy(cur_pos, ch, buf, sizeof(buf)); \
2331 if (cur_pos == NULL) { \
2332 g_warning("cur_pos == NULL\n"); \
2333 procmsg_msginfo_free(msginfo); \
2338 PARSE_ONE_ELEMENT(' ');
2341 PARSE_ONE_ELEMENT(' ');
2342 g_return_val_if_fail(!strcmp(buf, "FETCH"), NULL);
2344 g_return_val_if_fail(*cur_pos == '(', NULL);
2347 while (*cur_pos != '\0' && *cur_pos != ')') {
2348 while (*cur_pos == ' ') cur_pos++;
2350 if (!strncmp(cur_pos, "UID ", 4)) {
2352 uid = strtoul(cur_pos, &cur_pos, 10);
2353 } else if (!strncmp(cur_pos, "FLAGS ", 6)) {
2355 if (*cur_pos != '(') {
2356 g_warning("*cur_pos != '('\n");
2357 procmsg_msginfo_free(msginfo);
2361 PARSE_ONE_ELEMENT(')');
2362 imap_flags = imap_parse_flags(buf);
2363 } else if (!strncmp(cur_pos, "RFC822.SIZE ", 12)) {
2365 size = strtol(cur_pos, &cur_pos, 10);
2366 } else if (!strncmp(cur_pos, "BODY[HEADER.FIELDS ", 19)) {
2370 if (*cur_pos != '(') {
2371 g_warning("*cur_pos != '('\n");
2372 procmsg_msginfo_free(msginfo);
2376 PARSE_ONE_ELEMENT(')');
2377 if (*cur_pos != ']') {
2378 g_warning("*cur_pos != ']'\n");
2379 procmsg_msginfo_free(msginfo);
2384 cur_pos = imap_get_header(sock, cur_pos, &headers,
2386 msginfo = procheader_parse_str(headers, flags, FALSE, FALSE);
2389 g_warning("invalid FETCH response: %s\n", cur_pos);
2395 msginfo->msgnum = uid;
2396 msginfo->size = size;
2397 msginfo->flags.tmp_flags |= imap_flags.tmp_flags;
2398 msginfo->flags.perm_flags = imap_flags.perm_flags;
2404 static gchar *imap_get_flag_str(IMAPFlags flags)
2409 str = g_string_new(NULL);
2411 if (IMAP_IS_SEEN(flags)) g_string_append(str, "\\Seen ");
2412 if (IMAP_IS_ANSWERED(flags)) g_string_append(str, "\\Answered ");
2413 if (IMAP_IS_FLAGGED(flags)) g_string_append(str, "\\Flagged ");
2414 if (IMAP_IS_DELETED(flags)) g_string_append(str, "\\Deleted ");
2415 if (IMAP_IS_DRAFT(flags)) g_string_append(str, "\\Draft");
2417 if (str->len > 0 && str->str[str->len - 1] == ' ')
2418 g_string_truncate(str, str->len - 1);
2421 g_string_free(str, FALSE);
2426 static gint imap_set_message_flags(IMAPSession *session,
2427 MsgNumberList *numlist,
2434 GSList *seq_list, *cur;
2437 flag_str = imap_get_flag_str(flags);
2438 cmd = g_strconcat(is_set ? "+FLAGS.SILENT (" : "-FLAGS.SILENT (",
2439 flag_str, ")", NULL);
2442 seq_list = imap_get_seq_set_from_numlist(numlist);
2443 for (cur = seq_list; cur != NULL; cur = g_slist_next(cur)) {
2444 imapset = cur->data;
2446 ok = imap_cmd_store(session, imapset, cmd);
2448 imap_seq_set_free(seq_list);
2454 static gint imap_select(IMAPSession *session, IMAPFolder *folder,
2456 gint *exists, gint *recent, gint *unseen,
2457 guint32 *uid_validity)
2461 gint exists_, recent_, unseen_, uid_validity_;
2463 if (!exists || !recent || !unseen || !uid_validity) {
2464 if (session->mbox && strcmp(session->mbox, path) == 0)
2465 return IMAP_SUCCESS;
2469 uid_validity = &uid_validity_;
2472 g_free(session->mbox);
2473 session->mbox = NULL;
2475 real_path = imap_get_real_path(folder, path);
2476 ok = imap_cmd_select(session, real_path,
2477 exists, recent, unseen, uid_validity);
2478 if (ok != IMAP_SUCCESS)
2479 log_warning(_("can't select folder: %s\n"), real_path);
2481 session->mbox = g_strdup(path);
2482 session->folder_content_changed = FALSE;
2489 #define THROW(err) { ok = err; goto catch; }
2491 static gint imap_status(IMAPSession *session, IMAPFolder *folder,
2493 gint *messages, gint *recent,
2494 guint32 *uid_next, guint32 *uid_validity,
2500 GPtrArray *argbuf = NULL;
2503 if (messages && recent && uid_next && uid_validity && unseen) {
2504 *messages = *recent = *uid_next = *uid_validity = *unseen = 0;
2505 argbuf = g_ptr_array_new();
2508 real_path = imap_get_real_path(folder, path);
2509 QUOTE_IF_REQUIRED(real_path_, real_path);
2510 imap_gen_send(session, "STATUS %s "
2511 "(MESSAGES RECENT UIDNEXT UIDVALIDITY UNSEEN)",
2514 ok = imap_cmd_ok(session, argbuf);
2515 if (ok != IMAP_SUCCESS || !argbuf) THROW(ok);
2517 str = search_array_str(argbuf, "STATUS");
2518 if (!str) THROW(IMAP_ERROR);
2520 str = strchr(str, '(');
2521 if (!str) THROW(IMAP_ERROR);
2523 while (*str != '\0' && *str != ')') {
2524 while (*str == ' ') str++;
2526 if (!strncmp(str, "MESSAGES ", 9)) {
2528 *messages = strtol(str, &str, 10);
2529 } else if (!strncmp(str, "RECENT ", 7)) {
2531 *recent = strtol(str, &str, 10);
2532 } else if (!strncmp(str, "UIDNEXT ", 8)) {
2534 *uid_next = strtoul(str, &str, 10);
2535 } else if (!strncmp(str, "UIDVALIDITY ", 12)) {
2537 *uid_validity = strtoul(str, &str, 10);
2538 } else if (!strncmp(str, "UNSEEN ", 7)) {
2540 *unseen = strtol(str, &str, 10);
2542 g_warning("invalid STATUS response: %s\n", str);
2550 ptr_array_free_strings(argbuf);
2551 g_ptr_array_free(argbuf, TRUE);
2559 static gboolean imap_has_capability(IMAPSession *session, const gchar *cap)
2563 for (p = session->capability; *p != NULL; ++p) {
2564 if (!g_strcasecmp(*p, cap))
2571 static void imap_free_capabilities(IMAPSession *session)
2573 g_strfreev(session->capability);
2574 session->capability = NULL;
2577 /* low-level IMAP4rev1 commands */
2579 static gint imap_cmd_authenticate(IMAPSession *session, const gchar *user,
2580 const gchar *pass, IMAPAuthType type)
2587 gchar hexdigest[33];
2591 auth_type = "CRAM-MD5";
2593 imap_gen_send(session, "AUTHENTICATE %s", auth_type);
2594 ok = imap_gen_recv(session, &buf);
2595 if (ok != IMAP_SUCCESS || buf[0] != '+' || buf[1] != ' ') {
2600 challenge = g_malloc(strlen(buf + 2) + 1);
2601 challenge_len = base64_decode(challenge, buf + 2, -1);
2602 challenge[challenge_len] = '\0';
2604 log_print("IMAP< [Decoded: %s]\n", challenge);
2606 md5_hex_hmac(hexdigest, challenge, challenge_len, pass, strlen(pass));
2609 response = g_strdup_printf("%s %s", user, hexdigest);
2610 log_print("IMAP> [Encoded: %s]\n", response);
2611 response64 = g_malloc((strlen(response) + 3) * 2 + 1);
2612 base64_encode(response64, response, strlen(response));
2615 log_print("IMAP> %s\n", response64);
2616 sock_puts(SESSION(session)->sock, response64);
2617 ok = imap_cmd_ok(session, NULL);
2618 if (ok != IMAP_SUCCESS)
2619 log_warning(_("IMAP4 authentication failed.\n"));
2624 static gint imap_cmd_login(IMAPSession *session,
2625 const gchar *user, const gchar *pass)
2627 gchar *user_, *pass_;
2630 QUOTE_IF_REQUIRED(user_, user);
2631 QUOTE_IF_REQUIRED(pass_, pass);
2632 imap_gen_send(session, "LOGIN %s %s", user_, pass_);
2634 ok = imap_cmd_ok(session, NULL);
2635 if (ok != IMAP_SUCCESS)
2636 log_warning(_("IMAP4 login failed.\n"));
2641 static gint imap_cmd_logout(IMAPSession *session)
2643 imap_gen_send(session, "LOGOUT");
2644 return imap_cmd_ok(session, NULL);
2647 static gint imap_cmd_noop(IMAPSession *session)
2649 imap_gen_send(session, "NOOP");
2650 return imap_cmd_ok(session, NULL);
2653 static gint imap_cmd_starttls(IMAPSession *session)
2655 imap_gen_send(session, "STARTTLS");
2656 return imap_cmd_ok(session, NULL);
2659 #define THROW(err) { ok = err; goto catch; }
2661 static gint imap_cmd_namespace(IMAPSession *session, gchar **ns_str)
2667 argbuf = g_ptr_array_new();
2669 imap_gen_send(session, "NAMESPACE");
2670 if ((ok = imap_cmd_ok(session, argbuf)) != IMAP_SUCCESS) THROW(ok);
2672 str = search_array_str(argbuf, "NAMESPACE");
2673 if (!str) THROW(IMAP_ERROR);
2675 *ns_str = g_strdup(str);
2678 ptr_array_free_strings(argbuf);
2679 g_ptr_array_free(argbuf, TRUE);
2686 static gint imap_cmd_list(IMAPSession *session, const gchar *ref,
2687 const gchar *mailbox, GPtrArray *argbuf)
2689 gchar *ref_, *mailbox_;
2691 if (!ref) ref = "\"\"";
2692 if (!mailbox) mailbox = "\"\"";
2694 QUOTE_IF_REQUIRED(ref_, ref);
2695 QUOTE_IF_REQUIRED(mailbox_, mailbox);
2696 imap_gen_send(session, "LIST %s %s", ref_, mailbox_);
2698 return imap_cmd_ok(session, argbuf);
2701 #define THROW goto catch
2703 static gint imap_cmd_do_select(IMAPSession *session, const gchar *folder,
2705 gint *exists, gint *recent, gint *unseen,
2706 guint32 *uid_validity)
2714 *exists = *recent = *unseen = *uid_validity = 0;
2715 argbuf = g_ptr_array_new();
2718 select_cmd = "EXAMINE";
2720 select_cmd = "SELECT";
2722 QUOTE_IF_REQUIRED(folder_, folder);
2723 imap_gen_send(session, "%s %s", select_cmd, folder_);
2725 if ((ok = imap_cmd_ok(session, argbuf)) != IMAP_SUCCESS) THROW;
2727 resp_str = search_array_contain_str(argbuf, "EXISTS");
2729 if (sscanf(resp_str,"%d EXISTS", exists) != 1) {
2730 g_warning("imap_cmd_select(): invalid EXISTS line.\n");
2735 resp_str = search_array_contain_str(argbuf, "RECENT");
2737 if (sscanf(resp_str, "%d RECENT", recent) != 1) {
2738 g_warning("imap_cmd_select(): invalid RECENT line.\n");
2743 resp_str = search_array_contain_str(argbuf, "UIDVALIDITY");
2745 if (sscanf(resp_str, "OK [UIDVALIDITY %u] ", uid_validity)
2747 g_warning("imap_cmd_select(): invalid UIDVALIDITY line.\n");
2752 resp_str = search_array_contain_str(argbuf, "UNSEEN");
2754 if (sscanf(resp_str, "OK [UNSEEN %d] ", unseen) != 1) {
2755 g_warning("imap_cmd_select(): invalid UNSEEN line.\n");
2761 ptr_array_free_strings(argbuf);
2762 g_ptr_array_free(argbuf, TRUE);
2767 static gint imap_cmd_select(IMAPSession *session, const gchar *folder,
2768 gint *exists, gint *recent, gint *unseen,
2769 guint32 *uid_validity)
2771 return imap_cmd_do_select(session, folder, FALSE,
2772 exists, recent, unseen, uid_validity);
2775 static gint imap_cmd_examine(IMAPSession *session, const gchar *folder,
2776 gint *exists, gint *recent, gint *unseen,
2777 guint32 *uid_validity)
2779 return imap_cmd_do_select(session, folder, TRUE,
2780 exists, recent, unseen, uid_validity);
2785 static gint imap_cmd_create(IMAPSession *session, const gchar *folder)
2789 QUOTE_IF_REQUIRED(folder_, folder);
2790 imap_gen_send(session, "CREATE %s", folder_);
2792 return imap_cmd_ok(session, NULL);
2795 static gint imap_cmd_rename(IMAPSession *session, const gchar *old_folder,
2796 const gchar *new_folder)
2798 gchar *old_folder_, *new_folder_;
2800 QUOTE_IF_REQUIRED(old_folder_, old_folder);
2801 QUOTE_IF_REQUIRED(new_folder_, new_folder);
2802 imap_gen_send(session, "RENAME %s %s", old_folder_, new_folder_);
2804 return imap_cmd_ok(session, NULL);
2807 static gint imap_cmd_delete(IMAPSession *session, const gchar *folder)
2811 QUOTE_IF_REQUIRED(folder_, folder);
2812 imap_gen_send(session, "DELETE %s", folder_);
2814 return imap_cmd_ok(session, NULL);
2817 static gint imap_cmd_search(IMAPSession *session, const gchar *criteria,
2824 g_return_val_if_fail(criteria != NULL, IMAP_ERROR);
2825 g_return_val_if_fail(list != NULL, IMAP_ERROR);
2829 argbuf = g_ptr_array_new();
2830 imap_gen_send(session, "UID SEARCH %s", criteria);
2832 ok = imap_cmd_ok(session, argbuf);
2833 if (ok != IMAP_SUCCESS) {
2834 ptr_array_free_strings(argbuf);
2835 g_ptr_array_free(argbuf, TRUE);
2839 if ((uidlist = search_array_str(argbuf, "SEARCH ")) != NULL) {
2840 gchar **strlist, **p;
2842 strlist = g_strsplit(uidlist + 7, " ", 0);
2843 for (p = strlist; *p != NULL; ++p) {
2846 if (sscanf(*p, "%d", &msgnum) == 1)
2847 *list = g_slist_append(*list, GINT_TO_POINTER(msgnum));
2849 g_strfreev(strlist);
2851 ptr_array_free_strings(argbuf);
2852 g_ptr_array_free(argbuf, TRUE);
2854 return IMAP_SUCCESS;
2857 static gint imap_cmd_fetch(IMAPSession *session, guint32 uid,
2858 const gchar *filename)
2866 g_return_val_if_fail(filename != NULL, IMAP_ERROR);
2868 imap_gen_send(session, "UID FETCH %d BODY.PEEK[]", uid);
2870 while ((ok = imap_gen_recv(session, &buf)) == IMAP_SUCCESS) {
2871 if (buf[0] != '*' || buf[1] != ' ') {
2875 if (strstr(buf, "FETCH") != NULL) break;
2878 if (ok != IMAP_SUCCESS) {
2883 #define RETURN_ERROR_IF_FAIL(cond) \
2886 return IMAP_ERROR; \
2889 cur_pos = strchr(buf, '{');
2890 RETURN_ERROR_IF_FAIL(cur_pos != NULL);
2891 cur_pos = strchr_cpy(cur_pos + 1, '}', size_str, sizeof(size_str));
2892 RETURN_ERROR_IF_FAIL(cur_pos != NULL);
2893 size_num = atol(size_str);
2894 RETURN_ERROR_IF_FAIL(size_num >= 0);
2896 RETURN_ERROR_IF_FAIL(*cur_pos == '\0');
2898 #undef RETURN_ERROR_IF_FAIL
2902 if (recv_bytes_write_to_file(SESSION(session)->sock,
2903 size_num, filename) != 0)
2906 if (imap_gen_recv(session, &buf) != IMAP_SUCCESS) {
2911 if (buf[0] == '\0' || buf[strlen(buf) - 1] != ')') {
2917 ok = imap_cmd_ok(session, NULL);
2922 static gint imap_cmd_append(IMAPSession *session, const gchar *destfolder,
2923 const gchar *file, IMAPFlags flags,
2932 gchar buf[BUFFSIZE];
2937 g_return_val_if_fail(file != NULL, IMAP_ERROR);
2939 size = get_file_size_as_crlf(file);
2940 if ((fp = fopen(file, "rb")) == NULL) {
2941 FILE_OP_ERROR(file, "fopen");
2944 QUOTE_IF_REQUIRED(destfolder_, destfolder);
2945 flag_str = imap_get_flag_str(flags);
2946 imap_gen_send(session, "APPEND %s (%s) {%d}",
2947 destfolder_, flag_str, size);
2950 ok = imap_gen_recv(session, &ret);
2951 if (ok != IMAP_SUCCESS || ret[0] != '+' || ret[1] != ' ') {
2952 log_warning(_("can't append %s to %s\n"), file, destfolder_);
2959 log_print("IMAP4> %s\n", _("(sending file...)"));
2961 while (fgets(buf, sizeof(buf), fp) != NULL) {
2963 if (sock_puts(SESSION(session)->sock, buf) < 0) {
2970 FILE_OP_ERROR(file, "fgets");
2975 sock_puts(SESSION(session)->sock, "");
2979 if (new_uid != NULL)
2982 if (new_uid != NULL && session->uidplus) {
2983 argbuf = g_ptr_array_new();
2985 ok = imap_cmd_ok(session, argbuf);
2986 if ((ok == IMAP_SUCCESS) && (argbuf->len > 0)) {
2987 resp_str = g_ptr_array_index(argbuf, argbuf->len - 1);
2989 sscanf(resp_str, "%*u OK [APPENDUID %*u %u]",
2991 *new_uid = new_uid_;
2995 ptr_array_free_strings(argbuf);
2996 g_ptr_array_free(argbuf, TRUE);
2998 ok = imap_cmd_ok(session, NULL);
3000 if (ok != IMAP_SUCCESS)
3001 log_warning(_("can't append message to %s\n"),
3007 static MsgNumberList *imapset_to_numlist(IMAPSet imapset)
3009 gchar **ranges, **range;
3011 MsgNumberList *uids = NULL;
3013 ranges = g_strsplit(imapset, ",", 0);
3014 for (range = ranges; *range != NULL; range++) {
3015 printf("%s\n", *range);
3016 if(sscanf(*range, "%u:%u", &low, &high) == 1)
3017 uids = g_slist_prepend(uids, GINT_TO_POINTER(low));
3020 for (i = low; i <= high; i++)
3021 uids = g_slist_prepend(uids, GINT_TO_POINTER(i));
3024 uids = g_slist_reverse(uids);
3030 static gint imap_cmd_copy(IMAPSession *session, const gchar *seq_set,
3031 const gchar *destfolder, GRelation *uid_mapping)
3036 g_return_val_if_fail(session != NULL, IMAP_ERROR);
3037 g_return_val_if_fail(seq_set != NULL, IMAP_ERROR);
3038 g_return_val_if_fail(destfolder != NULL, IMAP_ERROR);
3040 QUOTE_IF_REQUIRED(destfolder_, destfolder);
3041 imap_gen_send(session, "UID COPY %s %s", seq_set, destfolder_);
3043 if (uid_mapping != NULL && session->uidplus) {
3045 gchar *resp_str = NULL, *olduids_str, *newuids_str;
3046 MsgNumberList *olduids, *old_cur, *newuids, *new_cur;
3048 reply = g_ptr_array_new();
3049 ok = imap_cmd_ok(session, reply);
3050 if ((ok == IMAP_SUCCESS) && (reply->len > 0)) {
3051 resp_str = g_ptr_array_index(reply, reply->len - 1);
3053 olduids_str = g_new0(gchar, strlen(resp_str));
3054 newuids_str = g_new0(gchar, strlen(resp_str));
3055 if (sscanf(resp_str, "%*s OK [COPYUID %*u %[0-9,:] %[0-9,:]]",
3056 olduids_str, newuids_str) == 2) {
3057 olduids = imapset_to_numlist(olduids_str);
3058 newuids = imapset_to_numlist(newuids_str);
3062 while(old_cur != NULL && new_cur != NULL) {
3063 g_relation_insert(uid_mapping,
3064 GPOINTER_TO_INT(old_cur->data),
3065 GPOINTER_TO_INT(new_cur->data));
3066 old_cur = g_slist_next(old_cur);
3067 new_cur = g_slist_next(new_cur);
3070 g_slist_free(olduids);
3071 g_slist_free(newuids);
3073 g_free(olduids_str);
3074 g_free(newuids_str);
3077 ptr_array_free_strings(reply);
3078 g_ptr_array_free(reply, TRUE);
3080 ok = imap_cmd_ok(session, NULL);
3082 if (ok != IMAP_SUCCESS)
3083 log_warning(_("can't copy %s to %s\n"), seq_set, destfolder_);
3088 gint imap_cmd_envelope(IMAPSession *session, IMAPSet set)
3090 static GString *header_fields = NULL;
3092 if (header_fields == NULL) {
3093 const HeaderEntry *headers, *elem;
3095 headers = procheader_get_headernames(FALSE);
3096 header_fields = g_string_new("");
3098 for (elem = headers; elem->name != NULL; ++elem) {
3099 gint namelen = strlen(elem->name);
3101 /* Header fields ending with space are not rfc822 headers */
3102 if (elem->name[namelen - 1] == ' ')
3105 /* strip : at the of header field */
3106 if(elem->name[namelen - 1] == ':')
3112 g_string_sprintfa(header_fields, "%s%.*s",
3113 header_fields->str[0] != '\0' ? " " : "",
3114 namelen, elem->name);
3119 (session, "UID FETCH %s (UID FLAGS RFC822.SIZE BODY.PEEK[HEADER.FIELDS (%s)])",
3120 set, header_fields->str);
3122 return IMAP_SUCCESS;
3125 static gint imap_cmd_store(IMAPSession *session, IMAPSet seq_set,
3130 imap_gen_send(session, "UID STORE %s %s", seq_set, sub_cmd);
3132 if ((ok = imap_cmd_ok(session, NULL)) != IMAP_SUCCESS) {
3133 log_warning(_("error while imap command: STORE %s %s\n"),
3138 return IMAP_SUCCESS;
3141 static gint imap_cmd_expunge(IMAPSession *session, IMAPSet seq_set)
3145 if (seq_set && session->uidplus)
3146 imap_gen_send(session, "UID EXPUNGE %s", seq_set);
3148 imap_gen_send(session, "EXPUNGE");
3149 if ((ok = imap_cmd_ok(session, NULL)) != IMAP_SUCCESS) {
3150 log_warning(_("error while imap command: EXPUNGE\n"));
3154 return IMAP_SUCCESS;
3157 static gint imap_cmd_close(IMAPSession *session)
3161 imap_gen_send(session, "CLOSE");
3162 if ((ok = imap_cmd_ok(session, NULL)) != IMAP_SUCCESS)
3163 log_warning(_("error while imap command: CLOSE\n"));
3168 static gint imap_cmd_ok(IMAPSession *session, GPtrArray *argbuf)
3170 gint ok = IMAP_SUCCESS;
3175 while ((ok = imap_gen_recv(session, &buf))
3177 // make sure data is long enough for any substring of buf
3178 data = alloca(strlen(buf) + 1);
3180 // untagged line read
3181 if (buf[0] == '*' && buf[1] == ' ') {
3184 g_ptr_array_add(argbuf, g_strdup(buf + 2));
3186 if (sscanf(buf + 2, "%d %s", &num, data) >= 2) {
3187 if (!strcmp(data, "EXISTS")) {
3188 session->exists = num;
3189 session->folder_content_changed = TRUE;
3192 if(!strcmp(data, "EXPUNGE")) {
3194 session->folder_content_changed = TRUE;
3197 // tagged line with correct tag and OK response found
3198 } else if ((sscanf(buf, "%d %s", &cmd_num, data) >= 2) &&
3199 (cmd_num == session->cmd_count) &&
3200 !strcmp(data, "OK")) {
3202 g_ptr_array_add(argbuf, g_strdup(buf));
3216 static void imap_gen_send(IMAPSession *session, const gchar *format, ...)
3223 va_start(args, format);
3224 tmp = g_strdup_vprintf(format, args);
3227 session->cmd_count++;
3229 buf = g_strdup_printf("%d %s\r\n", session->cmd_count, tmp);
3230 if (!strncasecmp(tmp, "LOGIN ", 6) && (p = strchr(tmp + 6, ' '))) {
3232 log_print("IMAP4> %d %s ********\n", session->cmd_count, tmp);
3234 log_print("IMAP4> %d %s\n", session->cmd_count, tmp);
3236 sock_write_all(SESSION(session)->sock, buf, strlen(buf));
3241 static gint imap_gen_recv(IMAPSession *session, gchar **ret)
3243 if ((*ret = sock_getline(SESSION(session)->sock)) == NULL)
3248 log_print("IMAP4< %s\n", *ret);
3250 return IMAP_SUCCESS;
3254 /* misc utility functions */
3256 static gchar *strchr_cpy(const gchar *src, gchar ch, gchar *dest, gint len)
3261 tmp = strchr(src, ch);
3265 memcpy(dest, src, MIN(tmp - src, len - 1));
3266 dest[MIN(tmp - src, len - 1)] = '\0';
3271 static gchar *get_quoted(const gchar *src, gchar ch, gchar *dest, gint len)
3273 const gchar *p = src;
3276 g_return_val_if_fail(*p == ch, NULL);
3281 while (*p != '\0' && *p != ch) {
3283 if (*p == '\\' && *(p + 1) != '\0')
3292 return (gchar *)(*p == ch ? p + 1 : p);
3295 static gchar *search_array_contain_str(GPtrArray *array, const gchar *str)
3299 for (i = 0; i < array->len; i++) {
3302 tmp = g_ptr_array_index(array, i);
3303 if (strstr(tmp, str) != NULL)
3310 static gchar *search_array_str(GPtrArray *array, const gchar *str)
3317 for (i = 0; i < array->len; i++) {
3320 tmp = g_ptr_array_index(array, i);
3321 if (!strncmp(tmp, str, len))
3328 static void imap_path_separator_subst(gchar *str, gchar separator)
3331 gboolean in_escape = FALSE;
3333 if (!separator || separator == '/') return;
3335 for (p = str; *p != '\0'; p++) {
3336 if (*p == '/' && !in_escape)
3338 else if (*p == '&' && *(p + 1) != '-' && !in_escape)
3340 else if (*p == '-' && in_escape)
3345 static gchar *imap_modified_utf7_to_locale(const gchar *mutf7_str)
3348 const gchar *from_p;
3351 to = g_malloc(strlen(mutf7_str) + 1);
3354 for (from_p = mutf7_str; *from_p != '\0'; from_p++) {
3355 if (*from_p == '&' && *(from_p + 1) == '-') {
3365 static iconv_t cd = (iconv_t)-1;
3366 static gboolean iconv_ok = TRUE;
3369 size_t norm_utf7_len;
3371 gchar *to_str, *to_p;
3373 gboolean in_escape = FALSE;
3375 if (!iconv_ok) return g_strdup(mutf7_str);
3377 if (cd == (iconv_t)-1) {
3378 cd = iconv_open(conv_get_current_charset_str(), "UTF-7");
3379 if (cd == (iconv_t)-1) {
3380 g_warning("iconv cannot convert UTF-7 to %s\n",
3381 conv_get_current_charset_str());
3383 return g_strdup(mutf7_str);
3387 norm_utf7 = g_string_new(NULL);
3389 for (p = mutf7_str; *p != '\0'; p++) {
3390 /* replace: '&' -> '+',
3392 escaped ',' -> '/' */
3393 if (!in_escape && *p == '&') {
3394 if (*(p + 1) != '-') {
3395 g_string_append_c(norm_utf7, '+');
3398 g_string_append_c(norm_utf7, '&');
3401 } else if (in_escape && *p == ',') {
3402 g_string_append_c(norm_utf7, '/');
3403 } else if (in_escape && *p == '-') {
3404 g_string_append_c(norm_utf7, '-');
3407 g_string_append_c(norm_utf7, *p);
3411 norm_utf7_p = norm_utf7->str;
3412 norm_utf7_len = norm_utf7->len;
3413 to_len = strlen(mutf7_str) * 5;
3414 to_p = to_str = g_malloc(to_len + 1);
3416 if (iconv(cd, (ICONV_CONST gchar **)&norm_utf7_p, &norm_utf7_len,
3417 &to_p, &to_len) == -1) {
3418 g_warning(_("iconv cannot convert UTF-7 to %s\n"),
3419 conv_get_current_charset_str());
3420 g_string_free(norm_utf7, TRUE);
3422 return g_strdup(mutf7_str);
3425 /* second iconv() call for flushing */
3426 iconv(cd, NULL, NULL, &to_p, &to_len);
3427 g_string_free(norm_utf7, TRUE);
3431 #endif /* !HAVE_ICONV */
3434 static gchar *imap_locale_to_modified_utf7(const gchar *from)
3437 const gchar *from_p;
3440 to = g_malloc(strlen(from) * 2 + 1);
3443 for (from_p = from; *from_p != '\0'; from_p++) {
3444 if (*from_p == '&') {
3454 static iconv_t cd = (iconv_t)-1;
3455 static gboolean iconv_ok = TRUE;
3456 gchar *norm_utf7, *norm_utf7_p;
3457 size_t from_len, norm_utf7_len;
3459 gchar *from_tmp, *to, *p;
3460 gboolean in_escape = FALSE;
3462 if (!iconv_ok) return g_strdup(from);
3464 if (cd == (iconv_t)-1) {
3465 cd = iconv_open("UTF-7", conv_get_current_charset_str());
3466 if (cd == (iconv_t)-1) {
3467 g_warning("iconv cannot convert %s to UTF-7\n",
3468 conv_get_current_charset_str());
3470 return g_strdup(from);
3474 Xstrdup_a(from_tmp, from, return g_strdup(from));
3475 from_len = strlen(from);
3476 norm_utf7_len = from_len * 5;
3477 Xalloca(norm_utf7, norm_utf7_len + 1, return g_strdup(from));
3478 norm_utf7_p = norm_utf7;
3480 #define IS_PRINT(ch) (isprint(ch) && isascii(ch))
3482 while (from_len > 0) {
3483 if (*from_tmp == '+') {
3484 *norm_utf7_p++ = '+';
3485 *norm_utf7_p++ = '-';
3489 } else if (IS_PRINT(*from_tmp)) {
3490 /* printable ascii char */
3491 *norm_utf7_p = *from_tmp;
3497 size_t mb_len = 0, conv_len = 0;
3499 /* unprintable char: convert to UTF-7 */
3501 while (!IS_PRINT(*p) && conv_len < from_len) {
3502 mb_len = mblen(p, MB_LEN_MAX);
3504 g_warning("wrong multibyte sequence\n");
3505 return g_strdup(from);
3511 from_len -= conv_len;
3512 if (iconv(cd, (ICONV_CONST gchar **)&from_tmp,
3514 &norm_utf7_p, &norm_utf7_len) == -1) {
3515 g_warning("iconv cannot convert %s to UTF-7\n",
3516 conv_get_current_charset_str());
3517 return g_strdup(from);
3520 /* second iconv() call for flushing */
3521 iconv(cd, NULL, NULL, &norm_utf7_p, &norm_utf7_len);
3527 *norm_utf7_p = '\0';
3528 to_str = g_string_new(NULL);
3529 for (p = norm_utf7; p < norm_utf7_p; p++) {
3530 /* replace: '&' -> "&-",
3533 BASE64 '/' -> ',' */
3534 if (!in_escape && *p == '&') {
3535 g_string_append(to_str, "&-");
3536 } else if (!in_escape && *p == '+') {
3537 if (*(p + 1) == '-') {
3538 g_string_append_c(to_str, '+');
3541 g_string_append_c(to_str, '&');
3544 } else if (in_escape && *p == '/') {
3545 g_string_append_c(to_str, ',');
3546 } else if (in_escape && *p == '-') {
3547 g_string_append_c(to_str, '-');
3550 g_string_append_c(to_str, *p);
3556 g_string_append_c(to_str, '-');
3560 g_string_free(to_str, FALSE);
3563 #endif /* !HAVE_ICONV */
3566 static GSList *imap_get_seq_set_from_numlist(MsgNumberList *numlist)
3569 GSList *sorted_list, *cur;
3570 guint first, last, next;
3572 GSList *ret_list = NULL;
3574 if (numlist == NULL)
3577 str = g_string_sized_new(256);
3579 sorted_list = g_slist_copy(numlist);
3580 sorted_list = g_slist_sort(sorted_list, g_int_compare);
3582 first = GPOINTER_TO_INT(sorted_list->data);
3584 for (cur = sorted_list; cur != NULL; cur = g_slist_next(cur)) {
3585 last = GPOINTER_TO_INT(cur->data);
3587 next = GPOINTER_TO_INT(cur->next->data);
3591 if (last + 1 != next || next == 0) {
3593 g_string_append_c(str, ',');
3595 g_string_sprintfa(str, "%u", first);
3597 g_string_sprintfa(str, "%u:%u", first, last);
3601 if (str->len > IMAP_CMD_LIMIT) {
3602 ret_str = g_strdup(str->str);
3603 ret_list = g_slist_append(ret_list, ret_str);
3604 g_string_truncate(str, 0);
3610 ret_str = g_strdup(str->str);
3611 ret_list = g_slist_append(ret_list, ret_str);
3614 g_slist_free(sorted_list);
3615 g_string_free(str, TRUE);
3620 static GSList *imap_get_seq_set_from_msglist(MsgInfoList *msglist)
3622 MsgNumberList *numlist = NULL;
3626 for (cur = msglist; cur != NULL; cur = g_slist_next(cur)) {
3627 MsgInfo *msginfo = (MsgInfo *) cur->data;
3629 numlist = g_slist_append(numlist, GINT_TO_POINTER(msginfo->msgnum));
3631 seq_list = imap_get_seq_set_from_numlist(numlist);
3632 g_slist_free(numlist);
3637 static void imap_seq_set_free(GSList *seq_list)
3639 slist_free_strings(seq_list);
3640 g_slist_free(seq_list);
3644 static gboolean imap_rename_folder_func(GNode *node, gpointer data)
3646 FolderItem *item = node->data;
3647 gchar **paths = data;
3648 const gchar *oldpath = paths[0];
3649 const gchar *newpath = paths[1];
3651 gchar *new_itempath;
3654 oldpathlen = strlen(oldpath);
3655 if (strncmp(oldpath, item->path, oldpathlen) != 0) {
3656 g_warning("path doesn't match: %s, %s\n", oldpath, item->path);
3660 base = item->path + oldpathlen;
3661 while (*base == G_DIR_SEPARATOR) base++;
3663 new_itempath = g_strdup(newpath);
3665 new_itempath = g_strconcat(newpath, G_DIR_SEPARATOR_S, base,
3668 item->path = new_itempath;
3673 static gint get_list_of_uids(Folder *folder, IMAPFolderItem *item, GSList **msgnum_list)
3675 gint ok, nummsgs = 0, lastuid_old;
3676 IMAPSession *session;
3677 GSList *uidlist, *elem;
3680 session = imap_session_get(folder);
3681 g_return_val_if_fail(session != NULL, -1);
3683 ok = imap_select(session, IMAP_FOLDER(folder), item->item.path,
3684 NULL, NULL, NULL, NULL);
3685 if (ok != IMAP_SUCCESS)
3688 cmd_buf = g_strdup_printf("UID %d:*", item->lastuid + 1);
3689 ok = imap_cmd_search(session, cmd_buf, &uidlist);
3692 if (ok == IMAP_SOCKET) {
3693 session_destroy((Session *)session);
3694 ((RemoteFolder *)folder)->session = NULL;
3698 if (ok != IMAP_SUCCESS) {
3702 argbuf = g_ptr_array_new();
3704 cmd_buf = g_strdup_printf("UID FETCH %d:* (UID)", item->lastuid + 1);
3705 imap_gen_send(session, cmd_buf);
3707 ok = imap_cmd_ok(session, argbuf);
3708 if (ok != IMAP_SUCCESS) {
3709 ptr_array_free_strings(argbuf);
3710 g_ptr_array_free(argbuf, TRUE);
3714 for(i = 0; i < argbuf->len; i++) {
3717 if((ret = sscanf(g_ptr_array_index(argbuf, i),
3718 "%*d FETCH (UID %d)", &msgnum)) == 1)
3719 uidlist = g_slist_prepend(uidlist, GINT_TO_POINTER(msgnum));
3721 ptr_array_free_strings(argbuf);
3722 g_ptr_array_free(argbuf, TRUE);
3725 lastuid_old = item->lastuid;
3726 *msgnum_list = g_slist_copy(item->uid_list);
3727 nummsgs = g_slist_length(*msgnum_list);
3728 debug_print("Got %d uids from cache\n", g_slist_length(item->uid_list));
3730 for (elem = uidlist; elem != NULL; elem = g_slist_next(elem)) {
3733 msgnum = GPOINTER_TO_INT(elem->data);
3734 if (msgnum > lastuid_old) {
3735 *msgnum_list = g_slist_prepend(*msgnum_list, GINT_TO_POINTER(msgnum));
3736 item->uid_list = g_slist_prepend(item->uid_list, GINT_TO_POINTER(msgnum));
3739 if(msgnum > item->lastuid)
3740 item->lastuid = msgnum;
3743 g_slist_free(uidlist);
3748 gint imap_get_num_list(Folder *folder, FolderItem *_item, GSList **msgnum_list, gboolean *old_uids_valid)
3750 IMAPFolderItem *item = (IMAPFolderItem *)_item;
3751 IMAPSession *session;
3752 gint ok, nummsgs = 0, exists, recent, uid_val, uid_next, unseen;
3755 gboolean selected_folder;
3757 g_return_val_if_fail(folder != NULL, -1);
3758 g_return_val_if_fail(item != NULL, -1);
3759 g_return_val_if_fail(item->item.path != NULL, -1);
3760 g_return_val_if_fail(FOLDER_CLASS(folder) == &imap_class, -1);
3761 g_return_val_if_fail(folder->account != NULL, -1);
3763 session = imap_session_get(folder);
3764 g_return_val_if_fail(session != NULL, -1);
3766 selected_folder = (session->mbox != NULL) &&
3767 (!strcmp(session->mbox, item->item.path));
3768 if (selected_folder) {
3769 ok = imap_cmd_noop(session);
3770 if (ok != IMAP_SUCCESS)
3772 exists = session->exists;
3774 *old_uids_valid = TRUE;
3776 ok = imap_status(session, IMAP_FOLDER(folder), item->item.path,
3777 &exists, &recent, &uid_next, &uid_val, &unseen);
3778 if (ok != IMAP_SUCCESS)
3781 if(item->item.mtime == uid_val)
3782 *old_uids_valid = TRUE;
3784 *old_uids_valid = FALSE;
3786 debug_print("Freeing imap uid cache\n");
3788 g_slist_free(item->uid_list);
3789 item->uid_list = NULL;
3791 item->item.mtime = uid_val;
3793 imap_delete_all_cached_messages((FolderItem *)item);
3797 if (!selected_folder)
3798 item->uid_next = uid_next;
3800 /* If old uid_next matches new uid_next we can be sure no message
3801 was added to the folder */
3802 if (( selected_folder && !session->folder_content_changed) ||
3803 (!selected_folder && uid_next == item->uid_next)) {
3804 nummsgs = g_slist_length(item->uid_list);
3806 /* If number of messages is still the same we
3807 know our caches message numbers are still valid,
3808 otherwise if the number of messages has decrease
3809 we discard our cache to start a new scan to find
3810 out which numbers have been removed */
3811 if (exists == nummsgs) {
3812 *msgnum_list = g_slist_copy(item->uid_list);
3814 } else if (exists < nummsgs) {
3815 debug_print("Freeing imap uid cache");
3817 g_slist_free(item->uid_list);
3818 item->uid_list = NULL;
3823 *msgnum_list = NULL;
3827 nummsgs = get_list_of_uids(folder, item, &uidlist);
3829 if (nummsgs != exists) {
3830 /* Cache contains more messages then folder, we have cached
3831 an old UID of a message that was removed and new messages
3832 have been added too, otherwise the uid_next check would
3834 debug_print("Freeing imap uid cache");
3836 g_slist_free(item->uid_list);
3837 item->uid_list = NULL;
3839 g_slist_free(*msgnum_list);
3841 nummsgs = get_list_of_uids(folder, item, &uidlist);
3844 *msgnum_list = uidlist;
3846 dir = folder_item_get_path((FolderItem *)item);
3847 debug_print("removing old messages from %s\n", dir);
3848 remove_numbered_files_not_in_list(dir, *msgnum_list);
3854 static MsgInfo *imap_parse_msg(const gchar *file, FolderItem *item)
3859 flags.perm_flags = MSG_NEW|MSG_UNREAD;
3860 flags.tmp_flags = 0;
3862 g_return_val_if_fail(item != NULL, NULL);
3863 g_return_val_if_fail(file != NULL, NULL);
3865 if (item->stype == F_QUEUE) {
3866 MSG_SET_TMP_FLAGS(flags, MSG_QUEUED);
3867 } else if (item->stype == F_DRAFT) {
3868 MSG_SET_TMP_FLAGS(flags, MSG_DRAFT);
3871 msginfo = procheader_parse_file(file, flags, FALSE, FALSE);
3872 if (!msginfo) return NULL;
3874 msginfo->folder = item;
3879 GSList *imap_get_msginfos(Folder *folder, FolderItem *item, GSList *msgnum_list)
3881 IMAPSession *session;
3882 MsgInfoList *ret = NULL;
3885 g_return_val_if_fail(folder != NULL, NULL);
3886 g_return_val_if_fail(item != NULL, NULL);
3887 g_return_val_if_fail(msgnum_list != NULL, NULL);
3889 session = imap_session_get(folder);
3890 g_return_val_if_fail(session != NULL, NULL);
3892 ok = imap_select(session, IMAP_FOLDER(folder), item->path,
3893 NULL, NULL, NULL, NULL);
3894 if (ok != IMAP_SUCCESS)
3897 if (!(item->stype == F_QUEUE || item->stype == F_DRAFT)) {
3898 ret = g_slist_concat(ret,
3899 imap_get_uncached_messages(
3900 session, item, msgnum_list));
3902 MsgNumberList *sorted_list, *elem;
3903 gint startnum, lastnum;
3905 sorted_list = g_slist_sort(g_slist_copy(msgnum_list), g_int_compare);
3907 startnum = lastnum = GPOINTER_TO_INT(sorted_list->data);
3909 for (elem = sorted_list;; elem = g_slist_next(elem)) {
3913 num = GPOINTER_TO_INT(elem->data);
3915 if (num > lastnum + 1 || elem == NULL) {
3917 for (i = startnum; i <= lastnum; ++i) {
3920 file = imap_fetch_msg(folder, item, i);
3922 MsgInfo *msginfo = imap_parse_msg(file, item);
3923 if (msginfo != NULL) {
3924 msginfo->msgnum = i;
3925 ret = g_slist_append(ret, msginfo);
3939 g_slist_free(sorted_list);
3945 MsgInfo *imap_get_msginfo(Folder *folder, FolderItem *item, gint uid)
3947 MsgInfo *msginfo = NULL;
3948 MsgInfoList *msginfolist;
3949 MsgNumberList numlist;
3951 numlist.next = NULL;
3952 numlist.data = GINT_TO_POINTER(uid);
3954 msginfolist = imap_get_msginfos(folder, item, &numlist);
3955 if (msginfolist != NULL) {
3956 msginfo = msginfolist->data;
3957 g_slist_free(msginfolist);
3963 gboolean imap_scan_required(Folder *folder, FolderItem *_item)
3965 IMAPSession *session;
3966 IMAPFolderItem *item = (IMAPFolderItem *)_item;
3967 gint ok, exists = 0, recent = 0, unseen = 0;
3968 guint32 uid_next, uid_val = 0;
3969 gboolean selected_folder;
3971 g_return_val_if_fail(folder != NULL, FALSE);
3972 g_return_val_if_fail(item != NULL, FALSE);
3973 g_return_val_if_fail(item->item.folder != NULL, FALSE);
3974 g_return_val_if_fail(FOLDER_CLASS(item->item.folder) == &imap_class, FALSE);
3976 if (item->item.path == NULL)
3979 session = imap_session_get(folder);
3980 g_return_val_if_fail(session != NULL, FALSE);
3982 selected_folder = (session->mbox != NULL) &&
3983 (!strcmp(session->mbox, item->item.path));
3984 if (selected_folder) {
3985 ok = imap_cmd_noop(session);
3986 if (ok != IMAP_SUCCESS)
3989 if (session->folder_content_changed)
3992 ok = imap_status(session, IMAP_FOLDER(folder), item->item.path,
3993 &exists, &recent, &uid_next, &uid_val, &unseen);
3994 if (ok != IMAP_SUCCESS)
3997 if ((uid_next != item->uid_next) || (exists < item->item.total_msgs))
4004 void imap_change_flags(Folder *folder, FolderItem *item, MsgInfo *msginfo, MsgPermFlags newflags)
4006 IMAPSession *session;
4007 IMAPFlags flags_set = 0, flags_unset = 0;
4008 gint ok = IMAP_SUCCESS;
4009 MsgNumberList numlist;
4011 g_return_if_fail(folder != NULL);
4012 g_return_if_fail(folder->klass == &imap_class);
4013 g_return_if_fail(item != NULL);
4014 g_return_if_fail(item->folder == folder);
4015 g_return_if_fail(msginfo != NULL);
4016 g_return_if_fail(msginfo->folder == item);
4018 session = imap_session_get(folder);
4019 if (!session) return;
4021 if ((ok = imap_select(session, IMAP_FOLDER(folder), msginfo->folder->path,
4022 NULL, NULL, NULL, NULL)) != IMAP_SUCCESS)
4025 if (!MSG_IS_MARKED(msginfo->flags) && (newflags & MSG_MARKED))
4026 flags_set |= IMAP_FLAG_FLAGGED;
4027 if ( MSG_IS_MARKED(msginfo->flags) && !(newflags & MSG_MARKED))
4028 flags_unset |= IMAP_FLAG_FLAGGED;
4030 if (!MSG_IS_UNREAD(msginfo->flags) && (newflags & MSG_UNREAD))
4031 flags_unset |= IMAP_FLAG_SEEN;
4032 if ( MSG_IS_UNREAD(msginfo->flags) && !(newflags & MSG_UNREAD))
4033 flags_set |= IMAP_FLAG_SEEN;
4035 if (!MSG_IS_REPLIED(msginfo->flags) && (newflags & MSG_REPLIED))
4036 flags_set |= IMAP_FLAG_ANSWERED;
4037 if ( MSG_IS_REPLIED(msginfo->flags) && !(newflags & MSG_REPLIED))
4038 flags_set |= IMAP_FLAG_ANSWERED;
4040 numlist.next = NULL;
4041 numlist.data = GINT_TO_POINTER(msginfo->msgnum);
4044 ok = imap_set_message_flags(session, &numlist, flags_set, TRUE);
4045 if (ok != IMAP_SUCCESS) return;
4049 ok = imap_set_message_flags(session, &numlist, flags_unset, FALSE);
4050 if (ok != IMAP_SUCCESS) return;
4053 msginfo->flags.perm_flags = newflags;