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>
50 #include "procheader.h"
51 #include "prefs_account.h"
56 #include "prefs_common.h"
57 #include "inputdialog.h"
59 #include "remotefolder.h"
60 #include "alertpanel.h"
62 #include "statusbar.h"
68 typedef struct _thread_data {
80 typedef struct _IMAPFolder IMAPFolder;
81 typedef struct _IMAPSession IMAPSession;
82 typedef struct _IMAPNameSpace IMAPNameSpace;
83 typedef struct _IMAPFolderItem IMAPFolderItem;
85 #include "prefs_account.h"
87 #define IMAP_FOLDER(obj) ((IMAPFolder *)obj)
88 #define IMAP_FOLDER_ITEM(obj) ((IMAPFolderItem *)obj)
89 #define IMAP_SESSION(obj) ((IMAPSession *)obj)
95 /* list of IMAPNameSpace */
105 gboolean authenticated;
114 gboolean folder_content_changed;
118 struct _IMAPNameSpace
124 #define IMAP_SUCCESS 0
125 #define IMAP_SOCKET 2
126 #define IMAP_AUTHFAIL 3
127 #define IMAP_PROTOCOL 4
128 #define IMAP_SYNTAX 5
132 #define IMAPBUFSIZE 8192
136 IMAP_FLAG_SEEN = 1 << 0,
137 IMAP_FLAG_ANSWERED = 1 << 1,
138 IMAP_FLAG_FLAGGED = 1 << 2,
139 IMAP_FLAG_DELETED = 1 << 3,
140 IMAP_FLAG_DRAFT = 1 << 4
143 #define IMAP_IS_SEEN(flags) ((flags & IMAP_FLAG_SEEN) != 0)
144 #define IMAP_IS_ANSWERED(flags) ((flags & IMAP_FLAG_ANSWERED) != 0)
145 #define IMAP_IS_FLAGGED(flags) ((flags & IMAP_FLAG_FLAGGED) != 0)
146 #define IMAP_IS_DELETED(flags) ((flags & IMAP_FLAG_DELETED) != 0)
147 #define IMAP_IS_DRAFT(flags) ((flags & IMAP_FLAG_DRAFT) != 0)
150 #define IMAP4_PORT 143
152 #define IMAPS_PORT 993
155 #define IMAP_CMD_LIMIT 1000
157 #define QUOTE_IF_REQUIRED(out, str) \
159 if (*str != '"' && strpbrk(str, " \t(){}[]%*") != NULL) { \
163 len = strlen(str) + 3; \
164 Xalloca(__tmp, len, return IMAP_ERROR); \
165 g_snprintf(__tmp, len, "\"%s\"", str); \
168 Xstrdup_a(out, str, return IMAP_ERROR); \
172 typedef gchar * IMAPSet;
174 struct _IMAPFolderItem
183 static void imap_folder_init (Folder *folder,
187 static Folder *imap_folder_new (const gchar *name,
189 static void imap_folder_destroy (Folder *folder);
191 static IMAPSession *imap_session_new (const PrefsAccount *account);
192 static void imap_session_authenticate(IMAPSession *session,
193 const PrefsAccount *account);
194 static void imap_session_destroy (Session *session);
196 static gchar *imap_fetch_msg (Folder *folder,
199 static gint imap_add_msg (Folder *folder,
203 static gint imap_add_msgs (Folder *folder,
206 GRelation *relation);
208 static gint imap_copy_msg (Folder *folder,
211 static gint imap_copy_msgs (Folder *folder,
213 MsgInfoList *msglist,
214 GRelation *relation);
216 static gint imap_remove_msg (Folder *folder,
219 static gint imap_remove_all_msg (Folder *folder,
222 static gboolean imap_is_msg_changed (Folder *folder,
226 static gint imap_close (Folder *folder,
229 static gint imap_scan_tree (Folder *folder);
231 static gint imap_create_tree (Folder *folder);
233 static FolderItem *imap_create_folder (Folder *folder,
236 static gint imap_rename_folder (Folder *folder,
239 static gint imap_remove_folder (Folder *folder,
242 static FolderItem *imap_folder_item_new (Folder *folder);
243 static void imap_folder_item_destroy (Folder *folder,
246 static IMAPSession *imap_session_get (Folder *folder);
248 static gint imap_greeting (IMAPSession *session);
249 static gint imap_auth (IMAPSession *session,
254 static gint imap_scan_tree_recursive (IMAPSession *session,
256 static GSList *imap_parse_list (IMAPFolder *folder,
257 IMAPSession *session,
258 const gchar *real_path,
261 static void imap_create_missing_folders (Folder *folder);
262 static FolderItem *imap_create_special_folder
264 SpecialFolderItemType stype,
267 static gint imap_do_copy_msgs (Folder *folder,
269 MsgInfoList *msglist,
270 GRelation *relation);
272 static void imap_delete_all_cached_messages (FolderItem *item);
275 static SockInfo *imap_open (const gchar *server,
279 static SockInfo *imap_open (const gchar *server,
284 static SockInfo *imap_open_tunnel(const gchar *server,
285 const gchar *tunnelcmd,
288 static SockInfo *imap_open_tunnel(const gchar *server,
289 const gchar *tunnelcmd);
293 static SockInfo *imap_init_sock(SockInfo *sock, SSLType ssl_type);
295 static SockInfo *imap_init_sock(SockInfo *sock);
298 static gchar *imap_get_flag_str (IMAPFlags flags);
299 static gint imap_set_message_flags (IMAPSession *session,
300 MsgNumberList *numlist,
303 static gint imap_select (IMAPSession *session,
309 guint32 *uid_validity);
310 static gint imap_status (IMAPSession *session,
316 guint32 *uid_validity,
319 static void imap_parse_namespace (IMAPSession *session,
321 static void imap_get_namespace_by_list (IMAPSession *session,
323 static IMAPNameSpace *imap_find_namespace (IMAPFolder *folder,
325 static gchar imap_get_path_separator (IMAPFolder *folder,
327 static gchar *imap_get_real_path (IMAPFolder *folder,
330 static gchar *imap_parse_atom (SockInfo *sock,
335 static MsgFlags imap_parse_flags (const gchar *flag_str);
336 static MsgInfo *imap_parse_envelope (SockInfo *sock,
340 static gboolean imap_has_capability (IMAPSession *session,
342 static void imap_free_capabilities (IMAPSession *session);
344 /* low-level IMAP4rev1 commands */
345 static gint imap_cmd_authenticate
346 (IMAPSession *session,
350 static gint imap_cmd_login (IMAPSession *session,
353 static gint imap_cmd_logout (IMAPSession *session);
354 static gint imap_cmd_noop (IMAPSession *session);
356 static gint imap_cmd_starttls (IMAPSession *session);
358 static gint imap_cmd_namespace (IMAPSession *session,
360 static gint imap_cmd_list (IMAPSession *session,
362 const gchar *mailbox,
364 static gint imap_cmd_do_select (IMAPSession *session,
370 guint32 *uid_validity);
371 static gint imap_cmd_select (IMAPSession *session,
376 guint32 *uid_validity);
377 static gint imap_cmd_examine (IMAPSession *session,
382 guint32 *uid_validity);
383 static gint imap_cmd_create (IMAPSession *sock,
384 const gchar *folder);
385 static gint imap_cmd_rename (IMAPSession *sock,
386 const gchar *oldfolder,
387 const gchar *newfolder);
388 static gint imap_cmd_delete (IMAPSession *session,
389 const gchar *folder);
390 static gint imap_cmd_envelope (IMAPSession *session,
392 static gint imap_cmd_fetch (IMAPSession *sock,
394 const gchar *filename);
395 static gint imap_cmd_append (IMAPSession *session,
396 const gchar *destfolder,
400 static gint imap_cmd_copy (IMAPSession *session,
401 const gchar *seq_set,
402 const gchar *destfolder,
403 GRelation *uid_mapping);
404 static gint imap_cmd_store (IMAPSession *session,
407 static gint imap_cmd_expunge (IMAPSession *session,
409 static gint imap_cmd_close (IMAPSession *session);
411 static gint imap_cmd_ok (IMAPSession *session,
413 static void imap_gen_send (IMAPSession *session,
414 const gchar *format, ...);
415 static gint imap_gen_recv (IMAPSession *session,
418 /* misc utility functions */
419 static gchar *strchr_cpy (const gchar *src,
423 static gchar *get_quoted (const gchar *src,
427 static gchar *search_array_contain_str (GPtrArray *array,
429 static gchar *search_array_str (GPtrArray *array,
431 static void imap_path_separator_subst (gchar *str,
434 static gchar *imap_utf8_to_modified_utf7 (const gchar *from);
435 static gchar *imap_modified_utf7_to_utf8 (const gchar *mutf7_str);
437 static GSList *imap_get_seq_set_from_numlist (MsgNumberList *msglist);
438 static GSList *imap_get_seq_set_from_msglist (MsgInfoList *msglist);
439 static void imap_seq_set_free (GSList *seq_list);
441 static gboolean imap_rename_folder_func (GNode *node,
443 static gint imap_get_num_list (Folder *folder,
446 gboolean *old_uids_valid);
447 static GSList *imap_get_msginfos (Folder *folder,
449 GSList *msgnum_list);
450 static MsgInfo *imap_get_msginfo (Folder *folder,
453 static gboolean imap_scan_required (Folder *folder,
455 static void imap_change_flags (Folder *folder,
458 MsgPermFlags newflags);
459 static gint imap_get_flags (Folder *folder,
461 MsgInfoList *msglist,
462 GRelation *msgflags);
463 static gchar *imap_folder_get_path (Folder *folder);
464 static gchar *imap_item_get_path (Folder *folder,
467 static FolderClass imap_class;
470 void *imap_getline_thread(void *data)
472 thread_data *td = (thread_data *)data;
475 line = sock_getline(td->sock);
483 static gchar *imap_getline(SockInfo *sock)
485 #if (defined USE_PTHREAD && defined __GLIBC__ && (__GLIBC__ > 2 || (__GLIBC__ == 2 && __GLIBC_MINOR__ >= 3)))
486 thread_data *td = g_new0(thread_data, 1);
492 debug_print("creating imap_getline_thread...\n");
493 if (pthread_create(&pt, PTHREAD_CREATE_JOINABLE,
494 imap_getline_thread, td) != 0) {
496 return sock_getline(sock);
499 debug_print("waiting for imap_getline_thread...\n");
501 /* don't let the interface freeze while waiting */
505 /* get the thread's return value and clean its resources */
506 pthread_join(pt, (void *)&line);
509 debug_print("imap_getline_thread returned %s\n", line);
512 return sock_getline(sock);
516 FolderClass *imap_get_class(void)
518 if (imap_class.idstr == NULL) {
519 imap_class.type = F_IMAP;
520 imap_class.idstr = "imap";
521 imap_class.uistr = "IMAP4";
523 /* Folder functions */
524 imap_class.new_folder = imap_folder_new;
525 imap_class.destroy_folder = imap_folder_destroy;
526 imap_class.scan_tree = imap_scan_tree;
527 imap_class.create_tree = imap_create_tree;
529 /* FolderItem functions */
530 imap_class.item_new = imap_folder_item_new;
531 imap_class.item_destroy = imap_folder_item_destroy;
532 imap_class.item_get_path = imap_item_get_path;
533 imap_class.create_folder = imap_create_folder;
534 imap_class.rename_folder = imap_rename_folder;
535 imap_class.remove_folder = imap_remove_folder;
536 imap_class.close = imap_close;
537 imap_class.get_num_list = imap_get_num_list;
538 imap_class.scan_required = imap_scan_required;
540 /* Message functions */
541 imap_class.get_msginfo = imap_get_msginfo;
542 imap_class.get_msginfos = imap_get_msginfos;
543 imap_class.fetch_msg = imap_fetch_msg;
544 imap_class.add_msg = imap_add_msg;
545 imap_class.add_msgs = imap_add_msgs;
546 imap_class.copy_msg = imap_copy_msg;
547 imap_class.copy_msgs = imap_copy_msgs;
548 imap_class.remove_msg = imap_remove_msg;
549 imap_class.remove_all_msg = imap_remove_all_msg;
550 imap_class.is_msg_changed = imap_is_msg_changed;
551 imap_class.change_flags = imap_change_flags;
552 imap_class.get_flags = imap_get_flags;
558 static Folder *imap_folder_new(const gchar *name, const gchar *path)
562 folder = (Folder *)g_new0(IMAPFolder, 1);
563 folder->klass = &imap_class;
564 imap_folder_init(folder, name, path);
569 static void imap_folder_destroy(Folder *folder)
573 dir = imap_folder_get_path(folder);
574 if (is_dir_exist(dir))
575 remove_dir_recursive(dir);
578 folder_remote_folder_destroy(REMOTE_FOLDER(folder));
581 static void imap_folder_init(Folder *folder, const gchar *name,
584 folder_remote_folder_init((Folder *)folder, name, path);
587 static FolderItem *imap_folder_item_new(Folder *folder)
589 IMAPFolderItem *item;
591 item = g_new0(IMAPFolderItem, 1);
594 item->uid_list = NULL;
596 return (FolderItem *)item;
599 static void imap_folder_item_destroy(Folder *folder, FolderItem *_item)
601 IMAPFolderItem *item = (IMAPFolderItem *)_item;
603 g_return_if_fail(item != NULL);
604 g_slist_free(item->uid_list);
609 static gboolean imap_reset_uid_lists_func(GNode *node, gpointer data)
611 IMAPFolderItem *item = (IMAPFolderItem *)node->data;
615 g_slist_free(item->uid_list);
616 item->uid_list = NULL;
621 static void imap_reset_uid_lists(Folder *folder)
623 if(folder->node == NULL)
626 /* Destroy all uid lists and rest last uid */
627 g_node_traverse(folder->node, G_IN_ORDER, G_TRAVERSE_ALL, -1, imap_reset_uid_lists_func, NULL);
630 /* Send CAPABILITY, and examine the server's response to see whether this
631 * connection is pre-authenticated or not and build a list of CAPABILITIES. */
632 static gint imap_greeting(IMAPSession *session)
637 imap_gen_send(session, "CAPABILITY");
639 argbuf = g_ptr_array_new();
641 if (imap_cmd_ok(session, argbuf) != IMAP_SUCCESS ||
642 ((capstr = search_array_str(argbuf, "CAPABILITY ")) == NULL)) {
643 ptr_array_free_strings(argbuf);
644 g_ptr_array_free(argbuf, TRUE);
648 session->authenticated = search_array_str(argbuf, "PREAUTH") != NULL;
650 capstr += strlen("CAPABILITY ");
652 IMAP_SESSION(session)->capability = g_strsplit(capstr, " ", 0);
654 ptr_array_free_strings(argbuf);
655 g_ptr_array_free(argbuf, TRUE);
657 if (imap_has_capability(session, "UIDPLUS"))
658 session->uidplus = TRUE;
663 static gint imap_auth(IMAPSession *session, const gchar *user, const gchar *pass,
668 if (type == 0 || type == IMAP_AUTH_LOGIN)
669 ok = imap_cmd_login(session, user, pass);
671 ok = imap_cmd_authenticate(session, user, pass, type);
673 if (ok == IMAP_SUCCESS)
674 session->authenticated = TRUE;
679 static IMAPSession *imap_session_get(Folder *folder)
681 RemoteFolder *rfolder = REMOTE_FOLDER(folder);
682 IMAPSession *session = NULL;
684 g_return_val_if_fail(folder != NULL, NULL);
685 g_return_val_if_fail(FOLDER_CLASS(folder) == &imap_class, NULL);
686 g_return_val_if_fail(folder->account != NULL, NULL);
688 if (prefs_common.work_offline && !imap_gtk_should_override())
691 /* Make sure we have a session */
692 if (rfolder->session != NULL) {
693 session = IMAP_SESSION(rfolder->session);
695 imap_reset_uid_lists(folder);
696 session = imap_session_new(folder->account);
701 if (SESSION(session)->sock->state == CONN_DISCONNECTED) {
702 debug_print("IMAP server disconnected\n");
703 session_destroy(SESSION(session));
704 imap_reset_uid_lists(folder);
705 session = imap_session_new(folder->account);
708 /* Make sure session is authenticated */
709 if (!IMAP_SESSION(session)->authenticated)
710 imap_session_authenticate(IMAP_SESSION(session), folder->account);
711 if (!IMAP_SESSION(session)->authenticated) {
712 session_destroy(SESSION(session));
713 rfolder->session = NULL;
717 /* Make sure we have parsed the IMAP namespace */
718 imap_parse_namespace(IMAP_SESSION(session),
719 IMAP_FOLDER(folder));
721 /* I think the point of this code is to avoid sending a
722 * keepalive if we've used the session recently and therefore
723 * think it's still alive. Unfortunately, most of the code
724 * does not yet check for errors on the socket, and so if the
725 * connection drops we don't notice until the timeout expires.
726 * A better solution than sending a NOOP every time would be
727 * for every command to be prepared to retry until it is
728 * successfully sent. -- mbp */
729 if (time(NULL) - SESSION(session)->last_access_time > SESSION_TIMEOUT_INTERVAL) {
730 /* verify that the session is still alive */
731 if (imap_cmd_noop(session) != IMAP_SUCCESS) {
732 /* Check if this is the first try to establish a
733 connection, if yes we don't try to reconnect */
734 if (rfolder->session == NULL) {
735 log_warning(_("Connecting to %s failed"),
736 folder->account->recv_server);
737 session_destroy(SESSION(session));
740 log_warning(_("IMAP4 connection to %s has been"
741 " disconnected. Reconnecting...\n"),
742 folder->account->recv_server);
743 statusbar_print_all(_("IMAP4 connection to %s has been"
744 " disconnected. Reconnecting...\n"),
745 folder->account->recv_server);
746 session_destroy(SESSION(session));
747 /* Clear folders session to make imap_session_get create
748 a new session, because of rfolder->session == NULL
749 it will not try to reconnect again and so avoid an
751 rfolder->session = NULL;
752 session = imap_session_get(folder);
758 rfolder->session = SESSION(session);
760 return IMAP_SESSION(session);
763 static IMAPSession *imap_session_new(const PrefsAccount *account)
765 IMAPSession *session;
770 /* FIXME: IMAP over SSL only... */
773 port = account->set_imapport ? account->imapport
774 : account->ssl_imap == SSL_TUNNEL ? IMAPS_PORT : IMAP4_PORT;
775 ssl_type = account->ssl_imap;
777 port = account->set_imapport ? account->imapport
781 if (account->set_tunnelcmd) {
782 log_message(_("creating tunneled IMAP4 connection\n"));
784 if ((imap_sock = imap_open_tunnel(account->recv_server,
788 if ((imap_sock = imap_open_tunnel(account->recv_server,
789 account->tunnelcmd)) == NULL)
793 g_return_val_if_fail(account->recv_server != NULL, NULL);
795 log_message(_("creating IMAP4 connection to %s:%d ...\n"),
796 account->recv_server, port);
799 if ((imap_sock = imap_open(account->recv_server, port,
802 if ((imap_sock = imap_open(account->recv_server, port)) == NULL)
807 session = g_new0(IMAPSession, 1);
808 session_init(SESSION(session));
809 SESSION(session)->type = SESSION_IMAP;
810 SESSION(session)->server = g_strdup(account->recv_server);
811 SESSION(session)->sock = imap_sock;
813 SESSION(session)->destroy = imap_session_destroy;
815 session->capability = NULL;
817 session->authenticated = FALSE;
818 session->mbox = NULL;
819 session->cmd_count = 0;
821 /* Only need to log in if the connection was not PREAUTH */
822 if (imap_greeting(session) != IMAP_SUCCESS) {
823 session_destroy(SESSION(session));
828 if (account->ssl_imap == SSL_STARTTLS &&
829 imap_has_capability(session, "STARTTLS")) {
832 ok = imap_cmd_starttls(session);
833 if (ok != IMAP_SUCCESS) {
834 log_warning(_("Can't start TLS session.\n"));
835 session_destroy(SESSION(session));
838 if (!ssl_init_socket_with_method(SESSION(session)->sock,
840 session_destroy(SESSION(session));
844 imap_free_capabilities(session);
845 session->authenticated = FALSE;
846 session->uidplus = FALSE;
847 session->cmd_count = 1;
849 if (imap_greeting(session) != IMAP_SUCCESS) {
850 session_destroy(SESSION(session));
855 log_message("IMAP connection is %s-authenticated\n",
856 (session->authenticated) ? "pre" : "un");
861 static void imap_session_authenticate(IMAPSession *session,
862 const PrefsAccount *account)
866 g_return_if_fail(account->userid != NULL);
868 pass = account->passwd;
871 tmp_pass = input_dialog_query_password(account->recv_server, account->userid);
874 Xstrdup_a(pass, tmp_pass, {g_free(tmp_pass); return;});
878 if (imap_auth(session, account->userid, pass, account->imap_auth_type) != IMAP_SUCCESS) {
879 imap_cmd_logout(session);
883 session->authenticated = TRUE;
886 static void imap_session_destroy(Session *session)
888 imap_free_capabilities(IMAP_SESSION(session));
889 g_free(IMAP_SESSION(session)->mbox);
890 sock_close(session->sock);
891 session->sock = NULL;
894 static gchar *imap_fetch_msg(Folder *folder, FolderItem *item, gint uid)
896 gchar *path, *filename;
897 IMAPSession *session;
900 g_return_val_if_fail(folder != NULL, NULL);
901 g_return_val_if_fail(item != NULL, NULL);
903 path = folder_item_get_path(item);
904 if (!is_dir_exist(path))
906 filename = g_strconcat(path, G_DIR_SEPARATOR_S, itos(uid), NULL);
909 if (is_file_exist(filename)) {
910 debug_print("message %d has been already cached.\n", uid);
914 session = imap_session_get(folder);
920 ok = imap_select(session, IMAP_FOLDER(folder), item->path,
921 NULL, NULL, NULL, NULL);
922 if (ok != IMAP_SUCCESS) {
923 g_warning("can't select mailbox %s\n", item->path);
928 debug_print("getting message %d...\n", uid);
929 ok = imap_cmd_fetch(session, (guint32)uid, filename);
931 if (ok != IMAP_SUCCESS) {
932 g_warning("can't fetch message %d\n", uid);
940 static gint imap_add_msg(Folder *folder, FolderItem *dest,
941 const gchar *file, MsgFlags *flags)
945 MsgFileInfo fileinfo;
947 g_return_val_if_fail(file != NULL, -1);
949 fileinfo.msginfo = NULL;
950 fileinfo.file = (gchar *)file;
951 fileinfo.flags = flags;
952 file_list.data = &fileinfo;
953 file_list.next = NULL;
955 ret = imap_add_msgs(folder, dest, &file_list, NULL);
959 static gint imap_add_msgs(Folder *folder, FolderItem *dest, GSList *file_list,
963 IMAPSession *session;
964 guint32 last_uid = 0;
966 MsgFileInfo *fileinfo;
969 g_return_val_if_fail(folder != NULL, -1);
970 g_return_val_if_fail(dest != NULL, -1);
971 g_return_val_if_fail(file_list != NULL, -1);
973 session = imap_session_get(folder);
974 if (!session) return -1;
976 destdir = imap_get_real_path(IMAP_FOLDER(folder), dest->path);
978 for (cur = file_list; cur != NULL; cur = cur->next) {
979 IMAPFlags iflags = 0;
982 fileinfo = (MsgFileInfo *)cur->data;
984 if (fileinfo->flags) {
985 if (MSG_IS_MARKED(*fileinfo->flags))
986 iflags |= IMAP_FLAG_FLAGGED;
987 if (MSG_IS_REPLIED(*fileinfo->flags))
988 iflags |= IMAP_FLAG_ANSWERED;
989 if (!MSG_IS_UNREAD(*fileinfo->flags))
990 iflags |= IMAP_FLAG_SEEN;
993 if (dest->stype == F_OUTBOX ||
994 dest->stype == F_QUEUE ||
995 dest->stype == F_DRAFT ||
996 dest->stype == F_TRASH)
997 iflags |= IMAP_FLAG_SEEN;
999 ok = imap_cmd_append(session, destdir, fileinfo->file, iflags,
1002 if (ok != IMAP_SUCCESS) {
1003 g_warning("can't append message %s\n", fileinfo->file);
1008 if (relation != NULL)
1009 g_relation_insert(relation, fileinfo->msginfo != NULL ?
1010 (gpointer) fileinfo->msginfo : (gpointer) fileinfo,
1011 GINT_TO_POINTER(dest->last_num + 1));
1012 if (last_uid < new_uid)
1021 static gint imap_do_copy_msgs(Folder *folder, FolderItem *dest,
1022 MsgInfoList *msglist, GRelation *relation)
1026 GSList *seq_list, *cur;
1028 IMAPSession *session;
1029 gint ok = IMAP_SUCCESS;
1030 GRelation *uid_mapping;
1033 g_return_val_if_fail(folder != NULL, -1);
1034 g_return_val_if_fail(dest != NULL, -1);
1035 g_return_val_if_fail(msglist != NULL, -1);
1037 session = imap_session_get(folder);
1038 if (!session) return -1;
1040 msginfo = (MsgInfo *)msglist->data;
1042 src = msginfo->folder;
1044 g_warning("the src folder is identical to the dest.\n");
1048 ok = imap_select(session, IMAP_FOLDER(folder), msginfo->folder->path,
1049 NULL, NULL, NULL, NULL);
1050 if (ok != IMAP_SUCCESS)
1053 destdir = imap_get_real_path(IMAP_FOLDER(folder), dest->path);
1054 seq_list = imap_get_seq_set_from_msglist(msglist);
1055 uid_mapping = g_relation_new(2);
1056 g_relation_index(uid_mapping, 0, g_direct_hash, g_direct_equal);
1058 for (cur = seq_list; cur != NULL; cur = g_slist_next(cur)) {
1059 gchar *seq_set = (gchar *)cur->data;
1061 debug_print("Copying message %s%c[%s] to %s ...\n",
1062 src->path, G_DIR_SEPARATOR,
1065 ok = imap_cmd_copy(session, seq_set, destdir, uid_mapping);
1066 if (ok != IMAP_SUCCESS) {
1067 g_relation_destroy(uid_mapping);
1068 imap_seq_set_free(seq_list);
1073 for (cur = msglist; cur != NULL; cur = g_slist_next(cur)) {
1074 MsgInfo *msginfo = (MsgInfo *)cur->data;
1077 tuples = g_relation_select(uid_mapping,
1078 GINT_TO_POINTER(msginfo->msgnum),
1080 if (tuples->len > 0) {
1081 gint num = GPOINTER_TO_INT(g_tuples_index(tuples, 0, 1));
1082 g_relation_insert(relation, msginfo,
1083 GPOINTER_TO_INT(num));
1087 g_relation_insert(relation, msginfo,
1088 GPOINTER_TO_INT(0));
1089 g_tuples_destroy(tuples);
1092 g_relation_destroy(uid_mapping);
1093 imap_seq_set_free(seq_list);
1097 if (ok == IMAP_SUCCESS)
1103 static gint imap_copy_msg(Folder *folder, FolderItem *dest, MsgInfo *msginfo)
1107 g_return_val_if_fail(msginfo != NULL, -1);
1109 msglist.data = msginfo;
1110 msglist.next = NULL;
1112 return imap_copy_msgs(folder, dest, &msglist, NULL);
1115 static gint imap_copy_msgs(Folder *folder, FolderItem *dest,
1116 MsgInfoList *msglist, GRelation *relation)
1122 g_return_val_if_fail(folder != NULL, -1);
1123 g_return_val_if_fail(dest != NULL, -1);
1124 g_return_val_if_fail(msglist != NULL, -1);
1126 msginfo = (MsgInfo *)msglist->data;
1127 g_return_val_if_fail(msginfo->folder != NULL, -1);
1129 if (folder == msginfo->folder->folder)
1130 return imap_do_copy_msgs(folder, dest, msglist, relation);
1132 file_list = procmsg_get_message_file_list(msglist);
1133 g_return_val_if_fail(file_list != NULL, -1);
1135 ret = imap_add_msgs(folder, dest, file_list, relation);
1137 procmsg_message_file_list_free(file_list);
1142 static gint imap_remove_msg(Folder *folder, FolderItem *item, gint uid)
1145 IMAPSession *session;
1147 MsgNumberList numlist;
1149 g_return_val_if_fail(folder != NULL, -1);
1150 g_return_val_if_fail(FOLDER_CLASS(folder) == &imap_class, -1);
1151 g_return_val_if_fail(item != NULL, -1);
1153 session = imap_session_get(folder);
1154 if (!session) return -1;
1156 ok = imap_select(session, IMAP_FOLDER(folder), item->path,
1157 NULL, NULL, NULL, NULL);
1158 if (ok != IMAP_SUCCESS)
1161 numlist.next = NULL;
1162 numlist.data = GINT_TO_POINTER(uid);
1164 ok = imap_set_message_flags
1165 (IMAP_SESSION(REMOTE_FOLDER(folder)->session),
1166 &numlist, IMAP_FLAG_DELETED, TRUE);
1167 if (ok != IMAP_SUCCESS) {
1168 log_warning(_("can't set deleted flags: %d\n"), uid);
1172 if (!session->uidplus) {
1173 ok = imap_cmd_expunge(session, NULL);
1177 uidstr = g_strdup_printf("%u", uid);
1178 ok = imap_cmd_expunge(session, uidstr);
1181 if (ok != IMAP_SUCCESS) {
1182 log_warning(_("can't expunge\n"));
1186 IMAP_FOLDER_ITEM(item)->uid_list = g_slist_remove(
1187 IMAP_FOLDER_ITEM(item)->uid_list, numlist.data);
1188 dir = folder_item_get_path(item);
1189 if (is_dir_exist(dir))
1190 remove_numbered_files(dir, uid, uid);
1193 return IMAP_SUCCESS;
1196 static gint imap_remove_all_msg(Folder *folder, FolderItem *item)
1199 IMAPSession *session;
1202 g_return_val_if_fail(folder != NULL, -1);
1203 g_return_val_if_fail(item != NULL, -1);
1205 session = imap_session_get(folder);
1206 if (!session) return -1;
1208 ok = imap_select(session, IMAP_FOLDER(folder), item->path,
1209 NULL, NULL, NULL, NULL);
1210 if (ok != IMAP_SUCCESS)
1213 imap_gen_send(session, "STORE 1:* +FLAGS.SILENT (\\Deleted)");
1214 ok = imap_cmd_ok(session, NULL);
1215 if (ok != IMAP_SUCCESS) {
1216 log_warning(_("can't set deleted flags: 1:*\n"));
1220 ok = imap_cmd_expunge(session, NULL);
1221 if (ok != IMAP_SUCCESS) {
1222 log_warning(_("can't expunge\n"));
1226 dir = folder_item_get_path(item);
1227 if (is_dir_exist(dir))
1228 remove_all_numbered_files(dir);
1231 return IMAP_SUCCESS;
1234 static gboolean imap_is_msg_changed(Folder *folder, FolderItem *item,
1237 /* TODO: properly implement this method */
1241 static gint imap_close(Folder *folder, FolderItem *item)
1244 IMAPSession *session;
1246 g_return_val_if_fail(folder != NULL, -1);
1247 g_return_val_if_fail(item != NULL, -1);
1248 g_return_val_if_fail(item->path != NULL, -1);
1250 session = imap_session_get(folder);
1251 if (!session) return -1;
1253 if (session->mbox) {
1254 if (strcmp2(session->mbox, item->path) != 0) return -1;
1256 ok = imap_cmd_close(session);
1257 if (ok != IMAP_SUCCESS)
1258 log_warning(_("can't close folder\n"));
1260 g_free(session->mbox);
1261 session->mbox = NULL;
1269 static gint imap_scan_tree(Folder *folder)
1271 FolderItem *item = NULL;
1272 IMAPSession *session;
1273 gchar *root_folder = NULL;
1275 g_return_val_if_fail(folder != NULL, -1);
1276 g_return_val_if_fail(folder->account != NULL, -1);
1278 session = imap_session_get(folder);
1280 if (!folder->node) {
1281 folder_tree_destroy(folder);
1282 item = folder_item_new(folder, folder->name, NULL);
1283 item->folder = folder;
1284 folder->node = item->node = g_node_new(item);
1289 if (folder->account->imap_dir && *folder->account->imap_dir) {
1294 Xstrdup_a(root_folder, folder->account->imap_dir, return -1);
1295 extract_quote(root_folder, '"');
1296 subst_char(root_folder,
1297 imap_get_path_separator(IMAP_FOLDER(folder),
1300 strtailchomp(root_folder, '/');
1301 real_path = imap_get_real_path
1302 (IMAP_FOLDER(folder), root_folder);
1303 debug_print("IMAP root directory: %s\n", real_path);
1305 /* check if root directory exist */
1306 argbuf = g_ptr_array_new();
1307 ok = imap_cmd_list(session, NULL, real_path, argbuf);
1308 if (ok != IMAP_SUCCESS ||
1309 search_array_str(argbuf, "LIST ") == NULL) {
1310 log_warning(_("root folder %s does not exist\n"), real_path);
1311 g_ptr_array_free(argbuf, TRUE);
1314 if (!folder->node) {
1315 item = folder_item_new(folder, folder->name, NULL);
1316 item->folder = folder;
1317 folder->node = item->node = g_node_new(item);
1321 g_ptr_array_free(argbuf, TRUE);
1326 item = FOLDER_ITEM(folder->node->data);
1327 if (!item || ((item->path || root_folder) &&
1328 strcmp2(item->path, root_folder) != 0)) {
1329 folder_tree_destroy(folder);
1330 item = folder_item_new(folder, folder->name, root_folder);
1331 item->folder = folder;
1332 folder->node = item->node = g_node_new(item);
1335 imap_scan_tree_recursive(session, FOLDER_ITEM(folder->node->data));
1336 imap_create_missing_folders(folder);
1341 static gint imap_scan_tree_recursive(IMAPSession *session, FolderItem *item)
1344 IMAPFolder *imapfolder;
1345 FolderItem *new_item;
1346 GSList *item_list, *cur;
1349 gchar *wildcard_path;
1353 g_return_val_if_fail(item != NULL, -1);
1354 g_return_val_if_fail(item->folder != NULL, -1);
1355 g_return_val_if_fail(item->no_sub == FALSE, -1);
1357 folder = item->folder;
1358 imapfolder = IMAP_FOLDER(folder);
1360 separator = imap_get_path_separator(imapfolder, item->path);
1362 if (folder->ui_func)
1363 folder->ui_func(folder, item, folder->ui_func_data);
1366 wildcard[0] = separator;
1369 real_path = imap_get_real_path(imapfolder, item->path);
1373 real_path = g_strdup("");
1376 Xstrcat_a(wildcard_path, real_path, wildcard,
1377 {g_free(real_path); return IMAP_ERROR;});
1378 QUOTE_IF_REQUIRED(wildcard_path, wildcard_path);
1380 imap_gen_send(session, "LIST \"\" %s",
1383 strtailchomp(real_path, separator);
1384 item_list = imap_parse_list(imapfolder, session, real_path, NULL);
1387 node = item->node->children;
1388 while (node != NULL) {
1389 FolderItem *old_item = FOLDER_ITEM(node->data);
1390 GNode *next = node->next;
1393 for (cur = item_list; cur != NULL; cur = cur->next) {
1394 FolderItem *cur_item = FOLDER_ITEM(cur->data);
1395 if (!strcmp2(old_item->path, cur_item->path)) {
1396 new_item = cur_item;
1401 debug_print("folder '%s' not found. removing...\n",
1403 folder_item_remove(old_item);
1405 old_item->no_sub = new_item->no_sub;
1406 old_item->no_select = new_item->no_select;
1407 if (old_item->no_sub == TRUE && node->children) {
1408 debug_print("folder '%s' doesn't have "
1409 "subfolders. removing...\n",
1411 folder_item_remove_children(old_item);
1418 for (cur = item_list; cur != NULL; cur = cur->next) {
1419 FolderItem *cur_item = FOLDER_ITEM(cur->data);
1421 for (node = item->node->children; node != NULL;
1422 node = node->next) {
1423 if (!strcmp2(FOLDER_ITEM(node->data)->path,
1425 new_item = FOLDER_ITEM(node->data);
1426 folder_item_destroy(cur_item);
1432 new_item = cur_item;
1433 debug_print("new folder '%s' found.\n", new_item->path);
1434 folder_item_append(item, new_item);
1437 if (!strcmp(new_item->path, "INBOX")) {
1438 new_item->stype = F_INBOX;
1439 folder->inbox = new_item;
1440 } else if (!folder_item_parent(item) || item->stype == F_INBOX) {
1443 base = g_path_get_basename(new_item->path);
1445 if (!folder->outbox && !g_ascii_strcasecmp(base, "Sent")) {
1446 new_item->stype = F_OUTBOX;
1447 folder->outbox = new_item;
1448 } else if (!folder->draft && !g_ascii_strcasecmp(base, "Drafts")) {
1449 new_item->stype = F_DRAFT;
1450 folder->draft = new_item;
1451 } else if (!folder->queue && !g_ascii_strcasecmp(base, "Queue")) {
1452 new_item->stype = F_QUEUE;
1453 folder->queue = new_item;
1454 } else if (!folder->trash && !g_ascii_strcasecmp(base, "Trash")) {
1455 new_item->stype = F_TRASH;
1456 folder->trash = new_item;
1461 if (new_item->no_sub == FALSE)
1462 imap_scan_tree_recursive(session, new_item);
1465 g_slist_free(item_list);
1467 return IMAP_SUCCESS;
1470 static GSList *imap_parse_list(IMAPFolder *folder, IMAPSession *session,
1471 const gchar *real_path, gchar *separator)
1473 gchar buf[IMAPBUFSIZE];
1475 gchar separator_str[16];
1478 gchar *loc_name, *loc_path;
1479 GSList *item_list = NULL;
1481 FolderItem *new_item;
1483 debug_print("getting list of %s ...\n",
1484 *real_path ? real_path : "\"\"");
1486 str = g_string_new(NULL);
1489 if (sock_gets(SESSION(session)->sock, buf, sizeof(buf)) <= 0) {
1490 log_warning(_("error occurred while getting LIST.\n"));
1494 if (buf[0] != '*' || buf[1] != ' ') {
1495 log_print("IMAP4< %s\n", buf);
1496 if (sscanf(buf, "%*d %16s", buf) < 1 ||
1497 strcmp(buf, "OK") != 0)
1498 log_warning(_("error occurred while getting LIST.\n"));
1502 debug_print("IMAP4< %s\n", buf);
1504 g_string_assign(str, buf);
1506 if (strncmp(p, "LIST ", 5) != 0) continue;
1509 if (*p != '(') continue;
1511 p = strchr_cpy(p, ')', flags, sizeof(flags));
1513 while (*p == ' ') p++;
1515 p = strchr_cpy(p, ' ', separator_str, sizeof(separator_str));
1517 extract_quote(separator_str, '"');
1518 if (!strcmp(separator_str, "NIL"))
1519 separator_str[0] = '\0';
1521 *separator = separator_str[0];
1524 while (*p == ' ') p++;
1525 if (*p == '{' || *p == '"')
1526 p = imap_parse_atom(SESSION(session)->sock, p,
1527 buf, sizeof(buf), str);
1529 strncpy2(buf, p, sizeof(buf));
1530 strtailchomp(buf, separator_str[0]);
1531 if (buf[0] == '\0') continue;
1532 if (!strcmp(buf, real_path)) continue;
1534 if (separator_str[0] != '\0')
1535 subst_char(buf, separator_str[0], '/');
1536 base = g_path_get_basename(buf);
1537 if (base[0] == '.') continue;
1539 loc_name = imap_modified_utf7_to_utf8(base);
1540 loc_path = imap_modified_utf7_to_utf8(buf);
1541 new_item = folder_item_new(FOLDER(folder), loc_name, loc_path);
1542 if (strcasestr(flags, "\\Noinferiors") != NULL)
1543 new_item->no_sub = TRUE;
1544 if (strcmp(buf, "INBOX") != 0 &&
1545 strcasestr(flags, "\\Noselect") != NULL)
1546 new_item->no_select = TRUE;
1548 item_list = g_slist_append(item_list, new_item);
1550 debug_print("folder '%s' found.\n", loc_path);
1556 g_string_free(str, TRUE);
1561 static gint imap_create_tree(Folder *folder)
1563 g_return_val_if_fail(folder != NULL, -1);
1564 g_return_val_if_fail(folder->node != NULL, -1);
1565 g_return_val_if_fail(folder->node->data != NULL, -1);
1566 g_return_val_if_fail(folder->account != NULL, -1);
1568 imap_scan_tree(folder);
1569 imap_create_missing_folders(folder);
1574 static void imap_create_missing_folders(Folder *folder)
1576 g_return_if_fail(folder != NULL);
1579 folder->inbox = imap_create_special_folder
1580 (folder, F_INBOX, "INBOX");
1582 if (!folder->outbox)
1583 folder->outbox = imap_create_special_folder
1584 (folder, F_OUTBOX, "Sent");
1586 folder->draft = imap_create_special_folder
1587 (folder, F_DRAFT, "Drafts");
1589 folder->queue = imap_create_special_folder
1590 (folder, F_QUEUE, "Queue");
1593 folder->trash = imap_create_special_folder
1594 (folder, F_TRASH, "Trash");
1597 static FolderItem *imap_create_special_folder(Folder *folder,
1598 SpecialFolderItemType stype,
1602 FolderItem *new_item;
1604 g_return_val_if_fail(folder != NULL, NULL);
1605 g_return_val_if_fail(folder->node != NULL, NULL);
1606 g_return_val_if_fail(folder->node->data != NULL, NULL);
1607 g_return_val_if_fail(folder->account != NULL, NULL);
1608 g_return_val_if_fail(name != NULL, NULL);
1610 item = FOLDER_ITEM(folder->node->data);
1611 new_item = imap_create_folder(folder, item, name);
1614 g_warning("Can't create '%s'\n", name);
1615 if (!folder->inbox) return NULL;
1617 new_item = imap_create_folder(folder, folder->inbox, name);
1619 g_warning("Can't create '%s' under INBOX\n", name);
1621 new_item->stype = stype;
1623 new_item->stype = stype;
1628 static gchar *imap_folder_get_path(Folder *folder)
1632 g_return_val_if_fail(folder != NULL, NULL);
1633 g_return_val_if_fail(folder->account != NULL, NULL);
1635 folder_path = g_strconcat(get_imap_cache_dir(),
1637 folder->account->recv_server,
1639 folder->account->userid,
1645 static gchar *imap_item_get_path(Folder *folder, FolderItem *item)
1647 gchar *folder_path, *path;
1649 g_return_val_if_fail(folder != NULL, NULL);
1650 g_return_val_if_fail(item != NULL, NULL);
1651 folder_path = imap_folder_get_path(folder);
1653 g_return_val_if_fail(folder_path != NULL, NULL);
1654 if (folder_path[0] == G_DIR_SEPARATOR) {
1656 path = g_strconcat(folder_path, G_DIR_SEPARATOR_S,
1659 path = g_strdup(folder_path);
1662 path = g_strconcat(get_home_dir(), G_DIR_SEPARATOR_S,
1663 folder_path, G_DIR_SEPARATOR_S,
1666 path = g_strconcat(get_home_dir(), G_DIR_SEPARATOR_S,
1669 g_free(folder_path);
1674 static FolderItem *imap_create_folder(Folder *folder, FolderItem *parent,
1677 gchar *dirpath, *imap_path;
1678 IMAPSession *session;
1679 FolderItem *new_item;
1685 g_return_val_if_fail(folder != NULL, NULL);
1686 g_return_val_if_fail(folder->account != NULL, NULL);
1687 g_return_val_if_fail(parent != NULL, NULL);
1688 g_return_val_if_fail(name != NULL, NULL);
1690 session = imap_session_get(folder);
1691 if (!session) return NULL;
1693 if (!folder_item_parent(parent) && strcmp(name, "INBOX") == 0)
1694 dirpath = g_strdup(name);
1695 else if (parent->path)
1696 dirpath = g_strconcat(parent->path, "/", name, NULL);
1697 else if ((p = strchr(name, '/')) != NULL && *(p + 1) != '\0')
1698 dirpath = g_strdup(name);
1699 else if (folder->account->imap_dir && *folder->account->imap_dir) {
1702 Xstrdup_a(imap_dir, folder->account->imap_dir, return NULL);
1703 strtailchomp(imap_dir, '/');
1704 dirpath = g_strconcat(imap_dir, "/", name, NULL);
1706 dirpath = g_strdup(name);
1708 /* keep trailing directory separator to create a folder that contains
1710 imap_path = imap_utf8_to_modified_utf7(dirpath);
1711 strtailchomp(dirpath, '/');
1712 Xstrdup_a(new_name, name, {g_free(dirpath); return NULL;});
1713 strtailchomp(new_name, '/');
1714 separator = imap_get_path_separator(IMAP_FOLDER(folder), imap_path);
1715 imap_path_separator_subst(imap_path, separator);
1716 subst_char(new_name, '/', separator);
1718 if (strcmp(name, "INBOX") != 0) {
1721 gboolean exist = FALSE;
1723 argbuf = g_ptr_array_new();
1724 ok = imap_cmd_list(session, NULL, imap_path,
1726 if (ok != IMAP_SUCCESS) {
1727 log_warning(_("can't create mailbox: LIST failed\n"));
1730 ptr_array_free_strings(argbuf);
1731 g_ptr_array_free(argbuf, TRUE);
1735 for (i = 0; i < argbuf->len; i++) {
1737 str = g_ptr_array_index(argbuf, i);
1738 if (!strncmp(str, "LIST ", 5)) {
1743 ptr_array_free_strings(argbuf);
1744 g_ptr_array_free(argbuf, TRUE);
1747 ok = imap_cmd_create(session, imap_path);
1748 if (ok != IMAP_SUCCESS) {
1749 log_warning(_("can't create mailbox\n"));
1757 new_item = folder_item_new(folder, new_name, dirpath);
1758 folder_item_append(parent, new_item);
1762 dirpath = folder_item_get_path(new_item);
1763 if (!is_dir_exist(dirpath))
1764 make_dir_hier(dirpath);
1770 static gint imap_rename_folder(Folder *folder, FolderItem *item,
1775 gchar *real_oldpath;
1776 gchar *real_newpath;
1778 gchar *old_cache_dir;
1779 gchar *new_cache_dir;
1780 IMAPSession *session;
1783 gint exists, recent, unseen;
1784 guint32 uid_validity;
1786 g_return_val_if_fail(folder != NULL, -1);
1787 g_return_val_if_fail(item != NULL, -1);
1788 g_return_val_if_fail(item->path != NULL, -1);
1789 g_return_val_if_fail(name != NULL, -1);
1791 if (strchr(name, imap_get_path_separator(IMAP_FOLDER(folder), item->path)) != NULL) {
1792 g_warning(_("New folder name must not contain the namespace "
1797 session = imap_session_get(folder);
1798 if (!session) return -1;
1800 real_oldpath = imap_get_real_path(IMAP_FOLDER(folder), item->path);
1802 g_free(session->mbox);
1803 session->mbox = NULL;
1804 ok = imap_cmd_examine(session, "INBOX",
1805 &exists, &recent, &unseen, &uid_validity);
1806 if (ok != IMAP_SUCCESS) {
1807 g_free(real_oldpath);
1811 separator = imap_get_path_separator(IMAP_FOLDER(folder), item->path);
1812 if (strchr(item->path, G_DIR_SEPARATOR)) {
1813 dirpath = g_path_get_dirname(item->path);
1814 newpath = g_strconcat(dirpath, G_DIR_SEPARATOR_S, name, NULL);
1817 newpath = g_strdup(name);
1819 real_newpath = imap_utf8_to_modified_utf7(newpath);
1820 imap_path_separator_subst(real_newpath, separator);
1822 ok = imap_cmd_rename(session, real_oldpath, real_newpath);
1823 if (ok != IMAP_SUCCESS) {
1824 log_warning(_("can't rename mailbox: %s to %s\n"),
1825 real_oldpath, real_newpath);
1826 g_free(real_oldpath);
1828 g_free(real_newpath);
1833 item->name = g_strdup(name);
1835 old_cache_dir = folder_item_get_path(item);
1837 paths[0] = g_strdup(item->path);
1839 g_node_traverse(item->node, G_PRE_ORDER, G_TRAVERSE_ALL, -1,
1840 imap_rename_folder_func, paths);
1842 if (is_dir_exist(old_cache_dir)) {
1843 new_cache_dir = folder_item_get_path(item);
1844 if (rename(old_cache_dir, new_cache_dir) < 0) {
1845 FILE_OP_ERROR(old_cache_dir, "rename");
1847 g_free(new_cache_dir);
1850 g_free(old_cache_dir);
1853 g_free(real_oldpath);
1854 g_free(real_newpath);
1859 static gint imap_remove_folder(Folder *folder, FolderItem *item)
1862 IMAPSession *session;
1865 gint exists, recent, unseen;
1866 guint32 uid_validity;
1868 g_return_val_if_fail(folder != NULL, -1);
1869 g_return_val_if_fail(item != NULL, -1);
1870 g_return_val_if_fail(item->path != NULL, -1);
1872 session = imap_session_get(folder);
1873 if (!session) return -1;
1875 path = imap_get_real_path(IMAP_FOLDER(folder), item->path);
1877 ok = imap_cmd_examine(session, "INBOX",
1878 &exists, &recent, &unseen, &uid_validity);
1879 if (ok != IMAP_SUCCESS) {
1884 ok = imap_cmd_delete(session, path);
1885 if (ok != IMAP_SUCCESS) {
1886 log_warning(_("can't delete mailbox\n"));
1892 cache_dir = folder_item_get_path(item);
1893 if (is_dir_exist(cache_dir) && remove_dir_recursive(cache_dir) < 0)
1894 g_warning("can't remove directory '%s'\n", cache_dir);
1896 folder_item_remove(item);
1901 static GSList *imap_get_uncached_messages(IMAPSession *session,
1903 MsgNumberList *numlist)
1906 GSList *newlist = NULL;
1907 GSList *llast = NULL;
1910 GSList *seq_list, *cur;
1913 g_return_val_if_fail(session != NULL, NULL);
1914 g_return_val_if_fail(item != NULL, NULL);
1915 g_return_val_if_fail(item->folder != NULL, NULL);
1916 g_return_val_if_fail(FOLDER_CLASS(item->folder) == &imap_class, NULL);
1918 seq_list = imap_get_seq_set_from_numlist(numlist);
1919 for (cur = seq_list; cur != NULL; cur = g_slist_next(cur)) {
1920 imapset = cur->data;
1922 if (imap_cmd_envelope(session, imapset)
1924 log_warning(_("can't get envelope\n"));
1928 str = g_string_new(NULL);
1931 if ((tmp = imap_getline(SESSION(session)->sock)) == NULL) {
1932 log_warning(_("error occurred while getting envelope.\n"));
1933 g_string_free(str, TRUE);
1937 if (tmp[0] != '*' || tmp[1] != ' ') {
1938 log_print("IMAP4< %s\n", tmp);
1942 if (strstr(tmp, "FETCH") == NULL) {
1943 log_print("IMAP4< %s\n", tmp);
1947 log_print("IMAP4< %s\n", tmp);
1948 g_string_assign(str, tmp);
1951 msginfo = imap_parse_envelope
1952 (SESSION(session)->sock, item, str);
1954 log_warning(_("can't parse envelope: %s\n"), str->str);
1957 if (item->stype == F_QUEUE) {
1958 MSG_SET_TMP_FLAGS(msginfo->flags, MSG_QUEUED);
1959 } else if (item->stype == F_DRAFT) {
1960 MSG_SET_TMP_FLAGS(msginfo->flags, MSG_DRAFT);
1963 msginfo->folder = item;
1966 llast = newlist = g_slist_append(newlist, msginfo);
1968 llast = g_slist_append(llast, msginfo);
1969 llast = llast->next;
1973 g_string_free(str, TRUE);
1975 imap_seq_set_free(seq_list);
1977 session_set_access_time(SESSION(session));
1982 static void imap_delete_all_cached_messages(FolderItem *item)
1986 g_return_if_fail(item != NULL);
1987 g_return_if_fail(item->folder != NULL);
1988 g_return_if_fail(FOLDER_CLASS(item->folder) == &imap_class);
1990 debug_print("Deleting all cached messages...\n");
1992 dir = folder_item_get_path(item);
1993 if (is_dir_exist(dir))
1994 remove_all_numbered_files(dir);
1997 debug_print("done.\n");
2001 static SockInfo *imap_open_tunnel(const gchar *server,
2002 const gchar *tunnelcmd,
2005 static SockInfo *imap_open_tunnel(const gchar *server,
2006 const gchar *tunnelcmd)
2011 if ((sock = sock_connect_cmd(server, tunnelcmd)) == NULL) {
2012 log_warning(_("Can't establish IMAP4 session with: %s\n"),
2017 return imap_init_sock(sock, ssl_type);
2019 return imap_init_sock(sock);
2025 void *imap_open_thread(void *data)
2027 SockInfo *sock = NULL;
2028 thread_data *td = (thread_data *)data;
2029 if ((sock = sock_connect(td->server, td->port)) == NULL) {
2030 log_warning(_("Can't connect to IMAP4 server: %s:%d\n"),
2031 td->server, td->port);
2037 if (td->ssl_type == SSL_TUNNEL && !ssl_init_socket(sock)) {
2038 log_warning(_("Can't establish IMAP4 session with: %s:%d\n"),
2039 td->server, td->port);
2052 static SockInfo *imap_open_blocking(const gchar *server, gushort port,
2055 static SockInfo *imap_open_blocking(const gchar *server, gushort port)
2059 if ((sock = sock_connect(server, port)) == NULL) {
2060 log_warning(_("Can't connect to IMAP4 server: %s:%d\n"),
2066 if (ssl_type == SSL_TUNNEL && !ssl_init_socket(sock)) {
2067 log_warning(_("Can't establish IMAP4 session with: %s:%d\n"),
2078 static SockInfo *imap_open(const gchar *server, gushort port,
2081 static SockInfo *imap_open(const gchar *server, gushort port)
2084 #if (defined USE_PTHREAD && defined __GLIBC__ && (__GLIBC__ > 2 || (__GLIBC__ == 2 && __GLIBC_MINOR__ >= 3)))
2085 /* non blocking stuff */
2086 thread_data *td = g_new0(thread_data, 1);
2088 SockInfo *sock = NULL;
2091 td->ssl_type = ssl_type;
2093 td->server = g_strdup(server);
2097 statusbar_print_all(_("Connecting to IMAP4 server: %s..."), server);
2099 debug_print("creating imap_open_thread...\n");
2100 if (pthread_create(&pt, PTHREAD_CREATE_JOINABLE,
2101 imap_open_thread, td) != 0) {
2102 statusbar_pop_all();
2106 return imap_open_blocking(server, port, ssl_type);
2108 return imap_open_blocking(server, port);
2112 debug_print("waiting for imap_open_thread...\n");
2114 /* don't let the interface freeze while waiting */
2118 /* get the thread's return value and clean its resources */
2119 pthread_join(pt, (void *)&sock);
2123 debug_print("imap_open_thread returned %p\n", sock);
2124 statusbar_pop_all();
2128 return imap_open_blocking(server, port, ssl_type);
2130 return imap_open_blocking(server, port);
2136 static SockInfo *imap_init_sock(SockInfo *sock, SSLType ssl_type)
2138 static SockInfo *imap_init_sock(SockInfo *sock)
2145 static GList *imap_parse_namespace_str(gchar *str)
2150 IMAPNameSpace *namespace;
2151 GList *ns_list = NULL;
2153 while (*p != '\0') {
2154 /* parse ("#foo" "/") */
2156 while (*p && *p != '(') p++;
2157 if (*p == '\0') break;
2160 while (*p && *p != '"') p++;
2161 if (*p == '\0') break;
2165 while (*p && *p != '"') p++;
2166 if (*p == '\0') break;
2170 while (*p && isspace(*p)) p++;
2171 if (*p == '\0') break;
2172 if (strncmp(p, "NIL", 3) == 0)
2174 else if (*p == '"') {
2177 while (*p && *p != '"') p++;
2178 if (*p == '\0') break;
2183 while (*p && *p != ')') p++;
2184 if (*p == '\0') break;
2187 namespace = g_new(IMAPNameSpace, 1);
2188 namespace->name = g_strdup(name);
2189 namespace->separator = separator ? separator[0] : '\0';
2190 ns_list = g_list_append(ns_list, namespace);
2196 static void imap_parse_namespace(IMAPSession *session, IMAPFolder *folder)
2201 g_return_if_fail(session != NULL);
2202 g_return_if_fail(folder != NULL);
2204 if (folder->ns_personal != NULL ||
2205 folder->ns_others != NULL ||
2206 folder->ns_shared != NULL)
2209 if (!imap_has_capability(session, "NAMESPACE")) {
2210 imap_get_namespace_by_list(session, folder);
2214 if (imap_cmd_namespace(session, &ns_str)
2216 log_warning(_("can't get namespace\n"));
2220 str_array = strsplit_parenthesis(ns_str, '(', ')', 3);
2221 if (str_array == NULL) {
2223 imap_get_namespace_by_list(session, folder);
2227 folder->ns_personal = imap_parse_namespace_str(str_array[0]);
2228 if (str_array[0] && str_array[1])
2229 folder->ns_others = imap_parse_namespace_str(str_array[1]);
2230 if (str_array[0] && str_array[1] && str_array[2])
2231 folder->ns_shared = imap_parse_namespace_str(str_array[2]);
2232 g_strfreev(str_array);
2236 static void imap_get_namespace_by_list(IMAPSession *session, IMAPFolder *folder)
2238 GSList *item_list, *cur;
2239 gchar separator = '\0';
2240 IMAPNameSpace *namespace;
2242 g_return_if_fail(session != NULL);
2243 g_return_if_fail(folder != NULL);
2245 if (folder->ns_personal != NULL ||
2246 folder->ns_others != NULL ||
2247 folder->ns_shared != NULL)
2250 imap_gen_send(session, "LIST \"\" \"\"");
2251 item_list = imap_parse_list(folder, session, "", &separator);
2252 for (cur = item_list; cur != NULL; cur = cur->next)
2253 folder_item_destroy(FOLDER_ITEM(cur->data));
2254 g_slist_free(item_list);
2256 namespace = g_new(IMAPNameSpace, 1);
2257 namespace->name = g_strdup("");
2258 namespace->separator = separator;
2259 folder->ns_personal = g_list_append(NULL, namespace);
2262 static IMAPNameSpace *imap_find_namespace_from_list(GList *ns_list,
2265 IMAPNameSpace *namespace = NULL;
2266 gchar *tmp_path, *name;
2268 if (!path) path = "";
2270 for (; ns_list != NULL; ns_list = ns_list->next) {
2271 IMAPNameSpace *tmp_ns = ns_list->data;
2273 Xstrcat_a(tmp_path, path, "/", return namespace);
2274 Xstrdup_a(name, tmp_ns->name, return namespace);
2275 if (tmp_ns->separator && tmp_ns->separator != '/') {
2276 subst_char(tmp_path, tmp_ns->separator, '/');
2277 subst_char(name, tmp_ns->separator, '/');
2279 if (strncmp(tmp_path, name, strlen(name)) == 0)
2286 static IMAPNameSpace *imap_find_namespace(IMAPFolder *folder,
2289 IMAPNameSpace *namespace;
2291 g_return_val_if_fail(folder != NULL, NULL);
2293 namespace = imap_find_namespace_from_list(folder->ns_personal, path);
2294 if (namespace) return namespace;
2295 namespace = imap_find_namespace_from_list(folder->ns_others, path);
2296 if (namespace) return namespace;
2297 namespace = imap_find_namespace_from_list(folder->ns_shared, path);
2298 if (namespace) return namespace;
2303 static gchar imap_get_path_separator(IMAPFolder *folder, const gchar *path)
2305 IMAPNameSpace *namespace;
2306 gchar separator = '/';
2308 namespace = imap_find_namespace(folder, path);
2309 if (namespace && namespace->separator)
2310 separator = namespace->separator;
2315 static gchar *imap_get_real_path(IMAPFolder *folder, const gchar *path)
2320 g_return_val_if_fail(folder != NULL, NULL);
2321 g_return_val_if_fail(path != NULL, NULL);
2323 real_path = imap_utf8_to_modified_utf7(path);
2324 separator = imap_get_path_separator(folder, path);
2325 imap_path_separator_subst(real_path, separator);
2330 static gchar *imap_parse_atom(SockInfo *sock, gchar *src,
2331 gchar *dest, gint dest_len, GString *str)
2333 gchar *cur_pos = src;
2336 g_return_val_if_fail(str != NULL, cur_pos);
2338 /* read the next line if the current response buffer is empty */
2339 while (isspace(*(guchar *)cur_pos)) cur_pos++;
2340 while (*cur_pos == '\0') {
2341 if ((nextline = imap_getline(sock)) == NULL)
2343 g_string_assign(str, nextline);
2345 strretchomp(nextline);
2346 /* log_print("IMAP4< %s\n", nextline); */
2347 debug_print("IMAP4< %s\n", nextline);
2350 while (isspace(*(guchar *)cur_pos)) cur_pos++;
2353 if (!strncmp(cur_pos, "NIL", 3)) {
2356 } else if (*cur_pos == '\"') {
2359 p = get_quoted(cur_pos, '\"', dest, dest_len);
2360 cur_pos = p ? p : cur_pos + 2;
2361 } else if (*cur_pos == '{') {
2366 cur_pos = strchr_cpy(cur_pos + 1, '}', buf, sizeof(buf));
2368 g_return_val_if_fail(len >= 0, cur_pos);
2370 g_string_truncate(str, 0);
2374 if ((nextline = imap_getline(sock)) == NULL)
2376 line_len += strlen(nextline);
2377 g_string_append(str, nextline);
2379 strretchomp(nextline);
2380 /* log_print("IMAP4< %s\n", nextline); */
2381 debug_print("IMAP4< %s\n", nextline);
2383 } while (line_len < len);
2385 memcpy(dest, cur_pos, MIN(len, dest_len - 1));
2386 dest[MIN(len, dest_len - 1)] = '\0';
2393 static gchar *imap_get_header(SockInfo *sock, gchar *cur_pos, gchar **headers,
2403 g_return_val_if_fail(str != NULL, cur_pos);
2405 while (isspace(*(guchar *)cur_pos)) cur_pos++;
2407 g_return_val_if_fail(*cur_pos == '{', cur_pos);
2409 cur_pos = strchr_cpy(cur_pos + 1, '}', buf, sizeof(buf));
2411 g_return_val_if_fail(len >= 0, cur_pos);
2413 g_string_truncate(str, 0);
2417 if ((nextline = imap_getline(sock)) == NULL)
2419 block_len += strlen(nextline);
2420 g_string_append(str, nextline);
2422 strretchomp(nextline);
2423 /* debug_print("IMAP4< %s\n", nextline); */
2425 } while (block_len < len);
2427 debug_print("IMAP4< [contents of BODY.PEEK[HEADER.FIELDS (...)]]\n");
2429 *headers = g_strndup(cur_pos, len);
2432 while (isspace(*(guchar *)cur_pos)) cur_pos++;
2433 while (*cur_pos == '\0') {
2434 if ((nextline = imap_getline(sock)) == NULL)
2436 g_string_assign(str, nextline);
2438 strretchomp(nextline);
2439 debug_print("IMAP4< %s\n", nextline);
2442 while (isspace(*(guchar *)cur_pos)) cur_pos++;
2448 static MsgFlags imap_parse_flags(const gchar *flag_str)
2450 const gchar *p = flag_str;
2451 MsgFlags flags = {0, 0};
2453 flags.perm_flags = MSG_UNREAD;
2455 while ((p = strchr(p, '\\')) != NULL) {
2458 if (g_ascii_strncasecmp(p, "Recent", 6) == 0 && MSG_IS_UNREAD(flags)) {
2459 MSG_SET_PERM_FLAGS(flags, MSG_NEW);
2460 } else if (g_ascii_strncasecmp(p, "Seen", 4) == 0) {
2461 MSG_UNSET_PERM_FLAGS(flags, MSG_NEW|MSG_UNREAD);
2462 } else if (g_ascii_strncasecmp(p, "Deleted", 7) == 0) {
2463 MSG_SET_PERM_FLAGS(flags, MSG_DELETED);
2464 } else if (g_ascii_strncasecmp(p, "Flagged", 7) == 0) {
2465 MSG_SET_PERM_FLAGS(flags, MSG_MARKED);
2466 } else if (g_ascii_strncasecmp(p, "Answered", 8) == 0) {
2467 MSG_SET_PERM_FLAGS(flags, MSG_REPLIED);
2474 static MsgInfo *imap_parse_envelope(SockInfo *sock, FolderItem *item,
2477 gchar buf[IMAPBUFSIZE];
2478 MsgInfo *msginfo = NULL;
2483 MsgFlags flags = {0, 0}, imap_flags = {0, 0};
2485 g_return_val_if_fail(line_str != NULL, NULL);
2486 g_return_val_if_fail(line_str->str[0] == '*' &&
2487 line_str->str[1] == ' ', NULL);
2489 MSG_SET_TMP_FLAGS(flags, MSG_IMAP);
2490 if (item->stype == F_QUEUE) {
2491 MSG_SET_TMP_FLAGS(flags, MSG_QUEUED);
2492 } else if (item->stype == F_DRAFT) {
2493 MSG_SET_TMP_FLAGS(flags, MSG_DRAFT);
2496 cur_pos = line_str->str + 2;
2498 #define PARSE_ONE_ELEMENT(ch) \
2500 cur_pos = strchr_cpy(cur_pos, ch, buf, sizeof(buf)); \
2501 if (cur_pos == NULL) { \
2502 g_warning("cur_pos == NULL\n"); \
2503 procmsg_msginfo_free(msginfo); \
2508 PARSE_ONE_ELEMENT(' ');
2511 PARSE_ONE_ELEMENT(' ');
2512 g_return_val_if_fail(!strcmp(buf, "FETCH"), NULL);
2514 g_return_val_if_fail(*cur_pos == '(', NULL);
2517 while (*cur_pos != '\0' && *cur_pos != ')') {
2518 while (*cur_pos == ' ') cur_pos++;
2520 if (!strncmp(cur_pos, "UID ", 4)) {
2522 uid = strtoul(cur_pos, &cur_pos, 10);
2523 } else if (!strncmp(cur_pos, "FLAGS ", 6)) {
2525 if (*cur_pos != '(') {
2526 g_warning("*cur_pos != '('\n");
2527 procmsg_msginfo_free(msginfo);
2531 PARSE_ONE_ELEMENT(')');
2532 imap_flags = imap_parse_flags(buf);
2533 } else if (!strncmp(cur_pos, "RFC822.SIZE ", 12)) {
2535 size = strtol(cur_pos, &cur_pos, 10);
2536 } else if (!strncmp(cur_pos, "BODY[HEADER.FIELDS ", 19)) {
2540 if (*cur_pos != '(') {
2541 g_warning("*cur_pos != '('\n");
2542 procmsg_msginfo_free(msginfo);
2546 PARSE_ONE_ELEMENT(')');
2547 if (*cur_pos != ']') {
2548 g_warning("*cur_pos != ']'\n");
2549 procmsg_msginfo_free(msginfo);
2554 cur_pos = imap_get_header(sock, cur_pos, &headers,
2556 msginfo = procheader_parse_str(headers, flags, FALSE, FALSE);
2559 g_warning("invalid FETCH response: %s\n", cur_pos);
2565 msginfo->msgnum = uid;
2566 msginfo->size = size;
2567 msginfo->flags.tmp_flags |= imap_flags.tmp_flags;
2568 msginfo->flags.perm_flags = imap_flags.perm_flags;
2574 static gchar *imap_get_flag_str(IMAPFlags flags)
2579 str = g_string_new(NULL);
2581 if (IMAP_IS_SEEN(flags)) g_string_append(str, "\\Seen ");
2582 if (IMAP_IS_ANSWERED(flags)) g_string_append(str, "\\Answered ");
2583 if (IMAP_IS_FLAGGED(flags)) g_string_append(str, "\\Flagged ");
2584 if (IMAP_IS_DELETED(flags)) g_string_append(str, "\\Deleted ");
2585 if (IMAP_IS_DRAFT(flags)) g_string_append(str, "\\Draft");
2587 if (str->len > 0 && str->str[str->len - 1] == ' ')
2588 g_string_truncate(str, str->len - 1);
2591 g_string_free(str, FALSE);
2596 static gint imap_set_message_flags(IMAPSession *session,
2597 MsgNumberList *numlist,
2604 GSList *seq_list, *cur;
2607 flag_str = imap_get_flag_str(flags);
2608 cmd = g_strconcat(is_set ? "+FLAGS.SILENT (" : "-FLAGS.SILENT (",
2609 flag_str, ")", NULL);
2612 seq_list = imap_get_seq_set_from_numlist(numlist);
2613 for (cur = seq_list; cur != NULL; cur = g_slist_next(cur)) {
2614 imapset = cur->data;
2616 ok = imap_cmd_store(session, imapset, cmd);
2618 imap_seq_set_free(seq_list);
2624 static gint imap_select(IMAPSession *session, IMAPFolder *folder,
2626 gint *exists, gint *recent, gint *unseen,
2627 guint32 *uid_validity)
2631 gint exists_, recent_, unseen_;
2632 guint32 uid_validity_;
2634 if (!exists || !recent || !unseen || !uid_validity) {
2635 if (session->mbox && strcmp(session->mbox, path) == 0)
2636 return IMAP_SUCCESS;
2640 uid_validity = &uid_validity_;
2643 g_free(session->mbox);
2644 session->mbox = NULL;
2646 real_path = imap_get_real_path(folder, path);
2647 ok = imap_cmd_select(session, real_path,
2648 exists, recent, unseen, uid_validity);
2649 if (ok != IMAP_SUCCESS)
2650 log_warning(_("can't select folder: %s\n"), real_path);
2652 session->mbox = g_strdup(path);
2653 session->folder_content_changed = FALSE;
2660 #define THROW(err) { ok = err; goto catch; }
2662 static gint imap_status(IMAPSession *session, IMAPFolder *folder,
2664 gint *messages, gint *recent,
2665 guint32 *uid_next, guint32 *uid_validity,
2671 GPtrArray *argbuf = NULL;
2674 if (messages && recent && uid_next && uid_validity && unseen) {
2675 *messages = *recent = *uid_next = *uid_validity = *unseen = 0;
2676 argbuf = g_ptr_array_new();
2679 real_path = imap_get_real_path(folder, path);
2680 QUOTE_IF_REQUIRED(real_path_, real_path);
2681 imap_gen_send(session, "STATUS %s "
2682 "(MESSAGES RECENT UIDNEXT UIDVALIDITY UNSEEN)",
2685 ok = imap_cmd_ok(session, argbuf);
2686 if (ok != IMAP_SUCCESS || !argbuf) THROW(ok);
2688 str = search_array_str(argbuf, "STATUS");
2689 if (!str) THROW(IMAP_ERROR);
2691 str = strchr(str, '(');
2692 if (!str) THROW(IMAP_ERROR);
2694 while (*str != '\0' && *str != ')') {
2695 while (*str == ' ') str++;
2697 if (!strncmp(str, "MESSAGES ", 9)) {
2699 *messages = strtol(str, &str, 10);
2700 } else if (!strncmp(str, "RECENT ", 7)) {
2702 *recent = strtol(str, &str, 10);
2703 } else if (!strncmp(str, "UIDNEXT ", 8)) {
2705 *uid_next = strtoul(str, &str, 10);
2706 } else if (!strncmp(str, "UIDVALIDITY ", 12)) {
2708 *uid_validity = strtoul(str, &str, 10);
2709 } else if (!strncmp(str, "UNSEEN ", 7)) {
2711 *unseen = strtol(str, &str, 10);
2713 g_warning("invalid STATUS response: %s\n", str);
2721 ptr_array_free_strings(argbuf);
2722 g_ptr_array_free(argbuf, TRUE);
2730 static gboolean imap_has_capability(IMAPSession *session, const gchar *cap)
2734 for (p = session->capability; *p != NULL; ++p) {
2735 if (!g_ascii_strcasecmp(*p, cap))
2742 static void imap_free_capabilities(IMAPSession *session)
2744 g_strfreev(session->capability);
2745 session->capability = NULL;
2748 /* low-level IMAP4rev1 commands */
2750 static gint imap_cmd_authenticate(IMAPSession *session, const gchar *user,
2751 const gchar *pass, IMAPAuthType type)
2758 gchar hexdigest[33];
2762 auth_type = "CRAM-MD5";
2764 imap_gen_send(session, "AUTHENTICATE %s", auth_type);
2765 ok = imap_gen_recv(session, &buf);
2766 if (ok != IMAP_SUCCESS || buf[0] != '+' || buf[1] != ' ') {
2771 challenge = g_malloc(strlen(buf + 2) + 1);
2772 challenge_len = base64_decode(challenge, buf + 2, -1);
2773 challenge[challenge_len] = '\0';
2775 log_print("IMAP< [Decoded: %s]\n", challenge);
2777 md5_hex_hmac(hexdigest, challenge, challenge_len, pass, strlen(pass));
2780 response = g_strdup_printf("%s %s", user, hexdigest);
2781 log_print("IMAP> [Encoded: %s]\n", response);
2782 response64 = g_malloc((strlen(response) + 3) * 2 + 1);
2783 base64_encode(response64, response, strlen(response));
2786 log_print("IMAP> %s\n", response64);
2787 sock_puts(SESSION(session)->sock, response64);
2788 ok = imap_cmd_ok(session, NULL);
2789 if (ok != IMAP_SUCCESS)
2790 log_warning(_("IMAP4 authentication failed.\n"));
2795 static gint imap_cmd_login(IMAPSession *session,
2796 const gchar *user, const gchar *pass)
2798 gchar *user_, *pass_;
2801 QUOTE_IF_REQUIRED(user_, user);
2802 QUOTE_IF_REQUIRED(pass_, pass);
2803 imap_gen_send(session, "LOGIN %s %s", user_, pass_);
2805 ok = imap_cmd_ok(session, NULL);
2806 if (ok != IMAP_SUCCESS)
2807 log_warning(_("IMAP4 login failed.\n"));
2812 static gint imap_cmd_logout(IMAPSession *session)
2814 imap_gen_send(session, "LOGOUT");
2815 return imap_cmd_ok(session, NULL);
2818 static gint imap_cmd_noop(IMAPSession *session)
2820 imap_gen_send(session, "NOOP");
2821 return imap_cmd_ok(session, NULL);
2825 static gint imap_cmd_starttls(IMAPSession *session)
2827 imap_gen_send(session, "STARTTLS");
2828 return imap_cmd_ok(session, NULL);
2832 #define THROW(err) { ok = err; goto catch; }
2834 static gint imap_cmd_namespace(IMAPSession *session, gchar **ns_str)
2840 argbuf = g_ptr_array_new();
2842 imap_gen_send(session, "NAMESPACE");
2843 if ((ok = imap_cmd_ok(session, argbuf)) != IMAP_SUCCESS) THROW(ok);
2845 str = search_array_str(argbuf, "NAMESPACE");
2846 if (!str) THROW(IMAP_ERROR);
2848 *ns_str = g_strdup(str);
2851 ptr_array_free_strings(argbuf);
2852 g_ptr_array_free(argbuf, TRUE);
2859 static gint imap_cmd_list(IMAPSession *session, const gchar *ref,
2860 const gchar *mailbox, GPtrArray *argbuf)
2862 gchar *ref_, *mailbox_;
2864 if (!ref) ref = "\"\"";
2865 if (!mailbox) mailbox = "\"\"";
2867 QUOTE_IF_REQUIRED(ref_, ref);
2868 QUOTE_IF_REQUIRED(mailbox_, mailbox);
2869 imap_gen_send(session, "LIST %s %s", ref_, mailbox_);
2871 return imap_cmd_ok(session, argbuf);
2874 #define THROW goto catch
2876 static gint imap_cmd_do_select(IMAPSession *session, const gchar *folder,
2878 gint *exists, gint *recent, gint *unseen,
2879 guint32 *uid_validity)
2886 unsigned int uid_validity_;
2888 *exists = *recent = *unseen = *uid_validity = 0;
2889 argbuf = g_ptr_array_new();
2892 select_cmd = "EXAMINE";
2894 select_cmd = "SELECT";
2896 QUOTE_IF_REQUIRED(folder_, folder);
2897 imap_gen_send(session, "%s %s", select_cmd, folder_);
2899 if ((ok = imap_cmd_ok(session, argbuf)) != IMAP_SUCCESS) THROW;
2901 resp_str = search_array_contain_str(argbuf, "EXISTS");
2903 if (sscanf(resp_str,"%d EXISTS", exists) != 1) {
2904 g_warning("imap_cmd_select(): invalid EXISTS line.\n");
2909 resp_str = search_array_contain_str(argbuf, "RECENT");
2911 if (sscanf(resp_str, "%d RECENT", recent) != 1) {
2912 g_warning("imap_cmd_select(): invalid RECENT line.\n");
2917 resp_str = search_array_contain_str(argbuf, "UIDVALIDITY");
2919 if (sscanf(resp_str, "OK [UIDVALIDITY %u] ", &uid_validity_)
2921 g_warning("imap_cmd_select(): invalid UIDVALIDITY line.\n");
2924 *uid_validity = uid_validity_;
2927 resp_str = search_array_contain_str(argbuf, "UNSEEN");
2929 if (sscanf(resp_str, "OK [UNSEEN %d] ", unseen) != 1) {
2930 g_warning("imap_cmd_select(): invalid UNSEEN line.\n");
2936 ptr_array_free_strings(argbuf);
2937 g_ptr_array_free(argbuf, TRUE);
2942 static gint imap_cmd_select(IMAPSession *session, const gchar *folder,
2943 gint *exists, gint *recent, gint *unseen,
2944 guint32 *uid_validity)
2946 return imap_cmd_do_select(session, folder, FALSE,
2947 exists, recent, unseen, uid_validity);
2950 static gint imap_cmd_examine(IMAPSession *session, const gchar *folder,
2951 gint *exists, gint *recent, gint *unseen,
2952 guint32 *uid_validity)
2954 return imap_cmd_do_select(session, folder, TRUE,
2955 exists, recent, unseen, uid_validity);
2960 static gint imap_cmd_create(IMAPSession *session, const gchar *folder)
2964 QUOTE_IF_REQUIRED(folder_, folder);
2965 imap_gen_send(session, "CREATE %s", folder_);
2967 return imap_cmd_ok(session, NULL);
2970 static gint imap_cmd_rename(IMAPSession *session, const gchar *old_folder,
2971 const gchar *new_folder)
2973 gchar *old_folder_, *new_folder_;
2975 QUOTE_IF_REQUIRED(old_folder_, old_folder);
2976 QUOTE_IF_REQUIRED(new_folder_, new_folder);
2977 imap_gen_send(session, "RENAME %s %s", old_folder_, new_folder_);
2979 return imap_cmd_ok(session, NULL);
2982 static gint imap_cmd_delete(IMAPSession *session, const gchar *folder)
2986 QUOTE_IF_REQUIRED(folder_, folder);
2987 imap_gen_send(session, "DELETE %s", folder_);
2989 return imap_cmd_ok(session, NULL);
2992 static gint imap_cmd_search(IMAPSession *session, const gchar *criteria,
2999 g_return_val_if_fail(criteria != NULL, IMAP_ERROR);
3000 g_return_val_if_fail(list != NULL, IMAP_ERROR);
3004 argbuf = g_ptr_array_new();
3005 imap_gen_send(session, "UID SEARCH %s", criteria);
3007 ok = imap_cmd_ok(session, argbuf);
3008 if (ok != IMAP_SUCCESS) {
3009 ptr_array_free_strings(argbuf);
3010 g_ptr_array_free(argbuf, TRUE);
3014 if ((uidlist = search_array_str(argbuf, "SEARCH ")) != NULL) {
3015 gchar **strlist, **p;
3017 strlist = g_strsplit(uidlist + 7, " ", 0);
3018 for (p = strlist; *p != NULL; ++p) {
3021 if (sscanf(*p, "%u", &msgnum) == 1)
3022 *list = g_slist_append(*list, GINT_TO_POINTER(msgnum));
3024 g_strfreev(strlist);
3026 ptr_array_free_strings(argbuf);
3027 g_ptr_array_free(argbuf, TRUE);
3029 return IMAP_SUCCESS;
3032 static gint imap_cmd_fetch(IMAPSession *session, guint32 uid,
3033 const gchar *filename)
3041 g_return_val_if_fail(filename != NULL, IMAP_ERROR);
3043 imap_gen_send(session, "UID FETCH %d BODY.PEEK[]", uid);
3045 while ((ok = imap_gen_recv(session, &buf)) == IMAP_SUCCESS) {
3046 if (buf[0] != '*' || buf[1] != ' ') {
3050 if (strstr(buf, "FETCH") != NULL) break;
3053 if (ok != IMAP_SUCCESS) {
3058 #define RETURN_ERROR_IF_FAIL(cond) \
3061 return IMAP_ERROR; \
3064 cur_pos = strchr(buf, '{');
3065 RETURN_ERROR_IF_FAIL(cur_pos != NULL);
3066 cur_pos = strchr_cpy(cur_pos + 1, '}', size_str, sizeof(size_str));
3067 RETURN_ERROR_IF_FAIL(cur_pos != NULL);
3068 size_num = atol(size_str);
3069 RETURN_ERROR_IF_FAIL(size_num >= 0);
3071 RETURN_ERROR_IF_FAIL(*cur_pos == '\0');
3073 #undef RETURN_ERROR_IF_FAIL
3077 if (recv_bytes_write_to_file(SESSION(session)->sock,
3078 size_num, filename) != 0)
3081 if (imap_gen_recv(session, &buf) != IMAP_SUCCESS) {
3086 if (buf[0] == '\0' || buf[strlen(buf) - 1] != ')') {
3092 ok = imap_cmd_ok(session, NULL);
3097 static gint imap_cmd_append(IMAPSession *session, const gchar *destfolder,
3098 const gchar *file, IMAPFlags flags,
3105 unsigned int new_uid_;
3107 gchar buf[BUFFSIZE];
3112 g_return_val_if_fail(file != NULL, IMAP_ERROR);
3114 size = get_file_size_as_crlf(file);
3115 if ((fp = fopen(file, "rb")) == NULL) {
3116 FILE_OP_ERROR(file, "fopen");
3119 QUOTE_IF_REQUIRED(destfolder_, destfolder);
3120 flag_str = imap_get_flag_str(flags);
3121 imap_gen_send(session, "APPEND %s (%s) {%d}",
3122 destfolder_, flag_str, size);
3125 ok = imap_gen_recv(session, &ret);
3126 if (ok != IMAP_SUCCESS || ret[0] != '+' || ret[1] != ' ') {
3127 log_warning(_("can't append %s to %s\n"), file, destfolder_);
3134 log_print("IMAP4> %s\n", "(sending file...)");
3136 while (fgets(buf, sizeof(buf), fp) != NULL) {
3138 if (sock_puts(SESSION(session)->sock, buf) < 0) {
3145 FILE_OP_ERROR(file, "fgets");
3150 sock_puts(SESSION(session)->sock, "");
3154 if (new_uid != NULL)
3157 if (new_uid != NULL && session->uidplus) {
3158 argbuf = g_ptr_array_new();
3160 ok = imap_cmd_ok(session, argbuf);
3161 if ((ok == IMAP_SUCCESS) && (argbuf->len > 0)) {
3162 resp_str = g_ptr_array_index(argbuf, argbuf->len - 1);
3164 sscanf(resp_str, "%*u OK [APPENDUID %*u %u]",
3166 *new_uid = new_uid_;
3170 ptr_array_free_strings(argbuf);
3171 g_ptr_array_free(argbuf, TRUE);
3173 ok = imap_cmd_ok(session, NULL);
3175 if (ok != IMAP_SUCCESS)
3176 log_warning(_("can't append message to %s\n"),
3182 static MsgNumberList *imapset_to_numlist(IMAPSet imapset)
3184 gchar **ranges, **range;
3185 unsigned int low, high;
3186 MsgNumberList *uids = NULL;
3188 ranges = g_strsplit(imapset, ",", 0);
3189 for (range = ranges; *range != NULL; range++) {
3190 printf("%s\n", *range);
3191 if (sscanf(*range, "%u:%u", &low, &high) == 1)
3192 uids = g_slist_prepend(uids, GINT_TO_POINTER(low));
3195 for (i = low; i <= high; i++)
3196 uids = g_slist_prepend(uids, GINT_TO_POINTER(i));
3199 uids = g_slist_reverse(uids);
3205 static gint imap_cmd_copy(IMAPSession *session, const gchar *seq_set,
3206 const gchar *destfolder, GRelation *uid_mapping)
3211 g_return_val_if_fail(session != NULL, IMAP_ERROR);
3212 g_return_val_if_fail(seq_set != NULL, IMAP_ERROR);
3213 g_return_val_if_fail(destfolder != NULL, IMAP_ERROR);
3215 QUOTE_IF_REQUIRED(destfolder_, destfolder);
3216 imap_gen_send(session, "UID COPY %s %s", seq_set, destfolder_);
3218 if (uid_mapping != NULL && session->uidplus) {
3220 gchar *resp_str = NULL, *olduids_str, *newuids_str;
3221 MsgNumberList *olduids, *old_cur, *newuids, *new_cur;
3223 reply = g_ptr_array_new();
3224 ok = imap_cmd_ok(session, reply);
3225 if ((ok == IMAP_SUCCESS) && (reply->len > 0)) {
3226 resp_str = g_ptr_array_index(reply, reply->len - 1);
3228 olduids_str = g_new0(gchar, strlen(resp_str));
3229 newuids_str = g_new0(gchar, strlen(resp_str));
3230 if (sscanf(resp_str, "%*s OK [COPYUID %*u %[0-9,:] %[0-9,:]]",
3231 olduids_str, newuids_str) == 2) {
3232 olduids = imapset_to_numlist(olduids_str);
3233 newuids = imapset_to_numlist(newuids_str);
3237 while(old_cur != NULL && new_cur != NULL) {
3238 g_relation_insert(uid_mapping,
3239 GPOINTER_TO_INT(old_cur->data),
3240 GPOINTER_TO_INT(new_cur->data));
3241 old_cur = g_slist_next(old_cur);
3242 new_cur = g_slist_next(new_cur);
3245 g_slist_free(olduids);
3246 g_slist_free(newuids);
3248 g_free(olduids_str);
3249 g_free(newuids_str);
3252 ptr_array_free_strings(reply);
3253 g_ptr_array_free(reply, TRUE);
3255 ok = imap_cmd_ok(session, NULL);
3257 if (ok != IMAP_SUCCESS)
3258 log_warning(_("can't copy %s to %s\n"), seq_set, destfolder_);
3263 gint imap_cmd_envelope(IMAPSession *session, IMAPSet set)
3265 static GString *header_fields = NULL;
3267 if (header_fields == NULL) {
3268 const HeaderEntry *headers, *elem;
3270 headers = procheader_get_headernames(FALSE);
3271 header_fields = g_string_new("");
3273 for (elem = headers; elem->name != NULL; ++elem) {
3274 gint namelen = strlen(elem->name);
3276 /* Header fields ending with space are not rfc822 headers */
3277 if (elem->name[namelen - 1] == ' ')
3280 /* strip : at the of header field */
3281 if(elem->name[namelen - 1] == ':')
3287 g_string_append_printf(header_fields, "%s%.*s",
3288 header_fields->str[0] != '\0' ? " " : "",
3289 namelen, elem->name);
3294 (session, "UID FETCH %s (UID FLAGS RFC822.SIZE BODY.PEEK[HEADER.FIELDS (%s)])",
3295 set, header_fields->str);
3297 return IMAP_SUCCESS;
3300 static gint imap_cmd_store(IMAPSession *session, IMAPSet seq_set,
3305 imap_gen_send(session, "UID STORE %s %s", seq_set, sub_cmd);
3307 if ((ok = imap_cmd_ok(session, NULL)) != IMAP_SUCCESS) {
3308 log_warning(_("error while imap command: STORE %s %s\n"),
3313 return IMAP_SUCCESS;
3316 static gint imap_cmd_expunge(IMAPSession *session, IMAPSet seq_set)
3320 if (seq_set && session->uidplus)
3321 imap_gen_send(session, "UID EXPUNGE %s", seq_set);
3323 imap_gen_send(session, "EXPUNGE");
3324 if ((ok = imap_cmd_ok(session, NULL)) != IMAP_SUCCESS) {
3325 log_warning(_("error while imap command: EXPUNGE\n"));
3329 return IMAP_SUCCESS;
3332 static gint imap_cmd_close(IMAPSession *session)
3336 imap_gen_send(session, "CLOSE");
3337 if ((ok = imap_cmd_ok(session, NULL)) != IMAP_SUCCESS)
3338 log_warning(_("error while imap command: CLOSE\n"));
3343 static gint imap_cmd_ok(IMAPSession *session, GPtrArray *argbuf)
3345 gint ok = IMAP_SUCCESS;
3350 while ((ok = imap_gen_recv(session, &buf))
3352 /* make sure data is long enough for any substring of buf */
3353 data = alloca(strlen(buf) + 1);
3355 /* untagged line read */
3356 if (buf[0] == '*' && buf[1] == ' ') {
3359 g_ptr_array_add(argbuf, g_strdup(buf + 2));
3361 if (sscanf(buf + 2, "%d %s", &num, data) >= 2) {
3362 if (!strcmp(data, "EXISTS")) {
3363 session->exists = num;
3364 session->folder_content_changed = TRUE;
3367 if(!strcmp(data, "EXPUNGE")) {
3369 session->folder_content_changed = TRUE;
3372 /* tagged line with correct tag and OK response found */
3373 } else if ((sscanf(buf, "%d %s", &cmd_num, data) >= 2) &&
3374 (cmd_num == session->cmd_count) &&
3375 !strcmp(data, "OK")) {
3377 g_ptr_array_add(argbuf, g_strdup(buf));
3379 /* everything else */
3391 static void imap_gen_send(IMAPSession *session, const gchar *format, ...)
3398 va_start(args, format);
3399 tmp = g_strdup_vprintf(format, args);
3402 session->cmd_count++;
3404 buf = g_strdup_printf("%d %s\r\n", session->cmd_count, tmp);
3405 if (!g_ascii_strncasecmp(tmp, "LOGIN ", 6) && (p = strchr(tmp + 6, ' '))) {
3407 log_print("IMAP4> %d %s ********\n", session->cmd_count, tmp);
3409 log_print("IMAP4> %d %s\n", session->cmd_count, tmp);
3411 sock_write_all(SESSION(session)->sock, buf, strlen(buf));
3416 static gint imap_gen_recv(IMAPSession *session, gchar **ret)
3418 if ((*ret = imap_getline(SESSION(session)->sock)) == NULL)
3423 log_print("IMAP4< %s\n", *ret);
3425 session_set_access_time(SESSION(session));
3427 return IMAP_SUCCESS;
3431 /* misc utility functions */
3433 static gchar *strchr_cpy(const gchar *src, gchar ch, gchar *dest, gint len)
3438 tmp = strchr(src, ch);
3442 memcpy(dest, src, MIN(tmp - src, len - 1));
3443 dest[MIN(tmp - src, len - 1)] = '\0';
3448 static gchar *get_quoted(const gchar *src, gchar ch, gchar *dest, gint len)
3450 const gchar *p = src;
3453 g_return_val_if_fail(*p == ch, NULL);
3458 while (*p != '\0' && *p != ch) {
3460 if (*p == '\\' && *(p + 1) != '\0')
3469 return (gchar *)(*p == ch ? p + 1 : p);
3472 static gchar *search_array_contain_str(GPtrArray *array, const gchar *str)
3476 for (i = 0; i < array->len; i++) {
3479 tmp = g_ptr_array_index(array, i);
3480 if (strstr(tmp, str) != NULL)
3487 static gchar *search_array_str(GPtrArray *array, const gchar *str)
3494 for (i = 0; i < array->len; i++) {
3497 tmp = g_ptr_array_index(array, i);
3498 if (!strncmp(tmp, str, len))
3505 static void imap_path_separator_subst(gchar *str, gchar separator)
3508 gboolean in_escape = FALSE;
3510 if (!separator || separator == '/') return;
3512 for (p = str; *p != '\0'; p++) {
3513 if (*p == '/' && !in_escape)
3515 else if (*p == '&' && *(p + 1) != '-' && !in_escape)
3517 else if (*p == '-' && in_escape)
3522 static gchar *imap_modified_utf7_to_utf8(const gchar *mutf7_str)
3524 static iconv_t cd = (iconv_t)-1;
3525 static gboolean iconv_ok = TRUE;
3528 size_t norm_utf7_len;
3530 gchar *to_str, *to_p;
3532 gboolean in_escape = FALSE;
3534 if (!iconv_ok) return g_strdup(mutf7_str);
3536 if (cd == (iconv_t)-1) {
3537 cd = iconv_open(CS_INTERNAL, CS_UTF_7);
3538 if (cd == (iconv_t)-1) {
3539 g_warning("iconv cannot convert UTF-7 to %s\n",
3542 return g_strdup(mutf7_str);
3546 /* modified UTF-7 to normal UTF-7 conversion */
3547 norm_utf7 = g_string_new(NULL);
3549 for (p = mutf7_str; *p != '\0'; p++) {
3550 /* replace: '&' -> '+',
3552 escaped ',' -> '/' */
3553 if (!in_escape && *p == '&') {
3554 if (*(p + 1) != '-') {
3555 g_string_append_c(norm_utf7, '+');
3558 g_string_append_c(norm_utf7, '&');
3561 } else if (in_escape && *p == ',') {
3562 g_string_append_c(norm_utf7, '/');
3563 } else if (in_escape && *p == '-') {
3564 g_string_append_c(norm_utf7, '-');
3567 g_string_append_c(norm_utf7, *p);
3571 norm_utf7_p = norm_utf7->str;
3572 norm_utf7_len = norm_utf7->len;
3573 to_len = strlen(mutf7_str) * 5;
3574 to_p = to_str = g_malloc(to_len + 1);
3576 if (iconv(cd, (ICONV_CONST gchar **)&norm_utf7_p, &norm_utf7_len,
3577 &to_p, &to_len) == -1) {
3578 g_warning(_("iconv cannot convert UTF-7 to %s\n"),
3579 conv_get_locale_charset_str());
3580 g_string_free(norm_utf7, TRUE);
3582 return g_strdup(mutf7_str);
3585 /* second iconv() call for flushing */
3586 iconv(cd, NULL, NULL, &to_p, &to_len);
3587 g_string_free(norm_utf7, TRUE);
3593 static gchar *imap_utf8_to_modified_utf7(const gchar *from)
3595 static iconv_t cd = (iconv_t)-1;
3596 static gboolean iconv_ok = TRUE;
3597 gchar *norm_utf7, *norm_utf7_p;
3598 size_t from_len, norm_utf7_len;
3600 gchar *from_tmp, *to, *p;
3601 gboolean in_escape = FALSE;
3603 if (!iconv_ok) return g_strdup(from);
3605 if (cd == (iconv_t)-1) {
3606 cd = iconv_open(CS_UTF_7, CS_INTERNAL);
3607 if (cd == (iconv_t)-1) {
3608 g_warning(_("iconv cannot convert %s to UTF-7\n"),
3611 return g_strdup(from);
3615 /* UTF-8 to normal UTF-7 conversion */
3616 Xstrdup_a(from_tmp, from, return g_strdup(from));
3617 from_len = strlen(from);
3618 norm_utf7_len = from_len * 5;
3619 Xalloca(norm_utf7, norm_utf7_len + 1, return g_strdup(from));
3620 norm_utf7_p = norm_utf7;
3622 #define IS_PRINT(ch) (isprint(ch) && IS_ASCII(ch))
3624 while (from_len > 0) {
3625 if (*from_tmp == '+') {
3626 *norm_utf7_p++ = '+';
3627 *norm_utf7_p++ = '-';
3631 } else if (IS_PRINT(*(guchar *)from_tmp)) {
3632 /* printable ascii char */
3633 *norm_utf7_p = *from_tmp;
3639 size_t conv_len = 0;
3641 /* unprintable char: convert to UTF-7 */
3643 while (!IS_PRINT(*(guchar *)p) && conv_len < from_len) {
3644 conv_len += g_utf8_skip[*(guchar *)p];
3645 p += g_utf8_skip[*(guchar *)p];
3648 from_len -= conv_len;
3649 if (iconv(cd, (ICONV_CONST gchar **)&from_tmp,
3651 &norm_utf7_p, &norm_utf7_len) == -1) {
3652 g_warning(_("iconv cannot convert UTF-8 to UTF-7\n"));
3653 return g_strdup(from);
3656 /* second iconv() call for flushing */
3657 iconv(cd, NULL, NULL, &norm_utf7_p, &norm_utf7_len);
3663 *norm_utf7_p = '\0';
3664 to_str = g_string_new(NULL);
3665 for (p = norm_utf7; p < norm_utf7_p; p++) {
3666 /* replace: '&' -> "&-",
3669 BASE64 '/' -> ',' */
3670 if (!in_escape && *p == '&') {
3671 g_string_append(to_str, "&-");
3672 } else if (!in_escape && *p == '+') {
3673 if (*(p + 1) == '-') {
3674 g_string_append_c(to_str, '+');
3677 g_string_append_c(to_str, '&');
3680 } else if (in_escape && *p == '/') {
3681 g_string_append_c(to_str, ',');
3682 } else if (in_escape && *p == '-') {
3683 g_string_append_c(to_str, '-');
3686 g_string_append_c(to_str, *p);
3692 g_string_append_c(to_str, '-');
3696 g_string_free(to_str, FALSE);
3701 static GSList *imap_get_seq_set_from_numlist(MsgNumberList *numlist)
3704 GSList *sorted_list, *cur;
3705 guint first, last, next;
3707 GSList *ret_list = NULL;
3709 if (numlist == NULL)
3712 str = g_string_sized_new(256);
3714 sorted_list = g_slist_copy(numlist);
3715 sorted_list = g_slist_sort(sorted_list, g_int_compare);
3717 first = GPOINTER_TO_INT(sorted_list->data);
3719 for (cur = sorted_list; cur != NULL; cur = g_slist_next(cur)) {
3720 last = GPOINTER_TO_INT(cur->data);
3722 next = GPOINTER_TO_INT(cur->next->data);
3726 if (last + 1 != next || next == 0) {
3728 g_string_append_c(str, ',');
3730 g_string_append_printf(str, "%u", first);
3732 g_string_append_printf(str, "%u:%u", first, last);
3736 if (str->len > IMAP_CMD_LIMIT) {
3737 ret_str = g_strdup(str->str);
3738 ret_list = g_slist_append(ret_list, ret_str);
3739 g_string_truncate(str, 0);
3745 ret_str = g_strdup(str->str);
3746 ret_list = g_slist_append(ret_list, ret_str);
3749 g_slist_free(sorted_list);
3750 g_string_free(str, TRUE);
3755 static GSList *imap_get_seq_set_from_msglist(MsgInfoList *msglist)
3757 MsgNumberList *numlist = NULL;
3761 for (cur = msglist; cur != NULL; cur = g_slist_next(cur)) {
3762 MsgInfo *msginfo = (MsgInfo *) cur->data;
3764 numlist = g_slist_append(numlist, GINT_TO_POINTER(msginfo->msgnum));
3766 seq_list = imap_get_seq_set_from_numlist(numlist);
3767 g_slist_free(numlist);
3772 static void imap_seq_set_free(GSList *seq_list)
3774 slist_free_strings(seq_list);
3775 g_slist_free(seq_list);
3779 static gboolean imap_rename_folder_func(GNode *node, gpointer data)
3781 FolderItem *item = node->data;
3782 gchar **paths = data;
3783 const gchar *oldpath = paths[0];
3784 const gchar *newpath = paths[1];
3786 gchar *new_itempath;
3789 oldpathlen = strlen(oldpath);
3790 if (strncmp(oldpath, item->path, oldpathlen) != 0) {
3791 g_warning("path doesn't match: %s, %s\n", oldpath, item->path);
3795 base = item->path + oldpathlen;
3796 while (*base == G_DIR_SEPARATOR) base++;
3798 new_itempath = g_strdup(newpath);
3800 new_itempath = g_strconcat(newpath, G_DIR_SEPARATOR_S, base,
3803 item->path = new_itempath;
3808 static gint get_list_of_uids(Folder *folder, IMAPFolderItem *item, GSList **msgnum_list)
3810 gint ok, nummsgs = 0, lastuid_old;
3811 IMAPSession *session;
3812 GSList *uidlist, *elem;
3815 session = imap_session_get(folder);
3816 g_return_val_if_fail(session != NULL, -1);
3818 ok = imap_select(session, IMAP_FOLDER(folder), item->item.path,
3819 NULL, NULL, NULL, NULL);
3820 if (ok != IMAP_SUCCESS)
3823 cmd_buf = g_strdup_printf("UID %d:*", item->lastuid + 1);
3824 ok = imap_cmd_search(session, cmd_buf, &uidlist);
3827 if (ok == IMAP_SOCKET) {
3828 session_destroy((Session *)session);
3829 ((RemoteFolder *)folder)->session = NULL;
3833 if (ok != IMAP_SUCCESS) {
3837 argbuf = g_ptr_array_new();
3839 cmd_buf = g_strdup_printf("UID FETCH %d:* (UID)", item->lastuid + 1);
3840 imap_gen_send(session, cmd_buf);
3842 ok = imap_cmd_ok(session, argbuf);
3843 if (ok != IMAP_SUCCESS) {
3844 ptr_array_free_strings(argbuf);
3845 g_ptr_array_free(argbuf, TRUE);
3849 for(i = 0; i < argbuf->len; i++) {
3852 if((ret = sscanf(g_ptr_array_index(argbuf, i),
3853 "%*d FETCH (UID %d)", &msgnum)) == 1)
3854 uidlist = g_slist_prepend(uidlist, GINT_TO_POINTER(msgnum));
3856 ptr_array_free_strings(argbuf);
3857 g_ptr_array_free(argbuf, TRUE);
3860 lastuid_old = item->lastuid;
3861 *msgnum_list = g_slist_copy(item->uid_list);
3862 nummsgs = g_slist_length(*msgnum_list);
3863 debug_print("Got %d uids from cache\n", g_slist_length(item->uid_list));
3865 for (elem = uidlist; elem != NULL; elem = g_slist_next(elem)) {
3868 msgnum = GPOINTER_TO_INT(elem->data);
3869 if (msgnum > lastuid_old) {
3870 *msgnum_list = g_slist_prepend(*msgnum_list, GINT_TO_POINTER(msgnum));
3871 item->uid_list = g_slist_prepend(item->uid_list, GINT_TO_POINTER(msgnum));
3874 if(msgnum > item->lastuid)
3875 item->lastuid = msgnum;
3878 g_slist_free(uidlist);
3883 gint imap_get_num_list(Folder *folder, FolderItem *_item, GSList **msgnum_list, gboolean *old_uids_valid)
3885 IMAPFolderItem *item = (IMAPFolderItem *)_item;
3886 IMAPSession *session;
3887 gint ok, nummsgs = 0, exists, recent, uid_val, uid_next, unseen;
3888 GSList *uidlist = NULL;
3890 gboolean selected_folder;
3892 g_return_val_if_fail(folder != NULL, -1);
3893 g_return_val_if_fail(item != NULL, -1);
3894 g_return_val_if_fail(item->item.path != NULL, -1);
3895 g_return_val_if_fail(FOLDER_CLASS(folder) == &imap_class, -1);
3896 g_return_val_if_fail(folder->account != NULL, -1);
3898 session = imap_session_get(folder);
3899 g_return_val_if_fail(session != NULL, -1);
3901 selected_folder = (session->mbox != NULL) &&
3902 (!strcmp(session->mbox, item->item.path));
3903 if (selected_folder) {
3904 ok = imap_cmd_noop(session);
3905 if (ok != IMAP_SUCCESS)
3907 exists = session->exists;
3909 *old_uids_valid = TRUE;
3911 ok = imap_status(session, IMAP_FOLDER(folder), item->item.path,
3912 &exists, &recent, &uid_next, &uid_val, &unseen);
3913 if (ok != IMAP_SUCCESS)
3916 if(item->item.mtime == uid_val)
3917 *old_uids_valid = TRUE;
3919 *old_uids_valid = FALSE;
3921 debug_print("Freeing imap uid cache\n");
3923 g_slist_free(item->uid_list);
3924 item->uid_list = NULL;
3926 item->item.mtime = uid_val;
3928 imap_delete_all_cached_messages((FolderItem *)item);
3932 if (!selected_folder)
3933 item->uid_next = uid_next;
3935 /* If old uid_next matches new uid_next we can be sure no message
3936 was added to the folder */
3937 if (( selected_folder && !session->folder_content_changed) ||
3938 (!selected_folder && uid_next == item->uid_next)) {
3939 nummsgs = g_slist_length(item->uid_list);
3941 /* If number of messages is still the same we
3942 know our caches message numbers are still valid,
3943 otherwise if the number of messages has decrease
3944 we discard our cache to start a new scan to find
3945 out which numbers have been removed */
3946 if (exists == nummsgs) {
3947 *msgnum_list = g_slist_copy(item->uid_list);
3949 } else if (exists < nummsgs) {
3950 debug_print("Freeing imap uid cache");
3952 g_slist_free(item->uid_list);
3953 item->uid_list = NULL;
3958 *msgnum_list = NULL;
3962 nummsgs = get_list_of_uids(folder, item, &uidlist);
3964 if (nummsgs != exists) {
3965 /* Cache contains more messages then folder, we have cached
3966 an old UID of a message that was removed and new messages
3967 have been added too, otherwise the uid_next check would
3969 debug_print("Freeing imap uid cache");
3971 g_slist_free(item->uid_list);
3972 item->uid_list = NULL;
3974 g_slist_free(*msgnum_list);
3976 nummsgs = get_list_of_uids(folder, item, &uidlist);
3979 *msgnum_list = uidlist;
3981 dir = folder_item_get_path((FolderItem *)item);
3982 debug_print("removing old messages from %s\n", dir);
3983 remove_numbered_files_not_in_list(dir, *msgnum_list);
3989 static MsgInfo *imap_parse_msg(const gchar *file, FolderItem *item)
3994 flags.perm_flags = MSG_NEW|MSG_UNREAD;
3995 flags.tmp_flags = 0;
3997 g_return_val_if_fail(item != NULL, NULL);
3998 g_return_val_if_fail(file != NULL, NULL);
4000 if (item->stype == F_QUEUE) {
4001 MSG_SET_TMP_FLAGS(flags, MSG_QUEUED);
4002 } else if (item->stype == F_DRAFT) {
4003 MSG_SET_TMP_FLAGS(flags, MSG_DRAFT);
4006 msginfo = procheader_parse_file(file, flags, FALSE, FALSE);
4007 if (!msginfo) return NULL;
4009 msginfo->folder = item;
4014 GSList *imap_get_msginfos(Folder *folder, FolderItem *item, GSList *msgnum_list)
4016 IMAPSession *session;
4017 MsgInfoList *ret = NULL;
4020 g_return_val_if_fail(folder != NULL, NULL);
4021 g_return_val_if_fail(item != NULL, NULL);
4022 g_return_val_if_fail(msgnum_list != NULL, NULL);
4024 session = imap_session_get(folder);
4025 g_return_val_if_fail(session != NULL, NULL);
4027 ok = imap_select(session, IMAP_FOLDER(folder), item->path,
4028 NULL, NULL, NULL, NULL);
4029 if (ok != IMAP_SUCCESS)
4032 if (!(item->stype == F_QUEUE || item->stype == F_DRAFT)) {
4033 ret = g_slist_concat(ret,
4034 imap_get_uncached_messages(
4035 session, item, msgnum_list));
4037 MsgNumberList *sorted_list, *elem;
4038 gint startnum, lastnum;
4040 sorted_list = g_slist_sort(g_slist_copy(msgnum_list), g_int_compare);
4042 startnum = lastnum = GPOINTER_TO_INT(sorted_list->data);
4044 for (elem = sorted_list;; elem = g_slist_next(elem)) {
4048 num = GPOINTER_TO_INT(elem->data);
4050 if (num > lastnum + 1 || elem == NULL) {
4052 for (i = startnum; i <= lastnum; ++i) {
4055 file = imap_fetch_msg(folder, item, i);
4057 MsgInfo *msginfo = imap_parse_msg(file, item);
4058 if (msginfo != NULL) {
4059 msginfo->msgnum = i;
4060 ret = g_slist_append(ret, msginfo);
4074 g_slist_free(sorted_list);
4080 MsgInfo *imap_get_msginfo(Folder *folder, FolderItem *item, gint uid)
4082 MsgInfo *msginfo = NULL;
4083 MsgInfoList *msginfolist;
4084 MsgNumberList numlist;
4086 numlist.next = NULL;
4087 numlist.data = GINT_TO_POINTER(uid);
4089 msginfolist = imap_get_msginfos(folder, item, &numlist);
4090 if (msginfolist != NULL) {
4091 msginfo = msginfolist->data;
4092 g_slist_free(msginfolist);
4098 gboolean imap_scan_required(Folder *folder, FolderItem *_item)
4100 IMAPSession *session;
4101 IMAPFolderItem *item = (IMAPFolderItem *)_item;
4102 gint ok, exists = 0, recent = 0, unseen = 0;
4103 guint32 uid_next, uid_val = 0;
4104 gboolean selected_folder;
4106 g_return_val_if_fail(folder != NULL, FALSE);
4107 g_return_val_if_fail(item != NULL, FALSE);
4108 g_return_val_if_fail(item->item.folder != NULL, FALSE);
4109 g_return_val_if_fail(FOLDER_CLASS(item->item.folder) == &imap_class, FALSE);
4111 if (item->item.path == NULL)
4114 session = imap_session_get(folder);
4115 g_return_val_if_fail(session != NULL, FALSE);
4117 selected_folder = (session->mbox != NULL) &&
4118 (!strcmp(session->mbox, item->item.path));
4119 if (selected_folder) {
4120 ok = imap_cmd_noop(session);
4121 if (ok != IMAP_SUCCESS)
4124 if (session->folder_content_changed)
4127 ok = imap_status(session, IMAP_FOLDER(folder), item->item.path,
4128 &exists, &recent, &uid_next, &uid_val, &unseen);
4129 if (ok != IMAP_SUCCESS)
4132 if ((uid_next != item->uid_next) || (exists < item->item.total_msgs))
4139 void imap_change_flags(Folder *folder, FolderItem *item, MsgInfo *msginfo, MsgPermFlags newflags)
4141 IMAPSession *session;
4142 IMAPFlags flags_set = 0, flags_unset = 0;
4143 gint ok = IMAP_SUCCESS;
4144 MsgNumberList numlist;
4146 g_return_if_fail(folder != NULL);
4147 g_return_if_fail(folder->klass == &imap_class);
4148 g_return_if_fail(item != NULL);
4149 g_return_if_fail(item->folder == folder);
4150 g_return_if_fail(msginfo != NULL);
4151 g_return_if_fail(msginfo->folder == item);
4153 session = imap_session_get(folder);
4154 if (!session) return;
4156 if ((ok = imap_select(session, IMAP_FOLDER(folder), msginfo->folder->path,
4157 NULL, NULL, NULL, NULL)) != IMAP_SUCCESS)
4160 if (!MSG_IS_MARKED(msginfo->flags) && (newflags & MSG_MARKED))
4161 flags_set |= IMAP_FLAG_FLAGGED;
4162 if ( MSG_IS_MARKED(msginfo->flags) && !(newflags & MSG_MARKED))
4163 flags_unset |= IMAP_FLAG_FLAGGED;
4165 if (!MSG_IS_UNREAD(msginfo->flags) && (newflags & MSG_UNREAD))
4166 flags_unset |= IMAP_FLAG_SEEN;
4167 if ( MSG_IS_UNREAD(msginfo->flags) && !(newflags & MSG_UNREAD))
4168 flags_set |= IMAP_FLAG_SEEN;
4170 if (!MSG_IS_REPLIED(msginfo->flags) && (newflags & MSG_REPLIED))
4171 flags_set |= IMAP_FLAG_ANSWERED;
4172 if ( MSG_IS_REPLIED(msginfo->flags) && !(newflags & MSG_REPLIED))
4173 flags_set |= IMAP_FLAG_ANSWERED;
4175 numlist.next = NULL;
4176 numlist.data = GINT_TO_POINTER(msginfo->msgnum);
4179 ok = imap_set_message_flags(session, &numlist, flags_set, TRUE);
4180 if (ok != IMAP_SUCCESS) return;
4184 ok = imap_set_message_flags(session, &numlist, flags_unset, FALSE);
4185 if (ok != IMAP_SUCCESS) return;
4188 msginfo->flags.perm_flags = newflags;
4193 static gint compare_msginfo(gconstpointer a, gconstpointer b)
4195 return ((MsgInfo *)a)->msgnum - ((MsgInfo *)b)->msgnum;
4198 static guint gslist_find_next_num(MsgNumberList **list, guint num)
4202 g_return_val_if_fail(list != NULL, -1);
4204 for (elem = *list; elem != NULL; elem = g_slist_next(elem))
4205 if (GPOINTER_TO_INT(elem->data) >= num)
4208 return elem != NULL ? GPOINTER_TO_INT(elem->data) : (gint)-1;
4212 * NEW and DELETED flags are not syncronized
4213 * - The NEW/RECENT flags in IMAP folders can not really be directly
4214 * modified by Sylpheed
4215 * - The DELETE/DELETED flag in IMAP and Sylpheed don't have the same
4216 * meaning, in IMAP it always removes the messages from the FolderItem
4217 * in Sylpheed it can mean to move the message to trash
4219 static gint imap_get_flags(Folder *folder, FolderItem *item,
4220 MsgInfoList *msginfo_list, GRelation *msgflags)
4222 IMAPSession *session;
4223 GSList *sorted_list;
4225 GSList *new = NULL, *p_new;
4226 GSList *deleted = NULL, *p_deleted;
4228 GSList *unseen = NULL, *answered = NULL, *flagged = NULL;
4229 GSList *p_unseen, *p_answered, *p_flagged;
4231 GSList *seq_list, *cur;
4232 gboolean reverse_seen = FALSE;
4235 gint exists_cnt, recent_cnt, unseen_cnt, uid_next;
4236 guint32 uidvalidity;
4238 g_return_val_if_fail(folder != NULL, -1);
4239 g_return_val_if_fail(item != NULL, -1);
4240 if (msginfo_list == NULL)
4243 session = imap_session_get(folder);
4244 g_return_val_if_fail(session != NULL, -1);
4246 ok = imap_select(session, IMAP_FOLDER(folder), item->path,
4247 NULL, NULL, NULL, NULL);
4248 if (ok != IMAP_SUCCESS)
4251 ok = imap_status(session, IMAP_FOLDER(folder), item->path,
4252 &exists_cnt, &recent_cnt, &uid_next, &uidvalidity, &unseen_cnt);
4254 if (unseen_cnt > exists_cnt / 2)
4255 reverse_seen = TRUE;
4257 cmd_buf = g_string_new(NULL);
4259 sorted_list = g_slist_sort(g_slist_copy(msginfo_list), compare_msginfo);
4261 seq_list = imap_get_seq_set_from_msglist(msginfo_list);
4263 for (cur = seq_list; cur != NULL; cur = g_slist_next(cur)) {
4264 IMAPSet imapset = cur->data;
4266 g_string_sprintf(cmd_buf, "RECENT UID %s", imapset);
4267 imap_cmd_search(session, cmd_buf->str, &p_new);
4268 new = g_slist_concat(new, p_new);
4270 g_string_sprintf(cmd_buf, "%sSEEN UID %s", reverse_seen ? "" : "UN", imapset);
4271 imap_cmd_search(session, cmd_buf->str, &p_unseen);
4272 unseen = g_slist_concat(unseen, p_unseen);
4274 g_string_sprintf(cmd_buf, "ANSWERED UID %s", imapset);
4275 imap_cmd_search(session, cmd_buf->str, &p_answered);
4276 answered = g_slist_concat(answered, p_answered);
4278 g_string_sprintf(cmd_buf, "FLAGGED UID %s", imapset);
4279 imap_cmd_search(session, cmd_buf->str, &p_flagged);
4280 flagged = g_slist_concat(flagged, p_flagged);
4282 g_string_sprintf(cmd_buf, "DELETED UID %s", imapset);
4283 imap_cmd_search(session, cmd_buf->str, &p_deleted);
4284 deleted = g_slist_concat(deleted, p_deleted);
4292 p_answered = answered;
4293 p_flagged = flagged;
4295 p_deleted = deleted;
4297 for (elem = sorted_list; elem != NULL; elem = g_slist_next(elem)) {
4302 msginfo = (MsgInfo *) elem->data;
4303 flags = msginfo->flags.perm_flags;
4304 wasnew = (flags & MSG_NEW);
4305 flags &= ~((reverse_seen ? 0 : MSG_UNREAD | MSG_NEW) | MSG_REPLIED | MSG_MARKED);
4307 flags |= MSG_UNREAD | (wasnew ? MSG_NEW : 0);
4309 if (gslist_find_next_num(&p_new, msginfo->msgnum) == msginfo->msgnum)
4312 if (gslist_find_next_num(&p_unseen, msginfo->msgnum) == msginfo->msgnum) {
4313 if (!reverse_seen) {
4314 flags |= MSG_UNREAD | (wasnew ? MSG_NEW : 0);
4316 flags &= ~(MSG_UNREAD | MSG_NEW);
4319 if (gslist_find_next_num(&p_answered, msginfo->msgnum) == msginfo->msgnum)
4320 flags |= MSG_REPLIED;
4321 if (gslist_find_next_num(&p_flagged, msginfo->msgnum) == msginfo->msgnum)
4322 flags |= MSG_MARKED;
4324 if (gslist_find_next_num(&p_deleted, msginfo->msgnum) == msginfo->msgnum)
4325 MSG_SET_PERM_FLAGS(msginfo->flags, MSG_DELETED);
4327 g_relation_insert(msgflags, msginfo, GINT_TO_POINTER(flags));
4330 imap_seq_set_free(seq_list);
4331 /* g_slist_free(deleted); */
4332 g_slist_free(flagged);
4333 g_slist_free(answered);
4334 g_slist_free(unseen);
4335 /* new not freed in original patch ??? */
4336 g_slist_free(sorted_list);
4337 g_string_free(cmd_buf, TRUE);