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 gboolean folder_content_changed;
105 #define IMAP_SUCCESS 0
106 #define IMAP_SOCKET 2
107 #define IMAP_AUTHFAIL 3
108 #define IMAP_PROTOCOL 4
109 #define IMAP_SYNTAX 5
113 #define IMAPBUFSIZE 8192
117 IMAP_FLAG_SEEN = 1 << 0,
118 IMAP_FLAG_ANSWERED = 1 << 1,
119 IMAP_FLAG_FLAGGED = 1 << 2,
120 IMAP_FLAG_DELETED = 1 << 3,
121 IMAP_FLAG_DRAFT = 1 << 4
124 #define IMAP_IS_SEEN(flags) ((flags & IMAP_FLAG_SEEN) != 0)
125 #define IMAP_IS_ANSWERED(flags) ((flags & IMAP_FLAG_ANSWERED) != 0)
126 #define IMAP_IS_FLAGGED(flags) ((flags & IMAP_FLAG_FLAGGED) != 0)
127 #define IMAP_IS_DELETED(flags) ((flags & IMAP_FLAG_DELETED) != 0)
128 #define IMAP_IS_DRAFT(flags) ((flags & IMAP_FLAG_DRAFT) != 0)
131 #define IMAP4_PORT 143
133 #define IMAPS_PORT 993
136 #define IMAP_CMD_LIMIT 1000
138 #define QUOTE_IF_REQUIRED(out, str) \
140 if (*str != '"' && strpbrk(str, " \t(){}%*") != NULL) { \
144 len = strlen(str) + 3; \
145 Xalloca(__tmp, len, return IMAP_ERROR); \
146 g_snprintf(__tmp, len, "\"%s\"", str); \
149 Xstrdup_a(out, str, return IMAP_ERROR); \
153 typedef gchar * IMAPSet;
155 struct _IMAPFolderItem
164 static void imap_folder_init (Folder *folder,
168 static Folder *imap_folder_new (const gchar *name,
170 static void imap_folder_destroy (Folder *folder);
172 static IMAPSession *imap_session_new (const PrefsAccount *account);
173 static void imap_session_authenticate(IMAPSession *session,
174 const PrefsAccount *account);
175 static void imap_session_destroy (Session *session);
177 static gchar *imap_fetch_msg (Folder *folder,
180 static gint imap_add_msg (Folder *folder,
184 static gint imap_add_msgs (Folder *folder,
187 GRelation *relation);
189 static gint imap_copy_msg (Folder *folder,
192 static gint imap_copy_msgs (Folder *folder,
194 MsgInfoList *msglist,
195 GRelation *relation);
197 static gint imap_remove_msg (Folder *folder,
200 static gint imap_remove_all_msg (Folder *folder,
203 static gboolean imap_is_msg_changed (Folder *folder,
207 static gint imap_close (Folder *folder,
210 static gint imap_scan_tree (Folder *folder);
212 static gint imap_create_tree (Folder *folder);
214 static FolderItem *imap_create_folder (Folder *folder,
217 static gint imap_rename_folder (Folder *folder,
220 static gint imap_remove_folder (Folder *folder,
223 static FolderItem *imap_folder_item_new (Folder *folder);
224 static void imap_folder_item_destroy (Folder *folder,
227 static IMAPSession *imap_session_get (Folder *folder);
229 static gint imap_greeting (IMAPSession *session);
230 static gint imap_auth (IMAPSession *session,
235 static gint imap_scan_tree_recursive (IMAPSession *session,
237 static GSList *imap_parse_list (IMAPFolder *folder,
238 IMAPSession *session,
239 const gchar *real_path,
242 static void imap_create_missing_folders (Folder *folder);
243 static FolderItem *imap_create_special_folder
245 SpecialFolderItemType stype,
248 static gint imap_do_copy_msgs (Folder *folder,
250 MsgInfoList *msglist,
251 GRelation *relation);
253 static void imap_delete_all_cached_messages (FolderItem *item);
256 static SockInfo *imap_open (const gchar *server,
260 static SockInfo *imap_open (const gchar *server,
265 static SockInfo *imap_open_tunnel(const gchar *server,
266 const gchar *tunnelcmd,
269 static SockInfo *imap_open_tunnel(const gchar *server,
270 const gchar *tunnelcmd);
274 static SockInfo *imap_init_sock(SockInfo *sock, SSLType ssl_type);
276 static SockInfo *imap_init_sock(SockInfo *sock);
279 static gchar *imap_get_flag_str (IMAPFlags flags);
280 static gint imap_set_message_flags (IMAPSession *session,
281 MsgNumberList *numlist,
284 static gint imap_select (IMAPSession *session,
290 guint32 *uid_validity);
291 static gint imap_status (IMAPSession *session,
297 guint32 *uid_validity,
300 static void imap_parse_namespace (IMAPSession *session,
302 static void imap_get_namespace_by_list (IMAPSession *session,
304 static IMAPNameSpace *imap_find_namespace (IMAPFolder *folder,
306 static gchar imap_get_path_separator (IMAPFolder *folder,
308 static gchar *imap_get_real_path (IMAPFolder *folder,
311 static gchar *imap_parse_atom (SockInfo *sock,
316 static MsgFlags imap_parse_flags (const gchar *flag_str);
317 static MsgInfo *imap_parse_envelope (SockInfo *sock,
321 static gboolean imap_has_capability (IMAPSession *session,
323 static void imap_free_capabilities (IMAPSession *session);
325 /* low-level IMAP4rev1 commands */
326 static gint imap_cmd_authenticate
327 (IMAPSession *session,
331 static gint imap_cmd_login (IMAPSession *session,
334 static gint imap_cmd_logout (IMAPSession *session);
335 static gint imap_cmd_noop (IMAPSession *session);
337 static gint imap_cmd_starttls (IMAPSession *session);
339 static gint imap_cmd_namespace (IMAPSession *session,
341 static gint imap_cmd_list (IMAPSession *session,
343 const gchar *mailbox,
345 static gint imap_cmd_do_select (IMAPSession *session,
351 guint32 *uid_validity);
352 static gint imap_cmd_select (IMAPSession *session,
357 guint32 *uid_validity);
358 static gint imap_cmd_examine (IMAPSession *session,
363 guint32 *uid_validity);
364 static gint imap_cmd_create (IMAPSession *sock,
365 const gchar *folder);
366 static gint imap_cmd_rename (IMAPSession *sock,
367 const gchar *oldfolder,
368 const gchar *newfolder);
369 static gint imap_cmd_delete (IMAPSession *session,
370 const gchar *folder);
371 static gint imap_cmd_envelope (IMAPSession *session,
373 static gint imap_cmd_fetch (IMAPSession *sock,
375 const gchar *filename);
376 static gint imap_cmd_append (IMAPSession *session,
377 const gchar *destfolder,
381 static gint imap_cmd_copy (IMAPSession *session,
382 const gchar *seq_set,
383 const gchar *destfolder,
384 GRelation *uid_mapping);
385 static gint imap_cmd_store (IMAPSession *session,
388 static gint imap_cmd_expunge (IMAPSession *session,
390 static gint imap_cmd_close (IMAPSession *session);
392 static gint imap_cmd_ok (IMAPSession *session,
394 static void imap_gen_send (IMAPSession *session,
395 const gchar *format, ...);
396 static gint imap_gen_recv (IMAPSession *session,
399 /* misc utility functions */
400 static gchar *strchr_cpy (const gchar *src,
404 static gchar *get_quoted (const gchar *src,
408 static gchar *search_array_contain_str (GPtrArray *array,
410 static gchar *search_array_str (GPtrArray *array,
412 static void imap_path_separator_subst (gchar *str,
415 static gchar *imap_utf8_to_modified_utf7 (const gchar *from);
416 static gchar *imap_modified_utf7_to_utf8 (const gchar *mutf7_str);
418 static GSList *imap_get_seq_set_from_numlist (MsgNumberList *msglist);
419 static GSList *imap_get_seq_set_from_msglist (MsgInfoList *msglist);
420 static void imap_seq_set_free (GSList *seq_list);
422 static gboolean imap_rename_folder_func (GNode *node,
424 static gint imap_get_num_list (Folder *folder,
427 gboolean *old_uids_valid);
428 static GSList *imap_get_msginfos (Folder *folder,
430 GSList *msgnum_list);
431 static MsgInfo *imap_get_msginfo (Folder *folder,
434 static gboolean imap_scan_required (Folder *folder,
436 static void imap_change_flags (Folder *folder,
439 MsgPermFlags newflags);
440 static gint imap_get_flags (Folder *folder,
442 MsgInfoList *msglist,
443 GRelation *msgflags);
444 static gchar *imap_folder_get_path (Folder *folder);
445 static gchar *imap_item_get_path (Folder *folder,
448 static FolderClass imap_class;
450 FolderClass *imap_get_class(void)
452 if (imap_class.idstr == NULL) {
453 imap_class.type = F_IMAP;
454 imap_class.idstr = "imap";
455 imap_class.uistr = "IMAP4";
457 /* Folder functions */
458 imap_class.new_folder = imap_folder_new;
459 imap_class.destroy_folder = imap_folder_destroy;
460 imap_class.scan_tree = imap_scan_tree;
461 imap_class.create_tree = imap_create_tree;
463 /* FolderItem functions */
464 imap_class.item_new = imap_folder_item_new;
465 imap_class.item_destroy = imap_folder_item_destroy;
466 imap_class.item_get_path = imap_item_get_path;
467 imap_class.create_folder = imap_create_folder;
468 imap_class.rename_folder = imap_rename_folder;
469 imap_class.remove_folder = imap_remove_folder;
470 imap_class.close = imap_close;
471 imap_class.get_num_list = imap_get_num_list;
472 imap_class.scan_required = imap_scan_required;
474 /* Message functions */
475 imap_class.get_msginfo = imap_get_msginfo;
476 imap_class.get_msginfos = imap_get_msginfos;
477 imap_class.fetch_msg = imap_fetch_msg;
478 imap_class.add_msg = imap_add_msg;
479 imap_class.add_msgs = imap_add_msgs;
480 imap_class.copy_msg = imap_copy_msg;
481 imap_class.copy_msgs = imap_copy_msgs;
482 imap_class.remove_msg = imap_remove_msg;
483 imap_class.remove_all_msg = imap_remove_all_msg;
484 imap_class.is_msg_changed = imap_is_msg_changed;
485 imap_class.change_flags = imap_change_flags;
486 imap_class.get_flags = imap_get_flags;
492 static Folder *imap_folder_new(const gchar *name, const gchar *path)
496 folder = (Folder *)g_new0(IMAPFolder, 1);
497 folder->klass = &imap_class;
498 imap_folder_init(folder, name, path);
503 static void imap_folder_destroy(Folder *folder)
507 dir = imap_folder_get_path(folder);
508 if (is_dir_exist(dir))
509 remove_dir_recursive(dir);
512 folder_remote_folder_destroy(REMOTE_FOLDER(folder));
515 static void imap_folder_init(Folder *folder, const gchar *name,
518 folder_remote_folder_init((Folder *)folder, name, path);
521 static FolderItem *imap_folder_item_new(Folder *folder)
523 IMAPFolderItem *item;
525 item = g_new0(IMAPFolderItem, 1);
528 item->uid_list = NULL;
530 return (FolderItem *)item;
533 static void imap_folder_item_destroy(Folder *folder, FolderItem *_item)
535 IMAPFolderItem *item = (IMAPFolderItem *)_item;
537 g_return_if_fail(item != NULL);
538 g_slist_free(item->uid_list);
543 static gboolean imap_reset_uid_lists_func(GNode *node, gpointer data)
545 IMAPFolderItem *item = (IMAPFolderItem *)node->data;
549 g_slist_free(item->uid_list);
550 item->uid_list = NULL;
555 static void imap_reset_uid_lists(Folder *folder)
557 if(folder->node == NULL)
560 /* Destroy all uid lists and rest last uid */
561 g_node_traverse(folder->node, G_IN_ORDER, G_TRAVERSE_ALL, -1, imap_reset_uid_lists_func, NULL);
564 /* Send CAPABILITY, and examine the server's response to see whether this
565 * connection is pre-authenticated or not and build a list of CAPABILITIES. */
566 static gint imap_greeting(IMAPSession *session)
571 imap_gen_send(session, "CAPABILITY");
573 argbuf = g_ptr_array_new();
575 if (imap_cmd_ok(session, argbuf) != IMAP_SUCCESS ||
576 ((capstr = search_array_str(argbuf, "CAPABILITY ")) == NULL)) {
577 ptr_array_free_strings(argbuf);
578 g_ptr_array_free(argbuf, TRUE);
582 session->authenticated = search_array_str(argbuf, "PREAUTH") != NULL;
584 capstr += strlen("CAPABILITY ");
586 IMAP_SESSION(session)->capability = g_strsplit(capstr, " ", 0);
588 ptr_array_free_strings(argbuf);
589 g_ptr_array_free(argbuf, TRUE);
591 if (imap_has_capability(session, "UIDPLUS"))
592 session->uidplus = TRUE;
597 static gint imap_auth(IMAPSession *session, const gchar *user, const gchar *pass,
602 if (type == 0 || type == IMAP_AUTH_LOGIN)
603 ok = imap_cmd_login(session, user, pass);
605 ok = imap_cmd_authenticate(session, user, pass, type);
607 if (ok == IMAP_SUCCESS)
608 session->authenticated = TRUE;
613 static IMAPSession *imap_session_get(Folder *folder)
615 RemoteFolder *rfolder = REMOTE_FOLDER(folder);
616 IMAPSession *session = NULL;
618 g_return_val_if_fail(folder != NULL, NULL);
619 g_return_val_if_fail(FOLDER_CLASS(folder) == &imap_class, NULL);
620 g_return_val_if_fail(folder->account != NULL, NULL);
622 if (prefs_common.work_offline)
625 /* Make sure we have a session */
626 if (rfolder->session != NULL) {
627 session = IMAP_SESSION(rfolder->session);
629 imap_reset_uid_lists(folder);
630 session = imap_session_new(folder->account);
635 if (SESSION(session)->sock->state == CONN_DISCONNECTED) {
636 debug_print("IMAP server disconnected\n");
637 session_destroy(SESSION(session));
638 imap_reset_uid_lists(folder);
639 session = imap_session_new(folder->account);
642 /* Make sure session is authenticated */
643 if (!IMAP_SESSION(session)->authenticated)
644 imap_session_authenticate(IMAP_SESSION(session), folder->account);
645 if (!IMAP_SESSION(session)->authenticated) {
646 session_destroy(SESSION(session));
647 rfolder->session = NULL;
651 /* Make sure we have parsed the IMAP namespace */
652 imap_parse_namespace(IMAP_SESSION(session),
653 IMAP_FOLDER(folder));
655 /* I think the point of this code is to avoid sending a
656 * keepalive if we've used the session recently and therefore
657 * think it's still alive. Unfortunately, most of the code
658 * does not yet check for errors on the socket, and so if the
659 * connection drops we don't notice until the timeout expires.
660 * A better solution than sending a NOOP every time would be
661 * for every command to be prepared to retry until it is
662 * successfully sent. -- mbp */
663 if (time(NULL) - SESSION(session)->last_access_time > SESSION_TIMEOUT_INTERVAL) {
664 /* verify that the session is still alive */
665 if (imap_cmd_noop(session) != IMAP_SUCCESS) {
666 /* Check if this is the first try to establish a
667 connection, if yes we don't try to reconnect */
668 if (rfolder->session == NULL) {
669 log_warning(_("Connecting to %s failed"),
670 folder->account->recv_server);
671 session_destroy(SESSION(session));
674 log_warning(_("IMAP4 connection to %s has been"
675 " disconnected. Reconnecting...\n"),
676 folder->account->recv_server);
677 session_destroy(SESSION(session));
678 /* Clear folders session to make imap_session_get create
679 a new session, because of rfolder->session == NULL
680 it will not try to reconnect again and so avoid an
682 rfolder->session = NULL;
683 session = imap_session_get(folder);
688 rfolder->session = SESSION(session);
690 return IMAP_SESSION(session);
693 static IMAPSession *imap_session_new(const PrefsAccount *account)
695 IMAPSession *session;
700 /* FIXME: IMAP over SSL only... */
703 port = account->set_imapport ? account->imapport
704 : account->ssl_imap == SSL_TUNNEL ? IMAPS_PORT : IMAP4_PORT;
705 ssl_type = account->ssl_imap;
707 port = account->set_imapport ? account->imapport
711 if (account->set_tunnelcmd) {
712 log_message(_("creating tunneled IMAP4 connection\n"));
714 if ((imap_sock = imap_open_tunnel(account->recv_server,
718 if ((imap_sock = imap_open_tunnel(account->recv_server,
719 account->tunnelcmd)) == NULL)
723 g_return_val_if_fail(account->recv_server != NULL, NULL);
725 log_message(_("creating IMAP4 connection to %s:%d ...\n"),
726 account->recv_server, port);
729 if ((imap_sock = imap_open(account->recv_server, port,
732 if ((imap_sock = imap_open(account->recv_server, port)) == NULL)
737 session = g_new0(IMAPSession, 1);
738 session_init(SESSION(session));
739 SESSION(session)->type = SESSION_IMAP;
740 SESSION(session)->server = g_strdup(account->recv_server);
741 SESSION(session)->sock = imap_sock;
743 SESSION(session)->destroy = imap_session_destroy;
745 session->capability = NULL;
747 session->authenticated = FALSE;
748 session->mbox = NULL;
749 session->cmd_count = 0;
751 /* Only need to log in if the connection was not PREAUTH */
752 if (imap_greeting(session) != IMAP_SUCCESS) {
753 session_destroy(SESSION(session));
758 if (account->ssl_imap == SSL_STARTTLS &&
759 imap_has_capability(session, "STARTTLS")) {
762 ok = imap_cmd_starttls(session);
763 if (ok != IMAP_SUCCESS) {
764 log_warning(_("Can't start TLS session.\n"));
765 session_destroy(SESSION(session));
768 if (!ssl_init_socket_with_method(SESSION(session)->sock,
770 session_destroy(SESSION(session));
774 imap_free_capabilities(session);
775 session->authenticated = FALSE;
776 session->uidplus = FALSE;
777 session->cmd_count = 1;
779 if (imap_greeting(session) != IMAP_SUCCESS) {
780 session_destroy(SESSION(session));
785 log_message("IMAP connection is %s-authenticated\n",
786 (session->authenticated) ? "pre" : "un");
791 static void imap_session_authenticate(IMAPSession *session,
792 const PrefsAccount *account)
796 g_return_if_fail(account->userid != NULL);
798 pass = account->passwd;
801 tmp_pass = input_dialog_query_password(account->recv_server, account->userid);
804 Xstrdup_a(pass, tmp_pass, {g_free(tmp_pass); return;});
808 if (imap_auth(session, account->userid, pass, account->imap_auth_type) != IMAP_SUCCESS) {
809 imap_cmd_logout(session);
813 session->authenticated = TRUE;
816 static void imap_session_destroy(Session *session)
818 imap_free_capabilities(IMAP_SESSION(session));
819 g_free(IMAP_SESSION(session)->mbox);
820 sock_close(session->sock);
821 session->sock = NULL;
824 static gchar *imap_fetch_msg(Folder *folder, FolderItem *item, gint uid)
826 gchar *path, *filename;
827 IMAPSession *session;
830 g_return_val_if_fail(folder != NULL, NULL);
831 g_return_val_if_fail(item != NULL, NULL);
833 path = folder_item_get_path(item);
834 if (!is_dir_exist(path))
836 filename = g_strconcat(path, G_DIR_SEPARATOR_S, itos(uid), NULL);
839 if (is_file_exist(filename)) {
840 debug_print("message %d has been already cached.\n", uid);
844 session = imap_session_get(folder);
850 ok = imap_select(session, IMAP_FOLDER(folder), item->path,
851 NULL, NULL, NULL, NULL);
852 if (ok != IMAP_SUCCESS) {
853 g_warning("can't select mailbox %s\n", item->path);
858 debug_print("getting message %d...\n", uid);
859 ok = imap_cmd_fetch(session, (guint32)uid, filename);
861 if (ok != IMAP_SUCCESS) {
862 g_warning("can't fetch message %d\n", uid);
870 static gint imap_add_msg(Folder *folder, FolderItem *dest,
871 const gchar *file, MsgFlags *flags)
875 MsgFileInfo fileinfo;
877 g_return_val_if_fail(file != NULL, -1);
879 fileinfo.msginfo = NULL;
880 fileinfo.file = (gchar *)file;
881 fileinfo.flags = flags;
882 file_list.data = &fileinfo;
883 file_list.next = NULL;
885 ret = imap_add_msgs(folder, dest, &file_list, NULL);
889 static gint imap_add_msgs(Folder *folder, FolderItem *dest, GSList *file_list,
893 IMAPSession *session;
894 guint32 last_uid = 0;
896 MsgFileInfo *fileinfo;
899 g_return_val_if_fail(folder != NULL, -1);
900 g_return_val_if_fail(dest != NULL, -1);
901 g_return_val_if_fail(file_list != NULL, -1);
903 session = imap_session_get(folder);
904 if (!session) return -1;
906 destdir = imap_get_real_path(IMAP_FOLDER(folder), dest->path);
908 for (cur = file_list; cur != NULL; cur = cur->next) {
909 IMAPFlags iflags = 0;
912 fileinfo = (MsgFileInfo *)cur->data;
914 if (fileinfo->flags) {
915 if (MSG_IS_MARKED(*fileinfo->flags))
916 iflags |= IMAP_FLAG_FLAGGED;
917 if (MSG_IS_REPLIED(*fileinfo->flags))
918 iflags |= IMAP_FLAG_ANSWERED;
919 if (!MSG_IS_UNREAD(*fileinfo->flags))
920 iflags |= IMAP_FLAG_SEEN;
923 if (dest->stype == F_OUTBOX ||
924 dest->stype == F_QUEUE ||
925 dest->stype == F_DRAFT ||
926 dest->stype == F_TRASH)
927 iflags |= IMAP_FLAG_SEEN;
929 ok = imap_cmd_append(session, destdir, fileinfo->file, iflags,
932 if (ok != IMAP_SUCCESS) {
933 g_warning("can't append message %s\n", fileinfo->file);
938 if (relation != NULL)
939 g_relation_insert(relation, fileinfo->msginfo != NULL ?
940 (gpointer) fileinfo->msginfo : (gpointer) fileinfo,
941 GINT_TO_POINTER(dest->last_num + 1));
942 if (last_uid < new_uid)
951 static gint imap_do_copy_msgs(Folder *folder, FolderItem *dest,
952 MsgInfoList *msglist, GRelation *relation)
956 GSList *seq_list, *cur;
958 IMAPSession *session;
959 gint ok = IMAP_SUCCESS;
960 GRelation *uid_mapping;
963 g_return_val_if_fail(folder != NULL, -1);
964 g_return_val_if_fail(dest != NULL, -1);
965 g_return_val_if_fail(msglist != NULL, -1);
967 session = imap_session_get(folder);
968 if (!session) return -1;
970 msginfo = (MsgInfo *)msglist->data;
972 src = msginfo->folder;
974 g_warning("the src folder is identical to the dest.\n");
978 ok = imap_select(session, IMAP_FOLDER(folder), msginfo->folder->path,
979 NULL, NULL, NULL, NULL);
980 if (ok != IMAP_SUCCESS)
983 destdir = imap_get_real_path(IMAP_FOLDER(folder), dest->path);
984 seq_list = imap_get_seq_set_from_msglist(msglist);
985 uid_mapping = g_relation_new(2);
986 g_relation_index(uid_mapping, 0, g_direct_hash, g_direct_equal);
988 for (cur = seq_list; cur != NULL; cur = g_slist_next(cur)) {
989 gchar *seq_set = (gchar *)cur->data;
991 debug_print("Copying message %s%c[%s] to %s ...\n",
992 src->path, G_DIR_SEPARATOR,
995 ok = imap_cmd_copy(session, seq_set, destdir, uid_mapping);
996 if (ok != IMAP_SUCCESS) {
997 g_relation_destroy(uid_mapping);
998 imap_seq_set_free(seq_list);
1003 for (cur = msglist; cur != NULL; cur = g_slist_next(cur)) {
1004 MsgInfo *msginfo = (MsgInfo *)cur->data;
1007 tuples = g_relation_select(uid_mapping,
1008 GINT_TO_POINTER(msginfo->msgnum),
1010 if (tuples->len > 0) {
1011 gint num = GPOINTER_TO_INT(g_tuples_index(tuples, 0, 1));
1012 g_relation_insert(relation, msginfo,
1013 GPOINTER_TO_INT(num));
1017 g_relation_insert(relation, msginfo,
1018 GPOINTER_TO_INT(0));
1019 g_tuples_destroy(tuples);
1022 g_relation_destroy(uid_mapping);
1023 imap_seq_set_free(seq_list);
1027 if (ok == IMAP_SUCCESS)
1033 static gint imap_copy_msg(Folder *folder, FolderItem *dest, MsgInfo *msginfo)
1037 g_return_val_if_fail(msginfo != NULL, -1);
1039 msglist.data = msginfo;
1040 msglist.next = NULL;
1042 return imap_copy_msgs(folder, dest, &msglist, NULL);
1045 static gint imap_copy_msgs(Folder *folder, FolderItem *dest,
1046 MsgInfoList *msglist, GRelation *relation)
1052 g_return_val_if_fail(folder != NULL, -1);
1053 g_return_val_if_fail(dest != NULL, -1);
1054 g_return_val_if_fail(msglist != NULL, -1);
1056 msginfo = (MsgInfo *)msglist->data;
1057 g_return_val_if_fail(msginfo->folder != NULL, -1);
1059 if (folder == msginfo->folder->folder)
1060 return imap_do_copy_msgs(folder, dest, msglist, relation);
1062 file_list = procmsg_get_message_file_list(msglist);
1063 g_return_val_if_fail(file_list != NULL, -1);
1065 ret = imap_add_msgs(folder, dest, file_list, relation);
1067 procmsg_message_file_list_free(file_list);
1072 static gint imap_remove_msg(Folder *folder, FolderItem *item, gint uid)
1075 IMAPSession *session;
1077 MsgNumberList numlist;
1079 g_return_val_if_fail(folder != NULL, -1);
1080 g_return_val_if_fail(FOLDER_CLASS(folder) == &imap_class, -1);
1081 g_return_val_if_fail(item != NULL, -1);
1083 session = imap_session_get(folder);
1084 if (!session) return -1;
1086 ok = imap_select(session, IMAP_FOLDER(folder), item->path,
1087 NULL, NULL, NULL, NULL);
1088 if (ok != IMAP_SUCCESS)
1091 numlist.next = NULL;
1092 numlist.data = GINT_TO_POINTER(uid);
1094 ok = imap_set_message_flags
1095 (IMAP_SESSION(REMOTE_FOLDER(folder)->session),
1096 &numlist, IMAP_FLAG_DELETED, TRUE);
1097 if (ok != IMAP_SUCCESS) {
1098 log_warning(_("can't set deleted flags: %d\n"), uid);
1102 if (!session->uidplus) {
1103 ok = imap_cmd_expunge(session, NULL);
1107 uidstr = g_strdup_printf("%u", uid);
1108 ok = imap_cmd_expunge(session, uidstr);
1111 if (ok != IMAP_SUCCESS) {
1112 log_warning(_("can't expunge\n"));
1116 IMAP_FOLDER_ITEM(item)->uid_list = g_slist_remove(
1117 IMAP_FOLDER_ITEM(item)->uid_list, numlist.data);
1118 dir = folder_item_get_path(item);
1119 if (is_dir_exist(dir))
1120 remove_numbered_files(dir, uid, uid);
1123 return IMAP_SUCCESS;
1126 static gint imap_remove_all_msg(Folder *folder, FolderItem *item)
1129 IMAPSession *session;
1132 g_return_val_if_fail(folder != NULL, -1);
1133 g_return_val_if_fail(item != NULL, -1);
1135 session = imap_session_get(folder);
1136 if (!session) return -1;
1138 ok = imap_select(session, IMAP_FOLDER(folder), item->path,
1139 NULL, NULL, NULL, NULL);
1140 if (ok != IMAP_SUCCESS)
1143 imap_gen_send(session, "STORE 1:* +FLAGS.SILENT (\\Deleted)");
1144 ok = imap_cmd_ok(session, NULL);
1145 if (ok != IMAP_SUCCESS) {
1146 log_warning(_("can't set deleted flags: 1:*\n"));
1150 ok = imap_cmd_expunge(session, NULL);
1151 if (ok != IMAP_SUCCESS) {
1152 log_warning(_("can't expunge\n"));
1156 dir = folder_item_get_path(item);
1157 if (is_dir_exist(dir))
1158 remove_all_numbered_files(dir);
1161 return IMAP_SUCCESS;
1164 static gboolean imap_is_msg_changed(Folder *folder, FolderItem *item,
1167 /* TODO: properly implement this method */
1171 static gint imap_close(Folder *folder, FolderItem *item)
1174 IMAPSession *session;
1176 g_return_val_if_fail(folder != NULL, -1);
1177 g_return_val_if_fail(item != NULL, -1);
1178 g_return_val_if_fail(item->path != NULL, -1);
1180 session = imap_session_get(folder);
1181 if (!session) return -1;
1183 if (session->mbox) {
1184 if (strcmp2(session->mbox, item->path) != 0) return -1;
1186 ok = imap_cmd_close(session);
1187 if (ok != IMAP_SUCCESS)
1188 log_warning(_("can't close folder\n"));
1190 g_free(session->mbox);
1191 session->mbox = NULL;
1199 static gint imap_scan_tree(Folder *folder)
1201 FolderItem *item = NULL;
1202 IMAPSession *session;
1203 gchar *root_folder = NULL;
1205 g_return_val_if_fail(folder != NULL, -1);
1206 g_return_val_if_fail(folder->account != NULL, -1);
1208 session = imap_session_get(folder);
1210 if (!folder->node) {
1211 folder_tree_destroy(folder);
1212 item = folder_item_new(folder, folder->name, NULL);
1213 item->folder = folder;
1214 folder->node = item->node = g_node_new(item);
1219 if (folder->account->imap_dir && *folder->account->imap_dir) {
1224 Xstrdup_a(root_folder, folder->account->imap_dir, return -1);
1225 extract_quote(root_folder, '"');
1226 subst_char(root_folder,
1227 imap_get_path_separator(IMAP_FOLDER(folder),
1230 strtailchomp(root_folder, '/');
1231 real_path = imap_get_real_path
1232 (IMAP_FOLDER(folder), root_folder);
1233 debug_print("IMAP root directory: %s\n", real_path);
1235 /* check if root directory exist */
1236 argbuf = g_ptr_array_new();
1237 ok = imap_cmd_list(session, NULL, real_path, argbuf);
1238 if (ok != IMAP_SUCCESS ||
1239 search_array_str(argbuf, "LIST ") == NULL) {
1240 log_warning(_("root folder %s does not exist\n"), real_path);
1241 g_ptr_array_free(argbuf, TRUE);
1244 if (!folder->node) {
1245 item = folder_item_new(folder, folder->name, NULL);
1246 item->folder = folder;
1247 folder->node = item->node = g_node_new(item);
1251 g_ptr_array_free(argbuf, TRUE);
1256 item = FOLDER_ITEM(folder->node->data);
1257 if (!item || ((item->path || root_folder) &&
1258 strcmp2(item->path, root_folder) != 0)) {
1259 folder_tree_destroy(folder);
1260 item = folder_item_new(folder, folder->name, root_folder);
1261 item->folder = folder;
1262 folder->node = item->node = g_node_new(item);
1265 imap_scan_tree_recursive(session, FOLDER_ITEM(folder->node->data));
1266 imap_create_missing_folders(folder);
1271 static gint imap_scan_tree_recursive(IMAPSession *session, FolderItem *item)
1274 IMAPFolder *imapfolder;
1275 FolderItem *new_item;
1276 GSList *item_list, *cur;
1279 gchar *wildcard_path;
1283 g_return_val_if_fail(item != NULL, -1);
1284 g_return_val_if_fail(item->folder != NULL, -1);
1285 g_return_val_if_fail(item->no_sub == FALSE, -1);
1287 folder = item->folder;
1288 imapfolder = IMAP_FOLDER(folder);
1290 separator = imap_get_path_separator(imapfolder, item->path);
1292 if (folder->ui_func)
1293 folder->ui_func(folder, item, folder->ui_func_data);
1296 wildcard[0] = separator;
1299 real_path = imap_get_real_path(imapfolder, item->path);
1303 real_path = g_strdup("");
1306 Xstrcat_a(wildcard_path, real_path, wildcard,
1307 {g_free(real_path); return IMAP_ERROR;});
1308 QUOTE_IF_REQUIRED(wildcard_path, wildcard_path);
1310 imap_gen_send(session, "LIST \"\" %s",
1313 strtailchomp(real_path, separator);
1314 item_list = imap_parse_list(imapfolder, session, real_path, NULL);
1317 node = item->node->children;
1318 while (node != NULL) {
1319 FolderItem *old_item = FOLDER_ITEM(node->data);
1320 GNode *next = node->next;
1323 for (cur = item_list; cur != NULL; cur = cur->next) {
1324 FolderItem *cur_item = FOLDER_ITEM(cur->data);
1325 if (!strcmp2(old_item->path, cur_item->path)) {
1326 new_item = cur_item;
1331 debug_print("folder '%s' not found. removing...\n",
1333 folder_item_remove(old_item);
1335 old_item->no_sub = new_item->no_sub;
1336 old_item->no_select = new_item->no_select;
1337 if (old_item->no_sub == TRUE && node->children) {
1338 debug_print("folder '%s' doesn't have "
1339 "subfolders. removing...\n",
1341 folder_item_remove_children(old_item);
1348 for (cur = item_list; cur != NULL; cur = cur->next) {
1349 FolderItem *cur_item = FOLDER_ITEM(cur->data);
1351 for (node = item->node->children; node != NULL;
1352 node = node->next) {
1353 if (!strcmp2(FOLDER_ITEM(node->data)->path,
1355 new_item = FOLDER_ITEM(node->data);
1356 folder_item_destroy(cur_item);
1362 new_item = cur_item;
1363 debug_print("new folder '%s' found.\n", new_item->path);
1364 folder_item_append(item, new_item);
1367 if (!strcmp(new_item->path, "INBOX")) {
1368 new_item->stype = F_INBOX;
1369 folder->inbox = new_item;
1370 } else if (!folder_item_parent(item) || item->stype == F_INBOX) {
1373 base = g_basename(new_item->path);
1375 if (!folder->outbox && !g_ascii_strcasecmp(base, "Sent")) {
1376 new_item->stype = F_OUTBOX;
1377 folder->outbox = new_item;
1378 } else if (!folder->draft && !g_ascii_strcasecmp(base, "Drafts")) {
1379 new_item->stype = F_DRAFT;
1380 folder->draft = new_item;
1381 } else if (!folder->queue && !g_ascii_strcasecmp(base, "Queue")) {
1382 new_item->stype = F_QUEUE;
1383 folder->queue = new_item;
1384 } else if (!folder->trash && !g_ascii_strcasecmp(base, "Trash")) {
1385 new_item->stype = F_TRASH;
1386 folder->trash = new_item;
1390 if (new_item->no_sub == FALSE)
1391 imap_scan_tree_recursive(session, new_item);
1394 g_slist_free(item_list);
1396 return IMAP_SUCCESS;
1399 static GSList *imap_parse_list(IMAPFolder *folder, IMAPSession *session,
1400 const gchar *real_path, gchar *separator)
1402 gchar buf[IMAPBUFSIZE];
1404 gchar separator_str[16];
1407 gchar *loc_name, *loc_path;
1408 GSList *item_list = NULL;
1410 FolderItem *new_item;
1412 debug_print("getting list of %s ...\n",
1413 *real_path ? real_path : "\"\"");
1415 str = g_string_new(NULL);
1418 if (sock_gets(SESSION(session)->sock, buf, sizeof(buf)) <= 0) {
1419 log_warning(_("error occurred while getting LIST.\n"));
1423 if (buf[0] != '*' || buf[1] != ' ') {
1424 log_print("IMAP4< %s\n", buf);
1425 if (sscanf(buf, "%*d %16s", buf) < 1 ||
1426 strcmp(buf, "OK") != 0)
1427 log_warning(_("error occurred while getting LIST.\n"));
1431 debug_print("IMAP4< %s\n", buf);
1433 g_string_assign(str, buf);
1435 if (strncmp(p, "LIST ", 5) != 0) continue;
1438 if (*p != '(') continue;
1440 p = strchr_cpy(p, ')', flags, sizeof(flags));
1442 while (*p == ' ') p++;
1444 p = strchr_cpy(p, ' ', separator_str, sizeof(separator_str));
1446 extract_quote(separator_str, '"');
1447 if (!strcmp(separator_str, "NIL"))
1448 separator_str[0] = '\0';
1450 *separator = separator_str[0];
1453 while (*p == ' ') p++;
1454 if (*p == '{' || *p == '"')
1455 p = imap_parse_atom(SESSION(session)->sock, p,
1456 buf, sizeof(buf), str);
1458 strncpy2(buf, p, sizeof(buf));
1459 strtailchomp(buf, separator_str[0]);
1460 if (buf[0] == '\0') continue;
1461 if (!strcmp(buf, real_path)) continue;
1463 if (separator_str[0] != '\0')
1464 subst_char(buf, separator_str[0], '/');
1465 name = g_basename(buf);
1466 if (name[0] == '.') continue;
1468 loc_name = imap_modified_utf7_to_utf8(name);
1469 loc_path = imap_modified_utf7_to_utf8(buf);
1470 new_item = folder_item_new(FOLDER(folder), loc_name, loc_path);
1471 if (strcasestr(flags, "\\Noinferiors") != NULL)
1472 new_item->no_sub = TRUE;
1473 if (strcmp(buf, "INBOX") != 0 &&
1474 strcasestr(flags, "\\Noselect") != NULL)
1475 new_item->no_select = TRUE;
1477 item_list = g_slist_append(item_list, new_item);
1479 debug_print("folder '%s' found.\n", loc_path);
1484 g_string_free(str, TRUE);
1489 static gint imap_create_tree(Folder *folder)
1491 g_return_val_if_fail(folder != NULL, -1);
1492 g_return_val_if_fail(folder->node != NULL, -1);
1493 g_return_val_if_fail(folder->node->data != NULL, -1);
1494 g_return_val_if_fail(folder->account != NULL, -1);
1496 imap_scan_tree(folder);
1497 imap_create_missing_folders(folder);
1502 static void imap_create_missing_folders(Folder *folder)
1504 g_return_if_fail(folder != NULL);
1507 folder->inbox = imap_create_special_folder
1508 (folder, F_INBOX, "INBOX");
1510 if (!folder->outbox)
1511 folder->outbox = imap_create_special_folder
1512 (folder, F_OUTBOX, "Sent");
1514 folder->draft = imap_create_special_folder
1515 (folder, F_DRAFT, "Drafts");
1517 folder->queue = imap_create_special_folder
1518 (folder, F_QUEUE, "Queue");
1521 folder->trash = imap_create_special_folder
1522 (folder, F_TRASH, "Trash");
1525 static FolderItem *imap_create_special_folder(Folder *folder,
1526 SpecialFolderItemType stype,
1530 FolderItem *new_item;
1532 g_return_val_if_fail(folder != NULL, NULL);
1533 g_return_val_if_fail(folder->node != NULL, NULL);
1534 g_return_val_if_fail(folder->node->data != NULL, NULL);
1535 g_return_val_if_fail(folder->account != NULL, NULL);
1536 g_return_val_if_fail(name != NULL, NULL);
1538 item = FOLDER_ITEM(folder->node->data);
1539 new_item = imap_create_folder(folder, item, name);
1542 g_warning("Can't create '%s'\n", name);
1543 if (!folder->inbox) return NULL;
1545 new_item = imap_create_folder(folder, folder->inbox, name);
1547 g_warning("Can't create '%s' under INBOX\n", name);
1549 new_item->stype = stype;
1551 new_item->stype = stype;
1556 static gchar *imap_folder_get_path(Folder *folder)
1560 g_return_val_if_fail(folder != NULL, NULL);
1561 g_return_val_if_fail(folder->account != NULL, NULL);
1563 folder_path = g_strconcat(get_imap_cache_dir(),
1565 folder->account->recv_server,
1567 folder->account->userid,
1573 static gchar *imap_item_get_path(Folder *folder, FolderItem *item)
1575 gchar *folder_path, *path;
1577 g_return_val_if_fail(folder != NULL, NULL);
1578 g_return_val_if_fail(item != NULL, NULL);
1579 folder_path = imap_folder_get_path(folder);
1581 g_return_val_if_fail(folder_path != NULL, NULL);
1582 if (folder_path[0] == G_DIR_SEPARATOR) {
1584 path = g_strconcat(folder_path, G_DIR_SEPARATOR_S,
1587 path = g_strdup(folder_path);
1590 path = g_strconcat(get_home_dir(), G_DIR_SEPARATOR_S,
1591 folder_path, G_DIR_SEPARATOR_S,
1594 path = g_strconcat(get_home_dir(), G_DIR_SEPARATOR_S,
1597 g_free(folder_path);
1602 static FolderItem *imap_create_folder(Folder *folder, FolderItem *parent,
1605 gchar *dirpath, *imap_path;
1606 IMAPSession *session;
1607 FolderItem *new_item;
1613 g_return_val_if_fail(folder != NULL, NULL);
1614 g_return_val_if_fail(folder->account != NULL, NULL);
1615 g_return_val_if_fail(parent != NULL, NULL);
1616 g_return_val_if_fail(name != NULL, NULL);
1618 session = imap_session_get(folder);
1619 if (!session) return NULL;
1621 if (!folder_item_parent(parent) && strcmp(name, "INBOX") == 0)
1622 dirpath = g_strdup(name);
1623 else if (parent->path)
1624 dirpath = g_strconcat(parent->path, "/", name, NULL);
1625 else if ((p = strchr(name, '/')) != NULL && *(p + 1) != '\0')
1626 dirpath = g_strdup(name);
1627 else if (folder->account->imap_dir && *folder->account->imap_dir) {
1630 Xstrdup_a(imap_dir, folder->account->imap_dir, return NULL);
1631 strtailchomp(imap_dir, '/');
1632 dirpath = g_strconcat(imap_dir, "/", name, NULL);
1634 dirpath = g_strdup(name);
1636 /* keep trailing directory separator to create a folder that contains
1638 imap_path = imap_utf8_to_modified_utf7(dirpath);
1639 strtailchomp(dirpath, '/');
1640 Xstrdup_a(new_name, name, {g_free(dirpath); return NULL;});
1641 strtailchomp(new_name, '/');
1642 separator = imap_get_path_separator(IMAP_FOLDER(folder), imap_path);
1643 imap_path_separator_subst(imap_path, separator);
1644 subst_char(new_name, '/', separator);
1646 if (strcmp(name, "INBOX") != 0) {
1649 gboolean exist = FALSE;
1651 argbuf = g_ptr_array_new();
1652 ok = imap_cmd_list(session, NULL, imap_path,
1654 if (ok != IMAP_SUCCESS) {
1655 log_warning(_("can't create mailbox: LIST failed\n"));
1658 ptr_array_free_strings(argbuf);
1659 g_ptr_array_free(argbuf, TRUE);
1663 for (i = 0; i < argbuf->len; i++) {
1665 str = g_ptr_array_index(argbuf, i);
1666 if (!strncmp(str, "LIST ", 5)) {
1671 ptr_array_free_strings(argbuf);
1672 g_ptr_array_free(argbuf, TRUE);
1675 ok = imap_cmd_create(session, imap_path);
1676 if (ok != IMAP_SUCCESS) {
1677 log_warning(_("can't create mailbox\n"));
1685 new_item = folder_item_new(folder, new_name, dirpath);
1686 folder_item_append(parent, new_item);
1690 dirpath = folder_item_get_path(new_item);
1691 if (!is_dir_exist(dirpath))
1692 make_dir_hier(dirpath);
1698 static gint imap_rename_folder(Folder *folder, FolderItem *item,
1703 gchar *real_oldpath;
1704 gchar *real_newpath;
1706 gchar *old_cache_dir;
1707 gchar *new_cache_dir;
1708 IMAPSession *session;
1711 gint exists, recent, unseen;
1712 guint32 uid_validity;
1714 g_return_val_if_fail(folder != NULL, -1);
1715 g_return_val_if_fail(item != NULL, -1);
1716 g_return_val_if_fail(item->path != NULL, -1);
1717 g_return_val_if_fail(name != NULL, -1);
1719 if (strchr(name, imap_get_path_separator(IMAP_FOLDER(folder), item->path)) != NULL) {
1720 g_warning(_("New folder name must not contain the namespace "
1725 session = imap_session_get(folder);
1726 if (!session) return -1;
1728 real_oldpath = imap_get_real_path(IMAP_FOLDER(folder), item->path);
1730 g_free(session->mbox);
1731 session->mbox = NULL;
1732 ok = imap_cmd_examine(session, "INBOX",
1733 &exists, &recent, &unseen, &uid_validity);
1734 if (ok != IMAP_SUCCESS) {
1735 g_free(real_oldpath);
1739 separator = imap_get_path_separator(IMAP_FOLDER(folder), item->path);
1740 if (strchr(item->path, G_DIR_SEPARATOR)) {
1741 dirpath = g_path_get_dirname(item->path);
1742 newpath = g_strconcat(dirpath, G_DIR_SEPARATOR_S, name, NULL);
1745 newpath = g_strdup(name);
1747 real_newpath = imap_utf8_to_modified_utf7(newpath);
1748 imap_path_separator_subst(real_newpath, separator);
1750 ok = imap_cmd_rename(session, real_oldpath, real_newpath);
1751 if (ok != IMAP_SUCCESS) {
1752 log_warning(_("can't rename mailbox: %s to %s\n"),
1753 real_oldpath, real_newpath);
1754 g_free(real_oldpath);
1756 g_free(real_newpath);
1761 item->name = g_strdup(name);
1763 old_cache_dir = folder_item_get_path(item);
1765 paths[0] = g_strdup(item->path);
1767 g_node_traverse(item->node, G_PRE_ORDER, G_TRAVERSE_ALL, -1,
1768 imap_rename_folder_func, paths);
1770 if (is_dir_exist(old_cache_dir)) {
1771 new_cache_dir = folder_item_get_path(item);
1772 if (rename(old_cache_dir, new_cache_dir) < 0) {
1773 FILE_OP_ERROR(old_cache_dir, "rename");
1775 g_free(new_cache_dir);
1778 g_free(old_cache_dir);
1781 g_free(real_oldpath);
1782 g_free(real_newpath);
1787 static gint imap_remove_folder(Folder *folder, FolderItem *item)
1790 IMAPSession *session;
1793 gint exists, recent, unseen;
1794 guint32 uid_validity;
1796 g_return_val_if_fail(folder != NULL, -1);
1797 g_return_val_if_fail(item != NULL, -1);
1798 g_return_val_if_fail(item->path != NULL, -1);
1800 session = imap_session_get(folder);
1801 if (!session) return -1;
1803 path = imap_get_real_path(IMAP_FOLDER(folder), item->path);
1805 ok = imap_cmd_examine(session, "INBOX",
1806 &exists, &recent, &unseen, &uid_validity);
1807 if (ok != IMAP_SUCCESS) {
1812 ok = imap_cmd_delete(session, path);
1813 if (ok != IMAP_SUCCESS) {
1814 log_warning(_("can't delete mailbox\n"));
1820 cache_dir = folder_item_get_path(item);
1821 if (is_dir_exist(cache_dir) && remove_dir_recursive(cache_dir) < 0)
1822 g_warning("can't remove directory '%s'\n", cache_dir);
1824 folder_item_remove(item);
1829 static GSList *imap_get_uncached_messages(IMAPSession *session,
1831 MsgNumberList *numlist)
1834 GSList *newlist = NULL;
1835 GSList *llast = NULL;
1838 GSList *seq_list, *cur;
1841 g_return_val_if_fail(session != NULL, NULL);
1842 g_return_val_if_fail(item != NULL, NULL);
1843 g_return_val_if_fail(item->folder != NULL, NULL);
1844 g_return_val_if_fail(FOLDER_CLASS(item->folder) == &imap_class, NULL);
1846 seq_list = imap_get_seq_set_from_numlist(numlist);
1847 for (cur = seq_list; cur != NULL; cur = g_slist_next(cur)) {
1848 imapset = cur->data;
1850 if (imap_cmd_envelope(session, imapset)
1852 log_warning(_("can't get envelope\n"));
1856 str = g_string_new(NULL);
1859 if ((tmp = sock_getline(SESSION(session)->sock)) == NULL) {
1860 log_warning(_("error occurred while getting envelope.\n"));
1861 g_string_free(str, TRUE);
1865 if (tmp[0] != '*' || tmp[1] != ' ') {
1866 log_print("IMAP4< %s\n", tmp);
1870 if (strstr(tmp, "FETCH") == NULL) {
1871 log_print("IMAP4< %s\n", tmp);
1875 log_print("IMAP4< %s\n", tmp);
1876 g_string_assign(str, tmp);
1879 msginfo = imap_parse_envelope
1880 (SESSION(session)->sock, item, str);
1882 log_warning(_("can't parse envelope: %s\n"), str->str);
1885 if (item->stype == F_QUEUE) {
1886 MSG_SET_TMP_FLAGS(msginfo->flags, MSG_QUEUED);
1887 } else if (item->stype == F_DRAFT) {
1888 MSG_SET_TMP_FLAGS(msginfo->flags, MSG_DRAFT);
1891 msginfo->folder = item;
1894 llast = newlist = g_slist_append(newlist, msginfo);
1896 llast = g_slist_append(llast, msginfo);
1897 llast = llast->next;
1901 g_string_free(str, TRUE);
1903 imap_seq_set_free(seq_list);
1905 session_set_access_time(SESSION(session));
1910 static void imap_delete_all_cached_messages(FolderItem *item)
1914 g_return_if_fail(item != NULL);
1915 g_return_if_fail(item->folder != NULL);
1916 g_return_if_fail(FOLDER_CLASS(item->folder) == &imap_class);
1918 debug_print("Deleting all cached messages...\n");
1920 dir = folder_item_get_path(item);
1921 if (is_dir_exist(dir))
1922 remove_all_numbered_files(dir);
1925 debug_print("done.\n");
1929 static SockInfo *imap_open_tunnel(const gchar *server,
1930 const gchar *tunnelcmd,
1933 static SockInfo *imap_open_tunnel(const gchar *server,
1934 const gchar *tunnelcmd)
1939 if ((sock = sock_connect_cmd(server, tunnelcmd)) == NULL) {
1940 log_warning(_("Can't establish IMAP4 session with: %s\n"),
1945 return imap_init_sock(sock, ssl_type);
1947 return imap_init_sock(sock);
1953 static SockInfo *imap_open(const gchar *server, gushort port,
1956 static SockInfo *imap_open(const gchar *server, gushort port)
1961 if ((sock = sock_connect(server, port)) == NULL) {
1962 log_warning(_("Can't connect to IMAP4 server: %s:%d\n"),
1968 if (ssl_type == SSL_TUNNEL && !ssl_init_socket(sock)) {
1969 log_warning(_("Can't establish IMAP4 session with: %s:%d\n"),
1979 static SockInfo *imap_init_sock(SockInfo *sock, SSLType ssl_type)
1981 static SockInfo *imap_init_sock(SockInfo *sock)
1988 static GList *imap_parse_namespace_str(gchar *str)
1993 IMAPNameSpace *namespace;
1994 GList *ns_list = NULL;
1996 while (*p != '\0') {
1997 /* parse ("#foo" "/") */
1999 while (*p && *p != '(') p++;
2000 if (*p == '\0') break;
2003 while (*p && *p != '"') p++;
2004 if (*p == '\0') break;
2008 while (*p && *p != '"') p++;
2009 if (*p == '\0') break;
2013 while (*p && isspace(*p)) p++;
2014 if (*p == '\0') break;
2015 if (strncmp(p, "NIL", 3) == 0)
2017 else if (*p == '"') {
2020 while (*p && *p != '"') p++;
2021 if (*p == '\0') break;
2026 while (*p && *p != ')') p++;
2027 if (*p == '\0') break;
2030 namespace = g_new(IMAPNameSpace, 1);
2031 namespace->name = g_strdup(name);
2032 namespace->separator = separator ? separator[0] : '\0';
2033 ns_list = g_list_append(ns_list, namespace);
2039 static void imap_parse_namespace(IMAPSession *session, IMAPFolder *folder)
2044 g_return_if_fail(session != NULL);
2045 g_return_if_fail(folder != NULL);
2047 if (folder->ns_personal != NULL ||
2048 folder->ns_others != NULL ||
2049 folder->ns_shared != NULL)
2052 if (!imap_has_capability(session, "NAMESPACE")) {
2053 imap_get_namespace_by_list(session, folder);
2057 if (imap_cmd_namespace(session, &ns_str)
2059 log_warning(_("can't get namespace\n"));
2063 str_array = strsplit_parenthesis(ns_str, '(', ')', 3);
2064 if (str_array == NULL) {
2066 imap_get_namespace_by_list(session, folder);
2070 folder->ns_personal = imap_parse_namespace_str(str_array[0]);
2071 if (str_array[0] && str_array[1])
2072 folder->ns_others = imap_parse_namespace_str(str_array[1]);
2073 if (str_array[0] && str_array[1] && str_array[2])
2074 folder->ns_shared = imap_parse_namespace_str(str_array[2]);
2075 g_strfreev(str_array);
2079 static void imap_get_namespace_by_list(IMAPSession *session, IMAPFolder *folder)
2081 GSList *item_list, *cur;
2082 gchar separator = '\0';
2083 IMAPNameSpace *namespace;
2085 g_return_if_fail(session != NULL);
2086 g_return_if_fail(folder != NULL);
2088 if (folder->ns_personal != NULL ||
2089 folder->ns_others != NULL ||
2090 folder->ns_shared != NULL)
2093 imap_gen_send(session, "LIST \"\" \"\"");
2094 item_list = imap_parse_list(folder, session, "", &separator);
2095 for (cur = item_list; cur != NULL; cur = cur->next)
2096 folder_item_destroy(FOLDER_ITEM(cur->data));
2097 g_slist_free(item_list);
2099 namespace = g_new(IMAPNameSpace, 1);
2100 namespace->name = g_strdup("");
2101 namespace->separator = separator;
2102 folder->ns_personal = g_list_append(NULL, namespace);
2105 static IMAPNameSpace *imap_find_namespace_from_list(GList *ns_list,
2108 IMAPNameSpace *namespace = NULL;
2109 gchar *tmp_path, *name;
2111 if (!path) path = "";
2113 for (; ns_list != NULL; ns_list = ns_list->next) {
2114 IMAPNameSpace *tmp_ns = ns_list->data;
2116 Xstrcat_a(tmp_path, path, "/", return namespace);
2117 Xstrdup_a(name, tmp_ns->name, return namespace);
2118 if (tmp_ns->separator && tmp_ns->separator != '/') {
2119 subst_char(tmp_path, tmp_ns->separator, '/');
2120 subst_char(name, tmp_ns->separator, '/');
2122 if (strncmp(tmp_path, name, strlen(name)) == 0)
2129 static IMAPNameSpace *imap_find_namespace(IMAPFolder *folder,
2132 IMAPNameSpace *namespace;
2134 g_return_val_if_fail(folder != NULL, NULL);
2136 namespace = imap_find_namespace_from_list(folder->ns_personal, path);
2137 if (namespace) return namespace;
2138 namespace = imap_find_namespace_from_list(folder->ns_others, path);
2139 if (namespace) return namespace;
2140 namespace = imap_find_namespace_from_list(folder->ns_shared, path);
2141 if (namespace) return namespace;
2146 static gchar imap_get_path_separator(IMAPFolder *folder, const gchar *path)
2148 IMAPNameSpace *namespace;
2149 gchar separator = '/';
2151 namespace = imap_find_namespace(folder, path);
2152 if (namespace && namespace->separator)
2153 separator = namespace->separator;
2158 static gchar *imap_get_real_path(IMAPFolder *folder, const gchar *path)
2163 g_return_val_if_fail(folder != NULL, NULL);
2164 g_return_val_if_fail(path != NULL, NULL);
2166 real_path = imap_utf8_to_modified_utf7(path);
2167 separator = imap_get_path_separator(folder, path);
2168 imap_path_separator_subst(real_path, separator);
2173 static gchar *imap_parse_atom(SockInfo *sock, gchar *src,
2174 gchar *dest, gint dest_len, GString *str)
2176 gchar *cur_pos = src;
2179 g_return_val_if_fail(str != NULL, cur_pos);
2181 /* read the next line if the current response buffer is empty */
2182 while (isspace(*(guchar *)cur_pos)) cur_pos++;
2183 while (*cur_pos == '\0') {
2184 if ((nextline = sock_getline(sock)) == NULL)
2186 g_string_assign(str, nextline);
2188 strretchomp(nextline);
2189 /* log_print("IMAP4< %s\n", nextline); */
2190 debug_print("IMAP4< %s\n", nextline);
2193 while (isspace(*(guchar *)cur_pos)) cur_pos++;
2196 if (!strncmp(cur_pos, "NIL", 3)) {
2199 } else if (*cur_pos == '\"') {
2202 p = get_quoted(cur_pos, '\"', dest, dest_len);
2203 cur_pos = p ? p : cur_pos + 2;
2204 } else if (*cur_pos == '{') {
2209 cur_pos = strchr_cpy(cur_pos + 1, '}', buf, sizeof(buf));
2211 g_return_val_if_fail(len >= 0, cur_pos);
2213 g_string_truncate(str, 0);
2217 if ((nextline = sock_getline(sock)) == NULL)
2219 line_len += strlen(nextline);
2220 g_string_append(str, nextline);
2222 strretchomp(nextline);
2223 /* log_print("IMAP4< %s\n", nextline); */
2224 debug_print("IMAP4< %s\n", nextline);
2226 } while (line_len < len);
2228 memcpy(dest, cur_pos, MIN(len, dest_len - 1));
2229 dest[MIN(len, dest_len - 1)] = '\0';
2236 static gchar *imap_get_header(SockInfo *sock, gchar *cur_pos, gchar **headers,
2246 g_return_val_if_fail(str != NULL, cur_pos);
2248 while (isspace(*(guchar *)cur_pos)) cur_pos++;
2250 g_return_val_if_fail(*cur_pos == '{', cur_pos);
2252 cur_pos = strchr_cpy(cur_pos + 1, '}', buf, sizeof(buf));
2254 g_return_val_if_fail(len >= 0, cur_pos);
2256 g_string_truncate(str, 0);
2260 if ((nextline = sock_getline(sock)) == NULL)
2262 block_len += strlen(nextline);
2263 g_string_append(str, nextline);
2265 strretchomp(nextline);
2266 /* debug_print("IMAP4< %s\n", nextline); */
2268 } while (block_len < len);
2270 debug_print("IMAP4< [contents of BODY.PEEK[HEADER.FIELDS (...)]]\n");
2272 *headers = g_strndup(cur_pos, len);
2275 while (isspace(*(guchar *)cur_pos)) cur_pos++;
2276 while (*cur_pos == '\0') {
2277 if ((nextline = sock_getline(sock)) == NULL)
2279 g_string_assign(str, nextline);
2281 strretchomp(nextline);
2282 debug_print("IMAP4< %s\n", nextline);
2285 while (isspace(*(guchar *)cur_pos)) cur_pos++;
2291 static MsgFlags imap_parse_flags(const gchar *flag_str)
2293 const gchar *p = flag_str;
2294 MsgFlags flags = {0, 0};
2296 flags.perm_flags = MSG_UNREAD;
2298 while ((p = strchr(p, '\\')) != NULL) {
2301 if (g_ascii_strncasecmp(p, "Recent", 6) == 0 && MSG_IS_UNREAD(flags)) {
2302 MSG_SET_PERM_FLAGS(flags, MSG_NEW);
2303 } else if (g_ascii_strncasecmp(p, "Seen", 4) == 0) {
2304 MSG_UNSET_PERM_FLAGS(flags, MSG_NEW|MSG_UNREAD);
2305 } else if (g_ascii_strncasecmp(p, "Deleted", 7) == 0) {
2306 MSG_SET_PERM_FLAGS(flags, MSG_DELETED);
2307 } else if (g_ascii_strncasecmp(p, "Flagged", 7) == 0) {
2308 MSG_SET_PERM_FLAGS(flags, MSG_MARKED);
2309 } else if (g_ascii_strncasecmp(p, "Answered", 8) == 0) {
2310 MSG_SET_PERM_FLAGS(flags, MSG_REPLIED);
2317 static MsgInfo *imap_parse_envelope(SockInfo *sock, FolderItem *item,
2320 gchar buf[IMAPBUFSIZE];
2321 MsgInfo *msginfo = NULL;
2326 MsgFlags flags = {0, 0}, imap_flags = {0, 0};
2328 g_return_val_if_fail(line_str != NULL, NULL);
2329 g_return_val_if_fail(line_str->str[0] == '*' &&
2330 line_str->str[1] == ' ', NULL);
2332 MSG_SET_TMP_FLAGS(flags, MSG_IMAP);
2333 if (item->stype == F_QUEUE) {
2334 MSG_SET_TMP_FLAGS(flags, MSG_QUEUED);
2335 } else if (item->stype == F_DRAFT) {
2336 MSG_SET_TMP_FLAGS(flags, MSG_DRAFT);
2339 cur_pos = line_str->str + 2;
2341 #define PARSE_ONE_ELEMENT(ch) \
2343 cur_pos = strchr_cpy(cur_pos, ch, buf, sizeof(buf)); \
2344 if (cur_pos == NULL) { \
2345 g_warning("cur_pos == NULL\n"); \
2346 procmsg_msginfo_free(msginfo); \
2351 PARSE_ONE_ELEMENT(' ');
2354 PARSE_ONE_ELEMENT(' ');
2355 g_return_val_if_fail(!strcmp(buf, "FETCH"), NULL);
2357 g_return_val_if_fail(*cur_pos == '(', NULL);
2360 while (*cur_pos != '\0' && *cur_pos != ')') {
2361 while (*cur_pos == ' ') cur_pos++;
2363 if (!strncmp(cur_pos, "UID ", 4)) {
2365 uid = strtoul(cur_pos, &cur_pos, 10);
2366 } else if (!strncmp(cur_pos, "FLAGS ", 6)) {
2368 if (*cur_pos != '(') {
2369 g_warning("*cur_pos != '('\n");
2370 procmsg_msginfo_free(msginfo);
2374 PARSE_ONE_ELEMENT(')');
2375 imap_flags = imap_parse_flags(buf);
2376 } else if (!strncmp(cur_pos, "RFC822.SIZE ", 12)) {
2378 size = strtol(cur_pos, &cur_pos, 10);
2379 } else if (!strncmp(cur_pos, "BODY[HEADER.FIELDS ", 19)) {
2383 if (*cur_pos != '(') {
2384 g_warning("*cur_pos != '('\n");
2385 procmsg_msginfo_free(msginfo);
2389 PARSE_ONE_ELEMENT(')');
2390 if (*cur_pos != ']') {
2391 g_warning("*cur_pos != ']'\n");
2392 procmsg_msginfo_free(msginfo);
2397 cur_pos = imap_get_header(sock, cur_pos, &headers,
2399 msginfo = procheader_parse_str(headers, flags, FALSE, FALSE);
2402 g_warning("invalid FETCH response: %s\n", cur_pos);
2408 msginfo->msgnum = uid;
2409 msginfo->size = size;
2410 msginfo->flags.tmp_flags |= imap_flags.tmp_flags;
2411 msginfo->flags.perm_flags = imap_flags.perm_flags;
2417 static gchar *imap_get_flag_str(IMAPFlags flags)
2422 str = g_string_new(NULL);
2424 if (IMAP_IS_SEEN(flags)) g_string_append(str, "\\Seen ");
2425 if (IMAP_IS_ANSWERED(flags)) g_string_append(str, "\\Answered ");
2426 if (IMAP_IS_FLAGGED(flags)) g_string_append(str, "\\Flagged ");
2427 if (IMAP_IS_DELETED(flags)) g_string_append(str, "\\Deleted ");
2428 if (IMAP_IS_DRAFT(flags)) g_string_append(str, "\\Draft");
2430 if (str->len > 0 && str->str[str->len - 1] == ' ')
2431 g_string_truncate(str, str->len - 1);
2434 g_string_free(str, FALSE);
2439 static gint imap_set_message_flags(IMAPSession *session,
2440 MsgNumberList *numlist,
2447 GSList *seq_list, *cur;
2450 flag_str = imap_get_flag_str(flags);
2451 cmd = g_strconcat(is_set ? "+FLAGS.SILENT (" : "-FLAGS.SILENT (",
2452 flag_str, ")", NULL);
2455 seq_list = imap_get_seq_set_from_numlist(numlist);
2456 for (cur = seq_list; cur != NULL; cur = g_slist_next(cur)) {
2457 imapset = cur->data;
2459 ok = imap_cmd_store(session, imapset, cmd);
2461 imap_seq_set_free(seq_list);
2467 static gint imap_select(IMAPSession *session, IMAPFolder *folder,
2469 gint *exists, gint *recent, gint *unseen,
2470 guint32 *uid_validity)
2474 gint exists_, recent_, unseen_;
2475 guint32 uid_validity_;
2477 if (!exists || !recent || !unseen || !uid_validity) {
2478 if (session->mbox && strcmp(session->mbox, path) == 0)
2479 return IMAP_SUCCESS;
2483 uid_validity = &uid_validity_;
2486 g_free(session->mbox);
2487 session->mbox = NULL;
2489 real_path = imap_get_real_path(folder, path);
2490 ok = imap_cmd_select(session, real_path,
2491 exists, recent, unseen, uid_validity);
2492 if (ok != IMAP_SUCCESS)
2493 log_warning(_("can't select folder: %s\n"), real_path);
2495 session->mbox = g_strdup(path);
2496 session->folder_content_changed = FALSE;
2503 #define THROW(err) { ok = err; goto catch; }
2505 static gint imap_status(IMAPSession *session, IMAPFolder *folder,
2507 gint *messages, gint *recent,
2508 guint32 *uid_next, guint32 *uid_validity,
2514 GPtrArray *argbuf = NULL;
2517 if (messages && recent && uid_next && uid_validity && unseen) {
2518 *messages = *recent = *uid_next = *uid_validity = *unseen = 0;
2519 argbuf = g_ptr_array_new();
2522 real_path = imap_get_real_path(folder, path);
2523 QUOTE_IF_REQUIRED(real_path_, real_path);
2524 imap_gen_send(session, "STATUS %s "
2525 "(MESSAGES RECENT UIDNEXT UIDVALIDITY UNSEEN)",
2528 ok = imap_cmd_ok(session, argbuf);
2529 if (ok != IMAP_SUCCESS || !argbuf) THROW(ok);
2531 str = search_array_str(argbuf, "STATUS");
2532 if (!str) THROW(IMAP_ERROR);
2534 str = strchr(str, '(');
2535 if (!str) THROW(IMAP_ERROR);
2537 while (*str != '\0' && *str != ')') {
2538 while (*str == ' ') str++;
2540 if (!strncmp(str, "MESSAGES ", 9)) {
2542 *messages = strtol(str, &str, 10);
2543 } else if (!strncmp(str, "RECENT ", 7)) {
2545 *recent = strtol(str, &str, 10);
2546 } else if (!strncmp(str, "UIDNEXT ", 8)) {
2548 *uid_next = strtoul(str, &str, 10);
2549 } else if (!strncmp(str, "UIDVALIDITY ", 12)) {
2551 *uid_validity = strtoul(str, &str, 10);
2552 } else if (!strncmp(str, "UNSEEN ", 7)) {
2554 *unseen = strtol(str, &str, 10);
2556 g_warning("invalid STATUS response: %s\n", str);
2564 ptr_array_free_strings(argbuf);
2565 g_ptr_array_free(argbuf, TRUE);
2573 static gboolean imap_has_capability(IMAPSession *session, const gchar *cap)
2577 for (p = session->capability; *p != NULL; ++p) {
2578 if (!g_ascii_strcasecmp(*p, cap))
2585 static void imap_free_capabilities(IMAPSession *session)
2587 g_strfreev(session->capability);
2588 session->capability = NULL;
2591 /* low-level IMAP4rev1 commands */
2593 static gint imap_cmd_authenticate(IMAPSession *session, const gchar *user,
2594 const gchar *pass, IMAPAuthType type)
2601 gchar hexdigest[33];
2605 auth_type = "CRAM-MD5";
2607 imap_gen_send(session, "AUTHENTICATE %s", auth_type);
2608 ok = imap_gen_recv(session, &buf);
2609 if (ok != IMAP_SUCCESS || buf[0] != '+' || buf[1] != ' ') {
2614 challenge = g_malloc(strlen(buf + 2) + 1);
2615 challenge_len = base64_decode(challenge, buf + 2, -1);
2616 challenge[challenge_len] = '\0';
2618 log_print("IMAP< [Decoded: %s]\n", challenge);
2620 md5_hex_hmac(hexdigest, challenge, challenge_len, pass, strlen(pass));
2623 response = g_strdup_printf("%s %s", user, hexdigest);
2624 log_print("IMAP> [Encoded: %s]\n", response);
2625 response64 = g_malloc((strlen(response) + 3) * 2 + 1);
2626 base64_encode(response64, response, strlen(response));
2629 log_print("IMAP> %s\n", response64);
2630 sock_puts(SESSION(session)->sock, response64);
2631 ok = imap_cmd_ok(session, NULL);
2632 if (ok != IMAP_SUCCESS)
2633 log_warning(_("IMAP4 authentication failed.\n"));
2638 static gint imap_cmd_login(IMAPSession *session,
2639 const gchar *user, const gchar *pass)
2641 gchar *user_, *pass_;
2644 QUOTE_IF_REQUIRED(user_, user);
2645 QUOTE_IF_REQUIRED(pass_, pass);
2646 imap_gen_send(session, "LOGIN %s %s", user_, pass_);
2648 ok = imap_cmd_ok(session, NULL);
2649 if (ok != IMAP_SUCCESS)
2650 log_warning(_("IMAP4 login failed.\n"));
2655 static gint imap_cmd_logout(IMAPSession *session)
2657 imap_gen_send(session, "LOGOUT");
2658 return imap_cmd_ok(session, NULL);
2661 static gint imap_cmd_noop(IMAPSession *session)
2663 imap_gen_send(session, "NOOP");
2664 return imap_cmd_ok(session, NULL);
2668 static gint imap_cmd_starttls(IMAPSession *session)
2670 imap_gen_send(session, "STARTTLS");
2671 return imap_cmd_ok(session, NULL);
2675 #define THROW(err) { ok = err; goto catch; }
2677 static gint imap_cmd_namespace(IMAPSession *session, gchar **ns_str)
2683 argbuf = g_ptr_array_new();
2685 imap_gen_send(session, "NAMESPACE");
2686 if ((ok = imap_cmd_ok(session, argbuf)) != IMAP_SUCCESS) THROW(ok);
2688 str = search_array_str(argbuf, "NAMESPACE");
2689 if (!str) THROW(IMAP_ERROR);
2691 *ns_str = g_strdup(str);
2694 ptr_array_free_strings(argbuf);
2695 g_ptr_array_free(argbuf, TRUE);
2702 static gint imap_cmd_list(IMAPSession *session, const gchar *ref,
2703 const gchar *mailbox, GPtrArray *argbuf)
2705 gchar *ref_, *mailbox_;
2707 if (!ref) ref = "\"\"";
2708 if (!mailbox) mailbox = "\"\"";
2710 QUOTE_IF_REQUIRED(ref_, ref);
2711 QUOTE_IF_REQUIRED(mailbox_, mailbox);
2712 imap_gen_send(session, "LIST %s %s", ref_, mailbox_);
2714 return imap_cmd_ok(session, argbuf);
2717 #define THROW goto catch
2719 static gint imap_cmd_do_select(IMAPSession *session, const gchar *folder,
2721 gint *exists, gint *recent, gint *unseen,
2722 guint32 *uid_validity)
2729 unsigned int uid_validity_;
2731 *exists = *recent = *unseen = *uid_validity = 0;
2732 argbuf = g_ptr_array_new();
2735 select_cmd = "EXAMINE";
2737 select_cmd = "SELECT";
2739 QUOTE_IF_REQUIRED(folder_, folder);
2740 imap_gen_send(session, "%s %s", select_cmd, folder_);
2742 if ((ok = imap_cmd_ok(session, argbuf)) != IMAP_SUCCESS) THROW;
2744 resp_str = search_array_contain_str(argbuf, "EXISTS");
2746 if (sscanf(resp_str,"%d EXISTS", exists) != 1) {
2747 g_warning("imap_cmd_select(): invalid EXISTS line.\n");
2752 resp_str = search_array_contain_str(argbuf, "RECENT");
2754 if (sscanf(resp_str, "%d RECENT", recent) != 1) {
2755 g_warning("imap_cmd_select(): invalid RECENT line.\n");
2760 resp_str = search_array_contain_str(argbuf, "UIDVALIDITY");
2762 if (sscanf(resp_str, "OK [UIDVALIDITY %u] ", &uid_validity_)
2764 g_warning("imap_cmd_select(): invalid UIDVALIDITY line.\n");
2767 *uid_validity = uid_validity_;
2770 resp_str = search_array_contain_str(argbuf, "UNSEEN");
2772 if (sscanf(resp_str, "OK [UNSEEN %d] ", unseen) != 1) {
2773 g_warning("imap_cmd_select(): invalid UNSEEN line.\n");
2779 ptr_array_free_strings(argbuf);
2780 g_ptr_array_free(argbuf, TRUE);
2785 static gint imap_cmd_select(IMAPSession *session, const gchar *folder,
2786 gint *exists, gint *recent, gint *unseen,
2787 guint32 *uid_validity)
2789 return imap_cmd_do_select(session, folder, FALSE,
2790 exists, recent, unseen, uid_validity);
2793 static gint imap_cmd_examine(IMAPSession *session, const gchar *folder,
2794 gint *exists, gint *recent, gint *unseen,
2795 guint32 *uid_validity)
2797 return imap_cmd_do_select(session, folder, TRUE,
2798 exists, recent, unseen, uid_validity);
2803 static gint imap_cmd_create(IMAPSession *session, const gchar *folder)
2807 QUOTE_IF_REQUIRED(folder_, folder);
2808 imap_gen_send(session, "CREATE %s", folder_);
2810 return imap_cmd_ok(session, NULL);
2813 static gint imap_cmd_rename(IMAPSession *session, const gchar *old_folder,
2814 const gchar *new_folder)
2816 gchar *old_folder_, *new_folder_;
2818 QUOTE_IF_REQUIRED(old_folder_, old_folder);
2819 QUOTE_IF_REQUIRED(new_folder_, new_folder);
2820 imap_gen_send(session, "RENAME %s %s", old_folder_, new_folder_);
2822 return imap_cmd_ok(session, NULL);
2825 static gint imap_cmd_delete(IMAPSession *session, const gchar *folder)
2829 QUOTE_IF_REQUIRED(folder_, folder);
2830 imap_gen_send(session, "DELETE %s", folder_);
2832 return imap_cmd_ok(session, NULL);
2835 static gint imap_cmd_search(IMAPSession *session, const gchar *criteria,
2842 g_return_val_if_fail(criteria != NULL, IMAP_ERROR);
2843 g_return_val_if_fail(list != NULL, IMAP_ERROR);
2847 argbuf = g_ptr_array_new();
2848 imap_gen_send(session, "UID SEARCH %s", criteria);
2850 ok = imap_cmd_ok(session, argbuf);
2851 if (ok != IMAP_SUCCESS) {
2852 ptr_array_free_strings(argbuf);
2853 g_ptr_array_free(argbuf, TRUE);
2857 if ((uidlist = search_array_str(argbuf, "SEARCH ")) != NULL) {
2858 gchar **strlist, **p;
2860 strlist = g_strsplit(uidlist + 7, " ", 0);
2861 for (p = strlist; *p != NULL; ++p) {
2864 if (sscanf(*p, "%u", &msgnum) == 1)
2865 *list = g_slist_append(*list, GINT_TO_POINTER(msgnum));
2867 g_strfreev(strlist);
2869 ptr_array_free_strings(argbuf);
2870 g_ptr_array_free(argbuf, TRUE);
2872 return IMAP_SUCCESS;
2875 static gint imap_cmd_fetch(IMAPSession *session, guint32 uid,
2876 const gchar *filename)
2884 g_return_val_if_fail(filename != NULL, IMAP_ERROR);
2886 imap_gen_send(session, "UID FETCH %d BODY.PEEK[]", uid);
2888 while ((ok = imap_gen_recv(session, &buf)) == IMAP_SUCCESS) {
2889 if (buf[0] != '*' || buf[1] != ' ') {
2893 if (strstr(buf, "FETCH") != NULL) break;
2896 if (ok != IMAP_SUCCESS) {
2901 #define RETURN_ERROR_IF_FAIL(cond) \
2904 return IMAP_ERROR; \
2907 cur_pos = strchr(buf, '{');
2908 RETURN_ERROR_IF_FAIL(cur_pos != NULL);
2909 cur_pos = strchr_cpy(cur_pos + 1, '}', size_str, sizeof(size_str));
2910 RETURN_ERROR_IF_FAIL(cur_pos != NULL);
2911 size_num = atol(size_str);
2912 RETURN_ERROR_IF_FAIL(size_num >= 0);
2914 RETURN_ERROR_IF_FAIL(*cur_pos == '\0');
2916 #undef RETURN_ERROR_IF_FAIL
2920 if (recv_bytes_write_to_file(SESSION(session)->sock,
2921 size_num, filename) != 0)
2924 if (imap_gen_recv(session, &buf) != IMAP_SUCCESS) {
2929 if (buf[0] == '\0' || buf[strlen(buf) - 1] != ')') {
2935 ok = imap_cmd_ok(session, NULL);
2940 static gint imap_cmd_append(IMAPSession *session, const gchar *destfolder,
2941 const gchar *file, IMAPFlags flags,
2948 unsigned int new_uid_;
2950 gchar buf[BUFFSIZE];
2955 g_return_val_if_fail(file != NULL, IMAP_ERROR);
2957 size = get_file_size_as_crlf(file);
2958 if ((fp = fopen(file, "rb")) == NULL) {
2959 FILE_OP_ERROR(file, "fopen");
2962 QUOTE_IF_REQUIRED(destfolder_, destfolder);
2963 flag_str = imap_get_flag_str(flags);
2964 imap_gen_send(session, "APPEND %s (%s) {%d}",
2965 destfolder_, flag_str, size);
2968 ok = imap_gen_recv(session, &ret);
2969 if (ok != IMAP_SUCCESS || ret[0] != '+' || ret[1] != ' ') {
2970 log_warning(_("can't append %s to %s\n"), file, destfolder_);
2977 log_print("IMAP4> %s\n", "(sending file...)");
2979 while (fgets(buf, sizeof(buf), fp) != NULL) {
2981 if (sock_puts(SESSION(session)->sock, buf) < 0) {
2988 FILE_OP_ERROR(file, "fgets");
2993 sock_puts(SESSION(session)->sock, "");
2997 if (new_uid != NULL)
3000 if (new_uid != NULL && session->uidplus) {
3001 argbuf = g_ptr_array_new();
3003 ok = imap_cmd_ok(session, argbuf);
3004 if ((ok == IMAP_SUCCESS) && (argbuf->len > 0)) {
3005 resp_str = g_ptr_array_index(argbuf, argbuf->len - 1);
3007 sscanf(resp_str, "%*u OK [APPENDUID %*u %u]",
3009 *new_uid = new_uid_;
3013 ptr_array_free_strings(argbuf);
3014 g_ptr_array_free(argbuf, TRUE);
3016 ok = imap_cmd_ok(session, NULL);
3018 if (ok != IMAP_SUCCESS)
3019 log_warning(_("can't append message to %s\n"),
3025 static MsgNumberList *imapset_to_numlist(IMAPSet imapset)
3027 gchar **ranges, **range;
3028 unsigned int low, high;
3029 MsgNumberList *uids = NULL;
3031 ranges = g_strsplit(imapset, ",", 0);
3032 for (range = ranges; *range != NULL; range++) {
3033 printf("%s\n", *range);
3034 if (sscanf(*range, "%u:%u", &low, &high) == 1)
3035 uids = g_slist_prepend(uids, GINT_TO_POINTER(low));
3038 for (i = low; i <= high; i++)
3039 uids = g_slist_prepend(uids, GINT_TO_POINTER(i));
3042 uids = g_slist_reverse(uids);
3048 static gint imap_cmd_copy(IMAPSession *session, const gchar *seq_set,
3049 const gchar *destfolder, GRelation *uid_mapping)
3054 g_return_val_if_fail(session != NULL, IMAP_ERROR);
3055 g_return_val_if_fail(seq_set != NULL, IMAP_ERROR);
3056 g_return_val_if_fail(destfolder != NULL, IMAP_ERROR);
3058 QUOTE_IF_REQUIRED(destfolder_, destfolder);
3059 imap_gen_send(session, "UID COPY %s %s", seq_set, destfolder_);
3061 if (uid_mapping != NULL && session->uidplus) {
3063 gchar *resp_str = NULL, *olduids_str, *newuids_str;
3064 MsgNumberList *olduids, *old_cur, *newuids, *new_cur;
3066 reply = g_ptr_array_new();
3067 ok = imap_cmd_ok(session, reply);
3068 if ((ok == IMAP_SUCCESS) && (reply->len > 0)) {
3069 resp_str = g_ptr_array_index(reply, reply->len - 1);
3071 olduids_str = g_new0(gchar, strlen(resp_str));
3072 newuids_str = g_new0(gchar, strlen(resp_str));
3073 if (sscanf(resp_str, "%*s OK [COPYUID %*u %[0-9,:] %[0-9,:]]",
3074 olduids_str, newuids_str) == 2) {
3075 olduids = imapset_to_numlist(olduids_str);
3076 newuids = imapset_to_numlist(newuids_str);
3080 while(old_cur != NULL && new_cur != NULL) {
3081 g_relation_insert(uid_mapping,
3082 GPOINTER_TO_INT(old_cur->data),
3083 GPOINTER_TO_INT(new_cur->data));
3084 old_cur = g_slist_next(old_cur);
3085 new_cur = g_slist_next(new_cur);
3088 g_slist_free(olduids);
3089 g_slist_free(newuids);
3091 g_free(olduids_str);
3092 g_free(newuids_str);
3095 ptr_array_free_strings(reply);
3096 g_ptr_array_free(reply, TRUE);
3098 ok = imap_cmd_ok(session, NULL);
3100 if (ok != IMAP_SUCCESS)
3101 log_warning(_("can't copy %s to %s\n"), seq_set, destfolder_);
3106 gint imap_cmd_envelope(IMAPSession *session, IMAPSet set)
3108 static GString *header_fields = NULL;
3110 if (header_fields == NULL) {
3111 const HeaderEntry *headers, *elem;
3113 headers = procheader_get_headernames(FALSE);
3114 header_fields = g_string_new("");
3116 for (elem = headers; elem->name != NULL; ++elem) {
3117 gint namelen = strlen(elem->name);
3119 /* Header fields ending with space are not rfc822 headers */
3120 if (elem->name[namelen - 1] == ' ')
3123 /* strip : at the of header field */
3124 if(elem->name[namelen - 1] == ':')
3130 g_string_append_printf(header_fields, "%s%.*s",
3131 header_fields->str[0] != '\0' ? " " : "",
3132 namelen, elem->name);
3137 (session, "UID FETCH %s (UID FLAGS RFC822.SIZE BODY.PEEK[HEADER.FIELDS (%s)])",
3138 set, header_fields->str);
3140 return IMAP_SUCCESS;
3143 static gint imap_cmd_store(IMAPSession *session, IMAPSet seq_set,
3148 imap_gen_send(session, "UID STORE %s %s", seq_set, sub_cmd);
3150 if ((ok = imap_cmd_ok(session, NULL)) != IMAP_SUCCESS) {
3151 log_warning(_("error while imap command: STORE %s %s\n"),
3156 return IMAP_SUCCESS;
3159 static gint imap_cmd_expunge(IMAPSession *session, IMAPSet seq_set)
3163 if (seq_set && session->uidplus)
3164 imap_gen_send(session, "UID EXPUNGE %s", seq_set);
3166 imap_gen_send(session, "EXPUNGE");
3167 if ((ok = imap_cmd_ok(session, NULL)) != IMAP_SUCCESS) {
3168 log_warning(_("error while imap command: EXPUNGE\n"));
3172 return IMAP_SUCCESS;
3175 static gint imap_cmd_close(IMAPSession *session)
3179 imap_gen_send(session, "CLOSE");
3180 if ((ok = imap_cmd_ok(session, NULL)) != IMAP_SUCCESS)
3181 log_warning(_("error while imap command: CLOSE\n"));
3186 static gint imap_cmd_ok(IMAPSession *session, GPtrArray *argbuf)
3188 gint ok = IMAP_SUCCESS;
3193 while ((ok = imap_gen_recv(session, &buf))
3195 /* make sure data is long enough for any substring of buf */
3196 data = alloca(strlen(buf) + 1);
3198 /* untagged line read */
3199 if (buf[0] == '*' && buf[1] == ' ') {
3202 g_ptr_array_add(argbuf, g_strdup(buf + 2));
3204 if (sscanf(buf + 2, "%d %s", &num, data) >= 2) {
3205 if (!strcmp(data, "EXISTS")) {
3206 session->exists = num;
3207 session->folder_content_changed = TRUE;
3210 if(!strcmp(data, "EXPUNGE")) {
3212 session->folder_content_changed = TRUE;
3215 /* tagged line with correct tag and OK response found */
3216 } else if ((sscanf(buf, "%d %s", &cmd_num, data) >= 2) &&
3217 (cmd_num == session->cmd_count) &&
3218 !strcmp(data, "OK")) {
3220 g_ptr_array_add(argbuf, g_strdup(buf));
3222 /* everything else */
3234 static void imap_gen_send(IMAPSession *session, const gchar *format, ...)
3241 va_start(args, format);
3242 tmp = g_strdup_vprintf(format, args);
3245 session->cmd_count++;
3247 buf = g_strdup_printf("%d %s\r\n", session->cmd_count, tmp);
3248 if (!g_ascii_strncasecmp(tmp, "LOGIN ", 6) && (p = strchr(tmp + 6, ' '))) {
3250 log_print("IMAP4> %d %s ********\n", session->cmd_count, tmp);
3252 log_print("IMAP4> %d %s\n", session->cmd_count, tmp);
3254 sock_write_all(SESSION(session)->sock, buf, strlen(buf));
3259 static gint imap_gen_recv(IMAPSession *session, gchar **ret)
3261 if ((*ret = sock_getline(SESSION(session)->sock)) == NULL)
3266 log_print("IMAP4< %s\n", *ret);
3268 session_set_access_time(SESSION(session));
3270 return IMAP_SUCCESS;
3274 /* misc utility functions */
3276 static gchar *strchr_cpy(const gchar *src, gchar ch, gchar *dest, gint len)
3281 tmp = strchr(src, ch);
3285 memcpy(dest, src, MIN(tmp - src, len - 1));
3286 dest[MIN(tmp - src, len - 1)] = '\0';
3291 static gchar *get_quoted(const gchar *src, gchar ch, gchar *dest, gint len)
3293 const gchar *p = src;
3296 g_return_val_if_fail(*p == ch, NULL);
3301 while (*p != '\0' && *p != ch) {
3303 if (*p == '\\' && *(p + 1) != '\0')
3312 return (gchar *)(*p == ch ? p + 1 : p);
3315 static gchar *search_array_contain_str(GPtrArray *array, const gchar *str)
3319 for (i = 0; i < array->len; i++) {
3322 tmp = g_ptr_array_index(array, i);
3323 if (strstr(tmp, str) != NULL)
3330 static gchar *search_array_str(GPtrArray *array, const gchar *str)
3337 for (i = 0; i < array->len; i++) {
3340 tmp = g_ptr_array_index(array, i);
3341 if (!strncmp(tmp, str, len))
3348 static void imap_path_separator_subst(gchar *str, gchar separator)
3351 gboolean in_escape = FALSE;
3353 if (!separator || separator == '/') return;
3355 for (p = str; *p != '\0'; p++) {
3356 if (*p == '/' && !in_escape)
3358 else if (*p == '&' && *(p + 1) != '-' && !in_escape)
3360 else if (*p == '-' && in_escape)
3365 static gchar *imap_modified_utf7_to_utf8(const gchar *mutf7_str)
3368 const gchar *from_p;
3371 to = g_malloc(strlen(mutf7_str) + 1);
3374 for (from_p = mutf7_str; *from_p != '\0'; from_p++) {
3375 if (*from_p == '&' && *(from_p + 1) == '-') {
3385 static iconv_t cd = (iconv_t)-1;
3386 static gboolean iconv_ok = TRUE;
3389 size_t norm_utf7_len;
3391 gchar *to_str, *to_p;
3393 gboolean in_escape = FALSE;
3395 if (!iconv_ok) return g_strdup(mutf7_str);
3397 if (cd == (iconv_t)-1) {
3398 cd = iconv_open(conv_get_current_charset_str(), CS_UTF_8);
3399 if (cd == (iconv_t)-1) {
3400 g_warning(_("iconv cannot convert UTF-7 to UTF-8\n"));
3402 return g_strdup(mutf7_str);
3406 norm_utf7 = g_string_new(NULL);
3408 for (p = mutf7_str; *p != '\0'; p++) {
3409 /* replace: '&' -> '+',
3411 escaped ',' -> '/' */
3412 if (!in_escape && *p == '&') {
3413 if (*(p + 1) != '-') {
3414 g_string_append_c(norm_utf7, '+');
3417 g_string_append_c(norm_utf7, '&');
3420 } else if (in_escape && *p == ',') {
3421 g_string_append_c(norm_utf7, '/');
3422 } else if (in_escape && *p == '-') {
3423 g_string_append_c(norm_utf7, '-');
3426 g_string_append_c(norm_utf7, *p);
3430 norm_utf7_p = norm_utf7->str;
3431 norm_utf7_len = norm_utf7->len;
3432 to_len = strlen(mutf7_str) * 5;
3433 to_p = to_str = g_malloc(to_len + 1);
3435 if (iconv(cd, (ICONV_CONST gchar **)&norm_utf7_p, &norm_utf7_len,
3436 &to_p, &to_len) == -1) {
3437 g_warning(_("iconv cannot convert UTF-7 to UTF-8\n"));
3438 g_string_free(norm_utf7, TRUE);
3440 return g_strdup(mutf7_str);
3443 /* second iconv() call for flushing */
3444 iconv(cd, NULL, NULL, &to_p, &to_len);
3445 g_string_free(norm_utf7, TRUE);
3449 #endif /* !HAVE_ICONV */
3452 static gchar *imap_utf8_to_modified_utf7(const gchar *from)
3455 const gchar *from_p;
3458 to = g_malloc(strlen(from) * 2 + 1);
3461 for (from_p = from; *from_p != '\0'; from_p++) {
3462 if (*from_p == '&') {
3472 static iconv_t cd = (iconv_t)-1;
3473 static gboolean iconv_ok = TRUE;
3474 gchar *norm_utf7, *norm_utf7_p;
3475 size_t from_len, norm_utf7_len;
3477 gchar *from_tmp, *to, *p;
3478 gboolean in_escape = FALSE;
3480 if (!iconv_ok) return g_strdup(from);
3482 if (cd == (iconv_t)-1) {
3483 cd = iconv_open("UTF-7", CS_UTF_8);
3484 if (cd == (iconv_t)-1) {
3485 g_warning("iconv cannot convert UTF-8 to UTF-7\n");
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) && IS_ASCII(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 UTF-8 to UTF-7\n"));
3533 return g_strdup(from);
3536 /* second iconv() call for flushing */
3537 iconv(cd, NULL, NULL, &norm_utf7_p, &norm_utf7_len);
3543 *norm_utf7_p = '\0';
3544 to_str = g_string_new(NULL);
3545 for (p = norm_utf7; p < norm_utf7_p; p++) {
3546 /* replace: '&' -> "&-",
3549 BASE64 '/' -> ',' */
3550 if (!in_escape && *p == '&') {
3551 g_string_append(to_str, "&-");
3552 } else if (!in_escape && *p == '+') {
3553 if (*(p + 1) == '-') {
3554 g_string_append_c(to_str, '+');
3557 g_string_append_c(to_str, '&');
3560 } else if (in_escape && *p == '/') {
3561 g_string_append_c(to_str, ',');
3562 } else if (in_escape && *p == '-') {
3563 g_string_append_c(to_str, '-');
3566 g_string_append_c(to_str, *p);
3572 g_string_append_c(to_str, '-');
3576 g_string_free(to_str, FALSE);
3579 #endif /* !HAVE_ICONV */
3582 static GSList *imap_get_seq_set_from_numlist(MsgNumberList *numlist)
3585 GSList *sorted_list, *cur;
3586 guint first, last, next;
3588 GSList *ret_list = NULL;
3590 if (numlist == NULL)
3593 str = g_string_sized_new(256);
3595 sorted_list = g_slist_copy(numlist);
3596 sorted_list = g_slist_sort(sorted_list, g_int_compare);
3598 first = GPOINTER_TO_INT(sorted_list->data);
3600 for (cur = sorted_list; cur != NULL; cur = g_slist_next(cur)) {
3601 last = GPOINTER_TO_INT(cur->data);
3603 next = GPOINTER_TO_INT(cur->next->data);
3607 if (last + 1 != next || next == 0) {
3609 g_string_append_c(str, ',');
3611 g_string_append_printf(str, "%u", first);
3613 g_string_append_printf(str, "%u:%u", first, last);
3617 if (str->len > IMAP_CMD_LIMIT) {
3618 ret_str = g_strdup(str->str);
3619 ret_list = g_slist_append(ret_list, ret_str);
3620 g_string_truncate(str, 0);
3626 ret_str = g_strdup(str->str);
3627 ret_list = g_slist_append(ret_list, ret_str);
3630 g_slist_free(sorted_list);
3631 g_string_free(str, TRUE);
3636 static GSList *imap_get_seq_set_from_msglist(MsgInfoList *msglist)
3638 MsgNumberList *numlist = NULL;
3642 for (cur = msglist; cur != NULL; cur = g_slist_next(cur)) {
3643 MsgInfo *msginfo = (MsgInfo *) cur->data;
3645 numlist = g_slist_append(numlist, GINT_TO_POINTER(msginfo->msgnum));
3647 seq_list = imap_get_seq_set_from_numlist(numlist);
3648 g_slist_free(numlist);
3653 static void imap_seq_set_free(GSList *seq_list)
3655 slist_free_strings(seq_list);
3656 g_slist_free(seq_list);
3660 static gboolean imap_rename_folder_func(GNode *node, gpointer data)
3662 FolderItem *item = node->data;
3663 gchar **paths = data;
3664 const gchar *oldpath = paths[0];
3665 const gchar *newpath = paths[1];
3667 gchar *new_itempath;
3670 oldpathlen = strlen(oldpath);
3671 if (strncmp(oldpath, item->path, oldpathlen) != 0) {
3672 g_warning("path doesn't match: %s, %s\n", oldpath, item->path);
3676 base = item->path + oldpathlen;
3677 while (*base == G_DIR_SEPARATOR) base++;
3679 new_itempath = g_strdup(newpath);
3681 new_itempath = g_strconcat(newpath, G_DIR_SEPARATOR_S, base,
3684 item->path = new_itempath;
3689 static gint get_list_of_uids(Folder *folder, IMAPFolderItem *item, GSList **msgnum_list)
3691 gint ok, nummsgs = 0, lastuid_old;
3692 IMAPSession *session;
3693 GSList *uidlist, *elem;
3696 session = imap_session_get(folder);
3697 g_return_val_if_fail(session != NULL, -1);
3699 ok = imap_select(session, IMAP_FOLDER(folder), item->item.path,
3700 NULL, NULL, NULL, NULL);
3701 if (ok != IMAP_SUCCESS)
3704 cmd_buf = g_strdup_printf("UID %d:*", item->lastuid + 1);
3705 ok = imap_cmd_search(session, cmd_buf, &uidlist);
3708 if (ok == IMAP_SOCKET) {
3709 session_destroy((Session *)session);
3710 ((RemoteFolder *)folder)->session = NULL;
3714 if (ok != IMAP_SUCCESS) {
3718 argbuf = g_ptr_array_new();
3720 cmd_buf = g_strdup_printf("UID FETCH %d:* (UID)", item->lastuid + 1);
3721 imap_gen_send(session, cmd_buf);
3723 ok = imap_cmd_ok(session, argbuf);
3724 if (ok != IMAP_SUCCESS) {
3725 ptr_array_free_strings(argbuf);
3726 g_ptr_array_free(argbuf, TRUE);
3730 for(i = 0; i < argbuf->len; i++) {
3733 if((ret = sscanf(g_ptr_array_index(argbuf, i),
3734 "%*d FETCH (UID %d)", &msgnum)) == 1)
3735 uidlist = g_slist_prepend(uidlist, GINT_TO_POINTER(msgnum));
3737 ptr_array_free_strings(argbuf);
3738 g_ptr_array_free(argbuf, TRUE);
3741 lastuid_old = item->lastuid;
3742 *msgnum_list = g_slist_copy(item->uid_list);
3743 nummsgs = g_slist_length(*msgnum_list);
3744 debug_print("Got %d uids from cache\n", g_slist_length(item->uid_list));
3746 for (elem = uidlist; elem != NULL; elem = g_slist_next(elem)) {
3749 msgnum = GPOINTER_TO_INT(elem->data);
3750 if (msgnum > lastuid_old) {
3751 *msgnum_list = g_slist_prepend(*msgnum_list, GINT_TO_POINTER(msgnum));
3752 item->uid_list = g_slist_prepend(item->uid_list, GINT_TO_POINTER(msgnum));
3755 if(msgnum > item->lastuid)
3756 item->lastuid = msgnum;
3759 g_slist_free(uidlist);
3764 gint imap_get_num_list(Folder *folder, FolderItem *_item, GSList **msgnum_list, gboolean *old_uids_valid)
3766 IMAPFolderItem *item = (IMAPFolderItem *)_item;
3767 IMAPSession *session;
3768 gint ok, nummsgs = 0, exists, recent, uid_val, uid_next, unseen;
3769 GSList *uidlist = NULL;
3771 gboolean selected_folder;
3773 g_return_val_if_fail(folder != NULL, -1);
3774 g_return_val_if_fail(item != NULL, -1);
3775 g_return_val_if_fail(item->item.path != NULL, -1);
3776 g_return_val_if_fail(FOLDER_CLASS(folder) == &imap_class, -1);
3777 g_return_val_if_fail(folder->account != NULL, -1);
3779 session = imap_session_get(folder);
3780 g_return_val_if_fail(session != NULL, -1);
3782 selected_folder = (session->mbox != NULL) &&
3783 (!strcmp(session->mbox, item->item.path));
3784 if (selected_folder) {
3785 ok = imap_cmd_noop(session);
3786 if (ok != IMAP_SUCCESS)
3788 exists = session->exists;
3790 *old_uids_valid = TRUE;
3792 ok = imap_status(session, IMAP_FOLDER(folder), item->item.path,
3793 &exists, &recent, &uid_next, &uid_val, &unseen);
3794 if (ok != IMAP_SUCCESS)
3797 if(item->item.mtime == uid_val)
3798 *old_uids_valid = TRUE;
3800 *old_uids_valid = FALSE;
3802 debug_print("Freeing imap uid cache\n");
3804 g_slist_free(item->uid_list);
3805 item->uid_list = NULL;
3807 item->item.mtime = uid_val;
3809 imap_delete_all_cached_messages((FolderItem *)item);
3813 if (!selected_folder)
3814 item->uid_next = uid_next;
3816 /* If old uid_next matches new uid_next we can be sure no message
3817 was added to the folder */
3818 if (( selected_folder && !session->folder_content_changed) ||
3819 (!selected_folder && uid_next == item->uid_next)) {
3820 nummsgs = g_slist_length(item->uid_list);
3822 /* If number of messages is still the same we
3823 know our caches message numbers are still valid,
3824 otherwise if the number of messages has decrease
3825 we discard our cache to start a new scan to find
3826 out which numbers have been removed */
3827 if (exists == nummsgs) {
3828 *msgnum_list = g_slist_copy(item->uid_list);
3830 } else if (exists < nummsgs) {
3831 debug_print("Freeing imap uid cache");
3833 g_slist_free(item->uid_list);
3834 item->uid_list = NULL;
3839 *msgnum_list = NULL;
3843 nummsgs = get_list_of_uids(folder, item, &uidlist);
3845 if (nummsgs != exists) {
3846 /* Cache contains more messages then folder, we have cached
3847 an old UID of a message that was removed and new messages
3848 have been added too, otherwise the uid_next check would
3850 debug_print("Freeing imap uid cache");
3852 g_slist_free(item->uid_list);
3853 item->uid_list = NULL;
3855 g_slist_free(*msgnum_list);
3857 nummsgs = get_list_of_uids(folder, item, &uidlist);
3860 *msgnum_list = uidlist;
3862 dir = folder_item_get_path((FolderItem *)item);
3863 debug_print("removing old messages from %s\n", dir);
3864 remove_numbered_files_not_in_list(dir, *msgnum_list);
3870 static MsgInfo *imap_parse_msg(const gchar *file, FolderItem *item)
3875 flags.perm_flags = MSG_NEW|MSG_UNREAD;
3876 flags.tmp_flags = 0;
3878 g_return_val_if_fail(item != NULL, NULL);
3879 g_return_val_if_fail(file != NULL, NULL);
3881 if (item->stype == F_QUEUE) {
3882 MSG_SET_TMP_FLAGS(flags, MSG_QUEUED);
3883 } else if (item->stype == F_DRAFT) {
3884 MSG_SET_TMP_FLAGS(flags, MSG_DRAFT);
3887 msginfo = procheader_parse_file(file, flags, FALSE, FALSE);
3888 if (!msginfo) return NULL;
3890 msginfo->folder = item;
3895 GSList *imap_get_msginfos(Folder *folder, FolderItem *item, GSList *msgnum_list)
3897 IMAPSession *session;
3898 MsgInfoList *ret = NULL;
3901 g_return_val_if_fail(folder != NULL, NULL);
3902 g_return_val_if_fail(item != NULL, NULL);
3903 g_return_val_if_fail(msgnum_list != NULL, NULL);
3905 session = imap_session_get(folder);
3906 g_return_val_if_fail(session != NULL, NULL);
3908 ok = imap_select(session, IMAP_FOLDER(folder), item->path,
3909 NULL, NULL, NULL, NULL);
3910 if (ok != IMAP_SUCCESS)
3913 if (!(item->stype == F_QUEUE || item->stype == F_DRAFT)) {
3914 ret = g_slist_concat(ret,
3915 imap_get_uncached_messages(
3916 session, item, msgnum_list));
3918 MsgNumberList *sorted_list, *elem;
3919 gint startnum, lastnum;
3921 sorted_list = g_slist_sort(g_slist_copy(msgnum_list), g_int_compare);
3923 startnum = lastnum = GPOINTER_TO_INT(sorted_list->data);
3925 for (elem = sorted_list;; elem = g_slist_next(elem)) {
3929 num = GPOINTER_TO_INT(elem->data);
3931 if (num > lastnum + 1 || elem == NULL) {
3933 for (i = startnum; i <= lastnum; ++i) {
3936 file = imap_fetch_msg(folder, item, i);
3938 MsgInfo *msginfo = imap_parse_msg(file, item);
3939 if (msginfo != NULL) {
3940 msginfo->msgnum = i;
3941 ret = g_slist_append(ret, msginfo);
3955 g_slist_free(sorted_list);
3961 MsgInfo *imap_get_msginfo(Folder *folder, FolderItem *item, gint uid)
3963 MsgInfo *msginfo = NULL;
3964 MsgInfoList *msginfolist;
3965 MsgNumberList numlist;
3967 numlist.next = NULL;
3968 numlist.data = GINT_TO_POINTER(uid);
3970 msginfolist = imap_get_msginfos(folder, item, &numlist);
3971 if (msginfolist != NULL) {
3972 msginfo = msginfolist->data;
3973 g_slist_free(msginfolist);
3979 gboolean imap_scan_required(Folder *folder, FolderItem *_item)
3981 IMAPSession *session;
3982 IMAPFolderItem *item = (IMAPFolderItem *)_item;
3983 gint ok, exists = 0, recent = 0, unseen = 0;
3984 guint32 uid_next, uid_val = 0;
3985 gboolean selected_folder;
3987 g_return_val_if_fail(folder != NULL, FALSE);
3988 g_return_val_if_fail(item != NULL, FALSE);
3989 g_return_val_if_fail(item->item.folder != NULL, FALSE);
3990 g_return_val_if_fail(FOLDER_CLASS(item->item.folder) == &imap_class, FALSE);
3992 if (item->item.path == NULL)
3995 session = imap_session_get(folder);
3996 g_return_val_if_fail(session != NULL, FALSE);
3998 selected_folder = (session->mbox != NULL) &&
3999 (!strcmp(session->mbox, item->item.path));
4000 if (selected_folder) {
4001 ok = imap_cmd_noop(session);
4002 if (ok != IMAP_SUCCESS)
4005 if (session->folder_content_changed)
4008 ok = imap_status(session, IMAP_FOLDER(folder), item->item.path,
4009 &exists, &recent, &uid_next, &uid_val, &unseen);
4010 if (ok != IMAP_SUCCESS)
4013 if ((uid_next != item->uid_next) || (exists < item->item.total_msgs))
4020 void imap_change_flags(Folder *folder, FolderItem *item, MsgInfo *msginfo, MsgPermFlags newflags)
4022 IMAPSession *session;
4023 IMAPFlags flags_set = 0, flags_unset = 0;
4024 gint ok = IMAP_SUCCESS;
4025 MsgNumberList numlist;
4027 g_return_if_fail(folder != NULL);
4028 g_return_if_fail(folder->klass == &imap_class);
4029 g_return_if_fail(item != NULL);
4030 g_return_if_fail(item->folder == folder);
4031 g_return_if_fail(msginfo != NULL);
4032 g_return_if_fail(msginfo->folder == item);
4034 session = imap_session_get(folder);
4035 if (!session) return;
4037 if ((ok = imap_select(session, IMAP_FOLDER(folder), msginfo->folder->path,
4038 NULL, NULL, NULL, NULL)) != IMAP_SUCCESS)
4041 if (!MSG_IS_MARKED(msginfo->flags) && (newflags & MSG_MARKED))
4042 flags_set |= IMAP_FLAG_FLAGGED;
4043 if ( MSG_IS_MARKED(msginfo->flags) && !(newflags & MSG_MARKED))
4044 flags_unset |= IMAP_FLAG_FLAGGED;
4046 if (!MSG_IS_UNREAD(msginfo->flags) && (newflags & MSG_UNREAD))
4047 flags_unset |= IMAP_FLAG_SEEN;
4048 if ( MSG_IS_UNREAD(msginfo->flags) && !(newflags & MSG_UNREAD))
4049 flags_set |= IMAP_FLAG_SEEN;
4051 if (!MSG_IS_REPLIED(msginfo->flags) && (newflags & MSG_REPLIED))
4052 flags_set |= IMAP_FLAG_ANSWERED;
4053 if ( MSG_IS_REPLIED(msginfo->flags) && !(newflags & MSG_REPLIED))
4054 flags_set |= IMAP_FLAG_ANSWERED;
4056 numlist.next = NULL;
4057 numlist.data = GINT_TO_POINTER(msginfo->msgnum);
4060 ok = imap_set_message_flags(session, &numlist, flags_set, TRUE);
4061 if (ok != IMAP_SUCCESS) return;
4065 ok = imap_set_message_flags(session, &numlist, flags_unset, FALSE);
4066 if (ok != IMAP_SUCCESS) return;
4069 msginfo->flags.perm_flags = newflags;
4074 static gint compare_msginfo(gconstpointer a, gconstpointer b)
4076 return ((MsgInfo *)a)->msgnum - ((MsgInfo *)b)->msgnum;
4079 static guint gslist_find_next_num(MsgNumberList **list, guint num)
4083 g_return_val_if_fail(list != NULL, -1);
4085 for (elem = *list; elem != NULL; elem = g_slist_next(elem))
4086 if (GPOINTER_TO_INT(elem->data) >= num)
4089 return elem != NULL ? GPOINTER_TO_INT(elem->data) : (gint)-1;
4093 * NEW and DELETED flags are not syncronized
4094 * - The NEW/RECENT flags in IMAP folders can not really be directly
4095 * modified by Sylpheed
4096 * - The DELETE/DELETED flag in IMAP and Sylpheed don't have the same
4097 * meaning, in IMAP it always removes the messages from the FolderItem
4098 * in Sylpheed it can mean to move the message to trash
4100 static gint imap_get_flags(Folder *folder, FolderItem *item,
4101 MsgInfoList *msginfo_list, GRelation *msgflags)
4103 IMAPSession *session;
4104 GSList *sorted_list;
4106 GSList *new = NULL, *p_new;
4107 GSList *deleted = NULL, *p_deleted;
4109 GSList *unseen = NULL, *answered = NULL, *flagged = NULL;
4110 GSList *p_unseen, *p_answered, *p_flagged;
4112 GSList *seq_list, *cur;
4113 gboolean reverse_seen = FALSE;
4116 gint exists_cnt, recent_cnt, unseen_cnt, uid_next;
4117 guint32 uidvalidity;
4119 g_return_val_if_fail(folder != NULL, -1);
4120 g_return_val_if_fail(item != NULL, -1);
4121 if (msginfo_list == NULL)
4124 session = imap_session_get(folder);
4125 g_return_val_if_fail(session != NULL, -1);
4127 ok = imap_select(session, IMAP_FOLDER(folder), item->path,
4128 NULL, NULL, NULL, NULL);
4129 if (ok != IMAP_SUCCESS)
4132 ok = imap_status(session, IMAP_FOLDER(folder), item->path,
4133 &exists_cnt, &recent_cnt, &uid_next, &uidvalidity, &unseen_cnt);
4135 if (unseen_cnt > exists_cnt / 2)
4136 reverse_seen = TRUE;
4138 cmd_buf = g_string_new(NULL);
4140 sorted_list = g_slist_sort(g_slist_copy(msginfo_list), compare_msginfo);
4142 seq_list = imap_get_seq_set_from_msglist(msginfo_list);
4144 for (cur = seq_list; cur != NULL; cur = g_slist_next(cur)) {
4145 IMAPSet imapset = cur->data;
4147 g_string_sprintf(cmd_buf, "RECENT UID %s", imapset);
4148 imap_cmd_search(session, cmd_buf->str, &p_new);
4149 new = g_slist_concat(new, p_new);
4151 g_string_sprintf(cmd_buf, "%sSEEN UID %s", reverse_seen ? "" : "UN", imapset);
4152 imap_cmd_search(session, cmd_buf->str, &p_unseen);
4153 unseen = g_slist_concat(unseen, p_unseen);
4155 g_string_sprintf(cmd_buf, "ANSWERED UID %s", imapset);
4156 imap_cmd_search(session, cmd_buf->str, &p_answered);
4157 answered = g_slist_concat(answered, p_answered);
4159 g_string_sprintf(cmd_buf, "FLAGGED UID %s", imapset);
4160 imap_cmd_search(session, cmd_buf->str, &p_flagged);
4161 flagged = g_slist_concat(flagged, p_flagged);
4163 g_string_sprintf(cmd_buf, "DELETED UID %s", imapset);
4164 imap_cmd_search(session, cmd_buf->str, &p_deleted);
4165 deleted = g_slist_concat(deleted, p_deleted);
4173 p_answered = answered;
4174 p_flagged = flagged;
4176 p_deleted = deleted;
4178 for (elem = sorted_list; elem != NULL; elem = g_slist_next(elem)) {
4183 msginfo = (MsgInfo *) elem->data;
4184 flags = msginfo->flags.perm_flags;
4185 wasnew = (flags & MSG_NEW);
4186 flags &= ~((reverse_seen ? 0 : MSG_UNREAD | MSG_NEW) | MSG_REPLIED | MSG_MARKED);
4188 flags |= MSG_UNREAD | (wasnew ? MSG_NEW : 0);
4190 if (gslist_find_next_num(&p_new, msginfo->msgnum) == msginfo->msgnum)
4193 if (gslist_find_next_num(&p_unseen, msginfo->msgnum) == msginfo->msgnum) {
4194 if (!reverse_seen) {
4195 flags |= MSG_UNREAD | (wasnew ? MSG_NEW : 0);
4197 flags &= ~(MSG_UNREAD | MSG_NEW);
4200 if (gslist_find_next_num(&p_answered, msginfo->msgnum) == msginfo->msgnum)
4201 flags |= MSG_REPLIED;
4202 if (gslist_find_next_num(&p_flagged, msginfo->msgnum) == msginfo->msgnum)
4203 flags |= MSG_MARKED;
4205 if (gslist_find_next_num(&p_deleted, msginfo->msgnum) == msginfo->msgnum)
4206 MSG_SET_PERM_FLAGS(msginfo->flags, MSG_DELETED);
4208 g_relation_insert(msgflags, msginfo, GINT_TO_POINTER(flags));
4211 imap_seq_set_free(seq_list);
4212 /* g_slist_free(deleted); */
4213 g_slist_free(flagged);
4214 g_slist_free(answered);
4215 g_slist_free(unseen);
4216 /* new not freed in original patch ??? */
4217 g_slist_free(sorted_list);
4218 g_string_free(cmd_buf, TRUE);