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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
27 #include <glib/gi18n.h>
55 #include "procheader.h"
56 #include "prefs_account.h"
61 #include "prefs_common.h"
62 #include "inputdialog.h"
64 #include "remotefolder.h"
65 #include "alertpanel.h"
67 #include "statusbar.h"
69 #include "imap-thread.h"
71 typedef struct _IMAPFolder IMAPFolder;
72 typedef struct _IMAPSession IMAPSession;
73 typedef struct _IMAPNameSpace IMAPNameSpace;
74 typedef struct _IMAPFolderItem IMAPFolderItem;
76 #include "prefs_account.h"
78 #define IMAP_FOLDER(obj) ((IMAPFolder *)obj)
79 #define IMAP_FOLDER_ITEM(obj) ((IMAPFolderItem *)obj)
80 #define IMAP_SESSION(obj) ((IMAPSession *)obj)
86 /* list of IMAPNameSpace */
90 gchar last_seen_separator;
98 gboolean authenticated;
107 gboolean folder_content_changed;
113 struct _IMAPNameSpace
119 #define IMAP_SUCCESS 0
120 #define IMAP_SOCKET 2
121 #define IMAP_AUTHFAIL 3
122 #define IMAP_PROTOCOL 4
123 #define IMAP_SYNTAX 5
127 #define IMAPBUFSIZE 8192
131 IMAP_FLAG_SEEN = 1 << 0,
132 IMAP_FLAG_ANSWERED = 1 << 1,
133 IMAP_FLAG_FLAGGED = 1 << 2,
134 IMAP_FLAG_DELETED = 1 << 3,
135 IMAP_FLAG_DRAFT = 1 << 4
138 #define IMAP_IS_SEEN(flags) ((flags & IMAP_FLAG_SEEN) != 0)
139 #define IMAP_IS_ANSWERED(flags) ((flags & IMAP_FLAG_ANSWERED) != 0)
140 #define IMAP_IS_FLAGGED(flags) ((flags & IMAP_FLAG_FLAGGED) != 0)
141 #define IMAP_IS_DELETED(flags) ((flags & IMAP_FLAG_DELETED) != 0)
142 #define IMAP_IS_DRAFT(flags) ((flags & IMAP_FLAG_DRAFT) != 0)
145 #define IMAP4_PORT 143
147 #define IMAPS_PORT 993
150 #define IMAP_CMD_LIMIT 1000
152 struct _IMAPFolderItem
164 guint32 c_uid_validity;
167 GHashTable *flags_set_table;
168 GHashTable *flags_unset_table;
171 static void imap_folder_init (Folder *folder,
175 static Folder *imap_folder_new (const gchar *name,
177 static void imap_folder_destroy (Folder *folder);
179 static IMAPSession *imap_session_new (Folder *folder,
180 const PrefsAccount *account);
181 static void imap_session_authenticate(IMAPSession *session,
182 const PrefsAccount *account);
183 static void imap_session_destroy (Session *session);
185 static gchar *imap_fetch_msg (Folder *folder,
188 static gchar *imap_fetch_msg_full (Folder *folder,
193 static gint imap_add_msg (Folder *folder,
197 static gint imap_add_msgs (Folder *folder,
200 GRelation *relation);
202 static gint imap_copy_msg (Folder *folder,
205 static gint imap_copy_msgs (Folder *folder,
207 MsgInfoList *msglist,
208 GRelation *relation);
210 static gint imap_remove_msg (Folder *folder,
213 static gint imap_remove_msgs (Folder *folder,
215 MsgInfoList *msglist,
216 GRelation *relation);
217 static gint imap_remove_all_msg (Folder *folder,
220 static gboolean imap_is_msg_changed (Folder *folder,
224 static gint imap_close (Folder *folder,
227 static gint imap_scan_tree (Folder *folder);
229 static gint imap_create_tree (Folder *folder);
231 static FolderItem *imap_create_folder (Folder *folder,
234 static gint imap_rename_folder (Folder *folder,
237 static gint imap_remove_folder (Folder *folder,
240 static FolderItem *imap_folder_item_new (Folder *folder);
241 static void imap_folder_item_destroy (Folder *folder,
244 static IMAPSession *imap_session_get (Folder *folder);
246 static gint imap_auth (IMAPSession *session,
251 static gint imap_scan_tree_recursive (IMAPSession *session,
254 static void imap_create_missing_folders (Folder *folder);
255 static FolderItem *imap_create_special_folder
257 SpecialFolderItemType stype,
260 static gint imap_do_copy_msgs (Folder *folder,
262 MsgInfoList *msglist,
263 GRelation *relation);
265 static void imap_delete_all_cached_messages (FolderItem *item);
266 static void imap_set_batch (Folder *folder,
269 static gint imap_set_message_flags (IMAPSession *session,
270 MsgNumberList *numlist,
273 static gint imap_select (IMAPSession *session,
279 guint32 *uid_validity,
281 static gint imap_status (IMAPSession *session,
284 IMAPFolderItem *item,
287 guint32 *uid_validity,
291 static IMAPNameSpace *imap_find_namespace (IMAPFolder *folder,
293 static gchar imap_get_path_separator (IMAPFolder *folder,
295 static gchar *imap_get_real_path (IMAPFolder *folder,
297 static void imap_synchronise (FolderItem *item);
299 static void imap_free_capabilities (IMAPSession *session);
301 /* low-level IMAP4rev1 commands */
302 static gint imap_cmd_login (IMAPSession *session,
306 static gint imap_cmd_logout (IMAPSession *session);
307 static gint imap_cmd_noop (IMAPSession *session);
309 static gint imap_cmd_starttls (IMAPSession *session);
311 static gint imap_cmd_select (IMAPSession *session,
316 guint32 *uid_validity,
318 static gint imap_cmd_examine (IMAPSession *session,
323 guint32 *uid_validity,
325 static gint imap_cmd_create (IMAPSession *sock,
326 const gchar *folder);
327 static gint imap_cmd_rename (IMAPSession *sock,
328 const gchar *oldfolder,
329 const gchar *newfolder);
330 static gint imap_cmd_delete (IMAPSession *session,
331 const gchar *folder);
332 static gint imap_cmd_fetch (IMAPSession *sock,
334 const gchar *filename,
337 static gint imap_cmd_append (IMAPSession *session,
338 const gchar *destfolder,
342 static gint imap_cmd_copy (IMAPSession *session,
343 struct mailimap_set * set,
344 const gchar *destfolder,
345 GRelation *uid_mapping);
346 static gint imap_cmd_store (IMAPSession *session,
347 struct mailimap_set * set,
350 static gint imap_cmd_expunge (IMAPSession *session);
352 static void imap_path_separator_subst (gchar *str,
355 static gchar *imap_utf8_to_modified_utf7 (const gchar *from);
356 static gchar *imap_modified_utf7_to_utf8 (const gchar *mutf7_str);
358 static gboolean imap_rename_folder_func (GNode *node,
360 static gint imap_get_num_list (Folder *folder,
363 gboolean *old_uids_valid);
364 static GSList *imap_get_msginfos (Folder *folder,
366 GSList *msgnum_list);
367 static MsgInfo *imap_get_msginfo (Folder *folder,
370 static gboolean imap_scan_required (Folder *folder,
372 static void imap_change_flags (Folder *folder,
375 MsgPermFlags newflags);
376 static gint imap_get_flags (Folder *folder,
378 MsgInfoList *msglist,
379 GRelation *msgflags);
380 static gchar *imap_folder_get_path (Folder *folder);
381 static gchar *imap_item_get_path (Folder *folder,
383 static MsgInfo *imap_parse_msg(const gchar *file, FolderItem *item);
386 /* data types conversion libetpan <-> sylpheed */
387 static GSList * imap_list_from_lep(IMAPFolder * folder,
388 clist * list, const gchar * real_path, gboolean all);
389 static GSList * imap_get_lep_set_from_numlist(MsgNumberList *numlist);
390 static GSList * imap_get_lep_set_from_msglist(MsgInfoList *msglist);
391 static GSList * imap_uid_list_from_lep(clist * list);
392 static GSList * imap_uid_list_from_lep_tab(carray * list);
393 static MsgInfo *imap_envelope_from_lep(struct imap_fetch_env_info * info,
395 static void imap_lep_set_free(GSList *seq_list);
396 static struct mailimap_flag_list * imap_flag_to_lep(IMAPFlags flags);
398 typedef struct _hashtable_data {
399 IMAPSession *session;
401 IMAPFolderItem *item;
404 static FolderClass imap_class;
406 typedef struct _thread_data {
416 FolderClass *imap_get_class(void)
418 if (imap_class.idstr == NULL) {
419 imap_class.type = F_IMAP;
420 imap_class.idstr = "imap";
421 imap_class.uistr = "IMAP4";
423 /* Folder functions */
424 imap_class.new_folder = imap_folder_new;
425 imap_class.destroy_folder = imap_folder_destroy;
426 imap_class.scan_tree = imap_scan_tree;
427 imap_class.create_tree = imap_create_tree;
429 /* FolderItem functions */
430 imap_class.item_new = imap_folder_item_new;
431 imap_class.item_destroy = imap_folder_item_destroy;
432 imap_class.item_get_path = imap_item_get_path;
433 imap_class.create_folder = imap_create_folder;
434 imap_class.rename_folder = imap_rename_folder;
435 imap_class.remove_folder = imap_remove_folder;
436 imap_class.close = imap_close;
437 imap_class.get_num_list = imap_get_num_list;
438 imap_class.scan_required = imap_scan_required;
440 /* Message functions */
441 imap_class.get_msginfo = imap_get_msginfo;
442 imap_class.get_msginfos = imap_get_msginfos;
443 imap_class.fetch_msg = imap_fetch_msg;
444 imap_class.fetch_msg_full = imap_fetch_msg_full;
445 imap_class.add_msg = imap_add_msg;
446 imap_class.add_msgs = imap_add_msgs;
447 imap_class.copy_msg = imap_copy_msg;
448 imap_class.copy_msgs = imap_copy_msgs;
449 imap_class.remove_msg = imap_remove_msg;
450 imap_class.remove_msgs = imap_remove_msgs;
451 imap_class.remove_all_msg = imap_remove_all_msg;
452 imap_class.is_msg_changed = imap_is_msg_changed;
453 imap_class.change_flags = imap_change_flags;
454 imap_class.get_flags = imap_get_flags;
455 imap_class.set_batch = imap_set_batch;
456 imap_class.synchronise = imap_synchronise;
458 pthread_mutex_init(&imap_mutex, NULL);
465 static Folder *imap_folder_new(const gchar *name, const gchar *path)
469 folder = (Folder *)g_new0(IMAPFolder, 1);
470 folder->klass = &imap_class;
471 imap_folder_init(folder, name, path);
476 static void imap_folder_destroy(Folder *folder)
480 while (imap_folder_get_refcnt(folder) > 0)
481 gtk_main_iteration();
483 dir = imap_folder_get_path(folder);
484 if (is_dir_exist(dir))
485 remove_dir_recursive(dir);
488 folder_remote_folder_destroy(REMOTE_FOLDER(folder));
492 static void imap_folder_init(Folder *folder, const gchar *name,
495 folder_remote_folder_init((Folder *)folder, name, path);
498 static FolderItem *imap_folder_item_new(Folder *folder)
500 IMAPFolderItem *item;
502 item = g_new0(IMAPFolderItem, 1);
505 item->uid_list = NULL;
507 return (FolderItem *)item;
510 static void imap_folder_item_destroy(Folder *folder, FolderItem *_item)
512 IMAPFolderItem *item = (IMAPFolderItem *)_item;
514 g_return_if_fail(item != NULL);
515 g_slist_free(item->uid_list);
520 static gboolean imap_reset_uid_lists_func(GNode *node, gpointer data)
522 IMAPFolderItem *item = (IMAPFolderItem *)node->data;
526 g_slist_free(item->uid_list);
527 item->uid_list = NULL;
532 static void imap_reset_uid_lists(Folder *folder)
534 if(folder->node == NULL)
537 /* Destroy all uid lists and rest last uid */
538 g_node_traverse(folder->node, G_IN_ORDER, G_TRAVERSE_ALL, -1, imap_reset_uid_lists_func, NULL);
541 void imap_get_capabilities(IMAPSession *session)
543 struct mailimap_capability_data *capabilities = NULL;
546 if (session->capability != NULL)
549 capabilities = imap_threaded_capability(session->folder);
551 if (capabilities == NULL)
554 for(cur = clist_begin(capabilities->cap_list) ; cur != NULL ;
555 cur = clist_next(cur)) {
556 struct mailimap_capability * cap =
558 if (!cap || cap->cap_data.cap_name == NULL)
560 session->capability = g_slist_append
561 (session->capability,
562 g_strdup(cap->cap_data.cap_name));
563 debug_print("got capa %s\n", cap->cap_data.cap_name);
565 mailimap_capability_data_free(capabilities);
568 gboolean imap_has_capability(IMAPSession *session, const gchar *cap)
571 for (cur = session->capability; cur; cur = cur->next) {
572 if (!g_ascii_strcasecmp(cur->data, cap))
578 static gint imap_auth(IMAPSession *session, const gchar *user, const gchar *pass,
581 gint ok = IMAP_ERROR;
582 static time_t last_login_err = 0;
584 imap_get_capabilities(session);
587 case IMAP_AUTH_CRAM_MD5:
588 ok = imap_cmd_login(session, user, pass, "CRAM-MD5");
590 case IMAP_AUTH_LOGIN:
591 ok = imap_cmd_login(session, user, pass, "LOGIN");
594 debug_print("capabilities:\n"
597 imap_has_capability(session, "CRAM-MD5"),
598 imap_has_capability(session, "LOGIN"));
599 if (imap_has_capability(session, "CRAM-MD5"))
600 ok = imap_cmd_login(session, user, pass, "CRAM-MD5");
601 if (ok == IMAP_ERROR) /* we always try LOGIN before giving up */
602 ok = imap_cmd_login(session, user, pass, "LOGIN");
604 if (ok == IMAP_SUCCESS)
605 session->authenticated = TRUE;
607 gchar *ext_info = NULL;
609 if (type == IMAP_AUTH_CRAM_MD5) {
610 ext_info = _("\n\nCRAM-MD5 logins only work if libetpan has been "
611 "compiled with SASL support and the "
612 "CRAM-MD5 SASL plugin is installed.");
617 if (time(NULL) - last_login_err > 10) {
618 if (!prefs_common.no_recv_err_panel) {
619 alertpanel_error(_("Connection to %s failed: "
621 SESSION(session)->server, ext_info);
623 log_error(_("Connection to %s failed: "
624 "login refused.%s\n"),
625 SESSION(session)->server, ext_info);
628 last_login_err = time(NULL);
633 static IMAPSession *imap_reconnect_if_possible(Folder *folder, IMAPSession *session)
635 RemoteFolder *rfolder = REMOTE_FOLDER(folder);
636 /* Check if this is the first try to establish a
637 connection, if yes we don't try to reconnect */
638 debug_print("reconnecting\n");
639 if (rfolder->session == NULL) {
640 log_warning(_("Connecting to %s failed"),
641 folder->account->recv_server);
642 session_destroy(SESSION(session));
645 log_warning(_("IMAP4 connection to %s has been"
646 " disconnected. Reconnecting...\n"),
647 folder->account->recv_server);
648 statusbar_print_all(_("IMAP4 connection to %s has been"
649 " disconnected. Reconnecting...\n"),
650 folder->account->recv_server);
651 SESSION(session)->state = SESSION_DISCONNECTED;
652 session_destroy(SESSION(session));
653 /* Clear folders session to make imap_session_get create
654 a new session, because of rfolder->session == NULL
655 it will not try to reconnect again and so avoid an
657 rfolder->session = NULL;
658 session = imap_session_get(folder);
659 rfolder->session = SESSION(session);
665 #define lock_session() {\
666 debug_print("locking session\n"); \
667 session->busy = TRUE;\
670 #define unlock_session() {\
671 debug_print("unlocking session\n"); \
672 session->busy = FALSE;\
675 static IMAPSession *imap_session_get(Folder *folder)
677 RemoteFolder *rfolder = REMOTE_FOLDER(folder);
678 IMAPSession *session = NULL;
680 g_return_val_if_fail(folder != NULL, NULL);
681 g_return_val_if_fail(FOLDER_CLASS(folder) == &imap_class, NULL);
682 g_return_val_if_fail(folder->account != NULL, NULL);
684 if (prefs_common.work_offline && !inc_offline_should_override()) {
688 /* Make sure we have a session */
689 if (rfolder->session != NULL) {
690 session = IMAP_SESSION(rfolder->session);
691 /* don't do that yet...
696 imap_reset_uid_lists(folder);
697 session = imap_session_new(folder, folder->account);
702 /* Make sure session is authenticated */
703 if (!IMAP_SESSION(session)->authenticated)
704 imap_session_authenticate(IMAP_SESSION(session), folder->account);
706 if (!IMAP_SESSION(session)->authenticated) {
707 session_destroy(SESSION(session));
708 rfolder->session = NULL;
712 /* I think the point of this code is to avoid sending a
713 * keepalive if we've used the session recently and therefore
714 * think it's still alive. Unfortunately, most of the code
715 * does not yet check for errors on the socket, and so if the
716 * connection drops we don't notice until the timeout expires.
717 * A better solution than sending a NOOP every time would be
718 * for every command to be prepared to retry until it is
719 * successfully sent. -- mbp */
720 if (time(NULL) - SESSION(session)->last_access_time > SESSION_TIMEOUT_INTERVAL) {
721 /* verify that the session is still alive */
722 if (imap_cmd_noop(session) != IMAP_SUCCESS) {
723 debug_print("disconnected!\n");
724 session = imap_reconnect_if_possible(folder, session);
728 rfolder->session = SESSION(session);
730 return IMAP_SESSION(session);
733 static IMAPSession *imap_session_new(Folder * folder,
734 const PrefsAccount *account)
736 IMAPSession *session;
742 /* FIXME: IMAP over SSL only... */
745 port = account->set_imapport ? account->imapport
746 : account->ssl_imap == SSL_TUNNEL ? IMAPS_PORT : IMAP4_PORT;
747 ssl_type = account->ssl_imap;
749 if (account->ssl_imap != SSL_NONE) {
750 if (alertpanel_full(_("Insecure connection"),
751 _("This connection is configured to be secured "
752 "using SSL, but SSL is not available in this "
753 "build of Sylpheed-Claws. \n\n"
754 "Do you want to continue connecting to this "
755 "server? The communication would not be "
757 _("Con_tinue connecting"),
758 GTK_STOCK_CANCEL, NULL,
759 FALSE, NULL, ALERT_WARNING,
760 G_ALERTALTERNATE) != G_ALERTDEFAULT)
763 port = account->set_imapport ? account->imapport
768 statusbar_print_all(_("Connecting to IMAP4 server: %s..."), folder->account->recv_server);
769 if (account->set_tunnelcmd) {
770 r = imap_threaded_connect_cmd(folder,
772 account->recv_server,
777 if (ssl_type == SSL_TUNNEL) {
778 r = imap_threaded_connect_ssl(folder,
779 account->recv_server,
785 r = imap_threaded_connect(folder,
786 account->recv_server,
792 if (r == MAILIMAP_NO_ERROR_AUTHENTICATED) {
793 authenticated = TRUE;
795 else if (r == MAILIMAP_NO_ERROR_NON_AUTHENTICATED) {
796 authenticated = FALSE;
799 if(!prefs_common.no_recv_err_panel) {
800 alertpanel_error(_("Can't connect to IMAP4 server: %s:%d"),
801 account->recv_server, port);
803 log_error(_("Can't connect to IMAP4 server: %s:%d\n"),
804 account->recv_server, port);
810 session = g_new0(IMAPSession, 1);
811 session_init(SESSION(session));
812 SESSION(session)->type = SESSION_IMAP;
813 SESSION(session)->server = g_strdup(account->recv_server);
814 SESSION(session)->sock = NULL;
816 SESSION(session)->destroy = imap_session_destroy;
818 session->capability = NULL;
820 session->authenticated = authenticated;
821 session->mbox = NULL;
822 session->cmd_count = 0;
823 session->folder = folder;
824 IMAP_FOLDER(session->folder)->last_seen_separator = 0;
827 if (account->ssl_imap == SSL_STARTTLS) {
830 ok = imap_cmd_starttls(session);
831 if (ok != IMAP_SUCCESS) {
832 log_warning(_("Can't start TLS session.\n"));
833 session_destroy(SESSION(session));
837 imap_free_capabilities(session);
838 session->authenticated = FALSE;
839 session->uidplus = FALSE;
840 session->cmd_count = 1;
843 log_message("IMAP connection is %s-authenticated\n",
844 (session->authenticated) ? "pre" : "un");
849 static void imap_session_authenticate(IMAPSession *session,
850 const PrefsAccount *account)
854 g_return_if_fail(account->userid != NULL);
856 pass = account->passwd;
859 tmp_pass = input_dialog_query_password(account->recv_server, account->userid);
861 tmp_pass = g_strdup(""); /* allow empty password */
862 Xstrdup_a(pass, tmp_pass, {g_free(tmp_pass); return;});
865 statusbar_print_all(_("Connecting to IMAP4 server %s...\n"),
866 account->recv_server);
867 if (imap_auth(session, account->userid, pass, account->imap_auth_type) != IMAP_SUCCESS) {
868 imap_threaded_disconnect(session->folder);
869 imap_cmd_logout(session);
875 session->authenticated = TRUE;
878 static void imap_session_destroy(Session *session)
880 if (session->state != SESSION_DISCONNECTED)
881 imap_threaded_disconnect(IMAP_SESSION(session)->folder);
883 imap_free_capabilities(IMAP_SESSION(session));
884 g_free(IMAP_SESSION(session)->mbox);
885 sock_close(session->sock);
886 session->sock = NULL;
889 static gchar *imap_fetch_msg(Folder *folder, FolderItem *item, gint uid)
891 return imap_fetch_msg_full(folder, item, uid, TRUE, TRUE);
894 static guint get_size_with_crs(MsgInfo *info)
903 fp = procmsg_open_message(info);
907 while (fgets(buf, sizeof (buf), fp) != NULL) {
909 if (!strstr(buf, "\r") && strstr(buf, "\n"))
917 static gchar *imap_fetch_msg_full(Folder *folder, FolderItem *item, gint uid,
918 gboolean headers, gboolean body)
920 gchar *path, *filename;
921 IMAPSession *session;
924 g_return_val_if_fail(folder != NULL, NULL);
925 g_return_val_if_fail(item != NULL, NULL);
930 path = folder_item_get_path(item);
931 if (!is_dir_exist(path))
933 filename = g_strconcat(path, G_DIR_SEPARATOR_S, itos(uid), NULL);
936 if (is_file_exist(filename)) {
937 /* see whether the local file represents the whole message
938 * or not. As the IMAP server reports size with \r chars,
939 * we have to update the local file (UNIX \n only) size */
940 MsgInfo *msginfo = imap_parse_msg(filename, item);
941 MsgInfo *cached = msgcache_get_msg(item->cache,uid);
942 guint have_size = get_size_with_crs(msginfo);
945 debug_print("message %d has been already %scached (%d/%d).\n", uid,
946 have_size == cached->size ? "fully ":"",
947 have_size, (int)cached->size);
949 if (cached && (cached->size == have_size || !body)) {
950 procmsg_msginfo_free(cached);
951 procmsg_msginfo_free(msginfo);
952 file_strip_crs(filename);
955 procmsg_msginfo_free(cached);
956 procmsg_msginfo_free(msginfo);
960 session = imap_session_get(folder);
969 debug_print("IMAP fetching messages\n");
970 ok = imap_select(session, IMAP_FOLDER(folder), item->path,
971 NULL, NULL, NULL, NULL, FALSE);
972 if (ok != IMAP_SUCCESS) {
973 g_warning("can't select mailbox %s\n", item->path);
979 debug_print("getting message %d...\n", uid);
980 ok = imap_cmd_fetch(session, (guint32)uid, filename, headers, body);
982 if (ok != IMAP_SUCCESS) {
983 g_warning("can't fetch message %d\n", uid);
990 file_strip_crs(filename);
994 static gint imap_add_msg(Folder *folder, FolderItem *dest,
995 const gchar *file, MsgFlags *flags)
999 MsgFileInfo fileinfo;
1001 g_return_val_if_fail(file != NULL, -1);
1003 fileinfo.msginfo = NULL;
1004 fileinfo.file = (gchar *)file;
1005 fileinfo.flags = flags;
1006 file_list.data = &fileinfo;
1007 file_list.next = NULL;
1009 ret = imap_add_msgs(folder, dest, &file_list, NULL);
1013 static gint imap_add_msgs(Folder *folder, FolderItem *dest, GSList *file_list,
1014 GRelation *relation)
1017 IMAPSession *session;
1018 guint32 last_uid = 0;
1020 MsgFileInfo *fileinfo;
1022 gint curnum = 0, total = 0;
1025 g_return_val_if_fail(folder != NULL, -1);
1026 g_return_val_if_fail(dest != NULL, -1);
1027 g_return_val_if_fail(file_list != NULL, -1);
1029 session = imap_session_get(folder);
1034 destdir = imap_get_real_path(IMAP_FOLDER(folder), dest->path);
1036 statusbar_print_all(_("Adding messages..."));
1037 total = g_slist_length(file_list);
1038 for (cur = file_list; cur != NULL; cur = cur->next) {
1039 IMAPFlags iflags = 0;
1040 guint32 new_uid = 0;
1041 gchar *real_file = NULL;
1042 gboolean file_is_tmp = FALSE;
1043 fileinfo = (MsgFileInfo *)cur->data;
1045 statusbar_progress_all(curnum, total, 1);
1048 if (fileinfo->flags) {
1049 if (MSG_IS_MARKED(*fileinfo->flags))
1050 iflags |= IMAP_FLAG_FLAGGED;
1051 if (MSG_IS_REPLIED(*fileinfo->flags))
1052 iflags |= IMAP_FLAG_ANSWERED;
1053 if (!MSG_IS_UNREAD(*fileinfo->flags))
1054 iflags |= IMAP_FLAG_SEEN;
1057 if (fileinfo->flags) {
1058 if ((MSG_IS_QUEUED(*fileinfo->flags)
1059 || MSG_IS_DRAFT(*fileinfo->flags))
1060 && !folder_has_parent_of_type(dest, F_QUEUE)
1061 && !folder_has_parent_of_type(dest, F_DRAFT)) {
1062 real_file = get_tmp_file();
1064 if (procmsg_remove_special_headers(
1074 if (real_file == NULL)
1075 real_file = g_strdup(fileinfo->file);
1077 if (folder_has_parent_of_type(dest, F_QUEUE) ||
1078 folder_has_parent_of_type(dest, F_OUTBOX) ||
1079 folder_has_parent_of_type(dest, F_DRAFT) ||
1080 folder_has_parent_of_type(dest, F_TRASH))
1081 iflags |= IMAP_FLAG_SEEN;
1083 ok = imap_cmd_append(session, destdir, real_file, iflags,
1086 if (ok != IMAP_SUCCESS) {
1087 g_warning("can't append message %s\n", real_file);
1089 g_unlink(real_file);
1093 statusbar_progress_all(0,0,0);
1094 statusbar_pop_all();
1098 if (relation != NULL)
1099 g_relation_insert(relation, fileinfo->msginfo != NULL ?
1100 (gpointer) fileinfo->msginfo : (gpointer) fileinfo,
1101 GINT_TO_POINTER(dest->last_num + 1));
1102 if (last_uid < new_uid)
1105 g_unlink(real_file);
1107 statusbar_progress_all(0,0,0);
1108 statusbar_pop_all();
1117 static gint imap_do_copy_msgs(Folder *folder, FolderItem *dest,
1118 MsgInfoList *msglist, GRelation *relation)
1122 GSList *seq_list, *cur;
1124 IMAPSession *session;
1125 gint ok = IMAP_SUCCESS;
1126 GRelation *uid_mapping;
1129 g_return_val_if_fail(folder != NULL, -1);
1130 g_return_val_if_fail(dest != NULL, -1);
1131 g_return_val_if_fail(msglist != NULL, -1);
1133 session = imap_session_get(folder);
1139 msginfo = (MsgInfo *)msglist->data;
1141 src = msginfo->folder;
1143 g_warning("the src folder is identical to the dest.\n");
1148 ok = imap_select(session, IMAP_FOLDER(folder), msginfo->folder->path,
1149 NULL, NULL, NULL, NULL, FALSE);
1150 if (ok != IMAP_SUCCESS) {
1155 destdir = imap_get_real_path(IMAP_FOLDER(folder), dest->path);
1156 seq_list = imap_get_lep_set_from_msglist(msglist);
1157 uid_mapping = g_relation_new(2);
1158 g_relation_index(uid_mapping, 0, g_direct_hash, g_direct_equal);
1160 statusbar_print_all(_("Copying messages..."));
1161 for (cur = seq_list; cur != NULL; cur = g_slist_next(cur)) {
1162 struct mailimap_set * seq_set;
1163 seq_set = cur->data;
1165 debug_print("Copying messages from %s to %s ...\n",
1166 src->path, destdir);
1168 ok = imap_cmd_copy(session, seq_set, destdir, uid_mapping);
1169 if (ok != IMAP_SUCCESS) {
1170 g_relation_destroy(uid_mapping);
1171 imap_lep_set_free(seq_list);
1177 for (cur = msglist; cur != NULL; cur = g_slist_next(cur)) {
1178 MsgInfo *msginfo = (MsgInfo *)cur->data;
1181 tuples = g_relation_select(uid_mapping,
1182 GINT_TO_POINTER(msginfo->msgnum),
1184 if (tuples->len > 0) {
1185 gint num = GPOINTER_TO_INT(g_tuples_index(tuples, 0, 1));
1186 g_relation_insert(relation, msginfo,
1187 GPOINTER_TO_INT(num));
1191 g_relation_insert(relation, msginfo,
1192 GPOINTER_TO_INT(0));
1193 g_tuples_destroy(tuples);
1195 statusbar_pop_all();
1197 g_relation_destroy(uid_mapping);
1198 imap_lep_set_free(seq_list);
1202 IMAP_FOLDER_ITEM(dest)->lastuid = 0;
1203 IMAP_FOLDER_ITEM(dest)->uid_next = 0;
1204 g_slist_free(IMAP_FOLDER_ITEM(dest)->uid_list);
1205 IMAP_FOLDER_ITEM(dest)->uid_list = NULL;
1208 if (ok == IMAP_SUCCESS)
1214 static gint imap_copy_msg(Folder *folder, FolderItem *dest, MsgInfo *msginfo)
1218 g_return_val_if_fail(msginfo != NULL, -1);
1220 msglist.data = msginfo;
1221 msglist.next = NULL;
1223 return imap_copy_msgs(folder, dest, &msglist, NULL);
1226 static gint imap_copy_msgs(Folder *folder, FolderItem *dest,
1227 MsgInfoList *msglist, GRelation *relation)
1233 g_return_val_if_fail(folder != NULL, -1);
1234 g_return_val_if_fail(dest != NULL, -1);
1235 g_return_val_if_fail(msglist != NULL, -1);
1237 msginfo = (MsgInfo *)msglist->data;
1238 g_return_val_if_fail(msginfo->folder != NULL, -1);
1240 if (folder == msginfo->folder->folder &&
1241 !folder_has_parent_of_type(msginfo->folder, F_DRAFT) &&
1242 !folder_has_parent_of_type(msginfo->folder, F_QUEUE)) {
1243 ret = imap_do_copy_msgs(folder, dest, msglist, relation);
1247 file_list = procmsg_get_message_file_list(msglist);
1248 g_return_val_if_fail(file_list != NULL, -1);
1250 ret = imap_add_msgs(folder, dest, file_list, relation);
1252 procmsg_message_file_list_free(file_list);
1258 static gint imap_do_remove_msgs(Folder *folder, FolderItem *dest,
1259 MsgInfoList *msglist, GRelation *relation)
1262 GSList *numlist = NULL, *cur;
1264 IMAPSession *session;
1265 gint ok = IMAP_SUCCESS;
1266 GRelation *uid_mapping;
1268 g_return_val_if_fail(folder != NULL, -1);
1269 g_return_val_if_fail(dest != NULL, -1);
1270 g_return_val_if_fail(msglist != NULL, -1);
1272 session = imap_session_get(folder);
1277 msginfo = (MsgInfo *)msglist->data;
1279 ok = imap_select(session, IMAP_FOLDER(folder), msginfo->folder->path,
1280 NULL, NULL, NULL, NULL, FALSE);
1281 if (ok != IMAP_SUCCESS) {
1286 destdir = imap_get_real_path(IMAP_FOLDER(folder), dest->path);
1287 for (cur = msglist; cur; cur = cur->next) {
1288 msginfo = (MsgInfo *)cur->data;
1289 if (!MSG_IS_DELETED(msginfo->flags))
1290 numlist = g_slist_append(numlist, GINT_TO_POINTER(msginfo->msgnum));
1293 uid_mapping = g_relation_new(2);
1294 g_relation_index(uid_mapping, 0, g_direct_hash, g_direct_equal);
1296 ok = imap_set_message_flags
1297 (IMAP_SESSION(REMOTE_FOLDER(folder)->session),
1298 numlist, IMAP_FLAG_DELETED, TRUE);
1299 if (ok != IMAP_SUCCESS) {
1300 log_warning(_("can't set deleted flags\n"));
1304 ok = imap_cmd_expunge(session);
1305 if (ok != IMAP_SUCCESS) {
1306 log_warning(_("can't expunge\n"));
1311 g_relation_destroy(uid_mapping);
1312 g_slist_free(numlist);
1316 if (ok == IMAP_SUCCESS)
1322 static gint imap_remove_msgs(Folder *folder, FolderItem *dest,
1323 MsgInfoList *msglist, GRelation *relation)
1327 g_return_val_if_fail(folder != NULL, -1);
1328 g_return_val_if_fail(dest != NULL, -1);
1329 if (msglist == NULL)
1332 msginfo = (MsgInfo *)msglist->data;
1333 g_return_val_if_fail(msginfo->folder != NULL, -1);
1335 return imap_do_remove_msgs(folder, dest, msglist, relation);
1338 static gint imap_remove_all_msg(Folder *folder, FolderItem *item)
1340 GSList *list = folder_item_get_msg_list(item);
1341 gint res = imap_remove_msgs(folder, item, list, NULL);
1342 procmsg_msg_list_free(list);
1346 static gboolean imap_is_msg_changed(Folder *folder, FolderItem *item,
1349 /* TODO: properly implement this method */
1353 static gint imap_close(Folder *folder, FolderItem *item)
1358 static gint imap_scan_tree(Folder *folder)
1360 FolderItem *item = NULL;
1361 IMAPSession *session;
1362 gchar *root_folder = NULL;
1364 g_return_val_if_fail(folder != NULL, -1);
1365 g_return_val_if_fail(folder->account != NULL, -1);
1367 session = imap_session_get(folder);
1369 if (!folder->node) {
1370 folder_tree_destroy(folder);
1371 item = folder_item_new(folder, folder->name, NULL);
1372 item->folder = folder;
1373 folder->node = item->node = g_node_new(item);
1379 if (folder->account->imap_dir && *folder->account->imap_dir) {
1384 Xstrdup_a(root_folder, folder->account->imap_dir, {unlock_session();return -1;});
1385 extract_quote(root_folder, '"');
1386 subst_char(root_folder,
1387 imap_get_path_separator(IMAP_FOLDER(folder),
1390 strtailchomp(root_folder, '/');
1391 real_path = imap_get_real_path
1392 (IMAP_FOLDER(folder), root_folder);
1393 debug_print("IMAP root directory: %s\n", real_path);
1395 /* check if root directory exist */
1397 r = imap_threaded_list(session->folder, "", real_path,
1399 if ((r != MAILIMAP_NO_ERROR) || (clist_count(lep_list) == 0)) {
1400 if (!folder->node) {
1401 item = folder_item_new(folder, folder->name, NULL);
1402 item->folder = folder;
1403 folder->node = item->node = g_node_new(item);
1408 mailimap_list_result_free(lep_list);
1414 item = FOLDER_ITEM(folder->node->data);
1415 if (!item || ((item->path || root_folder) &&
1416 strcmp2(item->path, root_folder) != 0)) {
1417 folder_tree_destroy(folder);
1418 item = folder_item_new(folder, folder->name, root_folder);
1419 item->folder = folder;
1420 folder->node = item->node = g_node_new(item);
1423 imap_scan_tree_recursive(session, FOLDER_ITEM(folder->node->data));
1424 imap_create_missing_folders(folder);
1430 static gint imap_scan_tree_recursive(IMAPSession *session, FolderItem *item)
1433 IMAPFolder *imapfolder;
1434 FolderItem *new_item;
1435 GSList *item_list, *cur;
1438 gchar *wildcard_path;
1444 g_return_val_if_fail(item != NULL, -1);
1445 g_return_val_if_fail(item->folder != NULL, -1);
1446 g_return_val_if_fail(item->no_sub == FALSE, -1);
1448 folder = item->folder;
1449 imapfolder = IMAP_FOLDER(folder);
1451 separator = imap_get_path_separator(imapfolder, item->path);
1453 if (folder->ui_func)
1454 folder->ui_func(folder, item, folder->ui_func_data);
1457 wildcard[0] = separator;
1460 real_path = imap_get_real_path(imapfolder, item->path);
1464 real_path = g_strdup("");
1467 Xstrcat_a(wildcard_path, real_path, wildcard,
1468 {g_free(real_path); return IMAP_ERROR;});
1470 r = imap_threaded_list(folder, "", wildcard_path, &lep_list);
1471 if (r != MAILIMAP_NO_ERROR) {
1475 item_list = imap_list_from_lep(imapfolder,
1476 lep_list, real_path, FALSE);
1477 mailimap_list_result_free(lep_list);
1482 node = item->node->children;
1483 while (node != NULL) {
1484 FolderItem *old_item = FOLDER_ITEM(node->data);
1485 GNode *next = node->next;
1488 for (cur = item_list; cur != NULL; cur = cur->next) {
1489 FolderItem *cur_item = FOLDER_ITEM(cur->data);
1490 if (!strcmp2(old_item->path, cur_item->path)) {
1491 new_item = cur_item;
1496 debug_print("folder '%s' not found. removing...\n",
1498 folder_item_remove(old_item);
1500 old_item->no_sub = new_item->no_sub;
1501 old_item->no_select = new_item->no_select;
1502 if (old_item->no_sub == TRUE && node->children) {
1503 debug_print("folder '%s' doesn't have "
1504 "subfolders. removing...\n",
1506 folder_item_remove_children(old_item);
1513 for (cur = item_list; cur != NULL; cur = cur->next) {
1514 FolderItem *cur_item = FOLDER_ITEM(cur->data);
1517 for (node = item->node->children; node != NULL;
1518 node = node->next) {
1519 if (!strcmp2(FOLDER_ITEM(node->data)->path,
1521 new_item = FOLDER_ITEM(node->data);
1522 folder_item_destroy(cur_item);
1528 new_item = cur_item;
1529 debug_print("new folder '%s' found.\n", new_item->path);
1530 folder_item_append(item, new_item);
1533 if (!strcmp(new_item->path, "INBOX")) {
1534 new_item->stype = F_INBOX;
1535 folder->inbox = new_item;
1536 } else if (!folder_item_parent(item) || item->stype == F_INBOX) {
1539 base = g_path_get_basename(new_item->path);
1541 if (!folder->outbox && !g_ascii_strcasecmp(base, "Sent")) {
1542 new_item->stype = F_OUTBOX;
1543 folder->outbox = new_item;
1544 } else if (!folder->draft && !g_ascii_strcasecmp(base, "Drafts")) {
1545 new_item->stype = F_DRAFT;
1546 folder->draft = new_item;
1547 } else if (!folder->queue && !g_ascii_strcasecmp(base, "Queue")) {
1548 new_item->stype = F_QUEUE;
1549 folder->queue = new_item;
1550 } else if (!folder->trash && !g_ascii_strcasecmp(base, "Trash")) {
1551 new_item->stype = F_TRASH;
1552 folder->trash = new_item;
1557 if (new_item->no_sub == FALSE)
1558 imap_scan_tree_recursive(session, new_item);
1561 g_slist_free(item_list);
1563 return IMAP_SUCCESS;
1566 static gint imap_create_tree(Folder *folder)
1568 g_return_val_if_fail(folder != NULL, -1);
1569 g_return_val_if_fail(folder->node != NULL, -1);
1570 g_return_val_if_fail(folder->node->data != NULL, -1);
1571 g_return_val_if_fail(folder->account != NULL, -1);
1573 imap_scan_tree(folder);
1574 imap_create_missing_folders(folder);
1579 static void imap_create_missing_folders(Folder *folder)
1581 g_return_if_fail(folder != NULL);
1584 folder->inbox = imap_create_special_folder
1585 (folder, F_INBOX, "INBOX");
1587 folder->trash = imap_create_special_folder
1588 (folder, F_TRASH, "Trash");
1590 folder->queue = imap_create_special_folder
1591 (folder, F_QUEUE, "Queue");
1592 if (!folder->outbox)
1593 folder->outbox = imap_create_special_folder
1594 (folder, F_OUTBOX, "Sent");
1596 folder->draft = imap_create_special_folder
1597 (folder, F_DRAFT, "Drafts");
1600 static FolderItem *imap_create_special_folder(Folder *folder,
1601 SpecialFolderItemType stype,
1605 FolderItem *new_item;
1607 g_return_val_if_fail(folder != NULL, NULL);
1608 g_return_val_if_fail(folder->node != NULL, NULL);
1609 g_return_val_if_fail(folder->node->data != NULL, NULL);
1610 g_return_val_if_fail(folder->account != NULL, NULL);
1611 g_return_val_if_fail(name != NULL, NULL);
1613 item = FOLDER_ITEM(folder->node->data);
1614 new_item = imap_create_folder(folder, item, name);
1617 g_warning("Can't create '%s'\n", name);
1618 if (!folder->inbox) return NULL;
1620 new_item = imap_create_folder(folder, folder->inbox, name);
1622 g_warning("Can't create '%s' under INBOX\n", name);
1624 new_item->stype = stype;
1626 new_item->stype = stype;
1631 static gchar *imap_folder_get_path(Folder *folder)
1635 g_return_val_if_fail(folder != NULL, NULL);
1636 g_return_val_if_fail(folder->account != NULL, NULL);
1638 folder_path = g_strconcat(get_imap_cache_dir(),
1640 folder->account->recv_server,
1642 folder->account->userid,
1648 static gchar *imap_item_get_path(Folder *folder, FolderItem *item)
1650 gchar *folder_path, *path;
1652 g_return_val_if_fail(folder != NULL, NULL);
1653 g_return_val_if_fail(item != NULL, NULL);
1654 folder_path = imap_folder_get_path(folder);
1656 g_return_val_if_fail(folder_path != NULL, NULL);
1657 if (folder_path[0] == G_DIR_SEPARATOR) {
1659 path = g_strconcat(folder_path, G_DIR_SEPARATOR_S,
1662 path = g_strdup(folder_path);
1665 path = g_strconcat(get_home_dir(), G_DIR_SEPARATOR_S,
1666 folder_path, G_DIR_SEPARATOR_S,
1669 path = g_strconcat(get_home_dir(), G_DIR_SEPARATOR_S,
1672 g_free(folder_path);
1677 static FolderItem *imap_create_folder(Folder *folder, FolderItem *parent,
1680 gchar *dirpath, *imap_path;
1681 IMAPSession *session;
1682 FolderItem *new_item;
1687 gboolean no_select = FALSE, no_sub = FALSE;
1689 g_return_val_if_fail(folder != NULL, NULL);
1690 g_return_val_if_fail(folder->account != NULL, NULL);
1691 g_return_val_if_fail(parent != NULL, NULL);
1692 g_return_val_if_fail(name != NULL, NULL);
1694 session = imap_session_get(folder);
1700 if (!folder_item_parent(parent) && strcmp(name, "INBOX") == 0) {
1701 dirpath = g_strdup(name);
1702 }else if (parent->path)
1703 dirpath = g_strconcat(parent->path, "/", name, NULL);
1704 else if ((p = strchr(name, '/')) != NULL && *(p + 1) != '\0')
1705 dirpath = g_strdup(name);
1706 else if (folder->account->imap_dir && *folder->account->imap_dir) {
1709 Xstrdup_a(imap_dir, folder->account->imap_dir, {unlock_session();return NULL;});
1710 strtailchomp(imap_dir, '/');
1711 dirpath = g_strconcat(imap_dir, "/", name, NULL);
1713 dirpath = g_strdup(name);
1717 /* keep trailing directory separator to create a folder that contains
1719 imap_path = imap_utf8_to_modified_utf7(dirpath);
1721 strtailchomp(dirpath, '/');
1722 Xstrdup_a(new_name, name, {
1727 separator = imap_get_path_separator(IMAP_FOLDER(folder), imap_path);
1728 imap_path_separator_subst(imap_path, separator);
1729 /* remove trailing / for display */
1730 strtailchomp(new_name, '/');
1732 if (strcmp(dirpath, "INBOX") != 0) {
1734 gboolean exist = FALSE;
1738 argbuf = g_ptr_array_new();
1739 r = imap_threaded_list(folder, "", imap_path, &lep_list);
1740 if (r != MAILIMAP_NO_ERROR) {
1741 log_warning(_("can't create mailbox: LIST failed\n"));
1744 ptr_array_free_strings(argbuf);
1745 g_ptr_array_free(argbuf, TRUE);
1750 if (clist_count(lep_list) > 0)
1752 mailimap_list_result_free(lep_list);
1755 ok = imap_cmd_create(session, imap_path);
1756 if (ok != IMAP_SUCCESS) {
1757 log_warning(_("can't create mailbox\n"));
1763 r = imap_threaded_list(folder, "", imap_path, &lep_list);
1764 if (r == MAILIMAP_NO_ERROR) {
1765 GSList *item_list = imap_list_from_lep(IMAP_FOLDER(folder),
1766 lep_list, dirpath, TRUE);
1768 FolderItem *cur_item = FOLDER_ITEM(item_list->data);
1769 no_select = cur_item->no_select;
1770 no_sub = cur_item->no_sub;
1771 g_slist_free(item_list);
1773 mailimap_list_result_free(lep_list);
1780 /* just get flags */
1781 r = imap_threaded_list(folder, "", "INBOX", &lep_list);
1782 if (r == MAILIMAP_NO_ERROR) {
1783 GSList *item_list = imap_list_from_lep(IMAP_FOLDER(folder),
1784 lep_list, dirpath, TRUE);
1786 FolderItem *cur_item = FOLDER_ITEM(item_list->data);
1787 no_select = cur_item->no_select;
1788 no_sub = cur_item->no_sub;
1789 g_slist_free(item_list);
1791 mailimap_list_result_free(lep_list);
1795 new_item = folder_item_new(folder, new_name, dirpath);
1796 new_item->no_select = no_select;
1797 new_item->no_sub = no_sub;
1798 folder_item_append(parent, new_item);
1802 dirpath = folder_item_get_path(new_item);
1803 if (!is_dir_exist(dirpath))
1804 make_dir_hier(dirpath);
1810 static gint imap_rename_folder(Folder *folder, FolderItem *item,
1815 gchar *real_oldpath;
1816 gchar *real_newpath;
1818 gchar *old_cache_dir;
1819 gchar *new_cache_dir;
1820 IMAPSession *session;
1823 gint exists, recent, unseen;
1824 guint32 uid_validity;
1826 g_return_val_if_fail(folder != NULL, -1);
1827 g_return_val_if_fail(item != NULL, -1);
1828 g_return_val_if_fail(item->path != NULL, -1);
1829 g_return_val_if_fail(name != NULL, -1);
1831 session = imap_session_get(folder);
1837 if (strchr(name, imap_get_path_separator(IMAP_FOLDER(folder), item->path)) != NULL) {
1838 g_warning(_("New folder name must not contain the namespace "
1844 real_oldpath = imap_get_real_path(IMAP_FOLDER(folder), item->path);
1846 g_free(session->mbox);
1847 session->mbox = NULL;
1848 ok = imap_cmd_examine(session, "INBOX",
1849 &exists, &recent, &unseen, &uid_validity, FALSE);
1850 if (ok != IMAP_SUCCESS) {
1851 g_free(real_oldpath);
1856 separator = imap_get_path_separator(IMAP_FOLDER(folder), item->path);
1857 if (strchr(item->path, G_DIR_SEPARATOR)) {
1858 dirpath = g_path_get_dirname(item->path);
1859 newpath = g_strconcat(dirpath, G_DIR_SEPARATOR_S, name, NULL);
1862 newpath = g_strdup(name);
1864 real_newpath = imap_utf8_to_modified_utf7(newpath);
1865 imap_path_separator_subst(real_newpath, separator);
1867 ok = imap_cmd_rename(session, real_oldpath, real_newpath);
1868 if (ok != IMAP_SUCCESS) {
1869 log_warning(_("can't rename mailbox: %s to %s\n"),
1870 real_oldpath, real_newpath);
1871 g_free(real_oldpath);
1873 g_free(real_newpath);
1879 item->name = g_strdup(name);
1881 old_cache_dir = folder_item_get_path(item);
1883 paths[0] = g_strdup(item->path);
1885 g_node_traverse(item->node, G_PRE_ORDER, G_TRAVERSE_ALL, -1,
1886 imap_rename_folder_func, paths);
1888 if (is_dir_exist(old_cache_dir)) {
1889 new_cache_dir = folder_item_get_path(item);
1890 if (rename(old_cache_dir, new_cache_dir) < 0) {
1891 FILE_OP_ERROR(old_cache_dir, "rename");
1893 g_free(new_cache_dir);
1896 g_free(old_cache_dir);
1899 g_free(real_oldpath);
1900 g_free(real_newpath);
1905 static gint imap_remove_folder_real(Folder *folder, FolderItem *item)
1908 IMAPSession *session;
1912 g_return_val_if_fail(folder != NULL, -1);
1913 g_return_val_if_fail(item != NULL, -1);
1914 g_return_val_if_fail(item->path != NULL, -1);
1916 session = imap_session_get(folder);
1921 path = imap_get_real_path(IMAP_FOLDER(folder), item->path);
1923 ok = imap_cmd_delete(session, path);
1924 if (ok != IMAP_SUCCESS) {
1925 gchar *tmp = g_strdup_printf("%s%c", path,
1926 imap_get_path_separator(IMAP_FOLDER(folder), path));
1929 ok = imap_cmd_delete(session, path);
1932 if (ok != IMAP_SUCCESS) {
1933 log_warning(_("can't delete mailbox\n"));
1940 cache_dir = folder_item_get_path(item);
1941 if (is_dir_exist(cache_dir) && remove_dir_recursive(cache_dir) < 0)
1942 g_warning("can't remove directory '%s'\n", cache_dir);
1944 folder_item_remove(item);
1949 static gint imap_remove_folder(Folder *folder, FolderItem *item)
1953 g_return_val_if_fail(item != NULL, -1);
1954 g_return_val_if_fail(item->folder != NULL, -1);
1955 g_return_val_if_fail(item->node != NULL, -1);
1957 node = item->node->children;
1958 while (node != NULL) {
1960 if (imap_remove_folder(folder, FOLDER_ITEM(node->data)) < 0)
1964 debug_print("IMAP removing %s\n", item->path);
1966 if (imap_remove_all_msg(folder, item) < 0)
1968 return imap_remove_folder_real(folder, item);
1971 typedef struct _uncached_data {
1972 IMAPSession *session;
1974 MsgNumberList *numlist;
1980 static void *imap_get_uncached_messages_thread(void *data)
1982 uncached_data *stuff = (uncached_data *)data;
1983 IMAPSession *session = stuff->session;
1984 FolderItem *item = stuff->item;
1985 MsgNumberList *numlist = stuff->numlist;
1987 GSList *newlist = NULL;
1988 GSList *llast = NULL;
1989 GSList *seq_list, *cur;
1991 debug_print("uncached_messages\n");
1993 if (session == NULL || item == NULL || item->folder == NULL
1994 || FOLDER_CLASS(item->folder) != &imap_class) {
1999 seq_list = imap_get_lep_set_from_numlist(numlist);
2000 debug_print("get msgs info\n");
2001 for (cur = seq_list; cur != NULL; cur = g_slist_next(cur)) {
2002 struct mailimap_set * imapset;
2008 imapset = cur->data;
2010 r = imap_threaded_fetch_env(session->folder,
2011 imapset, &env_list);
2012 if (r != MAILIMAP_NO_ERROR)
2016 for(i = 0 ; i < carray_count(env_list) ; i ++) {
2017 struct imap_fetch_env_info * info;
2020 info = carray_get(env_list, i);
2021 msginfo = imap_envelope_from_lep(info, item);
2022 if (msginfo == NULL)
2024 msginfo->folder = item;
2026 llast = newlist = g_slist_append(newlist, msginfo);
2028 llast = g_slist_append(llast, msginfo);
2029 llast = llast->next;
2034 imap_fetch_env_free(env_list);
2037 session_set_access_time(SESSION(session));
2042 #define MAX_MSG_NUM 50
2044 static GSList *imap_get_uncached_messages(IMAPSession *session,
2046 MsgNumberList *numlist)
2048 GSList *result = NULL;
2050 uncached_data *data = g_new0(uncached_data, 1);
2055 data->total = g_slist_length(numlist);
2056 debug_print("messages list : %i\n", data->total);
2058 while (cur != NULL) {
2059 GSList * partial_result;
2067 while (count < MAX_MSG_NUM) {
2072 if (newlist == NULL)
2073 llast = newlist = g_slist_append(newlist, p);
2075 llast = g_slist_append(llast, p);
2076 llast = llast->next;
2086 data->session = session;
2088 data->numlist = newlist;
2091 if (prefs_common.work_offline && !inc_offline_should_override()) {
2097 (GSList *)imap_get_uncached_messages_thread(data);
2099 statusbar_progress_all(data->cur,data->total, 1);
2101 g_slist_free(newlist);
2103 result = g_slist_concat(result, partial_result);
2107 statusbar_progress_all(0,0,0);
2108 statusbar_pop_all();
2113 static void imap_delete_all_cached_messages(FolderItem *item)
2117 g_return_if_fail(item != NULL);
2118 g_return_if_fail(item->folder != NULL);
2119 g_return_if_fail(FOLDER_CLASS(item->folder) == &imap_class);
2121 debug_print("Deleting all cached messages...\n");
2123 dir = folder_item_get_path(item);
2124 if (is_dir_exist(dir))
2125 remove_all_numbered_files(dir);
2128 debug_print("done.\n");
2131 static IMAPNameSpace *imap_find_namespace_from_list(GList *ns_list,
2134 IMAPNameSpace *namespace = NULL;
2135 gchar *tmp_path, *name;
2137 if (!path) path = "";
2139 for (; ns_list != NULL; ns_list = ns_list->next) {
2140 IMAPNameSpace *tmp_ns = ns_list->data;
2142 Xstrcat_a(tmp_path, path, "/", return namespace);
2143 Xstrdup_a(name, tmp_ns->name, return namespace);
2144 if (tmp_ns->separator && tmp_ns->separator != '/') {
2145 subst_char(tmp_path, tmp_ns->separator, '/');
2146 subst_char(name, tmp_ns->separator, '/');
2148 if (strncmp(tmp_path, name, strlen(name)) == 0)
2155 static IMAPNameSpace *imap_find_namespace(IMAPFolder *folder,
2158 IMAPNameSpace *namespace;
2160 g_return_val_if_fail(folder != NULL, NULL);
2162 namespace = imap_find_namespace_from_list(folder->ns_personal, path);
2163 if (namespace) return namespace;
2164 namespace = imap_find_namespace_from_list(folder->ns_others, path);
2165 if (namespace) return namespace;
2166 namespace = imap_find_namespace_from_list(folder->ns_shared, path);
2167 if (namespace) return namespace;
2173 static gchar imap_get_path_separator(IMAPFolder *folder, const gchar *path)
2175 IMAPNameSpace *namespace;
2176 gchar separator = '/';
2178 if (folder->last_seen_separator == 0) {
2180 int r = imap_threaded_list((Folder *)folder, "", "", &lep_list);
2181 if (r != MAILIMAP_NO_ERROR) {
2182 log_warning(_("LIST failed\n"));
2186 if (clist_count(lep_list) > 0) {
2187 clistiter * iter = clist_begin(lep_list);
2188 struct mailimap_mailbox_list * mb;
2189 mb = clist_content(iter);
2191 folder->last_seen_separator = mb->mb_delimiter;
2192 debug_print("got separator: %c\n", folder->last_seen_separator);
2194 mailimap_list_result_free(lep_list);
2197 if (folder->last_seen_separator != 0) {
2198 debug_print("using separator: %c\n", folder->last_seen_separator);
2199 return folder->last_seen_separator;
2202 namespace = imap_find_namespace(folder, path);
2203 if (namespace && namespace->separator)
2204 separator = namespace->separator;
2209 static gchar *imap_get_real_path(IMAPFolder *folder, const gchar *path)
2214 g_return_val_if_fail(folder != NULL, NULL);
2215 g_return_val_if_fail(path != NULL, NULL);
2217 real_path = imap_utf8_to_modified_utf7(path);
2218 separator = imap_get_path_separator(folder, path);
2219 imap_path_separator_subst(real_path, separator);
2224 static gint imap_set_message_flags(IMAPSession *session,
2225 MsgNumberList *numlist,
2233 seq_list = imap_get_lep_set_from_numlist(numlist);
2235 for(cur = seq_list ; cur != NULL ; cur = g_slist_next(cur)) {
2236 struct mailimap_set * imapset;
2238 imapset = cur->data;
2240 ok = imap_cmd_store(session, imapset,
2244 imap_lep_set_free(seq_list);
2246 return IMAP_SUCCESS;
2249 typedef struct _select_data {
2250 IMAPSession *session;
2255 guint32 *uid_validity;
2259 static gint imap_select(IMAPSession *session, IMAPFolder *folder,
2261 gint *exists, gint *recent, gint *unseen,
2262 guint32 *uid_validity, gboolean block)
2266 gint exists_, recent_, unseen_;
2267 guint32 uid_validity_;
2269 if (!exists && !recent && !unseen && !uid_validity) {
2270 if (session->mbox && strcmp(session->mbox, path) == 0)
2271 return IMAP_SUCCESS;
2280 uid_validity = &uid_validity_;
2282 g_free(session->mbox);
2283 session->mbox = NULL;
2285 real_path = imap_get_real_path(folder, path);
2287 ok = imap_cmd_select(session, real_path,
2288 exists, recent, unseen, uid_validity, block);
2289 if (ok != IMAP_SUCCESS)
2290 log_warning(_("can't select folder: %s\n"), real_path);
2292 session->mbox = g_strdup(path);
2293 session->folder_content_changed = FALSE;
2300 static gint imap_status(IMAPSession *session, IMAPFolder *folder,
2301 const gchar *path, IMAPFolderItem *item,
2303 guint32 *uid_next, guint32 *uid_validity,
2304 gint *unseen, gboolean block)
2308 struct mailimap_mailbox_data_status * data_status;
2313 real_path = imap_get_real_path(folder, path);
2316 if (time(NULL) - item->last_update >= 5 && item->last_update != 1) {
2317 /* do the full stuff */
2318 item->last_update = 1; /* force update */
2319 debug_print("updating everything\n");
2320 r = imap_status(session, folder, path, item,
2321 &item->c_messages, &item->c_uid_next,
2322 &item->c_uid_validity, &item->c_unseen, block);
2323 if (r != MAILIMAP_NO_ERROR) {
2324 debug_print("status err %d\n", r);
2327 item->last_update = time(NULL);
2329 *messages = item->c_messages;
2331 *uid_next = item->c_uid_next;
2333 *uid_validity = item->c_uid_validity;
2335 *unseen = item->c_unseen;
2337 } else if (time(NULL) - item->last_update < 5) {
2338 /* return cached stuff */
2339 debug_print("using cache\n");
2341 *messages = item->c_messages;
2343 *uid_next = item->c_uid_next;
2345 *uid_validity = item->c_uid_validity;
2347 *unseen = item->c_unseen;
2352 /* if we get there, we're updating cache */
2366 r = imap_threaded_status(FOLDER(folder), real_path,
2367 &data_status, mask);
2370 if (r != MAILIMAP_NO_ERROR) {
2371 debug_print("status err %d\n", r);
2375 if (data_status->st_info_list == NULL) {
2376 mailimap_mailbox_data_status_free(data_status);
2377 debug_print("status->st_info_list == NULL\n");
2382 for(iter = clist_begin(data_status->st_info_list) ; iter != NULL ;
2383 iter = clist_next(iter)) {
2384 struct mailimap_status_info * info;
2386 info = clist_content(iter);
2387 switch (info->st_att) {
2388 case MAILIMAP_STATUS_ATT_MESSAGES:
2389 * messages = info->st_value;
2390 got_values |= 1 << 0;
2393 case MAILIMAP_STATUS_ATT_UIDNEXT:
2394 * uid_next = info->st_value;
2395 got_values |= 1 << 2;
2398 case MAILIMAP_STATUS_ATT_UIDVALIDITY:
2399 * uid_validity = info->st_value;
2400 got_values |= 1 << 3;
2403 case MAILIMAP_STATUS_ATT_UNSEEN:
2404 * unseen = info->st_value;
2405 got_values |= 1 << 4;
2409 mailimap_mailbox_data_status_free(data_status);
2411 if (got_values != mask) {
2412 debug_print("status: incomplete values received (%d)\n", got_values);
2415 return IMAP_SUCCESS;
2418 static void imap_free_capabilities(IMAPSession *session)
2420 slist_free_strings(session->capability);
2421 g_slist_free(session->capability);
2422 session->capability = NULL;
2425 /* low-level IMAP4rev1 commands */
2428 static gint imap_cmd_authenticate(IMAPSession *session, const gchar *user,
2429 const gchar *pass, IMAPAuthType type)
2436 gchar hexdigest[33];
2440 auth_type = "CRAM-MD5";
2442 imap_gen_send(session, "AUTHENTICATE %s", auth_type);
2443 ok = imap_gen_recv(session, &buf);
2444 if (ok != IMAP_SUCCESS || buf[0] != '+' || buf[1] != ' ') {
2449 challenge = g_malloc(strlen(buf + 2) + 1);
2450 challenge_len = base64_decode(challenge, buf + 2, -1);
2451 challenge[challenge_len] = '\0';
2454 md5_hex_hmac(hexdigest, challenge, challenge_len, pass, strlen(pass));
2457 response = g_strdup_printf("%s %s", user, hexdigest);
2458 response64 = g_malloc((strlen(response) + 3) * 2 + 1);
2459 base64_encode(response64, response, strlen(response));
2462 sock_puts(SESSION(session)->sock, response64);
2463 ok = imap_cmd_ok(session, NULL);
2464 if (ok != IMAP_SUCCESS)
2465 log_warning(_("IMAP4 authentication failed.\n"));
2471 static gint imap_cmd_login(IMAPSession *session,
2472 const gchar *user, const gchar *pass,
2478 log_print("IMAP4> Logging %s to %s using %s\n",
2480 SESSION(session)->server,
2482 r = imap_threaded_login(session->folder, user, pass, type);
2483 if (r != MAILIMAP_NO_ERROR) {
2484 log_error("IMAP4< Error logging in to %s\n",
2485 SESSION(session)->server);
2493 static gint imap_cmd_logout(IMAPSession *session)
2495 imap_threaded_disconnect(session->folder);
2497 return IMAP_SUCCESS;
2500 static gint imap_cmd_noop(IMAPSession *session)
2503 unsigned int exists;
2505 r = imap_threaded_noop(session->folder, &exists);
2506 if (r != MAILIMAP_NO_ERROR) {
2507 debug_print("noop err %d\n", r);
2510 session->exists = exists;
2511 session_set_access_time(SESSION(session));
2513 return IMAP_SUCCESS;
2517 static gint imap_cmd_starttls(IMAPSession *session)
2521 r = imap_threaded_starttls(session->folder);
2522 if (r != MAILIMAP_NO_ERROR) {
2523 debug_print("starttls err %d\n", r);
2526 return IMAP_SUCCESS;
2530 static gint imap_cmd_select(IMAPSession *session, const gchar *folder,
2531 gint *exists, gint *recent, gint *unseen,
2532 guint32 *uid_validity, gboolean block)
2536 r = imap_threaded_select(session->folder, folder,
2537 exists, recent, unseen, uid_validity);
2538 if (r != MAILIMAP_NO_ERROR) {
2539 debug_print("select err %d\n", r);
2542 return IMAP_SUCCESS;
2545 static gint imap_cmd_examine(IMAPSession *session, const gchar *folder,
2546 gint *exists, gint *recent, gint *unseen,
2547 guint32 *uid_validity, gboolean block)
2551 r = imap_threaded_examine(session->folder, folder,
2552 exists, recent, unseen, uid_validity);
2553 if (r != MAILIMAP_NO_ERROR) {
2554 debug_print("examine err %d\n", r);
2558 return IMAP_SUCCESS;
2561 static gint imap_cmd_create(IMAPSession *session, const gchar *folder)
2565 r = imap_threaded_create(session->folder, folder);
2566 if (r != MAILIMAP_NO_ERROR) {
2571 return IMAP_SUCCESS;
2574 static gint imap_cmd_rename(IMAPSession *session, const gchar *old_folder,
2575 const gchar *new_folder)
2579 r = imap_threaded_rename(session->folder, old_folder,
2581 if (r != MAILIMAP_NO_ERROR) {
2586 return IMAP_SUCCESS;
2589 static gint imap_cmd_delete(IMAPSession *session, const gchar *folder)
2594 r = imap_threaded_delete(session->folder, folder);
2595 if (r != MAILIMAP_NO_ERROR) {
2600 return IMAP_SUCCESS;
2603 typedef struct _fetch_data {
2604 IMAPSession *session;
2606 const gchar *filename;
2612 static void *imap_cmd_fetch_thread(void *data)
2614 fetch_data *stuff = (fetch_data *)data;
2615 IMAPSession *session = stuff->session;
2616 guint32 uid = stuff->uid;
2617 const gchar *filename = stuff->filename;
2621 r = imap_threaded_fetch_content(session->folder,
2625 r = imap_threaded_fetch_content(session->folder,
2628 if (r != MAILIMAP_NO_ERROR) {
2629 debug_print("fetch err %d\n", r);
2630 return GINT_TO_POINTER(IMAP_ERROR);
2632 return GINT_TO_POINTER(IMAP_SUCCESS);
2635 static gint imap_cmd_fetch(IMAPSession *session, guint32 uid,
2636 const gchar *filename, gboolean headers,
2639 fetch_data *data = g_new0(fetch_data, 1);
2642 data->session = session;
2644 data->filename = filename;
2645 data->headers = headers;
2648 if (prefs_common.work_offline && !inc_offline_should_override()) {
2652 statusbar_print_all(_("Fetching message..."));
2653 result = GPOINTER_TO_INT(imap_cmd_fetch_thread(data));
2654 statusbar_pop_all();
2660 static gint imap_cmd_append(IMAPSession *session, const gchar *destfolder,
2661 const gchar *file, IMAPFlags flags,
2664 struct mailimap_flag_list * flag_list;
2667 g_return_val_if_fail(file != NULL, IMAP_ERROR);
2669 flag_list = imap_flag_to_lep(flags);
2670 r = imap_threaded_append(session->folder, destfolder,
2672 if (new_uid != NULL)
2675 if (r != MAILIMAP_NO_ERROR) {
2676 debug_print("append err %d\n", r);
2679 return IMAP_SUCCESS;
2682 static gint imap_cmd_copy(IMAPSession *session, struct mailimap_set * set,
2683 const gchar *destfolder, GRelation *uid_mapping)
2687 g_return_val_if_fail(session != NULL, IMAP_ERROR);
2688 g_return_val_if_fail(set != NULL, IMAP_ERROR);
2689 g_return_val_if_fail(destfolder != NULL, IMAP_ERROR);
2691 r = imap_threaded_copy(session->folder, set, destfolder);
2692 if (r != MAILIMAP_NO_ERROR) {
2697 return IMAP_SUCCESS;
2700 static gint imap_cmd_store(IMAPSession *session, struct mailimap_set * set,
2701 IMAPFlags flags, int do_add)
2704 struct mailimap_flag_list * flag_list;
2705 struct mailimap_store_att_flags * store_att_flags;
2707 flag_list = imap_flag_to_lep(flags);
2711 mailimap_store_att_flags_new_add_flags_silent(flag_list);
2714 mailimap_store_att_flags_new_remove_flags_silent(flag_list);
2716 r = imap_threaded_store(session->folder, set, store_att_flags);
2717 if (r != MAILIMAP_NO_ERROR) {
2722 return IMAP_SUCCESS;
2725 static gint imap_cmd_expunge(IMAPSession *session)
2729 if (prefs_common.work_offline && !inc_offline_should_override()) {
2733 r = imap_threaded_expunge(session->folder);
2734 if (r != MAILIMAP_NO_ERROR) {
2739 return IMAP_SUCCESS;
2742 static void imap_path_separator_subst(gchar *str, gchar separator)
2745 gboolean in_escape = FALSE;
2747 if (!separator || separator == '/') return;
2749 for (p = str; *p != '\0'; p++) {
2750 if (*p == '/' && !in_escape)
2752 else if (*p == '&' && *(p + 1) != '-' && !in_escape)
2754 else if (*p == '-' && in_escape)
2759 static gchar *imap_modified_utf7_to_utf8(const gchar *mutf7_str)
2761 static iconv_t cd = (iconv_t)-1;
2762 static gboolean iconv_ok = TRUE;
2765 size_t norm_utf7_len;
2767 gchar *to_str, *to_p;
2769 gboolean in_escape = FALSE;
2771 if (!iconv_ok) return g_strdup(mutf7_str);
2773 if (cd == (iconv_t)-1) {
2774 cd = iconv_open(CS_INTERNAL, CS_UTF_7);
2775 if (cd == (iconv_t)-1) {
2776 g_warning("iconv cannot convert UTF-7 to %s\n",
2779 return g_strdup(mutf7_str);
2783 /* modified UTF-7 to normal UTF-7 conversion */
2784 norm_utf7 = g_string_new(NULL);
2786 for (p = mutf7_str; *p != '\0'; p++) {
2787 /* replace: '&' -> '+',
2789 escaped ',' -> '/' */
2790 if (!in_escape && *p == '&') {
2791 if (*(p + 1) != '-') {
2792 g_string_append_c(norm_utf7, '+');
2795 g_string_append_c(norm_utf7, '&');
2798 } else if (in_escape && *p == ',') {
2799 g_string_append_c(norm_utf7, '/');
2800 } else if (in_escape && *p == '-') {
2801 g_string_append_c(norm_utf7, '-');
2804 g_string_append_c(norm_utf7, *p);
2808 norm_utf7_p = norm_utf7->str;
2809 norm_utf7_len = norm_utf7->len;
2810 to_len = strlen(mutf7_str) * 5;
2811 to_p = to_str = g_malloc(to_len + 1);
2813 if (iconv(cd, (ICONV_CONST gchar **)&norm_utf7_p, &norm_utf7_len,
2814 &to_p, &to_len) == -1) {
2815 g_warning(_("iconv cannot convert UTF-7 to %s\n"),
2816 conv_get_locale_charset_str());
2817 g_string_free(norm_utf7, TRUE);
2819 return g_strdup(mutf7_str);
2822 /* second iconv() call for flushing */
2823 iconv(cd, NULL, NULL, &to_p, &to_len);
2824 g_string_free(norm_utf7, TRUE);
2830 static gchar *imap_utf8_to_modified_utf7(const gchar *from)
2832 static iconv_t cd = (iconv_t)-1;
2833 static gboolean iconv_ok = TRUE;
2834 gchar *norm_utf7, *norm_utf7_p;
2835 size_t from_len, norm_utf7_len;
2837 gchar *from_tmp, *to, *p;
2838 gboolean in_escape = FALSE;
2840 if (!iconv_ok) return g_strdup(from);
2842 if (cd == (iconv_t)-1) {
2843 cd = iconv_open(CS_UTF_7, CS_INTERNAL);
2844 if (cd == (iconv_t)-1) {
2845 g_warning(_("iconv cannot convert %s to UTF-7\n"),
2848 return g_strdup(from);
2852 /* UTF-8 to normal UTF-7 conversion */
2853 Xstrdup_a(from_tmp, from, return g_strdup(from));
2854 from_len = strlen(from);
2855 norm_utf7_len = from_len * 5;
2856 Xalloca(norm_utf7, norm_utf7_len + 1, return g_strdup(from));
2857 norm_utf7_p = norm_utf7;
2859 #define IS_PRINT(ch) (isprint(ch) && IS_ASCII(ch))
2861 while (from_len > 0) {
2862 if (*from_tmp == '+') {
2863 *norm_utf7_p++ = '+';
2864 *norm_utf7_p++ = '-';
2868 } else if (IS_PRINT(*(guchar *)from_tmp)) {
2869 /* printable ascii char */
2870 *norm_utf7_p = *from_tmp;
2876 size_t conv_len = 0;
2878 /* unprintable char: convert to UTF-7 */
2880 while (!IS_PRINT(*(guchar *)p) && conv_len < from_len) {
2881 conv_len += g_utf8_skip[*(guchar *)p];
2882 p += g_utf8_skip[*(guchar *)p];
2885 from_len -= conv_len;
2886 if (iconv(cd, (ICONV_CONST gchar **)&from_tmp,
2888 &norm_utf7_p, &norm_utf7_len) == -1) {
2889 g_warning(_("iconv cannot convert UTF-8 to UTF-7\n"));
2890 return g_strdup(from);
2893 /* second iconv() call for flushing */
2894 iconv(cd, NULL, NULL, &norm_utf7_p, &norm_utf7_len);
2900 *norm_utf7_p = '\0';
2901 to_str = g_string_new(NULL);
2902 for (p = norm_utf7; p < norm_utf7_p; p++) {
2903 /* replace: '&' -> "&-",
2906 BASE64 '/' -> ',' */
2907 if (!in_escape && *p == '&') {
2908 g_string_append(to_str, "&-");
2909 } else if (!in_escape && *p == '+') {
2910 if (*(p + 1) == '-') {
2911 g_string_append_c(to_str, '+');
2914 g_string_append_c(to_str, '&');
2917 } else if (in_escape && *p == '/') {
2918 g_string_append_c(to_str, ',');
2919 } else if (in_escape && *p == '-') {
2920 g_string_append_c(to_str, '-');
2923 g_string_append_c(to_str, *p);
2929 g_string_append_c(to_str, '-');
2933 g_string_free(to_str, FALSE);
2938 static gboolean imap_rename_folder_func(GNode *node, gpointer data)
2940 FolderItem *item = node->data;
2941 gchar **paths = data;
2942 const gchar *oldpath = paths[0];
2943 const gchar *newpath = paths[1];
2945 gchar *new_itempath;
2948 oldpathlen = strlen(oldpath);
2949 if (strncmp(oldpath, item->path, oldpathlen) != 0) {
2950 g_warning("path doesn't match: %s, %s\n", oldpath, item->path);
2954 base = item->path + oldpathlen;
2955 while (*base == G_DIR_SEPARATOR) base++;
2957 new_itempath = g_strdup(newpath);
2959 new_itempath = g_strconcat(newpath, G_DIR_SEPARATOR_S, base,
2962 item->path = new_itempath;
2967 typedef struct _get_list_uid_data {
2969 IMAPSession *session;
2970 IMAPFolderItem *item;
2971 GSList **msgnum_list;
2973 } get_list_uid_data;
2975 static void *get_list_of_uids_thread(void *data)
2977 get_list_uid_data *stuff = (get_list_uid_data *)data;
2978 Folder *folder = stuff->folder;
2979 IMAPFolderItem *item = stuff->item;
2980 GSList **msgnum_list = stuff->msgnum_list;
2981 gint ok, nummsgs = 0, lastuid_old;
2982 IMAPSession *session;
2983 GSList *uidlist, *elem;
2984 struct mailimap_set * set;
2985 clist * lep_uidlist;
2988 session = stuff->session;
2989 if (session == NULL) {
2991 return GINT_TO_POINTER(-1);
2993 /* no session locking here, it's already locked by caller */
2994 ok = imap_select(session, IMAP_FOLDER(folder), item->item.path,
2995 NULL, NULL, NULL, NULL, TRUE);
2996 if (ok != IMAP_SUCCESS) {
2998 return GINT_TO_POINTER(-1);
3003 set = mailimap_set_new_interval(item->lastuid + 1, 0);
3005 r = imap_threaded_search(folder, IMAP_SEARCH_TYPE_SIMPLE, set,
3007 if (r == MAILIMAP_NO_ERROR) {
3008 GSList * fetchuid_list;
3011 imap_uid_list_from_lep(lep_uidlist);
3012 uidlist = g_slist_concat(fetchuid_list, uidlist);
3015 GSList * fetchuid_list;
3016 carray * lep_uidtab;
3018 r = imap_threaded_fetch_uid(folder, item->lastuid + 1,
3020 if (r == MAILIMAP_NO_ERROR) {
3022 imap_uid_list_from_lep_tab(lep_uidtab);
3023 uidlist = g_slist_concat(fetchuid_list, uidlist);
3027 lastuid_old = item->lastuid;
3028 *msgnum_list = g_slist_copy(item->uid_list);
3029 nummsgs = g_slist_length(*msgnum_list);
3030 debug_print("Got %d uids from cache\n", g_slist_length(item->uid_list));
3032 for (elem = uidlist; elem != NULL; elem = g_slist_next(elem)) {
3035 msgnum = GPOINTER_TO_INT(elem->data);
3036 if (msgnum > lastuid_old) {
3037 *msgnum_list = g_slist_prepend(*msgnum_list, GINT_TO_POINTER(msgnum));
3038 item->uid_list = g_slist_prepend(item->uid_list, GINT_TO_POINTER(msgnum));
3041 if(msgnum > item->lastuid)
3042 item->lastuid = msgnum;
3045 g_slist_free(uidlist);
3047 return GINT_TO_POINTER(nummsgs);
3050 static gint get_list_of_uids(IMAPSession *session, Folder *folder, IMAPFolderItem *item, GSList **msgnum_list)
3053 get_list_uid_data *data = g_new0(get_list_uid_data, 1);
3055 data->folder = folder;
3057 data->msgnum_list = msgnum_list;
3058 data->session = session;
3059 if (prefs_common.work_offline && !inc_offline_should_override()) {
3064 result = GPOINTER_TO_INT(get_list_of_uids_thread(data));
3070 gint imap_get_num_list(Folder *folder, FolderItem *_item, GSList **msgnum_list, gboolean *old_uids_valid)
3072 IMAPFolderItem *item = (IMAPFolderItem *)_item;
3073 IMAPSession *session;
3074 gint ok, nummsgs = 0, exists, uid_val, uid_next;
3075 GSList *uidlist = NULL;
3077 gboolean selected_folder;
3079 debug_print("get_num_list\n");
3081 g_return_val_if_fail(folder != NULL, -1);
3082 g_return_val_if_fail(item != NULL, -1);
3083 g_return_val_if_fail(item->item.path != NULL, -1);
3084 g_return_val_if_fail(FOLDER_CLASS(folder) == &imap_class, -1);
3085 g_return_val_if_fail(folder->account != NULL, -1);
3087 session = imap_session_get(folder);
3088 g_return_val_if_fail(session != NULL, -1);
3090 statusbar_print_all("Scanning %s...\n", FOLDER_ITEM(item)->path
3091 ? FOLDER_ITEM(item)->path:"");
3093 selected_folder = (session->mbox != NULL) &&
3094 (!strcmp(session->mbox, item->item.path));
3095 if (selected_folder) {
3096 ok = imap_cmd_noop(session);
3097 if (ok != IMAP_SUCCESS) {
3098 debug_print("disconnected!\n");
3099 session = imap_reconnect_if_possible(folder, session);
3100 if (session == NULL) {
3101 statusbar_pop_all();
3106 exists = session->exists;
3108 *old_uids_valid = TRUE;
3110 if (item->use_cache && time(NULL) - item->use_cache < 2) {
3111 exists = item->c_messages;
3112 uid_next = item->c_uid_next;
3113 uid_val = item->c_uid_validity;
3115 debug_print("using cache %d %d %d\n", exists, uid_next, uid_val);
3117 ok = imap_status(session, IMAP_FOLDER(folder), item->item.path, item,
3118 &exists, &uid_next, &uid_val, NULL, FALSE);
3120 item->use_cache = (time_t)0;
3121 if (ok != IMAP_SUCCESS) {
3122 statusbar_pop_all();
3126 if(item->item.mtime == uid_val)
3127 *old_uids_valid = TRUE;
3129 *old_uids_valid = FALSE;
3131 debug_print("Freeing imap uid cache\n");
3133 g_slist_free(item->uid_list);
3134 item->uid_list = NULL;
3136 item->item.mtime = uid_val;
3138 imap_delete_all_cached_messages((FolderItem *)item);
3142 if (!selected_folder)
3143 item->uid_next = uid_next;
3145 /* If old uid_next matches new uid_next we can be sure no message
3146 was added to the folder */
3147 if (( selected_folder && !session->folder_content_changed) ||
3148 (!selected_folder && uid_next == item->uid_next)) {
3149 nummsgs = g_slist_length(item->uid_list);
3151 /* If number of messages is still the same we
3152 know our caches message numbers are still valid,
3153 otherwise if the number of messages has decrease
3154 we discard our cache to start a new scan to find
3155 out which numbers have been removed */
3156 if (exists == nummsgs) {
3157 *msgnum_list = g_slist_copy(item->uid_list);
3158 statusbar_pop_all();
3161 } else if (exists < nummsgs) {
3162 debug_print("Freeing imap uid cache");
3164 g_slist_free(item->uid_list);
3165 item->uid_list = NULL;
3170 *msgnum_list = NULL;
3171 statusbar_pop_all();
3176 nummsgs = get_list_of_uids(session, folder, item, &uidlist);
3179 statusbar_pop_all();
3184 if (nummsgs != exists) {
3185 /* Cache contains more messages then folder, we have cached
3186 an old UID of a message that was removed and new messages
3187 have been added too, otherwise the uid_next check would
3189 debug_print("Freeing imap uid cache");
3191 g_slist_free(item->uid_list);
3192 item->uid_list = NULL;
3194 g_slist_free(*msgnum_list);
3196 nummsgs = get_list_of_uids(session, folder, item, &uidlist);
3199 *msgnum_list = uidlist;
3201 dir = folder_item_get_path((FolderItem *)item);
3202 debug_print("removing old messages from %s\n", dir);
3203 remove_numbered_files_not_in_list(dir, *msgnum_list);
3206 debug_print("get_num_list - ok - %i\n", nummsgs);
3207 statusbar_pop_all();
3212 static MsgInfo *imap_parse_msg(const gchar *file, FolderItem *item)
3217 flags.perm_flags = MSG_NEW|MSG_UNREAD;
3218 flags.tmp_flags = 0;
3220 g_return_val_if_fail(item != NULL, NULL);
3221 g_return_val_if_fail(file != NULL, NULL);
3223 if (folder_has_parent_of_type(item, F_QUEUE)) {
3224 MSG_SET_TMP_FLAGS(flags, MSG_QUEUED);
3225 } else if (folder_has_parent_of_type(item, F_DRAFT)) {
3226 MSG_SET_TMP_FLAGS(flags, MSG_DRAFT);
3229 msginfo = procheader_parse_file(file, flags, FALSE, FALSE);
3230 if (!msginfo) return NULL;
3232 msginfo->plaintext_file = g_strdup(file);
3233 msginfo->folder = item;
3238 GSList *imap_get_msginfos(Folder *folder, FolderItem *item,
3239 GSList *msgnum_list)
3241 IMAPSession *session;
3242 MsgInfoList *ret = NULL;
3245 debug_print("get_msginfos\n");
3247 g_return_val_if_fail(folder != NULL, NULL);
3248 g_return_val_if_fail(item != NULL, NULL);
3249 g_return_val_if_fail(msgnum_list != NULL, NULL);
3251 session = imap_session_get(folder);
3252 g_return_val_if_fail(session != NULL, NULL);
3254 debug_print("IMAP getting msginfos\n");
3255 ok = imap_select(session, IMAP_FOLDER(folder), item->path,
3256 NULL, NULL, NULL, NULL, FALSE);
3257 if (ok != IMAP_SUCCESS) {
3261 if (!(folder_has_parent_of_type(item, F_DRAFT) ||
3262 folder_has_parent_of_type(item, F_QUEUE))) {
3263 ret = g_slist_concat(ret,
3264 imap_get_uncached_messages(session, item,
3267 MsgNumberList *sorted_list, *elem;
3268 gint startnum, lastnum;
3270 sorted_list = g_slist_sort(g_slist_copy(msgnum_list), g_int_compare);
3272 startnum = lastnum = GPOINTER_TO_INT(sorted_list->data);
3274 for (elem = sorted_list;; elem = g_slist_next(elem)) {
3278 num = GPOINTER_TO_INT(elem->data);
3280 if (num > lastnum + 1 || elem == NULL) {
3282 for (i = startnum; i <= lastnum; ++i) {
3285 file = imap_fetch_msg(folder, item, i);
3287 MsgInfo *msginfo = imap_parse_msg(file, item);
3288 if (msginfo != NULL) {
3289 msginfo->msgnum = i;
3290 ret = g_slist_append(ret, msginfo);
3304 g_slist_free(sorted_list);
3310 MsgInfo *imap_get_msginfo(Folder *folder, FolderItem *item, gint uid)
3312 MsgInfo *msginfo = NULL;
3313 MsgInfoList *msginfolist;
3314 MsgNumberList numlist;
3316 numlist.next = NULL;
3317 numlist.data = GINT_TO_POINTER(uid);
3319 msginfolist = imap_get_msginfos(folder, item, &numlist);
3320 if (msginfolist != NULL) {
3321 msginfo = msginfolist->data;
3322 g_slist_free(msginfolist);
3328 gboolean imap_scan_required(Folder *folder, FolderItem *_item)
3330 IMAPSession *session;
3331 IMAPFolderItem *item = (IMAPFolderItem *)_item;
3332 gint ok, exists = 0, unseen = 0;
3333 guint32 uid_next, uid_val;
3334 gboolean selected_folder;
3336 g_return_val_if_fail(folder != NULL, FALSE);
3337 g_return_val_if_fail(item != NULL, FALSE);
3338 g_return_val_if_fail(item->item.folder != NULL, FALSE);
3339 g_return_val_if_fail(FOLDER_CLASS(item->item.folder) == &imap_class, FALSE);
3341 if (item->item.path == NULL)
3344 session = imap_session_get(folder);
3345 g_return_val_if_fail(session != NULL, FALSE);
3347 selected_folder = (session->mbox != NULL) &&
3348 (!strcmp(session->mbox, item->item.path));
3349 if (selected_folder) {
3350 ok = imap_cmd_noop(session);
3351 if (ok != IMAP_SUCCESS) {
3352 debug_print("disconnected!\n");
3353 session = imap_reconnect_if_possible(folder, session);
3354 if (session == NULL)
3359 if (session->folder_content_changed
3360 || session->exists != item->item.total_msgs) {
3365 ok = imap_status(session, IMAP_FOLDER(folder), item->item.path, IMAP_FOLDER_ITEM(item),
3366 &exists, &uid_next, &uid_val, &unseen, FALSE);
3367 if (ok != IMAP_SUCCESS) {
3372 item->use_cache = time(NULL);
3373 item->c_messages = exists;
3374 item->c_uid_next = uid_next;
3375 item->c_uid_validity = uid_val;
3376 item->c_unseen = unseen;
3378 if ((uid_next != item->uid_next) || (exists != item->item.total_msgs)) {
3387 void imap_change_flags(Folder *folder, FolderItem *item, MsgInfo *msginfo, MsgPermFlags newflags)
3389 IMAPSession *session;
3390 IMAPFlags flags_set = 0, flags_unset = 0;
3391 gint ok = IMAP_SUCCESS;
3392 MsgNumberList numlist;
3393 hashtable_data *ht_data = NULL;
3395 g_return_if_fail(folder != NULL);
3396 g_return_if_fail(folder->klass == &imap_class);
3397 g_return_if_fail(item != NULL);
3398 g_return_if_fail(item->folder == folder);
3399 g_return_if_fail(msginfo != NULL);
3400 g_return_if_fail(msginfo->folder == item);
3402 session = imap_session_get(folder);
3407 if ((ok = imap_select(session, IMAP_FOLDER(folder), msginfo->folder->path,
3408 NULL, NULL, NULL, NULL, FALSE)) != IMAP_SUCCESS) {
3413 if (!MSG_IS_MARKED(msginfo->flags) && (newflags & MSG_MARKED))
3414 flags_set |= IMAP_FLAG_FLAGGED;
3415 if ( MSG_IS_MARKED(msginfo->flags) && !(newflags & MSG_MARKED))
3416 flags_unset |= IMAP_FLAG_FLAGGED;
3418 if (!MSG_IS_UNREAD(msginfo->flags) && (newflags & MSG_UNREAD))
3419 flags_unset |= IMAP_FLAG_SEEN;
3420 if ( MSG_IS_UNREAD(msginfo->flags) && !(newflags & MSG_UNREAD))
3421 flags_set |= IMAP_FLAG_SEEN;
3423 if (!MSG_IS_REPLIED(msginfo->flags) && (newflags & MSG_REPLIED))
3424 flags_set |= IMAP_FLAG_ANSWERED;
3425 if ( MSG_IS_REPLIED(msginfo->flags) && !(newflags & MSG_REPLIED))
3426 flags_unset |= IMAP_FLAG_ANSWERED;
3428 if (!MSG_IS_DELETED(msginfo->flags) && (newflags & MSG_DELETED))
3429 flags_set |= IMAP_FLAG_DELETED;
3430 if ( MSG_IS_DELETED(msginfo->flags) && !(newflags & MSG_DELETED))
3431 flags_unset |= IMAP_FLAG_DELETED;
3433 numlist.next = NULL;
3434 numlist.data = GINT_TO_POINTER(msginfo->msgnum);
3436 if (IMAP_FOLDER_ITEM(item)->batching) {
3437 /* instead of performing an UID STORE command for each message change,
3438 * as a lot of them can change "together", we just fill in hashtables
3439 * and defer the treatment so that we're able to send only one
3442 debug_print("IMAP batch mode on, deferring flags change\n");
3444 ht_data = g_hash_table_lookup(IMAP_FOLDER_ITEM(item)->flags_set_table,
3445 GINT_TO_POINTER(flags_set));
3446 if (ht_data == NULL) {
3447 ht_data = g_new0(hashtable_data, 1);
3448 ht_data->session = session;
3449 ht_data->item = IMAP_FOLDER_ITEM(item);
3450 g_hash_table_insert(IMAP_FOLDER_ITEM(item)->flags_set_table,
3451 GINT_TO_POINTER(flags_set), ht_data);
3453 if (!g_slist_find(ht_data->msglist, GINT_TO_POINTER(msginfo->msgnum)))
3454 ht_data->msglist = g_slist_prepend(ht_data->msglist, GINT_TO_POINTER(msginfo->msgnum));
3457 ht_data = g_hash_table_lookup(IMAP_FOLDER_ITEM(item)->flags_unset_table,
3458 GINT_TO_POINTER(flags_unset));
3459 if (ht_data == NULL) {
3460 ht_data = g_new0(hashtable_data, 1);
3461 ht_data->session = session;
3462 ht_data->item = IMAP_FOLDER_ITEM(item);
3463 g_hash_table_insert(IMAP_FOLDER_ITEM(item)->flags_unset_table,
3464 GINT_TO_POINTER(flags_unset), ht_data);
3466 if (!g_slist_find(ht_data->msglist, GINT_TO_POINTER(msginfo->msgnum)))
3467 ht_data->msglist = g_slist_prepend(ht_data->msglist,
3468 GINT_TO_POINTER(msginfo->msgnum));
3471 debug_print("IMAP changing flags\n");
3473 ok = imap_set_message_flags(session, &numlist, flags_set, TRUE);
3474 if (ok != IMAP_SUCCESS) {
3481 ok = imap_set_message_flags(session, &numlist, flags_unset, FALSE);
3482 if (ok != IMAP_SUCCESS) {
3488 msginfo->flags.perm_flags = newflags;
3493 static gint imap_remove_msg(Folder *folder, FolderItem *item, gint uid)
3496 IMAPSession *session;
3498 MsgNumberList numlist;
3500 g_return_val_if_fail(folder != NULL, -1);
3501 g_return_val_if_fail(FOLDER_CLASS(folder) == &imap_class, -1);
3502 g_return_val_if_fail(item != NULL, -1);
3504 session = imap_session_get(folder);
3505 if (!session) return -1;
3507 ok = imap_select(session, IMAP_FOLDER(folder), item->path,
3508 NULL, NULL, NULL, NULL, FALSE);
3509 if (ok != IMAP_SUCCESS) {
3513 numlist.next = NULL;
3514 numlist.data = GINT_TO_POINTER(uid);
3516 ok = imap_set_message_flags
3517 (IMAP_SESSION(REMOTE_FOLDER(folder)->session),
3518 &numlist, IMAP_FLAG_DELETED, TRUE);
3519 if (ok != IMAP_SUCCESS) {
3520 log_warning(_("can't set deleted flags: %d\n"), uid);
3525 if (!session->uidplus) {
3526 ok = imap_cmd_expunge(session);
3530 uidstr = g_strdup_printf("%u", uid);
3531 ok = imap_cmd_expunge(session);
3534 if (ok != IMAP_SUCCESS) {
3535 log_warning(_("can't expunge\n"));
3540 IMAP_FOLDER_ITEM(item)->uid_list = g_slist_remove(
3541 IMAP_FOLDER_ITEM(item)->uid_list, numlist.data);
3542 dir = folder_item_get_path(item);
3543 if (is_dir_exist(dir))
3544 remove_numbered_files(dir, uid, uid);
3547 return IMAP_SUCCESS;
3550 static gint compare_msginfo(gconstpointer a, gconstpointer b)
3552 return ((MsgInfo *)a)->msgnum - ((MsgInfo *)b)->msgnum;
3555 static guint gslist_find_next_num(MsgNumberList **list, guint num)
3559 g_return_val_if_fail(list != NULL, -1);
3561 for (elem = *list; elem != NULL; elem = g_slist_next(elem))
3562 if (GPOINTER_TO_INT(elem->data) >= num)
3565 return elem != NULL ? GPOINTER_TO_INT(elem->data) : (gint)-1;
3569 * NEW and DELETED flags are not syncronized
3570 * - The NEW/RECENT flags in IMAP folders can not really be directly
3571 * modified by Sylpheed
3572 * - The DELETE/DELETED flag in IMAP and Sylpheed don't have the same
3573 * meaning, in IMAP it always removes the messages from the FolderItem
3574 * in Sylpheed it can mean to move the message to trash
3577 typedef struct _get_flags_data {
3580 MsgInfoList *msginfo_list;
3581 GRelation *msgflags;
3582 gboolean full_search;
3586 static /*gint*/ void *imap_get_flags_thread(void *data)
3588 get_flags_data *stuff = (get_flags_data *)data;
3589 Folder *folder = stuff->folder;
3590 FolderItem *item = stuff->item;
3591 MsgInfoList *msginfo_list = stuff->msginfo_list;
3592 GRelation *msgflags = stuff->msgflags;
3593 gboolean full_search = stuff->full_search;
3594 IMAPSession *session;
3595 GSList *sorted_list = NULL;
3596 GSList *unseen = NULL, *answered = NULL, *flagged = NULL, *deleted = NULL;
3597 GSList *p_unseen, *p_answered, *p_flagged, *p_deleted;
3599 GSList *seq_list, *cur;
3600 gboolean reverse_seen = FALSE;
3603 gint exists_cnt, unseen_cnt;
3604 gboolean selected_folder;
3606 if (folder == NULL || item == NULL) {
3608 return GINT_TO_POINTER(-1);
3611 session = imap_session_get(folder);
3612 if (session == NULL) {
3614 return GINT_TO_POINTER(-1);
3617 selected_folder = (session->mbox != NULL) &&
3618 (!strcmp(session->mbox, item->path));
3620 if (!selected_folder) {
3621 ok = imap_select(session, IMAP_FOLDER(folder), item->path,
3622 &exists_cnt, NULL, &unseen_cnt, NULL, TRUE);
3623 if (ok != IMAP_SUCCESS) {
3626 return GINT_TO_POINTER(-1);
3629 if (unseen_cnt > exists_cnt / 2)
3630 reverse_seen = TRUE;
3633 if (item->unread_msgs > item->total_msgs / 2)
3634 reverse_seen = TRUE;
3637 cmd_buf = g_string_new(NULL);
3639 sorted_list = g_slist_sort(g_slist_copy(msginfo_list), compare_msginfo);
3641 seq_list = imap_get_lep_set_from_msglist(msginfo_list);
3643 struct mailimap_set * set;
3644 set = mailimap_set_new_interval(1, 0);
3645 seq_list = g_slist_append(NULL, set);
3648 for (cur = seq_list; cur != NULL; cur = g_slist_next(cur)) {
3649 struct mailimap_set * imapset;
3650 clist * lep_uidlist;
3653 imapset = cur->data;
3655 r = imap_threaded_search(folder, IMAP_SEARCH_TYPE_SEEN,
3656 imapset, &lep_uidlist);
3659 r = imap_threaded_search(folder,
3660 IMAP_SEARCH_TYPE_UNSEEN,
3661 imapset, &lep_uidlist);
3663 if (r == MAILIMAP_NO_ERROR) {
3666 uidlist = imap_uid_list_from_lep(lep_uidlist);
3667 mailimap_search_result_free(lep_uidlist);
3669 unseen = g_slist_concat(unseen, uidlist);
3672 r = imap_threaded_search(folder, IMAP_SEARCH_TYPE_ANSWERED,
3673 imapset, &lep_uidlist);
3674 if (r == MAILIMAP_NO_ERROR) {
3677 uidlist = imap_uid_list_from_lep(lep_uidlist);
3678 mailimap_search_result_free(lep_uidlist);
3680 answered = g_slist_concat(answered, uidlist);
3683 r = imap_threaded_search(folder, IMAP_SEARCH_TYPE_FLAGGED,
3684 imapset, &lep_uidlist);
3685 if (r == MAILIMAP_NO_ERROR) {
3688 uidlist = imap_uid_list_from_lep(lep_uidlist);
3689 mailimap_search_result_free(lep_uidlist);
3691 flagged = g_slist_concat(flagged, uidlist);
3694 r = imap_threaded_search(folder, IMAP_SEARCH_TYPE_DELETED,
3695 imapset, &lep_uidlist);
3696 if (r == MAILIMAP_NO_ERROR) {
3699 uidlist = imap_uid_list_from_lep(lep_uidlist);
3700 mailimap_search_result_free(lep_uidlist);
3702 deleted = g_slist_concat(deleted, uidlist);
3707 p_answered = answered;
3708 p_flagged = flagged;
3709 p_deleted = deleted;
3711 for (elem = sorted_list; elem != NULL; elem = g_slist_next(elem)) {
3716 msginfo = (MsgInfo *) elem->data;
3717 flags = msginfo->flags.perm_flags;
3718 wasnew = (flags & MSG_NEW);
3719 flags &= ~((reverse_seen ? 0 : MSG_UNREAD | MSG_NEW) | MSG_REPLIED | MSG_MARKED);
3721 flags |= MSG_UNREAD | (wasnew ? MSG_NEW : 0);
3722 if (gslist_find_next_num(&p_unseen, msginfo->msgnum) == msginfo->msgnum) {
3723 if (!reverse_seen) {
3724 flags |= MSG_UNREAD | (wasnew ? MSG_NEW : 0);
3726 flags &= ~(MSG_UNREAD | MSG_NEW);
3729 if (gslist_find_next_num(&p_answered, msginfo->msgnum) == msginfo->msgnum)
3730 flags |= MSG_REPLIED;
3732 flags &= ~MSG_REPLIED;
3733 if (gslist_find_next_num(&p_flagged, msginfo->msgnum) == msginfo->msgnum)
3734 flags |= MSG_MARKED;
3736 flags &= ~MSG_MARKED;
3737 if (gslist_find_next_num(&p_deleted, msginfo->msgnum) == msginfo->msgnum)
3738 flags |= MSG_DELETED;
3740 flags &= ~MSG_DELETED;
3741 g_relation_insert(msgflags, msginfo, GINT_TO_POINTER(flags));
3744 imap_lep_set_free(seq_list);
3745 g_slist_free(flagged);
3746 g_slist_free(deleted);
3747 g_slist_free(answered);
3748 g_slist_free(unseen);
3749 g_slist_free(sorted_list);
3750 g_string_free(cmd_buf, TRUE);
3754 return GINT_TO_POINTER(0);
3757 static gint imap_get_flags(Folder *folder, FolderItem *item,
3758 MsgInfoList *msginfo_list, GRelation *msgflags)
3761 get_flags_data *data = g_new0(get_flags_data, 1);
3763 data->folder = folder;
3765 data->msginfo_list = msginfo_list;
3766 data->msgflags = msgflags;
3767 data->full_search = FALSE;
3769 GSList *tmp = NULL, *cur;
3771 if (prefs_common.work_offline && !inc_offline_should_override()) {
3776 tmp = folder_item_get_msg_list(item);
3778 if (g_slist_length(tmp) == g_slist_length(msginfo_list))
3779 data->full_search = TRUE;
3781 for (cur = tmp; cur; cur = cur->next)
3782 procmsg_msginfo_free((MsgInfo *)cur->data);
3786 result = GPOINTER_TO_INT(imap_get_flags_thread(data));
3793 static gboolean process_flags(gpointer key, gpointer value, gpointer user_data)
3795 gboolean flags_set = GPOINTER_TO_INT(user_data);
3796 gint flags_value = GPOINTER_TO_INT(key);
3797 hashtable_data *data = (hashtable_data *)value;
3798 IMAPFolderItem *_item = data->item;
3799 FolderItem *item = (FolderItem *)_item;
3800 gint ok = IMAP_ERROR;
3801 IMAPSession *session = imap_session_get(item->folder);
3803 data->msglist = g_slist_reverse(data->msglist);
3805 debug_print("IMAP %ssetting flags to %d for %d messages\n",
3808 g_slist_length(data->msglist));
3812 ok = imap_select(session, IMAP_FOLDER(item->folder), item->path,
3813 NULL, NULL, NULL, NULL, FALSE);
3815 if (ok == IMAP_SUCCESS) {
3816 imap_set_message_flags(data->session, data->msglist, flags_value, flags_set);
3818 g_warning("can't select mailbox %s\n", item->path);
3822 g_slist_free(data->msglist);
3827 static void process_hashtable(IMAPFolderItem *item)
3829 if (item->flags_set_table) {
3830 g_hash_table_foreach_remove(item->flags_set_table, process_flags, GINT_TO_POINTER(TRUE));
3831 g_free(item->flags_set_table);
3832 item->flags_set_table = NULL;
3834 if (item->flags_unset_table) {
3835 g_hash_table_foreach_remove(item->flags_unset_table, process_flags, GINT_TO_POINTER(FALSE));
3836 g_free(item->flags_unset_table);
3837 item->flags_unset_table = NULL;
3841 static void imap_set_batch (Folder *folder, FolderItem *_item, gboolean batch)
3843 IMAPFolderItem *item = (IMAPFolderItem *)_item;
3845 g_return_if_fail(item != NULL);
3847 if (item->batching == batch)
3851 item->batching = TRUE;
3852 debug_print("IMAP switching to batch mode\n");
3853 if (!item->flags_set_table) {
3854 item->flags_set_table = g_hash_table_new(NULL, g_direct_equal);
3856 if (!item->flags_unset_table) {
3857 item->flags_unset_table = g_hash_table_new(NULL, g_direct_equal);
3860 debug_print("IMAP switching away from batch mode\n");
3862 process_hashtable(item);
3863 item->batching = FALSE;
3869 /* data types conversion libetpan <-> sylpheed */
3873 #define ETPAN_IMAP_MB_MARKED 1
3874 #define ETPAN_IMAP_MB_UNMARKED 2
3875 #define ETPAN_IMAP_MB_NOSELECT 4
3876 #define ETPAN_IMAP_MB_NOINFERIORS 8
3878 static int imap_flags_to_flags(struct mailimap_mbx_list_flags * imap_flags)
3884 if (imap_flags->mbf_type == MAILIMAP_MBX_LIST_FLAGS_SFLAG) {
3885 switch (imap_flags->mbf_sflag) {
3886 case MAILIMAP_MBX_LIST_SFLAG_MARKED:
3887 flags |= ETPAN_IMAP_MB_MARKED;
3889 case MAILIMAP_MBX_LIST_SFLAG_NOSELECT:
3890 flags |= ETPAN_IMAP_MB_NOSELECT;
3892 case MAILIMAP_MBX_LIST_SFLAG_UNMARKED:
3893 flags |= ETPAN_IMAP_MB_UNMARKED;
3898 for(cur = clist_begin(imap_flags->mbf_oflags) ; cur != NULL ;
3899 cur = clist_next(cur)) {
3900 struct mailimap_mbx_list_oflag * oflag;
3902 oflag = clist_content(cur);
3904 switch (oflag->of_type) {
3905 case MAILIMAP_MBX_LIST_OFLAG_NOINFERIORS:
3906 flags |= ETPAN_IMAP_MB_NOINFERIORS;
3914 static GSList * imap_list_from_lep(IMAPFolder * folder,
3915 clist * list, const gchar * real_path, gboolean all)
3922 for(iter = clist_begin(list) ; iter != NULL ;
3923 iter = clist_next(iter)) {
3924 struct mailimap_mailbox_list * mb;
3932 FolderItem *new_item;
3934 mb = clist_content(iter);
3940 if (mb->mb_flag != NULL)
3941 flags = imap_flags_to_flags(mb->mb_flag);
3943 delimiter = mb->mb_delimiter;
3946 dup_name = strdup(name);
3947 if (delimiter != '\0')
3948 subst_char(dup_name, delimiter, '/');
3950 base = g_path_get_basename(dup_name);
3951 if (base[0] == '.') {
3957 if (!all && strcmp(dup_name, real_path) == 0) {
3963 if (!all && dup_name[strlen(dup_name)-1] == '/') {
3969 loc_name = imap_modified_utf7_to_utf8(base);
3970 loc_path = imap_modified_utf7_to_utf8(dup_name);
3972 new_item = folder_item_new(FOLDER(folder), loc_name, loc_path);
3973 if ((flags & ETPAN_IMAP_MB_NOINFERIORS) != 0)
3974 new_item->no_sub = TRUE;
3975 if (strcmp(dup_name, "INBOX") != 0 &&
3976 ((flags & ETPAN_IMAP_MB_NOSELECT) != 0))
3977 new_item->no_select = TRUE;
3979 item_list = g_slist_append(item_list, new_item);
3981 debug_print("folder '%s' found.\n", loc_path);
3992 static GSList * imap_get_lep_set_from_numlist(MsgNumberList *numlist)
3994 GSList *sorted_list, *cur;
3995 guint first, last, next;
3996 GSList *ret_list = NULL;
3998 struct mailimap_set * current_set;
3999 unsigned int item_count;
4001 if (numlist == NULL)
4005 current_set = mailimap_set_new_empty();
4007 sorted_list = g_slist_copy(numlist);
4008 sorted_list = g_slist_sort(sorted_list, g_int_compare);
4010 first = GPOINTER_TO_INT(sorted_list->data);
4013 for (cur = sorted_list; cur != NULL; cur = g_slist_next(cur)) {
4014 if (GPOINTER_TO_INT(cur->data) == 0)
4019 last = GPOINTER_TO_INT(cur->data);
4021 next = GPOINTER_TO_INT(cur->next->data);
4025 if (last + 1 != next || next == 0) {
4027 struct mailimap_set_item * item;
4028 item = mailimap_set_item_new(first, last);
4029 mailimap_set_add(current_set, item);
4034 if (count >= IMAP_SET_MAX_COUNT) {
4035 ret_list = g_slist_append(ret_list,
4037 current_set = mailimap_set_new_empty();
4044 if (clist_count(current_set->set_list) > 0) {
4045 ret_list = g_slist_append(ret_list,
4049 g_slist_free(sorted_list);
4054 static GSList * imap_get_lep_set_from_msglist(MsgInfoList *msglist)
4056 MsgNumberList *numlist = NULL;
4060 for (cur = msglist; cur != NULL; cur = g_slist_next(cur)) {
4061 MsgInfo *msginfo = (MsgInfo *) cur->data;
4063 numlist = g_slist_append(numlist, GINT_TO_POINTER(msginfo->msgnum));
4065 seq_list = imap_get_lep_set_from_numlist(numlist);
4066 g_slist_free(numlist);
4071 static GSList * imap_uid_list_from_lep(clist * list)
4078 for(iter = clist_begin(list) ; iter != NULL ;
4079 iter = clist_next(iter)) {
4082 puid = clist_content(iter);
4083 result = g_slist_append(result, GINT_TO_POINTER(* puid));
4089 static GSList * imap_uid_list_from_lep_tab(carray * list)
4096 for(i = 0 ; i < carray_count(list) ; i ++) {
4099 puid = carray_get(list, i);
4100 result = g_slist_append(result, GINT_TO_POINTER(* puid));
4106 static MsgInfo *imap_envelope_from_lep(struct imap_fetch_env_info * info,
4109 MsgInfo *msginfo = NULL;
4112 MsgFlags flags = {0, 0};
4114 if (info->headers == NULL)
4117 MSG_SET_TMP_FLAGS(flags, MSG_IMAP);
4118 if (folder_has_parent_of_type(item, F_QUEUE)) {
4119 MSG_SET_TMP_FLAGS(flags, MSG_QUEUED);
4120 } else if (folder_has_parent_of_type(item, F_DRAFT)) {
4121 MSG_SET_TMP_FLAGS(flags, MSG_DRAFT);
4123 flags.perm_flags = info->flags;
4127 msginfo = procheader_parse_str(info->headers, flags, FALSE, FALSE);
4130 msginfo->msgnum = uid;
4131 msginfo->size = size;
4137 static void imap_lep_set_free(GSList *seq_list)
4141 for(cur = seq_list ; cur != NULL ; cur = g_slist_next(cur)) {
4142 struct mailimap_set * imapset;
4144 imapset = cur->data;
4145 mailimap_set_free(imapset);
4147 g_slist_free(seq_list);
4150 static struct mailimap_flag_list * imap_flag_to_lep(IMAPFlags flags)
4152 struct mailimap_flag_list * flag_list;
4154 flag_list = mailimap_flag_list_new_empty();
4156 if (IMAP_IS_SEEN(flags))
4157 mailimap_flag_list_add(flag_list,
4158 mailimap_flag_new_seen());
4159 if (IMAP_IS_ANSWERED(flags))
4160 mailimap_flag_list_add(flag_list,
4161 mailimap_flag_new_answered());
4162 if (IMAP_IS_FLAGGED(flags))
4163 mailimap_flag_list_add(flag_list,
4164 mailimap_flag_new_flagged());
4165 if (IMAP_IS_DELETED(flags))
4166 mailimap_flag_list_add(flag_list,
4167 mailimap_flag_new_deleted());
4168 if (IMAP_IS_DRAFT(flags))
4169 mailimap_flag_list_add(flag_list,
4170 mailimap_flag_new_draft());
4175 guint imap_folder_get_refcnt(Folder *folder)
4177 return ((IMAPFolder *)folder)->refcnt;
4180 void imap_folder_ref(Folder *folder)
4182 ((IMAPFolder *)folder)->refcnt++;
4185 void imap_folder_unref(Folder *folder)
4187 if (((IMAPFolder *)folder)->refcnt > 0)
4188 ((IMAPFolder *)folder)->refcnt--;
4191 #else /* HAVE_LIBETPAN */
4193 static FolderClass imap_class;
4195 static Folder *imap_folder_new (const gchar *name,
4200 static gint imap_create_tree (Folder *folder)
4204 static FolderItem *imap_create_folder (Folder *folder,
4210 static gint imap_rename_folder (Folder *folder,
4217 FolderClass *imap_get_class(void)
4219 if (imap_class.idstr == NULL) {
4220 imap_class.type = F_IMAP;
4221 imap_class.idstr = "imap";
4222 imap_class.uistr = "IMAP4";
4224 imap_class.new_folder = imap_folder_new;
4225 imap_class.create_tree = imap_create_tree;
4226 imap_class.create_folder = imap_create_folder;
4227 imap_class.rename_folder = imap_rename_folder;
4228 /* nothing implemented */
4235 void imap_synchronise(FolderItem *item)
4237 imap_gtk_synchronise(item);