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 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);
1247 g_ptr_array_free(argbuf, TRUE);
1252 item = FOLDER_ITEM(folder->node->data);
1253 if (!item || ((item->path || root_folder) &&
1254 strcmp2(item->path, root_folder) != 0)) {
1255 folder_tree_destroy(folder);
1256 item = folder_item_new(folder, folder->name, root_folder);
1257 item->folder = folder;
1258 folder->node = item->node = g_node_new(item);
1261 imap_scan_tree_recursive(session, FOLDER_ITEM(folder->node->data));
1262 imap_create_missing_folders(folder);
1267 static gint imap_scan_tree_recursive(IMAPSession *session, FolderItem *item)
1270 IMAPFolder *imapfolder;
1271 FolderItem *new_item;
1272 GSList *item_list, *cur;
1275 gchar *wildcard_path;
1279 g_return_val_if_fail(item != NULL, -1);
1280 g_return_val_if_fail(item->folder != NULL, -1);
1281 g_return_val_if_fail(item->no_sub == FALSE, -1);
1283 folder = item->folder;
1284 imapfolder = IMAP_FOLDER(folder);
1286 separator = imap_get_path_separator(imapfolder, item->path);
1288 if (folder->ui_func)
1289 folder->ui_func(folder, item, folder->ui_func_data);
1292 wildcard[0] = separator;
1295 real_path = imap_get_real_path(imapfolder, item->path);
1299 real_path = g_strdup("");
1302 Xstrcat_a(wildcard_path, real_path, wildcard,
1303 {g_free(real_path); return IMAP_ERROR;});
1304 QUOTE_IF_REQUIRED(wildcard_path, wildcard_path);
1306 imap_gen_send(session, "LIST \"\" %s",
1309 strtailchomp(real_path, separator);
1310 item_list = imap_parse_list(imapfolder, session, real_path, NULL);
1313 node = item->node->children;
1314 while (node != NULL) {
1315 FolderItem *old_item = FOLDER_ITEM(node->data);
1316 GNode *next = node->next;
1319 for (cur = item_list; cur != NULL; cur = cur->next) {
1320 FolderItem *cur_item = FOLDER_ITEM(cur->data);
1321 if (!strcmp2(old_item->path, cur_item->path)) {
1322 new_item = cur_item;
1327 debug_print("folder '%s' not found. removing...\n",
1329 folder_item_remove(old_item);
1331 old_item->no_sub = new_item->no_sub;
1332 old_item->no_select = new_item->no_select;
1333 if (old_item->no_sub == TRUE && node->children) {
1334 debug_print("folder '%s' doesn't have "
1335 "subfolders. removing...\n",
1337 folder_item_remove_children(old_item);
1344 for (cur = item_list; cur != NULL; cur = cur->next) {
1345 FolderItem *cur_item = FOLDER_ITEM(cur->data);
1347 for (node = item->node->children; node != NULL;
1348 node = node->next) {
1349 if (!strcmp2(FOLDER_ITEM(node->data)->path,
1351 new_item = FOLDER_ITEM(node->data);
1352 folder_item_destroy(cur_item);
1358 new_item = cur_item;
1359 debug_print("new folder '%s' found.\n", new_item->path);
1360 folder_item_append(item, new_item);
1363 if (!strcmp(new_item->path, "INBOX")) {
1364 new_item->stype = F_INBOX;
1365 folder->inbox = new_item;
1366 } else if (!item->parent || item->stype == F_INBOX) {
1369 base = g_basename(new_item->path);
1371 if (!folder->outbox && !strcasecmp(base, "Sent")) {
1372 new_item->stype = F_OUTBOX;
1373 folder->outbox = new_item;
1374 } else if (!folder->draft && !strcasecmp(base, "Drafts")) {
1375 new_item->stype = F_DRAFT;
1376 folder->draft = new_item;
1377 } else if (!folder->queue && !strcasecmp(base, "Queue")) {
1378 new_item->stype = F_QUEUE;
1379 folder->queue = new_item;
1380 } else if (!folder->trash && !strcasecmp(base, "Trash")) {
1381 new_item->stype = F_TRASH;
1382 folder->trash = new_item;
1386 if (new_item->no_sub == FALSE)
1387 imap_scan_tree_recursive(session, new_item);
1390 g_slist_free(item_list);
1392 return IMAP_SUCCESS;
1395 static GSList *imap_parse_list(IMAPFolder *folder, IMAPSession *session,
1396 const gchar *real_path, gchar *separator)
1398 gchar buf[IMAPBUFSIZE];
1400 gchar separator_str[16];
1403 gchar *loc_name, *loc_path;
1404 GSList *item_list = NULL;
1406 FolderItem *new_item;
1408 debug_print("getting list of %s ...\n",
1409 *real_path ? real_path : "\"\"");
1411 str = g_string_new(NULL);
1414 if (sock_gets(SESSION(session)->sock, buf, sizeof(buf)) <= 0) {
1415 log_warning(_("error occurred while getting LIST.\n"));
1419 if (buf[0] != '*' || buf[1] != ' ') {
1420 log_print("IMAP4< %s\n", buf);
1421 if (sscanf(buf, "%*d %16s", buf) < 1 ||
1422 strcmp(buf, "OK") != 0)
1423 log_warning(_("error occurred while getting LIST.\n"));
1427 debug_print("IMAP4< %s\n", buf);
1429 g_string_assign(str, buf);
1431 if (strncmp(p, "LIST ", 5) != 0) continue;
1434 if (*p != '(') continue;
1436 p = strchr_cpy(p, ')', flags, sizeof(flags));
1438 while (*p == ' ') p++;
1440 p = strchr_cpy(p, ' ', separator_str, sizeof(separator_str));
1442 extract_quote(separator_str, '"');
1443 if (!strcmp(separator_str, "NIL"))
1444 separator_str[0] = '\0';
1446 *separator = separator_str[0];
1449 while (*p == ' ') p++;
1450 if (*p == '{' || *p == '"')
1451 p = imap_parse_atom(SESSION(session)->sock, p,
1452 buf, sizeof(buf), str);
1454 strncpy2(buf, p, sizeof(buf));
1455 strtailchomp(buf, separator_str[0]);
1456 if (buf[0] == '\0') continue;
1457 if (!strcmp(buf, real_path)) continue;
1459 if (separator_str[0] != '\0')
1460 subst_char(buf, separator_str[0], '/');
1461 name = g_basename(buf);
1462 if (name[0] == '.') continue;
1464 loc_name = imap_modified_utf7_to_locale(name);
1465 loc_path = imap_modified_utf7_to_locale(buf);
1466 new_item = folder_item_new(FOLDER(folder), loc_name, loc_path);
1467 if (strcasestr(flags, "\\Noinferiors") != NULL)
1468 new_item->no_sub = TRUE;
1469 if (strcmp(buf, "INBOX") != 0 &&
1470 strcasestr(flags, "\\Noselect") != NULL)
1471 new_item->no_select = TRUE;
1473 item_list = g_slist_append(item_list, new_item);
1475 debug_print("folder '%s' found.\n", loc_path);
1480 g_string_free(str, TRUE);
1485 static gint imap_create_tree(Folder *folder)
1487 g_return_val_if_fail(folder != NULL, -1);
1488 g_return_val_if_fail(folder->node != NULL, -1);
1489 g_return_val_if_fail(folder->node->data != NULL, -1);
1490 g_return_val_if_fail(folder->account != NULL, -1);
1492 imap_scan_tree(folder);
1493 imap_create_missing_folders(folder);
1498 static void imap_create_missing_folders(Folder *folder)
1500 g_return_if_fail(folder != NULL);
1503 folder->inbox = imap_create_special_folder
1504 (folder, F_INBOX, "INBOX");
1506 if (!folder->outbox)
1507 folder->outbox = imap_create_special_folder
1508 (folder, F_OUTBOX, "Sent");
1510 folder->draft = imap_create_special_folder
1511 (folder, F_DRAFT, "Drafts");
1513 folder->queue = imap_create_special_folder
1514 (folder, F_QUEUE, "Queue");
1517 folder->trash = imap_create_special_folder
1518 (folder, F_TRASH, "Trash");
1521 static FolderItem *imap_create_special_folder(Folder *folder,
1522 SpecialFolderItemType stype,
1526 FolderItem *new_item;
1528 g_return_val_if_fail(folder != NULL, NULL);
1529 g_return_val_if_fail(folder->node != NULL, NULL);
1530 g_return_val_if_fail(folder->node->data != NULL, NULL);
1531 g_return_val_if_fail(folder->account != NULL, NULL);
1532 g_return_val_if_fail(name != NULL, NULL);
1534 item = FOLDER_ITEM(folder->node->data);
1535 new_item = imap_create_folder(folder, item, name);
1538 g_warning("Can't create '%s'\n", name);
1539 if (!folder->inbox) return NULL;
1541 new_item = imap_create_folder(folder, folder->inbox, name);
1543 g_warning("Can't create '%s' under INBOX\n", name);
1545 new_item->stype = stype;
1547 new_item->stype = stype;
1552 static gchar *imap_folder_get_path(Folder *folder)
1556 g_return_val_if_fail(folder != NULL, NULL);
1557 g_return_val_if_fail(folder->account != NULL, NULL);
1559 folder_path = g_strconcat(get_imap_cache_dir(),
1561 folder->account->recv_server,
1563 folder->account->userid,
1569 static gchar *imap_item_get_path(Folder *folder, FolderItem *item)
1571 gchar *folder_path, *path;
1573 g_return_val_if_fail(folder != NULL, NULL);
1574 g_return_val_if_fail(item != NULL, NULL);
1575 folder_path = imap_folder_get_path(folder);
1577 g_return_val_if_fail(folder_path != NULL, NULL);
1578 if (folder_path[0] == G_DIR_SEPARATOR) {
1580 path = g_strconcat(folder_path, G_DIR_SEPARATOR_S,
1583 path = g_strdup(folder_path);
1586 path = g_strconcat(get_home_dir(), G_DIR_SEPARATOR_S,
1587 folder_path, G_DIR_SEPARATOR_S,
1590 path = g_strconcat(get_home_dir(), G_DIR_SEPARATOR_S,
1593 g_free(folder_path);
1598 static FolderItem *imap_create_folder(Folder *folder, FolderItem *parent,
1601 gchar *dirpath, *imap_path;
1602 IMAPSession *session;
1603 FolderItem *new_item;
1609 g_return_val_if_fail(folder != NULL, NULL);
1610 g_return_val_if_fail(folder->account != NULL, NULL);
1611 g_return_val_if_fail(parent != NULL, NULL);
1612 g_return_val_if_fail(name != NULL, NULL);
1614 session = imap_session_get(folder);
1615 if (!session) return NULL;
1617 if (!parent->parent && strcmp(name, "INBOX") == 0)
1618 dirpath = g_strdup(name);
1619 else if (parent->path)
1620 dirpath = g_strconcat(parent->path, "/", name, NULL);
1621 else if ((p = strchr(name, '/')) != NULL && *(p + 1) != '\0')
1622 dirpath = g_strdup(name);
1623 else if (folder->account->imap_dir && *folder->account->imap_dir) {
1626 Xstrdup_a(imap_dir, folder->account->imap_dir, return NULL);
1627 strtailchomp(imap_dir, '/');
1628 dirpath = g_strconcat(imap_dir, "/", name, NULL);
1630 dirpath = g_strdup(name);
1632 /* keep trailing directory separator to create a folder that contains
1634 imap_path = imap_locale_to_modified_utf7(dirpath);
1635 strtailchomp(dirpath, '/');
1636 Xstrdup_a(new_name, name, {g_free(dirpath); return NULL;});
1637 strtailchomp(new_name, '/');
1638 separator = imap_get_path_separator(IMAP_FOLDER(folder), imap_path);
1639 imap_path_separator_subst(imap_path, separator);
1640 subst_char(new_name, '/', separator);
1642 if (strcmp(name, "INBOX") != 0) {
1645 gboolean exist = FALSE;
1647 argbuf = g_ptr_array_new();
1648 ok = imap_cmd_list(session, NULL, imap_path,
1650 if (ok != IMAP_SUCCESS) {
1651 log_warning(_("can't create mailbox: LIST failed\n"));
1654 ptr_array_free_strings(argbuf);
1655 g_ptr_array_free(argbuf, TRUE);
1659 for (i = 0; i < argbuf->len; i++) {
1661 str = g_ptr_array_index(argbuf, i);
1662 if (!strncmp(str, "LIST ", 5)) {
1667 ptr_array_free_strings(argbuf);
1668 g_ptr_array_free(argbuf, TRUE);
1671 ok = imap_cmd_create(session, imap_path);
1672 if (ok != IMAP_SUCCESS) {
1673 log_warning(_("can't create mailbox\n"));
1681 new_item = folder_item_new(folder, new_name, dirpath);
1682 folder_item_append(parent, new_item);
1686 dirpath = folder_item_get_path(new_item);
1687 if (!is_dir_exist(dirpath))
1688 make_dir_hier(dirpath);
1694 static gint imap_rename_folder(Folder *folder, FolderItem *item,
1699 gchar *real_oldpath;
1700 gchar *real_newpath;
1702 gchar *old_cache_dir;
1703 gchar *new_cache_dir;
1704 IMAPSession *session;
1707 gint exists, recent, unseen;
1708 guint32 uid_validity;
1710 g_return_val_if_fail(folder != NULL, -1);
1711 g_return_val_if_fail(item != NULL, -1);
1712 g_return_val_if_fail(item->path != NULL, -1);
1713 g_return_val_if_fail(name != NULL, -1);
1715 session = imap_session_get(folder);
1716 if (!session) return -1;
1718 real_oldpath = imap_get_real_path(IMAP_FOLDER(folder), item->path);
1720 g_free(session->mbox);
1721 session->mbox = NULL;
1722 ok = imap_cmd_examine(session, "INBOX",
1723 &exists, &recent, &unseen, &uid_validity);
1724 if (ok != IMAP_SUCCESS) {
1725 g_free(real_oldpath);
1729 separator = imap_get_path_separator(IMAP_FOLDER(folder), item->path);
1730 if (strchr(item->path, G_DIR_SEPARATOR)) {
1731 dirpath = g_dirname(item->path);
1732 newpath = g_strconcat(dirpath, G_DIR_SEPARATOR_S, name, NULL);
1735 newpath = g_strdup(name);
1737 real_newpath = imap_locale_to_modified_utf7(newpath);
1738 imap_path_separator_subst(real_newpath, separator);
1740 ok = imap_cmd_rename(session, real_oldpath, real_newpath);
1741 if (ok != IMAP_SUCCESS) {
1742 log_warning(_("can't rename mailbox: %s to %s\n"),
1743 real_oldpath, real_newpath);
1744 g_free(real_oldpath);
1746 g_free(real_newpath);
1751 item->name = g_strdup(name);
1753 old_cache_dir = folder_item_get_path(item);
1755 paths[0] = g_strdup(item->path);
1757 g_node_traverse(item->node, G_PRE_ORDER, G_TRAVERSE_ALL, -1,
1758 imap_rename_folder_func, paths);
1760 if (is_dir_exist(old_cache_dir)) {
1761 new_cache_dir = folder_item_get_path(item);
1762 if (rename(old_cache_dir, new_cache_dir) < 0) {
1763 FILE_OP_ERROR(old_cache_dir, "rename");
1765 g_free(new_cache_dir);
1768 g_free(old_cache_dir);
1771 g_free(real_oldpath);
1772 g_free(real_newpath);
1777 static gint imap_remove_folder(Folder *folder, FolderItem *item)
1780 IMAPSession *session;
1783 gint exists, recent, unseen;
1784 guint32 uid_validity;
1786 g_return_val_if_fail(folder != NULL, -1);
1787 g_return_val_if_fail(item != NULL, -1);
1788 g_return_val_if_fail(item->path != NULL, -1);
1790 session = imap_session_get(folder);
1791 if (!session) return -1;
1793 path = imap_get_real_path(IMAP_FOLDER(folder), item->path);
1795 ok = imap_cmd_examine(session, "INBOX",
1796 &exists, &recent, &unseen, &uid_validity);
1797 if (ok != IMAP_SUCCESS) {
1802 ok = imap_cmd_delete(session, path);
1803 if (ok != IMAP_SUCCESS) {
1804 log_warning(_("can't delete mailbox\n"));
1810 cache_dir = folder_item_get_path(item);
1811 if (is_dir_exist(cache_dir) && remove_dir_recursive(cache_dir) < 0)
1812 g_warning("can't remove directory '%s'\n", cache_dir);
1814 folder_item_remove(item);
1819 static GSList *imap_get_uncached_messages(IMAPSession *session,
1821 MsgNumberList *numlist)
1824 GSList *newlist = NULL;
1825 GSList *llast = NULL;
1828 GSList *seq_list, *cur;
1831 g_return_val_if_fail(session != NULL, NULL);
1832 g_return_val_if_fail(item != NULL, NULL);
1833 g_return_val_if_fail(item->folder != NULL, NULL);
1834 g_return_val_if_fail(FOLDER_CLASS(item->folder) == &imap_class, NULL);
1836 seq_list = imap_get_seq_set_from_numlist(numlist);
1837 for (cur = seq_list; cur != NULL; cur = g_slist_next(cur)) {
1838 imapset = cur->data;
1840 if (imap_cmd_envelope(session, imapset)
1842 log_warning(_("can't get envelope\n"));
1846 str = g_string_new(NULL);
1849 if ((tmp = sock_getline(SESSION(session)->sock)) == NULL) {
1850 log_warning(_("error occurred while getting envelope.\n"));
1851 g_string_free(str, TRUE);
1855 if (tmp[0] != '*' || tmp[1] != ' ') {
1856 log_print("IMAP4< %s\n", tmp);
1860 if (strstr(tmp, "FETCH") == NULL) {
1861 log_print("IMAP4< %s\n", tmp);
1865 log_print("IMAP4< %s\n", tmp);
1866 g_string_assign(str, tmp);
1869 msginfo = imap_parse_envelope
1870 (SESSION(session)->sock, item, str);
1872 log_warning(_("can't parse envelope: %s\n"), str->str);
1875 if (item->stype == F_QUEUE) {
1876 MSG_SET_TMP_FLAGS(msginfo->flags, MSG_QUEUED);
1877 } else if (item->stype == F_DRAFT) {
1878 MSG_SET_TMP_FLAGS(msginfo->flags, MSG_DRAFT);
1881 msginfo->folder = item;
1884 llast = newlist = g_slist_append(newlist, msginfo);
1886 llast = g_slist_append(llast, msginfo);
1887 llast = llast->next;
1891 g_string_free(str, TRUE);
1893 imap_seq_set_free(seq_list);
1898 static void imap_delete_all_cached_messages(FolderItem *item)
1902 g_return_if_fail(item != NULL);
1903 g_return_if_fail(item->folder != NULL);
1904 g_return_if_fail(FOLDER_CLASS(item->folder) == &imap_class);
1906 debug_print("Deleting all cached messages...\n");
1908 dir = folder_item_get_path(item);
1909 if (is_dir_exist(dir))
1910 remove_all_numbered_files(dir);
1913 debug_print("done.\n");
1917 static SockInfo *imap_open_tunnel(const gchar *server,
1918 const gchar *tunnelcmd,
1921 static SockInfo *imap_open_tunnel(const gchar *server,
1922 const gchar *tunnelcmd)
1927 if ((sock = sock_connect_cmd(server, tunnelcmd)) == NULL) {
1928 log_warning(_("Can't establish IMAP4 session with: %s\n"),
1933 return imap_init_sock(sock, ssl_type);
1935 return imap_init_sock(sock);
1941 static SockInfo *imap_open(const gchar *server, gushort port,
1944 static SockInfo *imap_open(const gchar *server, gushort port)
1949 if ((sock = sock_connect(server, port)) == NULL) {
1950 log_warning(_("Can't connect to IMAP4 server: %s:%d\n"),
1956 if (ssl_type == SSL_TUNNEL && !ssl_init_socket(sock)) {
1957 log_warning(_("Can't establish IMAP4 session with: %s:%d\n"),
1967 static SockInfo *imap_init_sock(SockInfo *sock, SSLType ssl_type)
1969 static SockInfo *imap_init_sock(SockInfo *sock)
1976 static GList *imap_parse_namespace_str(gchar *str)
1981 IMAPNameSpace *namespace;
1982 GList *ns_list = NULL;
1984 while (*p != '\0') {
1985 /* parse ("#foo" "/") */
1987 while (*p && *p != '(') p++;
1988 if (*p == '\0') break;
1991 while (*p && *p != '"') p++;
1992 if (*p == '\0') break;
1996 while (*p && *p != '"') p++;
1997 if (*p == '\0') break;
2001 while (*p && isspace(*p)) p++;
2002 if (*p == '\0') break;
2003 if (strncmp(p, "NIL", 3) == 0)
2005 else if (*p == '"') {
2008 while (*p && *p != '"') p++;
2009 if (*p == '\0') break;
2014 while (*p && *p != ')') p++;
2015 if (*p == '\0') break;
2018 namespace = g_new(IMAPNameSpace, 1);
2019 namespace->name = g_strdup(name);
2020 namespace->separator = separator ? separator[0] : '\0';
2021 ns_list = g_list_append(ns_list, namespace);
2027 static void imap_parse_namespace(IMAPSession *session, IMAPFolder *folder)
2032 g_return_if_fail(session != NULL);
2033 g_return_if_fail(folder != NULL);
2035 if (folder->ns_personal != NULL ||
2036 folder->ns_others != NULL ||
2037 folder->ns_shared != NULL)
2040 if (!imap_has_capability(session, "NAMESPACE")) {
2041 imap_get_namespace_by_list(session, folder);
2045 if (imap_cmd_namespace(session, &ns_str)
2047 log_warning(_("can't get namespace\n"));
2051 str_array = strsplit_parenthesis(ns_str, '(', ')', 3);
2052 if (str_array == NULL) {
2054 imap_get_namespace_by_list(session, folder);
2058 folder->ns_personal = imap_parse_namespace_str(str_array[0]);
2059 if (str_array[0] && str_array[1])
2060 folder->ns_others = imap_parse_namespace_str(str_array[1]);
2061 if (str_array[0] && str_array[1] && str_array[2])
2062 folder->ns_shared = imap_parse_namespace_str(str_array[2]);
2063 g_strfreev(str_array);
2067 static void imap_get_namespace_by_list(IMAPSession *session, IMAPFolder *folder)
2069 GSList *item_list, *cur;
2070 gchar separator = '\0';
2071 IMAPNameSpace *namespace;
2073 g_return_if_fail(session != NULL);
2074 g_return_if_fail(folder != NULL);
2076 if (folder->ns_personal != NULL ||
2077 folder->ns_others != NULL ||
2078 folder->ns_shared != NULL)
2081 imap_gen_send(session, "LIST \"\" \"\"");
2082 item_list = imap_parse_list(folder, session, "", &separator);
2083 for (cur = item_list; cur != NULL; cur = cur->next)
2084 folder_item_destroy(FOLDER_ITEM(cur->data));
2085 g_slist_free(item_list);
2087 namespace = g_new(IMAPNameSpace, 1);
2088 namespace->name = g_strdup("");
2089 namespace->separator = separator;
2090 folder->ns_personal = g_list_append(NULL, namespace);
2093 static IMAPNameSpace *imap_find_namespace_from_list(GList *ns_list,
2096 IMAPNameSpace *namespace = NULL;
2097 gchar *tmp_path, *name;
2099 if (!path) path = "";
2101 for (; ns_list != NULL; ns_list = ns_list->next) {
2102 IMAPNameSpace *tmp_ns = ns_list->data;
2104 Xstrcat_a(tmp_path, path, "/", return namespace);
2105 Xstrdup_a(name, tmp_ns->name, return namespace);
2106 if (tmp_ns->separator && tmp_ns->separator != '/') {
2107 subst_char(tmp_path, tmp_ns->separator, '/');
2108 subst_char(name, tmp_ns->separator, '/');
2110 if (strncmp(tmp_path, name, strlen(name)) == 0)
2117 static IMAPNameSpace *imap_find_namespace(IMAPFolder *folder,
2120 IMAPNameSpace *namespace;
2122 g_return_val_if_fail(folder != NULL, NULL);
2124 namespace = imap_find_namespace_from_list(folder->ns_personal, path);
2125 if (namespace) return namespace;
2126 namespace = imap_find_namespace_from_list(folder->ns_others, path);
2127 if (namespace) return namespace;
2128 namespace = imap_find_namespace_from_list(folder->ns_shared, path);
2129 if (namespace) return namespace;
2134 static gchar imap_get_path_separator(IMAPFolder *folder, const gchar *path)
2136 IMAPNameSpace *namespace;
2137 gchar separator = '/';
2139 namespace = imap_find_namespace(folder, path);
2140 if (namespace && namespace->separator)
2141 separator = namespace->separator;
2146 static gchar *imap_get_real_path(IMAPFolder *folder, const gchar *path)
2151 g_return_val_if_fail(folder != NULL, NULL);
2152 g_return_val_if_fail(path != NULL, NULL);
2154 real_path = imap_locale_to_modified_utf7(path);
2155 separator = imap_get_path_separator(folder, path);
2156 imap_path_separator_subst(real_path, separator);
2161 static gchar *imap_parse_atom(SockInfo *sock, gchar *src,
2162 gchar *dest, gint dest_len, GString *str)
2164 gchar *cur_pos = src;
2167 g_return_val_if_fail(str != NULL, cur_pos);
2169 /* read the next line if the current response buffer is empty */
2170 while (isspace(*cur_pos)) cur_pos++;
2171 while (*cur_pos == '\0') {
2172 if ((nextline = sock_getline(sock)) == NULL)
2174 g_string_assign(str, nextline);
2176 strretchomp(nextline);
2177 /* log_print("IMAP4< %s\n", nextline); */
2178 debug_print("IMAP4< %s\n", nextline);
2181 while (isspace(*cur_pos)) cur_pos++;
2184 if (!strncmp(cur_pos, "NIL", 3)) {
2187 } else if (*cur_pos == '\"') {
2190 p = get_quoted(cur_pos, '\"', dest, dest_len);
2191 cur_pos = p ? p : cur_pos + 2;
2192 } else if (*cur_pos == '{') {
2197 cur_pos = strchr_cpy(cur_pos + 1, '}', buf, sizeof(buf));
2199 g_return_val_if_fail(len >= 0, cur_pos);
2201 g_string_truncate(str, 0);
2205 if ((nextline = sock_getline(sock)) == NULL)
2207 line_len += strlen(nextline);
2208 g_string_append(str, nextline);
2210 strretchomp(nextline);
2211 /* log_print("IMAP4< %s\n", nextline); */
2212 debug_print("IMAP4< %s\n", nextline);
2214 } while (line_len < len);
2216 memcpy(dest, cur_pos, MIN(len, dest_len - 1));
2217 dest[MIN(len, dest_len - 1)] = '\0';
2224 static gchar *imap_get_header(SockInfo *sock, gchar *cur_pos, gchar **headers,
2234 g_return_val_if_fail(str != NULL, cur_pos);
2236 while (isspace(*cur_pos)) cur_pos++;
2238 g_return_val_if_fail(*cur_pos == '{', cur_pos);
2240 cur_pos = strchr_cpy(cur_pos + 1, '}', buf, sizeof(buf));
2242 g_return_val_if_fail(len >= 0, cur_pos);
2244 g_string_truncate(str, 0);
2248 if ((nextline = sock_getline(sock)) == NULL)
2250 block_len += strlen(nextline);
2251 g_string_append(str, nextline);
2253 strretchomp(nextline);
2254 /* debug_print("IMAP4< %s\n", nextline); */
2256 } while (block_len < len);
2258 debug_print("IMAP4< [contents of BODY.PEEK[HEADER.FIELDS (...)]]\n");
2260 *headers = g_strndup(cur_pos, len);
2263 while (isspace(*cur_pos)) cur_pos++;
2264 while (*cur_pos == '\0') {
2265 if ((nextline = sock_getline(sock)) == NULL)
2267 g_string_assign(str, nextline);
2269 strretchomp(nextline);
2270 debug_print("IMAP4< %s\n", nextline);
2273 while (isspace(*cur_pos)) cur_pos++;
2279 static MsgFlags imap_parse_flags(const gchar *flag_str)
2281 const gchar *p = flag_str;
2282 MsgFlags flags = {0, 0};
2284 flags.perm_flags = MSG_UNREAD;
2286 while ((p = strchr(p, '\\')) != NULL) {
2289 if (g_strncasecmp(p, "Recent", 6) == 0 && MSG_IS_UNREAD(flags)) {
2290 MSG_SET_PERM_FLAGS(flags, MSG_NEW);
2291 } else if (g_strncasecmp(p, "Seen", 4) == 0) {
2292 MSG_UNSET_PERM_FLAGS(flags, MSG_NEW|MSG_UNREAD);
2293 } else if (g_strncasecmp(p, "Deleted", 7) == 0) {
2294 MSG_SET_PERM_FLAGS(flags, MSG_DELETED);
2295 } else if (g_strncasecmp(p, "Flagged", 7) == 0) {
2296 MSG_SET_PERM_FLAGS(flags, MSG_MARKED);
2297 } else if (g_strncasecmp(p, "Answered", 8) == 0) {
2298 MSG_SET_PERM_FLAGS(flags, MSG_REPLIED);
2305 static MsgInfo *imap_parse_envelope(SockInfo *sock, FolderItem *item,
2308 gchar buf[IMAPBUFSIZE];
2309 MsgInfo *msginfo = NULL;
2314 MsgFlags flags = {0, 0}, imap_flags = {0, 0};
2316 g_return_val_if_fail(line_str != NULL, NULL);
2317 g_return_val_if_fail(line_str->str[0] == '*' &&
2318 line_str->str[1] == ' ', NULL);
2320 MSG_SET_TMP_FLAGS(flags, MSG_IMAP);
2321 if (item->stype == F_QUEUE) {
2322 MSG_SET_TMP_FLAGS(flags, MSG_QUEUED);
2323 } else if (item->stype == F_DRAFT) {
2324 MSG_SET_TMP_FLAGS(flags, MSG_DRAFT);
2327 cur_pos = line_str->str + 2;
2329 #define PARSE_ONE_ELEMENT(ch) \
2331 cur_pos = strchr_cpy(cur_pos, ch, buf, sizeof(buf)); \
2332 if (cur_pos == NULL) { \
2333 g_warning("cur_pos == NULL\n"); \
2334 procmsg_msginfo_free(msginfo); \
2339 PARSE_ONE_ELEMENT(' ');
2342 PARSE_ONE_ELEMENT(' ');
2343 g_return_val_if_fail(!strcmp(buf, "FETCH"), NULL);
2345 g_return_val_if_fail(*cur_pos == '(', NULL);
2348 while (*cur_pos != '\0' && *cur_pos != ')') {
2349 while (*cur_pos == ' ') cur_pos++;
2351 if (!strncmp(cur_pos, "UID ", 4)) {
2353 uid = strtoul(cur_pos, &cur_pos, 10);
2354 } else if (!strncmp(cur_pos, "FLAGS ", 6)) {
2356 if (*cur_pos != '(') {
2357 g_warning("*cur_pos != '('\n");
2358 procmsg_msginfo_free(msginfo);
2362 PARSE_ONE_ELEMENT(')');
2363 imap_flags = imap_parse_flags(buf);
2364 } else if (!strncmp(cur_pos, "RFC822.SIZE ", 12)) {
2366 size = strtol(cur_pos, &cur_pos, 10);
2367 } else if (!strncmp(cur_pos, "BODY[HEADER.FIELDS ", 19)) {
2371 if (*cur_pos != '(') {
2372 g_warning("*cur_pos != '('\n");
2373 procmsg_msginfo_free(msginfo);
2377 PARSE_ONE_ELEMENT(')');
2378 if (*cur_pos != ']') {
2379 g_warning("*cur_pos != ']'\n");
2380 procmsg_msginfo_free(msginfo);
2385 cur_pos = imap_get_header(sock, cur_pos, &headers,
2387 msginfo = procheader_parse_str(headers, flags, FALSE, FALSE);
2390 g_warning("invalid FETCH response: %s\n", cur_pos);
2396 msginfo->msgnum = uid;
2397 msginfo->size = size;
2398 msginfo->flags.tmp_flags |= imap_flags.tmp_flags;
2399 msginfo->flags.perm_flags = imap_flags.perm_flags;
2405 static gchar *imap_get_flag_str(IMAPFlags flags)
2410 str = g_string_new(NULL);
2412 if (IMAP_IS_SEEN(flags)) g_string_append(str, "\\Seen ");
2413 if (IMAP_IS_ANSWERED(flags)) g_string_append(str, "\\Answered ");
2414 if (IMAP_IS_FLAGGED(flags)) g_string_append(str, "\\Flagged ");
2415 if (IMAP_IS_DELETED(flags)) g_string_append(str, "\\Deleted ");
2416 if (IMAP_IS_DRAFT(flags)) g_string_append(str, "\\Draft");
2418 if (str->len > 0 && str->str[str->len - 1] == ' ')
2419 g_string_truncate(str, str->len - 1);
2422 g_string_free(str, FALSE);
2427 static gint imap_set_message_flags(IMAPSession *session,
2428 MsgNumberList *numlist,
2435 GSList *seq_list, *cur;
2438 flag_str = imap_get_flag_str(flags);
2439 cmd = g_strconcat(is_set ? "+FLAGS.SILENT (" : "-FLAGS.SILENT (",
2440 flag_str, ")", NULL);
2443 seq_list = imap_get_seq_set_from_numlist(numlist);
2444 for (cur = seq_list; cur != NULL; cur = g_slist_next(cur)) {
2445 imapset = cur->data;
2447 ok = imap_cmd_store(session, imapset, cmd);
2449 imap_seq_set_free(seq_list);
2455 static gint imap_select(IMAPSession *session, IMAPFolder *folder,
2457 gint *exists, gint *recent, gint *unseen,
2458 guint32 *uid_validity)
2462 gint exists_, recent_, unseen_, uid_validity_;
2464 if (!exists || !recent || !unseen || !uid_validity) {
2465 if (session->mbox && strcmp(session->mbox, path) == 0)
2466 return IMAP_SUCCESS;
2470 uid_validity = &uid_validity_;
2473 g_free(session->mbox);
2474 session->mbox = NULL;
2476 real_path = imap_get_real_path(folder, path);
2477 ok = imap_cmd_select(session, real_path,
2478 exists, recent, unseen, uid_validity);
2479 if (ok != IMAP_SUCCESS)
2480 log_warning(_("can't select folder: %s\n"), real_path);
2482 session->mbox = g_strdup(path);
2483 session->folder_content_changed = FALSE;
2490 #define THROW(err) { ok = err; goto catch; }
2492 static gint imap_status(IMAPSession *session, IMAPFolder *folder,
2494 gint *messages, gint *recent,
2495 guint32 *uid_next, guint32 *uid_validity,
2501 GPtrArray *argbuf = NULL;
2504 if (messages && recent && uid_next && uid_validity && unseen) {
2505 *messages = *recent = *uid_next = *uid_validity = *unseen = 0;
2506 argbuf = g_ptr_array_new();
2509 real_path = imap_get_real_path(folder, path);
2510 QUOTE_IF_REQUIRED(real_path_, real_path);
2511 imap_gen_send(session, "STATUS %s "
2512 "(MESSAGES RECENT UIDNEXT UIDVALIDITY UNSEEN)",
2515 ok = imap_cmd_ok(session, argbuf);
2516 if (ok != IMAP_SUCCESS || !argbuf) THROW(ok);
2518 str = search_array_str(argbuf, "STATUS");
2519 if (!str) THROW(IMAP_ERROR);
2521 str = strchr(str, '(');
2522 if (!str) THROW(IMAP_ERROR);
2524 while (*str != '\0' && *str != ')') {
2525 while (*str == ' ') str++;
2527 if (!strncmp(str, "MESSAGES ", 9)) {
2529 *messages = strtol(str, &str, 10);
2530 } else if (!strncmp(str, "RECENT ", 7)) {
2532 *recent = strtol(str, &str, 10);
2533 } else if (!strncmp(str, "UIDNEXT ", 8)) {
2535 *uid_next = strtoul(str, &str, 10);
2536 } else if (!strncmp(str, "UIDVALIDITY ", 12)) {
2538 *uid_validity = strtoul(str, &str, 10);
2539 } else if (!strncmp(str, "UNSEEN ", 7)) {
2541 *unseen = strtol(str, &str, 10);
2543 g_warning("invalid STATUS response: %s\n", str);
2551 ptr_array_free_strings(argbuf);
2552 g_ptr_array_free(argbuf, TRUE);
2560 static gboolean imap_has_capability(IMAPSession *session, const gchar *cap)
2564 for (p = session->capability; *p != NULL; ++p) {
2565 if (!g_strcasecmp(*p, cap))
2572 static void imap_free_capabilities(IMAPSession *session)
2574 g_strfreev(session->capability);
2575 session->capability = NULL;
2578 /* low-level IMAP4rev1 commands */
2580 static gint imap_cmd_authenticate(IMAPSession *session, const gchar *user,
2581 const gchar *pass, IMAPAuthType type)
2588 gchar hexdigest[33];
2592 auth_type = "CRAM-MD5";
2594 imap_gen_send(session, "AUTHENTICATE %s", auth_type);
2595 ok = imap_gen_recv(session, &buf);
2596 if (ok != IMAP_SUCCESS || buf[0] != '+' || buf[1] != ' ') {
2601 challenge = g_malloc(strlen(buf + 2) + 1);
2602 challenge_len = base64_decode(challenge, buf + 2, -1);
2603 challenge[challenge_len] = '\0';
2605 log_print("IMAP< [Decoded: %s]\n", challenge);
2607 md5_hex_hmac(hexdigest, challenge, challenge_len, pass, strlen(pass));
2610 response = g_strdup_printf("%s %s", user, hexdigest);
2611 log_print("IMAP> [Encoded: %s]\n", response);
2612 response64 = g_malloc((strlen(response) + 3) * 2 + 1);
2613 base64_encode(response64, response, strlen(response));
2616 log_print("IMAP> %s\n", response64);
2617 sock_puts(SESSION(session)->sock, response64);
2618 ok = imap_cmd_ok(session, NULL);
2619 if (ok != IMAP_SUCCESS)
2620 log_warning(_("IMAP4 authentication failed.\n"));
2625 static gint imap_cmd_login(IMAPSession *session,
2626 const gchar *user, const gchar *pass)
2628 gchar *user_, *pass_;
2631 QUOTE_IF_REQUIRED(user_, user);
2632 QUOTE_IF_REQUIRED(pass_, pass);
2633 imap_gen_send(session, "LOGIN %s %s", user_, pass_);
2635 ok = imap_cmd_ok(session, NULL);
2636 if (ok != IMAP_SUCCESS)
2637 log_warning(_("IMAP4 login failed.\n"));
2642 static gint imap_cmd_logout(IMAPSession *session)
2644 imap_gen_send(session, "LOGOUT");
2645 return imap_cmd_ok(session, NULL);
2648 static gint imap_cmd_noop(IMAPSession *session)
2650 imap_gen_send(session, "NOOP");
2651 return imap_cmd_ok(session, NULL);
2654 static gint imap_cmd_starttls(IMAPSession *session)
2656 imap_gen_send(session, "STARTTLS");
2657 return imap_cmd_ok(session, NULL);
2660 #define THROW(err) { ok = err; goto catch; }
2662 static gint imap_cmd_namespace(IMAPSession *session, gchar **ns_str)
2668 argbuf = g_ptr_array_new();
2670 imap_gen_send(session, "NAMESPACE");
2671 if ((ok = imap_cmd_ok(session, argbuf)) != IMAP_SUCCESS) THROW(ok);
2673 str = search_array_str(argbuf, "NAMESPACE");
2674 if (!str) THROW(IMAP_ERROR);
2676 *ns_str = g_strdup(str);
2679 ptr_array_free_strings(argbuf);
2680 g_ptr_array_free(argbuf, TRUE);
2687 static gint imap_cmd_list(IMAPSession *session, const gchar *ref,
2688 const gchar *mailbox, GPtrArray *argbuf)
2690 gchar *ref_, *mailbox_;
2692 if (!ref) ref = "\"\"";
2693 if (!mailbox) mailbox = "\"\"";
2695 QUOTE_IF_REQUIRED(ref_, ref);
2696 QUOTE_IF_REQUIRED(mailbox_, mailbox);
2697 imap_gen_send(session, "LIST %s %s", ref_, mailbox_);
2699 return imap_cmd_ok(session, argbuf);
2702 #define THROW goto catch
2704 static gint imap_cmd_do_select(IMAPSession *session, const gchar *folder,
2706 gint *exists, gint *recent, gint *unseen,
2707 guint32 *uid_validity)
2715 *exists = *recent = *unseen = *uid_validity = 0;
2716 argbuf = g_ptr_array_new();
2719 select_cmd = "EXAMINE";
2721 select_cmd = "SELECT";
2723 QUOTE_IF_REQUIRED(folder_, folder);
2724 imap_gen_send(session, "%s %s", select_cmd, folder_);
2726 if ((ok = imap_cmd_ok(session, argbuf)) != IMAP_SUCCESS) THROW;
2728 resp_str = search_array_contain_str(argbuf, "EXISTS");
2730 if (sscanf(resp_str,"%d EXISTS", exists) != 1) {
2731 g_warning("imap_cmd_select(): invalid EXISTS line.\n");
2736 resp_str = search_array_contain_str(argbuf, "RECENT");
2738 if (sscanf(resp_str, "%d RECENT", recent) != 1) {
2739 g_warning("imap_cmd_select(): invalid RECENT line.\n");
2744 resp_str = search_array_contain_str(argbuf, "UIDVALIDITY");
2746 if (sscanf(resp_str, "OK [UIDVALIDITY %u] ", uid_validity)
2748 g_warning("imap_cmd_select(): invalid UIDVALIDITY line.\n");
2753 resp_str = search_array_contain_str(argbuf, "UNSEEN");
2755 if (sscanf(resp_str, "OK [UNSEEN %d] ", unseen) != 1) {
2756 g_warning("imap_cmd_select(): invalid UNSEEN line.\n");
2762 ptr_array_free_strings(argbuf);
2763 g_ptr_array_free(argbuf, TRUE);
2768 static gint imap_cmd_select(IMAPSession *session, const gchar *folder,
2769 gint *exists, gint *recent, gint *unseen,
2770 guint32 *uid_validity)
2772 return imap_cmd_do_select(session, folder, FALSE,
2773 exists, recent, unseen, uid_validity);
2776 static gint imap_cmd_examine(IMAPSession *session, const gchar *folder,
2777 gint *exists, gint *recent, gint *unseen,
2778 guint32 *uid_validity)
2780 return imap_cmd_do_select(session, folder, TRUE,
2781 exists, recent, unseen, uid_validity);
2786 static gint imap_cmd_create(IMAPSession *session, const gchar *folder)
2790 QUOTE_IF_REQUIRED(folder_, folder);
2791 imap_gen_send(session, "CREATE %s", folder_);
2793 return imap_cmd_ok(session, NULL);
2796 static gint imap_cmd_rename(IMAPSession *session, const gchar *old_folder,
2797 const gchar *new_folder)
2799 gchar *old_folder_, *new_folder_;
2801 QUOTE_IF_REQUIRED(old_folder_, old_folder);
2802 QUOTE_IF_REQUIRED(new_folder_, new_folder);
2803 imap_gen_send(session, "RENAME %s %s", old_folder_, new_folder_);
2805 return imap_cmd_ok(session, NULL);
2808 static gint imap_cmd_delete(IMAPSession *session, const gchar *folder)
2812 QUOTE_IF_REQUIRED(folder_, folder);
2813 imap_gen_send(session, "DELETE %s", folder_);
2815 return imap_cmd_ok(session, NULL);
2818 static gint imap_cmd_search(IMAPSession *session, const gchar *criteria,
2825 g_return_val_if_fail(criteria != NULL, IMAP_ERROR);
2826 g_return_val_if_fail(list != NULL, IMAP_ERROR);
2830 argbuf = g_ptr_array_new();
2831 imap_gen_send(session, "UID SEARCH %s", criteria);
2833 ok = imap_cmd_ok(session, argbuf);
2834 if (ok != IMAP_SUCCESS) {
2835 ptr_array_free_strings(argbuf);
2836 g_ptr_array_free(argbuf, TRUE);
2840 if ((uidlist = search_array_str(argbuf, "SEARCH ")) != NULL) {
2841 gchar **strlist, **p;
2843 strlist = g_strsplit(uidlist + 7, " ", 0);
2844 for (p = strlist; *p != NULL; ++p) {
2847 if (sscanf(*p, "%d", &msgnum) == 1)
2848 *list = g_slist_append(*list, GINT_TO_POINTER(msgnum));
2850 g_strfreev(strlist);
2852 ptr_array_free_strings(argbuf);
2853 g_ptr_array_free(argbuf, TRUE);
2855 return IMAP_SUCCESS;
2858 static gint imap_cmd_fetch(IMAPSession *session, guint32 uid,
2859 const gchar *filename)
2867 g_return_val_if_fail(filename != NULL, IMAP_ERROR);
2869 imap_gen_send(session, "UID FETCH %d BODY.PEEK[]", uid);
2871 while ((ok = imap_gen_recv(session, &buf)) == IMAP_SUCCESS) {
2872 if (buf[0] != '*' || buf[1] != ' ') {
2876 if (strstr(buf, "FETCH") != NULL) break;
2879 if (ok != IMAP_SUCCESS) {
2884 #define RETURN_ERROR_IF_FAIL(cond) \
2887 return IMAP_ERROR; \
2890 cur_pos = strchr(buf, '{');
2891 RETURN_ERROR_IF_FAIL(cur_pos != NULL);
2892 cur_pos = strchr_cpy(cur_pos + 1, '}', size_str, sizeof(size_str));
2893 RETURN_ERROR_IF_FAIL(cur_pos != NULL);
2894 size_num = atol(size_str);
2895 RETURN_ERROR_IF_FAIL(size_num >= 0);
2897 RETURN_ERROR_IF_FAIL(*cur_pos == '\0');
2899 #undef RETURN_ERROR_IF_FAIL
2903 if (recv_bytes_write_to_file(SESSION(session)->sock,
2904 size_num, filename) != 0)
2907 if (imap_gen_recv(session, &buf) != IMAP_SUCCESS) {
2912 if (buf[0] == '\0' || buf[strlen(buf) - 1] != ')') {
2918 ok = imap_cmd_ok(session, NULL);
2923 static gint imap_cmd_append(IMAPSession *session, const gchar *destfolder,
2924 const gchar *file, IMAPFlags flags,
2933 gchar buf[BUFFSIZE];
2938 g_return_val_if_fail(file != NULL, IMAP_ERROR);
2940 size = get_file_size_as_crlf(file);
2941 if ((fp = fopen(file, "rb")) == NULL) {
2942 FILE_OP_ERROR(file, "fopen");
2945 QUOTE_IF_REQUIRED(destfolder_, destfolder);
2946 flag_str = imap_get_flag_str(flags);
2947 imap_gen_send(session, "APPEND %s (%s) {%d}",
2948 destfolder_, flag_str, size);
2951 ok = imap_gen_recv(session, &ret);
2952 if (ok != IMAP_SUCCESS || ret[0] != '+' || ret[1] != ' ') {
2953 log_warning(_("can't append %s to %s\n"), file, destfolder_);
2960 log_print("IMAP4> %s\n", _("(sending file...)"));
2962 while (fgets(buf, sizeof(buf), fp) != NULL) {
2964 if (sock_puts(SESSION(session)->sock, buf) < 0) {
2971 FILE_OP_ERROR(file, "fgets");
2976 sock_puts(SESSION(session)->sock, "");
2980 if (new_uid != NULL)
2983 if (new_uid != NULL && session->uidplus) {
2984 argbuf = g_ptr_array_new();
2986 ok = imap_cmd_ok(session, argbuf);
2987 if ((ok == IMAP_SUCCESS) && (argbuf->len > 0)) {
2988 resp_str = g_ptr_array_index(argbuf, argbuf->len - 1);
2990 sscanf(resp_str, "%*u OK [APPENDUID %*u %u]",
2992 *new_uid = new_uid_;
2996 ptr_array_free_strings(argbuf);
2997 g_ptr_array_free(argbuf, TRUE);
2999 ok = imap_cmd_ok(session, NULL);
3001 if (ok != IMAP_SUCCESS)
3002 log_warning(_("can't append message to %s\n"),
3008 static MsgNumberList *imapset_to_numlist(IMAPSet imapset)
3010 gchar **ranges, **range;
3012 MsgNumberList *uids = NULL;
3014 ranges = g_strsplit(imapset, ",", 0);
3015 for (range = ranges; *range != NULL; range++) {
3016 printf("%s\n", *range);
3017 if(sscanf(*range, "%u:%u", &low, &high) == 1)
3018 uids = g_slist_prepend(uids, GINT_TO_POINTER(low));
3021 for (i = low; i <= high; i++)
3022 uids = g_slist_prepend(uids, GINT_TO_POINTER(i));
3025 uids = g_slist_reverse(uids);
3031 static gint imap_cmd_copy(IMAPSession *session, const gchar *seq_set,
3032 const gchar *destfolder, GRelation *uid_mapping)
3037 g_return_val_if_fail(session != NULL, IMAP_ERROR);
3038 g_return_val_if_fail(seq_set != NULL, IMAP_ERROR);
3039 g_return_val_if_fail(destfolder != NULL, IMAP_ERROR);
3041 QUOTE_IF_REQUIRED(destfolder_, destfolder);
3042 imap_gen_send(session, "UID COPY %s %s", seq_set, destfolder_);
3044 if (uid_mapping != NULL && session->uidplus) {
3046 gchar *resp_str = NULL, *olduids_str, *newuids_str;
3047 MsgNumberList *olduids, *old_cur, *newuids, *new_cur;
3049 reply = g_ptr_array_new();
3050 ok = imap_cmd_ok(session, reply);
3051 if ((ok == IMAP_SUCCESS) && (reply->len > 0)) {
3052 resp_str = g_ptr_array_index(reply, reply->len - 1);
3054 olduids_str = g_new0(gchar, strlen(resp_str));
3055 newuids_str = g_new0(gchar, strlen(resp_str));
3056 if (sscanf(resp_str, "%*s OK [COPYUID %*u %[0-9,:] %[0-9,:]]",
3057 olduids_str, newuids_str) == 2) {
3058 olduids = imapset_to_numlist(olduids_str);
3059 newuids = imapset_to_numlist(newuids_str);
3063 while(old_cur != NULL && new_cur != NULL) {
3064 g_relation_insert(uid_mapping,
3065 GPOINTER_TO_INT(old_cur->data),
3066 GPOINTER_TO_INT(new_cur->data));
3067 old_cur = g_slist_next(old_cur);
3068 new_cur = g_slist_next(new_cur);
3071 g_slist_free(olduids);
3072 g_slist_free(newuids);
3074 g_free(olduids_str);
3075 g_free(newuids_str);
3078 ptr_array_free_strings(reply);
3079 g_ptr_array_free(reply, TRUE);
3081 ok = imap_cmd_ok(session, NULL);
3083 if (ok != IMAP_SUCCESS)
3084 log_warning(_("can't copy %s to %s\n"), seq_set, destfolder_);
3089 gint imap_cmd_envelope(IMAPSession *session, IMAPSet set)
3091 static GString *header_fields = NULL;
3093 if (header_fields == NULL) {
3094 const HeaderEntry *headers, *elem;
3096 headers = procheader_get_headernames(FALSE);
3097 header_fields = g_string_new("");
3099 for (elem = headers; elem->name != NULL; ++elem) {
3100 gint namelen = strlen(elem->name);
3102 /* Header fields ending with space are not rfc822 headers */
3103 if (elem->name[namelen - 1] == ' ')
3106 /* strip : at the of header field */
3107 if(elem->name[namelen - 1] == ':')
3113 g_string_sprintfa(header_fields, "%s%.*s",
3114 header_fields->str[0] != '\0' ? " " : "",
3115 namelen, elem->name);
3120 (session, "UID FETCH %s (UID FLAGS RFC822.SIZE BODY.PEEK[HEADER.FIELDS (%s)])",
3121 set, header_fields->str);
3123 return IMAP_SUCCESS;
3126 static gint imap_cmd_store(IMAPSession *session, IMAPSet seq_set,
3131 imap_gen_send(session, "UID STORE %s %s", seq_set, sub_cmd);
3133 if ((ok = imap_cmd_ok(session, NULL)) != IMAP_SUCCESS) {
3134 log_warning(_("error while imap command: STORE %s %s\n"),
3139 return IMAP_SUCCESS;
3142 static gint imap_cmd_expunge(IMAPSession *session, IMAPSet seq_set)
3146 if (seq_set && session->uidplus)
3147 imap_gen_send(session, "UID EXPUNGE %s", seq_set);
3149 imap_gen_send(session, "EXPUNGE");
3150 if ((ok = imap_cmd_ok(session, NULL)) != IMAP_SUCCESS) {
3151 log_warning(_("error while imap command: EXPUNGE\n"));
3155 return IMAP_SUCCESS;
3158 static gint imap_cmd_close(IMAPSession *session)
3162 imap_gen_send(session, "CLOSE");
3163 if ((ok = imap_cmd_ok(session, NULL)) != IMAP_SUCCESS)
3164 log_warning(_("error while imap command: CLOSE\n"));
3169 static gint imap_cmd_ok(IMAPSession *session, GPtrArray *argbuf)
3171 gint ok = IMAP_SUCCESS;
3176 while ((ok = imap_gen_recv(session, &buf))
3178 // make sure data is long enough for any substring of buf
3179 data = alloca(strlen(buf) + 1);
3181 // untagged line read
3182 if (buf[0] == '*' && buf[1] == ' ') {
3185 g_ptr_array_add(argbuf, g_strdup(buf + 2));
3187 if (sscanf(buf + 2, "%d %s", &num, data) >= 2) {
3188 if (!strcmp(data, "EXISTS")) {
3189 session->exists = num;
3190 session->folder_content_changed = TRUE;
3193 if(!strcmp(data, "EXPUNGE")) {
3195 session->folder_content_changed = TRUE;
3198 // tagged line with correct tag and OK response found
3199 } else if ((sscanf(buf, "%d %s", &cmd_num, data) >= 2) &&
3200 (cmd_num == session->cmd_count) &&
3201 !strcmp(data, "OK")) {
3203 g_ptr_array_add(argbuf, g_strdup(buf));
3217 static void imap_gen_send(IMAPSession *session, const gchar *format, ...)
3224 va_start(args, format);
3225 tmp = g_strdup_vprintf(format, args);
3228 session->cmd_count++;
3230 buf = g_strdup_printf("%d %s\r\n", session->cmd_count, tmp);
3231 if (!strncasecmp(tmp, "LOGIN ", 6) && (p = strchr(tmp + 6, ' '))) {
3233 log_print("IMAP4> %d %s ********\n", session->cmd_count, tmp);
3235 log_print("IMAP4> %d %s\n", session->cmd_count, tmp);
3237 sock_write_all(SESSION(session)->sock, buf, strlen(buf));
3242 static gint imap_gen_recv(IMAPSession *session, gchar **ret)
3244 if ((*ret = sock_getline(SESSION(session)->sock)) == NULL)
3249 log_print("IMAP4< %s\n", *ret);
3251 return IMAP_SUCCESS;
3255 /* misc utility functions */
3257 static gchar *strchr_cpy(const gchar *src, gchar ch, gchar *dest, gint len)
3262 tmp = strchr(src, ch);
3266 memcpy(dest, src, MIN(tmp - src, len - 1));
3267 dest[MIN(tmp - src, len - 1)] = '\0';
3272 static gchar *get_quoted(const gchar *src, gchar ch, gchar *dest, gint len)
3274 const gchar *p = src;
3277 g_return_val_if_fail(*p == ch, NULL);
3282 while (*p != '\0' && *p != ch) {
3284 if (*p == '\\' && *(p + 1) != '\0')
3293 return (gchar *)(*p == ch ? p + 1 : p);
3296 static gchar *search_array_contain_str(GPtrArray *array, const gchar *str)
3300 for (i = 0; i < array->len; i++) {
3303 tmp = g_ptr_array_index(array, i);
3304 if (strstr(tmp, str) != NULL)
3311 static gchar *search_array_str(GPtrArray *array, const gchar *str)
3318 for (i = 0; i < array->len; i++) {
3321 tmp = g_ptr_array_index(array, i);
3322 if (!strncmp(tmp, str, len))
3329 static void imap_path_separator_subst(gchar *str, gchar separator)
3332 gboolean in_escape = FALSE;
3334 if (!separator || separator == '/') return;
3336 for (p = str; *p != '\0'; p++) {
3337 if (*p == '/' && !in_escape)
3339 else if (*p == '&' && *(p + 1) != '-' && !in_escape)
3341 else if (*p == '-' && in_escape)
3346 static gchar *imap_modified_utf7_to_locale(const gchar *mutf7_str)
3349 const gchar *from_p;
3352 to = g_malloc(strlen(mutf7_str) + 1);
3355 for (from_p = mutf7_str; *from_p != '\0'; from_p++) {
3356 if (*from_p == '&' && *(from_p + 1) == '-') {
3366 static iconv_t cd = (iconv_t)-1;
3367 static gboolean iconv_ok = TRUE;
3370 size_t norm_utf7_len;
3372 gchar *to_str, *to_p;
3374 gboolean in_escape = FALSE;
3376 if (!iconv_ok) return g_strdup(mutf7_str);
3378 if (cd == (iconv_t)-1) {
3379 cd = iconv_open(conv_get_current_charset_str(), "UTF-7");
3380 if (cd == (iconv_t)-1) {
3381 g_warning("iconv cannot convert UTF-7 to %s\n",
3382 conv_get_current_charset_str());
3384 return g_strdup(mutf7_str);
3388 norm_utf7 = g_string_new(NULL);
3390 for (p = mutf7_str; *p != '\0'; p++) {
3391 /* replace: '&' -> '+',
3393 escaped ',' -> '/' */
3394 if (!in_escape && *p == '&') {
3395 if (*(p + 1) != '-') {
3396 g_string_append_c(norm_utf7, '+');
3399 g_string_append_c(norm_utf7, '&');
3402 } else if (in_escape && *p == ',') {
3403 g_string_append_c(norm_utf7, '/');
3404 } else if (in_escape && *p == '-') {
3405 g_string_append_c(norm_utf7, '-');
3408 g_string_append_c(norm_utf7, *p);
3412 norm_utf7_p = norm_utf7->str;
3413 norm_utf7_len = norm_utf7->len;
3414 to_len = strlen(mutf7_str) * 5;
3415 to_p = to_str = g_malloc(to_len + 1);
3417 if (iconv(cd, (ICONV_CONST gchar **)&norm_utf7_p, &norm_utf7_len,
3418 &to_p, &to_len) == -1) {
3419 g_warning(_("iconv cannot convert UTF-7 to %s\n"),
3420 conv_get_current_charset_str());
3421 g_string_free(norm_utf7, TRUE);
3423 return g_strdup(mutf7_str);
3426 /* second iconv() call for flushing */
3427 iconv(cd, NULL, NULL, &to_p, &to_len);
3428 g_string_free(norm_utf7, TRUE);
3432 #endif /* !HAVE_ICONV */
3435 static gchar *imap_locale_to_modified_utf7(const gchar *from)
3438 const gchar *from_p;
3441 to = g_malloc(strlen(from) * 2 + 1);
3444 for (from_p = from; *from_p != '\0'; from_p++) {
3445 if (*from_p == '&') {
3455 static iconv_t cd = (iconv_t)-1;
3456 static gboolean iconv_ok = TRUE;
3457 gchar *norm_utf7, *norm_utf7_p;
3458 size_t from_len, norm_utf7_len;
3460 gchar *from_tmp, *to, *p;
3461 gboolean in_escape = FALSE;
3463 if (!iconv_ok) return g_strdup(from);
3465 if (cd == (iconv_t)-1) {
3466 cd = iconv_open("UTF-7", conv_get_current_charset_str());
3467 if (cd == (iconv_t)-1) {
3468 g_warning("iconv cannot convert %s to UTF-7\n",
3469 conv_get_current_charset_str());
3471 return g_strdup(from);
3475 Xstrdup_a(from_tmp, from, return g_strdup(from));
3476 from_len = strlen(from);
3477 norm_utf7_len = from_len * 5;
3478 Xalloca(norm_utf7, norm_utf7_len + 1, return g_strdup(from));
3479 norm_utf7_p = norm_utf7;
3481 #define IS_PRINT(ch) (isprint(ch) && isascii(ch))
3483 while (from_len > 0) {
3484 if (*from_tmp == '+') {
3485 *norm_utf7_p++ = '+';
3486 *norm_utf7_p++ = '-';
3490 } else if (IS_PRINT(*from_tmp)) {
3491 /* printable ascii char */
3492 *norm_utf7_p = *from_tmp;
3498 size_t mb_len = 0, conv_len = 0;
3500 /* unprintable char: convert to UTF-7 */
3502 while (!IS_PRINT(*p) && conv_len < from_len) {
3503 mb_len = mblen(p, MB_LEN_MAX);
3505 g_warning("wrong multibyte sequence\n");
3506 return g_strdup(from);
3512 from_len -= conv_len;
3513 if (iconv(cd, (ICONV_CONST gchar **)&from_tmp,
3515 &norm_utf7_p, &norm_utf7_len) == -1) {
3516 g_warning("iconv cannot convert %s to UTF-7\n",
3517 conv_get_current_charset_str());
3518 return g_strdup(from);
3521 /* second iconv() call for flushing */
3522 iconv(cd, NULL, NULL, &norm_utf7_p, &norm_utf7_len);
3528 *norm_utf7_p = '\0';
3529 to_str = g_string_new(NULL);
3530 for (p = norm_utf7; p < norm_utf7_p; p++) {
3531 /* replace: '&' -> "&-",
3534 BASE64 '/' -> ',' */
3535 if (!in_escape && *p == '&') {
3536 g_string_append(to_str, "&-");
3537 } else if (!in_escape && *p == '+') {
3538 if (*(p + 1) == '-') {
3539 g_string_append_c(to_str, '+');
3542 g_string_append_c(to_str, '&');
3545 } else if (in_escape && *p == '/') {
3546 g_string_append_c(to_str, ',');
3547 } else if (in_escape && *p == '-') {
3548 g_string_append_c(to_str, '-');
3551 g_string_append_c(to_str, *p);
3557 g_string_append_c(to_str, '-');
3561 g_string_free(to_str, FALSE);
3564 #endif /* !HAVE_ICONV */
3567 static GSList *imap_get_seq_set_from_numlist(MsgNumberList *numlist)
3570 GSList *sorted_list, *cur;
3571 guint first, last, next;
3573 GSList *ret_list = NULL;
3575 if (numlist == NULL)
3578 str = g_string_sized_new(256);
3580 sorted_list = g_slist_copy(numlist);
3581 sorted_list = g_slist_sort(sorted_list, g_int_compare);
3583 first = GPOINTER_TO_INT(sorted_list->data);
3585 for (cur = sorted_list; cur != NULL; cur = g_slist_next(cur)) {
3586 last = GPOINTER_TO_INT(cur->data);
3588 next = GPOINTER_TO_INT(cur->next->data);
3592 if (last + 1 != next || next == 0) {
3594 g_string_append_c(str, ',');
3596 g_string_sprintfa(str, "%u", first);
3598 g_string_sprintfa(str, "%u:%u", first, last);
3602 if (str->len > IMAP_CMD_LIMIT) {
3603 ret_str = g_strdup(str->str);
3604 ret_list = g_slist_append(ret_list, ret_str);
3605 g_string_truncate(str, 0);
3611 ret_str = g_strdup(str->str);
3612 ret_list = g_slist_append(ret_list, ret_str);
3615 g_slist_free(sorted_list);
3616 g_string_free(str, TRUE);
3621 static GSList *imap_get_seq_set_from_msglist(MsgInfoList *msglist)
3623 MsgNumberList *numlist = NULL;
3627 for (cur = msglist; cur != NULL; cur = g_slist_next(cur)) {
3628 MsgInfo *msginfo = (MsgInfo *) cur->data;
3630 numlist = g_slist_append(numlist, GINT_TO_POINTER(msginfo->msgnum));
3632 seq_list = imap_get_seq_set_from_numlist(numlist);
3633 g_slist_free(numlist);
3638 static void imap_seq_set_free(GSList *seq_list)
3640 slist_free_strings(seq_list);
3641 g_slist_free(seq_list);
3645 static gboolean imap_rename_folder_func(GNode *node, gpointer data)
3647 FolderItem *item = node->data;
3648 gchar **paths = data;
3649 const gchar *oldpath = paths[0];
3650 const gchar *newpath = paths[1];
3652 gchar *new_itempath;
3655 oldpathlen = strlen(oldpath);
3656 if (strncmp(oldpath, item->path, oldpathlen) != 0) {
3657 g_warning("path doesn't match: %s, %s\n", oldpath, item->path);
3661 base = item->path + oldpathlen;
3662 while (*base == G_DIR_SEPARATOR) base++;
3664 new_itempath = g_strdup(newpath);
3666 new_itempath = g_strconcat(newpath, G_DIR_SEPARATOR_S, base,
3669 item->path = new_itempath;
3674 static gint get_list_of_uids(Folder *folder, IMAPFolderItem *item, GSList **msgnum_list)
3676 gint ok, nummsgs = 0, lastuid_old;
3677 IMAPSession *session;
3678 GSList *uidlist, *elem;
3681 session = imap_session_get(folder);
3682 g_return_val_if_fail(session != NULL, -1);
3684 ok = imap_select(session, IMAP_FOLDER(folder), item->item.path,
3685 NULL, NULL, NULL, NULL);
3686 if (ok != IMAP_SUCCESS)
3689 cmd_buf = g_strdup_printf("UID %d:*", item->lastuid + 1);
3690 ok = imap_cmd_search(session, cmd_buf, &uidlist);
3693 if (ok == IMAP_SOCKET) {
3694 session_destroy((Session *)session);
3695 ((RemoteFolder *)folder)->session = NULL;
3699 if (ok != IMAP_SUCCESS) {
3703 argbuf = g_ptr_array_new();
3705 cmd_buf = g_strdup_printf("UID FETCH %d:* (UID)", item->lastuid + 1);
3706 imap_gen_send(session, cmd_buf);
3708 ok = imap_cmd_ok(session, argbuf);
3709 if (ok != IMAP_SUCCESS) {
3710 ptr_array_free_strings(argbuf);
3711 g_ptr_array_free(argbuf, TRUE);
3715 for(i = 0; i < argbuf->len; i++) {
3718 if((ret = sscanf(g_ptr_array_index(argbuf, i),
3719 "%*d FETCH (UID %d)", &msgnum)) == 1)
3720 uidlist = g_slist_prepend(uidlist, GINT_TO_POINTER(msgnum));
3722 ptr_array_free_strings(argbuf);
3723 g_ptr_array_free(argbuf, TRUE);
3726 lastuid_old = item->lastuid;
3727 *msgnum_list = g_slist_copy(item->uid_list);
3728 nummsgs = g_slist_length(*msgnum_list);
3729 debug_print("Got %d uids from cache\n", g_slist_length(item->uid_list));
3731 for (elem = uidlist; elem != NULL; elem = g_slist_next(elem)) {
3734 msgnum = GPOINTER_TO_INT(elem->data);
3735 if (msgnum > lastuid_old) {
3736 *msgnum_list = g_slist_prepend(*msgnum_list, GINT_TO_POINTER(msgnum));
3737 item->uid_list = g_slist_prepend(item->uid_list, GINT_TO_POINTER(msgnum));
3740 if(msgnum > item->lastuid)
3741 item->lastuid = msgnum;
3744 g_slist_free(uidlist);
3749 gint imap_get_num_list(Folder *folder, FolderItem *_item, GSList **msgnum_list, gboolean *old_uids_valid)
3751 IMAPFolderItem *item = (IMAPFolderItem *)_item;
3752 IMAPSession *session;
3753 gint ok, nummsgs = 0, exists, recent, uid_val, uid_next, unseen;
3756 gboolean selected_folder;
3758 g_return_val_if_fail(folder != NULL, -1);
3759 g_return_val_if_fail(item != NULL, -1);
3760 g_return_val_if_fail(item->item.path != NULL, -1);
3761 g_return_val_if_fail(FOLDER_CLASS(folder) == &imap_class, -1);
3762 g_return_val_if_fail(folder->account != NULL, -1);
3764 session = imap_session_get(folder);
3765 g_return_val_if_fail(session != NULL, -1);
3767 selected_folder = (session->mbox != NULL) &&
3768 (!strcmp(session->mbox, item->item.path));
3769 if (selected_folder) {
3770 ok = imap_cmd_noop(session);
3771 if (ok != IMAP_SUCCESS)
3773 exists = session->exists;
3775 *old_uids_valid = TRUE;
3777 ok = imap_status(session, IMAP_FOLDER(folder), item->item.path,
3778 &exists, &recent, &uid_next, &uid_val, &unseen);
3779 if (ok != IMAP_SUCCESS)
3782 if(item->item.mtime == uid_val)
3783 *old_uids_valid = TRUE;
3785 *old_uids_valid = FALSE;
3787 debug_print("Freeing imap uid cache\n");
3789 g_slist_free(item->uid_list);
3790 item->uid_list = NULL;
3792 item->item.mtime = uid_val;
3794 imap_delete_all_cached_messages((FolderItem *)item);
3798 if (!selected_folder)
3799 item->uid_next = uid_next;
3801 /* If old uid_next matches new uid_next we can be sure no message
3802 was added to the folder */
3803 if (( selected_folder && !session->folder_content_changed) ||
3804 (!selected_folder && uid_next == item->uid_next)) {
3805 nummsgs = g_slist_length(item->uid_list);
3807 /* If number of messages is still the same we
3808 know our caches message numbers are still valid,
3809 otherwise if the number of messages has decrease
3810 we discard our cache to start a new scan to find
3811 out which numbers have been removed */
3812 if (exists == nummsgs) {
3813 *msgnum_list = g_slist_copy(item->uid_list);
3815 } else if (exists < nummsgs) {
3816 debug_print("Freeing imap uid cache");
3818 g_slist_free(item->uid_list);
3819 item->uid_list = NULL;
3824 *msgnum_list = NULL;
3828 nummsgs = get_list_of_uids(folder, item, &uidlist);
3830 if (nummsgs != exists) {
3831 /* Cache contains more messages then folder, we have cached
3832 an old UID of a message that was removed and new messages
3833 have been added too, otherwise the uid_next check would
3835 debug_print("Freeing imap uid cache");
3837 g_slist_free(item->uid_list);
3838 item->uid_list = NULL;
3840 g_slist_free(*msgnum_list);
3842 nummsgs = get_list_of_uids(folder, item, &uidlist);
3845 *msgnum_list = uidlist;
3847 dir = folder_item_get_path((FolderItem *)item);
3848 debug_print("removing old messages from %s\n", dir);
3849 remove_numbered_files_not_in_list(dir, *msgnum_list);
3855 static MsgInfo *imap_parse_msg(const gchar *file, FolderItem *item)
3860 flags.perm_flags = MSG_NEW|MSG_UNREAD;
3861 flags.tmp_flags = 0;
3863 g_return_val_if_fail(item != NULL, NULL);
3864 g_return_val_if_fail(file != NULL, NULL);
3866 if (item->stype == F_QUEUE) {
3867 MSG_SET_TMP_FLAGS(flags, MSG_QUEUED);
3868 } else if (item->stype == F_DRAFT) {
3869 MSG_SET_TMP_FLAGS(flags, MSG_DRAFT);
3872 msginfo = procheader_parse_file(file, flags, FALSE, FALSE);
3873 if (!msginfo) return NULL;
3875 msginfo->folder = item;
3880 GSList *imap_get_msginfos(Folder *folder, FolderItem *item, GSList *msgnum_list)
3882 IMAPSession *session;
3883 MsgInfoList *ret = NULL;
3886 g_return_val_if_fail(folder != NULL, NULL);
3887 g_return_val_if_fail(item != NULL, NULL);
3888 g_return_val_if_fail(msgnum_list != NULL, NULL);
3890 session = imap_session_get(folder);
3891 g_return_val_if_fail(session != NULL, NULL);
3893 ok = imap_select(session, IMAP_FOLDER(folder), item->path,
3894 NULL, NULL, NULL, NULL);
3895 if (ok != IMAP_SUCCESS)
3898 if (!(item->stype == F_QUEUE || item->stype == F_DRAFT)) {
3899 ret = g_slist_concat(ret,
3900 imap_get_uncached_messages(
3901 session, item, msgnum_list));
3903 MsgNumberList *sorted_list, *elem;
3904 gint startnum, lastnum;
3906 sorted_list = g_slist_sort(g_slist_copy(msgnum_list), g_int_compare);
3908 startnum = lastnum = GPOINTER_TO_INT(sorted_list->data);
3910 for (elem = sorted_list;; elem = g_slist_next(elem)) {
3914 num = GPOINTER_TO_INT(elem->data);
3916 if (num > lastnum + 1 || elem == NULL) {
3918 for (i = startnum; i <= lastnum; ++i) {
3921 file = imap_fetch_msg(folder, item, i);
3923 MsgInfo *msginfo = imap_parse_msg(file, item);
3924 if (msginfo != NULL) {
3925 msginfo->msgnum = i;
3926 ret = g_slist_append(ret, msginfo);
3940 g_slist_free(sorted_list);
3946 MsgInfo *imap_get_msginfo(Folder *folder, FolderItem *item, gint uid)
3948 MsgInfo *msginfo = NULL;
3949 MsgInfoList *msginfolist;
3950 MsgNumberList numlist;
3952 numlist.next = NULL;
3953 numlist.data = GINT_TO_POINTER(uid);
3955 msginfolist = imap_get_msginfos(folder, item, &numlist);
3956 if (msginfolist != NULL) {
3957 msginfo = msginfolist->data;
3958 g_slist_free(msginfolist);
3964 gboolean imap_scan_required(Folder *folder, FolderItem *_item)
3966 IMAPSession *session;
3967 IMAPFolderItem *item = (IMAPFolderItem *)_item;
3968 gint ok, exists = 0, recent = 0, unseen = 0;
3969 guint32 uid_next, uid_val = 0;
3970 gboolean selected_folder;
3972 g_return_val_if_fail(folder != NULL, FALSE);
3973 g_return_val_if_fail(item != NULL, FALSE);
3974 g_return_val_if_fail(item->item.folder != NULL, FALSE);
3975 g_return_val_if_fail(FOLDER_CLASS(item->item.folder) == &imap_class, FALSE);
3977 if (item->item.path == NULL)
3980 session = imap_session_get(folder);
3981 g_return_val_if_fail(session != NULL, FALSE);
3983 selected_folder = (session->mbox != NULL) &&
3984 (!strcmp(session->mbox, item->item.path));
3985 if (selected_folder) {
3986 ok = imap_cmd_noop(session);
3987 if (ok != IMAP_SUCCESS)
3990 if (session->folder_content_changed)
3993 ok = imap_status(session, IMAP_FOLDER(folder), item->item.path,
3994 &exists, &recent, &uid_next, &uid_val, &unseen);
3995 if (ok != IMAP_SUCCESS)
3998 if ((uid_next != item->uid_next) || (exists < item->item.total_msgs))
4005 void imap_change_flags(Folder *folder, FolderItem *item, MsgInfo *msginfo, MsgPermFlags newflags)
4007 IMAPSession *session;
4008 IMAPFlags flags_set = 0, flags_unset = 0;
4009 gint ok = IMAP_SUCCESS;
4010 MsgNumberList numlist;
4012 g_return_if_fail(folder != NULL);
4013 g_return_if_fail(folder->klass == &imap_class);
4014 g_return_if_fail(item != NULL);
4015 g_return_if_fail(item->folder == folder);
4016 g_return_if_fail(msginfo != NULL);
4017 g_return_if_fail(msginfo->folder == item);
4019 session = imap_session_get(folder);
4020 if (!session) return;
4022 if ((ok = imap_select(session, IMAP_FOLDER(folder), msginfo->folder->path,
4023 NULL, NULL, NULL, NULL)) != IMAP_SUCCESS)
4026 if (!MSG_IS_MARKED(msginfo->flags) && (newflags & MSG_MARKED))
4027 flags_set |= IMAP_FLAG_FLAGGED;
4028 if ( MSG_IS_MARKED(msginfo->flags) && !(newflags & MSG_MARKED))
4029 flags_unset |= IMAP_FLAG_FLAGGED;
4031 if (!MSG_IS_UNREAD(msginfo->flags) && (newflags & MSG_UNREAD))
4032 flags_unset |= IMAP_FLAG_SEEN;
4033 if ( MSG_IS_UNREAD(msginfo->flags) && !(newflags & MSG_UNREAD))
4034 flags_set |= IMAP_FLAG_SEEN;
4036 if (!MSG_IS_REPLIED(msginfo->flags) && (newflags & MSG_REPLIED))
4037 flags_set |= IMAP_FLAG_ANSWERED;
4038 if ( MSG_IS_REPLIED(msginfo->flags) && !(newflags & MSG_REPLIED))
4039 flags_set |= IMAP_FLAG_ANSWERED;
4041 numlist.next = NULL;
4042 numlist.data = GINT_TO_POINTER(msginfo->msgnum);
4045 ok = imap_set_message_flags(session, &numlist, flags_set, TRUE);
4046 if (ok != IMAP_SUCCESS) return;
4050 ok = imap_set_message_flags(session, &numlist, flags_unset, FALSE);
4051 if (ok != IMAP_SUCCESS) return;
4054 msginfo->flags.perm_flags = newflags;