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;
1911 gint exists, recent, unseen;
1912 guint32 uid_validity;
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 session_set_access_time(SESSION(session));
2044 #define MAX_MSG_NUM 50
2046 static GSList *imap_get_uncached_messages(IMAPSession *session,
2048 MsgNumberList *numlist)
2050 GSList *result = NULL;
2052 uncached_data *data = g_new0(uncached_data, 1);
2057 data->total = g_slist_length(numlist);
2058 debug_print("messages list : %i\n", data->total);
2060 while (cur != NULL) {
2061 GSList * partial_result;
2069 while (count < MAX_MSG_NUM) {
2074 if (newlist == NULL)
2075 llast = newlist = g_slist_append(newlist, p);
2077 llast = g_slist_append(llast, p);
2078 llast = llast->next;
2088 data->session = session;
2090 data->numlist = newlist;
2093 if (prefs_common.work_offline && !inc_offline_should_override()) {
2099 (GSList *)imap_get_uncached_messages_thread(data);
2101 statusbar_progress_all(data->cur,data->total, 1);
2103 g_slist_free(newlist);
2105 result = g_slist_concat(result, partial_result);
2109 statusbar_progress_all(0,0,0);
2110 statusbar_pop_all();
2115 static void imap_delete_all_cached_messages(FolderItem *item)
2119 g_return_if_fail(item != NULL);
2120 g_return_if_fail(item->folder != NULL);
2121 g_return_if_fail(FOLDER_CLASS(item->folder) == &imap_class);
2123 debug_print("Deleting all cached messages...\n");
2125 dir = folder_item_get_path(item);
2126 if (is_dir_exist(dir))
2127 remove_all_numbered_files(dir);
2130 debug_print("done.\n");
2133 static IMAPNameSpace *imap_find_namespace_from_list(GList *ns_list,
2136 IMAPNameSpace *namespace = NULL;
2137 gchar *tmp_path, *name;
2139 if (!path) path = "";
2141 for (; ns_list != NULL; ns_list = ns_list->next) {
2142 IMAPNameSpace *tmp_ns = ns_list->data;
2144 Xstrcat_a(tmp_path, path, "/", return namespace);
2145 Xstrdup_a(name, tmp_ns->name, return namespace);
2146 if (tmp_ns->separator && tmp_ns->separator != '/') {
2147 subst_char(tmp_path, tmp_ns->separator, '/');
2148 subst_char(name, tmp_ns->separator, '/');
2150 if (strncmp(tmp_path, name, strlen(name)) == 0)
2157 static IMAPNameSpace *imap_find_namespace(IMAPFolder *folder,
2160 IMAPNameSpace *namespace;
2162 g_return_val_if_fail(folder != NULL, NULL);
2164 namespace = imap_find_namespace_from_list(folder->ns_personal, path);
2165 if (namespace) return namespace;
2166 namespace = imap_find_namespace_from_list(folder->ns_others, path);
2167 if (namespace) return namespace;
2168 namespace = imap_find_namespace_from_list(folder->ns_shared, path);
2169 if (namespace) return namespace;
2175 static gchar imap_get_path_separator(IMAPFolder *folder, const gchar *path)
2177 IMAPNameSpace *namespace;
2178 gchar separator = '/';
2180 if (folder->last_seen_separator == 0) {
2182 int r = imap_threaded_list((Folder *)folder, "", "", &lep_list);
2183 if (r != MAILIMAP_NO_ERROR) {
2184 log_warning(_("LIST failed\n"));
2188 if (clist_count(lep_list) > 0) {
2189 clistiter * iter = clist_begin(lep_list);
2190 struct mailimap_mailbox_list * mb;
2191 mb = clist_content(iter);
2193 folder->last_seen_separator = mb->mb_delimiter;
2194 debug_print("got separator: %c\n", folder->last_seen_separator);
2196 mailimap_list_result_free(lep_list);
2199 if (folder->last_seen_separator != 0) {
2200 debug_print("using separator: %c\n", folder->last_seen_separator);
2201 return folder->last_seen_separator;
2204 namespace = imap_find_namespace(folder, path);
2205 if (namespace && namespace->separator)
2206 separator = namespace->separator;
2211 static gchar *imap_get_real_path(IMAPFolder *folder, const gchar *path)
2216 g_return_val_if_fail(folder != NULL, NULL);
2217 g_return_val_if_fail(path != NULL, NULL);
2219 real_path = imap_utf8_to_modified_utf7(path);
2220 separator = imap_get_path_separator(folder, path);
2221 imap_path_separator_subst(real_path, separator);
2226 static gint imap_set_message_flags(IMAPSession *session,
2227 MsgNumberList *numlist,
2235 seq_list = imap_get_lep_set_from_numlist(numlist);
2237 for(cur = seq_list ; cur != NULL ; cur = g_slist_next(cur)) {
2238 struct mailimap_set * imapset;
2240 imapset = cur->data;
2242 ok = imap_cmd_store(session, imapset,
2246 imap_lep_set_free(seq_list);
2248 return IMAP_SUCCESS;
2251 typedef struct _select_data {
2252 IMAPSession *session;
2257 guint32 *uid_validity;
2261 static gint imap_select(IMAPSession *session, IMAPFolder *folder,
2263 gint *exists, gint *recent, gint *unseen,
2264 guint32 *uid_validity, gboolean block)
2268 gint exists_, recent_, unseen_;
2269 guint32 uid_validity_;
2271 if (!exists && !recent && !unseen && !uid_validity) {
2272 if (session->mbox && strcmp(session->mbox, path) == 0)
2273 return IMAP_SUCCESS;
2282 uid_validity = &uid_validity_;
2284 g_free(session->mbox);
2285 session->mbox = NULL;
2287 real_path = imap_get_real_path(folder, path);
2289 ok = imap_cmd_select(session, real_path,
2290 exists, recent, unseen, uid_validity, block);
2291 if (ok != IMAP_SUCCESS)
2292 log_warning(_("can't select folder: %s\n"), real_path);
2294 session->mbox = g_strdup(path);
2295 session->folder_content_changed = FALSE;
2302 static gint imap_status(IMAPSession *session, IMAPFolder *folder,
2303 const gchar *path, IMAPFolderItem *item,
2305 guint32 *uid_next, guint32 *uid_validity,
2306 gint *unseen, gboolean block)
2310 struct mailimap_mailbox_data_status * data_status;
2315 real_path = imap_get_real_path(folder, path);
2318 if (time(NULL) - item->last_update >= 5 && item->last_update != 1) {
2319 /* do the full stuff */
2320 item->last_update = 1; /* force update */
2321 debug_print("updating everything\n");
2322 r = imap_status(session, folder, path, item,
2323 &item->c_messages, &item->c_uid_next,
2324 &item->c_uid_validity, &item->c_unseen, block);
2325 if (r != MAILIMAP_NO_ERROR) {
2326 debug_print("status err %d\n", r);
2329 item->last_update = time(NULL);
2331 *messages = item->c_messages;
2333 *uid_next = item->c_uid_next;
2335 *uid_validity = item->c_uid_validity;
2337 *unseen = item->c_unseen;
2339 } else if (time(NULL) - item->last_update < 5) {
2340 /* return cached stuff */
2341 debug_print("using cache\n");
2343 *messages = item->c_messages;
2345 *uid_next = item->c_uid_next;
2347 *uid_validity = item->c_uid_validity;
2349 *unseen = item->c_unseen;
2354 /* if we get there, we're updating cache */
2368 r = imap_threaded_status(FOLDER(folder), real_path,
2369 &data_status, mask);
2372 if (r != MAILIMAP_NO_ERROR) {
2373 debug_print("status err %d\n", r);
2377 if (data_status->st_info_list == NULL) {
2378 mailimap_mailbox_data_status_free(data_status);
2379 debug_print("status->st_info_list == NULL\n");
2384 for(iter = clist_begin(data_status->st_info_list) ; iter != NULL ;
2385 iter = clist_next(iter)) {
2386 struct mailimap_status_info * info;
2388 info = clist_content(iter);
2389 switch (info->st_att) {
2390 case MAILIMAP_STATUS_ATT_MESSAGES:
2391 * messages = info->st_value;
2392 got_values |= 1 << 0;
2395 case MAILIMAP_STATUS_ATT_UIDNEXT:
2396 * uid_next = info->st_value;
2397 got_values |= 1 << 2;
2400 case MAILIMAP_STATUS_ATT_UIDVALIDITY:
2401 * uid_validity = info->st_value;
2402 got_values |= 1 << 3;
2405 case MAILIMAP_STATUS_ATT_UNSEEN:
2406 * unseen = info->st_value;
2407 got_values |= 1 << 4;
2411 mailimap_mailbox_data_status_free(data_status);
2413 if (got_values != mask) {
2414 debug_print("status: incomplete values received (%d)\n", got_values);
2417 return IMAP_SUCCESS;
2420 static void imap_free_capabilities(IMAPSession *session)
2422 slist_free_strings(session->capability);
2423 g_slist_free(session->capability);
2424 session->capability = NULL;
2427 /* low-level IMAP4rev1 commands */
2430 static gint imap_cmd_authenticate(IMAPSession *session, const gchar *user,
2431 const gchar *pass, IMAPAuthType type)
2438 gchar hexdigest[33];
2442 auth_type = "CRAM-MD5";
2444 imap_gen_send(session, "AUTHENTICATE %s", auth_type);
2445 ok = imap_gen_recv(session, &buf);
2446 if (ok != IMAP_SUCCESS || buf[0] != '+' || buf[1] != ' ') {
2451 challenge = g_malloc(strlen(buf + 2) + 1);
2452 challenge_len = base64_decode(challenge, buf + 2, -1);
2453 challenge[challenge_len] = '\0';
2456 md5_hex_hmac(hexdigest, challenge, challenge_len, pass, strlen(pass));
2459 response = g_strdup_printf("%s %s", user, hexdigest);
2460 response64 = g_malloc((strlen(response) + 3) * 2 + 1);
2461 base64_encode(response64, response, strlen(response));
2464 sock_puts(SESSION(session)->sock, response64);
2465 ok = imap_cmd_ok(session, NULL);
2466 if (ok != IMAP_SUCCESS)
2467 log_warning(_("IMAP4 authentication failed.\n"));
2473 static gint imap_cmd_login(IMAPSession *session,
2474 const gchar *user, const gchar *pass,
2480 log_print("IMAP4> Logging %s to %s using %s\n",
2482 SESSION(session)->server,
2484 r = imap_threaded_login(session->folder, user, pass, type);
2485 if (r != MAILIMAP_NO_ERROR) {
2486 log_error("IMAP4< Error logging in to %s\n",
2487 SESSION(session)->server);
2495 static gint imap_cmd_logout(IMAPSession *session)
2497 imap_threaded_disconnect(session->folder);
2499 return IMAP_SUCCESS;
2502 static gint imap_cmd_noop(IMAPSession *session)
2505 unsigned int exists;
2507 r = imap_threaded_noop(session->folder, &exists);
2508 if (r != MAILIMAP_NO_ERROR) {
2509 debug_print("noop err %d\n", r);
2512 session->exists = exists;
2513 session_set_access_time(SESSION(session));
2515 return IMAP_SUCCESS;
2519 static gint imap_cmd_starttls(IMAPSession *session)
2523 r = imap_threaded_starttls(session->folder);
2524 if (r != MAILIMAP_NO_ERROR) {
2525 debug_print("starttls err %d\n", r);
2528 return IMAP_SUCCESS;
2532 static gint imap_cmd_select(IMAPSession *session, const gchar *folder,
2533 gint *exists, gint *recent, gint *unseen,
2534 guint32 *uid_validity, gboolean block)
2538 r = imap_threaded_select(session->folder, folder,
2539 exists, recent, unseen, uid_validity);
2540 if (r != MAILIMAP_NO_ERROR) {
2541 debug_print("select err %d\n", r);
2544 return IMAP_SUCCESS;
2547 static gint imap_cmd_examine(IMAPSession *session, const gchar *folder,
2548 gint *exists, gint *recent, gint *unseen,
2549 guint32 *uid_validity, gboolean block)
2553 r = imap_threaded_examine(session->folder, folder,
2554 exists, recent, unseen, uid_validity);
2555 if (r != MAILIMAP_NO_ERROR) {
2556 debug_print("examine err %d\n", r);
2560 return IMAP_SUCCESS;
2563 static gint imap_cmd_create(IMAPSession *session, const gchar *folder)
2567 r = imap_threaded_create(session->folder, folder);
2568 if (r != MAILIMAP_NO_ERROR) {
2573 return IMAP_SUCCESS;
2576 static gint imap_cmd_rename(IMAPSession *session, const gchar *old_folder,
2577 const gchar *new_folder)
2581 r = imap_threaded_rename(session->folder, old_folder,
2583 if (r != MAILIMAP_NO_ERROR) {
2588 return IMAP_SUCCESS;
2591 static gint imap_cmd_delete(IMAPSession *session, const gchar *folder)
2596 r = imap_threaded_delete(session->folder, folder);
2597 if (r != MAILIMAP_NO_ERROR) {
2602 return IMAP_SUCCESS;
2605 typedef struct _fetch_data {
2606 IMAPSession *session;
2608 const gchar *filename;
2614 static void *imap_cmd_fetch_thread(void *data)
2616 fetch_data *stuff = (fetch_data *)data;
2617 IMAPSession *session = stuff->session;
2618 guint32 uid = stuff->uid;
2619 const gchar *filename = stuff->filename;
2623 r = imap_threaded_fetch_content(session->folder,
2627 r = imap_threaded_fetch_content(session->folder,
2630 if (r != MAILIMAP_NO_ERROR) {
2631 debug_print("fetch err %d\n", r);
2632 return GINT_TO_POINTER(IMAP_ERROR);
2634 return GINT_TO_POINTER(IMAP_SUCCESS);
2637 static gint imap_cmd_fetch(IMAPSession *session, guint32 uid,
2638 const gchar *filename, gboolean headers,
2641 fetch_data *data = g_new0(fetch_data, 1);
2644 data->session = session;
2646 data->filename = filename;
2647 data->headers = headers;
2650 if (prefs_common.work_offline && !inc_offline_should_override()) {
2654 statusbar_print_all(_("Fetching message..."));
2655 result = GPOINTER_TO_INT(imap_cmd_fetch_thread(data));
2656 statusbar_pop_all();
2662 static gint imap_cmd_append(IMAPSession *session, const gchar *destfolder,
2663 const gchar *file, IMAPFlags flags,
2666 struct mailimap_flag_list * flag_list;
2669 g_return_val_if_fail(file != NULL, IMAP_ERROR);
2671 flag_list = imap_flag_to_lep(flags);
2672 r = imap_threaded_append(session->folder, destfolder,
2674 if (new_uid != NULL)
2677 if (r != MAILIMAP_NO_ERROR) {
2678 debug_print("append err %d\n", r);
2681 return IMAP_SUCCESS;
2684 static gint imap_cmd_copy(IMAPSession *session, struct mailimap_set * set,
2685 const gchar *destfolder, GRelation *uid_mapping)
2689 g_return_val_if_fail(session != NULL, IMAP_ERROR);
2690 g_return_val_if_fail(set != NULL, IMAP_ERROR);
2691 g_return_val_if_fail(destfolder != NULL, IMAP_ERROR);
2693 r = imap_threaded_copy(session->folder, set, destfolder);
2694 if (r != MAILIMAP_NO_ERROR) {
2699 return IMAP_SUCCESS;
2702 static gint imap_cmd_store(IMAPSession *session, struct mailimap_set * set,
2703 IMAPFlags flags, int do_add)
2706 struct mailimap_flag_list * flag_list;
2707 struct mailimap_store_att_flags * store_att_flags;
2709 flag_list = imap_flag_to_lep(flags);
2713 mailimap_store_att_flags_new_add_flags_silent(flag_list);
2716 mailimap_store_att_flags_new_remove_flags_silent(flag_list);
2718 r = imap_threaded_store(session->folder, set, store_att_flags);
2719 if (r != MAILIMAP_NO_ERROR) {
2724 return IMAP_SUCCESS;
2727 static gint imap_cmd_expunge(IMAPSession *session)
2731 if (prefs_common.work_offline && !inc_offline_should_override()) {
2735 r = imap_threaded_expunge(session->folder);
2736 if (r != MAILIMAP_NO_ERROR) {
2741 return IMAP_SUCCESS;
2744 static void imap_path_separator_subst(gchar *str, gchar separator)
2747 gboolean in_escape = FALSE;
2749 if (!separator || separator == '/') return;
2751 for (p = str; *p != '\0'; p++) {
2752 if (*p == '/' && !in_escape)
2754 else if (*p == '&' && *(p + 1) != '-' && !in_escape)
2756 else if (*p == '-' && in_escape)
2761 static gchar *imap_modified_utf7_to_utf8(const gchar *mutf7_str)
2763 static iconv_t cd = (iconv_t)-1;
2764 static gboolean iconv_ok = TRUE;
2767 size_t norm_utf7_len;
2769 gchar *to_str, *to_p;
2771 gboolean in_escape = FALSE;
2773 if (!iconv_ok) return g_strdup(mutf7_str);
2775 if (cd == (iconv_t)-1) {
2776 cd = iconv_open(CS_INTERNAL, CS_UTF_7);
2777 if (cd == (iconv_t)-1) {
2778 g_warning("iconv cannot convert UTF-7 to %s\n",
2781 return g_strdup(mutf7_str);
2785 /* modified UTF-7 to normal UTF-7 conversion */
2786 norm_utf7 = g_string_new(NULL);
2788 for (p = mutf7_str; *p != '\0'; p++) {
2789 /* replace: '&' -> '+',
2791 escaped ',' -> '/' */
2792 if (!in_escape && *p == '&') {
2793 if (*(p + 1) != '-') {
2794 g_string_append_c(norm_utf7, '+');
2797 g_string_append_c(norm_utf7, '&');
2800 } else if (in_escape && *p == ',') {
2801 g_string_append_c(norm_utf7, '/');
2802 } else if (in_escape && *p == '-') {
2803 g_string_append_c(norm_utf7, '-');
2806 g_string_append_c(norm_utf7, *p);
2810 norm_utf7_p = norm_utf7->str;
2811 norm_utf7_len = norm_utf7->len;
2812 to_len = strlen(mutf7_str) * 5;
2813 to_p = to_str = g_malloc(to_len + 1);
2815 if (iconv(cd, (ICONV_CONST gchar **)&norm_utf7_p, &norm_utf7_len,
2816 &to_p, &to_len) == -1) {
2817 g_warning(_("iconv cannot convert UTF-7 to %s\n"),
2818 conv_get_locale_charset_str());
2819 g_string_free(norm_utf7, TRUE);
2821 return g_strdup(mutf7_str);
2824 /* second iconv() call for flushing */
2825 iconv(cd, NULL, NULL, &to_p, &to_len);
2826 g_string_free(norm_utf7, TRUE);
2832 static gchar *imap_utf8_to_modified_utf7(const gchar *from)
2834 static iconv_t cd = (iconv_t)-1;
2835 static gboolean iconv_ok = TRUE;
2836 gchar *norm_utf7, *norm_utf7_p;
2837 size_t from_len, norm_utf7_len;
2839 gchar *from_tmp, *to, *p;
2840 gboolean in_escape = FALSE;
2842 if (!iconv_ok) return g_strdup(from);
2844 if (cd == (iconv_t)-1) {
2845 cd = iconv_open(CS_UTF_7, CS_INTERNAL);
2846 if (cd == (iconv_t)-1) {
2847 g_warning(_("iconv cannot convert %s to UTF-7\n"),
2850 return g_strdup(from);
2854 /* UTF-8 to normal UTF-7 conversion */
2855 Xstrdup_a(from_tmp, from, return g_strdup(from));
2856 from_len = strlen(from);
2857 norm_utf7_len = from_len * 5;
2858 Xalloca(norm_utf7, norm_utf7_len + 1, return g_strdup(from));
2859 norm_utf7_p = norm_utf7;
2861 #define IS_PRINT(ch) (isprint(ch) && IS_ASCII(ch))
2863 while (from_len > 0) {
2864 if (*from_tmp == '+') {
2865 *norm_utf7_p++ = '+';
2866 *norm_utf7_p++ = '-';
2870 } else if (IS_PRINT(*(guchar *)from_tmp)) {
2871 /* printable ascii char */
2872 *norm_utf7_p = *from_tmp;
2878 size_t conv_len = 0;
2880 /* unprintable char: convert to UTF-7 */
2882 while (!IS_PRINT(*(guchar *)p) && conv_len < from_len) {
2883 conv_len += g_utf8_skip[*(guchar *)p];
2884 p += g_utf8_skip[*(guchar *)p];
2887 from_len -= conv_len;
2888 if (iconv(cd, (ICONV_CONST gchar **)&from_tmp,
2890 &norm_utf7_p, &norm_utf7_len) == -1) {
2891 g_warning(_("iconv cannot convert UTF-8 to UTF-7\n"));
2892 return g_strdup(from);
2895 /* second iconv() call for flushing */
2896 iconv(cd, NULL, NULL, &norm_utf7_p, &norm_utf7_len);
2902 *norm_utf7_p = '\0';
2903 to_str = g_string_new(NULL);
2904 for (p = norm_utf7; p < norm_utf7_p; p++) {
2905 /* replace: '&' -> "&-",
2908 BASE64 '/' -> ',' */
2909 if (!in_escape && *p == '&') {
2910 g_string_append(to_str, "&-");
2911 } else if (!in_escape && *p == '+') {
2912 if (*(p + 1) == '-') {
2913 g_string_append_c(to_str, '+');
2916 g_string_append_c(to_str, '&');
2919 } else if (in_escape && *p == '/') {
2920 g_string_append_c(to_str, ',');
2921 } else if (in_escape && *p == '-') {
2922 g_string_append_c(to_str, '-');
2925 g_string_append_c(to_str, *p);
2931 g_string_append_c(to_str, '-');
2935 g_string_free(to_str, FALSE);
2940 static gboolean imap_rename_folder_func(GNode *node, gpointer data)
2942 FolderItem *item = node->data;
2943 gchar **paths = data;
2944 const gchar *oldpath = paths[0];
2945 const gchar *newpath = paths[1];
2947 gchar *new_itempath;
2950 oldpathlen = strlen(oldpath);
2951 if (strncmp(oldpath, item->path, oldpathlen) != 0) {
2952 g_warning("path doesn't match: %s, %s\n", oldpath, item->path);
2956 base = item->path + oldpathlen;
2957 while (*base == G_DIR_SEPARATOR) base++;
2959 new_itempath = g_strdup(newpath);
2961 new_itempath = g_strconcat(newpath, G_DIR_SEPARATOR_S, base,
2964 item->path = new_itempath;
2969 typedef struct _get_list_uid_data {
2971 IMAPSession *session;
2972 IMAPFolderItem *item;
2973 GSList **msgnum_list;
2975 } get_list_uid_data;
2977 static void *get_list_of_uids_thread(void *data)
2979 get_list_uid_data *stuff = (get_list_uid_data *)data;
2980 Folder *folder = stuff->folder;
2981 IMAPFolderItem *item = stuff->item;
2982 GSList **msgnum_list = stuff->msgnum_list;
2983 gint ok, nummsgs = 0, lastuid_old;
2984 IMAPSession *session;
2985 GSList *uidlist, *elem;
2986 struct mailimap_set * set;
2987 clist * lep_uidlist;
2990 session = stuff->session;
2991 if (session == NULL) {
2993 return GINT_TO_POINTER(-1);
2995 /* no session locking here, it's already locked by caller */
2996 ok = imap_select(session, IMAP_FOLDER(folder), item->item.path,
2997 NULL, NULL, NULL, NULL, TRUE);
2998 if (ok != IMAP_SUCCESS) {
3000 return GINT_TO_POINTER(-1);
3005 set = mailimap_set_new_interval(item->lastuid + 1, 0);
3007 r = imap_threaded_search(folder, IMAP_SEARCH_TYPE_SIMPLE, set,
3009 if (r == MAILIMAP_NO_ERROR) {
3010 GSList * fetchuid_list;
3013 imap_uid_list_from_lep(lep_uidlist);
3014 uidlist = g_slist_concat(fetchuid_list, uidlist);
3017 GSList * fetchuid_list;
3018 carray * lep_uidtab;
3020 r = imap_threaded_fetch_uid(folder, item->lastuid + 1,
3022 if (r == MAILIMAP_NO_ERROR) {
3024 imap_uid_list_from_lep_tab(lep_uidtab);
3025 uidlist = g_slist_concat(fetchuid_list, uidlist);
3029 lastuid_old = item->lastuid;
3030 *msgnum_list = g_slist_copy(item->uid_list);
3031 nummsgs = g_slist_length(*msgnum_list);
3032 debug_print("Got %d uids from cache\n", g_slist_length(item->uid_list));
3034 for (elem = uidlist; elem != NULL; elem = g_slist_next(elem)) {
3037 msgnum = GPOINTER_TO_INT(elem->data);
3038 if (msgnum > lastuid_old) {
3039 *msgnum_list = g_slist_prepend(*msgnum_list, GINT_TO_POINTER(msgnum));
3040 item->uid_list = g_slist_prepend(item->uid_list, GINT_TO_POINTER(msgnum));
3043 if(msgnum > item->lastuid)
3044 item->lastuid = msgnum;
3047 g_slist_free(uidlist);
3049 return GINT_TO_POINTER(nummsgs);
3052 static gint get_list_of_uids(IMAPSession *session, Folder *folder, IMAPFolderItem *item, GSList **msgnum_list)
3055 get_list_uid_data *data = g_new0(get_list_uid_data, 1);
3057 data->folder = folder;
3059 data->msgnum_list = msgnum_list;
3060 data->session = session;
3061 if (prefs_common.work_offline && !inc_offline_should_override()) {
3066 result = GPOINTER_TO_INT(get_list_of_uids_thread(data));
3072 gint imap_get_num_list(Folder *folder, FolderItem *_item, GSList **msgnum_list, gboolean *old_uids_valid)
3074 IMAPFolderItem *item = (IMAPFolderItem *)_item;
3075 IMAPSession *session;
3076 gint ok, nummsgs = 0, exists, uid_val, uid_next;
3077 GSList *uidlist = NULL;
3079 gboolean selected_folder;
3081 debug_print("get_num_list\n");
3083 g_return_val_if_fail(folder != NULL, -1);
3084 g_return_val_if_fail(item != NULL, -1);
3085 g_return_val_if_fail(item->item.path != NULL, -1);
3086 g_return_val_if_fail(FOLDER_CLASS(folder) == &imap_class, -1);
3087 g_return_val_if_fail(folder->account != NULL, -1);
3089 session = imap_session_get(folder);
3090 g_return_val_if_fail(session != NULL, -1);
3092 statusbar_print_all("Scanning %s...\n", FOLDER_ITEM(item)->path
3093 ? FOLDER_ITEM(item)->path:"");
3095 selected_folder = (session->mbox != NULL) &&
3096 (!strcmp(session->mbox, item->item.path));
3097 if (selected_folder) {
3098 ok = imap_cmd_noop(session);
3099 if (ok != IMAP_SUCCESS) {
3100 debug_print("disconnected!\n");
3101 session = imap_reconnect_if_possible(folder, session);
3102 if (session == NULL) {
3103 statusbar_pop_all();
3108 exists = session->exists;
3110 *old_uids_valid = TRUE;
3112 if (item->use_cache && time(NULL) - item->use_cache < 2) {
3113 exists = item->c_messages;
3114 uid_next = item->c_uid_next;
3115 uid_val = item->c_uid_validity;
3117 debug_print("using cache %d %d %d\n", exists, uid_next, uid_val);
3119 ok = imap_status(session, IMAP_FOLDER(folder), item->item.path, item,
3120 &exists, &uid_next, &uid_val, NULL, FALSE);
3122 item->use_cache = (time_t)0;
3123 if (ok != IMAP_SUCCESS) {
3124 statusbar_pop_all();
3128 if(item->item.mtime == uid_val)
3129 *old_uids_valid = TRUE;
3131 *old_uids_valid = FALSE;
3133 debug_print("Freeing imap uid cache\n");
3135 g_slist_free(item->uid_list);
3136 item->uid_list = NULL;
3138 item->item.mtime = uid_val;
3140 imap_delete_all_cached_messages((FolderItem *)item);
3144 if (!selected_folder)
3145 item->uid_next = uid_next;
3147 /* If old uid_next matches new uid_next we can be sure no message
3148 was added to the folder */
3149 if (( selected_folder && !session->folder_content_changed) ||
3150 (!selected_folder && uid_next == item->uid_next)) {
3151 nummsgs = g_slist_length(item->uid_list);
3153 /* If number of messages is still the same we
3154 know our caches message numbers are still valid,
3155 otherwise if the number of messages has decrease
3156 we discard our cache to start a new scan to find
3157 out which numbers have been removed */
3158 if (exists == nummsgs) {
3159 *msgnum_list = g_slist_copy(item->uid_list);
3160 statusbar_pop_all();
3163 } else if (exists < nummsgs) {
3164 debug_print("Freeing imap uid cache");
3166 g_slist_free(item->uid_list);
3167 item->uid_list = NULL;
3172 *msgnum_list = NULL;
3173 statusbar_pop_all();
3178 nummsgs = get_list_of_uids(session, folder, item, &uidlist);
3181 statusbar_pop_all();
3186 if (nummsgs != exists) {
3187 /* Cache contains more messages then folder, we have cached
3188 an old UID of a message that was removed and new messages
3189 have been added too, otherwise the uid_next check would
3191 debug_print("Freeing imap uid cache");
3193 g_slist_free(item->uid_list);
3194 item->uid_list = NULL;
3196 g_slist_free(*msgnum_list);
3198 nummsgs = get_list_of_uids(session, folder, item, &uidlist);
3201 *msgnum_list = uidlist;
3203 dir = folder_item_get_path((FolderItem *)item);
3204 debug_print("removing old messages from %s\n", dir);
3205 remove_numbered_files_not_in_list(dir, *msgnum_list);
3208 debug_print("get_num_list - ok - %i\n", nummsgs);
3209 statusbar_pop_all();
3214 static MsgInfo *imap_parse_msg(const gchar *file, FolderItem *item)
3219 flags.perm_flags = MSG_NEW|MSG_UNREAD;
3220 flags.tmp_flags = 0;
3222 g_return_val_if_fail(item != NULL, NULL);
3223 g_return_val_if_fail(file != NULL, NULL);
3225 if (folder_has_parent_of_type(item, F_QUEUE)) {
3226 MSG_SET_TMP_FLAGS(flags, MSG_QUEUED);
3227 } else if (folder_has_parent_of_type(item, F_DRAFT)) {
3228 MSG_SET_TMP_FLAGS(flags, MSG_DRAFT);
3231 msginfo = procheader_parse_file(file, flags, FALSE, FALSE);
3232 if (!msginfo) return NULL;
3234 msginfo->plaintext_file = g_strdup(file);
3235 msginfo->folder = item;
3240 GSList *imap_get_msginfos(Folder *folder, FolderItem *item,
3241 GSList *msgnum_list)
3243 IMAPSession *session;
3244 MsgInfoList *ret = NULL;
3247 debug_print("get_msginfos\n");
3249 g_return_val_if_fail(folder != NULL, NULL);
3250 g_return_val_if_fail(item != NULL, NULL);
3251 g_return_val_if_fail(msgnum_list != NULL, NULL);
3253 session = imap_session_get(folder);
3254 g_return_val_if_fail(session != NULL, NULL);
3256 debug_print("IMAP getting msginfos\n");
3257 ok = imap_select(session, IMAP_FOLDER(folder), item->path,
3258 NULL, NULL, NULL, NULL, FALSE);
3259 if (ok != IMAP_SUCCESS) {
3263 if (!(folder_has_parent_of_type(item, F_DRAFT) ||
3264 folder_has_parent_of_type(item, F_QUEUE))) {
3265 ret = g_slist_concat(ret,
3266 imap_get_uncached_messages(session, item,
3269 MsgNumberList *sorted_list, *elem;
3270 gint startnum, lastnum;
3272 sorted_list = g_slist_sort(g_slist_copy(msgnum_list), g_int_compare);
3274 startnum = lastnum = GPOINTER_TO_INT(sorted_list->data);
3276 for (elem = sorted_list;; elem = g_slist_next(elem)) {
3280 num = GPOINTER_TO_INT(elem->data);
3282 if (num > lastnum + 1 || elem == NULL) {
3284 for (i = startnum; i <= lastnum; ++i) {
3287 file = imap_fetch_msg(folder, item, i);
3289 MsgInfo *msginfo = imap_parse_msg(file, item);
3290 if (msginfo != NULL) {
3291 msginfo->msgnum = i;
3292 ret = g_slist_append(ret, msginfo);
3306 g_slist_free(sorted_list);
3312 MsgInfo *imap_get_msginfo(Folder *folder, FolderItem *item, gint uid)
3314 MsgInfo *msginfo = NULL;
3315 MsgInfoList *msginfolist;
3316 MsgNumberList numlist;
3318 numlist.next = NULL;
3319 numlist.data = GINT_TO_POINTER(uid);
3321 msginfolist = imap_get_msginfos(folder, item, &numlist);
3322 if (msginfolist != NULL) {
3323 msginfo = msginfolist->data;
3324 g_slist_free(msginfolist);
3330 gboolean imap_scan_required(Folder *folder, FolderItem *_item)
3332 IMAPSession *session;
3333 IMAPFolderItem *item = (IMAPFolderItem *)_item;
3334 gint ok, exists = 0, unseen = 0;
3335 guint32 uid_next, uid_val;
3336 gboolean selected_folder;
3338 g_return_val_if_fail(folder != NULL, FALSE);
3339 g_return_val_if_fail(item != NULL, FALSE);
3340 g_return_val_if_fail(item->item.folder != NULL, FALSE);
3341 g_return_val_if_fail(FOLDER_CLASS(item->item.folder) == &imap_class, FALSE);
3343 if (item->item.path == NULL)
3346 session = imap_session_get(folder);
3347 g_return_val_if_fail(session != NULL, FALSE);
3349 selected_folder = (session->mbox != NULL) &&
3350 (!strcmp(session->mbox, item->item.path));
3351 if (selected_folder) {
3352 ok = imap_cmd_noop(session);
3353 if (ok != IMAP_SUCCESS) {
3354 debug_print("disconnected!\n");
3355 session = imap_reconnect_if_possible(folder, session);
3356 if (session == NULL)
3361 if (session->folder_content_changed
3362 || session->exists != item->item.total_msgs) {
3367 ok = imap_status(session, IMAP_FOLDER(folder), item->item.path, IMAP_FOLDER_ITEM(item),
3368 &exists, &uid_next, &uid_val, &unseen, FALSE);
3369 if (ok != IMAP_SUCCESS) {
3374 item->use_cache = time(NULL);
3375 item->c_messages = exists;
3376 item->c_uid_next = uid_next;
3377 item->c_uid_validity = uid_val;
3378 item->c_unseen = unseen;
3380 if ((uid_next != item->uid_next) || (exists != item->item.total_msgs)) {
3389 void imap_change_flags(Folder *folder, FolderItem *item, MsgInfo *msginfo, MsgPermFlags newflags)
3391 IMAPSession *session;
3392 IMAPFlags flags_set = 0, flags_unset = 0;
3393 gint ok = IMAP_SUCCESS;
3394 MsgNumberList numlist;
3395 hashtable_data *ht_data = NULL;
3397 g_return_if_fail(folder != NULL);
3398 g_return_if_fail(folder->klass == &imap_class);
3399 g_return_if_fail(item != NULL);
3400 g_return_if_fail(item->folder == folder);
3401 g_return_if_fail(msginfo != NULL);
3402 g_return_if_fail(msginfo->folder == item);
3404 session = imap_session_get(folder);
3409 if ((ok = imap_select(session, IMAP_FOLDER(folder), msginfo->folder->path,
3410 NULL, NULL, NULL, NULL, FALSE)) != IMAP_SUCCESS) {
3415 if (!MSG_IS_MARKED(msginfo->flags) && (newflags & MSG_MARKED))
3416 flags_set |= IMAP_FLAG_FLAGGED;
3417 if ( MSG_IS_MARKED(msginfo->flags) && !(newflags & MSG_MARKED))
3418 flags_unset |= IMAP_FLAG_FLAGGED;
3420 if (!MSG_IS_UNREAD(msginfo->flags) && (newflags & MSG_UNREAD))
3421 flags_unset |= IMAP_FLAG_SEEN;
3422 if ( MSG_IS_UNREAD(msginfo->flags) && !(newflags & MSG_UNREAD))
3423 flags_set |= IMAP_FLAG_SEEN;
3425 if (!MSG_IS_REPLIED(msginfo->flags) && (newflags & MSG_REPLIED))
3426 flags_set |= IMAP_FLAG_ANSWERED;
3427 if ( MSG_IS_REPLIED(msginfo->flags) && !(newflags & MSG_REPLIED))
3428 flags_unset |= IMAP_FLAG_ANSWERED;
3430 if (!MSG_IS_DELETED(msginfo->flags) && (newflags & MSG_DELETED))
3431 flags_set |= IMAP_FLAG_DELETED;
3432 if ( MSG_IS_DELETED(msginfo->flags) && !(newflags & MSG_DELETED))
3433 flags_unset |= IMAP_FLAG_DELETED;
3435 numlist.next = NULL;
3436 numlist.data = GINT_TO_POINTER(msginfo->msgnum);
3438 if (IMAP_FOLDER_ITEM(item)->batching) {
3439 /* instead of performing an UID STORE command for each message change,
3440 * as a lot of them can change "together", we just fill in hashtables
3441 * and defer the treatment so that we're able to send only one
3444 debug_print("IMAP batch mode on, deferring flags change\n");
3446 ht_data = g_hash_table_lookup(IMAP_FOLDER_ITEM(item)->flags_set_table,
3447 GINT_TO_POINTER(flags_set));
3448 if (ht_data == NULL) {
3449 ht_data = g_new0(hashtable_data, 1);
3450 ht_data->session = session;
3451 ht_data->item = IMAP_FOLDER_ITEM(item);
3452 g_hash_table_insert(IMAP_FOLDER_ITEM(item)->flags_set_table,
3453 GINT_TO_POINTER(flags_set), ht_data);
3455 if (!g_slist_find(ht_data->msglist, GINT_TO_POINTER(msginfo->msgnum)))
3456 ht_data->msglist = g_slist_prepend(ht_data->msglist, GINT_TO_POINTER(msginfo->msgnum));
3459 ht_data = g_hash_table_lookup(IMAP_FOLDER_ITEM(item)->flags_unset_table,
3460 GINT_TO_POINTER(flags_unset));
3461 if (ht_data == NULL) {
3462 ht_data = g_new0(hashtable_data, 1);
3463 ht_data->session = session;
3464 ht_data->item = IMAP_FOLDER_ITEM(item);
3465 g_hash_table_insert(IMAP_FOLDER_ITEM(item)->flags_unset_table,
3466 GINT_TO_POINTER(flags_unset), ht_data);
3468 if (!g_slist_find(ht_data->msglist, GINT_TO_POINTER(msginfo->msgnum)))
3469 ht_data->msglist = g_slist_prepend(ht_data->msglist,
3470 GINT_TO_POINTER(msginfo->msgnum));
3473 debug_print("IMAP changing flags\n");
3475 ok = imap_set_message_flags(session, &numlist, flags_set, TRUE);
3476 if (ok != IMAP_SUCCESS) {
3483 ok = imap_set_message_flags(session, &numlist, flags_unset, FALSE);
3484 if (ok != IMAP_SUCCESS) {
3490 msginfo->flags.perm_flags = newflags;
3495 static gint imap_remove_msg(Folder *folder, FolderItem *item, gint uid)
3498 IMAPSession *session;
3500 MsgNumberList numlist;
3502 g_return_val_if_fail(folder != NULL, -1);
3503 g_return_val_if_fail(FOLDER_CLASS(folder) == &imap_class, -1);
3504 g_return_val_if_fail(item != NULL, -1);
3506 session = imap_session_get(folder);
3507 if (!session) return -1;
3509 ok = imap_select(session, IMAP_FOLDER(folder), item->path,
3510 NULL, NULL, NULL, NULL, FALSE);
3511 if (ok != IMAP_SUCCESS) {
3515 numlist.next = NULL;
3516 numlist.data = GINT_TO_POINTER(uid);
3518 ok = imap_set_message_flags
3519 (IMAP_SESSION(REMOTE_FOLDER(folder)->session),
3520 &numlist, IMAP_FLAG_DELETED, TRUE);
3521 if (ok != IMAP_SUCCESS) {
3522 log_warning(_("can't set deleted flags: %d\n"), uid);
3527 if (!session->uidplus) {
3528 ok = imap_cmd_expunge(session);
3532 uidstr = g_strdup_printf("%u", uid);
3533 ok = imap_cmd_expunge(session);
3536 if (ok != IMAP_SUCCESS) {
3537 log_warning(_("can't expunge\n"));
3542 IMAP_FOLDER_ITEM(item)->uid_list = g_slist_remove(
3543 IMAP_FOLDER_ITEM(item)->uid_list, numlist.data);
3544 dir = folder_item_get_path(item);
3545 if (is_dir_exist(dir))
3546 remove_numbered_files(dir, uid, uid);
3549 return IMAP_SUCCESS;
3552 static gint compare_msginfo(gconstpointer a, gconstpointer b)
3554 return ((MsgInfo *)a)->msgnum - ((MsgInfo *)b)->msgnum;
3557 static guint gslist_find_next_num(MsgNumberList **list, guint num)
3561 g_return_val_if_fail(list != NULL, -1);
3563 for (elem = *list; elem != NULL; elem = g_slist_next(elem))
3564 if (GPOINTER_TO_INT(elem->data) >= num)
3567 return elem != NULL ? GPOINTER_TO_INT(elem->data) : (gint)-1;
3571 * NEW and DELETED flags are not syncronized
3572 * - The NEW/RECENT flags in IMAP folders can not really be directly
3573 * modified by Sylpheed
3574 * - The DELETE/DELETED flag in IMAP and Sylpheed don't have the same
3575 * meaning, in IMAP it always removes the messages from the FolderItem
3576 * in Sylpheed it can mean to move the message to trash
3579 typedef struct _get_flags_data {
3582 MsgInfoList *msginfo_list;
3583 GRelation *msgflags;
3584 gboolean full_search;
3588 static /*gint*/ void *imap_get_flags_thread(void *data)
3590 get_flags_data *stuff = (get_flags_data *)data;
3591 Folder *folder = stuff->folder;
3592 FolderItem *item = stuff->item;
3593 MsgInfoList *msginfo_list = stuff->msginfo_list;
3594 GRelation *msgflags = stuff->msgflags;
3595 gboolean full_search = stuff->full_search;
3596 IMAPSession *session;
3597 GSList *sorted_list = NULL;
3598 GSList *unseen = NULL, *answered = NULL, *flagged = NULL, *deleted = NULL;
3599 GSList *p_unseen, *p_answered, *p_flagged, *p_deleted;
3601 GSList *seq_list, *cur;
3602 gboolean reverse_seen = FALSE;
3605 gint exists_cnt, unseen_cnt;
3606 gboolean selected_folder;
3608 if (folder == NULL || item == NULL) {
3610 return GINT_TO_POINTER(-1);
3613 session = imap_session_get(folder);
3614 if (session == NULL) {
3616 return GINT_TO_POINTER(-1);
3619 selected_folder = (session->mbox != NULL) &&
3620 (!strcmp(session->mbox, item->path));
3622 if (!selected_folder) {
3623 ok = imap_select(session, IMAP_FOLDER(folder), item->path,
3624 &exists_cnt, NULL, &unseen_cnt, NULL, TRUE);
3625 if (ok != IMAP_SUCCESS) {
3628 return GINT_TO_POINTER(-1);
3631 if (unseen_cnt > exists_cnt / 2)
3632 reverse_seen = TRUE;
3635 if (item->unread_msgs > item->total_msgs / 2)
3636 reverse_seen = TRUE;
3639 cmd_buf = g_string_new(NULL);
3641 sorted_list = g_slist_sort(g_slist_copy(msginfo_list), compare_msginfo);
3643 seq_list = imap_get_lep_set_from_msglist(msginfo_list);
3645 struct mailimap_set * set;
3646 set = mailimap_set_new_interval(1, 0);
3647 seq_list = g_slist_append(NULL, set);
3650 for (cur = seq_list; cur != NULL; cur = g_slist_next(cur)) {
3651 struct mailimap_set * imapset;
3652 clist * lep_uidlist;
3655 imapset = cur->data;
3657 r = imap_threaded_search(folder, IMAP_SEARCH_TYPE_SEEN,
3658 imapset, &lep_uidlist);
3661 r = imap_threaded_search(folder,
3662 IMAP_SEARCH_TYPE_UNSEEN,
3663 imapset, &lep_uidlist);
3665 if (r == MAILIMAP_NO_ERROR) {
3668 uidlist = imap_uid_list_from_lep(lep_uidlist);
3669 mailimap_search_result_free(lep_uidlist);
3671 unseen = g_slist_concat(unseen, uidlist);
3674 r = imap_threaded_search(folder, IMAP_SEARCH_TYPE_ANSWERED,
3675 imapset, &lep_uidlist);
3676 if (r == MAILIMAP_NO_ERROR) {
3679 uidlist = imap_uid_list_from_lep(lep_uidlist);
3680 mailimap_search_result_free(lep_uidlist);
3682 answered = g_slist_concat(answered, uidlist);
3685 r = imap_threaded_search(folder, IMAP_SEARCH_TYPE_FLAGGED,
3686 imapset, &lep_uidlist);
3687 if (r == MAILIMAP_NO_ERROR) {
3690 uidlist = imap_uid_list_from_lep(lep_uidlist);
3691 mailimap_search_result_free(lep_uidlist);
3693 flagged = g_slist_concat(flagged, uidlist);
3696 r = imap_threaded_search(folder, IMAP_SEARCH_TYPE_DELETED,
3697 imapset, &lep_uidlist);
3698 if (r == MAILIMAP_NO_ERROR) {
3701 uidlist = imap_uid_list_from_lep(lep_uidlist);
3702 mailimap_search_result_free(lep_uidlist);
3704 deleted = g_slist_concat(deleted, uidlist);
3709 p_answered = answered;
3710 p_flagged = flagged;
3711 p_deleted = deleted;
3713 for (elem = sorted_list; elem != NULL; elem = g_slist_next(elem)) {
3718 msginfo = (MsgInfo *) elem->data;
3719 flags = msginfo->flags.perm_flags;
3720 wasnew = (flags & MSG_NEW);
3721 flags &= ~((reverse_seen ? 0 : MSG_UNREAD | MSG_NEW) | MSG_REPLIED | MSG_MARKED);
3723 flags |= MSG_UNREAD | (wasnew ? MSG_NEW : 0);
3724 if (gslist_find_next_num(&p_unseen, msginfo->msgnum) == msginfo->msgnum) {
3725 if (!reverse_seen) {
3726 flags |= MSG_UNREAD | (wasnew ? MSG_NEW : 0);
3728 flags &= ~(MSG_UNREAD | MSG_NEW);
3731 if (gslist_find_next_num(&p_answered, msginfo->msgnum) == msginfo->msgnum)
3732 flags |= MSG_REPLIED;
3734 flags &= ~MSG_REPLIED;
3735 if (gslist_find_next_num(&p_flagged, msginfo->msgnum) == msginfo->msgnum)
3736 flags |= MSG_MARKED;
3738 flags &= ~MSG_MARKED;
3739 if (gslist_find_next_num(&p_deleted, msginfo->msgnum) == msginfo->msgnum)
3740 flags |= MSG_DELETED;
3742 flags &= ~MSG_DELETED;
3743 g_relation_insert(msgflags, msginfo, GINT_TO_POINTER(flags));
3746 imap_lep_set_free(seq_list);
3747 g_slist_free(flagged);
3748 g_slist_free(deleted);
3749 g_slist_free(answered);
3750 g_slist_free(unseen);
3751 g_slist_free(sorted_list);
3752 g_string_free(cmd_buf, TRUE);
3756 return GINT_TO_POINTER(0);
3759 static gint imap_get_flags(Folder *folder, FolderItem *item,
3760 MsgInfoList *msginfo_list, GRelation *msgflags)
3763 get_flags_data *data = g_new0(get_flags_data, 1);
3765 data->folder = folder;
3767 data->msginfo_list = msginfo_list;
3768 data->msgflags = msgflags;
3769 data->full_search = FALSE;
3771 GSList *tmp = NULL, *cur;
3773 if (prefs_common.work_offline && !inc_offline_should_override()) {
3778 tmp = folder_item_get_msg_list(item);
3780 if (g_slist_length(tmp) == g_slist_length(msginfo_list))
3781 data->full_search = TRUE;
3783 for (cur = tmp; cur; cur = cur->next)
3784 procmsg_msginfo_free((MsgInfo *)cur->data);
3788 result = GPOINTER_TO_INT(imap_get_flags_thread(data));
3795 static gboolean process_flags(gpointer key, gpointer value, gpointer user_data)
3797 gboolean flags_set = GPOINTER_TO_INT(user_data);
3798 gint flags_value = GPOINTER_TO_INT(key);
3799 hashtable_data *data = (hashtable_data *)value;
3800 IMAPFolderItem *_item = data->item;
3801 FolderItem *item = (FolderItem *)_item;
3802 gint ok = IMAP_ERROR;
3803 IMAPSession *session = imap_session_get(item->folder);
3805 data->msglist = g_slist_reverse(data->msglist);
3807 debug_print("IMAP %ssetting flags to %d for %d messages\n",
3810 g_slist_length(data->msglist));
3814 ok = imap_select(session, IMAP_FOLDER(item->folder), item->path,
3815 NULL, NULL, NULL, NULL, FALSE);
3817 if (ok == IMAP_SUCCESS) {
3818 imap_set_message_flags(data->session, data->msglist, flags_value, flags_set);
3820 g_warning("can't select mailbox %s\n", item->path);
3824 g_slist_free(data->msglist);
3829 static void process_hashtable(IMAPFolderItem *item)
3831 if (item->flags_set_table) {
3832 g_hash_table_foreach_remove(item->flags_set_table, process_flags, GINT_TO_POINTER(TRUE));
3833 g_free(item->flags_set_table);
3834 item->flags_set_table = NULL;
3836 if (item->flags_unset_table) {
3837 g_hash_table_foreach_remove(item->flags_unset_table, process_flags, GINT_TO_POINTER(FALSE));
3838 g_free(item->flags_unset_table);
3839 item->flags_unset_table = NULL;
3843 static void imap_set_batch (Folder *folder, FolderItem *_item, gboolean batch)
3845 IMAPFolderItem *item = (IMAPFolderItem *)_item;
3847 g_return_if_fail(item != NULL);
3849 if (item->batching == batch)
3853 item->batching = TRUE;
3854 debug_print("IMAP switching to batch mode\n");
3855 if (!item->flags_set_table) {
3856 item->flags_set_table = g_hash_table_new(NULL, g_direct_equal);
3858 if (!item->flags_unset_table) {
3859 item->flags_unset_table = g_hash_table_new(NULL, g_direct_equal);
3862 debug_print("IMAP switching away from batch mode\n");
3864 process_hashtable(item);
3865 item->batching = FALSE;
3871 /* data types conversion libetpan <-> sylpheed */
3875 #define ETPAN_IMAP_MB_MARKED 1
3876 #define ETPAN_IMAP_MB_UNMARKED 2
3877 #define ETPAN_IMAP_MB_NOSELECT 4
3878 #define ETPAN_IMAP_MB_NOINFERIORS 8
3880 static int imap_flags_to_flags(struct mailimap_mbx_list_flags * imap_flags)
3886 if (imap_flags->mbf_type == MAILIMAP_MBX_LIST_FLAGS_SFLAG) {
3887 switch (imap_flags->mbf_sflag) {
3888 case MAILIMAP_MBX_LIST_SFLAG_MARKED:
3889 flags |= ETPAN_IMAP_MB_MARKED;
3891 case MAILIMAP_MBX_LIST_SFLAG_NOSELECT:
3892 flags |= ETPAN_IMAP_MB_NOSELECT;
3894 case MAILIMAP_MBX_LIST_SFLAG_UNMARKED:
3895 flags |= ETPAN_IMAP_MB_UNMARKED;
3900 for(cur = clist_begin(imap_flags->mbf_oflags) ; cur != NULL ;
3901 cur = clist_next(cur)) {
3902 struct mailimap_mbx_list_oflag * oflag;
3904 oflag = clist_content(cur);
3906 switch (oflag->of_type) {
3907 case MAILIMAP_MBX_LIST_OFLAG_NOINFERIORS:
3908 flags |= ETPAN_IMAP_MB_NOINFERIORS;
3916 static GSList * imap_list_from_lep(IMAPFolder * folder,
3917 clist * list, const gchar * real_path, gboolean all)
3924 for(iter = clist_begin(list) ; iter != NULL ;
3925 iter = clist_next(iter)) {
3926 struct mailimap_mailbox_list * mb;
3934 FolderItem *new_item;
3936 mb = clist_content(iter);
3942 if (mb->mb_flag != NULL)
3943 flags = imap_flags_to_flags(mb->mb_flag);
3945 delimiter = mb->mb_delimiter;
3948 dup_name = strdup(name);
3949 if (delimiter != '\0')
3950 subst_char(dup_name, delimiter, '/');
3952 base = g_path_get_basename(dup_name);
3953 if (base[0] == '.') {
3959 if (!all && strcmp(dup_name, real_path) == 0) {
3965 if (!all && dup_name[strlen(dup_name)-1] == '/') {
3971 loc_name = imap_modified_utf7_to_utf8(base);
3972 loc_path = imap_modified_utf7_to_utf8(dup_name);
3974 new_item = folder_item_new(FOLDER(folder), loc_name, loc_path);
3975 if ((flags & ETPAN_IMAP_MB_NOINFERIORS) != 0)
3976 new_item->no_sub = TRUE;
3977 if (strcmp(dup_name, "INBOX") != 0 &&
3978 ((flags & ETPAN_IMAP_MB_NOSELECT) != 0))
3979 new_item->no_select = TRUE;
3981 item_list = g_slist_append(item_list, new_item);
3983 debug_print("folder '%s' found.\n", loc_path);
3994 static GSList * imap_get_lep_set_from_numlist(MsgNumberList *numlist)
3996 GSList *sorted_list, *cur;
3997 guint first, last, next;
3998 GSList *ret_list = NULL;
4000 struct mailimap_set * current_set;
4001 unsigned int item_count;
4003 if (numlist == NULL)
4007 current_set = mailimap_set_new_empty();
4009 sorted_list = g_slist_copy(numlist);
4010 sorted_list = g_slist_sort(sorted_list, g_int_compare);
4012 first = GPOINTER_TO_INT(sorted_list->data);
4015 for (cur = sorted_list; cur != NULL; cur = g_slist_next(cur)) {
4016 if (GPOINTER_TO_INT(cur->data) == 0)
4021 last = GPOINTER_TO_INT(cur->data);
4023 next = GPOINTER_TO_INT(cur->next->data);
4027 if (last + 1 != next || next == 0) {
4029 struct mailimap_set_item * item;
4030 item = mailimap_set_item_new(first, last);
4031 mailimap_set_add(current_set, item);
4036 if (count >= IMAP_SET_MAX_COUNT) {
4037 ret_list = g_slist_append(ret_list,
4039 current_set = mailimap_set_new_empty();
4046 if (clist_count(current_set->set_list) > 0) {
4047 ret_list = g_slist_append(ret_list,
4051 g_slist_free(sorted_list);
4056 static GSList * imap_get_lep_set_from_msglist(MsgInfoList *msglist)
4058 MsgNumberList *numlist = NULL;
4062 for (cur = msglist; cur != NULL; cur = g_slist_next(cur)) {
4063 MsgInfo *msginfo = (MsgInfo *) cur->data;
4065 numlist = g_slist_append(numlist, GINT_TO_POINTER(msginfo->msgnum));
4067 seq_list = imap_get_lep_set_from_numlist(numlist);
4068 g_slist_free(numlist);
4073 static GSList * imap_uid_list_from_lep(clist * list)
4080 for(iter = clist_begin(list) ; iter != NULL ;
4081 iter = clist_next(iter)) {
4084 puid = clist_content(iter);
4085 result = g_slist_append(result, GINT_TO_POINTER(* puid));
4091 static GSList * imap_uid_list_from_lep_tab(carray * list)
4098 for(i = 0 ; i < carray_count(list) ; i ++) {
4101 puid = carray_get(list, i);
4102 result = g_slist_append(result, GINT_TO_POINTER(* puid));
4108 static MsgInfo *imap_envelope_from_lep(struct imap_fetch_env_info * info,
4111 MsgInfo *msginfo = NULL;
4114 MsgFlags flags = {0, 0};
4116 if (info->headers == NULL)
4119 MSG_SET_TMP_FLAGS(flags, MSG_IMAP);
4120 if (folder_has_parent_of_type(item, F_QUEUE)) {
4121 MSG_SET_TMP_FLAGS(flags, MSG_QUEUED);
4122 } else if (folder_has_parent_of_type(item, F_DRAFT)) {
4123 MSG_SET_TMP_FLAGS(flags, MSG_DRAFT);
4125 flags.perm_flags = info->flags;
4129 msginfo = procheader_parse_str(info->headers, flags, FALSE, FALSE);
4132 msginfo->msgnum = uid;
4133 msginfo->size = size;
4139 static void imap_lep_set_free(GSList *seq_list)
4143 for(cur = seq_list ; cur != NULL ; cur = g_slist_next(cur)) {
4144 struct mailimap_set * imapset;
4146 imapset = cur->data;
4147 mailimap_set_free(imapset);
4149 g_slist_free(seq_list);
4152 static struct mailimap_flag_list * imap_flag_to_lep(IMAPFlags flags)
4154 struct mailimap_flag_list * flag_list;
4156 flag_list = mailimap_flag_list_new_empty();
4158 if (IMAP_IS_SEEN(flags))
4159 mailimap_flag_list_add(flag_list,
4160 mailimap_flag_new_seen());
4161 if (IMAP_IS_ANSWERED(flags))
4162 mailimap_flag_list_add(flag_list,
4163 mailimap_flag_new_answered());
4164 if (IMAP_IS_FLAGGED(flags))
4165 mailimap_flag_list_add(flag_list,
4166 mailimap_flag_new_flagged());
4167 if (IMAP_IS_DELETED(flags))
4168 mailimap_flag_list_add(flag_list,
4169 mailimap_flag_new_deleted());
4170 if (IMAP_IS_DRAFT(flags))
4171 mailimap_flag_list_add(flag_list,
4172 mailimap_flag_new_draft());
4177 guint imap_folder_get_refcnt(Folder *folder)
4179 return ((IMAPFolder *)folder)->refcnt;
4182 void imap_folder_ref(Folder *folder)
4184 ((IMAPFolder *)folder)->refcnt++;
4187 void imap_folder_unref(Folder *folder)
4189 if (((IMAPFolder *)folder)->refcnt > 0)
4190 ((IMAPFolder *)folder)->refcnt--;
4193 #else /* HAVE_LIBETPAN */
4195 static FolderClass imap_class;
4197 static Folder *imap_folder_new (const gchar *name,
4202 static gint imap_create_tree (Folder *folder)
4206 static FolderItem *imap_create_folder (Folder *folder,
4212 static gint imap_rename_folder (Folder *folder,
4219 FolderClass *imap_get_class(void)
4221 if (imap_class.idstr == NULL) {
4222 imap_class.type = F_IMAP;
4223 imap_class.idstr = "imap";
4224 imap_class.uistr = "IMAP4";
4226 imap_class.new_folder = imap_folder_new;
4227 imap_class.create_tree = imap_create_tree;
4228 imap_class.create_folder = imap_create_folder;
4229 imap_class.rename_folder = imap_rename_folder;
4230 /* nothing implemented */
4237 void imap_synchronise(FolderItem *item)
4239 imap_gtk_synchronise(item);