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 if (strchr(name, imap_get_path_separator(IMAP_FOLDER(folder), item->path)) != NULL) {
1832 g_warning(_("New folder name must not contain the namespace "
1837 session = imap_session_get(folder);
1842 real_oldpath = imap_get_real_path(IMAP_FOLDER(folder), item->path);
1844 g_free(session->mbox);
1845 session->mbox = NULL;
1846 ok = imap_cmd_examine(session, "INBOX",
1847 &exists, &recent, &unseen, &uid_validity, FALSE);
1848 if (ok != IMAP_SUCCESS) {
1849 g_free(real_oldpath);
1854 separator = imap_get_path_separator(IMAP_FOLDER(folder), item->path);
1855 if (strchr(item->path, G_DIR_SEPARATOR)) {
1856 dirpath = g_path_get_dirname(item->path);
1857 newpath = g_strconcat(dirpath, G_DIR_SEPARATOR_S, name, NULL);
1860 newpath = g_strdup(name);
1862 real_newpath = imap_utf8_to_modified_utf7(newpath);
1863 imap_path_separator_subst(real_newpath, separator);
1865 ok = imap_cmd_rename(session, real_oldpath, real_newpath);
1866 if (ok != IMAP_SUCCESS) {
1867 log_warning(_("can't rename mailbox: %s to %s\n"),
1868 real_oldpath, real_newpath);
1869 g_free(real_oldpath);
1871 g_free(real_newpath);
1877 item->name = g_strdup(name);
1879 old_cache_dir = folder_item_get_path(item);
1881 paths[0] = g_strdup(item->path);
1883 g_node_traverse(item->node, G_PRE_ORDER, G_TRAVERSE_ALL, -1,
1884 imap_rename_folder_func, paths);
1886 if (is_dir_exist(old_cache_dir)) {
1887 new_cache_dir = folder_item_get_path(item);
1888 if (rename(old_cache_dir, new_cache_dir) < 0) {
1889 FILE_OP_ERROR(old_cache_dir, "rename");
1891 g_free(new_cache_dir);
1894 g_free(old_cache_dir);
1897 g_free(real_oldpath);
1898 g_free(real_newpath);
1903 static gint imap_remove_folder_real(Folder *folder, FolderItem *item)
1906 IMAPSession *session;
1909 gint exists, recent, unseen;
1910 guint32 uid_validity;
1912 g_return_val_if_fail(folder != NULL, -1);
1913 g_return_val_if_fail(item != NULL, -1);
1914 g_return_val_if_fail(item->path != NULL, -1);
1916 session = imap_session_get(folder);
1921 path = imap_get_real_path(IMAP_FOLDER(folder), item->path);
1923 ok = imap_cmd_examine(session, "INBOX",
1924 &exists, &recent, &unseen, &uid_validity, FALSE);
1925 if (ok != IMAP_SUCCESS) {
1931 ok = imap_cmd_delete(session, path);
1932 if (ok != IMAP_SUCCESS) {
1933 gchar *tmp = g_strdup_printf("%s%c", path,
1934 imap_get_path_separator(IMAP_FOLDER(folder), path));
1937 ok = imap_cmd_delete(session, path);
1940 if (ok != IMAP_SUCCESS) {
1941 log_warning(_("can't delete mailbox\n"));
1948 cache_dir = folder_item_get_path(item);
1949 if (is_dir_exist(cache_dir) && remove_dir_recursive(cache_dir) < 0)
1950 g_warning("can't remove directory '%s'\n", cache_dir);
1952 folder_item_remove(item);
1957 static gint imap_remove_folder(Folder *folder, FolderItem *item)
1961 g_return_val_if_fail(item != NULL, -1);
1962 g_return_val_if_fail(item->folder != NULL, -1);
1963 g_return_val_if_fail(item->node != NULL, -1);
1965 node = item->node->children;
1966 while (node != NULL) {
1968 if (imap_remove_folder(folder, FOLDER_ITEM(node->data)) < 0)
1972 debug_print("IMAP removing %s\n", item->path);
1974 if (imap_remove_all_msg(folder, item) < 0)
1976 return imap_remove_folder_real(folder, item);
1979 typedef struct _uncached_data {
1980 IMAPSession *session;
1982 MsgNumberList *numlist;
1988 static void *imap_get_uncached_messages_thread(void *data)
1990 uncached_data *stuff = (uncached_data *)data;
1991 IMAPSession *session = stuff->session;
1992 FolderItem *item = stuff->item;
1993 MsgNumberList *numlist = stuff->numlist;
1995 GSList *newlist = NULL;
1996 GSList *llast = NULL;
1997 GSList *seq_list, *cur;
1999 debug_print("uncached_messages\n");
2001 if (session == NULL || item == NULL || item->folder == NULL
2002 || FOLDER_CLASS(item->folder) != &imap_class) {
2007 seq_list = imap_get_lep_set_from_numlist(numlist);
2008 debug_print("get msgs info\n");
2009 for (cur = seq_list; cur != NULL; cur = g_slist_next(cur)) {
2010 struct mailimap_set * imapset;
2016 imapset = cur->data;
2018 r = imap_threaded_fetch_env(session->folder,
2019 imapset, &env_list);
2020 if (r != MAILIMAP_NO_ERROR)
2024 for(i = 0 ; i < carray_count(env_list) ; i ++) {
2025 struct imap_fetch_env_info * info;
2028 info = carray_get(env_list, i);
2029 msginfo = imap_envelope_from_lep(info, item);
2030 msginfo->folder = item;
2032 llast = newlist = g_slist_append(newlist, msginfo);
2034 llast = g_slist_append(llast, msginfo);
2035 llast = llast->next;
2040 imap_fetch_env_free(env_list);
2043 session_set_access_time(SESSION(session));
2048 #define MAX_MSG_NUM 50
2050 static GSList *imap_get_uncached_messages(IMAPSession *session,
2052 MsgNumberList *numlist)
2054 GSList *result = NULL;
2056 uncached_data *data = g_new0(uncached_data, 1);
2061 data->total = g_slist_length(numlist);
2062 debug_print("messages list : %i\n", data->total);
2064 while (cur != NULL) {
2065 GSList * partial_result;
2073 while (count < MAX_MSG_NUM) {
2078 if (newlist == NULL)
2079 llast = newlist = g_slist_append(newlist, p);
2081 llast = g_slist_append(llast, p);
2082 llast = llast->next;
2092 data->session = session;
2094 data->numlist = newlist;
2097 if (prefs_common.work_offline && !inc_offline_should_override()) {
2103 (GSList *)imap_get_uncached_messages_thread(data);
2105 statusbar_progress_all(data->cur,data->total, 1);
2107 g_slist_free(newlist);
2109 result = g_slist_concat(result, partial_result);
2113 statusbar_progress_all(0,0,0);
2114 statusbar_pop_all();
2119 static void imap_delete_all_cached_messages(FolderItem *item)
2123 g_return_if_fail(item != NULL);
2124 g_return_if_fail(item->folder != NULL);
2125 g_return_if_fail(FOLDER_CLASS(item->folder) == &imap_class);
2127 debug_print("Deleting all cached messages...\n");
2129 dir = folder_item_get_path(item);
2130 if (is_dir_exist(dir))
2131 remove_all_numbered_files(dir);
2134 debug_print("done.\n");
2137 static IMAPNameSpace *imap_find_namespace_from_list(GList *ns_list,
2140 IMAPNameSpace *namespace = NULL;
2141 gchar *tmp_path, *name;
2143 if (!path) path = "";
2145 for (; ns_list != NULL; ns_list = ns_list->next) {
2146 IMAPNameSpace *tmp_ns = ns_list->data;
2148 Xstrcat_a(tmp_path, path, "/", return namespace);
2149 Xstrdup_a(name, tmp_ns->name, return namespace);
2150 if (tmp_ns->separator && tmp_ns->separator != '/') {
2151 subst_char(tmp_path, tmp_ns->separator, '/');
2152 subst_char(name, tmp_ns->separator, '/');
2154 if (strncmp(tmp_path, name, strlen(name)) == 0)
2161 static IMAPNameSpace *imap_find_namespace(IMAPFolder *folder,
2164 IMAPNameSpace *namespace;
2166 g_return_val_if_fail(folder != NULL, NULL);
2168 namespace = imap_find_namespace_from_list(folder->ns_personal, path);
2169 if (namespace) return namespace;
2170 namespace = imap_find_namespace_from_list(folder->ns_others, path);
2171 if (namespace) return namespace;
2172 namespace = imap_find_namespace_from_list(folder->ns_shared, path);
2173 if (namespace) return namespace;
2179 static gchar imap_get_path_separator(IMAPFolder *folder, const gchar *path)
2181 IMAPNameSpace *namespace;
2182 gchar separator = '/';
2184 if (folder->last_seen_separator == 0) {
2186 int r = imap_threaded_list((Folder *)folder, "", "", &lep_list);
2187 if (r != MAILIMAP_NO_ERROR) {
2188 log_warning(_("LIST failed\n"));
2192 if (clist_count(lep_list) > 0) {
2193 clistiter * iter = clist_begin(lep_list);
2194 struct mailimap_mailbox_list * mb;
2195 mb = clist_content(iter);
2197 folder->last_seen_separator = mb->mb_delimiter;
2198 debug_print("got separator: %c\n", folder->last_seen_separator);
2200 mailimap_list_result_free(lep_list);
2203 if (folder->last_seen_separator != 0) {
2204 debug_print("using separator: %c\n", folder->last_seen_separator);
2205 return folder->last_seen_separator;
2208 namespace = imap_find_namespace(folder, path);
2209 if (namespace && namespace->separator)
2210 separator = namespace->separator;
2215 static gchar *imap_get_real_path(IMAPFolder *folder, const gchar *path)
2220 g_return_val_if_fail(folder != NULL, NULL);
2221 g_return_val_if_fail(path != NULL, NULL);
2223 real_path = imap_utf8_to_modified_utf7(path);
2224 separator = imap_get_path_separator(folder, path);
2225 imap_path_separator_subst(real_path, separator);
2230 static gint imap_set_message_flags(IMAPSession *session,
2231 MsgNumberList *numlist,
2239 seq_list = imap_get_lep_set_from_numlist(numlist);
2241 for(cur = seq_list ; cur != NULL ; cur = g_slist_next(cur)) {
2242 struct mailimap_set * imapset;
2244 imapset = cur->data;
2246 ok = imap_cmd_store(session, imapset,
2250 imap_lep_set_free(seq_list);
2252 return IMAP_SUCCESS;
2255 typedef struct _select_data {
2256 IMAPSession *session;
2261 guint32 *uid_validity;
2265 static gint imap_select(IMAPSession *session, IMAPFolder *folder,
2267 gint *exists, gint *recent, gint *unseen,
2268 guint32 *uid_validity, gboolean block)
2272 gint exists_, recent_, unseen_;
2273 guint32 uid_validity_;
2275 if (!exists && !recent && !unseen && !uid_validity) {
2276 if (session->mbox && strcmp(session->mbox, path) == 0)
2277 return IMAP_SUCCESS;
2286 uid_validity = &uid_validity_;
2288 g_free(session->mbox);
2289 session->mbox = NULL;
2291 real_path = imap_get_real_path(folder, path);
2293 ok = imap_cmd_select(session, real_path,
2294 exists, recent, unseen, uid_validity, block);
2295 if (ok != IMAP_SUCCESS)
2296 log_warning(_("can't select folder: %s\n"), real_path);
2298 session->mbox = g_strdup(path);
2299 session->folder_content_changed = FALSE;
2306 static gint imap_status(IMAPSession *session, IMAPFolder *folder,
2307 const gchar *path, IMAPFolderItem *item,
2309 guint32 *uid_next, guint32 *uid_validity,
2310 gint *unseen, gboolean block)
2314 struct mailimap_mailbox_data_status * data_status;
2319 real_path = imap_get_real_path(folder, path);
2322 if (time(NULL) - item->last_update >= 5 && item->last_update != 1) {
2323 /* do the full stuff */
2324 item->last_update = 1; /* force update */
2325 debug_print("updating everything\n");
2326 r = imap_status(session, folder, path, item,
2327 &item->c_messages, &item->c_uid_next,
2328 &item->c_uid_validity, &item->c_unseen, block);
2329 if (r != MAILIMAP_NO_ERROR) {
2330 debug_print("status err %d\n", r);
2333 item->last_update = time(NULL);
2335 *messages = item->c_messages;
2337 *uid_next = item->c_uid_next;
2339 *uid_validity = item->c_uid_validity;
2341 *unseen = item->c_unseen;
2343 } else if (time(NULL) - item->last_update < 5) {
2344 /* return cached stuff */
2345 debug_print("using cache\n");
2347 *messages = item->c_messages;
2349 *uid_next = item->c_uid_next;
2351 *uid_validity = item->c_uid_validity;
2353 *unseen = item->c_unseen;
2358 /* if we get there, we're updating cache */
2372 r = imap_threaded_status(FOLDER(folder), real_path,
2373 &data_status, mask);
2376 if (r != MAILIMAP_NO_ERROR) {
2377 debug_print("status err %d\n", r);
2381 if (data_status->st_info_list == NULL) {
2382 mailimap_mailbox_data_status_free(data_status);
2383 debug_print("status->st_info_list == NULL\n");
2388 for(iter = clist_begin(data_status->st_info_list) ; iter != NULL ;
2389 iter = clist_next(iter)) {
2390 struct mailimap_status_info * info;
2392 info = clist_content(iter);
2393 switch (info->st_att) {
2394 case MAILIMAP_STATUS_ATT_MESSAGES:
2395 * messages = info->st_value;
2396 got_values |= 1 << 0;
2399 case MAILIMAP_STATUS_ATT_UIDNEXT:
2400 * uid_next = info->st_value;
2401 got_values |= 1 << 2;
2404 case MAILIMAP_STATUS_ATT_UIDVALIDITY:
2405 * uid_validity = info->st_value;
2406 got_values |= 1 << 3;
2409 case MAILIMAP_STATUS_ATT_UNSEEN:
2410 * unseen = info->st_value;
2411 got_values |= 1 << 4;
2415 mailimap_mailbox_data_status_free(data_status);
2417 if (got_values != mask) {
2418 debug_print("status: incomplete values received (%d)\n", got_values);
2421 return IMAP_SUCCESS;
2424 static void imap_free_capabilities(IMAPSession *session)
2426 slist_free_strings(session->capability);
2427 g_slist_free(session->capability);
2428 session->capability = NULL;
2431 /* low-level IMAP4rev1 commands */
2434 static gint imap_cmd_authenticate(IMAPSession *session, const gchar *user,
2435 const gchar *pass, IMAPAuthType type)
2442 gchar hexdigest[33];
2446 auth_type = "CRAM-MD5";
2448 imap_gen_send(session, "AUTHENTICATE %s", auth_type);
2449 ok = imap_gen_recv(session, &buf);
2450 if (ok != IMAP_SUCCESS || buf[0] != '+' || buf[1] != ' ') {
2455 challenge = g_malloc(strlen(buf + 2) + 1);
2456 challenge_len = base64_decode(challenge, buf + 2, -1);
2457 challenge[challenge_len] = '\0';
2460 md5_hex_hmac(hexdigest, challenge, challenge_len, pass, strlen(pass));
2463 response = g_strdup_printf("%s %s", user, hexdigest);
2464 response64 = g_malloc((strlen(response) + 3) * 2 + 1);
2465 base64_encode(response64, response, strlen(response));
2468 sock_puts(SESSION(session)->sock, response64);
2469 ok = imap_cmd_ok(session, NULL);
2470 if (ok != IMAP_SUCCESS)
2471 log_warning(_("IMAP4 authentication failed.\n"));
2477 static gint imap_cmd_login(IMAPSession *session,
2478 const gchar *user, const gchar *pass,
2484 log_print("IMAP4> Logging %s to %s using %s\n",
2486 SESSION(session)->server,
2488 r = imap_threaded_login(session->folder, user, pass, type);
2489 if (r != MAILIMAP_NO_ERROR) {
2490 log_error("IMAP4< Error logging in to %s\n",
2491 SESSION(session)->server);
2499 static gint imap_cmd_logout(IMAPSession *session)
2501 imap_threaded_disconnect(session->folder);
2503 return IMAP_SUCCESS;
2506 static gint imap_cmd_noop(IMAPSession *session)
2509 unsigned int exists;
2511 r = imap_threaded_noop(session->folder, &exists);
2512 if (r != MAILIMAP_NO_ERROR) {
2513 debug_print("noop err %d\n", r);
2516 session->exists = exists;
2517 session_set_access_time(SESSION(session));
2519 return IMAP_SUCCESS;
2523 static gint imap_cmd_starttls(IMAPSession *session)
2527 r = imap_threaded_starttls(session->folder);
2528 if (r != MAILIMAP_NO_ERROR) {
2529 debug_print("starttls err %d\n", r);
2532 return IMAP_SUCCESS;
2536 static gint imap_cmd_select(IMAPSession *session, const gchar *folder,
2537 gint *exists, gint *recent, gint *unseen,
2538 guint32 *uid_validity, gboolean block)
2542 r = imap_threaded_select(session->folder, folder,
2543 exists, recent, unseen, uid_validity);
2544 if (r != MAILIMAP_NO_ERROR) {
2545 debug_print("select err %d\n", r);
2548 return IMAP_SUCCESS;
2551 static gint imap_cmd_examine(IMAPSession *session, const gchar *folder,
2552 gint *exists, gint *recent, gint *unseen,
2553 guint32 *uid_validity, gboolean block)
2557 r = imap_threaded_examine(session->folder, folder,
2558 exists, recent, unseen, uid_validity);
2559 if (r != MAILIMAP_NO_ERROR) {
2560 debug_print("examine err %d\n", r);
2564 return IMAP_SUCCESS;
2567 static gint imap_cmd_create(IMAPSession *session, const gchar *folder)
2571 r = imap_threaded_create(session->folder, folder);
2572 if (r != MAILIMAP_NO_ERROR) {
2577 return IMAP_SUCCESS;
2580 static gint imap_cmd_rename(IMAPSession *session, const gchar *old_folder,
2581 const gchar *new_folder)
2585 r = imap_threaded_rename(session->folder, old_folder,
2587 if (r != MAILIMAP_NO_ERROR) {
2592 return IMAP_SUCCESS;
2595 static gint imap_cmd_delete(IMAPSession *session, const gchar *folder)
2600 r = imap_threaded_delete(session->folder, folder);
2601 if (r != MAILIMAP_NO_ERROR) {
2606 return IMAP_SUCCESS;
2609 typedef struct _fetch_data {
2610 IMAPSession *session;
2612 const gchar *filename;
2618 static void *imap_cmd_fetch_thread(void *data)
2620 fetch_data *stuff = (fetch_data *)data;
2621 IMAPSession *session = stuff->session;
2622 guint32 uid = stuff->uid;
2623 const gchar *filename = stuff->filename;
2627 r = imap_threaded_fetch_content(session->folder,
2631 r = imap_threaded_fetch_content(session->folder,
2634 if (r != MAILIMAP_NO_ERROR) {
2635 debug_print("fetch err %d\n", r);
2636 return GINT_TO_POINTER(IMAP_ERROR);
2638 return GINT_TO_POINTER(IMAP_SUCCESS);
2641 static gint imap_cmd_fetch(IMAPSession *session, guint32 uid,
2642 const gchar *filename, gboolean headers,
2645 fetch_data *data = g_new0(fetch_data, 1);
2648 data->session = session;
2650 data->filename = filename;
2651 data->headers = headers;
2654 if (prefs_common.work_offline && !inc_offline_should_override()) {
2658 statusbar_print_all(_("Fetching message..."));
2659 result = GPOINTER_TO_INT(imap_cmd_fetch_thread(data));
2660 statusbar_pop_all();
2666 static gint imap_cmd_append(IMAPSession *session, const gchar *destfolder,
2667 const gchar *file, IMAPFlags flags,
2670 struct mailimap_flag_list * flag_list;
2673 g_return_val_if_fail(file != NULL, IMAP_ERROR);
2675 flag_list = imap_flag_to_lep(flags);
2676 r = imap_threaded_append(session->folder, destfolder,
2678 if (new_uid != NULL)
2681 if (r != MAILIMAP_NO_ERROR) {
2682 debug_print("append err %d\n", r);
2685 return IMAP_SUCCESS;
2688 static gint imap_cmd_copy(IMAPSession *session, struct mailimap_set * set,
2689 const gchar *destfolder, GRelation *uid_mapping)
2693 g_return_val_if_fail(session != NULL, IMAP_ERROR);
2694 g_return_val_if_fail(set != NULL, IMAP_ERROR);
2695 g_return_val_if_fail(destfolder != NULL, IMAP_ERROR);
2697 r = imap_threaded_copy(session->folder, set, destfolder);
2698 if (r != MAILIMAP_NO_ERROR) {
2703 return IMAP_SUCCESS;
2706 static gint imap_cmd_store(IMAPSession *session, struct mailimap_set * set,
2707 IMAPFlags flags, int do_add)
2710 struct mailimap_flag_list * flag_list;
2711 struct mailimap_store_att_flags * store_att_flags;
2713 flag_list = imap_flag_to_lep(flags);
2717 mailimap_store_att_flags_new_add_flags_silent(flag_list);
2720 mailimap_store_att_flags_new_remove_flags_silent(flag_list);
2722 r = imap_threaded_store(session->folder, set, store_att_flags);
2723 if (r != MAILIMAP_NO_ERROR) {
2728 return IMAP_SUCCESS;
2731 static gint imap_cmd_expunge(IMAPSession *session)
2735 if (prefs_common.work_offline && !inc_offline_should_override()) {
2739 r = imap_threaded_expunge(session->folder);
2740 if (r != MAILIMAP_NO_ERROR) {
2745 return IMAP_SUCCESS;
2748 static void imap_path_separator_subst(gchar *str, gchar separator)
2751 gboolean in_escape = FALSE;
2753 if (!separator || separator == '/') return;
2755 for (p = str; *p != '\0'; p++) {
2756 if (*p == '/' && !in_escape)
2758 else if (*p == '&' && *(p + 1) != '-' && !in_escape)
2760 else if (*p == '-' && in_escape)
2765 static gchar *imap_modified_utf7_to_utf8(const gchar *mutf7_str)
2767 static iconv_t cd = (iconv_t)-1;
2768 static gboolean iconv_ok = TRUE;
2771 size_t norm_utf7_len;
2773 gchar *to_str, *to_p;
2775 gboolean in_escape = FALSE;
2777 if (!iconv_ok) return g_strdup(mutf7_str);
2779 if (cd == (iconv_t)-1) {
2780 cd = iconv_open(CS_INTERNAL, CS_UTF_7);
2781 if (cd == (iconv_t)-1) {
2782 g_warning("iconv cannot convert UTF-7 to %s\n",
2785 return g_strdup(mutf7_str);
2789 /* modified UTF-7 to normal UTF-7 conversion */
2790 norm_utf7 = g_string_new(NULL);
2792 for (p = mutf7_str; *p != '\0'; p++) {
2793 /* replace: '&' -> '+',
2795 escaped ',' -> '/' */
2796 if (!in_escape && *p == '&') {
2797 if (*(p + 1) != '-') {
2798 g_string_append_c(norm_utf7, '+');
2801 g_string_append_c(norm_utf7, '&');
2804 } else if (in_escape && *p == ',') {
2805 g_string_append_c(norm_utf7, '/');
2806 } else if (in_escape && *p == '-') {
2807 g_string_append_c(norm_utf7, '-');
2810 g_string_append_c(norm_utf7, *p);
2814 norm_utf7_p = norm_utf7->str;
2815 norm_utf7_len = norm_utf7->len;
2816 to_len = strlen(mutf7_str) * 5;
2817 to_p = to_str = g_malloc(to_len + 1);
2819 if (iconv(cd, (ICONV_CONST gchar **)&norm_utf7_p, &norm_utf7_len,
2820 &to_p, &to_len) == -1) {
2821 g_warning(_("iconv cannot convert UTF-7 to %s\n"),
2822 conv_get_locale_charset_str());
2823 g_string_free(norm_utf7, TRUE);
2825 return g_strdup(mutf7_str);
2828 /* second iconv() call for flushing */
2829 iconv(cd, NULL, NULL, &to_p, &to_len);
2830 g_string_free(norm_utf7, TRUE);
2836 static gchar *imap_utf8_to_modified_utf7(const gchar *from)
2838 static iconv_t cd = (iconv_t)-1;
2839 static gboolean iconv_ok = TRUE;
2840 gchar *norm_utf7, *norm_utf7_p;
2841 size_t from_len, norm_utf7_len;
2843 gchar *from_tmp, *to, *p;
2844 gboolean in_escape = FALSE;
2846 if (!iconv_ok) return g_strdup(from);
2848 if (cd == (iconv_t)-1) {
2849 cd = iconv_open(CS_UTF_7, CS_INTERNAL);
2850 if (cd == (iconv_t)-1) {
2851 g_warning(_("iconv cannot convert %s to UTF-7\n"),
2854 return g_strdup(from);
2858 /* UTF-8 to normal UTF-7 conversion */
2859 Xstrdup_a(from_tmp, from, return g_strdup(from));
2860 from_len = strlen(from);
2861 norm_utf7_len = from_len * 5;
2862 Xalloca(norm_utf7, norm_utf7_len + 1, return g_strdup(from));
2863 norm_utf7_p = norm_utf7;
2865 #define IS_PRINT(ch) (isprint(ch) && IS_ASCII(ch))
2867 while (from_len > 0) {
2868 if (*from_tmp == '+') {
2869 *norm_utf7_p++ = '+';
2870 *norm_utf7_p++ = '-';
2874 } else if (IS_PRINT(*(guchar *)from_tmp)) {
2875 /* printable ascii char */
2876 *norm_utf7_p = *from_tmp;
2882 size_t conv_len = 0;
2884 /* unprintable char: convert to UTF-7 */
2886 while (!IS_PRINT(*(guchar *)p) && conv_len < from_len) {
2887 conv_len += g_utf8_skip[*(guchar *)p];
2888 p += g_utf8_skip[*(guchar *)p];
2891 from_len -= conv_len;
2892 if (iconv(cd, (ICONV_CONST gchar **)&from_tmp,
2894 &norm_utf7_p, &norm_utf7_len) == -1) {
2895 g_warning(_("iconv cannot convert UTF-8 to UTF-7\n"));
2896 return g_strdup(from);
2899 /* second iconv() call for flushing */
2900 iconv(cd, NULL, NULL, &norm_utf7_p, &norm_utf7_len);
2906 *norm_utf7_p = '\0';
2907 to_str = g_string_new(NULL);
2908 for (p = norm_utf7; p < norm_utf7_p; p++) {
2909 /* replace: '&' -> "&-",
2912 BASE64 '/' -> ',' */
2913 if (!in_escape && *p == '&') {
2914 g_string_append(to_str, "&-");
2915 } else if (!in_escape && *p == '+') {
2916 if (*(p + 1) == '-') {
2917 g_string_append_c(to_str, '+');
2920 g_string_append_c(to_str, '&');
2923 } else if (in_escape && *p == '/') {
2924 g_string_append_c(to_str, ',');
2925 } else if (in_escape && *p == '-') {
2926 g_string_append_c(to_str, '-');
2929 g_string_append_c(to_str, *p);
2935 g_string_append_c(to_str, '-');
2939 g_string_free(to_str, FALSE);
2944 static gboolean imap_rename_folder_func(GNode *node, gpointer data)
2946 FolderItem *item = node->data;
2947 gchar **paths = data;
2948 const gchar *oldpath = paths[0];
2949 const gchar *newpath = paths[1];
2951 gchar *new_itempath;
2954 oldpathlen = strlen(oldpath);
2955 if (strncmp(oldpath, item->path, oldpathlen) != 0) {
2956 g_warning("path doesn't match: %s, %s\n", oldpath, item->path);
2960 base = item->path + oldpathlen;
2961 while (*base == G_DIR_SEPARATOR) base++;
2963 new_itempath = g_strdup(newpath);
2965 new_itempath = g_strconcat(newpath, G_DIR_SEPARATOR_S, base,
2968 item->path = new_itempath;
2973 typedef struct _get_list_uid_data {
2975 IMAPSession *session;
2976 IMAPFolderItem *item;
2977 GSList **msgnum_list;
2979 } get_list_uid_data;
2981 static void *get_list_of_uids_thread(void *data)
2983 get_list_uid_data *stuff = (get_list_uid_data *)data;
2984 Folder *folder = stuff->folder;
2985 IMAPFolderItem *item = stuff->item;
2986 GSList **msgnum_list = stuff->msgnum_list;
2987 gint ok, nummsgs = 0, lastuid_old;
2988 IMAPSession *session;
2989 GSList *uidlist, *elem;
2990 struct mailimap_set * set;
2991 clist * lep_uidlist;
2994 session = stuff->session;
2995 if (session == NULL) {
2997 return GINT_TO_POINTER(-1);
2999 /* no session locking here, it's already locked by caller */
3000 ok = imap_select(session, IMAP_FOLDER(folder), item->item.path,
3001 NULL, NULL, NULL, NULL, TRUE);
3002 if (ok != IMAP_SUCCESS) {
3004 return GINT_TO_POINTER(-1);
3009 set = mailimap_set_new_interval(item->lastuid + 1, 0);
3011 r = imap_threaded_search(folder, IMAP_SEARCH_TYPE_SIMPLE, set,
3013 if (r == MAILIMAP_NO_ERROR) {
3014 GSList * fetchuid_list;
3017 imap_uid_list_from_lep(lep_uidlist);
3018 uidlist = g_slist_concat(fetchuid_list, uidlist);
3021 GSList * fetchuid_list;
3022 carray * lep_uidtab;
3024 r = imap_threaded_fetch_uid(folder, item->lastuid + 1,
3026 if (r == MAILIMAP_NO_ERROR) {
3028 imap_uid_list_from_lep_tab(lep_uidtab);
3029 uidlist = g_slist_concat(fetchuid_list, uidlist);
3033 lastuid_old = item->lastuid;
3034 *msgnum_list = g_slist_copy(item->uid_list);
3035 nummsgs = g_slist_length(*msgnum_list);
3036 debug_print("Got %d uids from cache\n", g_slist_length(item->uid_list));
3038 for (elem = uidlist; elem != NULL; elem = g_slist_next(elem)) {
3041 msgnum = GPOINTER_TO_INT(elem->data);
3042 if (msgnum > lastuid_old) {
3043 *msgnum_list = g_slist_prepend(*msgnum_list, GINT_TO_POINTER(msgnum));
3044 item->uid_list = g_slist_prepend(item->uid_list, GINT_TO_POINTER(msgnum));
3047 if(msgnum > item->lastuid)
3048 item->lastuid = msgnum;
3051 g_slist_free(uidlist);
3053 return GINT_TO_POINTER(nummsgs);
3056 static gint get_list_of_uids(IMAPSession *session, Folder *folder, IMAPFolderItem *item, GSList **msgnum_list)
3059 get_list_uid_data *data = g_new0(get_list_uid_data, 1);
3061 data->folder = folder;
3063 data->msgnum_list = msgnum_list;
3064 data->session = session;
3065 if (prefs_common.work_offline && !inc_offline_should_override()) {
3070 result = GPOINTER_TO_INT(get_list_of_uids_thread(data));
3076 gint imap_get_num_list(Folder *folder, FolderItem *_item, GSList **msgnum_list, gboolean *old_uids_valid)
3078 IMAPFolderItem *item = (IMAPFolderItem *)_item;
3079 IMAPSession *session;
3080 gint ok, nummsgs = 0, exists, uid_val, uid_next;
3081 GSList *uidlist = NULL;
3083 gboolean selected_folder;
3085 debug_print("get_num_list\n");
3087 g_return_val_if_fail(folder != NULL, -1);
3088 g_return_val_if_fail(item != NULL, -1);
3089 g_return_val_if_fail(item->item.path != NULL, -1);
3090 g_return_val_if_fail(FOLDER_CLASS(folder) == &imap_class, -1);
3091 g_return_val_if_fail(folder->account != NULL, -1);
3093 session = imap_session_get(folder);
3094 g_return_val_if_fail(session != NULL, -1);
3096 statusbar_print_all("Scanning %s...\n", FOLDER_ITEM(item)->path
3097 ? FOLDER_ITEM(item)->path:"");
3099 selected_folder = (session->mbox != NULL) &&
3100 (!strcmp(session->mbox, item->item.path));
3101 if (selected_folder) {
3102 ok = imap_cmd_noop(session);
3103 if (ok != IMAP_SUCCESS) {
3104 debug_print("disconnected!\n");
3105 session = imap_reconnect_if_possible(folder, session);
3106 if (session == NULL) {
3107 statusbar_pop_all();
3112 exists = session->exists;
3114 *old_uids_valid = TRUE;
3116 if (item->use_cache && time(NULL) - item->use_cache < 2) {
3117 exists = item->c_messages;
3118 uid_next = item->c_uid_next;
3119 uid_val = item->c_uid_validity;
3121 debug_print("using cache %d %d %d\n", exists, uid_next, uid_val);
3123 ok = imap_status(session, IMAP_FOLDER(folder), item->item.path, item,
3124 &exists, &uid_next, &uid_val, NULL, FALSE);
3126 item->use_cache = (time_t)0;
3127 if (ok != IMAP_SUCCESS) {
3128 statusbar_pop_all();
3132 if(item->item.mtime == uid_val)
3133 *old_uids_valid = TRUE;
3135 *old_uids_valid = FALSE;
3137 debug_print("Freeing imap uid cache\n");
3139 g_slist_free(item->uid_list);
3140 item->uid_list = NULL;
3142 item->item.mtime = uid_val;
3144 imap_delete_all_cached_messages((FolderItem *)item);
3148 if (!selected_folder)
3149 item->uid_next = uid_next;
3151 /* If old uid_next matches new uid_next we can be sure no message
3152 was added to the folder */
3153 if (( selected_folder && !session->folder_content_changed) ||
3154 (!selected_folder && uid_next == item->uid_next)) {
3155 nummsgs = g_slist_length(item->uid_list);
3157 /* If number of messages is still the same we
3158 know our caches message numbers are still valid,
3159 otherwise if the number of messages has decrease
3160 we discard our cache to start a new scan to find
3161 out which numbers have been removed */
3162 if (exists == nummsgs) {
3163 *msgnum_list = g_slist_copy(item->uid_list);
3164 statusbar_pop_all();
3167 } else if (exists < nummsgs) {
3168 debug_print("Freeing imap uid cache");
3170 g_slist_free(item->uid_list);
3171 item->uid_list = NULL;
3176 *msgnum_list = NULL;
3177 statusbar_pop_all();
3182 nummsgs = get_list_of_uids(session, folder, item, &uidlist);
3185 statusbar_pop_all();
3190 if (nummsgs != exists) {
3191 /* Cache contains more messages then folder, we have cached
3192 an old UID of a message that was removed and new messages
3193 have been added too, otherwise the uid_next check would
3195 debug_print("Freeing imap uid cache");
3197 g_slist_free(item->uid_list);
3198 item->uid_list = NULL;
3200 g_slist_free(*msgnum_list);
3202 nummsgs = get_list_of_uids(session, folder, item, &uidlist);
3205 *msgnum_list = uidlist;
3207 dir = folder_item_get_path((FolderItem *)item);
3208 debug_print("removing old messages from %s\n", dir);
3209 remove_numbered_files_not_in_list(dir, *msgnum_list);
3212 debug_print("get_num_list - ok - %i\n", nummsgs);
3213 statusbar_pop_all();
3218 static MsgInfo *imap_parse_msg(const gchar *file, FolderItem *item)
3223 flags.perm_flags = MSG_NEW|MSG_UNREAD;
3224 flags.tmp_flags = 0;
3226 g_return_val_if_fail(item != NULL, NULL);
3227 g_return_val_if_fail(file != NULL, NULL);
3229 if (folder_has_parent_of_type(item, F_QUEUE)) {
3230 MSG_SET_TMP_FLAGS(flags, MSG_QUEUED);
3231 } else if (folder_has_parent_of_type(item, F_DRAFT)) {
3232 MSG_SET_TMP_FLAGS(flags, MSG_DRAFT);
3235 msginfo = procheader_parse_file(file, flags, FALSE, FALSE);
3236 if (!msginfo) return NULL;
3238 msginfo->plaintext_file = g_strdup(file);
3239 msginfo->folder = item;
3244 GSList *imap_get_msginfos(Folder *folder, FolderItem *item,
3245 GSList *msgnum_list)
3247 IMAPSession *session;
3248 MsgInfoList *ret = NULL;
3251 debug_print("get_msginfos\n");
3253 g_return_val_if_fail(folder != NULL, NULL);
3254 g_return_val_if_fail(item != NULL, NULL);
3255 g_return_val_if_fail(msgnum_list != NULL, NULL);
3257 session = imap_session_get(folder);
3258 g_return_val_if_fail(session != NULL, NULL);
3260 debug_print("IMAP getting msginfos\n");
3261 ok = imap_select(session, IMAP_FOLDER(folder), item->path,
3262 NULL, NULL, NULL, NULL, FALSE);
3263 if (ok != IMAP_SUCCESS) {
3267 if (!(folder_has_parent_of_type(item, F_DRAFT) ||
3268 folder_has_parent_of_type(item, F_QUEUE))) {
3269 ret = g_slist_concat(ret,
3270 imap_get_uncached_messages(session, item,
3273 MsgNumberList *sorted_list, *elem;
3274 gint startnum, lastnum;
3276 sorted_list = g_slist_sort(g_slist_copy(msgnum_list), g_int_compare);
3278 startnum = lastnum = GPOINTER_TO_INT(sorted_list->data);
3280 for (elem = sorted_list;; elem = g_slist_next(elem)) {
3284 num = GPOINTER_TO_INT(elem->data);
3286 if (num > lastnum + 1 || elem == NULL) {
3288 for (i = startnum; i <= lastnum; ++i) {
3291 file = imap_fetch_msg(folder, item, i);
3293 MsgInfo *msginfo = imap_parse_msg(file, item);
3294 if (msginfo != NULL) {
3295 msginfo->msgnum = i;
3296 ret = g_slist_append(ret, msginfo);
3310 g_slist_free(sorted_list);
3316 MsgInfo *imap_get_msginfo(Folder *folder, FolderItem *item, gint uid)
3318 MsgInfo *msginfo = NULL;
3319 MsgInfoList *msginfolist;
3320 MsgNumberList numlist;
3322 numlist.next = NULL;
3323 numlist.data = GINT_TO_POINTER(uid);
3325 msginfolist = imap_get_msginfos(folder, item, &numlist);
3326 if (msginfolist != NULL) {
3327 msginfo = msginfolist->data;
3328 g_slist_free(msginfolist);
3334 gboolean imap_scan_required(Folder *folder, FolderItem *_item)
3336 IMAPSession *session;
3337 IMAPFolderItem *item = (IMAPFolderItem *)_item;
3338 gint ok, exists = 0, unseen = 0;
3339 guint32 uid_next, uid_val;
3340 gboolean selected_folder;
3342 g_return_val_if_fail(folder != NULL, FALSE);
3343 g_return_val_if_fail(item != NULL, FALSE);
3344 g_return_val_if_fail(item->item.folder != NULL, FALSE);
3345 g_return_val_if_fail(FOLDER_CLASS(item->item.folder) == &imap_class, FALSE);
3347 if (item->item.path == NULL)
3350 session = imap_session_get(folder);
3351 g_return_val_if_fail(session != NULL, FALSE);
3353 selected_folder = (session->mbox != NULL) &&
3354 (!strcmp(session->mbox, item->item.path));
3355 if (selected_folder) {
3356 ok = imap_cmd_noop(session);
3357 if (ok != IMAP_SUCCESS) {
3358 debug_print("disconnected!\n");
3359 session = imap_reconnect_if_possible(folder, session);
3360 if (session == NULL)
3365 if (session->folder_content_changed
3366 || session->exists != item->item.total_msgs) {
3371 ok = imap_status(session, IMAP_FOLDER(folder), item->item.path, IMAP_FOLDER_ITEM(item),
3372 &exists, &uid_next, &uid_val, &unseen, FALSE);
3373 if (ok != IMAP_SUCCESS) {
3378 item->use_cache = time(NULL);
3379 item->c_messages = exists;
3380 item->c_uid_next = uid_next;
3381 item->c_uid_validity = uid_val;
3382 item->c_unseen = unseen;
3384 if ((uid_next != item->uid_next) || (exists != item->item.total_msgs)) {
3393 void imap_change_flags(Folder *folder, FolderItem *item, MsgInfo *msginfo, MsgPermFlags newflags)
3395 IMAPSession *session;
3396 IMAPFlags flags_set = 0, flags_unset = 0;
3397 gint ok = IMAP_SUCCESS;
3398 MsgNumberList numlist;
3399 hashtable_data *ht_data = NULL;
3401 g_return_if_fail(folder != NULL);
3402 g_return_if_fail(folder->klass == &imap_class);
3403 g_return_if_fail(item != NULL);
3404 g_return_if_fail(item->folder == folder);
3405 g_return_if_fail(msginfo != NULL);
3406 g_return_if_fail(msginfo->folder == item);
3408 session = imap_session_get(folder);
3413 if ((ok = imap_select(session, IMAP_FOLDER(folder), msginfo->folder->path,
3414 NULL, NULL, NULL, NULL, FALSE)) != IMAP_SUCCESS) {
3419 if (!MSG_IS_MARKED(msginfo->flags) && (newflags & MSG_MARKED))
3420 flags_set |= IMAP_FLAG_FLAGGED;
3421 if ( MSG_IS_MARKED(msginfo->flags) && !(newflags & MSG_MARKED))
3422 flags_unset |= IMAP_FLAG_FLAGGED;
3424 if (!MSG_IS_UNREAD(msginfo->flags) && (newflags & MSG_UNREAD))
3425 flags_unset |= IMAP_FLAG_SEEN;
3426 if ( MSG_IS_UNREAD(msginfo->flags) && !(newflags & MSG_UNREAD))
3427 flags_set |= IMAP_FLAG_SEEN;
3429 if (!MSG_IS_REPLIED(msginfo->flags) && (newflags & MSG_REPLIED))
3430 flags_set |= IMAP_FLAG_ANSWERED;
3431 if ( MSG_IS_REPLIED(msginfo->flags) && !(newflags & MSG_REPLIED))
3432 flags_unset |= IMAP_FLAG_ANSWERED;
3434 if (!MSG_IS_DELETED(msginfo->flags) && (newflags & MSG_DELETED))
3435 flags_set |= IMAP_FLAG_DELETED;
3436 if ( MSG_IS_DELETED(msginfo->flags) && !(newflags & MSG_DELETED))
3437 flags_unset |= IMAP_FLAG_DELETED;
3439 numlist.next = NULL;
3440 numlist.data = GINT_TO_POINTER(msginfo->msgnum);
3442 if (IMAP_FOLDER_ITEM(item)->batching) {
3443 /* instead of performing an UID STORE command for each message change,
3444 * as a lot of them can change "together", we just fill in hashtables
3445 * and defer the treatment so that we're able to send only one
3448 debug_print("IMAP batch mode on, deferring flags change\n");
3450 ht_data = g_hash_table_lookup(IMAP_FOLDER_ITEM(item)->flags_set_table,
3451 GINT_TO_POINTER(flags_set));
3452 if (ht_data == NULL) {
3453 ht_data = g_new0(hashtable_data, 1);
3454 ht_data->session = session;
3455 ht_data->item = IMAP_FOLDER_ITEM(item);
3456 g_hash_table_insert(IMAP_FOLDER_ITEM(item)->flags_set_table,
3457 GINT_TO_POINTER(flags_set), ht_data);
3459 if (!g_slist_find(ht_data->msglist, GINT_TO_POINTER(msginfo->msgnum)))
3460 ht_data->msglist = g_slist_prepend(ht_data->msglist, GINT_TO_POINTER(msginfo->msgnum));
3463 ht_data = g_hash_table_lookup(IMAP_FOLDER_ITEM(item)->flags_unset_table,
3464 GINT_TO_POINTER(flags_unset));
3465 if (ht_data == NULL) {
3466 ht_data = g_new0(hashtable_data, 1);
3467 ht_data->session = session;
3468 ht_data->item = IMAP_FOLDER_ITEM(item);
3469 g_hash_table_insert(IMAP_FOLDER_ITEM(item)->flags_unset_table,
3470 GINT_TO_POINTER(flags_unset), ht_data);
3472 if (!g_slist_find(ht_data->msglist, GINT_TO_POINTER(msginfo->msgnum)))
3473 ht_data->msglist = g_slist_prepend(ht_data->msglist,
3474 GINT_TO_POINTER(msginfo->msgnum));
3477 debug_print("IMAP changing flags\n");
3479 ok = imap_set_message_flags(session, &numlist, flags_set, TRUE);
3480 if (ok != IMAP_SUCCESS) {
3487 ok = imap_set_message_flags(session, &numlist, flags_unset, FALSE);
3488 if (ok != IMAP_SUCCESS) {
3494 msginfo->flags.perm_flags = newflags;
3499 static gint imap_remove_msg(Folder *folder, FolderItem *item, gint uid)
3502 IMAPSession *session;
3504 MsgNumberList numlist;
3506 g_return_val_if_fail(folder != NULL, -1);
3507 g_return_val_if_fail(FOLDER_CLASS(folder) == &imap_class, -1);
3508 g_return_val_if_fail(item != NULL, -1);
3510 session = imap_session_get(folder);
3511 if (!session) return -1;
3513 ok = imap_select(session, IMAP_FOLDER(folder), item->path,
3514 NULL, NULL, NULL, NULL, FALSE);
3515 if (ok != IMAP_SUCCESS) {
3519 numlist.next = NULL;
3520 numlist.data = GINT_TO_POINTER(uid);
3522 ok = imap_set_message_flags
3523 (IMAP_SESSION(REMOTE_FOLDER(folder)->session),
3524 &numlist, IMAP_FLAG_DELETED, TRUE);
3525 if (ok != IMAP_SUCCESS) {
3526 log_warning(_("can't set deleted flags: %d\n"), uid);
3531 if (!session->uidplus) {
3532 ok = imap_cmd_expunge(session);
3536 uidstr = g_strdup_printf("%u", uid);
3537 ok = imap_cmd_expunge(session);
3540 if (ok != IMAP_SUCCESS) {
3541 log_warning(_("can't expunge\n"));
3546 IMAP_FOLDER_ITEM(item)->uid_list = g_slist_remove(
3547 IMAP_FOLDER_ITEM(item)->uid_list, numlist.data);
3548 dir = folder_item_get_path(item);
3549 if (is_dir_exist(dir))
3550 remove_numbered_files(dir, uid, uid);
3553 return IMAP_SUCCESS;
3556 static gint compare_msginfo(gconstpointer a, gconstpointer b)
3558 return ((MsgInfo *)a)->msgnum - ((MsgInfo *)b)->msgnum;
3561 static guint gslist_find_next_num(MsgNumberList **list, guint num)
3565 g_return_val_if_fail(list != NULL, -1);
3567 for (elem = *list; elem != NULL; elem = g_slist_next(elem))
3568 if (GPOINTER_TO_INT(elem->data) >= num)
3571 return elem != NULL ? GPOINTER_TO_INT(elem->data) : (gint)-1;
3575 * NEW and DELETED flags are not syncronized
3576 * - The NEW/RECENT flags in IMAP folders can not really be directly
3577 * modified by Sylpheed
3578 * - The DELETE/DELETED flag in IMAP and Sylpheed don't have the same
3579 * meaning, in IMAP it always removes the messages from the FolderItem
3580 * in Sylpheed it can mean to move the message to trash
3583 typedef struct _get_flags_data {
3586 MsgInfoList *msginfo_list;
3587 GRelation *msgflags;
3588 gboolean full_search;
3592 static /*gint*/ void *imap_get_flags_thread(void *data)
3594 get_flags_data *stuff = (get_flags_data *)data;
3595 Folder *folder = stuff->folder;
3596 FolderItem *item = stuff->item;
3597 MsgInfoList *msginfo_list = stuff->msginfo_list;
3598 GRelation *msgflags = stuff->msgflags;
3599 gboolean full_search = stuff->full_search;
3600 IMAPSession *session;
3601 GSList *sorted_list = NULL;
3602 GSList *unseen = NULL, *answered = NULL, *flagged = NULL, *deleted = NULL;
3603 GSList *p_unseen, *p_answered, *p_flagged, *p_deleted;
3605 GSList *seq_list, *cur;
3606 gboolean reverse_seen = FALSE;
3609 gint exists_cnt, unseen_cnt;
3610 gboolean selected_folder;
3612 if (folder == NULL || item == NULL) {
3614 return GINT_TO_POINTER(-1);
3617 session = imap_session_get(folder);
3618 if (session == NULL) {
3620 return GINT_TO_POINTER(-1);
3623 selected_folder = (session->mbox != NULL) &&
3624 (!strcmp(session->mbox, item->path));
3626 if (!selected_folder) {
3627 ok = imap_select(session, IMAP_FOLDER(folder), item->path,
3628 &exists_cnt, NULL, &unseen_cnt, NULL, TRUE);
3629 if (ok != IMAP_SUCCESS) {
3632 return GINT_TO_POINTER(-1);
3635 if (unseen_cnt > exists_cnt / 2)
3636 reverse_seen = TRUE;
3639 if (item->unread_msgs > item->total_msgs / 2)
3640 reverse_seen = TRUE;
3643 cmd_buf = g_string_new(NULL);
3645 sorted_list = g_slist_sort(g_slist_copy(msginfo_list), compare_msginfo);
3647 seq_list = imap_get_lep_set_from_msglist(msginfo_list);
3649 struct mailimap_set * set;
3650 set = mailimap_set_new_interval(1, 0);
3651 seq_list = g_slist_append(NULL, set);
3654 for (cur = seq_list; cur != NULL; cur = g_slist_next(cur)) {
3655 struct mailimap_set * imapset;
3656 clist * lep_uidlist;
3659 imapset = cur->data;
3661 r = imap_threaded_search(folder, IMAP_SEARCH_TYPE_SEEN,
3662 imapset, &lep_uidlist);
3665 r = imap_threaded_search(folder,
3666 IMAP_SEARCH_TYPE_UNSEEN,
3667 imapset, &lep_uidlist);
3669 if (r == MAILIMAP_NO_ERROR) {
3672 uidlist = imap_uid_list_from_lep(lep_uidlist);
3673 mailimap_search_result_free(lep_uidlist);
3675 unseen = g_slist_concat(unseen, uidlist);
3678 r = imap_threaded_search(folder, IMAP_SEARCH_TYPE_ANSWERED,
3679 imapset, &lep_uidlist);
3680 if (r == MAILIMAP_NO_ERROR) {
3683 uidlist = imap_uid_list_from_lep(lep_uidlist);
3684 mailimap_search_result_free(lep_uidlist);
3686 answered = g_slist_concat(answered, uidlist);
3689 r = imap_threaded_search(folder, IMAP_SEARCH_TYPE_FLAGGED,
3690 imapset, &lep_uidlist);
3691 if (r == MAILIMAP_NO_ERROR) {
3694 uidlist = imap_uid_list_from_lep(lep_uidlist);
3695 mailimap_search_result_free(lep_uidlist);
3697 flagged = g_slist_concat(flagged, uidlist);
3700 r = imap_threaded_search(folder, IMAP_SEARCH_TYPE_DELETED,
3701 imapset, &lep_uidlist);
3702 if (r == MAILIMAP_NO_ERROR) {
3705 uidlist = imap_uid_list_from_lep(lep_uidlist);
3706 mailimap_search_result_free(lep_uidlist);
3708 deleted = g_slist_concat(deleted, uidlist);
3713 p_answered = answered;
3714 p_flagged = flagged;
3715 p_deleted = deleted;
3717 for (elem = sorted_list; elem != NULL; elem = g_slist_next(elem)) {
3722 msginfo = (MsgInfo *) elem->data;
3723 flags = msginfo->flags.perm_flags;
3724 wasnew = (flags & MSG_NEW);
3725 flags &= ~((reverse_seen ? 0 : MSG_UNREAD | MSG_NEW) | MSG_REPLIED | MSG_MARKED);
3727 flags |= MSG_UNREAD | (wasnew ? MSG_NEW : 0);
3728 if (gslist_find_next_num(&p_unseen, msginfo->msgnum) == msginfo->msgnum) {
3729 if (!reverse_seen) {
3730 flags |= MSG_UNREAD | (wasnew ? MSG_NEW : 0);
3732 flags &= ~(MSG_UNREAD | MSG_NEW);
3735 if (gslist_find_next_num(&p_answered, msginfo->msgnum) == msginfo->msgnum)
3736 flags |= MSG_REPLIED;
3738 flags &= ~MSG_REPLIED;
3739 if (gslist_find_next_num(&p_flagged, msginfo->msgnum) == msginfo->msgnum)
3740 flags |= MSG_MARKED;
3742 flags &= ~MSG_MARKED;
3743 if (gslist_find_next_num(&p_deleted, msginfo->msgnum) == msginfo->msgnum)
3744 flags |= MSG_DELETED;
3746 flags &= ~MSG_DELETED;
3747 g_relation_insert(msgflags, msginfo, GINT_TO_POINTER(flags));
3750 imap_lep_set_free(seq_list);
3751 g_slist_free(flagged);
3752 g_slist_free(deleted);
3753 g_slist_free(answered);
3754 g_slist_free(unseen);
3755 g_slist_free(sorted_list);
3756 g_string_free(cmd_buf, TRUE);
3760 return GINT_TO_POINTER(0);
3763 static gint imap_get_flags(Folder *folder, FolderItem *item,
3764 MsgInfoList *msginfo_list, GRelation *msgflags)
3767 get_flags_data *data = g_new0(get_flags_data, 1);
3769 data->folder = folder;
3771 data->msginfo_list = msginfo_list;
3772 data->msgflags = msgflags;
3773 data->full_search = FALSE;
3775 GSList *tmp = NULL, *cur;
3777 if (prefs_common.work_offline && !inc_offline_should_override()) {
3782 tmp = folder_item_get_msg_list(item);
3784 if (g_slist_length(tmp) == g_slist_length(msginfo_list))
3785 data->full_search = TRUE;
3787 for (cur = tmp; cur; cur = cur->next)
3788 procmsg_msginfo_free((MsgInfo *)cur->data);
3792 result = GPOINTER_TO_INT(imap_get_flags_thread(data));
3799 static gboolean process_flags(gpointer key, gpointer value, gpointer user_data)
3801 gboolean flags_set = GPOINTER_TO_INT(user_data);
3802 gint flags_value = GPOINTER_TO_INT(key);
3803 hashtable_data *data = (hashtable_data *)value;
3804 IMAPFolderItem *_item = data->item;
3805 FolderItem *item = (FolderItem *)_item;
3806 gint ok = IMAP_ERROR;
3807 IMAPSession *session = imap_session_get(item->folder);
3809 data->msglist = g_slist_reverse(data->msglist);
3811 debug_print("IMAP %ssetting flags to %d for %d messages\n",
3814 g_slist_length(data->msglist));
3818 ok = imap_select(session, IMAP_FOLDER(item->folder), item->path,
3819 NULL, NULL, NULL, NULL, FALSE);
3821 if (ok == IMAP_SUCCESS) {
3822 imap_set_message_flags(data->session, data->msglist, flags_value, flags_set);
3824 g_warning("can't select mailbox %s\n", item->path);
3828 g_slist_free(data->msglist);
3833 static void process_hashtable(IMAPFolderItem *item)
3835 if (item->flags_set_table) {
3836 g_hash_table_foreach_remove(item->flags_set_table, process_flags, GINT_TO_POINTER(TRUE));
3837 g_free(item->flags_set_table);
3838 item->flags_set_table = NULL;
3840 if (item->flags_unset_table) {
3841 g_hash_table_foreach_remove(item->flags_unset_table, process_flags, GINT_TO_POINTER(FALSE));
3842 g_free(item->flags_unset_table);
3843 item->flags_unset_table = NULL;
3847 static void imap_set_batch (Folder *folder, FolderItem *_item, gboolean batch)
3849 IMAPFolderItem *item = (IMAPFolderItem *)_item;
3851 g_return_if_fail(item != NULL);
3853 if (item->batching == batch)
3857 item->batching = TRUE;
3858 debug_print("IMAP switching to batch mode\n");
3859 if (!item->flags_set_table) {
3860 item->flags_set_table = g_hash_table_new(NULL, g_direct_equal);
3862 if (!item->flags_unset_table) {
3863 item->flags_unset_table = g_hash_table_new(NULL, g_direct_equal);
3866 debug_print("IMAP switching away from batch mode\n");
3868 process_hashtable(item);
3869 item->batching = FALSE;
3875 /* data types conversion libetpan <-> sylpheed */
3879 #define ETPAN_IMAP_MB_MARKED 1
3880 #define ETPAN_IMAP_MB_UNMARKED 2
3881 #define ETPAN_IMAP_MB_NOSELECT 4
3882 #define ETPAN_IMAP_MB_NOINFERIORS 8
3884 static int imap_flags_to_flags(struct mailimap_mbx_list_flags * imap_flags)
3890 if (imap_flags->mbf_type == MAILIMAP_MBX_LIST_FLAGS_SFLAG) {
3891 switch (imap_flags->mbf_sflag) {
3892 case MAILIMAP_MBX_LIST_SFLAG_MARKED:
3893 flags |= ETPAN_IMAP_MB_MARKED;
3895 case MAILIMAP_MBX_LIST_SFLAG_NOSELECT:
3896 flags |= ETPAN_IMAP_MB_NOSELECT;
3898 case MAILIMAP_MBX_LIST_SFLAG_UNMARKED:
3899 flags |= ETPAN_IMAP_MB_UNMARKED;
3904 for(cur = clist_begin(imap_flags->mbf_oflags) ; cur != NULL ;
3905 cur = clist_next(cur)) {
3906 struct mailimap_mbx_list_oflag * oflag;
3908 oflag = clist_content(cur);
3910 switch (oflag->of_type) {
3911 case MAILIMAP_MBX_LIST_OFLAG_NOINFERIORS:
3912 flags |= ETPAN_IMAP_MB_NOINFERIORS;
3920 static GSList * imap_list_from_lep(IMAPFolder * folder,
3921 clist * list, const gchar * real_path, gboolean all)
3928 for(iter = clist_begin(list) ; iter != NULL ;
3929 iter = clist_next(iter)) {
3930 struct mailimap_mailbox_list * mb;
3938 FolderItem *new_item;
3940 mb = clist_content(iter);
3946 if (mb->mb_flag != NULL)
3947 flags = imap_flags_to_flags(mb->mb_flag);
3949 delimiter = mb->mb_delimiter;
3952 dup_name = strdup(name);
3953 if (delimiter != '\0')
3954 subst_char(dup_name, delimiter, '/');
3956 base = g_path_get_basename(dup_name);
3957 if (base[0] == '.') {
3963 if (!all && strcmp(dup_name, real_path) == 0) {
3969 if (!all && dup_name[strlen(dup_name)-1] == '/') {
3975 loc_name = imap_modified_utf7_to_utf8(base);
3976 loc_path = imap_modified_utf7_to_utf8(dup_name);
3978 new_item = folder_item_new(FOLDER(folder), loc_name, loc_path);
3979 if ((flags & ETPAN_IMAP_MB_NOINFERIORS) != 0)
3980 new_item->no_sub = TRUE;
3981 if (strcmp(dup_name, "INBOX") != 0 &&
3982 ((flags & ETPAN_IMAP_MB_NOSELECT) != 0))
3983 new_item->no_select = TRUE;
3985 item_list = g_slist_append(item_list, new_item);
3987 debug_print("folder '%s' found.\n", loc_path);
3998 static GSList * imap_get_lep_set_from_numlist(MsgNumberList *numlist)
4000 GSList *sorted_list, *cur;
4001 guint first, last, next;
4002 GSList *ret_list = NULL;
4004 struct mailimap_set * current_set;
4005 unsigned int item_count;
4007 if (numlist == NULL)
4011 current_set = mailimap_set_new_empty();
4013 sorted_list = g_slist_copy(numlist);
4014 sorted_list = g_slist_sort(sorted_list, g_int_compare);
4016 first = GPOINTER_TO_INT(sorted_list->data);
4019 for (cur = sorted_list; cur != NULL; cur = g_slist_next(cur)) {
4020 if (GPOINTER_TO_INT(cur->data) == 0)
4025 last = GPOINTER_TO_INT(cur->data);
4027 next = GPOINTER_TO_INT(cur->next->data);
4031 if (last + 1 != next || next == 0) {
4033 struct mailimap_set_item * item;
4034 item = mailimap_set_item_new(first, last);
4035 mailimap_set_add(current_set, item);
4040 if (count >= IMAP_SET_MAX_COUNT) {
4041 ret_list = g_slist_append(ret_list,
4043 current_set = mailimap_set_new_empty();
4050 if (clist_count(current_set->set_list) > 0) {
4051 ret_list = g_slist_append(ret_list,
4055 g_slist_free(sorted_list);
4060 static GSList * imap_get_lep_set_from_msglist(MsgInfoList *msglist)
4062 MsgNumberList *numlist = NULL;
4066 for (cur = msglist; cur != NULL; cur = g_slist_next(cur)) {
4067 MsgInfo *msginfo = (MsgInfo *) cur->data;
4069 numlist = g_slist_append(numlist, GINT_TO_POINTER(msginfo->msgnum));
4071 seq_list = imap_get_lep_set_from_numlist(numlist);
4072 g_slist_free(numlist);
4077 static GSList * imap_uid_list_from_lep(clist * list)
4084 for(iter = clist_begin(list) ; iter != NULL ;
4085 iter = clist_next(iter)) {
4088 puid = clist_content(iter);
4089 result = g_slist_append(result, GINT_TO_POINTER(* puid));
4095 static GSList * imap_uid_list_from_lep_tab(carray * list)
4102 for(i = 0 ; i < carray_count(list) ; i ++) {
4105 puid = carray_get(list, i);
4106 result = g_slist_append(result, GINT_TO_POINTER(* puid));
4112 static MsgInfo *imap_envelope_from_lep(struct imap_fetch_env_info * info,
4115 MsgInfo *msginfo = NULL;
4118 MsgFlags flags = {0, 0};
4120 MSG_SET_TMP_FLAGS(flags, MSG_IMAP);
4121 if (folder_has_parent_of_type(item, F_QUEUE)) {
4122 MSG_SET_TMP_FLAGS(flags, MSG_QUEUED);
4123 } else if (folder_has_parent_of_type(item, F_DRAFT)) {
4124 MSG_SET_TMP_FLAGS(flags, MSG_DRAFT);
4126 flags.perm_flags = info->flags;
4130 msginfo = procheader_parse_str(info->headers, flags, FALSE, FALSE);
4133 msginfo->msgnum = uid;
4134 msginfo->size = size;
4140 static void imap_lep_set_free(GSList *seq_list)
4144 for(cur = seq_list ; cur != NULL ; cur = g_slist_next(cur)) {
4145 struct mailimap_set * imapset;
4147 imapset = cur->data;
4148 mailimap_set_free(imapset);
4150 g_slist_free(seq_list);
4153 static struct mailimap_flag_list * imap_flag_to_lep(IMAPFlags flags)
4155 struct mailimap_flag_list * flag_list;
4157 flag_list = mailimap_flag_list_new_empty();
4159 if (IMAP_IS_SEEN(flags))
4160 mailimap_flag_list_add(flag_list,
4161 mailimap_flag_new_seen());
4162 if (IMAP_IS_ANSWERED(flags))
4163 mailimap_flag_list_add(flag_list,
4164 mailimap_flag_new_answered());
4165 if (IMAP_IS_FLAGGED(flags))
4166 mailimap_flag_list_add(flag_list,
4167 mailimap_flag_new_flagged());
4168 if (IMAP_IS_DELETED(flags))
4169 mailimap_flag_list_add(flag_list,
4170 mailimap_flag_new_deleted());
4171 if (IMAP_IS_DRAFT(flags))
4172 mailimap_flag_list_add(flag_list,
4173 mailimap_flag_new_draft());
4178 guint imap_folder_get_refcnt(Folder *folder)
4180 return ((IMAPFolder *)folder)->refcnt;
4183 void imap_folder_ref(Folder *folder)
4185 ((IMAPFolder *)folder)->refcnt++;
4188 void imap_folder_unref(Folder *folder)
4190 if (((IMAPFolder *)folder)->refcnt > 0)
4191 ((IMAPFolder *)folder)->refcnt--;
4194 #else /* HAVE_LIBETPAN */
4196 static FolderClass imap_class;
4198 static Folder *imap_folder_new (const gchar *name,
4203 static gint imap_create_tree (Folder *folder)
4207 static FolderItem *imap_create_folder (Folder *folder,
4213 static gint imap_rename_folder (Folder *folder,
4220 FolderClass *imap_get_class(void)
4222 if (imap_class.idstr == NULL) {
4223 imap_class.type = F_IMAP;
4224 imap_class.idstr = "imap";
4225 imap_class.uistr = "IMAP4";
4227 imap_class.new_folder = imap_folder_new;
4228 imap_class.create_tree = imap_create_tree;
4229 imap_class.create_folder = imap_create_folder;
4230 imap_class.rename_folder = imap_rename_folder;
4231 /* nothing implemented */
4238 void imap_synchronise(FolderItem *item)
4240 imap_gtk_synchronise(item);