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);
1109 statusbar_progress_all(0,0,0);
1110 statusbar_pop_all();
1119 static gint imap_do_copy_msgs(Folder *folder, FolderItem *dest,
1120 MsgInfoList *msglist, GRelation *relation)
1124 GSList *seq_list, *cur;
1126 IMAPSession *session;
1127 gint ok = IMAP_SUCCESS;
1128 GRelation *uid_mapping;
1131 g_return_val_if_fail(folder != NULL, -1);
1132 g_return_val_if_fail(dest != NULL, -1);
1133 g_return_val_if_fail(msglist != NULL, -1);
1135 session = imap_session_get(folder);
1141 msginfo = (MsgInfo *)msglist->data;
1143 src = msginfo->folder;
1145 g_warning("the src folder is identical to the dest.\n");
1150 ok = imap_select(session, IMAP_FOLDER(folder), msginfo->folder->path,
1151 NULL, NULL, NULL, NULL, FALSE);
1152 if (ok != IMAP_SUCCESS) {
1157 destdir = imap_get_real_path(IMAP_FOLDER(folder), dest->path);
1158 seq_list = imap_get_lep_set_from_msglist(msglist);
1159 uid_mapping = g_relation_new(2);
1160 g_relation_index(uid_mapping, 0, g_direct_hash, g_direct_equal);
1162 statusbar_print_all(_("Copying messages..."));
1163 for (cur = seq_list; cur != NULL; cur = g_slist_next(cur)) {
1164 struct mailimap_set * seq_set;
1165 seq_set = cur->data;
1167 debug_print("Copying messages from %s to %s ...\n",
1168 src->path, destdir);
1170 ok = imap_cmd_copy(session, seq_set, destdir, uid_mapping);
1171 if (ok != IMAP_SUCCESS) {
1172 g_relation_destroy(uid_mapping);
1173 imap_lep_set_free(seq_list);
1179 for (cur = msglist; cur != NULL; cur = g_slist_next(cur)) {
1180 MsgInfo *msginfo = (MsgInfo *)cur->data;
1183 tuples = g_relation_select(uid_mapping,
1184 GINT_TO_POINTER(msginfo->msgnum),
1186 if (tuples->len > 0) {
1187 gint num = GPOINTER_TO_INT(g_tuples_index(tuples, 0, 1));
1188 g_relation_insert(relation, msginfo,
1189 GPOINTER_TO_INT(num));
1193 g_relation_insert(relation, msginfo,
1194 GPOINTER_TO_INT(0));
1195 g_tuples_destroy(tuples);
1197 statusbar_pop_all();
1199 g_relation_destroy(uid_mapping);
1200 imap_lep_set_free(seq_list);
1204 IMAP_FOLDER_ITEM(dest)->lastuid = 0;
1205 IMAP_FOLDER_ITEM(dest)->uid_next = 0;
1206 g_slist_free(IMAP_FOLDER_ITEM(dest)->uid_list);
1207 IMAP_FOLDER_ITEM(dest)->uid_list = NULL;
1210 if (ok == IMAP_SUCCESS)
1216 static gint imap_copy_msg(Folder *folder, FolderItem *dest, MsgInfo *msginfo)
1220 g_return_val_if_fail(msginfo != NULL, -1);
1222 msglist.data = msginfo;
1223 msglist.next = NULL;
1225 return imap_copy_msgs(folder, dest, &msglist, NULL);
1228 static gint imap_copy_msgs(Folder *folder, FolderItem *dest,
1229 MsgInfoList *msglist, GRelation *relation)
1235 g_return_val_if_fail(folder != NULL, -1);
1236 g_return_val_if_fail(dest != NULL, -1);
1237 g_return_val_if_fail(msglist != NULL, -1);
1239 msginfo = (MsgInfo *)msglist->data;
1240 g_return_val_if_fail(msginfo->folder != NULL, -1);
1242 if (folder == msginfo->folder->folder &&
1243 !folder_has_parent_of_type(msginfo->folder, F_DRAFT) &&
1244 !folder_has_parent_of_type(msginfo->folder, F_QUEUE)) {
1245 ret = imap_do_copy_msgs(folder, dest, msglist, relation);
1249 file_list = procmsg_get_message_file_list(msglist);
1250 g_return_val_if_fail(file_list != NULL, -1);
1252 ret = imap_add_msgs(folder, dest, file_list, relation);
1254 procmsg_message_file_list_free(file_list);
1260 static gint imap_do_remove_msgs(Folder *folder, FolderItem *dest,
1261 MsgInfoList *msglist, GRelation *relation)
1264 GSList *numlist = NULL, *cur;
1266 IMAPSession *session;
1267 gint ok = IMAP_SUCCESS;
1268 GRelation *uid_mapping;
1270 g_return_val_if_fail(folder != NULL, -1);
1271 g_return_val_if_fail(dest != NULL, -1);
1272 g_return_val_if_fail(msglist != NULL, -1);
1274 session = imap_session_get(folder);
1279 msginfo = (MsgInfo *)msglist->data;
1281 ok = imap_select(session, IMAP_FOLDER(folder), msginfo->folder->path,
1282 NULL, NULL, NULL, NULL, FALSE);
1283 if (ok != IMAP_SUCCESS) {
1288 destdir = imap_get_real_path(IMAP_FOLDER(folder), dest->path);
1289 for (cur = msglist; cur; cur = cur->next) {
1290 msginfo = (MsgInfo *)cur->data;
1291 if (!MSG_IS_DELETED(msginfo->flags))
1292 numlist = g_slist_append(numlist, GINT_TO_POINTER(msginfo->msgnum));
1295 uid_mapping = g_relation_new(2);
1296 g_relation_index(uid_mapping, 0, g_direct_hash, g_direct_equal);
1298 ok = imap_set_message_flags
1299 (IMAP_SESSION(REMOTE_FOLDER(folder)->session),
1300 numlist, IMAP_FLAG_DELETED, TRUE);
1301 if (ok != IMAP_SUCCESS) {
1302 log_warning(_("can't set deleted flags\n"));
1306 ok = imap_cmd_expunge(session);
1307 if (ok != IMAP_SUCCESS) {
1308 log_warning(_("can't expunge\n"));
1313 g_relation_destroy(uid_mapping);
1314 g_slist_free(numlist);
1318 if (ok == IMAP_SUCCESS)
1324 static gint imap_remove_msgs(Folder *folder, FolderItem *dest,
1325 MsgInfoList *msglist, GRelation *relation)
1329 g_return_val_if_fail(folder != NULL, -1);
1330 g_return_val_if_fail(dest != NULL, -1);
1331 if (msglist == NULL)
1334 msginfo = (MsgInfo *)msglist->data;
1335 g_return_val_if_fail(msginfo->folder != NULL, -1);
1337 return imap_do_remove_msgs(folder, dest, msglist, relation);
1340 static gint imap_remove_all_msg(Folder *folder, FolderItem *item)
1342 GSList *list = folder_item_get_msg_list(item);
1343 gint res = imap_remove_msgs(folder, item, list, NULL);
1344 procmsg_msg_list_free(list);
1348 static gboolean imap_is_msg_changed(Folder *folder, FolderItem *item,
1351 /* TODO: properly implement this method */
1355 static gint imap_close(Folder *folder, FolderItem *item)
1360 static gint imap_scan_tree(Folder *folder)
1362 FolderItem *item = NULL;
1363 IMAPSession *session;
1364 gchar *root_folder = NULL;
1366 g_return_val_if_fail(folder != NULL, -1);
1367 g_return_val_if_fail(folder->account != NULL, -1);
1369 session = imap_session_get(folder);
1371 if (!folder->node) {
1372 folder_tree_destroy(folder);
1373 item = folder_item_new(folder, folder->name, NULL);
1374 item->folder = folder;
1375 folder->node = item->node = g_node_new(item);
1381 if (folder->account->imap_dir && *folder->account->imap_dir) {
1386 Xstrdup_a(root_folder, folder->account->imap_dir, {unlock_session();return -1;});
1387 extract_quote(root_folder, '"');
1388 subst_char(root_folder,
1389 imap_get_path_separator(IMAP_FOLDER(folder),
1392 strtailchomp(root_folder, '/');
1393 real_path = imap_get_real_path
1394 (IMAP_FOLDER(folder), root_folder);
1395 debug_print("IMAP root directory: %s\n", real_path);
1397 /* check if root directory exist */
1399 r = imap_threaded_list(session->folder, "", real_path,
1401 if ((r != MAILIMAP_NO_ERROR) || (clist_count(lep_list) == 0)) {
1402 if (!folder->node) {
1403 item = folder_item_new(folder, folder->name, NULL);
1404 item->folder = folder;
1405 folder->node = item->node = g_node_new(item);
1410 mailimap_list_result_free(lep_list);
1416 item = FOLDER_ITEM(folder->node->data);
1417 if (!item || ((item->path || root_folder) &&
1418 strcmp2(item->path, root_folder) != 0)) {
1419 folder_tree_destroy(folder);
1420 item = folder_item_new(folder, folder->name, root_folder);
1421 item->folder = folder;
1422 folder->node = item->node = g_node_new(item);
1425 imap_scan_tree_recursive(session, FOLDER_ITEM(folder->node->data));
1426 imap_create_missing_folders(folder);
1432 static gint imap_scan_tree_recursive(IMAPSession *session, FolderItem *item)
1435 IMAPFolder *imapfolder;
1436 FolderItem *new_item;
1437 GSList *item_list, *cur;
1440 gchar *wildcard_path;
1446 g_return_val_if_fail(item != NULL, -1);
1447 g_return_val_if_fail(item->folder != NULL, -1);
1448 g_return_val_if_fail(item->no_sub == FALSE, -1);
1450 folder = item->folder;
1451 imapfolder = IMAP_FOLDER(folder);
1453 separator = imap_get_path_separator(imapfolder, item->path);
1455 if (folder->ui_func)
1456 folder->ui_func(folder, item, folder->ui_func_data);
1459 wildcard[0] = separator;
1462 real_path = imap_get_real_path(imapfolder, item->path);
1466 real_path = g_strdup("");
1469 Xstrcat_a(wildcard_path, real_path, wildcard,
1470 {g_free(real_path); return IMAP_ERROR;});
1472 r = imap_threaded_list(folder, "", wildcard_path, &lep_list);
1473 if (r != MAILIMAP_NO_ERROR) {
1477 item_list = imap_list_from_lep(imapfolder,
1478 lep_list, real_path, FALSE);
1479 mailimap_list_result_free(lep_list);
1484 node = item->node->children;
1485 while (node != NULL) {
1486 FolderItem *old_item = FOLDER_ITEM(node->data);
1487 GNode *next = node->next;
1490 for (cur = item_list; cur != NULL; cur = cur->next) {
1491 FolderItem *cur_item = FOLDER_ITEM(cur->data);
1492 if (!strcmp2(old_item->path, cur_item->path)) {
1493 new_item = cur_item;
1498 debug_print("folder '%s' not found. removing...\n",
1500 folder_item_remove(old_item);
1502 old_item->no_sub = new_item->no_sub;
1503 old_item->no_select = new_item->no_select;
1504 if (old_item->no_sub == TRUE && node->children) {
1505 debug_print("folder '%s' doesn't have "
1506 "subfolders. removing...\n",
1508 folder_item_remove_children(old_item);
1515 for (cur = item_list; cur != NULL; cur = cur->next) {
1516 FolderItem *cur_item = FOLDER_ITEM(cur->data);
1519 for (node = item->node->children; node != NULL;
1520 node = node->next) {
1521 if (!strcmp2(FOLDER_ITEM(node->data)->path,
1523 new_item = FOLDER_ITEM(node->data);
1524 folder_item_destroy(cur_item);
1530 new_item = cur_item;
1531 debug_print("new folder '%s' found.\n", new_item->path);
1532 folder_item_append(item, new_item);
1535 if (!strcmp(new_item->path, "INBOX")) {
1536 new_item->stype = F_INBOX;
1537 folder->inbox = new_item;
1538 } else if (!folder_item_parent(item) || item->stype == F_INBOX) {
1541 base = g_path_get_basename(new_item->path);
1543 if (!folder->outbox && !g_ascii_strcasecmp(base, "Sent")) {
1544 new_item->stype = F_OUTBOX;
1545 folder->outbox = new_item;
1546 } else if (!folder->draft && !g_ascii_strcasecmp(base, "Drafts")) {
1547 new_item->stype = F_DRAFT;
1548 folder->draft = new_item;
1549 } else if (!folder->queue && !g_ascii_strcasecmp(base, "Queue")) {
1550 new_item->stype = F_QUEUE;
1551 folder->queue = new_item;
1552 } else if (!folder->trash && !g_ascii_strcasecmp(base, "Trash")) {
1553 new_item->stype = F_TRASH;
1554 folder->trash = new_item;
1559 if (new_item->no_sub == FALSE)
1560 imap_scan_tree_recursive(session, new_item);
1563 g_slist_free(item_list);
1565 return IMAP_SUCCESS;
1568 static gint imap_create_tree(Folder *folder)
1570 g_return_val_if_fail(folder != NULL, -1);
1571 g_return_val_if_fail(folder->node != NULL, -1);
1572 g_return_val_if_fail(folder->node->data != NULL, -1);
1573 g_return_val_if_fail(folder->account != NULL, -1);
1575 imap_scan_tree(folder);
1576 imap_create_missing_folders(folder);
1581 static void imap_create_missing_folders(Folder *folder)
1583 g_return_if_fail(folder != NULL);
1586 folder->inbox = imap_create_special_folder
1587 (folder, F_INBOX, "INBOX");
1589 folder->trash = imap_create_special_folder
1590 (folder, F_TRASH, "Trash");
1592 folder->queue = imap_create_special_folder
1593 (folder, F_QUEUE, "Queue");
1594 if (!folder->outbox)
1595 folder->outbox = imap_create_special_folder
1596 (folder, F_OUTBOX, "Sent");
1598 folder->draft = imap_create_special_folder
1599 (folder, F_DRAFT, "Drafts");
1602 static FolderItem *imap_create_special_folder(Folder *folder,
1603 SpecialFolderItemType stype,
1607 FolderItem *new_item;
1609 g_return_val_if_fail(folder != NULL, NULL);
1610 g_return_val_if_fail(folder->node != NULL, NULL);
1611 g_return_val_if_fail(folder->node->data != NULL, NULL);
1612 g_return_val_if_fail(folder->account != NULL, NULL);
1613 g_return_val_if_fail(name != NULL, NULL);
1615 item = FOLDER_ITEM(folder->node->data);
1616 new_item = imap_create_folder(folder, item, name);
1619 g_warning("Can't create '%s'\n", name);
1620 if (!folder->inbox) return NULL;
1622 new_item = imap_create_folder(folder, folder->inbox, name);
1624 g_warning("Can't create '%s' under INBOX\n", name);
1626 new_item->stype = stype;
1628 new_item->stype = stype;
1633 static gchar *imap_folder_get_path(Folder *folder)
1637 g_return_val_if_fail(folder != NULL, NULL);
1638 g_return_val_if_fail(folder->account != NULL, NULL);
1640 folder_path = g_strconcat(get_imap_cache_dir(),
1642 folder->account->recv_server,
1644 folder->account->userid,
1650 static gchar *imap_item_get_path(Folder *folder, FolderItem *item)
1652 gchar *folder_path, *path;
1654 g_return_val_if_fail(folder != NULL, NULL);
1655 g_return_val_if_fail(item != NULL, NULL);
1656 folder_path = imap_folder_get_path(folder);
1658 g_return_val_if_fail(folder_path != NULL, NULL);
1659 if (folder_path[0] == G_DIR_SEPARATOR) {
1661 path = g_strconcat(folder_path, G_DIR_SEPARATOR_S,
1664 path = g_strdup(folder_path);
1667 path = g_strconcat(get_home_dir(), G_DIR_SEPARATOR_S,
1668 folder_path, G_DIR_SEPARATOR_S,
1671 path = g_strconcat(get_home_dir(), G_DIR_SEPARATOR_S,
1674 g_free(folder_path);
1679 static FolderItem *imap_create_folder(Folder *folder, FolderItem *parent,
1682 gchar *dirpath, *imap_path;
1683 IMAPSession *session;
1684 FolderItem *new_item;
1689 gboolean no_select = FALSE, no_sub = FALSE;
1691 g_return_val_if_fail(folder != NULL, NULL);
1692 g_return_val_if_fail(folder->account != NULL, NULL);
1693 g_return_val_if_fail(parent != NULL, NULL);
1694 g_return_val_if_fail(name != NULL, NULL);
1696 session = imap_session_get(folder);
1702 if (!folder_item_parent(parent) && strcmp(name, "INBOX") == 0) {
1703 dirpath = g_strdup(name);
1704 }else if (parent->path)
1705 dirpath = g_strconcat(parent->path, "/", name, NULL);
1706 else if ((p = strchr(name, '/')) != NULL && *(p + 1) != '\0')
1707 dirpath = g_strdup(name);
1708 else if (folder->account->imap_dir && *folder->account->imap_dir) {
1711 Xstrdup_a(imap_dir, folder->account->imap_dir, {unlock_session();return NULL;});
1712 strtailchomp(imap_dir, '/');
1713 dirpath = g_strconcat(imap_dir, "/", name, NULL);
1715 dirpath = g_strdup(name);
1719 /* keep trailing directory separator to create a folder that contains
1721 imap_path = imap_utf8_to_modified_utf7(dirpath);
1723 strtailchomp(dirpath, '/');
1724 Xstrdup_a(new_name, name, {
1729 separator = imap_get_path_separator(IMAP_FOLDER(folder), imap_path);
1730 imap_path_separator_subst(imap_path, separator);
1731 /* remove trailing / for display */
1732 strtailchomp(new_name, '/');
1734 if (strcmp(dirpath, "INBOX") != 0) {
1736 gboolean exist = FALSE;
1740 argbuf = g_ptr_array_new();
1741 r = imap_threaded_list(folder, "", imap_path, &lep_list);
1742 if (r != MAILIMAP_NO_ERROR) {
1743 log_warning(_("can't create mailbox: LIST failed\n"));
1746 ptr_array_free_strings(argbuf);
1747 g_ptr_array_free(argbuf, TRUE);
1752 if (clist_count(lep_list) > 0)
1754 mailimap_list_result_free(lep_list);
1757 ok = imap_cmd_create(session, imap_path);
1758 if (ok != IMAP_SUCCESS) {
1759 log_warning(_("can't create mailbox\n"));
1765 r = imap_threaded_list(folder, "", imap_path, &lep_list);
1766 if (r == MAILIMAP_NO_ERROR) {
1767 GSList *item_list = imap_list_from_lep(IMAP_FOLDER(folder),
1768 lep_list, dirpath, TRUE);
1770 FolderItem *cur_item = FOLDER_ITEM(item_list->data);
1771 no_select = cur_item->no_select;
1772 no_sub = cur_item->no_sub;
1773 g_slist_free(item_list);
1775 mailimap_list_result_free(lep_list);
1782 /* just get flags */
1783 r = imap_threaded_list(folder, "", "INBOX", &lep_list);
1784 if (r == MAILIMAP_NO_ERROR) {
1785 GSList *item_list = imap_list_from_lep(IMAP_FOLDER(folder),
1786 lep_list, dirpath, TRUE);
1788 FolderItem *cur_item = FOLDER_ITEM(item_list->data);
1789 no_select = cur_item->no_select;
1790 no_sub = cur_item->no_sub;
1791 g_slist_free(item_list);
1793 mailimap_list_result_free(lep_list);
1797 new_item = folder_item_new(folder, new_name, dirpath);
1798 new_item->no_select = no_select;
1799 new_item->no_sub = no_sub;
1800 folder_item_append(parent, new_item);
1804 dirpath = folder_item_get_path(new_item);
1805 if (!is_dir_exist(dirpath))
1806 make_dir_hier(dirpath);
1812 static gint imap_rename_folder(Folder *folder, FolderItem *item,
1817 gchar *real_oldpath;
1818 gchar *real_newpath;
1820 gchar *old_cache_dir;
1821 gchar *new_cache_dir;
1822 IMAPSession *session;
1825 gint exists, recent, unseen;
1826 guint32 uid_validity;
1828 g_return_val_if_fail(folder != NULL, -1);
1829 g_return_val_if_fail(item != NULL, -1);
1830 g_return_val_if_fail(item->path != NULL, -1);
1831 g_return_val_if_fail(name != NULL, -1);
1833 session = imap_session_get(folder);
1839 if (strchr(name, imap_get_path_separator(IMAP_FOLDER(folder), item->path)) != NULL) {
1840 g_warning(_("New folder name must not contain the namespace "
1846 real_oldpath = imap_get_real_path(IMAP_FOLDER(folder), item->path);
1848 g_free(session->mbox);
1849 session->mbox = NULL;
1850 ok = imap_cmd_examine(session, "INBOX",
1851 &exists, &recent, &unseen, &uid_validity, FALSE);
1852 if (ok != IMAP_SUCCESS) {
1853 g_free(real_oldpath);
1858 separator = imap_get_path_separator(IMAP_FOLDER(folder), item->path);
1859 if (strchr(item->path, G_DIR_SEPARATOR)) {
1860 dirpath = g_path_get_dirname(item->path);
1861 newpath = g_strconcat(dirpath, G_DIR_SEPARATOR_S, name, NULL);
1864 newpath = g_strdup(name);
1866 real_newpath = imap_utf8_to_modified_utf7(newpath);
1867 imap_path_separator_subst(real_newpath, separator);
1869 ok = imap_cmd_rename(session, real_oldpath, real_newpath);
1870 if (ok != IMAP_SUCCESS) {
1871 log_warning(_("can't rename mailbox: %s to %s\n"),
1872 real_oldpath, real_newpath);
1873 g_free(real_oldpath);
1875 g_free(real_newpath);
1881 item->name = g_strdup(name);
1883 old_cache_dir = folder_item_get_path(item);
1885 paths[0] = g_strdup(item->path);
1887 g_node_traverse(item->node, G_PRE_ORDER, G_TRAVERSE_ALL, -1,
1888 imap_rename_folder_func, paths);
1890 if (is_dir_exist(old_cache_dir)) {
1891 new_cache_dir = folder_item_get_path(item);
1892 if (rename(old_cache_dir, new_cache_dir) < 0) {
1893 FILE_OP_ERROR(old_cache_dir, "rename");
1895 g_free(new_cache_dir);
1898 g_free(old_cache_dir);
1901 g_free(real_oldpath);
1902 g_free(real_newpath);
1907 static gint imap_remove_folder_real(Folder *folder, FolderItem *item)
1910 IMAPSession *session;
1914 g_return_val_if_fail(folder != NULL, -1);
1915 g_return_val_if_fail(item != NULL, -1);
1916 g_return_val_if_fail(item->path != NULL, -1);
1918 session = imap_session_get(folder);
1923 path = imap_get_real_path(IMAP_FOLDER(folder), item->path);
1925 ok = imap_cmd_delete(session, path);
1926 if (ok != IMAP_SUCCESS) {
1927 gchar *tmp = g_strdup_printf("%s%c", path,
1928 imap_get_path_separator(IMAP_FOLDER(folder), path));
1931 ok = imap_cmd_delete(session, path);
1934 if (ok != IMAP_SUCCESS) {
1935 log_warning(_("can't delete mailbox\n"));
1942 cache_dir = folder_item_get_path(item);
1943 if (is_dir_exist(cache_dir) && remove_dir_recursive(cache_dir) < 0)
1944 g_warning("can't remove directory '%s'\n", cache_dir);
1946 folder_item_remove(item);
1951 static gint imap_remove_folder(Folder *folder, FolderItem *item)
1955 g_return_val_if_fail(item != NULL, -1);
1956 g_return_val_if_fail(item->folder != NULL, -1);
1957 g_return_val_if_fail(item->node != NULL, -1);
1959 node = item->node->children;
1960 while (node != NULL) {
1962 if (imap_remove_folder(folder, FOLDER_ITEM(node->data)) < 0)
1966 debug_print("IMAP removing %s\n", item->path);
1968 if (imap_remove_all_msg(folder, item) < 0)
1970 return imap_remove_folder_real(folder, item);
1973 typedef struct _uncached_data {
1974 IMAPSession *session;
1976 MsgNumberList *numlist;
1982 static void *imap_get_uncached_messages_thread(void *data)
1984 uncached_data *stuff = (uncached_data *)data;
1985 IMAPSession *session = stuff->session;
1986 FolderItem *item = stuff->item;
1987 MsgNumberList *numlist = stuff->numlist;
1989 GSList *newlist = NULL;
1990 GSList *llast = NULL;
1991 GSList *seq_list, *cur;
1993 debug_print("uncached_messages\n");
1995 if (session == NULL || item == NULL || item->folder == NULL
1996 || FOLDER_CLASS(item->folder) != &imap_class) {
2001 seq_list = imap_get_lep_set_from_numlist(numlist);
2002 debug_print("get msgs info\n");
2003 for (cur = seq_list; cur != NULL; cur = g_slist_next(cur)) {
2004 struct mailimap_set * imapset;
2010 imapset = cur->data;
2012 r = imap_threaded_fetch_env(session->folder,
2013 imapset, &env_list);
2014 if (r != MAILIMAP_NO_ERROR)
2018 for(i = 0 ; i < carray_count(env_list) ; i ++) {
2019 struct imap_fetch_env_info * info;
2022 info = carray_get(env_list, i);
2023 msginfo = imap_envelope_from_lep(info, item);
2024 if (msginfo == NULL)
2026 msginfo->folder = item;
2028 llast = newlist = g_slist_append(newlist, msginfo);
2030 llast = g_slist_append(llast, msginfo);
2031 llast = llast->next;
2036 imap_fetch_env_free(env_list);
2039 for (cur = seq_list; cur != NULL; cur = g_slist_next(cur)) {
2040 struct mailimap_set * imapset;
2042 imapset = cur->data;
2043 mailimap_set_free(imapset);
2046 session_set_access_time(SESSION(session));
2051 #define MAX_MSG_NUM 50
2053 static GSList *imap_get_uncached_messages(IMAPSession *session,
2055 MsgNumberList *numlist)
2057 GSList *result = NULL;
2059 uncached_data *data = g_new0(uncached_data, 1);
2064 data->total = g_slist_length(numlist);
2065 debug_print("messages list : %i\n", data->total);
2067 while (cur != NULL) {
2068 GSList * partial_result;
2076 while (count < MAX_MSG_NUM) {
2081 if (newlist == NULL)
2082 llast = newlist = g_slist_append(newlist, p);
2084 llast = g_slist_append(llast, p);
2085 llast = llast->next;
2095 data->session = session;
2097 data->numlist = newlist;
2100 if (prefs_common.work_offline && !inc_offline_should_override()) {
2106 (GSList *)imap_get_uncached_messages_thread(data);
2108 statusbar_progress_all(data->cur,data->total, 1);
2110 g_slist_free(newlist);
2112 result = g_slist_concat(result, partial_result);
2116 statusbar_progress_all(0,0,0);
2117 statusbar_pop_all();
2122 static void imap_delete_all_cached_messages(FolderItem *item)
2126 g_return_if_fail(item != NULL);
2127 g_return_if_fail(item->folder != NULL);
2128 g_return_if_fail(FOLDER_CLASS(item->folder) == &imap_class);
2130 debug_print("Deleting all cached messages...\n");
2132 dir = folder_item_get_path(item);
2133 if (is_dir_exist(dir))
2134 remove_all_numbered_files(dir);
2137 debug_print("done.\n");
2140 static IMAPNameSpace *imap_find_namespace_from_list(GList *ns_list,
2143 IMAPNameSpace *namespace = NULL;
2144 gchar *tmp_path, *name;
2146 if (!path) path = "";
2148 for (; ns_list != NULL; ns_list = ns_list->next) {
2149 IMAPNameSpace *tmp_ns = ns_list->data;
2151 Xstrcat_a(tmp_path, path, "/", return namespace);
2152 Xstrdup_a(name, tmp_ns->name, return namespace);
2153 if (tmp_ns->separator && tmp_ns->separator != '/') {
2154 subst_char(tmp_path, tmp_ns->separator, '/');
2155 subst_char(name, tmp_ns->separator, '/');
2157 if (strncmp(tmp_path, name, strlen(name)) == 0)
2164 static IMAPNameSpace *imap_find_namespace(IMAPFolder *folder,
2167 IMAPNameSpace *namespace;
2169 g_return_val_if_fail(folder != NULL, NULL);
2171 namespace = imap_find_namespace_from_list(folder->ns_personal, path);
2172 if (namespace) return namespace;
2173 namespace = imap_find_namespace_from_list(folder->ns_others, path);
2174 if (namespace) return namespace;
2175 namespace = imap_find_namespace_from_list(folder->ns_shared, path);
2176 if (namespace) return namespace;
2182 static gchar imap_get_path_separator(IMAPFolder *folder, const gchar *path)
2184 IMAPNameSpace *namespace;
2185 gchar separator = '/';
2187 if (folder->last_seen_separator == 0) {
2189 int r = imap_threaded_list((Folder *)folder, "", "", &lep_list);
2190 if (r != MAILIMAP_NO_ERROR) {
2191 log_warning(_("LIST failed\n"));
2195 if (clist_count(lep_list) > 0) {
2196 clistiter * iter = clist_begin(lep_list);
2197 struct mailimap_mailbox_list * mb;
2198 mb = clist_content(iter);
2200 folder->last_seen_separator = mb->mb_delimiter;
2201 debug_print("got separator: %c\n", folder->last_seen_separator);
2203 mailimap_list_result_free(lep_list);
2206 if (folder->last_seen_separator != 0) {
2207 debug_print("using separator: %c\n", folder->last_seen_separator);
2208 return folder->last_seen_separator;
2211 namespace = imap_find_namespace(folder, path);
2212 if (namespace && namespace->separator)
2213 separator = namespace->separator;
2218 static gchar *imap_get_real_path(IMAPFolder *folder, const gchar *path)
2223 g_return_val_if_fail(folder != NULL, NULL);
2224 g_return_val_if_fail(path != NULL, NULL);
2226 real_path = imap_utf8_to_modified_utf7(path);
2227 separator = imap_get_path_separator(folder, path);
2228 imap_path_separator_subst(real_path, separator);
2233 static gint imap_set_message_flags(IMAPSession *session,
2234 MsgNumberList *numlist,
2242 seq_list = imap_get_lep_set_from_numlist(numlist);
2244 for(cur = seq_list ; cur != NULL ; cur = g_slist_next(cur)) {
2245 struct mailimap_set * imapset;
2247 imapset = cur->data;
2249 ok = imap_cmd_store(session, imapset,
2253 imap_lep_set_free(seq_list);
2255 return IMAP_SUCCESS;
2258 typedef struct _select_data {
2259 IMAPSession *session;
2264 guint32 *uid_validity;
2268 static gint imap_select(IMAPSession *session, IMAPFolder *folder,
2270 gint *exists, gint *recent, gint *unseen,
2271 guint32 *uid_validity, gboolean block)
2275 gint exists_, recent_, unseen_;
2276 guint32 uid_validity_;
2278 if (!exists && !recent && !unseen && !uid_validity) {
2279 if (session->mbox && strcmp(session->mbox, path) == 0)
2280 return IMAP_SUCCESS;
2289 uid_validity = &uid_validity_;
2291 g_free(session->mbox);
2292 session->mbox = NULL;
2294 real_path = imap_get_real_path(folder, path);
2296 ok = imap_cmd_select(session, real_path,
2297 exists, recent, unseen, uid_validity, block);
2298 if (ok != IMAP_SUCCESS)
2299 log_warning(_("can't select folder: %s\n"), real_path);
2301 session->mbox = g_strdup(path);
2302 session->folder_content_changed = FALSE;
2309 static gint imap_status(IMAPSession *session, IMAPFolder *folder,
2310 const gchar *path, IMAPFolderItem *item,
2312 guint32 *uid_next, guint32 *uid_validity,
2313 gint *unseen, gboolean block)
2317 struct mailimap_mailbox_data_status * data_status;
2322 real_path = imap_get_real_path(folder, path);
2325 if (time(NULL) - item->last_update >= 5 && item->last_update != 1) {
2326 /* do the full stuff */
2327 item->last_update = 1; /* force update */
2328 debug_print("updating everything\n");
2329 r = imap_status(session, folder, path, item,
2330 &item->c_messages, &item->c_uid_next,
2331 &item->c_uid_validity, &item->c_unseen, block);
2332 if (r != MAILIMAP_NO_ERROR) {
2333 debug_print("status err %d\n", r);
2336 item->last_update = time(NULL);
2338 *messages = item->c_messages;
2340 *uid_next = item->c_uid_next;
2342 *uid_validity = item->c_uid_validity;
2344 *unseen = item->c_unseen;
2346 } else if (time(NULL) - item->last_update < 5) {
2347 /* return cached stuff */
2348 debug_print("using cache\n");
2350 *messages = item->c_messages;
2352 *uid_next = item->c_uid_next;
2354 *uid_validity = item->c_uid_validity;
2356 *unseen = item->c_unseen;
2361 /* if we get there, we're updating cache */
2375 r = imap_threaded_status(FOLDER(folder), real_path,
2376 &data_status, mask);
2379 if (r != MAILIMAP_NO_ERROR) {
2380 debug_print("status err %d\n", r);
2384 if (data_status->st_info_list == NULL) {
2385 mailimap_mailbox_data_status_free(data_status);
2386 debug_print("status->st_info_list == NULL\n");
2391 for(iter = clist_begin(data_status->st_info_list) ; iter != NULL ;
2392 iter = clist_next(iter)) {
2393 struct mailimap_status_info * info;
2395 info = clist_content(iter);
2396 switch (info->st_att) {
2397 case MAILIMAP_STATUS_ATT_MESSAGES:
2398 * messages = info->st_value;
2399 got_values |= 1 << 0;
2402 case MAILIMAP_STATUS_ATT_UIDNEXT:
2403 * uid_next = info->st_value;
2404 got_values |= 1 << 2;
2407 case MAILIMAP_STATUS_ATT_UIDVALIDITY:
2408 * uid_validity = info->st_value;
2409 got_values |= 1 << 3;
2412 case MAILIMAP_STATUS_ATT_UNSEEN:
2413 * unseen = info->st_value;
2414 got_values |= 1 << 4;
2418 mailimap_mailbox_data_status_free(data_status);
2420 if (got_values != mask) {
2421 debug_print("status: incomplete values received (%d)\n", got_values);
2424 return IMAP_SUCCESS;
2427 static void imap_free_capabilities(IMAPSession *session)
2429 slist_free_strings(session->capability);
2430 g_slist_free(session->capability);
2431 session->capability = NULL;
2434 /* low-level IMAP4rev1 commands */
2437 static gint imap_cmd_authenticate(IMAPSession *session, const gchar *user,
2438 const gchar *pass, IMAPAuthType type)
2445 gchar hexdigest[33];
2449 auth_type = "CRAM-MD5";
2451 imap_gen_send(session, "AUTHENTICATE %s", auth_type);
2452 ok = imap_gen_recv(session, &buf);
2453 if (ok != IMAP_SUCCESS || buf[0] != '+' || buf[1] != ' ') {
2458 challenge = g_malloc(strlen(buf + 2) + 1);
2459 challenge_len = base64_decode(challenge, buf + 2, -1);
2460 challenge[challenge_len] = '\0';
2463 md5_hex_hmac(hexdigest, challenge, challenge_len, pass, strlen(pass));
2466 response = g_strdup_printf("%s %s", user, hexdigest);
2467 response64 = g_malloc((strlen(response) + 3) * 2 + 1);
2468 base64_encode(response64, response, strlen(response));
2471 sock_puts(SESSION(session)->sock, response64);
2472 ok = imap_cmd_ok(session, NULL);
2473 if (ok != IMAP_SUCCESS)
2474 log_warning(_("IMAP4 authentication failed.\n"));
2480 static gint imap_cmd_login(IMAPSession *session,
2481 const gchar *user, const gchar *pass,
2487 log_print("IMAP4> Logging %s to %s using %s\n",
2489 SESSION(session)->server,
2491 r = imap_threaded_login(session->folder, user, pass, type);
2492 if (r != MAILIMAP_NO_ERROR) {
2493 log_error("IMAP4< Error logging in to %s\n",
2494 SESSION(session)->server);
2502 static gint imap_cmd_logout(IMAPSession *session)
2504 imap_threaded_disconnect(session->folder);
2506 return IMAP_SUCCESS;
2509 static gint imap_cmd_noop(IMAPSession *session)
2512 unsigned int exists;
2514 r = imap_threaded_noop(session->folder, &exists);
2515 if (r != MAILIMAP_NO_ERROR) {
2516 debug_print("noop err %d\n", r);
2519 session->exists = exists;
2520 session_set_access_time(SESSION(session));
2522 return IMAP_SUCCESS;
2526 static gint imap_cmd_starttls(IMAPSession *session)
2530 r = imap_threaded_starttls(session->folder);
2531 if (r != MAILIMAP_NO_ERROR) {
2532 debug_print("starttls err %d\n", r);
2535 return IMAP_SUCCESS;
2539 static gint imap_cmd_select(IMAPSession *session, const gchar *folder,
2540 gint *exists, gint *recent, gint *unseen,
2541 guint32 *uid_validity, gboolean block)
2545 r = imap_threaded_select(session->folder, folder,
2546 exists, recent, unseen, uid_validity);
2547 if (r != MAILIMAP_NO_ERROR) {
2548 debug_print("select err %d\n", r);
2551 return IMAP_SUCCESS;
2554 static gint imap_cmd_examine(IMAPSession *session, const gchar *folder,
2555 gint *exists, gint *recent, gint *unseen,
2556 guint32 *uid_validity, gboolean block)
2560 r = imap_threaded_examine(session->folder, folder,
2561 exists, recent, unseen, uid_validity);
2562 if (r != MAILIMAP_NO_ERROR) {
2563 debug_print("examine err %d\n", r);
2567 return IMAP_SUCCESS;
2570 static gint imap_cmd_create(IMAPSession *session, const gchar *folder)
2574 r = imap_threaded_create(session->folder, folder);
2575 if (r != MAILIMAP_NO_ERROR) {
2580 return IMAP_SUCCESS;
2583 static gint imap_cmd_rename(IMAPSession *session, const gchar *old_folder,
2584 const gchar *new_folder)
2588 r = imap_threaded_rename(session->folder, old_folder,
2590 if (r != MAILIMAP_NO_ERROR) {
2595 return IMAP_SUCCESS;
2598 static gint imap_cmd_delete(IMAPSession *session, const gchar *folder)
2603 r = imap_threaded_delete(session->folder, folder);
2604 if (r != MAILIMAP_NO_ERROR) {
2609 return IMAP_SUCCESS;
2612 typedef struct _fetch_data {
2613 IMAPSession *session;
2615 const gchar *filename;
2621 static void *imap_cmd_fetch_thread(void *data)
2623 fetch_data *stuff = (fetch_data *)data;
2624 IMAPSession *session = stuff->session;
2625 guint32 uid = stuff->uid;
2626 const gchar *filename = stuff->filename;
2630 r = imap_threaded_fetch_content(session->folder,
2634 r = imap_threaded_fetch_content(session->folder,
2637 if (r != MAILIMAP_NO_ERROR) {
2638 debug_print("fetch err %d\n", r);
2639 return GINT_TO_POINTER(IMAP_ERROR);
2641 return GINT_TO_POINTER(IMAP_SUCCESS);
2644 static gint imap_cmd_fetch(IMAPSession *session, guint32 uid,
2645 const gchar *filename, gboolean headers,
2648 fetch_data *data = g_new0(fetch_data, 1);
2651 data->session = session;
2653 data->filename = filename;
2654 data->headers = headers;
2657 if (prefs_common.work_offline && !inc_offline_should_override()) {
2661 statusbar_print_all(_("Fetching message..."));
2662 result = GPOINTER_TO_INT(imap_cmd_fetch_thread(data));
2663 statusbar_pop_all();
2669 static gint imap_cmd_append(IMAPSession *session, const gchar *destfolder,
2670 const gchar *file, IMAPFlags flags,
2673 struct mailimap_flag_list * flag_list;
2676 g_return_val_if_fail(file != NULL, IMAP_ERROR);
2678 flag_list = imap_flag_to_lep(flags);
2679 r = imap_threaded_append(session->folder, destfolder,
2681 mailimap_flag_list_free(flag_list);
2682 if (new_uid != NULL)
2685 if (r != MAILIMAP_NO_ERROR) {
2686 debug_print("append err %d\n", r);
2689 return IMAP_SUCCESS;
2692 static gint imap_cmd_copy(IMAPSession *session, struct mailimap_set * set,
2693 const gchar *destfolder, GRelation *uid_mapping)
2697 g_return_val_if_fail(session != NULL, IMAP_ERROR);
2698 g_return_val_if_fail(set != NULL, IMAP_ERROR);
2699 g_return_val_if_fail(destfolder != NULL, IMAP_ERROR);
2701 r = imap_threaded_copy(session->folder, set, destfolder);
2702 if (r != MAILIMAP_NO_ERROR) {
2707 return IMAP_SUCCESS;
2710 static gint imap_cmd_store(IMAPSession *session, struct mailimap_set * set,
2711 IMAPFlags flags, int do_add)
2714 struct mailimap_flag_list * flag_list;
2715 struct mailimap_store_att_flags * store_att_flags;
2717 flag_list = imap_flag_to_lep(flags);
2721 mailimap_store_att_flags_new_add_flags_silent(flag_list);
2724 mailimap_store_att_flags_new_remove_flags_silent(flag_list);
2726 r = imap_threaded_store(session->folder, set, store_att_flags);
2727 mailimap_store_att_flags_free(store_att_flags);
2728 if (r != MAILIMAP_NO_ERROR) {
2733 return IMAP_SUCCESS;
2736 static gint imap_cmd_expunge(IMAPSession *session)
2740 if (prefs_common.work_offline && !inc_offline_should_override()) {
2744 r = imap_threaded_expunge(session->folder);
2745 if (r != MAILIMAP_NO_ERROR) {
2750 return IMAP_SUCCESS;
2753 static void imap_path_separator_subst(gchar *str, gchar separator)
2756 gboolean in_escape = FALSE;
2758 if (!separator || separator == '/') return;
2760 for (p = str; *p != '\0'; p++) {
2761 if (*p == '/' && !in_escape)
2763 else if (*p == '&' && *(p + 1) != '-' && !in_escape)
2765 else if (*p == '-' && in_escape)
2770 static gchar *imap_modified_utf7_to_utf8(const gchar *mutf7_str)
2772 static iconv_t cd = (iconv_t)-1;
2773 static gboolean iconv_ok = TRUE;
2776 size_t norm_utf7_len;
2778 gchar *to_str, *to_p;
2780 gboolean in_escape = FALSE;
2782 if (!iconv_ok) return g_strdup(mutf7_str);
2784 if (cd == (iconv_t)-1) {
2785 cd = iconv_open(CS_INTERNAL, CS_UTF_7);
2786 if (cd == (iconv_t)-1) {
2787 g_warning("iconv cannot convert UTF-7 to %s\n",
2790 return g_strdup(mutf7_str);
2794 /* modified UTF-7 to normal UTF-7 conversion */
2795 norm_utf7 = g_string_new(NULL);
2797 for (p = mutf7_str; *p != '\0'; p++) {
2798 /* replace: '&' -> '+',
2800 escaped ',' -> '/' */
2801 if (!in_escape && *p == '&') {
2802 if (*(p + 1) != '-') {
2803 g_string_append_c(norm_utf7, '+');
2806 g_string_append_c(norm_utf7, '&');
2809 } else if (in_escape && *p == ',') {
2810 g_string_append_c(norm_utf7, '/');
2811 } else if (in_escape && *p == '-') {
2812 g_string_append_c(norm_utf7, '-');
2815 g_string_append_c(norm_utf7, *p);
2819 norm_utf7_p = norm_utf7->str;
2820 norm_utf7_len = norm_utf7->len;
2821 to_len = strlen(mutf7_str) * 5;
2822 to_p = to_str = g_malloc(to_len + 1);
2824 if (iconv(cd, (ICONV_CONST gchar **)&norm_utf7_p, &norm_utf7_len,
2825 &to_p, &to_len) == -1) {
2826 g_warning(_("iconv cannot convert UTF-7 to %s\n"),
2827 conv_get_locale_charset_str());
2828 g_string_free(norm_utf7, TRUE);
2830 return g_strdup(mutf7_str);
2833 /* second iconv() call for flushing */
2834 iconv(cd, NULL, NULL, &to_p, &to_len);
2835 g_string_free(norm_utf7, TRUE);
2841 static gchar *imap_utf8_to_modified_utf7(const gchar *from)
2843 static iconv_t cd = (iconv_t)-1;
2844 static gboolean iconv_ok = TRUE;
2845 gchar *norm_utf7, *norm_utf7_p;
2846 size_t from_len, norm_utf7_len;
2848 gchar *from_tmp, *to, *p;
2849 gboolean in_escape = FALSE;
2851 if (!iconv_ok) return g_strdup(from);
2853 if (cd == (iconv_t)-1) {
2854 cd = iconv_open(CS_UTF_7, CS_INTERNAL);
2855 if (cd == (iconv_t)-1) {
2856 g_warning(_("iconv cannot convert %s to UTF-7\n"),
2859 return g_strdup(from);
2863 /* UTF-8 to normal UTF-7 conversion */
2864 Xstrdup_a(from_tmp, from, return g_strdup(from));
2865 from_len = strlen(from);
2866 norm_utf7_len = from_len * 5;
2867 Xalloca(norm_utf7, norm_utf7_len + 1, return g_strdup(from));
2868 norm_utf7_p = norm_utf7;
2870 #define IS_PRINT(ch) (isprint(ch) && IS_ASCII(ch))
2872 while (from_len > 0) {
2873 if (*from_tmp == '+') {
2874 *norm_utf7_p++ = '+';
2875 *norm_utf7_p++ = '-';
2879 } else if (IS_PRINT(*(guchar *)from_tmp)) {
2880 /* printable ascii char */
2881 *norm_utf7_p = *from_tmp;
2887 size_t conv_len = 0;
2889 /* unprintable char: convert to UTF-7 */
2891 while (!IS_PRINT(*(guchar *)p) && conv_len < from_len) {
2892 conv_len += g_utf8_skip[*(guchar *)p];
2893 p += g_utf8_skip[*(guchar *)p];
2896 from_len -= conv_len;
2897 if (iconv(cd, (ICONV_CONST gchar **)&from_tmp,
2899 &norm_utf7_p, &norm_utf7_len) == -1) {
2900 g_warning(_("iconv cannot convert UTF-8 to UTF-7\n"));
2901 return g_strdup(from);
2904 /* second iconv() call for flushing */
2905 iconv(cd, NULL, NULL, &norm_utf7_p, &norm_utf7_len);
2911 *norm_utf7_p = '\0';
2912 to_str = g_string_new(NULL);
2913 for (p = norm_utf7; p < norm_utf7_p; p++) {
2914 /* replace: '&' -> "&-",
2917 BASE64 '/' -> ',' */
2918 if (!in_escape && *p == '&') {
2919 g_string_append(to_str, "&-");
2920 } else if (!in_escape && *p == '+') {
2921 if (*(p + 1) == '-') {
2922 g_string_append_c(to_str, '+');
2925 g_string_append_c(to_str, '&');
2928 } else if (in_escape && *p == '/') {
2929 g_string_append_c(to_str, ',');
2930 } else if (in_escape && *p == '-') {
2931 g_string_append_c(to_str, '-');
2934 g_string_append_c(to_str, *p);
2940 g_string_append_c(to_str, '-');
2944 g_string_free(to_str, FALSE);
2949 static gboolean imap_rename_folder_func(GNode *node, gpointer data)
2951 FolderItem *item = node->data;
2952 gchar **paths = data;
2953 const gchar *oldpath = paths[0];
2954 const gchar *newpath = paths[1];
2956 gchar *new_itempath;
2959 oldpathlen = strlen(oldpath);
2960 if (strncmp(oldpath, item->path, oldpathlen) != 0) {
2961 g_warning("path doesn't match: %s, %s\n", oldpath, item->path);
2965 base = item->path + oldpathlen;
2966 while (*base == G_DIR_SEPARATOR) base++;
2968 new_itempath = g_strdup(newpath);
2970 new_itempath = g_strconcat(newpath, G_DIR_SEPARATOR_S, base,
2973 item->path = new_itempath;
2978 typedef struct _get_list_uid_data {
2980 IMAPSession *session;
2981 IMAPFolderItem *item;
2982 GSList **msgnum_list;
2984 } get_list_uid_data;
2986 static void *get_list_of_uids_thread(void *data)
2988 get_list_uid_data *stuff = (get_list_uid_data *)data;
2989 Folder *folder = stuff->folder;
2990 IMAPFolderItem *item = stuff->item;
2991 GSList **msgnum_list = stuff->msgnum_list;
2992 gint ok, nummsgs = 0, lastuid_old;
2993 IMAPSession *session;
2994 GSList *uidlist, *elem;
2995 struct mailimap_set * set;
2996 clist * lep_uidlist;
2999 session = stuff->session;
3000 if (session == NULL) {
3002 return GINT_TO_POINTER(-1);
3004 /* no session locking here, it's already locked by caller */
3005 ok = imap_select(session, IMAP_FOLDER(folder), item->item.path,
3006 NULL, NULL, NULL, NULL, TRUE);
3007 if (ok != IMAP_SUCCESS) {
3009 return GINT_TO_POINTER(-1);
3014 set = mailimap_set_new_interval(item->lastuid + 1, 0);
3016 r = imap_threaded_search(folder, IMAP_SEARCH_TYPE_SIMPLE, set,
3018 mailimap_set_free(set);
3020 if (r == MAILIMAP_NO_ERROR) {
3021 GSList * fetchuid_list;
3024 imap_uid_list_from_lep(lep_uidlist);
3025 mailimap_search_result_free(lep_uidlist);
3027 uidlist = g_slist_concat(fetchuid_list, uidlist);
3030 GSList * fetchuid_list;
3031 carray * lep_uidtab;
3033 r = imap_threaded_fetch_uid(folder, item->lastuid + 1,
3035 if (r == MAILIMAP_NO_ERROR) {
3037 imap_uid_list_from_lep_tab(lep_uidtab);
3038 imap_fetch_uid_list_free(lep_uidtab);
3039 uidlist = g_slist_concat(fetchuid_list, uidlist);
3043 lastuid_old = item->lastuid;
3044 *msgnum_list = g_slist_copy(item->uid_list);
3045 nummsgs = g_slist_length(*msgnum_list);
3046 debug_print("Got %d uids from cache\n", g_slist_length(item->uid_list));
3048 for (elem = uidlist; elem != NULL; elem = g_slist_next(elem)) {
3051 msgnum = GPOINTER_TO_INT(elem->data);
3052 if (msgnum > lastuid_old) {
3053 *msgnum_list = g_slist_prepend(*msgnum_list, GINT_TO_POINTER(msgnum));
3054 item->uid_list = g_slist_prepend(item->uid_list, GINT_TO_POINTER(msgnum));
3057 if(msgnum > item->lastuid)
3058 item->lastuid = msgnum;
3061 g_slist_free(uidlist);
3063 return GINT_TO_POINTER(nummsgs);
3066 static gint get_list_of_uids(IMAPSession *session, Folder *folder, IMAPFolderItem *item, GSList **msgnum_list)
3069 get_list_uid_data *data = g_new0(get_list_uid_data, 1);
3071 data->folder = folder;
3073 data->msgnum_list = msgnum_list;
3074 data->session = session;
3075 if (prefs_common.work_offline && !inc_offline_should_override()) {
3080 result = GPOINTER_TO_INT(get_list_of_uids_thread(data));
3086 gint imap_get_num_list(Folder *folder, FolderItem *_item, GSList **msgnum_list, gboolean *old_uids_valid)
3088 IMAPFolderItem *item = (IMAPFolderItem *)_item;
3089 IMAPSession *session;
3090 gint ok, nummsgs = 0, exists, uid_val, uid_next;
3091 GSList *uidlist = NULL;
3093 gboolean selected_folder;
3095 debug_print("get_num_list\n");
3097 g_return_val_if_fail(folder != NULL, -1);
3098 g_return_val_if_fail(item != NULL, -1);
3099 g_return_val_if_fail(item->item.path != NULL, -1);
3100 g_return_val_if_fail(FOLDER_CLASS(folder) == &imap_class, -1);
3101 g_return_val_if_fail(folder->account != NULL, -1);
3103 session = imap_session_get(folder);
3104 g_return_val_if_fail(session != NULL, -1);
3106 statusbar_print_all("Scanning %s...\n", FOLDER_ITEM(item)->path
3107 ? FOLDER_ITEM(item)->path:"");
3109 selected_folder = (session->mbox != NULL) &&
3110 (!strcmp(session->mbox, item->item.path));
3111 if (selected_folder) {
3112 ok = imap_cmd_noop(session);
3113 if (ok != IMAP_SUCCESS) {
3114 debug_print("disconnected!\n");
3115 session = imap_reconnect_if_possible(folder, session);
3116 if (session == NULL) {
3117 statusbar_pop_all();
3122 exists = session->exists;
3124 *old_uids_valid = TRUE;
3126 if (item->use_cache && time(NULL) - item->use_cache < 2) {
3127 exists = item->c_messages;
3128 uid_next = item->c_uid_next;
3129 uid_val = item->c_uid_validity;
3131 debug_print("using cache %d %d %d\n", exists, uid_next, uid_val);
3133 ok = imap_status(session, IMAP_FOLDER(folder), item->item.path, item,
3134 &exists, &uid_next, &uid_val, NULL, FALSE);
3136 item->use_cache = (time_t)0;
3137 if (ok != IMAP_SUCCESS) {
3138 statusbar_pop_all();
3142 if(item->item.mtime == uid_val)
3143 *old_uids_valid = TRUE;
3145 *old_uids_valid = FALSE;
3147 debug_print("Freeing imap uid cache\n");
3149 g_slist_free(item->uid_list);
3150 item->uid_list = NULL;
3152 item->item.mtime = uid_val;
3154 imap_delete_all_cached_messages((FolderItem *)item);
3158 if (!selected_folder)
3159 item->uid_next = uid_next;
3161 /* If old uid_next matches new uid_next we can be sure no message
3162 was added to the folder */
3163 if (( selected_folder && !session->folder_content_changed) ||
3164 (!selected_folder && uid_next == item->uid_next)) {
3165 nummsgs = g_slist_length(item->uid_list);
3167 /* If number of messages is still the same we
3168 know our caches message numbers are still valid,
3169 otherwise if the number of messages has decrease
3170 we discard our cache to start a new scan to find
3171 out which numbers have been removed */
3172 if (exists == nummsgs) {
3173 *msgnum_list = g_slist_copy(item->uid_list);
3174 statusbar_pop_all();
3177 } else if (exists < nummsgs) {
3178 debug_print("Freeing imap uid cache");
3180 g_slist_free(item->uid_list);
3181 item->uid_list = NULL;
3186 *msgnum_list = NULL;
3187 statusbar_pop_all();
3192 nummsgs = get_list_of_uids(session, folder, item, &uidlist);
3195 statusbar_pop_all();
3200 if (nummsgs != exists) {
3201 /* Cache contains more messages then folder, we have cached
3202 an old UID of a message that was removed and new messages
3203 have been added too, otherwise the uid_next check would
3205 debug_print("Freeing imap uid cache");
3207 g_slist_free(item->uid_list);
3208 item->uid_list = NULL;
3210 g_slist_free(*msgnum_list);
3212 nummsgs = get_list_of_uids(session, folder, item, &uidlist);
3215 *msgnum_list = uidlist;
3217 dir = folder_item_get_path((FolderItem *)item);
3218 debug_print("removing old messages from %s\n", dir);
3219 remove_numbered_files_not_in_list(dir, *msgnum_list);
3222 debug_print("get_num_list - ok - %i\n", nummsgs);
3223 statusbar_pop_all();
3228 static MsgInfo *imap_parse_msg(const gchar *file, FolderItem *item)
3233 flags.perm_flags = MSG_NEW|MSG_UNREAD;
3234 flags.tmp_flags = 0;
3236 g_return_val_if_fail(item != NULL, NULL);
3237 g_return_val_if_fail(file != NULL, NULL);
3239 if (folder_has_parent_of_type(item, F_QUEUE)) {
3240 MSG_SET_TMP_FLAGS(flags, MSG_QUEUED);
3241 } else if (folder_has_parent_of_type(item, F_DRAFT)) {
3242 MSG_SET_TMP_FLAGS(flags, MSG_DRAFT);
3245 msginfo = procheader_parse_file(file, flags, FALSE, FALSE);
3246 if (!msginfo) return NULL;
3248 msginfo->plaintext_file = g_strdup(file);
3249 msginfo->folder = item;
3254 GSList *imap_get_msginfos(Folder *folder, FolderItem *item,
3255 GSList *msgnum_list)
3257 IMAPSession *session;
3258 MsgInfoList *ret = NULL;
3261 debug_print("get_msginfos\n");
3263 g_return_val_if_fail(folder != NULL, NULL);
3264 g_return_val_if_fail(item != NULL, NULL);
3265 g_return_val_if_fail(msgnum_list != NULL, NULL);
3267 session = imap_session_get(folder);
3268 g_return_val_if_fail(session != NULL, NULL);
3270 debug_print("IMAP getting msginfos\n");
3271 ok = imap_select(session, IMAP_FOLDER(folder), item->path,
3272 NULL, NULL, NULL, NULL, FALSE);
3273 if (ok != IMAP_SUCCESS) {
3277 if (!(folder_has_parent_of_type(item, F_DRAFT) ||
3278 folder_has_parent_of_type(item, F_QUEUE))) {
3279 ret = g_slist_concat(ret,
3280 imap_get_uncached_messages(session, item,
3283 MsgNumberList *sorted_list, *elem;
3284 gint startnum, lastnum;
3286 sorted_list = g_slist_sort(g_slist_copy(msgnum_list), g_int_compare);
3288 startnum = lastnum = GPOINTER_TO_INT(sorted_list->data);
3290 for (elem = sorted_list;; elem = g_slist_next(elem)) {
3294 num = GPOINTER_TO_INT(elem->data);
3296 if (num > lastnum + 1 || elem == NULL) {
3298 for (i = startnum; i <= lastnum; ++i) {
3301 file = imap_fetch_msg(folder, item, i);
3303 MsgInfo *msginfo = imap_parse_msg(file, item);
3304 if (msginfo != NULL) {
3305 msginfo->msgnum = i;
3306 ret = g_slist_append(ret, msginfo);
3320 g_slist_free(sorted_list);
3326 MsgInfo *imap_get_msginfo(Folder *folder, FolderItem *item, gint uid)
3328 MsgInfo *msginfo = NULL;
3329 MsgInfoList *msginfolist;
3330 MsgNumberList numlist;
3332 numlist.next = NULL;
3333 numlist.data = GINT_TO_POINTER(uid);
3335 msginfolist = imap_get_msginfos(folder, item, &numlist);
3336 if (msginfolist != NULL) {
3337 msginfo = msginfolist->data;
3338 g_slist_free(msginfolist);
3344 gboolean imap_scan_required(Folder *folder, FolderItem *_item)
3346 IMAPSession *session;
3347 IMAPFolderItem *item = (IMAPFolderItem *)_item;
3348 gint ok, exists = 0, unseen = 0;
3349 guint32 uid_next, uid_val;
3350 gboolean selected_folder;
3352 g_return_val_if_fail(folder != NULL, FALSE);
3353 g_return_val_if_fail(item != NULL, FALSE);
3354 g_return_val_if_fail(item->item.folder != NULL, FALSE);
3355 g_return_val_if_fail(FOLDER_CLASS(item->item.folder) == &imap_class, FALSE);
3357 if (item->item.path == NULL)
3360 session = imap_session_get(folder);
3361 g_return_val_if_fail(session != NULL, FALSE);
3363 selected_folder = (session->mbox != NULL) &&
3364 (!strcmp(session->mbox, item->item.path));
3365 if (selected_folder) {
3366 ok = imap_cmd_noop(session);
3367 if (ok != IMAP_SUCCESS) {
3368 debug_print("disconnected!\n");
3369 session = imap_reconnect_if_possible(folder, session);
3370 if (session == NULL)
3375 if (session->folder_content_changed
3376 || session->exists != item->item.total_msgs) {
3381 ok = imap_status(session, IMAP_FOLDER(folder), item->item.path, IMAP_FOLDER_ITEM(item),
3382 &exists, &uid_next, &uid_val, &unseen, FALSE);
3383 if (ok != IMAP_SUCCESS) {
3388 item->use_cache = time(NULL);
3389 item->c_messages = exists;
3390 item->c_uid_next = uid_next;
3391 item->c_uid_validity = uid_val;
3392 item->c_unseen = unseen;
3394 if ((uid_next != item->uid_next) || (exists != item->item.total_msgs)) {
3403 void imap_change_flags(Folder *folder, FolderItem *item, MsgInfo *msginfo, MsgPermFlags newflags)
3405 IMAPSession *session;
3406 IMAPFlags flags_set = 0, flags_unset = 0;
3407 gint ok = IMAP_SUCCESS;
3408 MsgNumberList numlist;
3409 hashtable_data *ht_data = NULL;
3411 g_return_if_fail(folder != NULL);
3412 g_return_if_fail(folder->klass == &imap_class);
3413 g_return_if_fail(item != NULL);
3414 g_return_if_fail(item->folder == folder);
3415 g_return_if_fail(msginfo != NULL);
3416 g_return_if_fail(msginfo->folder == item);
3418 session = imap_session_get(folder);
3423 if ((ok = imap_select(session, IMAP_FOLDER(folder), msginfo->folder->path,
3424 NULL, NULL, NULL, NULL, FALSE)) != IMAP_SUCCESS) {
3429 if (!MSG_IS_MARKED(msginfo->flags) && (newflags & MSG_MARKED))
3430 flags_set |= IMAP_FLAG_FLAGGED;
3431 if ( MSG_IS_MARKED(msginfo->flags) && !(newflags & MSG_MARKED))
3432 flags_unset |= IMAP_FLAG_FLAGGED;
3434 if (!MSG_IS_UNREAD(msginfo->flags) && (newflags & MSG_UNREAD))
3435 flags_unset |= IMAP_FLAG_SEEN;
3436 if ( MSG_IS_UNREAD(msginfo->flags) && !(newflags & MSG_UNREAD))
3437 flags_set |= IMAP_FLAG_SEEN;
3439 if (!MSG_IS_REPLIED(msginfo->flags) && (newflags & MSG_REPLIED))
3440 flags_set |= IMAP_FLAG_ANSWERED;
3441 if ( MSG_IS_REPLIED(msginfo->flags) && !(newflags & MSG_REPLIED))
3442 flags_unset |= IMAP_FLAG_ANSWERED;
3444 if (!MSG_IS_DELETED(msginfo->flags) && (newflags & MSG_DELETED))
3445 flags_set |= IMAP_FLAG_DELETED;
3446 if ( MSG_IS_DELETED(msginfo->flags) && !(newflags & MSG_DELETED))
3447 flags_unset |= IMAP_FLAG_DELETED;
3449 numlist.next = NULL;
3450 numlist.data = GINT_TO_POINTER(msginfo->msgnum);
3452 if (IMAP_FOLDER_ITEM(item)->batching) {
3453 /* instead of performing an UID STORE command for each message change,
3454 * as a lot of them can change "together", we just fill in hashtables
3455 * and defer the treatment so that we're able to send only one
3458 debug_print("IMAP batch mode on, deferring flags change\n");
3460 ht_data = g_hash_table_lookup(IMAP_FOLDER_ITEM(item)->flags_set_table,
3461 GINT_TO_POINTER(flags_set));
3462 if (ht_data == NULL) {
3463 ht_data = g_new0(hashtable_data, 1);
3464 ht_data->session = session;
3465 ht_data->item = IMAP_FOLDER_ITEM(item);
3466 g_hash_table_insert(IMAP_FOLDER_ITEM(item)->flags_set_table,
3467 GINT_TO_POINTER(flags_set), ht_data);
3469 if (!g_slist_find(ht_data->msglist, GINT_TO_POINTER(msginfo->msgnum)))
3470 ht_data->msglist = g_slist_prepend(ht_data->msglist, GINT_TO_POINTER(msginfo->msgnum));
3473 ht_data = g_hash_table_lookup(IMAP_FOLDER_ITEM(item)->flags_unset_table,
3474 GINT_TO_POINTER(flags_unset));
3475 if (ht_data == NULL) {
3476 ht_data = g_new0(hashtable_data, 1);
3477 ht_data->session = session;
3478 ht_data->item = IMAP_FOLDER_ITEM(item);
3479 g_hash_table_insert(IMAP_FOLDER_ITEM(item)->flags_unset_table,
3480 GINT_TO_POINTER(flags_unset), ht_data);
3482 if (!g_slist_find(ht_data->msglist, GINT_TO_POINTER(msginfo->msgnum)))
3483 ht_data->msglist = g_slist_prepend(ht_data->msglist,
3484 GINT_TO_POINTER(msginfo->msgnum));
3487 debug_print("IMAP changing flags\n");
3489 ok = imap_set_message_flags(session, &numlist, flags_set, TRUE);
3490 if (ok != IMAP_SUCCESS) {
3497 ok = imap_set_message_flags(session, &numlist, flags_unset, FALSE);
3498 if (ok != IMAP_SUCCESS) {
3504 msginfo->flags.perm_flags = newflags;
3509 static gint imap_remove_msg(Folder *folder, FolderItem *item, gint uid)
3512 IMAPSession *session;
3514 MsgNumberList numlist;
3516 g_return_val_if_fail(folder != NULL, -1);
3517 g_return_val_if_fail(FOLDER_CLASS(folder) == &imap_class, -1);
3518 g_return_val_if_fail(item != NULL, -1);
3520 session = imap_session_get(folder);
3521 if (!session) return -1;
3523 ok = imap_select(session, IMAP_FOLDER(folder), item->path,
3524 NULL, NULL, NULL, NULL, FALSE);
3525 if (ok != IMAP_SUCCESS) {
3529 numlist.next = NULL;
3530 numlist.data = GINT_TO_POINTER(uid);
3532 ok = imap_set_message_flags
3533 (IMAP_SESSION(REMOTE_FOLDER(folder)->session),
3534 &numlist, IMAP_FLAG_DELETED, TRUE);
3535 if (ok != IMAP_SUCCESS) {
3536 log_warning(_("can't set deleted flags: %d\n"), uid);
3541 if (!session->uidplus) {
3542 ok = imap_cmd_expunge(session);
3546 uidstr = g_strdup_printf("%u", uid);
3547 ok = imap_cmd_expunge(session);
3550 if (ok != IMAP_SUCCESS) {
3551 log_warning(_("can't expunge\n"));
3556 IMAP_FOLDER_ITEM(item)->uid_list = g_slist_remove(
3557 IMAP_FOLDER_ITEM(item)->uid_list, numlist.data);
3558 dir = folder_item_get_path(item);
3559 if (is_dir_exist(dir))
3560 remove_numbered_files(dir, uid, uid);
3563 return IMAP_SUCCESS;
3566 static gint compare_msginfo(gconstpointer a, gconstpointer b)
3568 return ((MsgInfo *)a)->msgnum - ((MsgInfo *)b)->msgnum;
3571 static guint gslist_find_next_num(MsgNumberList **list, guint num)
3575 g_return_val_if_fail(list != NULL, -1);
3577 for (elem = *list; elem != NULL; elem = g_slist_next(elem))
3578 if (GPOINTER_TO_INT(elem->data) >= num)
3581 return elem != NULL ? GPOINTER_TO_INT(elem->data) : (gint)-1;
3585 * NEW and DELETED flags are not syncronized
3586 * - The NEW/RECENT flags in IMAP folders can not really be directly
3587 * modified by Sylpheed
3588 * - The DELETE/DELETED flag in IMAP and Sylpheed don't have the same
3589 * meaning, in IMAP it always removes the messages from the FolderItem
3590 * in Sylpheed it can mean to move the message to trash
3593 typedef struct _get_flags_data {
3596 MsgInfoList *msginfo_list;
3597 GRelation *msgflags;
3598 gboolean full_search;
3602 static /*gint*/ void *imap_get_flags_thread(void *data)
3604 get_flags_data *stuff = (get_flags_data *)data;
3605 Folder *folder = stuff->folder;
3606 FolderItem *item = stuff->item;
3607 MsgInfoList *msginfo_list = stuff->msginfo_list;
3608 GRelation *msgflags = stuff->msgflags;
3609 gboolean full_search = stuff->full_search;
3610 IMAPSession *session;
3611 GSList *sorted_list = NULL;
3612 GSList *unseen = NULL, *answered = NULL, *flagged = NULL, *deleted = NULL;
3613 GSList *p_unseen, *p_answered, *p_flagged, *p_deleted;
3615 GSList *seq_list, *cur;
3616 gboolean reverse_seen = FALSE;
3619 gint exists_cnt, unseen_cnt;
3620 gboolean selected_folder;
3622 if (folder == NULL || item == NULL) {
3624 return GINT_TO_POINTER(-1);
3627 session = imap_session_get(folder);
3628 if (session == NULL) {
3630 return GINT_TO_POINTER(-1);
3633 selected_folder = (session->mbox != NULL) &&
3634 (!strcmp(session->mbox, item->path));
3636 if (!selected_folder) {
3637 ok = imap_select(session, IMAP_FOLDER(folder), item->path,
3638 &exists_cnt, NULL, &unseen_cnt, NULL, TRUE);
3639 if (ok != IMAP_SUCCESS) {
3642 return GINT_TO_POINTER(-1);
3645 if (unseen_cnt > exists_cnt / 2)
3646 reverse_seen = TRUE;
3649 if (item->unread_msgs > item->total_msgs / 2)
3650 reverse_seen = TRUE;
3653 cmd_buf = g_string_new(NULL);
3655 sorted_list = g_slist_sort(g_slist_copy(msginfo_list), compare_msginfo);
3657 seq_list = imap_get_lep_set_from_msglist(msginfo_list);
3659 struct mailimap_set * set;
3660 set = mailimap_set_new_interval(1, 0);
3661 seq_list = g_slist_append(NULL, set);
3664 for (cur = seq_list; cur != NULL; cur = g_slist_next(cur)) {
3665 struct mailimap_set * imapset;
3666 clist * lep_uidlist;
3669 imapset = cur->data;
3671 r = imap_threaded_search(folder, IMAP_SEARCH_TYPE_SEEN,
3672 imapset, &lep_uidlist);
3675 r = imap_threaded_search(folder,
3676 IMAP_SEARCH_TYPE_UNSEEN,
3677 imapset, &lep_uidlist);
3679 if (r == MAILIMAP_NO_ERROR) {
3682 uidlist = imap_uid_list_from_lep(lep_uidlist);
3683 mailimap_search_result_free(lep_uidlist);
3685 unseen = g_slist_concat(unseen, uidlist);
3688 r = imap_threaded_search(folder, IMAP_SEARCH_TYPE_ANSWERED,
3689 imapset, &lep_uidlist);
3690 if (r == MAILIMAP_NO_ERROR) {
3693 uidlist = imap_uid_list_from_lep(lep_uidlist);
3694 mailimap_search_result_free(lep_uidlist);
3696 answered = g_slist_concat(answered, uidlist);
3699 r = imap_threaded_search(folder, IMAP_SEARCH_TYPE_FLAGGED,
3700 imapset, &lep_uidlist);
3701 if (r == MAILIMAP_NO_ERROR) {
3704 uidlist = imap_uid_list_from_lep(lep_uidlist);
3705 mailimap_search_result_free(lep_uidlist);
3707 flagged = g_slist_concat(flagged, uidlist);
3710 r = imap_threaded_search(folder, IMAP_SEARCH_TYPE_DELETED,
3711 imapset, &lep_uidlist);
3712 if (r == MAILIMAP_NO_ERROR) {
3715 uidlist = imap_uid_list_from_lep(lep_uidlist);
3716 mailimap_search_result_free(lep_uidlist);
3718 deleted = g_slist_concat(deleted, uidlist);
3723 p_answered = answered;
3724 p_flagged = flagged;
3725 p_deleted = deleted;
3727 for (elem = sorted_list; elem != NULL; elem = g_slist_next(elem)) {
3732 msginfo = (MsgInfo *) elem->data;
3733 flags = msginfo->flags.perm_flags;
3734 wasnew = (flags & MSG_NEW);
3735 flags &= ~((reverse_seen ? 0 : MSG_UNREAD | MSG_NEW) | MSG_REPLIED | MSG_MARKED);
3737 flags |= MSG_UNREAD | (wasnew ? MSG_NEW : 0);
3738 if (gslist_find_next_num(&p_unseen, msginfo->msgnum) == msginfo->msgnum) {
3739 if (!reverse_seen) {
3740 flags |= MSG_UNREAD | (wasnew ? MSG_NEW : 0);
3742 flags &= ~(MSG_UNREAD | MSG_NEW);
3745 if (gslist_find_next_num(&p_answered, msginfo->msgnum) == msginfo->msgnum)
3746 flags |= MSG_REPLIED;
3748 flags &= ~MSG_REPLIED;
3749 if (gslist_find_next_num(&p_flagged, msginfo->msgnum) == msginfo->msgnum)
3750 flags |= MSG_MARKED;
3752 flags &= ~MSG_MARKED;
3753 if (gslist_find_next_num(&p_deleted, msginfo->msgnum) == msginfo->msgnum)
3754 flags |= MSG_DELETED;
3756 flags &= ~MSG_DELETED;
3757 g_relation_insert(msgflags, msginfo, GINT_TO_POINTER(flags));
3760 imap_lep_set_free(seq_list);
3761 g_slist_free(flagged);
3762 g_slist_free(deleted);
3763 g_slist_free(answered);
3764 g_slist_free(unseen);
3765 g_slist_free(sorted_list);
3766 g_string_free(cmd_buf, TRUE);
3770 return GINT_TO_POINTER(0);
3773 static gint imap_get_flags(Folder *folder, FolderItem *item,
3774 MsgInfoList *msginfo_list, GRelation *msgflags)
3777 get_flags_data *data = g_new0(get_flags_data, 1);
3779 data->folder = folder;
3781 data->msginfo_list = msginfo_list;
3782 data->msgflags = msgflags;
3783 data->full_search = FALSE;
3785 GSList *tmp = NULL, *cur;
3787 if (prefs_common.work_offline && !inc_offline_should_override()) {
3792 tmp = folder_item_get_msg_list(item);
3794 if (g_slist_length(tmp) == g_slist_length(msginfo_list))
3795 data->full_search = TRUE;
3797 for (cur = tmp; cur; cur = cur->next)
3798 procmsg_msginfo_free((MsgInfo *)cur->data);
3802 result = GPOINTER_TO_INT(imap_get_flags_thread(data));
3809 static gboolean process_flags(gpointer key, gpointer value, gpointer user_data)
3811 gboolean flags_set = GPOINTER_TO_INT(user_data);
3812 gint flags_value = GPOINTER_TO_INT(key);
3813 hashtable_data *data = (hashtable_data *)value;
3814 IMAPFolderItem *_item = data->item;
3815 FolderItem *item = (FolderItem *)_item;
3816 gint ok = IMAP_ERROR;
3817 IMAPSession *session = imap_session_get(item->folder);
3819 data->msglist = g_slist_reverse(data->msglist);
3821 debug_print("IMAP %ssetting flags to %d for %d messages\n",
3824 g_slist_length(data->msglist));
3828 ok = imap_select(session, IMAP_FOLDER(item->folder), item->path,
3829 NULL, NULL, NULL, NULL, FALSE);
3831 if (ok == IMAP_SUCCESS) {
3832 imap_set_message_flags(data->session, data->msglist, flags_value, flags_set);
3834 g_warning("can't select mailbox %s\n", item->path);
3838 g_slist_free(data->msglist);
3843 static void process_hashtable(IMAPFolderItem *item)
3845 if (item->flags_set_table) {
3846 g_hash_table_foreach_remove(item->flags_set_table, process_flags, GINT_TO_POINTER(TRUE));
3847 g_hash_table_destroy(item->flags_set_table);
3848 item->flags_set_table = NULL;
3850 if (item->flags_unset_table) {
3851 g_hash_table_foreach_remove(item->flags_unset_table, process_flags, GINT_TO_POINTER(FALSE));
3852 g_hash_table_destroy(item->flags_unset_table);
3853 item->flags_unset_table = NULL;
3857 static void imap_set_batch (Folder *folder, FolderItem *_item, gboolean batch)
3859 IMAPFolderItem *item = (IMAPFolderItem *)_item;
3861 g_return_if_fail(item != NULL);
3863 if (item->batching == batch)
3867 item->batching = TRUE;
3868 debug_print("IMAP switching to batch mode\n");
3869 if (!item->flags_set_table) {
3870 item->flags_set_table = g_hash_table_new(NULL, g_direct_equal);
3872 if (!item->flags_unset_table) {
3873 item->flags_unset_table = g_hash_table_new(NULL, g_direct_equal);
3876 debug_print("IMAP switching away from batch mode\n");
3878 process_hashtable(item);
3879 item->batching = FALSE;
3885 /* data types conversion libetpan <-> sylpheed */
3889 #define ETPAN_IMAP_MB_MARKED 1
3890 #define ETPAN_IMAP_MB_UNMARKED 2
3891 #define ETPAN_IMAP_MB_NOSELECT 4
3892 #define ETPAN_IMAP_MB_NOINFERIORS 8
3894 static int imap_flags_to_flags(struct mailimap_mbx_list_flags * imap_flags)
3900 if (imap_flags->mbf_type == MAILIMAP_MBX_LIST_FLAGS_SFLAG) {
3901 switch (imap_flags->mbf_sflag) {
3902 case MAILIMAP_MBX_LIST_SFLAG_MARKED:
3903 flags |= ETPAN_IMAP_MB_MARKED;
3905 case MAILIMAP_MBX_LIST_SFLAG_NOSELECT:
3906 flags |= ETPAN_IMAP_MB_NOSELECT;
3908 case MAILIMAP_MBX_LIST_SFLAG_UNMARKED:
3909 flags |= ETPAN_IMAP_MB_UNMARKED;
3914 for(cur = clist_begin(imap_flags->mbf_oflags) ; cur != NULL ;
3915 cur = clist_next(cur)) {
3916 struct mailimap_mbx_list_oflag * oflag;
3918 oflag = clist_content(cur);
3920 switch (oflag->of_type) {
3921 case MAILIMAP_MBX_LIST_OFLAG_NOINFERIORS:
3922 flags |= ETPAN_IMAP_MB_NOINFERIORS;
3930 static GSList * imap_list_from_lep(IMAPFolder * folder,
3931 clist * list, const gchar * real_path, gboolean all)
3938 for(iter = clist_begin(list) ; iter != NULL ;
3939 iter = clist_next(iter)) {
3940 struct mailimap_mailbox_list * mb;
3948 FolderItem *new_item;
3950 mb = clist_content(iter);
3956 if (mb->mb_flag != NULL)
3957 flags = imap_flags_to_flags(mb->mb_flag);
3959 delimiter = mb->mb_delimiter;
3962 dup_name = strdup(name);
3963 if (delimiter != '\0')
3964 subst_char(dup_name, delimiter, '/');
3966 base = g_path_get_basename(dup_name);
3967 if (base[0] == '.') {
3973 if (!all && strcmp(dup_name, real_path) == 0) {
3979 if (!all && dup_name[strlen(dup_name)-1] == '/') {
3985 loc_name = imap_modified_utf7_to_utf8(base);
3986 loc_path = imap_modified_utf7_to_utf8(dup_name);
3988 new_item = folder_item_new(FOLDER(folder), loc_name, loc_path);
3989 if ((flags & ETPAN_IMAP_MB_NOINFERIORS) != 0)
3990 new_item->no_sub = TRUE;
3991 if (strcmp(dup_name, "INBOX") != 0 &&
3992 ((flags & ETPAN_IMAP_MB_NOSELECT) != 0))
3993 new_item->no_select = TRUE;
3995 item_list = g_slist_append(item_list, new_item);
3997 debug_print("folder '%s' found.\n", loc_path);
4008 static GSList * imap_get_lep_set_from_numlist(MsgNumberList *numlist)
4010 GSList *sorted_list, *cur;
4011 guint first, last, next;
4012 GSList *ret_list = NULL;
4014 struct mailimap_set * current_set;
4015 unsigned int item_count;
4017 if (numlist == NULL)
4021 current_set = mailimap_set_new_empty();
4023 sorted_list = g_slist_copy(numlist);
4024 sorted_list = g_slist_sort(sorted_list, g_int_compare);
4026 first = GPOINTER_TO_INT(sorted_list->data);
4029 for (cur = sorted_list; cur != NULL; cur = g_slist_next(cur)) {
4030 if (GPOINTER_TO_INT(cur->data) == 0)
4035 last = GPOINTER_TO_INT(cur->data);
4037 next = GPOINTER_TO_INT(cur->next->data);
4041 if (last + 1 != next || next == 0) {
4043 struct mailimap_set_item * item;
4044 item = mailimap_set_item_new(first, last);
4045 mailimap_set_add(current_set, item);
4050 if (count >= IMAP_SET_MAX_COUNT) {
4051 ret_list = g_slist_append(ret_list,
4053 current_set = mailimap_set_new_empty();
4060 if (clist_count(current_set->set_list) > 0) {
4061 ret_list = g_slist_append(ret_list,
4065 g_slist_free(sorted_list);
4070 static GSList * imap_get_lep_set_from_msglist(MsgInfoList *msglist)
4072 MsgNumberList *numlist = NULL;
4076 for (cur = msglist; cur != NULL; cur = g_slist_next(cur)) {
4077 MsgInfo *msginfo = (MsgInfo *) cur->data;
4079 numlist = g_slist_append(numlist, GINT_TO_POINTER(msginfo->msgnum));
4081 seq_list = imap_get_lep_set_from_numlist(numlist);
4082 g_slist_free(numlist);
4087 static GSList * imap_uid_list_from_lep(clist * list)
4094 for(iter = clist_begin(list) ; iter != NULL ;
4095 iter = clist_next(iter)) {
4098 puid = clist_content(iter);
4099 result = g_slist_append(result, GINT_TO_POINTER(* puid));
4105 static GSList * imap_uid_list_from_lep_tab(carray * list)
4112 for(i = 0 ; i < carray_count(list) ; i ++) {
4115 puid = carray_get(list, i);
4116 result = g_slist_append(result, GINT_TO_POINTER(* puid));
4122 static MsgInfo *imap_envelope_from_lep(struct imap_fetch_env_info * info,
4125 MsgInfo *msginfo = NULL;
4128 MsgFlags flags = {0, 0};
4130 if (info->headers == NULL)
4133 MSG_SET_TMP_FLAGS(flags, MSG_IMAP);
4134 if (folder_has_parent_of_type(item, F_QUEUE)) {
4135 MSG_SET_TMP_FLAGS(flags, MSG_QUEUED);
4136 } else if (folder_has_parent_of_type(item, F_DRAFT)) {
4137 MSG_SET_TMP_FLAGS(flags, MSG_DRAFT);
4139 flags.perm_flags = info->flags;
4143 msginfo = procheader_parse_str(info->headers, flags, FALSE, FALSE);
4146 msginfo->msgnum = uid;
4147 msginfo->size = size;
4153 static void imap_lep_set_free(GSList *seq_list)
4157 for(cur = seq_list ; cur != NULL ; cur = g_slist_next(cur)) {
4158 struct mailimap_set * imapset;
4160 imapset = cur->data;
4161 mailimap_set_free(imapset);
4163 g_slist_free(seq_list);
4166 static struct mailimap_flag_list * imap_flag_to_lep(IMAPFlags flags)
4168 struct mailimap_flag_list * flag_list;
4170 flag_list = mailimap_flag_list_new_empty();
4172 if (IMAP_IS_SEEN(flags))
4173 mailimap_flag_list_add(flag_list,
4174 mailimap_flag_new_seen());
4175 if (IMAP_IS_ANSWERED(flags))
4176 mailimap_flag_list_add(flag_list,
4177 mailimap_flag_new_answered());
4178 if (IMAP_IS_FLAGGED(flags))
4179 mailimap_flag_list_add(flag_list,
4180 mailimap_flag_new_flagged());
4181 if (IMAP_IS_DELETED(flags))
4182 mailimap_flag_list_add(flag_list,
4183 mailimap_flag_new_deleted());
4184 if (IMAP_IS_DRAFT(flags))
4185 mailimap_flag_list_add(flag_list,
4186 mailimap_flag_new_draft());
4191 guint imap_folder_get_refcnt(Folder *folder)
4193 return ((IMAPFolder *)folder)->refcnt;
4196 void imap_folder_ref(Folder *folder)
4198 ((IMAPFolder *)folder)->refcnt++;
4201 void imap_folder_unref(Folder *folder)
4203 if (((IMAPFolder *)folder)->refcnt > 0)
4204 ((IMAPFolder *)folder)->refcnt--;
4207 #else /* HAVE_LIBETPAN */
4209 static FolderClass imap_class;
4211 static Folder *imap_folder_new (const gchar *name,
4216 static gint imap_create_tree (Folder *folder)
4220 static FolderItem *imap_create_folder (Folder *folder,
4226 static gint imap_rename_folder (Folder *folder,
4233 FolderClass *imap_get_class(void)
4235 if (imap_class.idstr == NULL) {
4236 imap_class.type = F_IMAP;
4237 imap_class.idstr = "imap";
4238 imap_class.uistr = "IMAP4";
4240 imap_class.new_folder = imap_folder_new;
4241 imap_class.create_tree = imap_create_tree;
4242 imap_class.create_folder = imap_create_folder;
4243 imap_class.rename_folder = imap_rename_folder;
4244 /* nothing implemented */
4251 void imap_synchronise(FolderItem *item)
4253 imap_gtk_synchronise(item);