2 * Sylpheed -- a GTK+ based, lightweight, and fast e-mail client
3 * Copyright (C) 1999-2005 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.
27 #include <glib/gi18n.h>
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_path_get_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;
1391 if (new_item->no_sub == FALSE)
1392 imap_scan_tree_recursive(session, new_item);
1395 g_slist_free(item_list);
1397 return IMAP_SUCCESS;
1400 static GSList *imap_parse_list(IMAPFolder *folder, IMAPSession *session,
1401 const gchar *real_path, gchar *separator)
1403 gchar buf[IMAPBUFSIZE];
1405 gchar separator_str[16];
1408 gchar *loc_name, *loc_path;
1409 GSList *item_list = NULL;
1411 FolderItem *new_item;
1413 debug_print("getting list of %s ...\n",
1414 *real_path ? real_path : "\"\"");
1416 str = g_string_new(NULL);
1419 if (sock_gets(SESSION(session)->sock, buf, sizeof(buf)) <= 0) {
1420 log_warning(_("error occurred while getting LIST.\n"));
1424 if (buf[0] != '*' || buf[1] != ' ') {
1425 log_print("IMAP4< %s\n", buf);
1426 if (sscanf(buf, "%*d %16s", buf) < 1 ||
1427 strcmp(buf, "OK") != 0)
1428 log_warning(_("error occurred while getting LIST.\n"));
1432 debug_print("IMAP4< %s\n", buf);
1434 g_string_assign(str, buf);
1436 if (strncmp(p, "LIST ", 5) != 0) continue;
1439 if (*p != '(') continue;
1441 p = strchr_cpy(p, ')', flags, sizeof(flags));
1443 while (*p == ' ') p++;
1445 p = strchr_cpy(p, ' ', separator_str, sizeof(separator_str));
1447 extract_quote(separator_str, '"');
1448 if (!strcmp(separator_str, "NIL"))
1449 separator_str[0] = '\0';
1451 *separator = separator_str[0];
1454 while (*p == ' ') p++;
1455 if (*p == '{' || *p == '"')
1456 p = imap_parse_atom(SESSION(session)->sock, p,
1457 buf, sizeof(buf), str);
1459 strncpy2(buf, p, sizeof(buf));
1460 strtailchomp(buf, separator_str[0]);
1461 if (buf[0] == '\0') continue;
1462 if (!strcmp(buf, real_path)) continue;
1464 if (separator_str[0] != '\0')
1465 subst_char(buf, separator_str[0], '/');
1466 base = g_path_get_basename(buf);
1467 if (base[0] == '.') continue;
1469 loc_name = imap_modified_utf7_to_utf8(base);
1470 loc_path = imap_modified_utf7_to_utf8(buf);
1471 new_item = folder_item_new(FOLDER(folder), loc_name, loc_path);
1472 if (strcasestr(flags, "\\Noinferiors") != NULL)
1473 new_item->no_sub = TRUE;
1474 if (strcmp(buf, "INBOX") != 0 &&
1475 strcasestr(flags, "\\Noselect") != NULL)
1476 new_item->no_select = TRUE;
1478 item_list = g_slist_append(item_list, new_item);
1480 debug_print("folder '%s' found.\n", loc_path);
1486 g_string_free(str, TRUE);
1491 static gint imap_create_tree(Folder *folder)
1493 g_return_val_if_fail(folder != NULL, -1);
1494 g_return_val_if_fail(folder->node != NULL, -1);
1495 g_return_val_if_fail(folder->node->data != NULL, -1);
1496 g_return_val_if_fail(folder->account != NULL, -1);
1498 imap_scan_tree(folder);
1499 imap_create_missing_folders(folder);
1504 static void imap_create_missing_folders(Folder *folder)
1506 g_return_if_fail(folder != NULL);
1509 folder->inbox = imap_create_special_folder
1510 (folder, F_INBOX, "INBOX");
1512 if (!folder->outbox)
1513 folder->outbox = imap_create_special_folder
1514 (folder, F_OUTBOX, "Sent");
1516 folder->draft = imap_create_special_folder
1517 (folder, F_DRAFT, "Drafts");
1519 folder->queue = imap_create_special_folder
1520 (folder, F_QUEUE, "Queue");
1523 folder->trash = imap_create_special_folder
1524 (folder, F_TRASH, "Trash");
1527 static FolderItem *imap_create_special_folder(Folder *folder,
1528 SpecialFolderItemType stype,
1532 FolderItem *new_item;
1534 g_return_val_if_fail(folder != NULL, NULL);
1535 g_return_val_if_fail(folder->node != NULL, NULL);
1536 g_return_val_if_fail(folder->node->data != NULL, NULL);
1537 g_return_val_if_fail(folder->account != NULL, NULL);
1538 g_return_val_if_fail(name != NULL, NULL);
1540 item = FOLDER_ITEM(folder->node->data);
1541 new_item = imap_create_folder(folder, item, name);
1544 g_warning("Can't create '%s'\n", name);
1545 if (!folder->inbox) return NULL;
1547 new_item = imap_create_folder(folder, folder->inbox, name);
1549 g_warning("Can't create '%s' under INBOX\n", name);
1551 new_item->stype = stype;
1553 new_item->stype = stype;
1558 static gchar *imap_folder_get_path(Folder *folder)
1562 g_return_val_if_fail(folder != NULL, NULL);
1563 g_return_val_if_fail(folder->account != NULL, NULL);
1565 folder_path = g_strconcat(get_imap_cache_dir(),
1567 folder->account->recv_server,
1569 folder->account->userid,
1575 static gchar *imap_item_get_path(Folder *folder, FolderItem *item)
1577 gchar *folder_path, *path;
1579 g_return_val_if_fail(folder != NULL, NULL);
1580 g_return_val_if_fail(item != NULL, NULL);
1581 folder_path = imap_folder_get_path(folder);
1583 g_return_val_if_fail(folder_path != NULL, NULL);
1584 if (folder_path[0] == G_DIR_SEPARATOR) {
1586 path = g_strconcat(folder_path, G_DIR_SEPARATOR_S,
1589 path = g_strdup(folder_path);
1592 path = g_strconcat(get_home_dir(), G_DIR_SEPARATOR_S,
1593 folder_path, G_DIR_SEPARATOR_S,
1596 path = g_strconcat(get_home_dir(), G_DIR_SEPARATOR_S,
1599 g_free(folder_path);
1604 static FolderItem *imap_create_folder(Folder *folder, FolderItem *parent,
1607 gchar *dirpath, *imap_path;
1608 IMAPSession *session;
1609 FolderItem *new_item;
1615 g_return_val_if_fail(folder != NULL, NULL);
1616 g_return_val_if_fail(folder->account != NULL, NULL);
1617 g_return_val_if_fail(parent != NULL, NULL);
1618 g_return_val_if_fail(name != NULL, NULL);
1620 session = imap_session_get(folder);
1621 if (!session) return NULL;
1623 if (!folder_item_parent(parent) && strcmp(name, "INBOX") == 0)
1624 dirpath = g_strdup(name);
1625 else if (parent->path)
1626 dirpath = g_strconcat(parent->path, "/", name, NULL);
1627 else if ((p = strchr(name, '/')) != NULL && *(p + 1) != '\0')
1628 dirpath = g_strdup(name);
1629 else if (folder->account->imap_dir && *folder->account->imap_dir) {
1632 Xstrdup_a(imap_dir, folder->account->imap_dir, return NULL);
1633 strtailchomp(imap_dir, '/');
1634 dirpath = g_strconcat(imap_dir, "/", name, NULL);
1636 dirpath = g_strdup(name);
1638 /* keep trailing directory separator to create a folder that contains
1640 imap_path = imap_utf8_to_modified_utf7(dirpath);
1641 strtailchomp(dirpath, '/');
1642 Xstrdup_a(new_name, name, {g_free(dirpath); return NULL;});
1643 strtailchomp(new_name, '/');
1644 separator = imap_get_path_separator(IMAP_FOLDER(folder), imap_path);
1645 imap_path_separator_subst(imap_path, separator);
1646 subst_char(new_name, '/', separator);
1648 if (strcmp(name, "INBOX") != 0) {
1651 gboolean exist = FALSE;
1653 argbuf = g_ptr_array_new();
1654 ok = imap_cmd_list(session, NULL, imap_path,
1656 if (ok != IMAP_SUCCESS) {
1657 log_warning(_("can't create mailbox: LIST failed\n"));
1660 ptr_array_free_strings(argbuf);
1661 g_ptr_array_free(argbuf, TRUE);
1665 for (i = 0; i < argbuf->len; i++) {
1667 str = g_ptr_array_index(argbuf, i);
1668 if (!strncmp(str, "LIST ", 5)) {
1673 ptr_array_free_strings(argbuf);
1674 g_ptr_array_free(argbuf, TRUE);
1677 ok = imap_cmd_create(session, imap_path);
1678 if (ok != IMAP_SUCCESS) {
1679 log_warning(_("can't create mailbox\n"));
1687 new_item = folder_item_new(folder, new_name, dirpath);
1688 folder_item_append(parent, new_item);
1692 dirpath = folder_item_get_path(new_item);
1693 if (!is_dir_exist(dirpath))
1694 make_dir_hier(dirpath);
1700 static gint imap_rename_folder(Folder *folder, FolderItem *item,
1705 gchar *real_oldpath;
1706 gchar *real_newpath;
1708 gchar *old_cache_dir;
1709 gchar *new_cache_dir;
1710 IMAPSession *session;
1713 gint exists, recent, unseen;
1714 guint32 uid_validity;
1716 g_return_val_if_fail(folder != NULL, -1);
1717 g_return_val_if_fail(item != NULL, -1);
1718 g_return_val_if_fail(item->path != NULL, -1);
1719 g_return_val_if_fail(name != NULL, -1);
1721 if (strchr(name, imap_get_path_separator(IMAP_FOLDER(folder), item->path)) != NULL) {
1722 g_warning(_("New folder name must not contain the namespace "
1727 session = imap_session_get(folder);
1728 if (!session) return -1;
1730 real_oldpath = imap_get_real_path(IMAP_FOLDER(folder), item->path);
1732 g_free(session->mbox);
1733 session->mbox = NULL;
1734 ok = imap_cmd_examine(session, "INBOX",
1735 &exists, &recent, &unseen, &uid_validity);
1736 if (ok != IMAP_SUCCESS) {
1737 g_free(real_oldpath);
1741 separator = imap_get_path_separator(IMAP_FOLDER(folder), item->path);
1742 if (strchr(item->path, G_DIR_SEPARATOR)) {
1743 dirpath = g_path_get_dirname(item->path);
1744 newpath = g_strconcat(dirpath, G_DIR_SEPARATOR_S, name, NULL);
1747 newpath = g_strdup(name);
1749 real_newpath = imap_utf8_to_modified_utf7(newpath);
1750 imap_path_separator_subst(real_newpath, separator);
1752 ok = imap_cmd_rename(session, real_oldpath, real_newpath);
1753 if (ok != IMAP_SUCCESS) {
1754 log_warning(_("can't rename mailbox: %s to %s\n"),
1755 real_oldpath, real_newpath);
1756 g_free(real_oldpath);
1758 g_free(real_newpath);
1763 item->name = g_strdup(name);
1765 old_cache_dir = folder_item_get_path(item);
1767 paths[0] = g_strdup(item->path);
1769 g_node_traverse(item->node, G_PRE_ORDER, G_TRAVERSE_ALL, -1,
1770 imap_rename_folder_func, paths);
1772 if (is_dir_exist(old_cache_dir)) {
1773 new_cache_dir = folder_item_get_path(item);
1774 if (rename(old_cache_dir, new_cache_dir) < 0) {
1775 FILE_OP_ERROR(old_cache_dir, "rename");
1777 g_free(new_cache_dir);
1780 g_free(old_cache_dir);
1783 g_free(real_oldpath);
1784 g_free(real_newpath);
1789 static gint imap_remove_folder(Folder *folder, FolderItem *item)
1792 IMAPSession *session;
1795 gint exists, recent, unseen;
1796 guint32 uid_validity;
1798 g_return_val_if_fail(folder != NULL, -1);
1799 g_return_val_if_fail(item != NULL, -1);
1800 g_return_val_if_fail(item->path != NULL, -1);
1802 session = imap_session_get(folder);
1803 if (!session) return -1;
1805 path = imap_get_real_path(IMAP_FOLDER(folder), item->path);
1807 ok = imap_cmd_examine(session, "INBOX",
1808 &exists, &recent, &unseen, &uid_validity);
1809 if (ok != IMAP_SUCCESS) {
1814 ok = imap_cmd_delete(session, path);
1815 if (ok != IMAP_SUCCESS) {
1816 log_warning(_("can't delete mailbox\n"));
1822 cache_dir = folder_item_get_path(item);
1823 if (is_dir_exist(cache_dir) && remove_dir_recursive(cache_dir) < 0)
1824 g_warning("can't remove directory '%s'\n", cache_dir);
1826 folder_item_remove(item);
1831 static GSList *imap_get_uncached_messages(IMAPSession *session,
1833 MsgNumberList *numlist)
1836 GSList *newlist = NULL;
1837 GSList *llast = NULL;
1840 GSList *seq_list, *cur;
1843 g_return_val_if_fail(session != NULL, NULL);
1844 g_return_val_if_fail(item != NULL, NULL);
1845 g_return_val_if_fail(item->folder != NULL, NULL);
1846 g_return_val_if_fail(FOLDER_CLASS(item->folder) == &imap_class, NULL);
1848 seq_list = imap_get_seq_set_from_numlist(numlist);
1849 for (cur = seq_list; cur != NULL; cur = g_slist_next(cur)) {
1850 imapset = cur->data;
1852 if (imap_cmd_envelope(session, imapset)
1854 log_warning(_("can't get envelope\n"));
1858 str = g_string_new(NULL);
1861 if ((tmp = sock_getline(SESSION(session)->sock)) == NULL) {
1862 log_warning(_("error occurred while getting envelope.\n"));
1863 g_string_free(str, TRUE);
1867 if (tmp[0] != '*' || tmp[1] != ' ') {
1868 log_print("IMAP4< %s\n", tmp);
1872 if (strstr(tmp, "FETCH") == NULL) {
1873 log_print("IMAP4< %s\n", tmp);
1877 log_print("IMAP4< %s\n", tmp);
1878 g_string_assign(str, tmp);
1881 msginfo = imap_parse_envelope
1882 (SESSION(session)->sock, item, str);
1884 log_warning(_("can't parse envelope: %s\n"), str->str);
1887 if (item->stype == F_QUEUE) {
1888 MSG_SET_TMP_FLAGS(msginfo->flags, MSG_QUEUED);
1889 } else if (item->stype == F_DRAFT) {
1890 MSG_SET_TMP_FLAGS(msginfo->flags, MSG_DRAFT);
1893 msginfo->folder = item;
1896 llast = newlist = g_slist_append(newlist, msginfo);
1898 llast = g_slist_append(llast, msginfo);
1899 llast = llast->next;
1903 g_string_free(str, TRUE);
1905 imap_seq_set_free(seq_list);
1907 session_set_access_time(SESSION(session));
1912 static void imap_delete_all_cached_messages(FolderItem *item)
1916 g_return_if_fail(item != NULL);
1917 g_return_if_fail(item->folder != NULL);
1918 g_return_if_fail(FOLDER_CLASS(item->folder) == &imap_class);
1920 debug_print("Deleting all cached messages...\n");
1922 dir = folder_item_get_path(item);
1923 if (is_dir_exist(dir))
1924 remove_all_numbered_files(dir);
1927 debug_print("done.\n");
1931 static SockInfo *imap_open_tunnel(const gchar *server,
1932 const gchar *tunnelcmd,
1935 static SockInfo *imap_open_tunnel(const gchar *server,
1936 const gchar *tunnelcmd)
1941 if ((sock = sock_connect_cmd(server, tunnelcmd)) == NULL) {
1942 log_warning(_("Can't establish IMAP4 session with: %s\n"),
1947 return imap_init_sock(sock, ssl_type);
1949 return imap_init_sock(sock);
1955 static SockInfo *imap_open(const gchar *server, gushort port,
1958 static SockInfo *imap_open(const gchar *server, gushort port)
1963 if ((sock = sock_connect(server, port)) == NULL) {
1964 log_warning(_("Can't connect to IMAP4 server: %s:%d\n"),
1970 if (ssl_type == SSL_TUNNEL && !ssl_init_socket(sock)) {
1971 log_warning(_("Can't establish IMAP4 session with: %s:%d\n"),
1981 static SockInfo *imap_init_sock(SockInfo *sock, SSLType ssl_type)
1983 static SockInfo *imap_init_sock(SockInfo *sock)
1990 static GList *imap_parse_namespace_str(gchar *str)
1995 IMAPNameSpace *namespace;
1996 GList *ns_list = NULL;
1998 while (*p != '\0') {
1999 /* parse ("#foo" "/") */
2001 while (*p && *p != '(') p++;
2002 if (*p == '\0') break;
2005 while (*p && *p != '"') p++;
2006 if (*p == '\0') break;
2010 while (*p && *p != '"') p++;
2011 if (*p == '\0') break;
2015 while (*p && isspace(*p)) p++;
2016 if (*p == '\0') break;
2017 if (strncmp(p, "NIL", 3) == 0)
2019 else if (*p == '"') {
2022 while (*p && *p != '"') p++;
2023 if (*p == '\0') break;
2028 while (*p && *p != ')') p++;
2029 if (*p == '\0') break;
2032 namespace = g_new(IMAPNameSpace, 1);
2033 namespace->name = g_strdup(name);
2034 namespace->separator = separator ? separator[0] : '\0';
2035 ns_list = g_list_append(ns_list, namespace);
2041 static void imap_parse_namespace(IMAPSession *session, IMAPFolder *folder)
2046 g_return_if_fail(session != NULL);
2047 g_return_if_fail(folder != NULL);
2049 if (folder->ns_personal != NULL ||
2050 folder->ns_others != NULL ||
2051 folder->ns_shared != NULL)
2054 if (!imap_has_capability(session, "NAMESPACE")) {
2055 imap_get_namespace_by_list(session, folder);
2059 if (imap_cmd_namespace(session, &ns_str)
2061 log_warning(_("can't get namespace\n"));
2065 str_array = strsplit_parenthesis(ns_str, '(', ')', 3);
2066 if (str_array == NULL) {
2068 imap_get_namespace_by_list(session, folder);
2072 folder->ns_personal = imap_parse_namespace_str(str_array[0]);
2073 if (str_array[0] && str_array[1])
2074 folder->ns_others = imap_parse_namespace_str(str_array[1]);
2075 if (str_array[0] && str_array[1] && str_array[2])
2076 folder->ns_shared = imap_parse_namespace_str(str_array[2]);
2077 g_strfreev(str_array);
2081 static void imap_get_namespace_by_list(IMAPSession *session, IMAPFolder *folder)
2083 GSList *item_list, *cur;
2084 gchar separator = '\0';
2085 IMAPNameSpace *namespace;
2087 g_return_if_fail(session != NULL);
2088 g_return_if_fail(folder != NULL);
2090 if (folder->ns_personal != NULL ||
2091 folder->ns_others != NULL ||
2092 folder->ns_shared != NULL)
2095 imap_gen_send(session, "LIST \"\" \"\"");
2096 item_list = imap_parse_list(folder, session, "", &separator);
2097 for (cur = item_list; cur != NULL; cur = cur->next)
2098 folder_item_destroy(FOLDER_ITEM(cur->data));
2099 g_slist_free(item_list);
2101 namespace = g_new(IMAPNameSpace, 1);
2102 namespace->name = g_strdup("");
2103 namespace->separator = separator;
2104 folder->ns_personal = g_list_append(NULL, namespace);
2107 static IMAPNameSpace *imap_find_namespace_from_list(GList *ns_list,
2110 IMAPNameSpace *namespace = NULL;
2111 gchar *tmp_path, *name;
2113 if (!path) path = "";
2115 for (; ns_list != NULL; ns_list = ns_list->next) {
2116 IMAPNameSpace *tmp_ns = ns_list->data;
2118 Xstrcat_a(tmp_path, path, "/", return namespace);
2119 Xstrdup_a(name, tmp_ns->name, return namespace);
2120 if (tmp_ns->separator && tmp_ns->separator != '/') {
2121 subst_char(tmp_path, tmp_ns->separator, '/');
2122 subst_char(name, tmp_ns->separator, '/');
2124 if (strncmp(tmp_path, name, strlen(name)) == 0)
2131 static IMAPNameSpace *imap_find_namespace(IMAPFolder *folder,
2134 IMAPNameSpace *namespace;
2136 g_return_val_if_fail(folder != NULL, NULL);
2138 namespace = imap_find_namespace_from_list(folder->ns_personal, path);
2139 if (namespace) return namespace;
2140 namespace = imap_find_namespace_from_list(folder->ns_others, path);
2141 if (namespace) return namespace;
2142 namespace = imap_find_namespace_from_list(folder->ns_shared, path);
2143 if (namespace) return namespace;
2148 static gchar imap_get_path_separator(IMAPFolder *folder, const gchar *path)
2150 IMAPNameSpace *namespace;
2151 gchar separator = '/';
2153 namespace = imap_find_namespace(folder, path);
2154 if (namespace && namespace->separator)
2155 separator = namespace->separator;
2160 static gchar *imap_get_real_path(IMAPFolder *folder, const gchar *path)
2165 g_return_val_if_fail(folder != NULL, NULL);
2166 g_return_val_if_fail(path != NULL, NULL);
2168 real_path = imap_utf8_to_modified_utf7(path);
2169 separator = imap_get_path_separator(folder, path);
2170 imap_path_separator_subst(real_path, separator);
2175 static gchar *imap_parse_atom(SockInfo *sock, gchar *src,
2176 gchar *dest, gint dest_len, GString *str)
2178 gchar *cur_pos = src;
2181 g_return_val_if_fail(str != NULL, cur_pos);
2183 /* read the next line if the current response buffer is empty */
2184 while (isspace(*(guchar *)cur_pos)) cur_pos++;
2185 while (*cur_pos == '\0') {
2186 if ((nextline = sock_getline(sock)) == NULL)
2188 g_string_assign(str, nextline);
2190 strretchomp(nextline);
2191 /* log_print("IMAP4< %s\n", nextline); */
2192 debug_print("IMAP4< %s\n", nextline);
2195 while (isspace(*(guchar *)cur_pos)) cur_pos++;
2198 if (!strncmp(cur_pos, "NIL", 3)) {
2201 } else if (*cur_pos == '\"') {
2204 p = get_quoted(cur_pos, '\"', dest, dest_len);
2205 cur_pos = p ? p : cur_pos + 2;
2206 } else if (*cur_pos == '{') {
2211 cur_pos = strchr_cpy(cur_pos + 1, '}', buf, sizeof(buf));
2213 g_return_val_if_fail(len >= 0, cur_pos);
2215 g_string_truncate(str, 0);
2219 if ((nextline = sock_getline(sock)) == NULL)
2221 line_len += strlen(nextline);
2222 g_string_append(str, nextline);
2224 strretchomp(nextline);
2225 /* log_print("IMAP4< %s\n", nextline); */
2226 debug_print("IMAP4< %s\n", nextline);
2228 } while (line_len < len);
2230 memcpy(dest, cur_pos, MIN(len, dest_len - 1));
2231 dest[MIN(len, dest_len - 1)] = '\0';
2238 static gchar *imap_get_header(SockInfo *sock, gchar *cur_pos, gchar **headers,
2248 g_return_val_if_fail(str != NULL, cur_pos);
2250 while (isspace(*(guchar *)cur_pos)) cur_pos++;
2252 g_return_val_if_fail(*cur_pos == '{', cur_pos);
2254 cur_pos = strchr_cpy(cur_pos + 1, '}', buf, sizeof(buf));
2256 g_return_val_if_fail(len >= 0, cur_pos);
2258 g_string_truncate(str, 0);
2262 if ((nextline = sock_getline(sock)) == NULL)
2264 block_len += strlen(nextline);
2265 g_string_append(str, nextline);
2267 strretchomp(nextline);
2268 /* debug_print("IMAP4< %s\n", nextline); */
2270 } while (block_len < len);
2272 debug_print("IMAP4< [contents of BODY.PEEK[HEADER.FIELDS (...)]]\n");
2274 *headers = g_strndup(cur_pos, len);
2277 while (isspace(*(guchar *)cur_pos)) cur_pos++;
2278 while (*cur_pos == '\0') {
2279 if ((nextline = sock_getline(sock)) == NULL)
2281 g_string_assign(str, nextline);
2283 strretchomp(nextline);
2284 debug_print("IMAP4< %s\n", nextline);
2287 while (isspace(*(guchar *)cur_pos)) cur_pos++;
2293 static MsgFlags imap_parse_flags(const gchar *flag_str)
2295 const gchar *p = flag_str;
2296 MsgFlags flags = {0, 0};
2298 flags.perm_flags = MSG_UNREAD;
2300 while ((p = strchr(p, '\\')) != NULL) {
2303 if (g_ascii_strncasecmp(p, "Recent", 6) == 0 && MSG_IS_UNREAD(flags)) {
2304 MSG_SET_PERM_FLAGS(flags, MSG_NEW);
2305 } else if (g_ascii_strncasecmp(p, "Seen", 4) == 0) {
2306 MSG_UNSET_PERM_FLAGS(flags, MSG_NEW|MSG_UNREAD);
2307 } else if (g_ascii_strncasecmp(p, "Deleted", 7) == 0) {
2308 MSG_SET_PERM_FLAGS(flags, MSG_DELETED);
2309 } else if (g_ascii_strncasecmp(p, "Flagged", 7) == 0) {
2310 MSG_SET_PERM_FLAGS(flags, MSG_MARKED);
2311 } else if (g_ascii_strncasecmp(p, "Answered", 8) == 0) {
2312 MSG_SET_PERM_FLAGS(flags, MSG_REPLIED);
2319 static MsgInfo *imap_parse_envelope(SockInfo *sock, FolderItem *item,
2322 gchar buf[IMAPBUFSIZE];
2323 MsgInfo *msginfo = NULL;
2328 MsgFlags flags = {0, 0}, imap_flags = {0, 0};
2330 g_return_val_if_fail(line_str != NULL, NULL);
2331 g_return_val_if_fail(line_str->str[0] == '*' &&
2332 line_str->str[1] == ' ', NULL);
2334 MSG_SET_TMP_FLAGS(flags, MSG_IMAP);
2335 if (item->stype == F_QUEUE) {
2336 MSG_SET_TMP_FLAGS(flags, MSG_QUEUED);
2337 } else if (item->stype == F_DRAFT) {
2338 MSG_SET_TMP_FLAGS(flags, MSG_DRAFT);
2341 cur_pos = line_str->str + 2;
2343 #define PARSE_ONE_ELEMENT(ch) \
2345 cur_pos = strchr_cpy(cur_pos, ch, buf, sizeof(buf)); \
2346 if (cur_pos == NULL) { \
2347 g_warning("cur_pos == NULL\n"); \
2348 procmsg_msginfo_free(msginfo); \
2353 PARSE_ONE_ELEMENT(' ');
2356 PARSE_ONE_ELEMENT(' ');
2357 g_return_val_if_fail(!strcmp(buf, "FETCH"), NULL);
2359 g_return_val_if_fail(*cur_pos == '(', NULL);
2362 while (*cur_pos != '\0' && *cur_pos != ')') {
2363 while (*cur_pos == ' ') cur_pos++;
2365 if (!strncmp(cur_pos, "UID ", 4)) {
2367 uid = strtoul(cur_pos, &cur_pos, 10);
2368 } else if (!strncmp(cur_pos, "FLAGS ", 6)) {
2370 if (*cur_pos != '(') {
2371 g_warning("*cur_pos != '('\n");
2372 procmsg_msginfo_free(msginfo);
2376 PARSE_ONE_ELEMENT(')');
2377 imap_flags = imap_parse_flags(buf);
2378 } else if (!strncmp(cur_pos, "RFC822.SIZE ", 12)) {
2380 size = strtol(cur_pos, &cur_pos, 10);
2381 } else if (!strncmp(cur_pos, "BODY[HEADER.FIELDS ", 19)) {
2385 if (*cur_pos != '(') {
2386 g_warning("*cur_pos != '('\n");
2387 procmsg_msginfo_free(msginfo);
2391 PARSE_ONE_ELEMENT(')');
2392 if (*cur_pos != ']') {
2393 g_warning("*cur_pos != ']'\n");
2394 procmsg_msginfo_free(msginfo);
2399 cur_pos = imap_get_header(sock, cur_pos, &headers,
2401 msginfo = procheader_parse_str(headers, flags, FALSE, FALSE);
2404 g_warning("invalid FETCH response: %s\n", cur_pos);
2410 msginfo->msgnum = uid;
2411 msginfo->size = size;
2412 msginfo->flags.tmp_flags |= imap_flags.tmp_flags;
2413 msginfo->flags.perm_flags = imap_flags.perm_flags;
2419 static gchar *imap_get_flag_str(IMAPFlags flags)
2424 str = g_string_new(NULL);
2426 if (IMAP_IS_SEEN(flags)) g_string_append(str, "\\Seen ");
2427 if (IMAP_IS_ANSWERED(flags)) g_string_append(str, "\\Answered ");
2428 if (IMAP_IS_FLAGGED(flags)) g_string_append(str, "\\Flagged ");
2429 if (IMAP_IS_DELETED(flags)) g_string_append(str, "\\Deleted ");
2430 if (IMAP_IS_DRAFT(flags)) g_string_append(str, "\\Draft");
2432 if (str->len > 0 && str->str[str->len - 1] == ' ')
2433 g_string_truncate(str, str->len - 1);
2436 g_string_free(str, FALSE);
2441 static gint imap_set_message_flags(IMAPSession *session,
2442 MsgNumberList *numlist,
2449 GSList *seq_list, *cur;
2452 flag_str = imap_get_flag_str(flags);
2453 cmd = g_strconcat(is_set ? "+FLAGS.SILENT (" : "-FLAGS.SILENT (",
2454 flag_str, ")", NULL);
2457 seq_list = imap_get_seq_set_from_numlist(numlist);
2458 for (cur = seq_list; cur != NULL; cur = g_slist_next(cur)) {
2459 imapset = cur->data;
2461 ok = imap_cmd_store(session, imapset, cmd);
2463 imap_seq_set_free(seq_list);
2469 static gint imap_select(IMAPSession *session, IMAPFolder *folder,
2471 gint *exists, gint *recent, gint *unseen,
2472 guint32 *uid_validity)
2476 gint exists_, recent_, unseen_;
2477 guint32 uid_validity_;
2479 if (!exists || !recent || !unseen || !uid_validity) {
2480 if (session->mbox && strcmp(session->mbox, path) == 0)
2481 return IMAP_SUCCESS;
2485 uid_validity = &uid_validity_;
2488 g_free(session->mbox);
2489 session->mbox = NULL;
2491 real_path = imap_get_real_path(folder, path);
2492 ok = imap_cmd_select(session, real_path,
2493 exists, recent, unseen, uid_validity);
2494 if (ok != IMAP_SUCCESS)
2495 log_warning(_("can't select folder: %s\n"), real_path);
2497 session->mbox = g_strdup(path);
2498 session->folder_content_changed = FALSE;
2505 #define THROW(err) { ok = err; goto catch; }
2507 static gint imap_status(IMAPSession *session, IMAPFolder *folder,
2509 gint *messages, gint *recent,
2510 guint32 *uid_next, guint32 *uid_validity,
2516 GPtrArray *argbuf = NULL;
2519 if (messages && recent && uid_next && uid_validity && unseen) {
2520 *messages = *recent = *uid_next = *uid_validity = *unseen = 0;
2521 argbuf = g_ptr_array_new();
2524 real_path = imap_get_real_path(folder, path);
2525 QUOTE_IF_REQUIRED(real_path_, real_path);
2526 imap_gen_send(session, "STATUS %s "
2527 "(MESSAGES RECENT UIDNEXT UIDVALIDITY UNSEEN)",
2530 ok = imap_cmd_ok(session, argbuf);
2531 if (ok != IMAP_SUCCESS || !argbuf) THROW(ok);
2533 str = search_array_str(argbuf, "STATUS");
2534 if (!str) THROW(IMAP_ERROR);
2536 str = strchr(str, '(');
2537 if (!str) THROW(IMAP_ERROR);
2539 while (*str != '\0' && *str != ')') {
2540 while (*str == ' ') str++;
2542 if (!strncmp(str, "MESSAGES ", 9)) {
2544 *messages = strtol(str, &str, 10);
2545 } else if (!strncmp(str, "RECENT ", 7)) {
2547 *recent = strtol(str, &str, 10);
2548 } else if (!strncmp(str, "UIDNEXT ", 8)) {
2550 *uid_next = strtoul(str, &str, 10);
2551 } else if (!strncmp(str, "UIDVALIDITY ", 12)) {
2553 *uid_validity = strtoul(str, &str, 10);
2554 } else if (!strncmp(str, "UNSEEN ", 7)) {
2556 *unseen = strtol(str, &str, 10);
2558 g_warning("invalid STATUS response: %s\n", str);
2566 ptr_array_free_strings(argbuf);
2567 g_ptr_array_free(argbuf, TRUE);
2575 static gboolean imap_has_capability(IMAPSession *session, const gchar *cap)
2579 for (p = session->capability; *p != NULL; ++p) {
2580 if (!g_ascii_strcasecmp(*p, cap))
2587 static void imap_free_capabilities(IMAPSession *session)
2589 g_strfreev(session->capability);
2590 session->capability = NULL;
2593 /* low-level IMAP4rev1 commands */
2595 static gint imap_cmd_authenticate(IMAPSession *session, const gchar *user,
2596 const gchar *pass, IMAPAuthType type)
2603 gchar hexdigest[33];
2607 auth_type = "CRAM-MD5";
2609 imap_gen_send(session, "AUTHENTICATE %s", auth_type);
2610 ok = imap_gen_recv(session, &buf);
2611 if (ok != IMAP_SUCCESS || buf[0] != '+' || buf[1] != ' ') {
2616 challenge = g_malloc(strlen(buf + 2) + 1);
2617 challenge_len = base64_decode(challenge, buf + 2, -1);
2618 challenge[challenge_len] = '\0';
2620 log_print("IMAP< [Decoded: %s]\n", challenge);
2622 md5_hex_hmac(hexdigest, challenge, challenge_len, pass, strlen(pass));
2625 response = g_strdup_printf("%s %s", user, hexdigest);
2626 log_print("IMAP> [Encoded: %s]\n", response);
2627 response64 = g_malloc((strlen(response) + 3) * 2 + 1);
2628 base64_encode(response64, response, strlen(response));
2631 log_print("IMAP> %s\n", response64);
2632 sock_puts(SESSION(session)->sock, response64);
2633 ok = imap_cmd_ok(session, NULL);
2634 if (ok != IMAP_SUCCESS)
2635 log_warning(_("IMAP4 authentication failed.\n"));
2640 static gint imap_cmd_login(IMAPSession *session,
2641 const gchar *user, const gchar *pass)
2643 gchar *user_, *pass_;
2646 QUOTE_IF_REQUIRED(user_, user);
2647 QUOTE_IF_REQUIRED(pass_, pass);
2648 imap_gen_send(session, "LOGIN %s %s", user_, pass_);
2650 ok = imap_cmd_ok(session, NULL);
2651 if (ok != IMAP_SUCCESS)
2652 log_warning(_("IMAP4 login failed.\n"));
2657 static gint imap_cmd_logout(IMAPSession *session)
2659 imap_gen_send(session, "LOGOUT");
2660 return imap_cmd_ok(session, NULL);
2663 static gint imap_cmd_noop(IMAPSession *session)
2665 imap_gen_send(session, "NOOP");
2666 return imap_cmd_ok(session, NULL);
2670 static gint imap_cmd_starttls(IMAPSession *session)
2672 imap_gen_send(session, "STARTTLS");
2673 return imap_cmd_ok(session, NULL);
2677 #define THROW(err) { ok = err; goto catch; }
2679 static gint imap_cmd_namespace(IMAPSession *session, gchar **ns_str)
2685 argbuf = g_ptr_array_new();
2687 imap_gen_send(session, "NAMESPACE");
2688 if ((ok = imap_cmd_ok(session, argbuf)) != IMAP_SUCCESS) THROW(ok);
2690 str = search_array_str(argbuf, "NAMESPACE");
2691 if (!str) THROW(IMAP_ERROR);
2693 *ns_str = g_strdup(str);
2696 ptr_array_free_strings(argbuf);
2697 g_ptr_array_free(argbuf, TRUE);
2704 static gint imap_cmd_list(IMAPSession *session, const gchar *ref,
2705 const gchar *mailbox, GPtrArray *argbuf)
2707 gchar *ref_, *mailbox_;
2709 if (!ref) ref = "\"\"";
2710 if (!mailbox) mailbox = "\"\"";
2712 QUOTE_IF_REQUIRED(ref_, ref);
2713 QUOTE_IF_REQUIRED(mailbox_, mailbox);
2714 imap_gen_send(session, "LIST %s %s", ref_, mailbox_);
2716 return imap_cmd_ok(session, argbuf);
2719 #define THROW goto catch
2721 static gint imap_cmd_do_select(IMAPSession *session, const gchar *folder,
2723 gint *exists, gint *recent, gint *unseen,
2724 guint32 *uid_validity)
2731 unsigned int uid_validity_;
2733 *exists = *recent = *unseen = *uid_validity = 0;
2734 argbuf = g_ptr_array_new();
2737 select_cmd = "EXAMINE";
2739 select_cmd = "SELECT";
2741 QUOTE_IF_REQUIRED(folder_, folder);
2742 imap_gen_send(session, "%s %s", select_cmd, folder_);
2744 if ((ok = imap_cmd_ok(session, argbuf)) != IMAP_SUCCESS) THROW;
2746 resp_str = search_array_contain_str(argbuf, "EXISTS");
2748 if (sscanf(resp_str,"%d EXISTS", exists) != 1) {
2749 g_warning("imap_cmd_select(): invalid EXISTS line.\n");
2754 resp_str = search_array_contain_str(argbuf, "RECENT");
2756 if (sscanf(resp_str, "%d RECENT", recent) != 1) {
2757 g_warning("imap_cmd_select(): invalid RECENT line.\n");
2762 resp_str = search_array_contain_str(argbuf, "UIDVALIDITY");
2764 if (sscanf(resp_str, "OK [UIDVALIDITY %u] ", &uid_validity_)
2766 g_warning("imap_cmd_select(): invalid UIDVALIDITY line.\n");
2769 *uid_validity = uid_validity_;
2772 resp_str = search_array_contain_str(argbuf, "UNSEEN");
2774 if (sscanf(resp_str, "OK [UNSEEN %d] ", unseen) != 1) {
2775 g_warning("imap_cmd_select(): invalid UNSEEN line.\n");
2781 ptr_array_free_strings(argbuf);
2782 g_ptr_array_free(argbuf, TRUE);
2787 static gint imap_cmd_select(IMAPSession *session, const gchar *folder,
2788 gint *exists, gint *recent, gint *unseen,
2789 guint32 *uid_validity)
2791 return imap_cmd_do_select(session, folder, FALSE,
2792 exists, recent, unseen, uid_validity);
2795 static gint imap_cmd_examine(IMAPSession *session, const gchar *folder,
2796 gint *exists, gint *recent, gint *unseen,
2797 guint32 *uid_validity)
2799 return imap_cmd_do_select(session, folder, TRUE,
2800 exists, recent, unseen, uid_validity);
2805 static gint imap_cmd_create(IMAPSession *session, const gchar *folder)
2809 QUOTE_IF_REQUIRED(folder_, folder);
2810 imap_gen_send(session, "CREATE %s", folder_);
2812 return imap_cmd_ok(session, NULL);
2815 static gint imap_cmd_rename(IMAPSession *session, const gchar *old_folder,
2816 const gchar *new_folder)
2818 gchar *old_folder_, *new_folder_;
2820 QUOTE_IF_REQUIRED(old_folder_, old_folder);
2821 QUOTE_IF_REQUIRED(new_folder_, new_folder);
2822 imap_gen_send(session, "RENAME %s %s", old_folder_, new_folder_);
2824 return imap_cmd_ok(session, NULL);
2827 static gint imap_cmd_delete(IMAPSession *session, const gchar *folder)
2831 QUOTE_IF_REQUIRED(folder_, folder);
2832 imap_gen_send(session, "DELETE %s", folder_);
2834 return imap_cmd_ok(session, NULL);
2837 static gint imap_cmd_search(IMAPSession *session, const gchar *criteria,
2844 g_return_val_if_fail(criteria != NULL, IMAP_ERROR);
2845 g_return_val_if_fail(list != NULL, IMAP_ERROR);
2849 argbuf = g_ptr_array_new();
2850 imap_gen_send(session, "UID SEARCH %s", criteria);
2852 ok = imap_cmd_ok(session, argbuf);
2853 if (ok != IMAP_SUCCESS) {
2854 ptr_array_free_strings(argbuf);
2855 g_ptr_array_free(argbuf, TRUE);
2859 if ((uidlist = search_array_str(argbuf, "SEARCH ")) != NULL) {
2860 gchar **strlist, **p;
2862 strlist = g_strsplit(uidlist + 7, " ", 0);
2863 for (p = strlist; *p != NULL; ++p) {
2866 if (sscanf(*p, "%u", &msgnum) == 1)
2867 *list = g_slist_append(*list, GINT_TO_POINTER(msgnum));
2869 g_strfreev(strlist);
2871 ptr_array_free_strings(argbuf);
2872 g_ptr_array_free(argbuf, TRUE);
2874 return IMAP_SUCCESS;
2877 static gint imap_cmd_fetch(IMAPSession *session, guint32 uid,
2878 const gchar *filename)
2886 g_return_val_if_fail(filename != NULL, IMAP_ERROR);
2888 imap_gen_send(session, "UID FETCH %d BODY.PEEK[]", uid);
2890 while ((ok = imap_gen_recv(session, &buf)) == IMAP_SUCCESS) {
2891 if (buf[0] != '*' || buf[1] != ' ') {
2895 if (strstr(buf, "FETCH") != NULL) break;
2898 if (ok != IMAP_SUCCESS) {
2903 #define RETURN_ERROR_IF_FAIL(cond) \
2906 return IMAP_ERROR; \
2909 cur_pos = strchr(buf, '{');
2910 RETURN_ERROR_IF_FAIL(cur_pos != NULL);
2911 cur_pos = strchr_cpy(cur_pos + 1, '}', size_str, sizeof(size_str));
2912 RETURN_ERROR_IF_FAIL(cur_pos != NULL);
2913 size_num = atol(size_str);
2914 RETURN_ERROR_IF_FAIL(size_num >= 0);
2916 RETURN_ERROR_IF_FAIL(*cur_pos == '\0');
2918 #undef RETURN_ERROR_IF_FAIL
2922 if (recv_bytes_write_to_file(SESSION(session)->sock,
2923 size_num, filename) != 0)
2926 if (imap_gen_recv(session, &buf) != IMAP_SUCCESS) {
2931 if (buf[0] == '\0' || buf[strlen(buf) - 1] != ')') {
2937 ok = imap_cmd_ok(session, NULL);
2942 static gint imap_cmd_append(IMAPSession *session, const gchar *destfolder,
2943 const gchar *file, IMAPFlags flags,
2950 unsigned int new_uid_;
2952 gchar buf[BUFFSIZE];
2957 g_return_val_if_fail(file != NULL, IMAP_ERROR);
2959 size = get_file_size_as_crlf(file);
2960 if ((fp = fopen(file, "rb")) == NULL) {
2961 FILE_OP_ERROR(file, "fopen");
2964 QUOTE_IF_REQUIRED(destfolder_, destfolder);
2965 flag_str = imap_get_flag_str(flags);
2966 imap_gen_send(session, "APPEND %s (%s) {%d}",
2967 destfolder_, flag_str, size);
2970 ok = imap_gen_recv(session, &ret);
2971 if (ok != IMAP_SUCCESS || ret[0] != '+' || ret[1] != ' ') {
2972 log_warning(_("can't append %s to %s\n"), file, destfolder_);
2979 log_print("IMAP4> %s\n", "(sending file...)");
2981 while (fgets(buf, sizeof(buf), fp) != NULL) {
2983 if (sock_puts(SESSION(session)->sock, buf) < 0) {
2990 FILE_OP_ERROR(file, "fgets");
2995 sock_puts(SESSION(session)->sock, "");
2999 if (new_uid != NULL)
3002 if (new_uid != NULL && session->uidplus) {
3003 argbuf = g_ptr_array_new();
3005 ok = imap_cmd_ok(session, argbuf);
3006 if ((ok == IMAP_SUCCESS) && (argbuf->len > 0)) {
3007 resp_str = g_ptr_array_index(argbuf, argbuf->len - 1);
3009 sscanf(resp_str, "%*u OK [APPENDUID %*u %u]",
3011 *new_uid = new_uid_;
3015 ptr_array_free_strings(argbuf);
3016 g_ptr_array_free(argbuf, TRUE);
3018 ok = imap_cmd_ok(session, NULL);
3020 if (ok != IMAP_SUCCESS)
3021 log_warning(_("can't append message to %s\n"),
3027 static MsgNumberList *imapset_to_numlist(IMAPSet imapset)
3029 gchar **ranges, **range;
3030 unsigned int low, high;
3031 MsgNumberList *uids = NULL;
3033 ranges = g_strsplit(imapset, ",", 0);
3034 for (range = ranges; *range != NULL; range++) {
3035 printf("%s\n", *range);
3036 if (sscanf(*range, "%u:%u", &low, &high) == 1)
3037 uids = g_slist_prepend(uids, GINT_TO_POINTER(low));
3040 for (i = low; i <= high; i++)
3041 uids = g_slist_prepend(uids, GINT_TO_POINTER(i));
3044 uids = g_slist_reverse(uids);
3050 static gint imap_cmd_copy(IMAPSession *session, const gchar *seq_set,
3051 const gchar *destfolder, GRelation *uid_mapping)
3056 g_return_val_if_fail(session != NULL, IMAP_ERROR);
3057 g_return_val_if_fail(seq_set != NULL, IMAP_ERROR);
3058 g_return_val_if_fail(destfolder != NULL, IMAP_ERROR);
3060 QUOTE_IF_REQUIRED(destfolder_, destfolder);
3061 imap_gen_send(session, "UID COPY %s %s", seq_set, destfolder_);
3063 if (uid_mapping != NULL && session->uidplus) {
3065 gchar *resp_str = NULL, *olduids_str, *newuids_str;
3066 MsgNumberList *olduids, *old_cur, *newuids, *new_cur;
3068 reply = g_ptr_array_new();
3069 ok = imap_cmd_ok(session, reply);
3070 if ((ok == IMAP_SUCCESS) && (reply->len > 0)) {
3071 resp_str = g_ptr_array_index(reply, reply->len - 1);
3073 olduids_str = g_new0(gchar, strlen(resp_str));
3074 newuids_str = g_new0(gchar, strlen(resp_str));
3075 if (sscanf(resp_str, "%*s OK [COPYUID %*u %[0-9,:] %[0-9,:]]",
3076 olduids_str, newuids_str) == 2) {
3077 olduids = imapset_to_numlist(olduids_str);
3078 newuids = imapset_to_numlist(newuids_str);
3082 while(old_cur != NULL && new_cur != NULL) {
3083 g_relation_insert(uid_mapping,
3084 GPOINTER_TO_INT(old_cur->data),
3085 GPOINTER_TO_INT(new_cur->data));
3086 old_cur = g_slist_next(old_cur);
3087 new_cur = g_slist_next(new_cur);
3090 g_slist_free(olduids);
3091 g_slist_free(newuids);
3093 g_free(olduids_str);
3094 g_free(newuids_str);
3097 ptr_array_free_strings(reply);
3098 g_ptr_array_free(reply, TRUE);
3100 ok = imap_cmd_ok(session, NULL);
3102 if (ok != IMAP_SUCCESS)
3103 log_warning(_("can't copy %s to %s\n"), seq_set, destfolder_);
3108 gint imap_cmd_envelope(IMAPSession *session, IMAPSet set)
3110 static GString *header_fields = NULL;
3112 if (header_fields == NULL) {
3113 const HeaderEntry *headers, *elem;
3115 headers = procheader_get_headernames(FALSE);
3116 header_fields = g_string_new("");
3118 for (elem = headers; elem->name != NULL; ++elem) {
3119 gint namelen = strlen(elem->name);
3121 /* Header fields ending with space are not rfc822 headers */
3122 if (elem->name[namelen - 1] == ' ')
3125 /* strip : at the of header field */
3126 if(elem->name[namelen - 1] == ':')
3132 g_string_append_printf(header_fields, "%s%.*s",
3133 header_fields->str[0] != '\0' ? " " : "",
3134 namelen, elem->name);
3139 (session, "UID FETCH %s (UID FLAGS RFC822.SIZE BODY.PEEK[HEADER.FIELDS (%s)])",
3140 set, header_fields->str);
3142 return IMAP_SUCCESS;
3145 static gint imap_cmd_store(IMAPSession *session, IMAPSet seq_set,
3150 imap_gen_send(session, "UID STORE %s %s", seq_set, sub_cmd);
3152 if ((ok = imap_cmd_ok(session, NULL)) != IMAP_SUCCESS) {
3153 log_warning(_("error while imap command: STORE %s %s\n"),
3158 return IMAP_SUCCESS;
3161 static gint imap_cmd_expunge(IMAPSession *session, IMAPSet seq_set)
3165 if (seq_set && session->uidplus)
3166 imap_gen_send(session, "UID EXPUNGE %s", seq_set);
3168 imap_gen_send(session, "EXPUNGE");
3169 if ((ok = imap_cmd_ok(session, NULL)) != IMAP_SUCCESS) {
3170 log_warning(_("error while imap command: EXPUNGE\n"));
3174 return IMAP_SUCCESS;
3177 static gint imap_cmd_close(IMAPSession *session)
3181 imap_gen_send(session, "CLOSE");
3182 if ((ok = imap_cmd_ok(session, NULL)) != IMAP_SUCCESS)
3183 log_warning(_("error while imap command: CLOSE\n"));
3188 static gint imap_cmd_ok(IMAPSession *session, GPtrArray *argbuf)
3190 gint ok = IMAP_SUCCESS;
3195 while ((ok = imap_gen_recv(session, &buf))
3197 /* make sure data is long enough for any substring of buf */
3198 data = alloca(strlen(buf) + 1);
3200 /* untagged line read */
3201 if (buf[0] == '*' && buf[1] == ' ') {
3204 g_ptr_array_add(argbuf, g_strdup(buf + 2));
3206 if (sscanf(buf + 2, "%d %s", &num, data) >= 2) {
3207 if (!strcmp(data, "EXISTS")) {
3208 session->exists = num;
3209 session->folder_content_changed = TRUE;
3212 if(!strcmp(data, "EXPUNGE")) {
3214 session->folder_content_changed = TRUE;
3217 /* tagged line with correct tag and OK response found */
3218 } else if ((sscanf(buf, "%d %s", &cmd_num, data) >= 2) &&
3219 (cmd_num == session->cmd_count) &&
3220 !strcmp(data, "OK")) {
3222 g_ptr_array_add(argbuf, g_strdup(buf));
3224 /* everything else */
3236 static void imap_gen_send(IMAPSession *session, const gchar *format, ...)
3243 va_start(args, format);
3244 tmp = g_strdup_vprintf(format, args);
3247 session->cmd_count++;
3249 buf = g_strdup_printf("%d %s\r\n", session->cmd_count, tmp);
3250 if (!g_ascii_strncasecmp(tmp, "LOGIN ", 6) && (p = strchr(tmp + 6, ' '))) {
3252 log_print("IMAP4> %d %s ********\n", session->cmd_count, tmp);
3254 log_print("IMAP4> %d %s\n", session->cmd_count, tmp);
3256 sock_write_all(SESSION(session)->sock, buf, strlen(buf));
3261 static gint imap_gen_recv(IMAPSession *session, gchar **ret)
3263 if ((*ret = sock_getline(SESSION(session)->sock)) == NULL)
3268 log_print("IMAP4< %s\n", *ret);
3270 session_set_access_time(SESSION(session));
3272 return IMAP_SUCCESS;
3276 /* misc utility functions */
3278 static gchar *strchr_cpy(const gchar *src, gchar ch, gchar *dest, gint len)
3283 tmp = strchr(src, ch);
3287 memcpy(dest, src, MIN(tmp - src, len - 1));
3288 dest[MIN(tmp - src, len - 1)] = '\0';
3293 static gchar *get_quoted(const gchar *src, gchar ch, gchar *dest, gint len)
3295 const gchar *p = src;
3298 g_return_val_if_fail(*p == ch, NULL);
3303 while (*p != '\0' && *p != ch) {
3305 if (*p == '\\' && *(p + 1) != '\0')
3314 return (gchar *)(*p == ch ? p + 1 : p);
3317 static gchar *search_array_contain_str(GPtrArray *array, const gchar *str)
3321 for (i = 0; i < array->len; i++) {
3324 tmp = g_ptr_array_index(array, i);
3325 if (strstr(tmp, str) != NULL)
3332 static gchar *search_array_str(GPtrArray *array, const gchar *str)
3339 for (i = 0; i < array->len; i++) {
3342 tmp = g_ptr_array_index(array, i);
3343 if (!strncmp(tmp, str, len))
3350 static void imap_path_separator_subst(gchar *str, gchar separator)
3353 gboolean in_escape = FALSE;
3355 if (!separator || separator == '/') return;
3357 for (p = str; *p != '\0'; p++) {
3358 if (*p == '/' && !in_escape)
3360 else if (*p == '&' && *(p + 1) != '-' && !in_escape)
3362 else if (*p == '-' && in_escape)
3367 static gchar *imap_modified_utf7_to_utf8(const gchar *mutf7_str)
3369 static iconv_t cd = (iconv_t)-1;
3370 static gboolean iconv_ok = TRUE;
3373 size_t norm_utf7_len;
3375 gchar *to_str, *to_p;
3377 gboolean in_escape = FALSE;
3379 if (!iconv_ok) return g_strdup(mutf7_str);
3381 if (cd == (iconv_t)-1) {
3382 cd = iconv_open(CS_INTERNAL, CS_UTF_7);
3383 if (cd == (iconv_t)-1) {
3384 g_warning("iconv cannot convert UTF-7 to %s\n",
3387 return g_strdup(mutf7_str);
3391 /* modified UTF-7 to normal UTF-7 conversion */
3392 norm_utf7 = g_string_new(NULL);
3394 for (p = mutf7_str; *p != '\0'; p++) {
3395 /* replace: '&' -> '+',
3397 escaped ',' -> '/' */
3398 if (!in_escape && *p == '&') {
3399 if (*(p + 1) != '-') {
3400 g_string_append_c(norm_utf7, '+');
3403 g_string_append_c(norm_utf7, '&');
3406 } else if (in_escape && *p == ',') {
3407 g_string_append_c(norm_utf7, '/');
3408 } else if (in_escape && *p == '-') {
3409 g_string_append_c(norm_utf7, '-');
3412 g_string_append_c(norm_utf7, *p);
3416 norm_utf7_p = norm_utf7->str;
3417 norm_utf7_len = norm_utf7->len;
3418 to_len = strlen(mutf7_str) * 5;
3419 to_p = to_str = g_malloc(to_len + 1);
3421 if (iconv(cd, (ICONV_CONST gchar **)&norm_utf7_p, &norm_utf7_len,
3422 &to_p, &to_len) == -1) {
3423 g_warning(_("iconv cannot convert UTF-7 to %s\n"),
3424 conv_get_locale_charset_str());
3425 g_string_free(norm_utf7, TRUE);
3427 return g_strdup(mutf7_str);
3430 /* second iconv() call for flushing */
3431 iconv(cd, NULL, NULL, &to_p, &to_len);
3432 g_string_free(norm_utf7, TRUE);
3438 static gchar *imap_utf8_to_modified_utf7(const gchar *from)
3440 static iconv_t cd = (iconv_t)-1;
3441 static gboolean iconv_ok = TRUE;
3442 gchar *norm_utf7, *norm_utf7_p;
3443 size_t from_len, norm_utf7_len;
3445 gchar *from_tmp, *to, *p;
3446 gboolean in_escape = FALSE;
3448 if (!iconv_ok) return g_strdup(from);
3450 if (cd == (iconv_t)-1) {
3451 cd = iconv_open(CS_UTF_7, CS_INTERNAL);
3452 if (cd == (iconv_t)-1) {
3453 g_warning(_("iconv cannot convert %s to UTF-7\n"),
3456 return g_strdup(from);
3460 /* UTF-8 to normal UTF-7 conversion */
3461 Xstrdup_a(from_tmp, from, return g_strdup(from));
3462 from_len = strlen(from);
3463 norm_utf7_len = from_len * 5;
3464 Xalloca(norm_utf7, norm_utf7_len + 1, return g_strdup(from));
3465 norm_utf7_p = norm_utf7;
3467 #define IS_PRINT(ch) (isprint(ch) && IS_ASCII(ch))
3469 while (from_len > 0) {
3470 if (*from_tmp == '+') {
3471 *norm_utf7_p++ = '+';
3472 *norm_utf7_p++ = '-';
3476 } else if (IS_PRINT(*(guchar *)from_tmp)) {
3477 /* printable ascii char */
3478 *norm_utf7_p = *from_tmp;
3484 size_t conv_len = 0;
3486 /* unprintable char: convert to UTF-7 */
3488 while (!IS_PRINT(*(guchar *)p) && conv_len < from_len) {
3489 conv_len += g_utf8_skip[*(guchar *)p];
3490 p += g_utf8_skip[*(guchar *)p];
3493 from_len -= conv_len;
3494 if (iconv(cd, (ICONV_CONST gchar **)&from_tmp,
3496 &norm_utf7_p, &norm_utf7_len) == -1) {
3497 g_warning(_("iconv cannot convert UTF-8 to UTF-7\n"));
3498 return g_strdup(from);
3501 /* second iconv() call for flushing */
3502 iconv(cd, NULL, NULL, &norm_utf7_p, &norm_utf7_len);
3508 *norm_utf7_p = '\0';
3509 to_str = g_string_new(NULL);
3510 for (p = norm_utf7; p < norm_utf7_p; p++) {
3511 /* replace: '&' -> "&-",
3514 BASE64 '/' -> ',' */
3515 if (!in_escape && *p == '&') {
3516 g_string_append(to_str, "&-");
3517 } else if (!in_escape && *p == '+') {
3518 if (*(p + 1) == '-') {
3519 g_string_append_c(to_str, '+');
3522 g_string_append_c(to_str, '&');
3525 } else if (in_escape && *p == '/') {
3526 g_string_append_c(to_str, ',');
3527 } else if (in_escape && *p == '-') {
3528 g_string_append_c(to_str, '-');
3531 g_string_append_c(to_str, *p);
3537 g_string_append_c(to_str, '-');
3541 g_string_free(to_str, FALSE);
3546 static GSList *imap_get_seq_set_from_numlist(MsgNumberList *numlist)
3549 GSList *sorted_list, *cur;
3550 guint first, last, next;
3552 GSList *ret_list = NULL;
3554 if (numlist == NULL)
3557 str = g_string_sized_new(256);
3559 sorted_list = g_slist_copy(numlist);
3560 sorted_list = g_slist_sort(sorted_list, g_int_compare);
3562 first = GPOINTER_TO_INT(sorted_list->data);
3564 for (cur = sorted_list; cur != NULL; cur = g_slist_next(cur)) {
3565 last = GPOINTER_TO_INT(cur->data);
3567 next = GPOINTER_TO_INT(cur->next->data);
3571 if (last + 1 != next || next == 0) {
3573 g_string_append_c(str, ',');
3575 g_string_append_printf(str, "%u", first);
3577 g_string_append_printf(str, "%u:%u", first, last);
3581 if (str->len > IMAP_CMD_LIMIT) {
3582 ret_str = g_strdup(str->str);
3583 ret_list = g_slist_append(ret_list, ret_str);
3584 g_string_truncate(str, 0);
3590 ret_str = g_strdup(str->str);
3591 ret_list = g_slist_append(ret_list, ret_str);
3594 g_slist_free(sorted_list);
3595 g_string_free(str, TRUE);
3600 static GSList *imap_get_seq_set_from_msglist(MsgInfoList *msglist)
3602 MsgNumberList *numlist = NULL;
3606 for (cur = msglist; cur != NULL; cur = g_slist_next(cur)) {
3607 MsgInfo *msginfo = (MsgInfo *) cur->data;
3609 numlist = g_slist_append(numlist, GINT_TO_POINTER(msginfo->msgnum));
3611 seq_list = imap_get_seq_set_from_numlist(numlist);
3612 g_slist_free(numlist);
3617 static void imap_seq_set_free(GSList *seq_list)
3619 slist_free_strings(seq_list);
3620 g_slist_free(seq_list);
3624 static gboolean imap_rename_folder_func(GNode *node, gpointer data)
3626 FolderItem *item = node->data;
3627 gchar **paths = data;
3628 const gchar *oldpath = paths[0];
3629 const gchar *newpath = paths[1];
3631 gchar *new_itempath;
3634 oldpathlen = strlen(oldpath);
3635 if (strncmp(oldpath, item->path, oldpathlen) != 0) {
3636 g_warning("path doesn't match: %s, %s\n", oldpath, item->path);
3640 base = item->path + oldpathlen;
3641 while (*base == G_DIR_SEPARATOR) base++;
3643 new_itempath = g_strdup(newpath);
3645 new_itempath = g_strconcat(newpath, G_DIR_SEPARATOR_S, base,
3648 item->path = new_itempath;
3653 static gint get_list_of_uids(Folder *folder, IMAPFolderItem *item, GSList **msgnum_list)
3655 gint ok, nummsgs = 0, lastuid_old;
3656 IMAPSession *session;
3657 GSList *uidlist, *elem;
3660 session = imap_session_get(folder);
3661 g_return_val_if_fail(session != NULL, -1);
3663 ok = imap_select(session, IMAP_FOLDER(folder), item->item.path,
3664 NULL, NULL, NULL, NULL);
3665 if (ok != IMAP_SUCCESS)
3668 cmd_buf = g_strdup_printf("UID %d:*", item->lastuid + 1);
3669 ok = imap_cmd_search(session, cmd_buf, &uidlist);
3672 if (ok == IMAP_SOCKET) {
3673 session_destroy((Session *)session);
3674 ((RemoteFolder *)folder)->session = NULL;
3678 if (ok != IMAP_SUCCESS) {
3682 argbuf = g_ptr_array_new();
3684 cmd_buf = g_strdup_printf("UID FETCH %d:* (UID)", item->lastuid + 1);
3685 imap_gen_send(session, cmd_buf);
3687 ok = imap_cmd_ok(session, argbuf);
3688 if (ok != IMAP_SUCCESS) {
3689 ptr_array_free_strings(argbuf);
3690 g_ptr_array_free(argbuf, TRUE);
3694 for(i = 0; i < argbuf->len; i++) {
3697 if((ret = sscanf(g_ptr_array_index(argbuf, i),
3698 "%*d FETCH (UID %d)", &msgnum)) == 1)
3699 uidlist = g_slist_prepend(uidlist, GINT_TO_POINTER(msgnum));
3701 ptr_array_free_strings(argbuf);
3702 g_ptr_array_free(argbuf, TRUE);
3705 lastuid_old = item->lastuid;
3706 *msgnum_list = g_slist_copy(item->uid_list);
3707 nummsgs = g_slist_length(*msgnum_list);
3708 debug_print("Got %d uids from cache\n", g_slist_length(item->uid_list));
3710 for (elem = uidlist; elem != NULL; elem = g_slist_next(elem)) {
3713 msgnum = GPOINTER_TO_INT(elem->data);
3714 if (msgnum > lastuid_old) {
3715 *msgnum_list = g_slist_prepend(*msgnum_list, GINT_TO_POINTER(msgnum));
3716 item->uid_list = g_slist_prepend(item->uid_list, GINT_TO_POINTER(msgnum));
3719 if(msgnum > item->lastuid)
3720 item->lastuid = msgnum;
3723 g_slist_free(uidlist);
3728 gint imap_get_num_list(Folder *folder, FolderItem *_item, GSList **msgnum_list, gboolean *old_uids_valid)
3730 IMAPFolderItem *item = (IMAPFolderItem *)_item;
3731 IMAPSession *session;
3732 gint ok, nummsgs = 0, exists, recent, uid_val, uid_next, unseen;
3733 GSList *uidlist = NULL;
3735 gboolean selected_folder;
3737 g_return_val_if_fail(folder != NULL, -1);
3738 g_return_val_if_fail(item != NULL, -1);
3739 g_return_val_if_fail(item->item.path != NULL, -1);
3740 g_return_val_if_fail(FOLDER_CLASS(folder) == &imap_class, -1);
3741 g_return_val_if_fail(folder->account != NULL, -1);
3743 session = imap_session_get(folder);
3744 g_return_val_if_fail(session != NULL, -1);
3746 selected_folder = (session->mbox != NULL) &&
3747 (!strcmp(session->mbox, item->item.path));
3748 if (selected_folder) {
3749 ok = imap_cmd_noop(session);
3750 if (ok != IMAP_SUCCESS)
3752 exists = session->exists;
3754 *old_uids_valid = TRUE;
3756 ok = imap_status(session, IMAP_FOLDER(folder), item->item.path,
3757 &exists, &recent, &uid_next, &uid_val, &unseen);
3758 if (ok != IMAP_SUCCESS)
3761 if(item->item.mtime == uid_val)
3762 *old_uids_valid = TRUE;
3764 *old_uids_valid = FALSE;
3766 debug_print("Freeing imap uid cache\n");
3768 g_slist_free(item->uid_list);
3769 item->uid_list = NULL;
3771 item->item.mtime = uid_val;
3773 imap_delete_all_cached_messages((FolderItem *)item);
3777 if (!selected_folder)
3778 item->uid_next = uid_next;
3780 /* If old uid_next matches new uid_next we can be sure no message
3781 was added to the folder */
3782 if (( selected_folder && !session->folder_content_changed) ||
3783 (!selected_folder && uid_next == item->uid_next)) {
3784 nummsgs = g_slist_length(item->uid_list);
3786 /* If number of messages is still the same we
3787 know our caches message numbers are still valid,
3788 otherwise if the number of messages has decrease
3789 we discard our cache to start a new scan to find
3790 out which numbers have been removed */
3791 if (exists == nummsgs) {
3792 *msgnum_list = g_slist_copy(item->uid_list);
3794 } else if (exists < nummsgs) {
3795 debug_print("Freeing imap uid cache");
3797 g_slist_free(item->uid_list);
3798 item->uid_list = NULL;
3803 *msgnum_list = NULL;
3807 nummsgs = get_list_of_uids(folder, item, &uidlist);
3809 if (nummsgs != exists) {
3810 /* Cache contains more messages then folder, we have cached
3811 an old UID of a message that was removed and new messages
3812 have been added too, otherwise the uid_next check would
3814 debug_print("Freeing imap uid cache");
3816 g_slist_free(item->uid_list);
3817 item->uid_list = NULL;
3819 g_slist_free(*msgnum_list);
3821 nummsgs = get_list_of_uids(folder, item, &uidlist);
3824 *msgnum_list = uidlist;
3826 dir = folder_item_get_path((FolderItem *)item);
3827 debug_print("removing old messages from %s\n", dir);
3828 remove_numbered_files_not_in_list(dir, *msgnum_list);
3834 static MsgInfo *imap_parse_msg(const gchar *file, FolderItem *item)
3839 flags.perm_flags = MSG_NEW|MSG_UNREAD;
3840 flags.tmp_flags = 0;
3842 g_return_val_if_fail(item != NULL, NULL);
3843 g_return_val_if_fail(file != NULL, NULL);
3845 if (item->stype == F_QUEUE) {
3846 MSG_SET_TMP_FLAGS(flags, MSG_QUEUED);
3847 } else if (item->stype == F_DRAFT) {
3848 MSG_SET_TMP_FLAGS(flags, MSG_DRAFT);
3851 msginfo = procheader_parse_file(file, flags, FALSE, FALSE);
3852 if (!msginfo) return NULL;
3854 msginfo->folder = item;
3859 GSList *imap_get_msginfos(Folder *folder, FolderItem *item, GSList *msgnum_list)
3861 IMAPSession *session;
3862 MsgInfoList *ret = NULL;
3865 g_return_val_if_fail(folder != NULL, NULL);
3866 g_return_val_if_fail(item != NULL, NULL);
3867 g_return_val_if_fail(msgnum_list != NULL, NULL);
3869 session = imap_session_get(folder);
3870 g_return_val_if_fail(session != NULL, NULL);
3872 ok = imap_select(session, IMAP_FOLDER(folder), item->path,
3873 NULL, NULL, NULL, NULL);
3874 if (ok != IMAP_SUCCESS)
3877 if (!(item->stype == F_QUEUE || item->stype == F_DRAFT)) {
3878 ret = g_slist_concat(ret,
3879 imap_get_uncached_messages(
3880 session, item, msgnum_list));
3882 MsgNumberList *sorted_list, *elem;
3883 gint startnum, lastnum;
3885 sorted_list = g_slist_sort(g_slist_copy(msgnum_list), g_int_compare);
3887 startnum = lastnum = GPOINTER_TO_INT(sorted_list->data);
3889 for (elem = sorted_list;; elem = g_slist_next(elem)) {
3893 num = GPOINTER_TO_INT(elem->data);
3895 if (num > lastnum + 1 || elem == NULL) {
3897 for (i = startnum; i <= lastnum; ++i) {
3900 file = imap_fetch_msg(folder, item, i);
3902 MsgInfo *msginfo = imap_parse_msg(file, item);
3903 if (msginfo != NULL) {
3904 msginfo->msgnum = i;
3905 ret = g_slist_append(ret, msginfo);
3919 g_slist_free(sorted_list);
3925 MsgInfo *imap_get_msginfo(Folder *folder, FolderItem *item, gint uid)
3927 MsgInfo *msginfo = NULL;
3928 MsgInfoList *msginfolist;
3929 MsgNumberList numlist;
3931 numlist.next = NULL;
3932 numlist.data = GINT_TO_POINTER(uid);
3934 msginfolist = imap_get_msginfos(folder, item, &numlist);
3935 if (msginfolist != NULL) {
3936 msginfo = msginfolist->data;
3937 g_slist_free(msginfolist);
3943 gboolean imap_scan_required(Folder *folder, FolderItem *_item)
3945 IMAPSession *session;
3946 IMAPFolderItem *item = (IMAPFolderItem *)_item;
3947 gint ok, exists = 0, recent = 0, unseen = 0;
3948 guint32 uid_next, uid_val = 0;
3949 gboolean selected_folder;
3951 g_return_val_if_fail(folder != NULL, FALSE);
3952 g_return_val_if_fail(item != NULL, FALSE);
3953 g_return_val_if_fail(item->item.folder != NULL, FALSE);
3954 g_return_val_if_fail(FOLDER_CLASS(item->item.folder) == &imap_class, FALSE);
3956 if (item->item.path == NULL)
3959 session = imap_session_get(folder);
3960 g_return_val_if_fail(session != NULL, FALSE);
3962 selected_folder = (session->mbox != NULL) &&
3963 (!strcmp(session->mbox, item->item.path));
3964 if (selected_folder) {
3965 ok = imap_cmd_noop(session);
3966 if (ok != IMAP_SUCCESS)
3969 if (session->folder_content_changed)
3972 ok = imap_status(session, IMAP_FOLDER(folder), item->item.path,
3973 &exists, &recent, &uid_next, &uid_val, &unseen);
3974 if (ok != IMAP_SUCCESS)
3977 if ((uid_next != item->uid_next) || (exists < item->item.total_msgs))
3984 void imap_change_flags(Folder *folder, FolderItem *item, MsgInfo *msginfo, MsgPermFlags newflags)
3986 IMAPSession *session;
3987 IMAPFlags flags_set = 0, flags_unset = 0;
3988 gint ok = IMAP_SUCCESS;
3989 MsgNumberList numlist;
3991 g_return_if_fail(folder != NULL);
3992 g_return_if_fail(folder->klass == &imap_class);
3993 g_return_if_fail(item != NULL);
3994 g_return_if_fail(item->folder == folder);
3995 g_return_if_fail(msginfo != NULL);
3996 g_return_if_fail(msginfo->folder == item);
3998 session = imap_session_get(folder);
3999 if (!session) return;
4001 if ((ok = imap_select(session, IMAP_FOLDER(folder), msginfo->folder->path,
4002 NULL, NULL, NULL, NULL)) != IMAP_SUCCESS)
4005 if (!MSG_IS_MARKED(msginfo->flags) && (newflags & MSG_MARKED))
4006 flags_set |= IMAP_FLAG_FLAGGED;
4007 if ( MSG_IS_MARKED(msginfo->flags) && !(newflags & MSG_MARKED))
4008 flags_unset |= IMAP_FLAG_FLAGGED;
4010 if (!MSG_IS_UNREAD(msginfo->flags) && (newflags & MSG_UNREAD))
4011 flags_unset |= IMAP_FLAG_SEEN;
4012 if ( MSG_IS_UNREAD(msginfo->flags) && !(newflags & MSG_UNREAD))
4013 flags_set |= IMAP_FLAG_SEEN;
4015 if (!MSG_IS_REPLIED(msginfo->flags) && (newflags & MSG_REPLIED))
4016 flags_set |= IMAP_FLAG_ANSWERED;
4017 if ( MSG_IS_REPLIED(msginfo->flags) && !(newflags & MSG_REPLIED))
4018 flags_set |= IMAP_FLAG_ANSWERED;
4020 numlist.next = NULL;
4021 numlist.data = GINT_TO_POINTER(msginfo->msgnum);
4024 ok = imap_set_message_flags(session, &numlist, flags_set, TRUE);
4025 if (ok != IMAP_SUCCESS) return;
4029 ok = imap_set_message_flags(session, &numlist, flags_unset, FALSE);
4030 if (ok != IMAP_SUCCESS) return;
4033 msginfo->flags.perm_flags = newflags;
4038 static gint compare_msginfo(gconstpointer a, gconstpointer b)
4040 return ((MsgInfo *)a)->msgnum - ((MsgInfo *)b)->msgnum;
4043 static guint gslist_find_next_num(MsgNumberList **list, guint num)
4047 g_return_val_if_fail(list != NULL, -1);
4049 for (elem = *list; elem != NULL; elem = g_slist_next(elem))
4050 if (GPOINTER_TO_INT(elem->data) >= num)
4053 return elem != NULL ? GPOINTER_TO_INT(elem->data) : (gint)-1;
4057 * NEW and DELETED flags are not syncronized
4058 * - The NEW/RECENT flags in IMAP folders can not really be directly
4059 * modified by Sylpheed
4060 * - The DELETE/DELETED flag in IMAP and Sylpheed don't have the same
4061 * meaning, in IMAP it always removes the messages from the FolderItem
4062 * in Sylpheed it can mean to move the message to trash
4064 static gint imap_get_flags(Folder *folder, FolderItem *item,
4065 MsgInfoList *msginfo_list, GRelation *msgflags)
4067 IMAPSession *session;
4068 GSList *sorted_list;
4070 GSList *new = NULL, *p_new;
4071 GSList *deleted = NULL, *p_deleted;
4073 GSList *unseen = NULL, *answered = NULL, *flagged = NULL;
4074 GSList *p_unseen, *p_answered, *p_flagged;
4076 GSList *seq_list, *cur;
4077 gboolean reverse_seen = FALSE;
4080 gint exists_cnt, recent_cnt, unseen_cnt, uid_next;
4081 guint32 uidvalidity;
4083 g_return_val_if_fail(folder != NULL, -1);
4084 g_return_val_if_fail(item != NULL, -1);
4085 if (msginfo_list == NULL)
4088 session = imap_session_get(folder);
4089 g_return_val_if_fail(session != NULL, -1);
4091 ok = imap_select(session, IMAP_FOLDER(folder), item->path,
4092 NULL, NULL, NULL, NULL);
4093 if (ok != IMAP_SUCCESS)
4096 ok = imap_status(session, IMAP_FOLDER(folder), item->path,
4097 &exists_cnt, &recent_cnt, &uid_next, &uidvalidity, &unseen_cnt);
4099 if (unseen_cnt > exists_cnt / 2)
4100 reverse_seen = TRUE;
4102 cmd_buf = g_string_new(NULL);
4104 sorted_list = g_slist_sort(g_slist_copy(msginfo_list), compare_msginfo);
4106 seq_list = imap_get_seq_set_from_msglist(msginfo_list);
4108 for (cur = seq_list; cur != NULL; cur = g_slist_next(cur)) {
4109 IMAPSet imapset = cur->data;
4111 g_string_sprintf(cmd_buf, "RECENT UID %s", imapset);
4112 imap_cmd_search(session, cmd_buf->str, &p_new);
4113 new = g_slist_concat(new, p_new);
4115 g_string_sprintf(cmd_buf, "%sSEEN UID %s", reverse_seen ? "" : "UN", imapset);
4116 imap_cmd_search(session, cmd_buf->str, &p_unseen);
4117 unseen = g_slist_concat(unseen, p_unseen);
4119 g_string_sprintf(cmd_buf, "ANSWERED UID %s", imapset);
4120 imap_cmd_search(session, cmd_buf->str, &p_answered);
4121 answered = g_slist_concat(answered, p_answered);
4123 g_string_sprintf(cmd_buf, "FLAGGED UID %s", imapset);
4124 imap_cmd_search(session, cmd_buf->str, &p_flagged);
4125 flagged = g_slist_concat(flagged, p_flagged);
4127 g_string_sprintf(cmd_buf, "DELETED UID %s", imapset);
4128 imap_cmd_search(session, cmd_buf->str, &p_deleted);
4129 deleted = g_slist_concat(deleted, p_deleted);
4137 p_answered = answered;
4138 p_flagged = flagged;
4140 p_deleted = deleted;
4142 for (elem = sorted_list; elem != NULL; elem = g_slist_next(elem)) {
4147 msginfo = (MsgInfo *) elem->data;
4148 flags = msginfo->flags.perm_flags;
4149 wasnew = (flags & MSG_NEW);
4150 flags &= ~((reverse_seen ? 0 : MSG_UNREAD | MSG_NEW) | MSG_REPLIED | MSG_MARKED);
4152 flags |= MSG_UNREAD | (wasnew ? MSG_NEW : 0);
4154 if (gslist_find_next_num(&p_new, msginfo->msgnum) == msginfo->msgnum)
4157 if (gslist_find_next_num(&p_unseen, msginfo->msgnum) == msginfo->msgnum) {
4158 if (!reverse_seen) {
4159 flags |= MSG_UNREAD | (wasnew ? MSG_NEW : 0);
4161 flags &= ~(MSG_UNREAD | MSG_NEW);
4164 if (gslist_find_next_num(&p_answered, msginfo->msgnum) == msginfo->msgnum)
4165 flags |= MSG_REPLIED;
4166 if (gslist_find_next_num(&p_flagged, msginfo->msgnum) == msginfo->msgnum)
4167 flags |= MSG_MARKED;
4169 if (gslist_find_next_num(&p_deleted, msginfo->msgnum) == msginfo->msgnum)
4170 MSG_SET_PERM_FLAGS(msginfo->flags, MSG_DELETED);
4172 g_relation_insert(msgflags, msginfo, GINT_TO_POINTER(flags));
4175 imap_seq_set_free(seq_list);
4176 /* g_slist_free(deleted); */
4177 g_slist_free(flagged);
4178 g_slist_free(answered);
4179 g_slist_free(unseen);
4180 /* new not freed in original patch ??? */
4181 g_slist_free(sorted_list);
4182 g_string_free(cmd_buf, TRUE);