2 * Sylpheed -- a GTK+ based, lightweight, and fast e-mail client
3 * Copyright (C) 1999-2004 Hiroyuki Yamamoto
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation; either version 2 of the License, or
8 * (at your option) any later version.
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
15 * You should have received a copy of the GNU General Public License
16 * along with this program; if not, write to the Free Software
17 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
49 #include "procheader.h"
50 #include "prefs_account.h"
55 #include "prefs_common.h"
56 #include "inputdialog.h"
58 #include "remotefolder.h"
59 #include "alertpanel.h"
61 typedef struct _IMAPFolder IMAPFolder;
62 typedef struct _IMAPSession IMAPSession;
63 typedef struct _IMAPNameSpace IMAPNameSpace;
64 typedef struct _IMAPFolderItem IMAPFolderItem;
66 #include "prefs_account.h"
68 #define IMAP_FOLDER(obj) ((IMAPFolder *)obj)
69 #define IMAP_FOLDER_ITEM(obj) ((IMAPFolderItem *)obj)
70 #define IMAP_SESSION(obj) ((IMAPSession *)obj)
76 /* list of IMAPNameSpace */
86 gboolean authenticated;
95 time_t last_access_time;
96 gboolean folder_content_changed;
100 struct _IMAPNameSpace
106 #define IMAP_SUCCESS 0
107 #define IMAP_SOCKET 2
108 #define IMAP_AUTHFAIL 3
109 #define IMAP_PROTOCOL 4
110 #define IMAP_SYNTAX 5
114 #define IMAPBUFSIZE 8192
118 IMAP_FLAG_SEEN = 1 << 0,
119 IMAP_FLAG_ANSWERED = 1 << 1,
120 IMAP_FLAG_FLAGGED = 1 << 2,
121 IMAP_FLAG_DELETED = 1 << 3,
122 IMAP_FLAG_DRAFT = 1 << 4
125 #define IMAP_IS_SEEN(flags) ((flags & IMAP_FLAG_SEEN) != 0)
126 #define IMAP_IS_ANSWERED(flags) ((flags & IMAP_FLAG_ANSWERED) != 0)
127 #define IMAP_IS_FLAGGED(flags) ((flags & IMAP_FLAG_FLAGGED) != 0)
128 #define IMAP_IS_DELETED(flags) ((flags & IMAP_FLAG_DELETED) != 0)
129 #define IMAP_IS_DRAFT(flags) ((flags & IMAP_FLAG_DRAFT) != 0)
132 #define IMAP4_PORT 143
134 #define IMAPS_PORT 993
137 #define IMAP_CMD_LIMIT 1000
139 #define QUOTE_IF_REQUIRED(out, str) \
141 if (*str != '"' && strpbrk(str, " \t(){}%*") != NULL) { \
145 len = strlen(str) + 3; \
146 Xalloca(__tmp, len, return IMAP_ERROR); \
147 g_snprintf(__tmp, len, "\"%s\"", str); \
150 Xstrdup_a(out, str, return IMAP_ERROR); \
154 typedef gchar * IMAPSet;
156 struct _IMAPFolderItem
165 static void imap_folder_init (Folder *folder,
169 static Folder *imap_folder_new (const gchar *name,
171 static void imap_folder_destroy (Folder *folder);
173 static IMAPSession *imap_session_new (const PrefsAccount *account);
174 static void imap_session_authenticate(IMAPSession *session,
175 const PrefsAccount *account);
176 static void imap_session_destroy (Session *session);
178 static gchar *imap_fetch_msg (Folder *folder,
181 static gint imap_add_msg (Folder *folder,
185 static gint imap_add_msgs (Folder *folder,
188 GRelation *relation);
190 static gint imap_copy_msg (Folder *folder,
193 static gint imap_copy_msgs (Folder *folder,
195 MsgInfoList *msglist,
196 GRelation *relation);
198 static gint imap_remove_msg (Folder *folder,
201 static gint imap_remove_all_msg (Folder *folder,
204 static gboolean imap_is_msg_changed (Folder *folder,
208 static gint imap_close (Folder *folder,
211 static gint imap_scan_tree (Folder *folder);
213 static gint imap_create_tree (Folder *folder);
215 static FolderItem *imap_create_folder (Folder *folder,
218 static gint imap_rename_folder (Folder *folder,
221 static gint imap_remove_folder (Folder *folder,
224 static FolderItem *imap_folder_item_new (Folder *folder);
225 static void imap_folder_item_destroy (Folder *folder,
228 static IMAPSession *imap_session_get (Folder *folder);
230 static gint imap_greeting (IMAPSession *session);
231 static gint imap_auth (IMAPSession *session,
236 static gint imap_scan_tree_recursive (IMAPSession *session,
238 static GSList *imap_parse_list (IMAPFolder *folder,
239 IMAPSession *session,
240 const gchar *real_path,
243 static void imap_create_missing_folders (Folder *folder);
244 static FolderItem *imap_create_special_folder
246 SpecialFolderItemType stype,
249 static gint imap_do_copy_msgs (Folder *folder,
251 MsgInfoList *msglist,
252 GRelation *relation);
254 static void imap_delete_all_cached_messages (FolderItem *item);
257 static SockInfo *imap_open (const gchar *server,
261 static SockInfo *imap_open (const gchar *server,
266 static SockInfo *imap_open_tunnel(const gchar *server,
267 const gchar *tunnelcmd,
270 static SockInfo *imap_open_tunnel(const gchar *server,
271 const gchar *tunnelcmd);
275 static SockInfo *imap_init_sock(SockInfo *sock, SSLType ssl_type);
277 static SockInfo *imap_init_sock(SockInfo *sock);
280 static gchar *imap_get_flag_str (IMAPFlags flags);
281 static gint imap_set_message_flags (IMAPSession *session,
282 MsgNumberList *numlist,
285 static gint imap_select (IMAPSession *session,
291 guint32 *uid_validity);
292 static gint imap_status (IMAPSession *session,
298 guint32 *uid_validity,
301 static void imap_parse_namespace (IMAPSession *session,
303 static void imap_get_namespace_by_list (IMAPSession *session,
305 static IMAPNameSpace *imap_find_namespace (IMAPFolder *folder,
307 static gchar imap_get_path_separator (IMAPFolder *folder,
309 static gchar *imap_get_real_path (IMAPFolder *folder,
312 static gchar *imap_parse_atom (SockInfo *sock,
317 static MsgFlags imap_parse_flags (const gchar *flag_str);
318 static MsgInfo *imap_parse_envelope (SockInfo *sock,
322 static gboolean imap_has_capability (IMAPSession *session,
324 static void imap_free_capabilities (IMAPSession *session);
326 /* low-level IMAP4rev1 commands */
327 static gint imap_cmd_authenticate
328 (IMAPSession *session,
332 static gint imap_cmd_login (IMAPSession *session,
335 static gint imap_cmd_logout (IMAPSession *session);
336 static gint imap_cmd_noop (IMAPSession *session);
337 static gint imap_cmd_starttls (IMAPSession *session);
338 static gint imap_cmd_namespace (IMAPSession *session,
340 static gint imap_cmd_list (IMAPSession *session,
342 const gchar *mailbox,
344 static gint imap_cmd_do_select (IMAPSession *session,
350 guint32 *uid_validity);
351 static gint imap_cmd_select (IMAPSession *session,
356 guint32 *uid_validity);
357 static gint imap_cmd_examine (IMAPSession *session,
362 guint32 *uid_validity);
363 static gint imap_cmd_create (IMAPSession *sock,
364 const gchar *folder);
365 static gint imap_cmd_rename (IMAPSession *sock,
366 const gchar *oldfolder,
367 const gchar *newfolder);
368 static gint imap_cmd_delete (IMAPSession *session,
369 const gchar *folder);
370 static gint imap_cmd_envelope (IMAPSession *session,
372 static gint imap_cmd_fetch (IMAPSession *sock,
374 const gchar *filename);
375 static gint imap_cmd_append (IMAPSession *session,
376 const gchar *destfolder,
380 static gint imap_cmd_copy (IMAPSession *session,
381 const gchar *seq_set,
382 const gchar *destfolder,
383 GRelation *uid_mapping);
384 static gint imap_cmd_store (IMAPSession *session,
387 static gint imap_cmd_expunge (IMAPSession *session,
389 static gint imap_cmd_close (IMAPSession *session);
391 static gint imap_cmd_ok (IMAPSession *session,
393 static void imap_gen_send (IMAPSession *session,
394 const gchar *format, ...);
395 static gint imap_gen_recv (IMAPSession *session,
398 /* misc utility functions */
399 static gchar *strchr_cpy (const gchar *src,
403 static gchar *get_quoted (const gchar *src,
407 static gchar *search_array_contain_str (GPtrArray *array,
409 static gchar *search_array_str (GPtrArray *array,
411 static void imap_path_separator_subst (gchar *str,
414 static gchar *imap_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 gint imap_get_flags (Folder *folder,
441 MsgInfoList *msglist,
442 GRelation *msgflags);
443 static gchar *imap_folder_get_path (Folder *folder);
444 static gchar *imap_item_get_path (Folder *folder,
447 static FolderClass imap_class;
449 FolderClass *imap_get_class(void)
451 if (imap_class.idstr == NULL) {
452 imap_class.type = F_IMAP;
453 imap_class.idstr = "imap";
454 imap_class.uistr = "IMAP4";
456 /* Folder functions */
457 imap_class.new_folder = imap_folder_new;
458 imap_class.destroy_folder = imap_folder_destroy;
459 imap_class.scan_tree = imap_scan_tree;
460 imap_class.create_tree = imap_create_tree;
462 /* FolderItem functions */
463 imap_class.item_new = imap_folder_item_new;
464 imap_class.item_destroy = imap_folder_item_destroy;
465 imap_class.item_get_path = imap_item_get_path;
466 imap_class.create_folder = imap_create_folder;
467 imap_class.rename_folder = imap_rename_folder;
468 imap_class.remove_folder = imap_remove_folder;
469 imap_class.close = imap_close;
470 imap_class.get_num_list = imap_get_num_list;
471 imap_class.scan_required = imap_scan_required;
473 /* Message functions */
474 imap_class.get_msginfo = imap_get_msginfo;
475 imap_class.get_msginfos = imap_get_msginfos;
476 imap_class.fetch_msg = imap_fetch_msg;
477 imap_class.add_msg = imap_add_msg;
478 imap_class.add_msgs = imap_add_msgs;
479 imap_class.copy_msg = imap_copy_msg;
480 imap_class.copy_msgs = imap_copy_msgs;
481 imap_class.remove_msg = imap_remove_msg;
482 imap_class.remove_all_msg = imap_remove_all_msg;
483 imap_class.is_msg_changed = imap_is_msg_changed;
484 imap_class.change_flags = imap_change_flags;
485 imap_class.get_flags = imap_get_flags;
491 static Folder *imap_folder_new(const gchar *name, const gchar *path)
495 folder = (Folder *)g_new0(IMAPFolder, 1);
496 folder->klass = &imap_class;
497 imap_folder_init(folder, name, path);
502 static void imap_folder_destroy(Folder *folder)
506 dir = imap_folder_get_path(folder);
507 if (is_dir_exist(dir))
508 remove_dir_recursive(dir);
511 folder_remote_folder_destroy(REMOTE_FOLDER(folder));
514 static void imap_folder_init(Folder *folder, const gchar *name,
517 folder_remote_folder_init((Folder *)folder, name, path);
520 static FolderItem *imap_folder_item_new(Folder *folder)
522 IMAPFolderItem *item;
524 item = g_new0(IMAPFolderItem, 1);
527 item->uid_list = NULL;
529 return (FolderItem *)item;
532 static void imap_folder_item_destroy(Folder *folder, FolderItem *_item)
534 IMAPFolderItem *item = (IMAPFolderItem *)_item;
536 g_return_if_fail(item != NULL);
537 g_slist_free(item->uid_list);
542 static gboolean imap_reset_uid_lists_func(GNode *node, gpointer data)
544 IMAPFolderItem *item = (IMAPFolderItem *)node->data;
548 g_slist_free(item->uid_list);
549 item->uid_list = NULL;
554 static void imap_reset_uid_lists(Folder *folder)
556 if(folder->node == NULL)
559 /* Destroy all uid lists and rest last uid */
560 g_node_traverse(folder->node, G_IN_ORDER, G_TRAVERSE_ALL, -1, imap_reset_uid_lists_func, NULL);
563 /* Send CAPABILITY, and examine the server's response to see whether this
564 * connection is pre-authenticated or not and build a list of CAPABILITIES. */
565 static gint imap_greeting(IMAPSession *session)
570 imap_gen_send(session, "CAPABILITY");
572 argbuf = g_ptr_array_new();
574 if (imap_cmd_ok(session, argbuf) != IMAP_SUCCESS ||
575 ((capstr = search_array_str(argbuf, "CAPABILITY ")) == NULL)) {
576 ptr_array_free_strings(argbuf);
577 g_ptr_array_free(argbuf, TRUE);
581 session->authenticated = search_array_str(argbuf, "PREAUTH") != NULL;
583 capstr += strlen("CAPABILITY ");
585 IMAP_SESSION(session)->capability = g_strsplit(capstr, " ", 0);
587 ptr_array_free_strings(argbuf);
588 g_ptr_array_free(argbuf, TRUE);
590 if (imap_has_capability(session, "UIDPLUS"))
591 session->uidplus = TRUE;
596 static gint imap_auth(IMAPSession *session, const gchar *user, const gchar *pass,
601 if (type == 0 || type == IMAP_AUTH_LOGIN)
602 ok = imap_cmd_login(session, user, pass);
604 ok = imap_cmd_authenticate(session, user, pass, type);
606 if (ok == IMAP_SUCCESS)
607 session->authenticated = TRUE;
612 static IMAPSession *imap_session_get(Folder *folder)
614 RemoteFolder *rfolder = REMOTE_FOLDER(folder);
615 IMAPSession *session = NULL;
617 g_return_val_if_fail(folder != NULL, NULL);
618 g_return_val_if_fail(FOLDER_CLASS(folder) == &imap_class, NULL);
619 g_return_val_if_fail(folder->account != NULL, NULL);
621 if (prefs_common.work_offline)
624 /* Make sure we have a session */
625 if (rfolder->session != NULL) {
626 session = IMAP_SESSION(rfolder->session);
628 imap_reset_uid_lists(folder);
629 session = imap_session_new(folder->account);
634 if (SESSION(session)->sock->state == CONN_DISCONNECTED) {
635 debug_print("IMAP server disconnected\n");
636 session_destroy(SESSION(session));
637 imap_reset_uid_lists(folder);
638 session = imap_session_new(folder->account);
641 /* Make sure session is authenticated */
642 if (!IMAP_SESSION(session)->authenticated)
643 imap_session_authenticate(IMAP_SESSION(session), folder->account);
644 if (!IMAP_SESSION(session)->authenticated) {
645 session_destroy(SESSION(session));
646 rfolder->session = NULL;
650 /* Make sure we have parsed the IMAP namespace */
651 imap_parse_namespace(IMAP_SESSION(session),
652 IMAP_FOLDER(folder));
654 /* I think the point of this code is to avoid sending a
655 * keepalive if we've used the session recently and therefore
656 * think it's still alive. Unfortunately, most of the code
657 * does not yet check for errors on the socket, and so if the
658 * connection drops we don't notice until the timeout expires.
659 * A better solution than sending a NOOP every time would be
660 * for every command to be prepared to retry until it is
661 * successfully sent. -- mbp */
662 if (time(NULL) - session->last_access_time > SESSION_TIMEOUT_INTERVAL) {
663 /* verify that the session is still alive */
664 if (imap_cmd_noop(session) != IMAP_SUCCESS) {
665 /* Check if this is the first try to establish a
666 connection, if yes we don't try to reconnect */
667 if (rfolder->session == NULL) {
668 log_warning(_("Connecting to %s failed"),
669 folder->account->recv_server);
670 session_destroy(SESSION(session));
673 log_warning(_("IMAP4 connection to %s has been"
674 " disconnected. Reconnecting...\n"),
675 folder->account->recv_server);
676 session_destroy(SESSION(session));
677 /* Clear folders session to make imap_session_get create
678 a new session, because of rfolder->session == NULL
679 it will not try to reconnect again and so avoid an
681 rfolder->session = NULL;
682 session = imap_session_get(folder);
687 rfolder->session = SESSION(session);
689 return IMAP_SESSION(session);
692 static IMAPSession *imap_session_new(const PrefsAccount *account)
694 IMAPSession *session;
699 /* FIXME: IMAP over SSL only... */
702 port = account->set_imapport ? account->imapport
703 : account->ssl_imap == SSL_TUNNEL ? IMAPS_PORT : IMAP4_PORT;
704 ssl_type = account->ssl_imap;
706 port = account->set_imapport ? account->imapport
710 if (account->set_tunnelcmd) {
711 log_message(_("creating tunneled IMAP4 connection\n"));
713 if ((imap_sock = imap_open_tunnel(account->recv_server,
717 if ((imap_sock = imap_open_tunnel(account->recv_server,
718 account->tunnelcmd)) == NULL)
722 g_return_val_if_fail(account->recv_server != NULL, NULL);
724 log_message(_("creating IMAP4 connection to %s:%d ...\n"),
725 account->recv_server, port);
728 if ((imap_sock = imap_open(account->recv_server, port,
731 if ((imap_sock = imap_open(account->recv_server, port)) == NULL)
736 session = g_new0(IMAPSession, 1);
737 session_init(SESSION(session));
738 SESSION(session)->type = SESSION_IMAP;
739 SESSION(session)->server = g_strdup(account->recv_server);
740 SESSION(session)->sock = imap_sock;
742 SESSION(session)->destroy = imap_session_destroy;
744 session->capability = NULL;
746 session->authenticated = FALSE;
747 session->mbox = NULL;
748 session->cmd_count = 0;
750 /* Only need to log in if the connection was not PREAUTH */
751 if (imap_greeting(session) != IMAP_SUCCESS) {
752 session_destroy(SESSION(session));
757 if (account->ssl_imap == SSL_STARTTLS &&
758 imap_has_capability(session, "STARTTLS")) {
761 ok = imap_cmd_starttls(session);
762 if (ok != IMAP_SUCCESS) {
763 log_warning(_("Can't start TLS session.\n"));
764 session_destroy(SESSION(session));
767 if (!ssl_init_socket_with_method(SESSION(session)->sock,
769 session_destroy(SESSION(session));
773 imap_free_capabilities(session);
774 session->authenticated = FALSE;
775 session->uidplus = FALSE;
776 session->cmd_count = 1;
778 if (imap_greeting(session) != IMAP_SUCCESS) {
779 session_destroy(SESSION(session));
784 log_message("IMAP connection is %s-authenticated\n",
785 (session->authenticated) ? "pre" : "un");
790 static void imap_session_authenticate(IMAPSession *session,
791 const PrefsAccount *account)
795 g_return_if_fail(account->userid != NULL);
797 pass = account->passwd;
800 tmp_pass = input_dialog_query_password(account->recv_server, account->userid);
803 Xstrdup_a(pass, tmp_pass, {g_free(tmp_pass); return;});
807 if (imap_auth(session, account->userid, pass, account->imap_auth_type) != IMAP_SUCCESS) {
808 imap_cmd_logout(session);
812 session->authenticated = TRUE;
815 static void imap_session_destroy(Session *session)
817 imap_free_capabilities(IMAP_SESSION(session));
818 g_free(IMAP_SESSION(session)->mbox);
819 sock_close(session->sock);
820 session->sock = NULL;
823 static gchar *imap_fetch_msg(Folder *folder, FolderItem *item, gint uid)
825 gchar *path, *filename;
826 IMAPSession *session;
829 g_return_val_if_fail(folder != NULL, NULL);
830 g_return_val_if_fail(item != NULL, NULL);
832 path = folder_item_get_path(item);
833 if (!is_dir_exist(path))
835 filename = g_strconcat(path, G_DIR_SEPARATOR_S, itos(uid), NULL);
838 if (is_file_exist(filename)) {
839 debug_print("message %d has been already cached.\n", uid);
843 session = imap_session_get(folder);
849 ok = imap_select(session, IMAP_FOLDER(folder), item->path,
850 NULL, NULL, NULL, NULL);
851 if (ok != IMAP_SUCCESS) {
852 g_warning("can't select mailbox %s\n", item->path);
857 debug_print("getting message %d...\n", uid);
858 ok = imap_cmd_fetch(session, (guint32)uid, filename);
860 if (ok != IMAP_SUCCESS) {
861 g_warning("can't fetch message %d\n", uid);
869 static gint imap_add_msg(Folder *folder, FolderItem *dest,
870 const gchar *file, MsgFlags *flags)
874 MsgFileInfo fileinfo;
876 g_return_val_if_fail(file != NULL, -1);
878 fileinfo.msginfo = NULL;
879 fileinfo.file = (gchar *)file;
880 fileinfo.flags = flags;
881 file_list.data = &fileinfo;
882 file_list.next = NULL;
884 ret = imap_add_msgs(folder, dest, &file_list, NULL);
888 static gint imap_add_msgs(Folder *folder, FolderItem *dest, GSList *file_list,
892 IMAPSession *session;
893 guint32 last_uid = 0;
895 MsgFileInfo *fileinfo;
898 g_return_val_if_fail(folder != NULL, -1);
899 g_return_val_if_fail(dest != NULL, -1);
900 g_return_val_if_fail(file_list != NULL, -1);
902 session = imap_session_get(folder);
903 if (!session) return -1;
905 destdir = imap_get_real_path(IMAP_FOLDER(folder), dest->path);
907 for (cur = file_list; cur != NULL; cur = cur->next) {
908 IMAPFlags iflags = 0;
911 fileinfo = (MsgFileInfo *)cur->data;
913 if (fileinfo->flags) {
914 if (MSG_IS_MARKED(*fileinfo->flags))
915 iflags |= IMAP_FLAG_FLAGGED;
916 if (MSG_IS_REPLIED(*fileinfo->flags))
917 iflags |= IMAP_FLAG_ANSWERED;
918 if (!MSG_IS_UNREAD(*fileinfo->flags))
919 iflags |= IMAP_FLAG_SEEN;
922 if (dest->stype == F_OUTBOX ||
923 dest->stype == F_QUEUE ||
924 dest->stype == F_DRAFT ||
925 dest->stype == F_TRASH)
926 iflags |= IMAP_FLAG_SEEN;
928 ok = imap_cmd_append(session, destdir, fileinfo->file, iflags,
931 if (ok != IMAP_SUCCESS) {
932 g_warning("can't append message %s\n", fileinfo->file);
937 if (relation != NULL)
938 g_relation_insert(relation, fileinfo->msginfo != NULL ?
939 (gpointer) fileinfo->msginfo : (gpointer) fileinfo,
940 GINT_TO_POINTER(dest->last_num + 1));
941 if (last_uid < new_uid)
950 static gint imap_do_copy_msgs(Folder *folder, FolderItem *dest,
951 MsgInfoList *msglist, GRelation *relation)
955 GSList *seq_list, *cur;
957 IMAPSession *session;
958 gint ok = IMAP_SUCCESS;
959 GRelation *uid_mapping;
962 g_return_val_if_fail(folder != NULL, -1);
963 g_return_val_if_fail(dest != NULL, -1);
964 g_return_val_if_fail(msglist != NULL, -1);
966 session = imap_session_get(folder);
967 if (!session) return -1;
969 msginfo = (MsgInfo *)msglist->data;
971 src = msginfo->folder;
973 g_warning("the src folder is identical to the dest.\n");
977 ok = imap_select(session, IMAP_FOLDER(folder), msginfo->folder->path,
978 NULL, NULL, NULL, NULL);
979 if (ok != IMAP_SUCCESS)
982 destdir = imap_get_real_path(IMAP_FOLDER(folder), dest->path);
983 seq_list = imap_get_seq_set_from_msglist(msglist);
984 uid_mapping = g_relation_new(2);
985 g_relation_index(uid_mapping, 0, g_direct_hash, g_direct_equal);
987 for (cur = seq_list; cur != NULL; cur = g_slist_next(cur)) {
988 gchar *seq_set = (gchar *)cur->data;
990 debug_print("Copying message %s%c[%s] to %s ...\n",
991 src->path, G_DIR_SEPARATOR,
994 ok = imap_cmd_copy(session, seq_set, destdir, uid_mapping);
995 if (ok != IMAP_SUCCESS) {
996 g_relation_destroy(uid_mapping);
997 imap_seq_set_free(seq_list);
1002 for (cur = msglist; cur != NULL; cur = g_slist_next(cur)) {
1003 MsgInfo *msginfo = (MsgInfo *)cur->data;
1006 tuples = g_relation_select(uid_mapping,
1007 GINT_TO_POINTER(msginfo->msgnum),
1009 if (tuples->len > 0) {
1010 gint num = GPOINTER_TO_INT(g_tuples_index(tuples, 0, 1));
1011 g_relation_insert(relation, msginfo,
1012 GPOINTER_TO_INT(num));
1016 g_relation_insert(relation, msginfo,
1017 GPOINTER_TO_INT(0));
1018 g_tuples_destroy(tuples);
1021 g_relation_destroy(uid_mapping);
1022 imap_seq_set_free(seq_list);
1026 if (ok == IMAP_SUCCESS)
1032 static gint imap_copy_msg(Folder *folder, FolderItem *dest, MsgInfo *msginfo)
1036 g_return_val_if_fail(msginfo != NULL, -1);
1038 msglist.data = msginfo;
1039 msglist.next = NULL;
1041 return imap_copy_msgs(folder, dest, &msglist, NULL);
1044 static gint imap_copy_msgs(Folder *folder, FolderItem *dest,
1045 MsgInfoList *msglist, GRelation *relation)
1051 g_return_val_if_fail(folder != NULL, -1);
1052 g_return_val_if_fail(dest != NULL, -1);
1053 g_return_val_if_fail(msglist != NULL, -1);
1055 msginfo = (MsgInfo *)msglist->data;
1056 g_return_val_if_fail(msginfo->folder != NULL, -1);
1058 if (folder == msginfo->folder->folder)
1059 return imap_do_copy_msgs(folder, dest, msglist, relation);
1061 file_list = procmsg_get_message_file_list(msglist);
1062 g_return_val_if_fail(file_list != NULL, -1);
1064 ret = imap_add_msgs(folder, dest, file_list, relation);
1066 procmsg_message_file_list_free(file_list);
1071 static gint imap_remove_msg(Folder *folder, FolderItem *item, gint uid)
1074 IMAPSession *session;
1076 MsgNumberList numlist;
1078 g_return_val_if_fail(folder != NULL, -1);
1079 g_return_val_if_fail(FOLDER_CLASS(folder) == &imap_class, -1);
1080 g_return_val_if_fail(item != NULL, -1);
1082 session = imap_session_get(folder);
1083 if (!session) return -1;
1085 ok = imap_select(session, IMAP_FOLDER(folder), item->path,
1086 NULL, NULL, NULL, NULL);
1087 if (ok != IMAP_SUCCESS)
1090 numlist.next = NULL;
1091 numlist.data = GINT_TO_POINTER(uid);
1093 ok = imap_set_message_flags
1094 (IMAP_SESSION(REMOTE_FOLDER(folder)->session),
1095 &numlist, IMAP_FLAG_DELETED, TRUE);
1096 if (ok != IMAP_SUCCESS) {
1097 log_warning(_("can't set deleted flags: %d\n"), uid);
1101 if (!session->uidplus) {
1102 ok = imap_cmd_expunge(session, NULL);
1106 uidstr = g_strdup_printf("%u", uid);
1107 ok = imap_cmd_expunge(session, uidstr);
1110 if (ok != IMAP_SUCCESS) {
1111 log_warning(_("can't expunge\n"));
1115 IMAP_FOLDER_ITEM(item)->uid_list = g_slist_remove(
1116 IMAP_FOLDER_ITEM(item)->uid_list, numlist.data);
1117 dir = folder_item_get_path(item);
1118 if (is_dir_exist(dir))
1119 remove_numbered_files(dir, uid, uid);
1122 return IMAP_SUCCESS;
1125 static gint imap_remove_all_msg(Folder *folder, FolderItem *item)
1128 IMAPSession *session;
1131 g_return_val_if_fail(folder != NULL, -1);
1132 g_return_val_if_fail(item != NULL, -1);
1134 session = imap_session_get(folder);
1135 if (!session) return -1;
1137 ok = imap_select(session, IMAP_FOLDER(folder), item->path,
1138 NULL, NULL, NULL, NULL);
1139 if (ok != IMAP_SUCCESS)
1142 imap_gen_send(session, "STORE 1:* +FLAGS.SILENT (\\Deleted)");
1143 ok = imap_cmd_ok(session, NULL);
1144 if (ok != IMAP_SUCCESS) {
1145 log_warning(_("can't set deleted flags: 1:*\n"));
1149 ok = imap_cmd_expunge(session, NULL);
1150 if (ok != IMAP_SUCCESS) {
1151 log_warning(_("can't expunge\n"));
1155 dir = folder_item_get_path(item);
1156 if (is_dir_exist(dir))
1157 remove_all_numbered_files(dir);
1160 return IMAP_SUCCESS;
1163 static gboolean imap_is_msg_changed(Folder *folder, FolderItem *item,
1166 /* TODO: properly implement this method */
1170 static gint imap_close(Folder *folder, FolderItem *item)
1173 IMAPSession *session;
1175 g_return_val_if_fail(folder != NULL, -1);
1176 g_return_val_if_fail(item != NULL, -1);
1177 g_return_val_if_fail(item->path != NULL, -1);
1179 session = imap_session_get(folder);
1180 if (!session) return -1;
1182 if (session->mbox) {
1183 if (strcmp2(session->mbox, item->path) != 0) return -1;
1185 ok = imap_cmd_close(session);
1186 if (ok != IMAP_SUCCESS)
1187 log_warning(_("can't close folder\n"));
1189 g_free(session->mbox);
1190 session->mbox = NULL;
1198 static gint imap_scan_tree(Folder *folder)
1200 FolderItem *item = NULL;
1201 IMAPSession *session;
1202 gchar *root_folder = NULL;
1204 g_return_val_if_fail(folder != NULL, -1);
1205 g_return_val_if_fail(folder->account != NULL, -1);
1207 session = imap_session_get(folder);
1209 if (!folder->node) {
1210 folder_tree_destroy(folder);
1211 item = folder_item_new(folder, folder->name, NULL);
1212 item->folder = folder;
1213 folder->node = item->node = g_node_new(item);
1218 if (folder->account->imap_dir && *folder->account->imap_dir) {
1223 Xstrdup_a(root_folder, folder->account->imap_dir, return -1);
1224 extract_quote(root_folder, '"');
1225 subst_char(root_folder,
1226 imap_get_path_separator(IMAP_FOLDER(folder),
1229 strtailchomp(root_folder, '/');
1230 real_path = imap_get_real_path
1231 (IMAP_FOLDER(folder), root_folder);
1232 debug_print("IMAP root directory: %s\n", real_path);
1234 /* check if root directory exist */
1235 argbuf = g_ptr_array_new();
1236 ok = imap_cmd_list(session, NULL, real_path, argbuf);
1237 if (ok != IMAP_SUCCESS ||
1238 search_array_str(argbuf, "LIST ") == NULL) {
1239 log_warning(_("root folder %s does not exist\n"), real_path);
1240 g_ptr_array_free(argbuf, TRUE);
1243 if (!folder->node) {
1244 item = folder_item_new(folder, folder->name, NULL);
1245 item->folder = folder;
1246 folder->node = item->node = g_node_new(item);
1250 g_ptr_array_free(argbuf, TRUE);
1255 item = FOLDER_ITEM(folder->node->data);
1256 if (!item || ((item->path || root_folder) &&
1257 strcmp2(item->path, root_folder) != 0)) {
1258 folder_tree_destroy(folder);
1259 item = folder_item_new(folder, folder->name, root_folder);
1260 item->folder = folder;
1261 folder->node = item->node = g_node_new(item);
1264 imap_scan_tree_recursive(session, FOLDER_ITEM(folder->node->data));
1265 imap_create_missing_folders(folder);
1270 static gint imap_scan_tree_recursive(IMAPSession *session, FolderItem *item)
1273 IMAPFolder *imapfolder;
1274 FolderItem *new_item;
1275 GSList *item_list, *cur;
1278 gchar *wildcard_path;
1282 g_return_val_if_fail(item != NULL, -1);
1283 g_return_val_if_fail(item->folder != NULL, -1);
1284 g_return_val_if_fail(item->no_sub == FALSE, -1);
1286 folder = item->folder;
1287 imapfolder = IMAP_FOLDER(folder);
1289 separator = imap_get_path_separator(imapfolder, item->path);
1291 if (folder->ui_func)
1292 folder->ui_func(folder, item, folder->ui_func_data);
1295 wildcard[0] = separator;
1298 real_path = imap_get_real_path(imapfolder, item->path);
1302 real_path = g_strdup("");
1305 Xstrcat_a(wildcard_path, real_path, wildcard,
1306 {g_free(real_path); return IMAP_ERROR;});
1307 QUOTE_IF_REQUIRED(wildcard_path, wildcard_path);
1309 imap_gen_send(session, "LIST \"\" %s",
1312 strtailchomp(real_path, separator);
1313 item_list = imap_parse_list(imapfolder, session, real_path, NULL);
1316 node = item->node->children;
1317 while (node != NULL) {
1318 FolderItem *old_item = FOLDER_ITEM(node->data);
1319 GNode *next = node->next;
1322 for (cur = item_list; cur != NULL; cur = cur->next) {
1323 FolderItem *cur_item = FOLDER_ITEM(cur->data);
1324 if (!strcmp2(old_item->path, cur_item->path)) {
1325 new_item = cur_item;
1330 debug_print("folder '%s' not found. removing...\n",
1332 folder_item_remove(old_item);
1334 old_item->no_sub = new_item->no_sub;
1335 old_item->no_select = new_item->no_select;
1336 if (old_item->no_sub == TRUE && node->children) {
1337 debug_print("folder '%s' doesn't have "
1338 "subfolders. removing...\n",
1340 folder_item_remove_children(old_item);
1347 for (cur = item_list; cur != NULL; cur = cur->next) {
1348 FolderItem *cur_item = FOLDER_ITEM(cur->data);
1350 for (node = item->node->children; node != NULL;
1351 node = node->next) {
1352 if (!strcmp2(FOLDER_ITEM(node->data)->path,
1354 new_item = FOLDER_ITEM(node->data);
1355 folder_item_destroy(cur_item);
1361 new_item = cur_item;
1362 debug_print("new folder '%s' found.\n", new_item->path);
1363 folder_item_append(item, new_item);
1366 if (!strcmp(new_item->path, "INBOX")) {
1367 new_item->stype = F_INBOX;
1368 folder->inbox = new_item;
1369 } else if (!folder_item_parent(item) || item->stype == F_INBOX) {
1372 base = g_basename(new_item->path);
1374 if (!folder->outbox && !strcasecmp(base, "Sent")) {
1375 new_item->stype = F_OUTBOX;
1376 folder->outbox = new_item;
1377 } else if (!folder->draft && !strcasecmp(base, "Drafts")) {
1378 new_item->stype = F_DRAFT;
1379 folder->draft = new_item;
1380 } else if (!folder->queue && !strcasecmp(base, "Queue")) {
1381 new_item->stype = F_QUEUE;
1382 folder->queue = new_item;
1383 } else if (!folder->trash && !strcasecmp(base, "Trash")) {
1384 new_item->stype = F_TRASH;
1385 folder->trash = new_item;
1389 if (new_item->no_sub == FALSE)
1390 imap_scan_tree_recursive(session, new_item);
1393 g_slist_free(item_list);
1395 return IMAP_SUCCESS;
1398 static GSList *imap_parse_list(IMAPFolder *folder, IMAPSession *session,
1399 const gchar *real_path, gchar *separator)
1401 gchar buf[IMAPBUFSIZE];
1403 gchar separator_str[16];
1406 gchar *loc_name, *loc_path;
1407 GSList *item_list = NULL;
1409 FolderItem *new_item;
1411 debug_print("getting list of %s ...\n",
1412 *real_path ? real_path : "\"\"");
1414 str = g_string_new(NULL);
1417 if (sock_gets(SESSION(session)->sock, buf, sizeof(buf)) <= 0) {
1418 log_warning(_("error occurred while getting LIST.\n"));
1422 if (buf[0] != '*' || buf[1] != ' ') {
1423 log_print("IMAP4< %s\n", buf);
1424 if (sscanf(buf, "%*d %16s", buf) < 1 ||
1425 strcmp(buf, "OK") != 0)
1426 log_warning(_("error occurred while getting LIST.\n"));
1430 debug_print("IMAP4< %s\n", buf);
1432 g_string_assign(str, buf);
1434 if (strncmp(p, "LIST ", 5) != 0) continue;
1437 if (*p != '(') continue;
1439 p = strchr_cpy(p, ')', flags, sizeof(flags));
1441 while (*p == ' ') p++;
1443 p = strchr_cpy(p, ' ', separator_str, sizeof(separator_str));
1445 extract_quote(separator_str, '"');
1446 if (!strcmp(separator_str, "NIL"))
1447 separator_str[0] = '\0';
1449 *separator = separator_str[0];
1452 while (*p == ' ') p++;
1453 if (*p == '{' || *p == '"')
1454 p = imap_parse_atom(SESSION(session)->sock, p,
1455 buf, sizeof(buf), str);
1457 strncpy2(buf, p, sizeof(buf));
1458 strtailchomp(buf, separator_str[0]);
1459 if (buf[0] == '\0') continue;
1460 if (!strcmp(buf, real_path)) continue;
1462 if (separator_str[0] != '\0')
1463 subst_char(buf, separator_str[0], '/');
1464 name = g_basename(buf);
1465 if (name[0] == '.') continue;
1467 loc_name = imap_modified_utf7_to_locale(name);
1468 loc_path = imap_modified_utf7_to_locale(buf);
1469 new_item = folder_item_new(FOLDER(folder), loc_name, loc_path);
1470 if (strcasestr(flags, "\\Noinferiors") != NULL)
1471 new_item->no_sub = TRUE;
1472 if (strcmp(buf, "INBOX") != 0 &&
1473 strcasestr(flags, "\\Noselect") != NULL)
1474 new_item->no_select = TRUE;
1476 item_list = g_slist_append(item_list, new_item);
1478 debug_print("folder '%s' found.\n", loc_path);
1483 g_string_free(str, TRUE);
1488 static gint imap_create_tree(Folder *folder)
1490 g_return_val_if_fail(folder != NULL, -1);
1491 g_return_val_if_fail(folder->node != NULL, -1);
1492 g_return_val_if_fail(folder->node->data != NULL, -1);
1493 g_return_val_if_fail(folder->account != NULL, -1);
1495 imap_scan_tree(folder);
1496 imap_create_missing_folders(folder);
1501 static void imap_create_missing_folders(Folder *folder)
1503 g_return_if_fail(folder != NULL);
1506 folder->inbox = imap_create_special_folder
1507 (folder, F_INBOX, "INBOX");
1509 if (!folder->outbox)
1510 folder->outbox = imap_create_special_folder
1511 (folder, F_OUTBOX, "Sent");
1513 folder->draft = imap_create_special_folder
1514 (folder, F_DRAFT, "Drafts");
1516 folder->queue = imap_create_special_folder
1517 (folder, F_QUEUE, "Queue");
1520 folder->trash = imap_create_special_folder
1521 (folder, F_TRASH, "Trash");
1524 static FolderItem *imap_create_special_folder(Folder *folder,
1525 SpecialFolderItemType stype,
1529 FolderItem *new_item;
1531 g_return_val_if_fail(folder != NULL, NULL);
1532 g_return_val_if_fail(folder->node != NULL, NULL);
1533 g_return_val_if_fail(folder->node->data != NULL, NULL);
1534 g_return_val_if_fail(folder->account != NULL, NULL);
1535 g_return_val_if_fail(name != NULL, NULL);
1537 item = FOLDER_ITEM(folder->node->data);
1538 new_item = imap_create_folder(folder, item, name);
1541 g_warning("Can't create '%s'\n", name);
1542 if (!folder->inbox) return NULL;
1544 new_item = imap_create_folder(folder, folder->inbox, name);
1546 g_warning("Can't create '%s' under INBOX\n", name);
1548 new_item->stype = stype;
1550 new_item->stype = stype;
1555 static gchar *imap_folder_get_path(Folder *folder)
1559 g_return_val_if_fail(folder != NULL, NULL);
1560 g_return_val_if_fail(folder->account != NULL, NULL);
1562 folder_path = g_strconcat(get_imap_cache_dir(),
1564 folder->account->recv_server,
1566 folder->account->userid,
1572 static gchar *imap_item_get_path(Folder *folder, FolderItem *item)
1574 gchar *folder_path, *path;
1576 g_return_val_if_fail(folder != NULL, NULL);
1577 g_return_val_if_fail(item != NULL, NULL);
1578 folder_path = imap_folder_get_path(folder);
1580 g_return_val_if_fail(folder_path != NULL, NULL);
1581 if (folder_path[0] == G_DIR_SEPARATOR) {
1583 path = g_strconcat(folder_path, G_DIR_SEPARATOR_S,
1586 path = g_strdup(folder_path);
1589 path = g_strconcat(get_home_dir(), G_DIR_SEPARATOR_S,
1590 folder_path, G_DIR_SEPARATOR_S,
1593 path = g_strconcat(get_home_dir(), G_DIR_SEPARATOR_S,
1596 g_free(folder_path);
1601 static FolderItem *imap_create_folder(Folder *folder, FolderItem *parent,
1604 gchar *dirpath, *imap_path;
1605 IMAPSession *session;
1606 FolderItem *new_item;
1612 g_return_val_if_fail(folder != NULL, NULL);
1613 g_return_val_if_fail(folder->account != NULL, NULL);
1614 g_return_val_if_fail(parent != NULL, NULL);
1615 g_return_val_if_fail(name != NULL, NULL);
1617 session = imap_session_get(folder);
1618 if (!session) return NULL;
1620 if (!folder_item_parent(parent) && strcmp(name, "INBOX") == 0)
1621 dirpath = g_strdup(name);
1622 else if (parent->path)
1623 dirpath = g_strconcat(parent->path, "/", name, NULL);
1624 else if ((p = strchr(name, '/')) != NULL && *(p + 1) != '\0')
1625 dirpath = g_strdup(name);
1626 else if (folder->account->imap_dir && *folder->account->imap_dir) {
1629 Xstrdup_a(imap_dir, folder->account->imap_dir, return NULL);
1630 strtailchomp(imap_dir, '/');
1631 dirpath = g_strconcat(imap_dir, "/", name, NULL);
1633 dirpath = g_strdup(name);
1635 /* keep trailing directory separator to create a folder that contains
1637 imap_path = imap_locale_to_modified_utf7(dirpath);
1638 strtailchomp(dirpath, '/');
1639 Xstrdup_a(new_name, name, {g_free(dirpath); return NULL;});
1640 strtailchomp(new_name, '/');
1641 separator = imap_get_path_separator(IMAP_FOLDER(folder), imap_path);
1642 imap_path_separator_subst(imap_path, separator);
1643 subst_char(new_name, '/', separator);
1645 if (strcmp(name, "INBOX") != 0) {
1648 gboolean exist = FALSE;
1650 argbuf = g_ptr_array_new();
1651 ok = imap_cmd_list(session, NULL, imap_path,
1653 if (ok != IMAP_SUCCESS) {
1654 log_warning(_("can't create mailbox: LIST failed\n"));
1657 ptr_array_free_strings(argbuf);
1658 g_ptr_array_free(argbuf, TRUE);
1662 for (i = 0; i < argbuf->len; i++) {
1664 str = g_ptr_array_index(argbuf, i);
1665 if (!strncmp(str, "LIST ", 5)) {
1670 ptr_array_free_strings(argbuf);
1671 g_ptr_array_free(argbuf, TRUE);
1674 ok = imap_cmd_create(session, imap_path);
1675 if (ok != IMAP_SUCCESS) {
1676 log_warning(_("can't create mailbox\n"));
1684 new_item = folder_item_new(folder, new_name, dirpath);
1685 folder_item_append(parent, new_item);
1689 dirpath = folder_item_get_path(new_item);
1690 if (!is_dir_exist(dirpath))
1691 make_dir_hier(dirpath);
1697 static gint imap_rename_folder(Folder *folder, FolderItem *item,
1702 gchar *real_oldpath;
1703 gchar *real_newpath;
1705 gchar *old_cache_dir;
1706 gchar *new_cache_dir;
1707 IMAPSession *session;
1710 gint exists, recent, unseen;
1711 guint32 uid_validity;
1713 g_return_val_if_fail(folder != NULL, -1);
1714 g_return_val_if_fail(item != NULL, -1);
1715 g_return_val_if_fail(item->path != NULL, -1);
1716 g_return_val_if_fail(name != NULL, -1);
1718 if (strchr(name, imap_get_path_separator(IMAP_FOLDER(folder), item->path)) != NULL) {
1719 g_warning(_("New folder name must not contain the namespace "
1724 session = imap_session_get(folder);
1725 if (!session) return -1;
1727 real_oldpath = imap_get_real_path(IMAP_FOLDER(folder), item->path);
1729 g_free(session->mbox);
1730 session->mbox = NULL;
1731 ok = imap_cmd_examine(session, "INBOX",
1732 &exists, &recent, &unseen, &uid_validity);
1733 if (ok != IMAP_SUCCESS) {
1734 g_free(real_oldpath);
1738 separator = imap_get_path_separator(IMAP_FOLDER(folder), item->path);
1739 if (strchr(item->path, G_DIR_SEPARATOR)) {
1740 dirpath = g_dirname(item->path);
1741 newpath = g_strconcat(dirpath, G_DIR_SEPARATOR_S, name, NULL);
1744 newpath = g_strdup(name);
1746 real_newpath = imap_locale_to_modified_utf7(newpath);
1747 imap_path_separator_subst(real_newpath, separator);
1749 ok = imap_cmd_rename(session, real_oldpath, real_newpath);
1750 if (ok != IMAP_SUCCESS) {
1751 log_warning(_("can't rename mailbox: %s to %s\n"),
1752 real_oldpath, real_newpath);
1753 g_free(real_oldpath);
1755 g_free(real_newpath);
1760 item->name = g_strdup(name);
1762 old_cache_dir = folder_item_get_path(item);
1764 paths[0] = g_strdup(item->path);
1766 g_node_traverse(item->node, G_PRE_ORDER, G_TRAVERSE_ALL, -1,
1767 imap_rename_folder_func, paths);
1769 if (is_dir_exist(old_cache_dir)) {
1770 new_cache_dir = folder_item_get_path(item);
1771 if (rename(old_cache_dir, new_cache_dir) < 0) {
1772 FILE_OP_ERROR(old_cache_dir, "rename");
1774 g_free(new_cache_dir);
1777 g_free(old_cache_dir);
1780 g_free(real_oldpath);
1781 g_free(real_newpath);
1786 static gint imap_remove_folder(Folder *folder, FolderItem *item)
1789 IMAPSession *session;
1792 gint exists, recent, unseen;
1793 guint32 uid_validity;
1795 g_return_val_if_fail(folder != NULL, -1);
1796 g_return_val_if_fail(item != NULL, -1);
1797 g_return_val_if_fail(item->path != NULL, -1);
1799 session = imap_session_get(folder);
1800 if (!session) return -1;
1802 path = imap_get_real_path(IMAP_FOLDER(folder), item->path);
1804 ok = imap_cmd_examine(session, "INBOX",
1805 &exists, &recent, &unseen, &uid_validity);
1806 if (ok != IMAP_SUCCESS) {
1811 ok = imap_cmd_delete(session, path);
1812 if (ok != IMAP_SUCCESS) {
1813 log_warning(_("can't delete mailbox\n"));
1819 cache_dir = folder_item_get_path(item);
1820 if (is_dir_exist(cache_dir) && remove_dir_recursive(cache_dir) < 0)
1821 g_warning("can't remove directory '%s'\n", cache_dir);
1823 folder_item_remove(item);
1828 static GSList *imap_get_uncached_messages(IMAPSession *session,
1830 MsgNumberList *numlist)
1833 GSList *newlist = NULL;
1834 GSList *llast = NULL;
1837 GSList *seq_list, *cur;
1840 g_return_val_if_fail(session != NULL, NULL);
1841 g_return_val_if_fail(item != NULL, NULL);
1842 g_return_val_if_fail(item->folder != NULL, NULL);
1843 g_return_val_if_fail(FOLDER_CLASS(item->folder) == &imap_class, NULL);
1845 seq_list = imap_get_seq_set_from_numlist(numlist);
1846 for (cur = seq_list; cur != NULL; cur = g_slist_next(cur)) {
1847 imapset = cur->data;
1849 if (imap_cmd_envelope(session, imapset)
1851 log_warning(_("can't get envelope\n"));
1855 str = g_string_new(NULL);
1858 if ((tmp = sock_getline(SESSION(session)->sock)) == NULL) {
1859 log_warning(_("error occurred while getting envelope.\n"));
1860 g_string_free(str, TRUE);
1864 if (tmp[0] != '*' || tmp[1] != ' ') {
1865 log_print("IMAP4< %s\n", tmp);
1869 if (strstr(tmp, "FETCH") == NULL) {
1870 log_print("IMAP4< %s\n", tmp);
1874 log_print("IMAP4< %s\n", tmp);
1875 g_string_assign(str, tmp);
1878 msginfo = imap_parse_envelope
1879 (SESSION(session)->sock, item, str);
1881 log_warning(_("can't parse envelope: %s\n"), str->str);
1884 if (item->stype == F_QUEUE) {
1885 MSG_SET_TMP_FLAGS(msginfo->flags, MSG_QUEUED);
1886 } else if (item->stype == F_DRAFT) {
1887 MSG_SET_TMP_FLAGS(msginfo->flags, MSG_DRAFT);
1890 msginfo->folder = item;
1893 llast = newlist = g_slist_append(newlist, msginfo);
1895 llast = g_slist_append(llast, msginfo);
1896 llast = llast->next;
1900 g_string_free(str, TRUE);
1902 imap_seq_set_free(seq_list);
1904 session_set_access_time(SESSION(session));
1909 static void imap_delete_all_cached_messages(FolderItem *item)
1913 g_return_if_fail(item != NULL);
1914 g_return_if_fail(item->folder != NULL);
1915 g_return_if_fail(FOLDER_CLASS(item->folder) == &imap_class);
1917 debug_print("Deleting all cached messages...\n");
1919 dir = folder_item_get_path(item);
1920 if (is_dir_exist(dir))
1921 remove_all_numbered_files(dir);
1924 debug_print("done.\n");
1928 static SockInfo *imap_open_tunnel(const gchar *server,
1929 const gchar *tunnelcmd,
1932 static SockInfo *imap_open_tunnel(const gchar *server,
1933 const gchar *tunnelcmd)
1938 if ((sock = sock_connect_cmd(server, tunnelcmd)) == NULL) {
1939 log_warning(_("Can't establish IMAP4 session with: %s\n"),
1944 return imap_init_sock(sock, ssl_type);
1946 return imap_init_sock(sock);
1952 static SockInfo *imap_open(const gchar *server, gushort port,
1955 static SockInfo *imap_open(const gchar *server, gushort port)
1960 if ((sock = sock_connect(server, port)) == NULL) {
1961 log_warning(_("Can't connect to IMAP4 server: %s:%d\n"),
1967 if (ssl_type == SSL_TUNNEL && !ssl_init_socket(sock)) {
1968 log_warning(_("Can't establish IMAP4 session with: %s:%d\n"),
1978 static SockInfo *imap_init_sock(SockInfo *sock, SSLType ssl_type)
1980 static SockInfo *imap_init_sock(SockInfo *sock)
1987 static GList *imap_parse_namespace_str(gchar *str)
1992 IMAPNameSpace *namespace;
1993 GList *ns_list = NULL;
1995 while (*p != '\0') {
1996 /* parse ("#foo" "/") */
1998 while (*p && *p != '(') p++;
1999 if (*p == '\0') break;
2002 while (*p && *p != '"') p++;
2003 if (*p == '\0') break;
2007 while (*p && *p != '"') p++;
2008 if (*p == '\0') break;
2012 while (*p && isspace(*p)) p++;
2013 if (*p == '\0') break;
2014 if (strncmp(p, "NIL", 3) == 0)
2016 else if (*p == '"') {
2019 while (*p && *p != '"') p++;
2020 if (*p == '\0') break;
2025 while (*p && *p != ')') p++;
2026 if (*p == '\0') break;
2029 namespace = g_new(IMAPNameSpace, 1);
2030 namespace->name = g_strdup(name);
2031 namespace->separator = separator ? separator[0] : '\0';
2032 ns_list = g_list_append(ns_list, namespace);
2038 static void imap_parse_namespace(IMAPSession *session, IMAPFolder *folder)
2043 g_return_if_fail(session != NULL);
2044 g_return_if_fail(folder != NULL);
2046 if (folder->ns_personal != NULL ||
2047 folder->ns_others != NULL ||
2048 folder->ns_shared != NULL)
2051 if (!imap_has_capability(session, "NAMESPACE")) {
2052 imap_get_namespace_by_list(session, folder);
2056 if (imap_cmd_namespace(session, &ns_str)
2058 log_warning(_("can't get namespace\n"));
2062 str_array = strsplit_parenthesis(ns_str, '(', ')', 3);
2063 if (str_array == NULL) {
2065 imap_get_namespace_by_list(session, folder);
2069 folder->ns_personal = imap_parse_namespace_str(str_array[0]);
2070 if (str_array[0] && str_array[1])
2071 folder->ns_others = imap_parse_namespace_str(str_array[1]);
2072 if (str_array[0] && str_array[1] && str_array[2])
2073 folder->ns_shared = imap_parse_namespace_str(str_array[2]);
2074 g_strfreev(str_array);
2078 static void imap_get_namespace_by_list(IMAPSession *session, IMAPFolder *folder)
2080 GSList *item_list, *cur;
2081 gchar separator = '\0';
2082 IMAPNameSpace *namespace;
2084 g_return_if_fail(session != NULL);
2085 g_return_if_fail(folder != NULL);
2087 if (folder->ns_personal != NULL ||
2088 folder->ns_others != NULL ||
2089 folder->ns_shared != NULL)
2092 imap_gen_send(session, "LIST \"\" \"\"");
2093 item_list = imap_parse_list(folder, session, "", &separator);
2094 for (cur = item_list; cur != NULL; cur = cur->next)
2095 folder_item_destroy(FOLDER_ITEM(cur->data));
2096 g_slist_free(item_list);
2098 namespace = g_new(IMAPNameSpace, 1);
2099 namespace->name = g_strdup("");
2100 namespace->separator = separator;
2101 folder->ns_personal = g_list_append(NULL, namespace);
2104 static IMAPNameSpace *imap_find_namespace_from_list(GList *ns_list,
2107 IMAPNameSpace *namespace = NULL;
2108 gchar *tmp_path, *name;
2110 if (!path) path = "";
2112 for (; ns_list != NULL; ns_list = ns_list->next) {
2113 IMAPNameSpace *tmp_ns = ns_list->data;
2115 Xstrcat_a(tmp_path, path, "/", return namespace);
2116 Xstrdup_a(name, tmp_ns->name, return namespace);
2117 if (tmp_ns->separator && tmp_ns->separator != '/') {
2118 subst_char(tmp_path, tmp_ns->separator, '/');
2119 subst_char(name, tmp_ns->separator, '/');
2121 if (strncmp(tmp_path, name, strlen(name)) == 0)
2128 static IMAPNameSpace *imap_find_namespace(IMAPFolder *folder,
2131 IMAPNameSpace *namespace;
2133 g_return_val_if_fail(folder != NULL, NULL);
2135 namespace = imap_find_namespace_from_list(folder->ns_personal, path);
2136 if (namespace) return namespace;
2137 namespace = imap_find_namespace_from_list(folder->ns_others, path);
2138 if (namespace) return namespace;
2139 namespace = imap_find_namespace_from_list(folder->ns_shared, path);
2140 if (namespace) return namespace;
2145 static gchar imap_get_path_separator(IMAPFolder *folder, const gchar *path)
2147 IMAPNameSpace *namespace;
2148 gchar separator = '/';
2150 namespace = imap_find_namespace(folder, path);
2151 if (namespace && namespace->separator)
2152 separator = namespace->separator;
2157 static gchar *imap_get_real_path(IMAPFolder *folder, const gchar *path)
2162 g_return_val_if_fail(folder != NULL, NULL);
2163 g_return_val_if_fail(path != NULL, NULL);
2165 real_path = imap_locale_to_modified_utf7(path);
2166 separator = imap_get_path_separator(folder, path);
2167 imap_path_separator_subst(real_path, separator);
2172 static gchar *imap_parse_atom(SockInfo *sock, gchar *src,
2173 gchar *dest, gint dest_len, GString *str)
2175 gchar *cur_pos = src;
2178 g_return_val_if_fail(str != NULL, cur_pos);
2180 /* read the next line if the current response buffer is empty */
2181 while (isspace(*(guchar *)cur_pos)) cur_pos++;
2182 while (*cur_pos == '\0') {
2183 if ((nextline = sock_getline(sock)) == NULL)
2185 g_string_assign(str, nextline);
2187 strretchomp(nextline);
2188 /* log_print("IMAP4< %s\n", nextline); */
2189 debug_print("IMAP4< %s\n", nextline);
2192 while (isspace(*(guchar *)cur_pos)) cur_pos++;
2195 if (!strncmp(cur_pos, "NIL", 3)) {
2198 } else if (*cur_pos == '\"') {
2201 p = get_quoted(cur_pos, '\"', dest, dest_len);
2202 cur_pos = p ? p : cur_pos + 2;
2203 } else if (*cur_pos == '{') {
2208 cur_pos = strchr_cpy(cur_pos + 1, '}', buf, sizeof(buf));
2210 g_return_val_if_fail(len >= 0, cur_pos);
2212 g_string_truncate(str, 0);
2216 if ((nextline = sock_getline(sock)) == NULL)
2218 line_len += strlen(nextline);
2219 g_string_append(str, nextline);
2221 strretchomp(nextline);
2222 /* log_print("IMAP4< %s\n", nextline); */
2223 debug_print("IMAP4< %s\n", nextline);
2225 } while (line_len < len);
2227 memcpy(dest, cur_pos, MIN(len, dest_len - 1));
2228 dest[MIN(len, dest_len - 1)] = '\0';
2235 static gchar *imap_get_header(SockInfo *sock, gchar *cur_pos, gchar **headers,
2245 g_return_val_if_fail(str != NULL, cur_pos);
2247 while (isspace(*(guchar *)cur_pos)) cur_pos++;
2249 g_return_val_if_fail(*cur_pos == '{', cur_pos);
2251 cur_pos = strchr_cpy(cur_pos + 1, '}', buf, sizeof(buf));
2253 g_return_val_if_fail(len >= 0, cur_pos);
2255 g_string_truncate(str, 0);
2259 if ((nextline = sock_getline(sock)) == NULL)
2261 block_len += strlen(nextline);
2262 g_string_append(str, nextline);
2264 strretchomp(nextline);
2265 /* debug_print("IMAP4< %s\n", nextline); */
2267 } while (block_len < len);
2269 debug_print("IMAP4< [contents of BODY.PEEK[HEADER.FIELDS (...)]]\n");
2271 *headers = g_strndup(cur_pos, len);
2274 while (isspace(*(guchar *)cur_pos)) cur_pos++;
2275 while (*cur_pos == '\0') {
2276 if ((nextline = sock_getline(sock)) == NULL)
2278 g_string_assign(str, nextline);
2280 strretchomp(nextline);
2281 debug_print("IMAP4< %s\n", nextline);
2284 while (isspace(*(guchar *)cur_pos)) cur_pos++;
2290 static MsgFlags imap_parse_flags(const gchar *flag_str)
2292 const gchar *p = flag_str;
2293 MsgFlags flags = {0, 0};
2295 flags.perm_flags = MSG_UNREAD;
2297 while ((p = strchr(p, '\\')) != NULL) {
2300 if (g_strncasecmp(p, "Recent", 6) == 0 && MSG_IS_UNREAD(flags)) {
2301 MSG_SET_PERM_FLAGS(flags, MSG_NEW);
2302 } else if (g_strncasecmp(p, "Seen", 4) == 0) {
2303 MSG_UNSET_PERM_FLAGS(flags, MSG_NEW|MSG_UNREAD);
2304 } else if (g_strncasecmp(p, "Deleted", 7) == 0) {
2305 MSG_SET_PERM_FLAGS(flags, MSG_DELETED);
2306 } else if (g_strncasecmp(p, "Flagged", 7) == 0) {
2307 MSG_SET_PERM_FLAGS(flags, MSG_MARKED);
2308 } else if (g_strncasecmp(p, "Answered", 8) == 0) {
2309 MSG_SET_PERM_FLAGS(flags, MSG_REPLIED);
2316 static MsgInfo *imap_parse_envelope(SockInfo *sock, FolderItem *item,
2319 gchar buf[IMAPBUFSIZE];
2320 MsgInfo *msginfo = NULL;
2325 MsgFlags flags = {0, 0}, imap_flags = {0, 0};
2327 g_return_val_if_fail(line_str != NULL, NULL);
2328 g_return_val_if_fail(line_str->str[0] == '*' &&
2329 line_str->str[1] == ' ', NULL);
2331 MSG_SET_TMP_FLAGS(flags, MSG_IMAP);
2332 if (item->stype == F_QUEUE) {
2333 MSG_SET_TMP_FLAGS(flags, MSG_QUEUED);
2334 } else if (item->stype == F_DRAFT) {
2335 MSG_SET_TMP_FLAGS(flags, MSG_DRAFT);
2338 cur_pos = line_str->str + 2;
2340 #define PARSE_ONE_ELEMENT(ch) \
2342 cur_pos = strchr_cpy(cur_pos, ch, buf, sizeof(buf)); \
2343 if (cur_pos == NULL) { \
2344 g_warning("cur_pos == NULL\n"); \
2345 procmsg_msginfo_free(msginfo); \
2350 PARSE_ONE_ELEMENT(' ');
2353 PARSE_ONE_ELEMENT(' ');
2354 g_return_val_if_fail(!strcmp(buf, "FETCH"), NULL);
2356 g_return_val_if_fail(*cur_pos == '(', NULL);
2359 while (*cur_pos != '\0' && *cur_pos != ')') {
2360 while (*cur_pos == ' ') cur_pos++;
2362 if (!strncmp(cur_pos, "UID ", 4)) {
2364 uid = strtoul(cur_pos, &cur_pos, 10);
2365 } else if (!strncmp(cur_pos, "FLAGS ", 6)) {
2367 if (*cur_pos != '(') {
2368 g_warning("*cur_pos != '('\n");
2369 procmsg_msginfo_free(msginfo);
2373 PARSE_ONE_ELEMENT(')');
2374 imap_flags = imap_parse_flags(buf);
2375 } else if (!strncmp(cur_pos, "RFC822.SIZE ", 12)) {
2377 size = strtol(cur_pos, &cur_pos, 10);
2378 } else if (!strncmp(cur_pos, "BODY[HEADER.FIELDS ", 19)) {
2382 if (*cur_pos != '(') {
2383 g_warning("*cur_pos != '('\n");
2384 procmsg_msginfo_free(msginfo);
2388 PARSE_ONE_ELEMENT(')');
2389 if (*cur_pos != ']') {
2390 g_warning("*cur_pos != ']'\n");
2391 procmsg_msginfo_free(msginfo);
2396 cur_pos = imap_get_header(sock, cur_pos, &headers,
2398 msginfo = procheader_parse_str(headers, flags, FALSE, FALSE);
2401 g_warning("invalid FETCH response: %s\n", cur_pos);
2407 msginfo->msgnum = uid;
2408 msginfo->size = size;
2409 msginfo->flags.tmp_flags |= imap_flags.tmp_flags;
2410 msginfo->flags.perm_flags = imap_flags.perm_flags;
2416 static gchar *imap_get_flag_str(IMAPFlags flags)
2421 str = g_string_new(NULL);
2423 if (IMAP_IS_SEEN(flags)) g_string_append(str, "\\Seen ");
2424 if (IMAP_IS_ANSWERED(flags)) g_string_append(str, "\\Answered ");
2425 if (IMAP_IS_FLAGGED(flags)) g_string_append(str, "\\Flagged ");
2426 if (IMAP_IS_DELETED(flags)) g_string_append(str, "\\Deleted ");
2427 if (IMAP_IS_DRAFT(flags)) g_string_append(str, "\\Draft");
2429 if (str->len > 0 && str->str[str->len - 1] == ' ')
2430 g_string_truncate(str, str->len - 1);
2433 g_string_free(str, FALSE);
2438 static gint imap_set_message_flags(IMAPSession *session,
2439 MsgNumberList *numlist,
2446 GSList *seq_list, *cur;
2449 flag_str = imap_get_flag_str(flags);
2450 cmd = g_strconcat(is_set ? "+FLAGS.SILENT (" : "-FLAGS.SILENT (",
2451 flag_str, ")", NULL);
2454 seq_list = imap_get_seq_set_from_numlist(numlist);
2455 for (cur = seq_list; cur != NULL; cur = g_slist_next(cur)) {
2456 imapset = cur->data;
2458 ok = imap_cmd_store(session, imapset, cmd);
2460 imap_seq_set_free(seq_list);
2466 static gint imap_select(IMAPSession *session, IMAPFolder *folder,
2468 gint *exists, gint *recent, gint *unseen,
2469 guint32 *uid_validity)
2473 gint exists_, recent_, unseen_;
2474 guint32 uid_validity_;
2476 if (!exists || !recent || !unseen || !uid_validity) {
2477 if (session->mbox && strcmp(session->mbox, path) == 0)
2478 return IMAP_SUCCESS;
2482 uid_validity = &uid_validity_;
2485 g_free(session->mbox);
2486 session->mbox = NULL;
2488 real_path = imap_get_real_path(folder, path);
2489 ok = imap_cmd_select(session, real_path,
2490 exists, recent, unseen, uid_validity);
2491 if (ok != IMAP_SUCCESS)
2492 log_warning(_("can't select folder: %s\n"), real_path);
2494 session->mbox = g_strdup(path);
2495 session->folder_content_changed = FALSE;
2502 #define THROW(err) { ok = err; goto catch; }
2504 static gint imap_status(IMAPSession *session, IMAPFolder *folder,
2506 gint *messages, gint *recent,
2507 guint32 *uid_next, guint32 *uid_validity,
2513 GPtrArray *argbuf = NULL;
2516 if (messages && recent && uid_next && uid_validity && unseen) {
2517 *messages = *recent = *uid_next = *uid_validity = *unseen = 0;
2518 argbuf = g_ptr_array_new();
2521 real_path = imap_get_real_path(folder, path);
2522 QUOTE_IF_REQUIRED(real_path_, real_path);
2523 imap_gen_send(session, "STATUS %s "
2524 "(MESSAGES RECENT UIDNEXT UIDVALIDITY UNSEEN)",
2527 ok = imap_cmd_ok(session, argbuf);
2528 if (ok != IMAP_SUCCESS || !argbuf) THROW(ok);
2530 str = search_array_str(argbuf, "STATUS");
2531 if (!str) THROW(IMAP_ERROR);
2533 str = strchr(str, '(');
2534 if (!str) THROW(IMAP_ERROR);
2536 while (*str != '\0' && *str != ')') {
2537 while (*str == ' ') str++;
2539 if (!strncmp(str, "MESSAGES ", 9)) {
2541 *messages = strtol(str, &str, 10);
2542 } else if (!strncmp(str, "RECENT ", 7)) {
2544 *recent = strtol(str, &str, 10);
2545 } else if (!strncmp(str, "UIDNEXT ", 8)) {
2547 *uid_next = strtoul(str, &str, 10);
2548 } else if (!strncmp(str, "UIDVALIDITY ", 12)) {
2550 *uid_validity = strtoul(str, &str, 10);
2551 } else if (!strncmp(str, "UNSEEN ", 7)) {
2553 *unseen = strtol(str, &str, 10);
2555 g_warning("invalid STATUS response: %s\n", str);
2563 ptr_array_free_strings(argbuf);
2564 g_ptr_array_free(argbuf, TRUE);
2572 static gboolean imap_has_capability(IMAPSession *session, const gchar *cap)
2576 for (p = session->capability; *p != NULL; ++p) {
2577 if (!g_strcasecmp(*p, cap))
2584 static void imap_free_capabilities(IMAPSession *session)
2586 g_strfreev(session->capability);
2587 session->capability = NULL;
2590 /* low-level IMAP4rev1 commands */
2592 static gint imap_cmd_authenticate(IMAPSession *session, const gchar *user,
2593 const gchar *pass, IMAPAuthType type)
2600 gchar hexdigest[33];
2604 auth_type = "CRAM-MD5";
2606 imap_gen_send(session, "AUTHENTICATE %s", auth_type);
2607 ok = imap_gen_recv(session, &buf);
2608 if (ok != IMAP_SUCCESS || buf[0] != '+' || buf[1] != ' ') {
2613 challenge = g_malloc(strlen(buf + 2) + 1);
2614 challenge_len = base64_decode(challenge, buf + 2, -1);
2615 challenge[challenge_len] = '\0';
2617 log_print("IMAP< [Decoded: %s]\n", challenge);
2619 md5_hex_hmac(hexdigest, challenge, challenge_len, pass, strlen(pass));
2622 response = g_strdup_printf("%s %s", user, hexdigest);
2623 log_print("IMAP> [Encoded: %s]\n", response);
2624 response64 = g_malloc((strlen(response) + 3) * 2 + 1);
2625 base64_encode(response64, response, strlen(response));
2628 log_print("IMAP> %s\n", response64);
2629 sock_puts(SESSION(session)->sock, response64);
2630 ok = imap_cmd_ok(session, NULL);
2631 if (ok != IMAP_SUCCESS)
2632 log_warning(_("IMAP4 authentication failed.\n"));
2637 static gint imap_cmd_login(IMAPSession *session,
2638 const gchar *user, const gchar *pass)
2640 gchar *user_, *pass_;
2643 QUOTE_IF_REQUIRED(user_, user);
2644 QUOTE_IF_REQUIRED(pass_, pass);
2645 imap_gen_send(session, "LOGIN %s %s", user_, pass_);
2647 ok = imap_cmd_ok(session, NULL);
2648 if (ok != IMAP_SUCCESS)
2649 log_warning(_("IMAP4 login failed.\n"));
2654 static gint imap_cmd_logout(IMAPSession *session)
2656 imap_gen_send(session, "LOGOUT");
2657 return imap_cmd_ok(session, NULL);
2660 static gint imap_cmd_noop(IMAPSession *session)
2662 imap_gen_send(session, "NOOP");
2663 return imap_cmd_ok(session, NULL);
2666 static gint imap_cmd_starttls(IMAPSession *session)
2668 imap_gen_send(session, "STARTTLS");
2669 return imap_cmd_ok(session, NULL);
2672 #define THROW(err) { ok = err; goto catch; }
2674 static gint imap_cmd_namespace(IMAPSession *session, gchar **ns_str)
2680 argbuf = g_ptr_array_new();
2682 imap_gen_send(session, "NAMESPACE");
2683 if ((ok = imap_cmd_ok(session, argbuf)) != IMAP_SUCCESS) THROW(ok);
2685 str = search_array_str(argbuf, "NAMESPACE");
2686 if (!str) THROW(IMAP_ERROR);
2688 *ns_str = g_strdup(str);
2691 ptr_array_free_strings(argbuf);
2692 g_ptr_array_free(argbuf, TRUE);
2699 static gint imap_cmd_list(IMAPSession *session, const gchar *ref,
2700 const gchar *mailbox, GPtrArray *argbuf)
2702 gchar *ref_, *mailbox_;
2704 if (!ref) ref = "\"\"";
2705 if (!mailbox) mailbox = "\"\"";
2707 QUOTE_IF_REQUIRED(ref_, ref);
2708 QUOTE_IF_REQUIRED(mailbox_, mailbox);
2709 imap_gen_send(session, "LIST %s %s", ref_, mailbox_);
2711 return imap_cmd_ok(session, argbuf);
2714 #define THROW goto catch
2716 static gint imap_cmd_do_select(IMAPSession *session, const gchar *folder,
2718 gint *exists, gint *recent, gint *unseen,
2719 guint32 *uid_validity)
2726 unsigned int uid_validity_;
2728 *exists = *recent = *unseen = *uid_validity = 0;
2729 argbuf = g_ptr_array_new();
2732 select_cmd = "EXAMINE";
2734 select_cmd = "SELECT";
2736 QUOTE_IF_REQUIRED(folder_, folder);
2737 imap_gen_send(session, "%s %s", select_cmd, folder_);
2739 if ((ok = imap_cmd_ok(session, argbuf)) != IMAP_SUCCESS) THROW;
2741 resp_str = search_array_contain_str(argbuf, "EXISTS");
2743 if (sscanf(resp_str,"%d EXISTS", exists) != 1) {
2744 g_warning("imap_cmd_select(): invalid EXISTS line.\n");
2749 resp_str = search_array_contain_str(argbuf, "RECENT");
2751 if (sscanf(resp_str, "%d RECENT", recent) != 1) {
2752 g_warning("imap_cmd_select(): invalid RECENT line.\n");
2757 resp_str = search_array_contain_str(argbuf, "UIDVALIDITY");
2759 if (sscanf(resp_str, "OK [UIDVALIDITY %u] ", &uid_validity_)
2761 g_warning("imap_cmd_select(): invalid UIDVALIDITY line.\n");
2764 *uid_validity = uid_validity_;
2767 resp_str = search_array_contain_str(argbuf, "UNSEEN");
2769 if (sscanf(resp_str, "OK [UNSEEN %d] ", unseen) != 1) {
2770 g_warning("imap_cmd_select(): invalid UNSEEN line.\n");
2776 ptr_array_free_strings(argbuf);
2777 g_ptr_array_free(argbuf, TRUE);
2782 static gint imap_cmd_select(IMAPSession *session, const gchar *folder,
2783 gint *exists, gint *recent, gint *unseen,
2784 guint32 *uid_validity)
2786 return imap_cmd_do_select(session, folder, FALSE,
2787 exists, recent, unseen, uid_validity);
2790 static gint imap_cmd_examine(IMAPSession *session, const gchar *folder,
2791 gint *exists, gint *recent, gint *unseen,
2792 guint32 *uid_validity)
2794 return imap_cmd_do_select(session, folder, TRUE,
2795 exists, recent, unseen, uid_validity);
2800 static gint imap_cmd_create(IMAPSession *session, const gchar *folder)
2804 QUOTE_IF_REQUIRED(folder_, folder);
2805 imap_gen_send(session, "CREATE %s", folder_);
2807 return imap_cmd_ok(session, NULL);
2810 static gint imap_cmd_rename(IMAPSession *session, const gchar *old_folder,
2811 const gchar *new_folder)
2813 gchar *old_folder_, *new_folder_;
2815 QUOTE_IF_REQUIRED(old_folder_, old_folder);
2816 QUOTE_IF_REQUIRED(new_folder_, new_folder);
2817 imap_gen_send(session, "RENAME %s %s", old_folder_, new_folder_);
2819 return imap_cmd_ok(session, NULL);
2822 static gint imap_cmd_delete(IMAPSession *session, const gchar *folder)
2826 QUOTE_IF_REQUIRED(folder_, folder);
2827 imap_gen_send(session, "DELETE %s", folder_);
2829 return imap_cmd_ok(session, NULL);
2832 static gint imap_cmd_search(IMAPSession *session, const gchar *criteria,
2839 g_return_val_if_fail(criteria != NULL, IMAP_ERROR);
2840 g_return_val_if_fail(list != NULL, IMAP_ERROR);
2844 argbuf = g_ptr_array_new();
2845 imap_gen_send(session, "UID SEARCH %s", criteria);
2847 ok = imap_cmd_ok(session, argbuf);
2848 if (ok != IMAP_SUCCESS) {
2849 ptr_array_free_strings(argbuf);
2850 g_ptr_array_free(argbuf, TRUE);
2854 if ((uidlist = search_array_str(argbuf, "SEARCH ")) != NULL) {
2855 gchar **strlist, **p;
2857 strlist = g_strsplit(uidlist + 7, " ", 0);
2858 for (p = strlist; *p != NULL; ++p) {
2861 if (sscanf(*p, "%u", &msgnum) == 1)
2862 *list = g_slist_append(*list, GINT_TO_POINTER(msgnum));
2864 g_strfreev(strlist);
2866 ptr_array_free_strings(argbuf);
2867 g_ptr_array_free(argbuf, TRUE);
2869 return IMAP_SUCCESS;
2872 static gint imap_cmd_fetch(IMAPSession *session, guint32 uid,
2873 const gchar *filename)
2881 g_return_val_if_fail(filename != NULL, IMAP_ERROR);
2883 imap_gen_send(session, "UID FETCH %d BODY.PEEK[]", uid);
2885 while ((ok = imap_gen_recv(session, &buf)) == IMAP_SUCCESS) {
2886 if (buf[0] != '*' || buf[1] != ' ') {
2890 if (strstr(buf, "FETCH") != NULL) break;
2893 if (ok != IMAP_SUCCESS) {
2898 #define RETURN_ERROR_IF_FAIL(cond) \
2901 return IMAP_ERROR; \
2904 cur_pos = strchr(buf, '{');
2905 RETURN_ERROR_IF_FAIL(cur_pos != NULL);
2906 cur_pos = strchr_cpy(cur_pos + 1, '}', size_str, sizeof(size_str));
2907 RETURN_ERROR_IF_FAIL(cur_pos != NULL);
2908 size_num = atol(size_str);
2909 RETURN_ERROR_IF_FAIL(size_num >= 0);
2911 RETURN_ERROR_IF_FAIL(*cur_pos == '\0');
2913 #undef RETURN_ERROR_IF_FAIL
2917 if (recv_bytes_write_to_file(SESSION(session)->sock,
2918 size_num, filename) != 0)
2921 if (imap_gen_recv(session, &buf) != IMAP_SUCCESS) {
2926 if (buf[0] == '\0' || buf[strlen(buf) - 1] != ')') {
2932 ok = imap_cmd_ok(session, NULL);
2937 static gint imap_cmd_append(IMAPSession *session, const gchar *destfolder,
2938 const gchar *file, IMAPFlags flags,
2945 unsigned int new_uid_;
2947 gchar buf[BUFFSIZE];
2952 g_return_val_if_fail(file != NULL, IMAP_ERROR);
2954 size = get_file_size_as_crlf(file);
2955 if ((fp = fopen(file, "rb")) == NULL) {
2956 FILE_OP_ERROR(file, "fopen");
2959 QUOTE_IF_REQUIRED(destfolder_, destfolder);
2960 flag_str = imap_get_flag_str(flags);
2961 imap_gen_send(session, "APPEND %s (%s) {%d}",
2962 destfolder_, flag_str, size);
2965 ok = imap_gen_recv(session, &ret);
2966 if (ok != IMAP_SUCCESS || ret[0] != '+' || ret[1] != ' ') {
2967 log_warning(_("can't append %s to %s\n"), file, destfolder_);
2974 log_print("IMAP4> %s\n", "(sending file...)");
2976 while (fgets(buf, sizeof(buf), fp) != NULL) {
2978 if (sock_puts(SESSION(session)->sock, buf) < 0) {
2985 FILE_OP_ERROR(file, "fgets");
2990 sock_puts(SESSION(session)->sock, "");
2994 if (new_uid != NULL)
2997 if (new_uid != NULL && session->uidplus) {
2998 argbuf = g_ptr_array_new();
3000 ok = imap_cmd_ok(session, argbuf);
3001 if ((ok == IMAP_SUCCESS) && (argbuf->len > 0)) {
3002 resp_str = g_ptr_array_index(argbuf, argbuf->len - 1);
3004 sscanf(resp_str, "%*u OK [APPENDUID %*u %u]",
3006 *new_uid = new_uid_;
3010 ptr_array_free_strings(argbuf);
3011 g_ptr_array_free(argbuf, TRUE);
3013 ok = imap_cmd_ok(session, NULL);
3015 if (ok != IMAP_SUCCESS)
3016 log_warning(_("can't append message to %s\n"),
3022 static MsgNumberList *imapset_to_numlist(IMAPSet imapset)
3024 gchar **ranges, **range;
3025 unsigned int low, high;
3026 MsgNumberList *uids = NULL;
3028 ranges = g_strsplit(imapset, ",", 0);
3029 for (range = ranges; *range != NULL; range++) {
3030 printf("%s\n", *range);
3031 if (sscanf(*range, "%u:%u", &low, &high) == 1)
3032 uids = g_slist_prepend(uids, GINT_TO_POINTER(low));
3035 for (i = low; i <= high; i++)
3036 uids = g_slist_prepend(uids, GINT_TO_POINTER(i));
3039 uids = g_slist_reverse(uids);
3045 static gint imap_cmd_copy(IMAPSession *session, const gchar *seq_set,
3046 const gchar *destfolder, GRelation *uid_mapping)
3051 g_return_val_if_fail(session != NULL, IMAP_ERROR);
3052 g_return_val_if_fail(seq_set != NULL, IMAP_ERROR);
3053 g_return_val_if_fail(destfolder != NULL, IMAP_ERROR);
3055 QUOTE_IF_REQUIRED(destfolder_, destfolder);
3056 imap_gen_send(session, "UID COPY %s %s", seq_set, destfolder_);
3058 if (uid_mapping != NULL && session->uidplus) {
3060 gchar *resp_str = NULL, *olduids_str, *newuids_str;
3061 MsgNumberList *olduids, *old_cur, *newuids, *new_cur;
3063 reply = g_ptr_array_new();
3064 ok = imap_cmd_ok(session, reply);
3065 if ((ok == IMAP_SUCCESS) && (reply->len > 0)) {
3066 resp_str = g_ptr_array_index(reply, reply->len - 1);
3068 olduids_str = g_new0(gchar, strlen(resp_str));
3069 newuids_str = g_new0(gchar, strlen(resp_str));
3070 if (sscanf(resp_str, "%*s OK [COPYUID %*u %[0-9,:] %[0-9,:]]",
3071 olduids_str, newuids_str) == 2) {
3072 olduids = imapset_to_numlist(olduids_str);
3073 newuids = imapset_to_numlist(newuids_str);
3077 while(old_cur != NULL && new_cur != NULL) {
3078 g_relation_insert(uid_mapping,
3079 GPOINTER_TO_INT(old_cur->data),
3080 GPOINTER_TO_INT(new_cur->data));
3081 old_cur = g_slist_next(old_cur);
3082 new_cur = g_slist_next(new_cur);
3085 g_slist_free(olduids);
3086 g_slist_free(newuids);
3088 g_free(olduids_str);
3089 g_free(newuids_str);
3092 ptr_array_free_strings(reply);
3093 g_ptr_array_free(reply, TRUE);
3095 ok = imap_cmd_ok(session, NULL);
3097 if (ok != IMAP_SUCCESS)
3098 log_warning(_("can't copy %s to %s\n"), seq_set, destfolder_);
3103 gint imap_cmd_envelope(IMAPSession *session, IMAPSet set)
3105 static GString *header_fields = NULL;
3107 if (header_fields == NULL) {
3108 const HeaderEntry *headers, *elem;
3110 headers = procheader_get_headernames(FALSE);
3111 header_fields = g_string_new("");
3113 for (elem = headers; elem->name != NULL; ++elem) {
3114 gint namelen = strlen(elem->name);
3116 /* Header fields ending with space are not rfc822 headers */
3117 if (elem->name[namelen - 1] == ' ')
3120 /* strip : at the of header field */
3121 if(elem->name[namelen - 1] == ':')
3127 g_string_sprintfa(header_fields, "%s%.*s",
3128 header_fields->str[0] != '\0' ? " " : "",
3129 namelen, elem->name);
3134 (session, "UID FETCH %s (UID FLAGS RFC822.SIZE BODY.PEEK[HEADER.FIELDS (%s)])",
3135 set, header_fields->str);
3137 return IMAP_SUCCESS;
3140 static gint imap_cmd_store(IMAPSession *session, IMAPSet seq_set,
3145 imap_gen_send(session, "UID STORE %s %s", seq_set, sub_cmd);
3147 if ((ok = imap_cmd_ok(session, NULL)) != IMAP_SUCCESS) {
3148 log_warning(_("error while imap command: STORE %s %s\n"),
3153 return IMAP_SUCCESS;
3156 static gint imap_cmd_expunge(IMAPSession *session, IMAPSet seq_set)
3160 if (seq_set && session->uidplus)
3161 imap_gen_send(session, "UID EXPUNGE %s", seq_set);
3163 imap_gen_send(session, "EXPUNGE");
3164 if ((ok = imap_cmd_ok(session, NULL)) != IMAP_SUCCESS) {
3165 log_warning(_("error while imap command: EXPUNGE\n"));
3169 return IMAP_SUCCESS;
3172 static gint imap_cmd_close(IMAPSession *session)
3176 imap_gen_send(session, "CLOSE");
3177 if ((ok = imap_cmd_ok(session, NULL)) != IMAP_SUCCESS)
3178 log_warning(_("error while imap command: CLOSE\n"));
3183 static gint imap_cmd_ok(IMAPSession *session, GPtrArray *argbuf)
3185 gint ok = IMAP_SUCCESS;
3190 while ((ok = imap_gen_recv(session, &buf))
3192 // make sure data is long enough for any substring of buf
3193 data = alloca(strlen(buf) + 1);
3195 // untagged line read
3196 if (buf[0] == '*' && buf[1] == ' ') {
3199 g_ptr_array_add(argbuf, g_strdup(buf + 2));
3201 if (sscanf(buf + 2, "%d %s", &num, data) >= 2) {
3202 if (!strcmp(data, "EXISTS")) {
3203 session->exists = num;
3204 session->folder_content_changed = TRUE;
3207 if(!strcmp(data, "EXPUNGE")) {
3209 session->folder_content_changed = TRUE;
3212 // tagged line with correct tag and OK response found
3213 } else if ((sscanf(buf, "%d %s", &cmd_num, data) >= 2) &&
3214 (cmd_num == session->cmd_count) &&
3215 !strcmp(data, "OK")) {
3217 g_ptr_array_add(argbuf, g_strdup(buf));
3231 static void imap_gen_send(IMAPSession *session, const gchar *format, ...)
3238 va_start(args, format);
3239 tmp = g_strdup_vprintf(format, args);
3242 session->cmd_count++;
3244 buf = g_strdup_printf("%d %s\r\n", session->cmd_count, tmp);
3245 if (!strncasecmp(tmp, "LOGIN ", 6) && (p = strchr(tmp + 6, ' '))) {
3247 log_print("IMAP4> %d %s ********\n", session->cmd_count, tmp);
3249 log_print("IMAP4> %d %s\n", session->cmd_count, tmp);
3251 sock_write_all(SESSION(session)->sock, buf, strlen(buf));
3256 static gint imap_gen_recv(IMAPSession *session, gchar **ret)
3258 if ((*ret = sock_getline(SESSION(session)->sock)) == NULL)
3263 log_print("IMAP4< %s\n", *ret);
3265 session_set_access_time(SESSION(session));
3267 return IMAP_SUCCESS;
3271 /* misc utility functions */
3273 static gchar *strchr_cpy(const gchar *src, gchar ch, gchar *dest, gint len)
3278 tmp = strchr(src, ch);
3282 memcpy(dest, src, MIN(tmp - src, len - 1));
3283 dest[MIN(tmp - src, len - 1)] = '\0';
3288 static gchar *get_quoted(const gchar *src, gchar ch, gchar *dest, gint len)
3290 const gchar *p = src;
3293 g_return_val_if_fail(*p == ch, NULL);
3298 while (*p != '\0' && *p != ch) {
3300 if (*p == '\\' && *(p + 1) != '\0')
3309 return (gchar *)(*p == ch ? p + 1 : p);
3312 static gchar *search_array_contain_str(GPtrArray *array, const gchar *str)
3316 for (i = 0; i < array->len; i++) {
3319 tmp = g_ptr_array_index(array, i);
3320 if (strstr(tmp, str) != NULL)
3327 static gchar *search_array_str(GPtrArray *array, const gchar *str)
3334 for (i = 0; i < array->len; i++) {
3337 tmp = g_ptr_array_index(array, i);
3338 if (!strncmp(tmp, str, len))
3345 static void imap_path_separator_subst(gchar *str, gchar separator)
3348 gboolean in_escape = FALSE;
3350 if (!separator || separator == '/') return;
3352 for (p = str; *p != '\0'; p++) {
3353 if (*p == '/' && !in_escape)
3355 else if (*p == '&' && *(p + 1) != '-' && !in_escape)
3357 else if (*p == '-' && in_escape)
3362 static gchar *imap_modified_utf7_to_locale(const gchar *mutf7_str)
3365 const gchar *from_p;
3368 to = g_malloc(strlen(mutf7_str) + 1);
3371 for (from_p = mutf7_str; *from_p != '\0'; from_p++) {
3372 if (*from_p == '&' && *(from_p + 1) == '-') {
3382 static iconv_t cd = (iconv_t)-1;
3383 static gboolean iconv_ok = TRUE;
3386 size_t norm_utf7_len;
3388 gchar *to_str, *to_p;
3390 gboolean in_escape = FALSE;
3392 if (!iconv_ok) return g_strdup(mutf7_str);
3394 if (cd == (iconv_t)-1) {
3395 cd = iconv_open(conv_get_current_charset_str(), "UTF-7");
3396 if (cd == (iconv_t)-1) {
3397 g_warning("iconv cannot convert UTF-7 to %s\n",
3398 conv_get_current_charset_str());
3400 return g_strdup(mutf7_str);
3404 norm_utf7 = g_string_new(NULL);
3406 for (p = mutf7_str; *p != '\0'; p++) {
3407 /* replace: '&' -> '+',
3409 escaped ',' -> '/' */
3410 if (!in_escape && *p == '&') {
3411 if (*(p + 1) != '-') {
3412 g_string_append_c(norm_utf7, '+');
3415 g_string_append_c(norm_utf7, '&');
3418 } else if (in_escape && *p == ',') {
3419 g_string_append_c(norm_utf7, '/');
3420 } else if (in_escape && *p == '-') {
3421 g_string_append_c(norm_utf7, '-');
3424 g_string_append_c(norm_utf7, *p);
3428 norm_utf7_p = norm_utf7->str;
3429 norm_utf7_len = norm_utf7->len;
3430 to_len = strlen(mutf7_str) * 5;
3431 to_p = to_str = g_malloc(to_len + 1);
3433 if (iconv(cd, (ICONV_CONST gchar **)&norm_utf7_p, &norm_utf7_len,
3434 &to_p, &to_len) == -1) {
3435 g_warning(_("iconv cannot convert UTF-7 to %s\n"),
3436 conv_get_current_charset_str());
3437 g_string_free(norm_utf7, TRUE);
3439 return g_strdup(mutf7_str);
3442 /* second iconv() call for flushing */
3443 iconv(cd, NULL, NULL, &to_p, &to_len);
3444 g_string_free(norm_utf7, TRUE);
3448 #endif /* !HAVE_ICONV */
3451 static gchar *imap_locale_to_modified_utf7(const gchar *from)
3454 const gchar *from_p;
3457 to = g_malloc(strlen(from) * 2 + 1);
3460 for (from_p = from; *from_p != '\0'; from_p++) {
3461 if (*from_p == '&') {
3471 static iconv_t cd = (iconv_t)-1;
3472 static gboolean iconv_ok = TRUE;
3473 gchar *norm_utf7, *norm_utf7_p;
3474 size_t from_len, norm_utf7_len;
3476 gchar *from_tmp, *to, *p;
3477 gboolean in_escape = FALSE;
3479 if (!iconv_ok) return g_strdup(from);
3481 if (cd == (iconv_t)-1) {
3482 cd = iconv_open("UTF-7", conv_get_current_charset_str());
3483 if (cd == (iconv_t)-1) {
3484 g_warning("iconv cannot convert %s to UTF-7\n",
3485 conv_get_current_charset_str());
3487 return g_strdup(from);
3491 Xstrdup_a(from_tmp, from, return g_strdup(from));
3492 from_len = strlen(from);
3493 norm_utf7_len = from_len * 5;
3494 Xalloca(norm_utf7, norm_utf7_len + 1, return g_strdup(from));
3495 norm_utf7_p = norm_utf7;
3497 #define IS_PRINT(ch) (isprint(ch) && isascii(ch))
3499 while (from_len > 0) {
3500 if (*from_tmp == '+') {
3501 *norm_utf7_p++ = '+';
3502 *norm_utf7_p++ = '-';
3506 } else if (IS_PRINT(*(guchar *)from_tmp)) {
3507 /* printable ascii char */
3508 *norm_utf7_p = *from_tmp;
3514 size_t mb_len = 0, conv_len = 0;
3516 /* unprintable char: convert to UTF-7 */
3518 while (!IS_PRINT(*(guchar *)p) && conv_len < from_len) {
3519 mb_len = mblen(p, MB_LEN_MAX);
3521 g_warning("wrong multibyte sequence\n");
3522 return g_strdup(from);
3528 from_len -= conv_len;
3529 if (iconv(cd, (ICONV_CONST gchar **)&from_tmp,
3531 &norm_utf7_p, &norm_utf7_len) == -1) {
3532 g_warning("iconv cannot convert %s to UTF-7\n",
3533 conv_get_current_charset_str());
3534 return g_strdup(from);
3537 /* second iconv() call for flushing */
3538 iconv(cd, NULL, NULL, &norm_utf7_p, &norm_utf7_len);
3544 *norm_utf7_p = '\0';
3545 to_str = g_string_new(NULL);
3546 for (p = norm_utf7; p < norm_utf7_p; p++) {
3547 /* replace: '&' -> "&-",
3550 BASE64 '/' -> ',' */
3551 if (!in_escape && *p == '&') {
3552 g_string_append(to_str, "&-");
3553 } else if (!in_escape && *p == '+') {
3554 if (*(p + 1) == '-') {
3555 g_string_append_c(to_str, '+');
3558 g_string_append_c(to_str, '&');
3561 } else if (in_escape && *p == '/') {
3562 g_string_append_c(to_str, ',');
3563 } else if (in_escape && *p == '-') {
3564 g_string_append_c(to_str, '-');
3567 g_string_append_c(to_str, *p);
3573 g_string_append_c(to_str, '-');
3577 g_string_free(to_str, FALSE);
3580 #endif /* !HAVE_ICONV */
3583 static GSList *imap_get_seq_set_from_numlist(MsgNumberList *numlist)
3586 GSList *sorted_list, *cur;
3587 guint first, last, next;
3589 GSList *ret_list = NULL;
3591 if (numlist == NULL)
3594 str = g_string_sized_new(256);
3596 sorted_list = g_slist_copy(numlist);
3597 sorted_list = g_slist_sort(sorted_list, g_int_compare);
3599 first = GPOINTER_TO_INT(sorted_list->data);
3601 for (cur = sorted_list; cur != NULL; cur = g_slist_next(cur)) {
3602 last = GPOINTER_TO_INT(cur->data);
3604 next = GPOINTER_TO_INT(cur->next->data);
3608 if (last + 1 != next || next == 0) {
3610 g_string_append_c(str, ',');
3612 g_string_sprintfa(str, "%u", first);
3614 g_string_sprintfa(str, "%u:%u", first, last);
3618 if (str->len > IMAP_CMD_LIMIT) {
3619 ret_str = g_strdup(str->str);
3620 ret_list = g_slist_append(ret_list, ret_str);
3621 g_string_truncate(str, 0);
3627 ret_str = g_strdup(str->str);
3628 ret_list = g_slist_append(ret_list, ret_str);
3631 g_slist_free(sorted_list);
3632 g_string_free(str, TRUE);
3637 static GSList *imap_get_seq_set_from_msglist(MsgInfoList *msglist)
3639 MsgNumberList *numlist = NULL;
3643 for (cur = msglist; cur != NULL; cur = g_slist_next(cur)) {
3644 MsgInfo *msginfo = (MsgInfo *) cur->data;
3646 numlist = g_slist_append(numlist, GINT_TO_POINTER(msginfo->msgnum));
3648 seq_list = imap_get_seq_set_from_numlist(numlist);
3649 g_slist_free(numlist);
3654 static void imap_seq_set_free(GSList *seq_list)
3656 slist_free_strings(seq_list);
3657 g_slist_free(seq_list);
3661 static gboolean imap_rename_folder_func(GNode *node, gpointer data)
3663 FolderItem *item = node->data;
3664 gchar **paths = data;
3665 const gchar *oldpath = paths[0];
3666 const gchar *newpath = paths[1];
3668 gchar *new_itempath;
3671 oldpathlen = strlen(oldpath);
3672 if (strncmp(oldpath, item->path, oldpathlen) != 0) {
3673 g_warning("path doesn't match: %s, %s\n", oldpath, item->path);
3677 base = item->path + oldpathlen;
3678 while (*base == G_DIR_SEPARATOR) base++;
3680 new_itempath = g_strdup(newpath);
3682 new_itempath = g_strconcat(newpath, G_DIR_SEPARATOR_S, base,
3685 item->path = new_itempath;
3690 static gint get_list_of_uids(Folder *folder, IMAPFolderItem *item, GSList **msgnum_list)
3692 gint ok, nummsgs = 0, lastuid_old;
3693 IMAPSession *session;
3694 GSList *uidlist, *elem;
3697 session = imap_session_get(folder);
3698 g_return_val_if_fail(session != NULL, -1);
3700 ok = imap_select(session, IMAP_FOLDER(folder), item->item.path,
3701 NULL, NULL, NULL, NULL);
3702 if (ok != IMAP_SUCCESS)
3705 cmd_buf = g_strdup_printf("UID %d:*", item->lastuid + 1);
3706 ok = imap_cmd_search(session, cmd_buf, &uidlist);
3709 if (ok == IMAP_SOCKET) {
3710 session_destroy((Session *)session);
3711 ((RemoteFolder *)folder)->session = NULL;
3715 if (ok != IMAP_SUCCESS) {
3719 argbuf = g_ptr_array_new();
3721 cmd_buf = g_strdup_printf("UID FETCH %d:* (UID)", item->lastuid + 1);
3722 imap_gen_send(session, cmd_buf);
3724 ok = imap_cmd_ok(session, argbuf);
3725 if (ok != IMAP_SUCCESS) {
3726 ptr_array_free_strings(argbuf);
3727 g_ptr_array_free(argbuf, TRUE);
3731 for(i = 0; i < argbuf->len; i++) {
3734 if((ret = sscanf(g_ptr_array_index(argbuf, i),
3735 "%*d FETCH (UID %d)", &msgnum)) == 1)
3736 uidlist = g_slist_prepend(uidlist, GINT_TO_POINTER(msgnum));
3738 ptr_array_free_strings(argbuf);
3739 g_ptr_array_free(argbuf, TRUE);
3742 lastuid_old = item->lastuid;
3743 *msgnum_list = g_slist_copy(item->uid_list);
3744 nummsgs = g_slist_length(*msgnum_list);
3745 debug_print("Got %d uids from cache\n", g_slist_length(item->uid_list));
3747 for (elem = uidlist; elem != NULL; elem = g_slist_next(elem)) {
3750 msgnum = GPOINTER_TO_INT(elem->data);
3751 if (msgnum > lastuid_old) {
3752 *msgnum_list = g_slist_prepend(*msgnum_list, GINT_TO_POINTER(msgnum));
3753 item->uid_list = g_slist_prepend(item->uid_list, GINT_TO_POINTER(msgnum));
3756 if(msgnum > item->lastuid)
3757 item->lastuid = msgnum;
3760 g_slist_free(uidlist);
3765 gint imap_get_num_list(Folder *folder, FolderItem *_item, GSList **msgnum_list, gboolean *old_uids_valid)
3767 IMAPFolderItem *item = (IMAPFolderItem *)_item;
3768 IMAPSession *session;
3769 gint ok, nummsgs = 0, exists, recent, uid_val, uid_next, unseen;
3770 GSList *uidlist = NULL;
3772 gboolean selected_folder;
3774 g_return_val_if_fail(folder != NULL, -1);
3775 g_return_val_if_fail(item != NULL, -1);
3776 g_return_val_if_fail(item->item.path != NULL, -1);
3777 g_return_val_if_fail(FOLDER_CLASS(folder) == &imap_class, -1);
3778 g_return_val_if_fail(folder->account != NULL, -1);
3780 session = imap_session_get(folder);
3781 g_return_val_if_fail(session != NULL, -1);
3783 selected_folder = (session->mbox != NULL) &&
3784 (!strcmp(session->mbox, item->item.path));
3785 if (selected_folder) {
3786 ok = imap_cmd_noop(session);
3787 if (ok != IMAP_SUCCESS)
3789 exists = session->exists;
3791 *old_uids_valid = TRUE;
3793 ok = imap_status(session, IMAP_FOLDER(folder), item->item.path,
3794 &exists, &recent, &uid_next, &uid_val, &unseen);
3795 if (ok != IMAP_SUCCESS)
3798 if(item->item.mtime == uid_val)
3799 *old_uids_valid = TRUE;
3801 *old_uids_valid = FALSE;
3803 debug_print("Freeing imap uid cache\n");
3805 g_slist_free(item->uid_list);
3806 item->uid_list = NULL;
3808 item->item.mtime = uid_val;
3810 imap_delete_all_cached_messages((FolderItem *)item);
3814 if (!selected_folder)
3815 item->uid_next = uid_next;
3817 /* If old uid_next matches new uid_next we can be sure no message
3818 was added to the folder */
3819 if (( selected_folder && !session->folder_content_changed) ||
3820 (!selected_folder && uid_next == item->uid_next)) {
3821 nummsgs = g_slist_length(item->uid_list);
3823 /* If number of messages is still the same we
3824 know our caches message numbers are still valid,
3825 otherwise if the number of messages has decrease
3826 we discard our cache to start a new scan to find
3827 out which numbers have been removed */
3828 if (exists == nummsgs) {
3829 *msgnum_list = g_slist_copy(item->uid_list);
3831 } else if (exists < nummsgs) {
3832 debug_print("Freeing imap uid cache");
3834 g_slist_free(item->uid_list);
3835 item->uid_list = NULL;
3840 *msgnum_list = NULL;
3844 nummsgs = get_list_of_uids(folder, item, &uidlist);
3846 if (nummsgs != exists) {
3847 /* Cache contains more messages then folder, we have cached
3848 an old UID of a message that was removed and new messages
3849 have been added too, otherwise the uid_next check would
3851 debug_print("Freeing imap uid cache");
3853 g_slist_free(item->uid_list);
3854 item->uid_list = NULL;
3856 g_slist_free(*msgnum_list);
3858 nummsgs = get_list_of_uids(folder, item, &uidlist);
3861 *msgnum_list = uidlist;
3863 dir = folder_item_get_path((FolderItem *)item);
3864 debug_print("removing old messages from %s\n", dir);
3865 remove_numbered_files_not_in_list(dir, *msgnum_list);
3871 static MsgInfo *imap_parse_msg(const gchar *file, FolderItem *item)
3876 flags.perm_flags = MSG_NEW|MSG_UNREAD;
3877 flags.tmp_flags = 0;
3879 g_return_val_if_fail(item != NULL, NULL);
3880 g_return_val_if_fail(file != NULL, NULL);
3882 if (item->stype == F_QUEUE) {
3883 MSG_SET_TMP_FLAGS(flags, MSG_QUEUED);
3884 } else if (item->stype == F_DRAFT) {
3885 MSG_SET_TMP_FLAGS(flags, MSG_DRAFT);
3888 msginfo = procheader_parse_file(file, flags, FALSE, FALSE);
3889 if (!msginfo) return NULL;
3891 msginfo->folder = item;
3896 GSList *imap_get_msginfos(Folder *folder, FolderItem *item, GSList *msgnum_list)
3898 IMAPSession *session;
3899 MsgInfoList *ret = NULL;
3902 g_return_val_if_fail(folder != NULL, NULL);
3903 g_return_val_if_fail(item != NULL, NULL);
3904 g_return_val_if_fail(msgnum_list != NULL, NULL);
3906 session = imap_session_get(folder);
3907 g_return_val_if_fail(session != NULL, NULL);
3909 ok = imap_select(session, IMAP_FOLDER(folder), item->path,
3910 NULL, NULL, NULL, NULL);
3911 if (ok != IMAP_SUCCESS)
3914 if (!(item->stype == F_QUEUE || item->stype == F_DRAFT)) {
3915 ret = g_slist_concat(ret,
3916 imap_get_uncached_messages(
3917 session, item, msgnum_list));
3919 MsgNumberList *sorted_list, *elem;
3920 gint startnum, lastnum;
3922 sorted_list = g_slist_sort(g_slist_copy(msgnum_list), g_int_compare);
3924 startnum = lastnum = GPOINTER_TO_INT(sorted_list->data);
3926 for (elem = sorted_list;; elem = g_slist_next(elem)) {
3930 num = GPOINTER_TO_INT(elem->data);
3932 if (num > lastnum + 1 || elem == NULL) {
3934 for (i = startnum; i <= lastnum; ++i) {
3937 file = imap_fetch_msg(folder, item, i);
3939 MsgInfo *msginfo = imap_parse_msg(file, item);
3940 if (msginfo != NULL) {
3941 msginfo->msgnum = i;
3942 ret = g_slist_append(ret, msginfo);
3956 g_slist_free(sorted_list);
3962 MsgInfo *imap_get_msginfo(Folder *folder, FolderItem *item, gint uid)
3964 MsgInfo *msginfo = NULL;
3965 MsgInfoList *msginfolist;
3966 MsgNumberList numlist;
3968 numlist.next = NULL;
3969 numlist.data = GINT_TO_POINTER(uid);
3971 msginfolist = imap_get_msginfos(folder, item, &numlist);
3972 if (msginfolist != NULL) {
3973 msginfo = msginfolist->data;
3974 g_slist_free(msginfolist);
3980 gboolean imap_scan_required(Folder *folder, FolderItem *_item)
3982 IMAPSession *session;
3983 IMAPFolderItem *item = (IMAPFolderItem *)_item;
3984 gint ok, exists = 0, recent = 0, unseen = 0;
3985 guint32 uid_next, uid_val = 0;
3986 gboolean selected_folder;
3988 g_return_val_if_fail(folder != NULL, FALSE);
3989 g_return_val_if_fail(item != NULL, FALSE);
3990 g_return_val_if_fail(item->item.folder != NULL, FALSE);
3991 g_return_val_if_fail(FOLDER_CLASS(item->item.folder) == &imap_class, FALSE);
3993 if (item->item.path == NULL)
3996 session = imap_session_get(folder);
3997 g_return_val_if_fail(session != NULL, FALSE);
3999 selected_folder = (session->mbox != NULL) &&
4000 (!strcmp(session->mbox, item->item.path));
4001 if (selected_folder) {
4002 ok = imap_cmd_noop(session);
4003 if (ok != IMAP_SUCCESS)
4006 if (session->folder_content_changed)
4009 ok = imap_status(session, IMAP_FOLDER(folder), item->item.path,
4010 &exists, &recent, &uid_next, &uid_val, &unseen);
4011 if (ok != IMAP_SUCCESS)
4014 if ((uid_next != item->uid_next) || (exists < item->item.total_msgs))
4021 void imap_change_flags(Folder *folder, FolderItem *item, MsgInfo *msginfo, MsgPermFlags newflags)
4023 IMAPSession *session;
4024 IMAPFlags flags_set = 0, flags_unset = 0;
4025 gint ok = IMAP_SUCCESS;
4026 MsgNumberList numlist;
4028 g_return_if_fail(folder != NULL);
4029 g_return_if_fail(folder->klass == &imap_class);
4030 g_return_if_fail(item != NULL);
4031 g_return_if_fail(item->folder == folder);
4032 g_return_if_fail(msginfo != NULL);
4033 g_return_if_fail(msginfo->folder == item);
4035 session = imap_session_get(folder);
4036 if (!session) return;
4038 if ((ok = imap_select(session, IMAP_FOLDER(folder), msginfo->folder->path,
4039 NULL, NULL, NULL, NULL)) != IMAP_SUCCESS)
4042 if (!MSG_IS_MARKED(msginfo->flags) && (newflags & MSG_MARKED))
4043 flags_set |= IMAP_FLAG_FLAGGED;
4044 if ( MSG_IS_MARKED(msginfo->flags) && !(newflags & MSG_MARKED))
4045 flags_unset |= IMAP_FLAG_FLAGGED;
4047 if (!MSG_IS_UNREAD(msginfo->flags) && (newflags & MSG_UNREAD))
4048 flags_unset |= IMAP_FLAG_SEEN;
4049 if ( MSG_IS_UNREAD(msginfo->flags) && !(newflags & MSG_UNREAD))
4050 flags_set |= IMAP_FLAG_SEEN;
4052 if (!MSG_IS_REPLIED(msginfo->flags) && (newflags & MSG_REPLIED))
4053 flags_set |= IMAP_FLAG_ANSWERED;
4054 if ( MSG_IS_REPLIED(msginfo->flags) && !(newflags & MSG_REPLIED))
4055 flags_set |= IMAP_FLAG_ANSWERED;
4057 numlist.next = NULL;
4058 numlist.data = GINT_TO_POINTER(msginfo->msgnum);
4061 ok = imap_set_message_flags(session, &numlist, flags_set, TRUE);
4062 if (ok != IMAP_SUCCESS) return;
4066 ok = imap_set_message_flags(session, &numlist, flags_unset, FALSE);
4067 if (ok != IMAP_SUCCESS) return;
4070 msginfo->flags.perm_flags = newflags;
4075 static gint compare_msginfo(gconstpointer a, gconstpointer b)
4077 return ((MsgInfo *)a)->msgnum - ((MsgInfo *)b)->msgnum;
4080 static guint gslist_find_next_num(MsgNumberList **list, guint num)
4084 g_return_val_if_fail(list != NULL, -1);
4086 for (elem = *list; elem != NULL; elem = g_slist_next(elem))
4087 if (GPOINTER_TO_INT(elem->data) >= num)
4090 return elem != NULL ? GPOINTER_TO_INT(elem->data) : (gint)-1;
4094 * NEW and DELETED flags are not syncronized
4095 * - The NEW/RECENT flags in IMAP folders can not really be directly
4096 * modified by Sylpheed
4097 * - The DELETE/DELETED flag in IMAP and Sylpheed don't have the same
4098 * meaning, in IMAP it always removes the messages from the FolderItem
4099 * in Sylpheed it can mean to move the message to trash
4101 static gint imap_get_flags(Folder *folder, FolderItem *item,
4102 MsgInfoList *msginfo_list, GRelation *msgflags)
4104 IMAPSession *session;
4105 GSList *sorted_list;
4107 GSList *new = NULL, *p_new;
4108 GSList *deleted = NULL, *p_deleted;
4110 GSList *unseen = NULL, *answered = NULL, *flagged = NULL;
4111 GSList *p_unseen, *p_answered, *p_flagged;
4113 GSList *seq_list, *cur;
4114 gboolean reverse_seen = FALSE;
4117 gint exists_cnt, recent_cnt, unseen_cnt, uid_next;
4118 guint32 uidvalidity;
4120 g_return_val_if_fail(folder != NULL, -1);
4121 g_return_val_if_fail(item != NULL, -1);
4122 if (msginfo_list == NULL)
4125 session = imap_session_get(folder);
4126 g_return_val_if_fail(session != NULL, -1);
4128 ok = imap_select(session, IMAP_FOLDER(folder), item->path,
4129 NULL, NULL, NULL, NULL);
4130 if (ok != IMAP_SUCCESS)
4133 ok = imap_status(session, IMAP_FOLDER(folder), item->path,
4134 &exists_cnt, &recent_cnt, &uid_next, &uidvalidity, &unseen_cnt);
4136 if (unseen_cnt > exists_cnt / 2)
4137 reverse_seen = TRUE;
4139 cmd_buf = g_string_new(NULL);
4141 sorted_list = g_slist_sort(g_slist_copy(msginfo_list), compare_msginfo);
4143 seq_list = imap_get_seq_set_from_msglist(msginfo_list);
4145 for (cur = seq_list; cur != NULL; cur = g_slist_next(cur)) {
4146 IMAPSet imapset = cur->data;
4148 g_string_sprintf(cmd_buf, "RECENT UID %s", imapset);
4149 imap_cmd_search(session, cmd_buf->str, &p_new);
4150 new = g_slist_concat(new, p_new);
4152 g_string_sprintf(cmd_buf, "%sSEEN UID %s", reverse_seen ? "" : "UN", imapset);
4153 imap_cmd_search(session, cmd_buf->str, &p_unseen);
4154 unseen = g_slist_concat(unseen, p_unseen);
4156 g_string_sprintf(cmd_buf, "ANSWERED UID %s", imapset);
4157 imap_cmd_search(session, cmd_buf->str, &p_answered);
4158 answered = g_slist_concat(answered, p_answered);
4160 g_string_sprintf(cmd_buf, "FLAGGED UID %s", imapset);
4161 imap_cmd_search(session, cmd_buf->str, &p_flagged);
4162 flagged = g_slist_concat(flagged, p_flagged);
4164 g_string_sprintf(cmd_buf, "DELETED UID %s", imapset);
4165 imap_cmd_search(session, cmd_buf->str, &p_deleted);
4166 deleted = g_slist_concat(deleted, p_deleted);
4174 p_answered = answered;
4175 p_flagged = flagged;
4177 p_deleted = deleted;
4179 for (elem = sorted_list; elem != NULL; elem = g_slist_next(elem)) {
4184 msginfo = (MsgInfo *) elem->data;
4185 flags = msginfo->flags.perm_flags;
4186 wasnew = (flags & MSG_NEW);
4187 flags &= ~((reverse_seen ? 0 : MSG_UNREAD | MSG_NEW) | MSG_REPLIED | MSG_MARKED);
4189 flags |= MSG_UNREAD | (wasnew ? MSG_NEW : 0);
4191 if (gslist_find_next_num(&p_new, msginfo->msgnum) == msginfo->msgnum)
4194 if (gslist_find_next_num(&p_unseen, msginfo->msgnum) == msginfo->msgnum) {
4195 if (!reverse_seen) {
4196 flags |= MSG_UNREAD | (wasnew ? MSG_NEW : 0);
4198 flags &= ~(MSG_UNREAD | MSG_NEW);
4201 if (gslist_find_next_num(&p_answered, msginfo->msgnum) == msginfo->msgnum)
4202 flags |= MSG_REPLIED;
4203 if (gslist_find_next_num(&p_flagged, msginfo->msgnum) == msginfo->msgnum)
4204 flags |= MSG_MARKED;
4206 if (gslist_find_next_num(&p_deleted, msginfo->msgnum) == msginfo->msgnum)
4207 MSG_SET_PERM_FLAGS(msginfo->flags, MSG_DELETED);
4209 g_relation_insert(msgflags, msginfo, GINT_TO_POINTER(flags));
4212 imap_seq_set_free(seq_list);
4213 /* g_slist_free(deleted); */
4214 g_slist_free(flagged);
4215 g_slist_free(answered);
4216 g_slist_free(unseen);
4217 /* new not freed in original patch ??? */
4218 g_slist_free(sorted_list);
4219 g_string_free(cmd_buf, TRUE);