2 * Sylpheed -- a GTK+ based, lightweight, and fast e-mail client
3 * Copyright (C) 1999-2006 Hiroyuki Yamamoto and the Sylpheed-Claws team
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));
1103 new_uid = dest->last_num+1;
1105 if (last_uid < new_uid)
1108 g_unlink(real_file);
1112 statusbar_progress_all(0,0,0);
1113 statusbar_pop_all();
1122 static gint imap_do_copy_msgs(Folder *folder, FolderItem *dest,
1123 MsgInfoList *msglist, GRelation *relation)
1127 GSList *seq_list, *cur;
1129 IMAPSession *session;
1130 gint ok = IMAP_SUCCESS;
1131 GRelation *uid_mapping;
1134 g_return_val_if_fail(folder != NULL, -1);
1135 g_return_val_if_fail(dest != NULL, -1);
1136 g_return_val_if_fail(msglist != NULL, -1);
1138 session = imap_session_get(folder);
1144 msginfo = (MsgInfo *)msglist->data;
1146 src = msginfo->folder;
1148 g_warning("the src folder is identical to the dest.\n");
1153 ok = imap_select(session, IMAP_FOLDER(folder), msginfo->folder->path,
1154 NULL, NULL, NULL, NULL, FALSE);
1155 if (ok != IMAP_SUCCESS) {
1160 destdir = imap_get_real_path(IMAP_FOLDER(folder), dest->path);
1161 seq_list = imap_get_lep_set_from_msglist(msglist);
1162 uid_mapping = g_relation_new(2);
1163 g_relation_index(uid_mapping, 0, g_direct_hash, g_direct_equal);
1165 statusbar_print_all(_("Copying messages..."));
1166 for (cur = seq_list; cur != NULL; cur = g_slist_next(cur)) {
1167 struct mailimap_set * seq_set;
1168 seq_set = cur->data;
1170 debug_print("Copying messages from %s to %s ...\n",
1171 src->path, destdir);
1173 ok = imap_cmd_copy(session, seq_set, destdir, uid_mapping);
1174 if (ok != IMAP_SUCCESS) {
1175 g_relation_destroy(uid_mapping);
1176 imap_lep_set_free(seq_list);
1182 for (cur = msglist; cur != NULL; cur = g_slist_next(cur)) {
1183 MsgInfo *msginfo = (MsgInfo *)cur->data;
1186 tuples = g_relation_select(uid_mapping,
1187 GINT_TO_POINTER(msginfo->msgnum),
1189 if (tuples->len > 0) {
1190 gint num = GPOINTER_TO_INT(g_tuples_index(tuples, 0, 1));
1191 g_relation_insert(relation, msginfo,
1192 GPOINTER_TO_INT(num));
1196 g_relation_insert(relation, msginfo,
1197 GPOINTER_TO_INT(0));
1198 g_tuples_destroy(tuples);
1200 statusbar_pop_all();
1202 g_relation_destroy(uid_mapping);
1203 imap_lep_set_free(seq_list);
1207 IMAP_FOLDER_ITEM(dest)->lastuid = 0;
1208 IMAP_FOLDER_ITEM(dest)->uid_next = 0;
1209 g_slist_free(IMAP_FOLDER_ITEM(dest)->uid_list);
1210 IMAP_FOLDER_ITEM(dest)->uid_list = NULL;
1213 if (ok == IMAP_SUCCESS)
1219 static gint imap_copy_msg(Folder *folder, FolderItem *dest, MsgInfo *msginfo)
1223 g_return_val_if_fail(msginfo != NULL, -1);
1225 msglist.data = msginfo;
1226 msglist.next = NULL;
1228 return imap_copy_msgs(folder, dest, &msglist, NULL);
1231 static gint imap_copy_msgs(Folder *folder, FolderItem *dest,
1232 MsgInfoList *msglist, GRelation *relation)
1238 g_return_val_if_fail(folder != NULL, -1);
1239 g_return_val_if_fail(dest != NULL, -1);
1240 g_return_val_if_fail(msglist != NULL, -1);
1242 msginfo = (MsgInfo *)msglist->data;
1243 g_return_val_if_fail(msginfo->folder != NULL, -1);
1245 if (folder == msginfo->folder->folder &&
1246 !folder_has_parent_of_type(msginfo->folder, F_DRAFT) &&
1247 !folder_has_parent_of_type(msginfo->folder, F_QUEUE)) {
1248 ret = imap_do_copy_msgs(folder, dest, msglist, relation);
1252 file_list = procmsg_get_message_file_list(msglist);
1253 g_return_val_if_fail(file_list != NULL, -1);
1255 ret = imap_add_msgs(folder, dest, file_list, relation);
1257 procmsg_message_file_list_free(file_list);
1263 static gint imap_do_remove_msgs(Folder *folder, FolderItem *dest,
1264 MsgInfoList *msglist, GRelation *relation)
1267 GSList *numlist = NULL, *cur;
1269 IMAPSession *session;
1270 gint ok = IMAP_SUCCESS;
1271 GRelation *uid_mapping;
1273 g_return_val_if_fail(folder != NULL, -1);
1274 g_return_val_if_fail(dest != NULL, -1);
1275 g_return_val_if_fail(msglist != NULL, -1);
1277 session = imap_session_get(folder);
1282 msginfo = (MsgInfo *)msglist->data;
1284 ok = imap_select(session, IMAP_FOLDER(folder), msginfo->folder->path,
1285 NULL, NULL, NULL, NULL, FALSE);
1286 if (ok != IMAP_SUCCESS) {
1291 destdir = imap_get_real_path(IMAP_FOLDER(folder), dest->path);
1292 for (cur = msglist; cur; cur = cur->next) {
1293 msginfo = (MsgInfo *)cur->data;
1294 if (!MSG_IS_DELETED(msginfo->flags))
1295 numlist = g_slist_append(numlist, GINT_TO_POINTER(msginfo->msgnum));
1298 uid_mapping = g_relation_new(2);
1299 g_relation_index(uid_mapping, 0, g_direct_hash, g_direct_equal);
1301 ok = imap_set_message_flags
1302 (IMAP_SESSION(REMOTE_FOLDER(folder)->session),
1303 numlist, IMAP_FLAG_DELETED, TRUE);
1304 if (ok != IMAP_SUCCESS) {
1305 log_warning(_("can't set deleted flags\n"));
1309 ok = imap_cmd_expunge(session);
1310 if (ok != IMAP_SUCCESS) {
1311 log_warning(_("can't expunge\n"));
1316 g_relation_destroy(uid_mapping);
1317 g_slist_free(numlist);
1321 if (ok == IMAP_SUCCESS)
1327 static gint imap_remove_msgs(Folder *folder, FolderItem *dest,
1328 MsgInfoList *msglist, GRelation *relation)
1332 g_return_val_if_fail(folder != NULL, -1);
1333 g_return_val_if_fail(dest != NULL, -1);
1334 if (msglist == NULL)
1337 msginfo = (MsgInfo *)msglist->data;
1338 g_return_val_if_fail(msginfo->folder != NULL, -1);
1340 return imap_do_remove_msgs(folder, dest, msglist, relation);
1343 static gint imap_remove_all_msg(Folder *folder, FolderItem *item)
1345 GSList *list = folder_item_get_msg_list(item);
1346 gint res = imap_remove_msgs(folder, item, list, NULL);
1347 procmsg_msg_list_free(list);
1351 static gboolean imap_is_msg_changed(Folder *folder, FolderItem *item,
1354 /* TODO: properly implement this method */
1358 static gint imap_close(Folder *folder, FolderItem *item)
1363 static gint imap_scan_tree(Folder *folder)
1365 FolderItem *item = NULL;
1366 IMAPSession *session;
1367 gchar *root_folder = NULL;
1369 g_return_val_if_fail(folder != NULL, -1);
1370 g_return_val_if_fail(folder->account != NULL, -1);
1372 session = imap_session_get(folder);
1374 if (!folder->node) {
1375 folder_tree_destroy(folder);
1376 item = folder_item_new(folder, folder->name, NULL);
1377 item->folder = folder;
1378 folder->node = item->node = g_node_new(item);
1384 if (folder->account->imap_dir && *folder->account->imap_dir) {
1389 Xstrdup_a(root_folder, folder->account->imap_dir, {unlock_session();return -1;});
1390 extract_quote(root_folder, '"');
1391 subst_char(root_folder,
1392 imap_get_path_separator(IMAP_FOLDER(folder),
1395 strtailchomp(root_folder, '/');
1396 real_path = imap_get_real_path
1397 (IMAP_FOLDER(folder), root_folder);
1398 debug_print("IMAP root directory: %s\n", real_path);
1400 /* check if root directory exist */
1402 r = imap_threaded_list(session->folder, "", real_path,
1404 if ((r != MAILIMAP_NO_ERROR) || (clist_count(lep_list) == 0)) {
1405 if (!folder->node) {
1406 item = folder_item_new(folder, folder->name, NULL);
1407 item->folder = folder;
1408 folder->node = item->node = g_node_new(item);
1413 mailimap_list_result_free(lep_list);
1419 item = FOLDER_ITEM(folder->node->data);
1420 if (!item || ((item->path || root_folder) &&
1421 strcmp2(item->path, root_folder) != 0)) {
1422 folder_tree_destroy(folder);
1423 item = folder_item_new(folder, folder->name, root_folder);
1424 item->folder = folder;
1425 folder->node = item->node = g_node_new(item);
1428 imap_scan_tree_recursive(session, FOLDER_ITEM(folder->node->data));
1429 imap_create_missing_folders(folder);
1435 static gint imap_scan_tree_recursive(IMAPSession *session, FolderItem *item)
1438 IMAPFolder *imapfolder;
1439 FolderItem *new_item;
1440 GSList *item_list, *cur;
1443 gchar *wildcard_path;
1449 g_return_val_if_fail(item != NULL, -1);
1450 g_return_val_if_fail(item->folder != NULL, -1);
1451 g_return_val_if_fail(item->no_sub == FALSE, -1);
1453 folder = item->folder;
1454 imapfolder = IMAP_FOLDER(folder);
1456 separator = imap_get_path_separator(imapfolder, item->path);
1458 if (folder->ui_func)
1459 folder->ui_func(folder, item, folder->ui_func_data);
1462 wildcard[0] = separator;
1465 real_path = imap_get_real_path(imapfolder, item->path);
1469 real_path = g_strdup("");
1472 Xstrcat_a(wildcard_path, real_path, wildcard,
1473 {g_free(real_path); return IMAP_ERROR;});
1475 r = imap_threaded_list(folder, "", wildcard_path, &lep_list);
1476 if (r != MAILIMAP_NO_ERROR) {
1480 item_list = imap_list_from_lep(imapfolder,
1481 lep_list, real_path, FALSE);
1482 mailimap_list_result_free(lep_list);
1487 node = item->node->children;
1488 while (node != NULL) {
1489 FolderItem *old_item = FOLDER_ITEM(node->data);
1490 GNode *next = node->next;
1493 for (cur = item_list; cur != NULL; cur = cur->next) {
1494 FolderItem *cur_item = FOLDER_ITEM(cur->data);
1495 if (!strcmp2(old_item->path, cur_item->path)) {
1496 new_item = cur_item;
1501 debug_print("folder '%s' not found. removing...\n",
1503 folder_item_remove(old_item);
1505 old_item->no_sub = new_item->no_sub;
1506 old_item->no_select = new_item->no_select;
1507 if (old_item->no_sub == TRUE && node->children) {
1508 debug_print("folder '%s' doesn't have "
1509 "subfolders. removing...\n",
1511 folder_item_remove_children(old_item);
1518 for (cur = item_list; cur != NULL; cur = cur->next) {
1519 FolderItem *cur_item = FOLDER_ITEM(cur->data);
1522 for (node = item->node->children; node != NULL;
1523 node = node->next) {
1524 if (!strcmp2(FOLDER_ITEM(node->data)->path,
1526 new_item = FOLDER_ITEM(node->data);
1527 folder_item_destroy(cur_item);
1533 new_item = cur_item;
1534 debug_print("new folder '%s' found.\n", new_item->path);
1535 folder_item_append(item, new_item);
1538 if (!strcmp(new_item->path, "INBOX")) {
1539 new_item->stype = F_INBOX;
1540 folder->inbox = new_item;
1541 } else if (!folder_item_parent(item) || item->stype == F_INBOX) {
1544 base = g_path_get_basename(new_item->path);
1546 if (!folder->outbox && !g_ascii_strcasecmp(base, "Sent")) {
1547 new_item->stype = F_OUTBOX;
1548 folder->outbox = new_item;
1549 } else if (!folder->draft && !g_ascii_strcasecmp(base, "Drafts")) {
1550 new_item->stype = F_DRAFT;
1551 folder->draft = new_item;
1552 } else if (!folder->queue && !g_ascii_strcasecmp(base, "Queue")) {
1553 new_item->stype = F_QUEUE;
1554 folder->queue = new_item;
1555 } else if (!folder->trash && !g_ascii_strcasecmp(base, "Trash")) {
1556 new_item->stype = F_TRASH;
1557 folder->trash = new_item;
1562 if (new_item->no_sub == FALSE)
1563 imap_scan_tree_recursive(session, new_item);
1566 g_slist_free(item_list);
1568 return IMAP_SUCCESS;
1571 static gint imap_create_tree(Folder *folder)
1573 g_return_val_if_fail(folder != NULL, -1);
1574 g_return_val_if_fail(folder->node != NULL, -1);
1575 g_return_val_if_fail(folder->node->data != NULL, -1);
1576 g_return_val_if_fail(folder->account != NULL, -1);
1578 imap_scan_tree(folder);
1579 imap_create_missing_folders(folder);
1584 static void imap_create_missing_folders(Folder *folder)
1586 g_return_if_fail(folder != NULL);
1589 folder->inbox = imap_create_special_folder
1590 (folder, F_INBOX, "INBOX");
1592 folder->trash = imap_create_special_folder
1593 (folder, F_TRASH, "Trash");
1595 folder->queue = imap_create_special_folder
1596 (folder, F_QUEUE, "Queue");
1597 if (!folder->outbox)
1598 folder->outbox = imap_create_special_folder
1599 (folder, F_OUTBOX, "Sent");
1601 folder->draft = imap_create_special_folder
1602 (folder, F_DRAFT, "Drafts");
1605 static FolderItem *imap_create_special_folder(Folder *folder,
1606 SpecialFolderItemType stype,
1610 FolderItem *new_item;
1612 g_return_val_if_fail(folder != NULL, NULL);
1613 g_return_val_if_fail(folder->node != NULL, NULL);
1614 g_return_val_if_fail(folder->node->data != NULL, NULL);
1615 g_return_val_if_fail(folder->account != NULL, NULL);
1616 g_return_val_if_fail(name != NULL, NULL);
1618 item = FOLDER_ITEM(folder->node->data);
1619 new_item = imap_create_folder(folder, item, name);
1622 g_warning("Can't create '%s'\n", name);
1623 if (!folder->inbox) return NULL;
1625 new_item = imap_create_folder(folder, folder->inbox, name);
1627 g_warning("Can't create '%s' under INBOX\n", name);
1629 new_item->stype = stype;
1631 new_item->stype = stype;
1636 static gchar *imap_folder_get_path(Folder *folder)
1640 g_return_val_if_fail(folder != NULL, NULL);
1641 g_return_val_if_fail(folder->account != NULL, NULL);
1643 folder_path = g_strconcat(get_imap_cache_dir(),
1645 folder->account->recv_server,
1647 folder->account->userid,
1653 static gchar *imap_item_get_path(Folder *folder, FolderItem *item)
1655 gchar *folder_path, *path;
1657 g_return_val_if_fail(folder != NULL, NULL);
1658 g_return_val_if_fail(item != NULL, NULL);
1659 folder_path = imap_folder_get_path(folder);
1661 g_return_val_if_fail(folder_path != NULL, NULL);
1662 if (folder_path[0] == G_DIR_SEPARATOR) {
1664 path = g_strconcat(folder_path, G_DIR_SEPARATOR_S,
1667 path = g_strdup(folder_path);
1670 path = g_strconcat(get_home_dir(), G_DIR_SEPARATOR_S,
1671 folder_path, G_DIR_SEPARATOR_S,
1674 path = g_strconcat(get_home_dir(), G_DIR_SEPARATOR_S,
1677 g_free(folder_path);
1682 static FolderItem *imap_create_folder(Folder *folder, FolderItem *parent,
1685 gchar *dirpath, *imap_path;
1686 IMAPSession *session;
1687 FolderItem *new_item;
1692 gboolean no_select = FALSE, no_sub = FALSE;
1694 g_return_val_if_fail(folder != NULL, NULL);
1695 g_return_val_if_fail(folder->account != NULL, NULL);
1696 g_return_val_if_fail(parent != NULL, NULL);
1697 g_return_val_if_fail(name != NULL, NULL);
1699 session = imap_session_get(folder);
1705 if (!folder_item_parent(parent) && strcmp(name, "INBOX") == 0) {
1706 dirpath = g_strdup(name);
1707 }else if (parent->path)
1708 dirpath = g_strconcat(parent->path, "/", name, NULL);
1709 else if ((p = strchr(name, '/')) != NULL && *(p + 1) != '\0')
1710 dirpath = g_strdup(name);
1711 else if (folder->account->imap_dir && *folder->account->imap_dir) {
1714 Xstrdup_a(imap_dir, folder->account->imap_dir, {unlock_session();return NULL;});
1715 strtailchomp(imap_dir, '/');
1716 dirpath = g_strconcat(imap_dir, "/", name, NULL);
1718 dirpath = g_strdup(name);
1722 /* keep trailing directory separator to create a folder that contains
1724 imap_path = imap_utf8_to_modified_utf7(dirpath);
1726 strtailchomp(dirpath, '/');
1727 Xstrdup_a(new_name, name, {
1732 separator = imap_get_path_separator(IMAP_FOLDER(folder), imap_path);
1733 imap_path_separator_subst(imap_path, separator);
1734 /* remove trailing / for display */
1735 strtailchomp(new_name, '/');
1737 if (strcmp(dirpath, "INBOX") != 0) {
1739 gboolean exist = FALSE;
1743 argbuf = g_ptr_array_new();
1744 r = imap_threaded_list(folder, "", imap_path, &lep_list);
1745 if (r != MAILIMAP_NO_ERROR) {
1746 log_warning(_("can't create mailbox: LIST failed\n"));
1749 ptr_array_free_strings(argbuf);
1750 g_ptr_array_free(argbuf, TRUE);
1755 if (clist_count(lep_list) > 0)
1757 mailimap_list_result_free(lep_list);
1760 ok = imap_cmd_create(session, imap_path);
1761 if (ok != IMAP_SUCCESS) {
1762 log_warning(_("can't create mailbox\n"));
1768 r = imap_threaded_list(folder, "", imap_path, &lep_list);
1769 if (r == MAILIMAP_NO_ERROR) {
1770 GSList *item_list = imap_list_from_lep(IMAP_FOLDER(folder),
1771 lep_list, dirpath, TRUE);
1773 FolderItem *cur_item = FOLDER_ITEM(item_list->data);
1774 no_select = cur_item->no_select;
1775 no_sub = cur_item->no_sub;
1776 g_slist_free(item_list);
1778 mailimap_list_result_free(lep_list);
1785 /* just get flags */
1786 r = imap_threaded_list(folder, "", "INBOX", &lep_list);
1787 if (r == MAILIMAP_NO_ERROR) {
1788 GSList *item_list = imap_list_from_lep(IMAP_FOLDER(folder),
1789 lep_list, dirpath, TRUE);
1791 FolderItem *cur_item = FOLDER_ITEM(item_list->data);
1792 no_select = cur_item->no_select;
1793 no_sub = cur_item->no_sub;
1794 g_slist_free(item_list);
1796 mailimap_list_result_free(lep_list);
1800 new_item = folder_item_new(folder, new_name, dirpath);
1801 new_item->no_select = no_select;
1802 new_item->no_sub = no_sub;
1803 folder_item_append(parent, new_item);
1807 dirpath = folder_item_get_path(new_item);
1808 if (!is_dir_exist(dirpath))
1809 make_dir_hier(dirpath);
1815 static gint imap_rename_folder(Folder *folder, FolderItem *item,
1820 gchar *real_oldpath;
1821 gchar *real_newpath;
1823 gchar *old_cache_dir;
1824 gchar *new_cache_dir;
1825 IMAPSession *session;
1828 gint exists, recent, unseen;
1829 guint32 uid_validity;
1831 g_return_val_if_fail(folder != NULL, -1);
1832 g_return_val_if_fail(item != NULL, -1);
1833 g_return_val_if_fail(item->path != NULL, -1);
1834 g_return_val_if_fail(name != NULL, -1);
1836 session = imap_session_get(folder);
1842 if (strchr(name, imap_get_path_separator(IMAP_FOLDER(folder), item->path)) != NULL) {
1843 g_warning(_("New folder name must not contain the namespace "
1849 real_oldpath = imap_get_real_path(IMAP_FOLDER(folder), item->path);
1851 g_free(session->mbox);
1852 session->mbox = NULL;
1853 ok = imap_cmd_examine(session, "INBOX",
1854 &exists, &recent, &unseen, &uid_validity, FALSE);
1855 if (ok != IMAP_SUCCESS) {
1856 g_free(real_oldpath);
1861 separator = imap_get_path_separator(IMAP_FOLDER(folder), item->path);
1862 if (strchr(item->path, G_DIR_SEPARATOR)) {
1863 dirpath = g_path_get_dirname(item->path);
1864 newpath = g_strconcat(dirpath, G_DIR_SEPARATOR_S, name, NULL);
1867 newpath = g_strdup(name);
1869 real_newpath = imap_utf8_to_modified_utf7(newpath);
1870 imap_path_separator_subst(real_newpath, separator);
1872 ok = imap_cmd_rename(session, real_oldpath, real_newpath);
1873 if (ok != IMAP_SUCCESS) {
1874 log_warning(_("can't rename mailbox: %s to %s\n"),
1875 real_oldpath, real_newpath);
1876 g_free(real_oldpath);
1878 g_free(real_newpath);
1884 item->name = g_strdup(name);
1886 old_cache_dir = folder_item_get_path(item);
1888 paths[0] = g_strdup(item->path);
1890 g_node_traverse(item->node, G_PRE_ORDER, G_TRAVERSE_ALL, -1,
1891 imap_rename_folder_func, paths);
1893 if (is_dir_exist(old_cache_dir)) {
1894 new_cache_dir = folder_item_get_path(item);
1895 if (rename(old_cache_dir, new_cache_dir) < 0) {
1896 FILE_OP_ERROR(old_cache_dir, "rename");
1898 g_free(new_cache_dir);
1901 g_free(old_cache_dir);
1904 g_free(real_oldpath);
1905 g_free(real_newpath);
1910 static gint imap_remove_folder_real(Folder *folder, FolderItem *item)
1913 IMAPSession *session;
1917 g_return_val_if_fail(folder != NULL, -1);
1918 g_return_val_if_fail(item != NULL, -1);
1919 g_return_val_if_fail(item->path != NULL, -1);
1921 session = imap_session_get(folder);
1926 path = imap_get_real_path(IMAP_FOLDER(folder), item->path);
1928 ok = imap_cmd_delete(session, path);
1929 if (ok != IMAP_SUCCESS) {
1930 gchar *tmp = g_strdup_printf("%s%c", path,
1931 imap_get_path_separator(IMAP_FOLDER(folder), path));
1934 ok = imap_cmd_delete(session, path);
1937 if (ok != IMAP_SUCCESS) {
1938 log_warning(_("can't delete mailbox\n"));
1945 cache_dir = folder_item_get_path(item);
1946 if (is_dir_exist(cache_dir) && remove_dir_recursive(cache_dir) < 0)
1947 g_warning("can't remove directory '%s'\n", cache_dir);
1949 folder_item_remove(item);
1954 static gint imap_remove_folder(Folder *folder, FolderItem *item)
1958 g_return_val_if_fail(item != NULL, -1);
1959 g_return_val_if_fail(item->folder != NULL, -1);
1960 g_return_val_if_fail(item->node != NULL, -1);
1962 node = item->node->children;
1963 while (node != NULL) {
1965 if (imap_remove_folder(folder, FOLDER_ITEM(node->data)) < 0)
1969 debug_print("IMAP removing %s\n", item->path);
1971 if (imap_remove_all_msg(folder, item) < 0)
1973 return imap_remove_folder_real(folder, item);
1976 typedef struct _uncached_data {
1977 IMAPSession *session;
1979 MsgNumberList *numlist;
1985 static void *imap_get_uncached_messages_thread(void *data)
1987 uncached_data *stuff = (uncached_data *)data;
1988 IMAPSession *session = stuff->session;
1989 FolderItem *item = stuff->item;
1990 MsgNumberList *numlist = stuff->numlist;
1992 GSList *newlist = NULL;
1993 GSList *llast = NULL;
1994 GSList *seq_list, *cur;
1996 debug_print("uncached_messages\n");
1998 if (session == NULL || item == NULL || item->folder == NULL
1999 || FOLDER_CLASS(item->folder) != &imap_class) {
2004 seq_list = imap_get_lep_set_from_numlist(numlist);
2005 debug_print("get msgs info\n");
2006 for (cur = seq_list; cur != NULL; cur = g_slist_next(cur)) {
2007 struct mailimap_set * imapset;
2013 imapset = cur->data;
2015 r = imap_threaded_fetch_env(session->folder,
2016 imapset, &env_list);
2017 if (r != MAILIMAP_NO_ERROR)
2021 for(i = 0 ; i < carray_count(env_list) ; i ++) {
2022 struct imap_fetch_env_info * info;
2025 info = carray_get(env_list, i);
2026 msginfo = imap_envelope_from_lep(info, item);
2027 if (msginfo == NULL)
2029 msginfo->folder = item;
2031 llast = newlist = g_slist_append(newlist, msginfo);
2033 llast = g_slist_append(llast, msginfo);
2034 llast = llast->next;
2039 imap_fetch_env_free(env_list);
2042 for (cur = seq_list; cur != NULL; cur = g_slist_next(cur)) {
2043 struct mailimap_set * imapset;
2045 imapset = cur->data;
2046 mailimap_set_free(imapset);
2049 session_set_access_time(SESSION(session));
2054 #define MAX_MSG_NUM 50
2056 static GSList *imap_get_uncached_messages(IMAPSession *session,
2058 MsgNumberList *numlist)
2060 GSList *result = NULL;
2062 uncached_data *data = g_new0(uncached_data, 1);
2067 data->total = g_slist_length(numlist);
2068 debug_print("messages list : %i\n", data->total);
2070 while (cur != NULL) {
2071 GSList * partial_result;
2079 while (count < MAX_MSG_NUM) {
2084 if (newlist == NULL)
2085 llast = newlist = g_slist_append(newlist, p);
2087 llast = g_slist_append(llast, p);
2088 llast = llast->next;
2098 data->session = session;
2100 data->numlist = newlist;
2103 if (prefs_common.work_offline && !inc_offline_should_override()) {
2109 (GSList *)imap_get_uncached_messages_thread(data);
2111 statusbar_progress_all(data->cur,data->total, 1);
2113 g_slist_free(newlist);
2115 result = g_slist_concat(result, partial_result);
2119 statusbar_progress_all(0,0,0);
2120 statusbar_pop_all();
2125 static void imap_delete_all_cached_messages(FolderItem *item)
2129 g_return_if_fail(item != NULL);
2130 g_return_if_fail(item->folder != NULL);
2131 g_return_if_fail(FOLDER_CLASS(item->folder) == &imap_class);
2133 debug_print("Deleting all cached messages...\n");
2135 dir = folder_item_get_path(item);
2136 if (is_dir_exist(dir))
2137 remove_all_numbered_files(dir);
2140 debug_print("done.\n");
2143 static IMAPNameSpace *imap_find_namespace_from_list(GList *ns_list,
2146 IMAPNameSpace *namespace = NULL;
2147 gchar *tmp_path, *name;
2149 if (!path) path = "";
2151 for (; ns_list != NULL; ns_list = ns_list->next) {
2152 IMAPNameSpace *tmp_ns = ns_list->data;
2154 Xstrcat_a(tmp_path, path, "/", return namespace);
2155 Xstrdup_a(name, tmp_ns->name, return namespace);
2156 if (tmp_ns->separator && tmp_ns->separator != '/') {
2157 subst_char(tmp_path, tmp_ns->separator, '/');
2158 subst_char(name, tmp_ns->separator, '/');
2160 if (strncmp(tmp_path, name, strlen(name)) == 0)
2167 static IMAPNameSpace *imap_find_namespace(IMAPFolder *folder,
2170 IMAPNameSpace *namespace;
2172 g_return_val_if_fail(folder != NULL, NULL);
2174 namespace = imap_find_namespace_from_list(folder->ns_personal, path);
2175 if (namespace) return namespace;
2176 namespace = imap_find_namespace_from_list(folder->ns_others, path);
2177 if (namespace) return namespace;
2178 namespace = imap_find_namespace_from_list(folder->ns_shared, path);
2179 if (namespace) return namespace;
2185 static gchar imap_get_path_separator(IMAPFolder *folder, const gchar *path)
2187 IMAPNameSpace *namespace;
2188 gchar separator = '/';
2190 if (folder->last_seen_separator == 0) {
2192 int r = imap_threaded_list((Folder *)folder, "", "", &lep_list);
2193 if (r != MAILIMAP_NO_ERROR) {
2194 log_warning(_("LIST failed\n"));
2198 if (clist_count(lep_list) > 0) {
2199 clistiter * iter = clist_begin(lep_list);
2200 struct mailimap_mailbox_list * mb;
2201 mb = clist_content(iter);
2203 folder->last_seen_separator = mb->mb_delimiter;
2204 debug_print("got separator: %c\n", folder->last_seen_separator);
2206 mailimap_list_result_free(lep_list);
2209 if (folder->last_seen_separator != 0) {
2210 debug_print("using separator: %c\n", folder->last_seen_separator);
2211 return folder->last_seen_separator;
2214 namespace = imap_find_namespace(folder, path);
2215 if (namespace && namespace->separator)
2216 separator = namespace->separator;
2221 static gchar *imap_get_real_path(IMAPFolder *folder, const gchar *path)
2226 g_return_val_if_fail(folder != NULL, NULL);
2227 g_return_val_if_fail(path != NULL, NULL);
2229 real_path = imap_utf8_to_modified_utf7(path);
2230 separator = imap_get_path_separator(folder, path);
2231 imap_path_separator_subst(real_path, separator);
2236 static gint imap_set_message_flags(IMAPSession *session,
2237 MsgNumberList *numlist,
2245 seq_list = imap_get_lep_set_from_numlist(numlist);
2247 for(cur = seq_list ; cur != NULL ; cur = g_slist_next(cur)) {
2248 struct mailimap_set * imapset;
2250 imapset = cur->data;
2252 ok = imap_cmd_store(session, imapset,
2256 imap_lep_set_free(seq_list);
2258 return IMAP_SUCCESS;
2261 typedef struct _select_data {
2262 IMAPSession *session;
2267 guint32 *uid_validity;
2271 static gint imap_select(IMAPSession *session, IMAPFolder *folder,
2273 gint *exists, gint *recent, gint *unseen,
2274 guint32 *uid_validity, gboolean block)
2278 gint exists_, recent_, unseen_;
2279 guint32 uid_validity_;
2281 if (!exists && !recent && !unseen && !uid_validity) {
2282 if (session->mbox && strcmp(session->mbox, path) == 0)
2283 return IMAP_SUCCESS;
2292 uid_validity = &uid_validity_;
2294 g_free(session->mbox);
2295 session->mbox = NULL;
2297 real_path = imap_get_real_path(folder, path);
2299 ok = imap_cmd_select(session, real_path,
2300 exists, recent, unseen, uid_validity, block);
2301 if (ok != IMAP_SUCCESS)
2302 log_warning(_("can't select folder: %s\n"), real_path);
2304 session->mbox = g_strdup(path);
2305 session->folder_content_changed = FALSE;
2312 static gint imap_status(IMAPSession *session, IMAPFolder *folder,
2313 const gchar *path, IMAPFolderItem *item,
2315 guint32 *uid_next, guint32 *uid_validity,
2316 gint *unseen, gboolean block)
2320 struct mailimap_mailbox_data_status * data_status;
2325 real_path = imap_get_real_path(folder, path);
2339 r = imap_threaded_status(FOLDER(folder), real_path,
2340 &data_status, mask);
2343 if (r != MAILIMAP_NO_ERROR) {
2344 debug_print("status err %d\n", r);
2348 if (data_status->st_info_list == NULL) {
2349 mailimap_mailbox_data_status_free(data_status);
2350 debug_print("status->st_info_list == NULL\n");
2355 for(iter = clist_begin(data_status->st_info_list) ; iter != NULL ;
2356 iter = clist_next(iter)) {
2357 struct mailimap_status_info * info;
2359 info = clist_content(iter);
2360 switch (info->st_att) {
2361 case MAILIMAP_STATUS_ATT_MESSAGES:
2362 * messages = info->st_value;
2363 got_values |= 1 << 0;
2366 case MAILIMAP_STATUS_ATT_UIDNEXT:
2367 * uid_next = info->st_value;
2368 got_values |= 1 << 2;
2371 case MAILIMAP_STATUS_ATT_UIDVALIDITY:
2372 * uid_validity = info->st_value;
2373 got_values |= 1 << 3;
2376 case MAILIMAP_STATUS_ATT_UNSEEN:
2377 * unseen = info->st_value;
2378 got_values |= 1 << 4;
2382 mailimap_mailbox_data_status_free(data_status);
2384 if (got_values != mask) {
2385 debug_print("status: incomplete values received (%d)\n", got_values);
2388 return IMAP_SUCCESS;
2391 static void imap_free_capabilities(IMAPSession *session)
2393 slist_free_strings(session->capability);
2394 g_slist_free(session->capability);
2395 session->capability = NULL;
2398 /* low-level IMAP4rev1 commands */
2401 static gint imap_cmd_authenticate(IMAPSession *session, const gchar *user,
2402 const gchar *pass, IMAPAuthType type)
2409 gchar hexdigest[33];
2413 auth_type = "CRAM-MD5";
2415 imap_gen_send(session, "AUTHENTICATE %s", auth_type);
2416 ok = imap_gen_recv(session, &buf);
2417 if (ok != IMAP_SUCCESS || buf[0] != '+' || buf[1] != ' ') {
2422 challenge = g_malloc(strlen(buf + 2) + 1);
2423 challenge_len = base64_decode(challenge, buf + 2, -1);
2424 challenge[challenge_len] = '\0';
2427 md5_hex_hmac(hexdigest, challenge, challenge_len, pass, strlen(pass));
2430 response = g_strdup_printf("%s %s", user, hexdigest);
2431 response64 = g_malloc((strlen(response) + 3) * 2 + 1);
2432 base64_encode(response64, response, strlen(response));
2435 sock_puts(SESSION(session)->sock, response64);
2436 ok = imap_cmd_ok(session, NULL);
2437 if (ok != IMAP_SUCCESS)
2438 log_warning(_("IMAP4 authentication failed.\n"));
2444 static gint imap_cmd_login(IMAPSession *session,
2445 const gchar *user, const gchar *pass,
2451 log_print("IMAP4> Logging %s to %s using %s\n",
2453 SESSION(session)->server,
2455 r = imap_threaded_login(session->folder, user, pass, type);
2456 if (r != MAILIMAP_NO_ERROR) {
2457 log_error("IMAP4< Error logging in to %s\n",
2458 SESSION(session)->server);
2466 static gint imap_cmd_logout(IMAPSession *session)
2468 imap_threaded_disconnect(session->folder);
2470 return IMAP_SUCCESS;
2473 static gint imap_cmd_noop(IMAPSession *session)
2476 unsigned int exists;
2478 r = imap_threaded_noop(session->folder, &exists);
2479 if (r != MAILIMAP_NO_ERROR) {
2480 debug_print("noop err %d\n", r);
2483 session->exists = exists;
2484 session_set_access_time(SESSION(session));
2486 return IMAP_SUCCESS;
2490 static gint imap_cmd_starttls(IMAPSession *session)
2494 r = imap_threaded_starttls(session->folder);
2495 if (r != MAILIMAP_NO_ERROR) {
2496 debug_print("starttls err %d\n", r);
2499 return IMAP_SUCCESS;
2503 static gint imap_cmd_select(IMAPSession *session, const gchar *folder,
2504 gint *exists, gint *recent, gint *unseen,
2505 guint32 *uid_validity, gboolean block)
2509 r = imap_threaded_select(session->folder, folder,
2510 exists, recent, unseen, uid_validity);
2511 if (r != MAILIMAP_NO_ERROR) {
2512 debug_print("select err %d\n", r);
2515 return IMAP_SUCCESS;
2518 static gint imap_cmd_examine(IMAPSession *session, const gchar *folder,
2519 gint *exists, gint *recent, gint *unseen,
2520 guint32 *uid_validity, gboolean block)
2524 r = imap_threaded_examine(session->folder, folder,
2525 exists, recent, unseen, uid_validity);
2526 if (r != MAILIMAP_NO_ERROR) {
2527 debug_print("examine err %d\n", r);
2531 return IMAP_SUCCESS;
2534 static gint imap_cmd_create(IMAPSession *session, const gchar *folder)
2538 r = imap_threaded_create(session->folder, folder);
2539 if (r != MAILIMAP_NO_ERROR) {
2544 return IMAP_SUCCESS;
2547 static gint imap_cmd_rename(IMAPSession *session, const gchar *old_folder,
2548 const gchar *new_folder)
2552 r = imap_threaded_rename(session->folder, old_folder,
2554 if (r != MAILIMAP_NO_ERROR) {
2559 return IMAP_SUCCESS;
2562 static gint imap_cmd_delete(IMAPSession *session, const gchar *folder)
2567 r = imap_threaded_delete(session->folder, folder);
2568 if (r != MAILIMAP_NO_ERROR) {
2573 return IMAP_SUCCESS;
2576 typedef struct _fetch_data {
2577 IMAPSession *session;
2579 const gchar *filename;
2585 static void *imap_cmd_fetch_thread(void *data)
2587 fetch_data *stuff = (fetch_data *)data;
2588 IMAPSession *session = stuff->session;
2589 guint32 uid = stuff->uid;
2590 const gchar *filename = stuff->filename;
2594 r = imap_threaded_fetch_content(session->folder,
2598 r = imap_threaded_fetch_content(session->folder,
2601 if (r != MAILIMAP_NO_ERROR) {
2602 debug_print("fetch err %d\n", r);
2603 return GINT_TO_POINTER(IMAP_ERROR);
2605 return GINT_TO_POINTER(IMAP_SUCCESS);
2608 static gint imap_cmd_fetch(IMAPSession *session, guint32 uid,
2609 const gchar *filename, gboolean headers,
2612 fetch_data *data = g_new0(fetch_data, 1);
2615 data->session = session;
2617 data->filename = filename;
2618 data->headers = headers;
2621 if (prefs_common.work_offline && !inc_offline_should_override()) {
2625 statusbar_print_all(_("Fetching message..."));
2626 result = GPOINTER_TO_INT(imap_cmd_fetch_thread(data));
2627 statusbar_pop_all();
2633 static gint imap_cmd_append(IMAPSession *session, const gchar *destfolder,
2634 const gchar *file, IMAPFlags flags,
2637 struct mailimap_flag_list * flag_list;
2640 g_return_val_if_fail(file != NULL, IMAP_ERROR);
2642 flag_list = imap_flag_to_lep(flags);
2643 r = imap_threaded_append(session->folder, destfolder,
2645 mailimap_flag_list_free(flag_list);
2646 if (new_uid != NULL)
2649 if (r != MAILIMAP_NO_ERROR) {
2650 debug_print("append err %d\n", r);
2653 return IMAP_SUCCESS;
2656 static gint imap_cmd_copy(IMAPSession *session, struct mailimap_set * set,
2657 const gchar *destfolder, GRelation *uid_mapping)
2661 g_return_val_if_fail(session != NULL, IMAP_ERROR);
2662 g_return_val_if_fail(set != NULL, IMAP_ERROR);
2663 g_return_val_if_fail(destfolder != NULL, IMAP_ERROR);
2665 r = imap_threaded_copy(session->folder, set, destfolder);
2666 if (r != MAILIMAP_NO_ERROR) {
2671 return IMAP_SUCCESS;
2674 static gint imap_cmd_store(IMAPSession *session, struct mailimap_set * set,
2675 IMAPFlags flags, int do_add)
2678 struct mailimap_flag_list * flag_list;
2679 struct mailimap_store_att_flags * store_att_flags;
2681 flag_list = imap_flag_to_lep(flags);
2685 mailimap_store_att_flags_new_add_flags_silent(flag_list);
2688 mailimap_store_att_flags_new_remove_flags_silent(flag_list);
2690 r = imap_threaded_store(session->folder, set, store_att_flags);
2691 mailimap_store_att_flags_free(store_att_flags);
2692 if (r != MAILIMAP_NO_ERROR) {
2697 return IMAP_SUCCESS;
2700 static gint imap_cmd_expunge(IMAPSession *session)
2704 if (prefs_common.work_offline && !inc_offline_should_override()) {
2708 r = imap_threaded_expunge(session->folder);
2709 if (r != MAILIMAP_NO_ERROR) {
2714 return IMAP_SUCCESS;
2717 static void imap_path_separator_subst(gchar *str, gchar separator)
2720 gboolean in_escape = FALSE;
2722 if (!separator || separator == '/') return;
2724 for (p = str; *p != '\0'; p++) {
2725 if (*p == '/' && !in_escape)
2727 else if (*p == '&' && *(p + 1) != '-' && !in_escape)
2729 else if (*p == '-' && in_escape)
2734 static gchar *imap_modified_utf7_to_utf8(const gchar *mutf7_str)
2736 static iconv_t cd = (iconv_t)-1;
2737 static gboolean iconv_ok = TRUE;
2740 size_t norm_utf7_len;
2742 gchar *to_str, *to_p;
2744 gboolean in_escape = FALSE;
2746 if (!iconv_ok) return g_strdup(mutf7_str);
2748 if (cd == (iconv_t)-1) {
2749 cd = iconv_open(CS_INTERNAL, CS_UTF_7);
2750 if (cd == (iconv_t)-1) {
2751 g_warning("iconv cannot convert UTF-7 to %s\n",
2754 return g_strdup(mutf7_str);
2758 /* modified UTF-7 to normal UTF-7 conversion */
2759 norm_utf7 = g_string_new(NULL);
2761 for (p = mutf7_str; *p != '\0'; p++) {
2762 /* replace: '&' -> '+',
2764 escaped ',' -> '/' */
2765 if (!in_escape && *p == '&') {
2766 if (*(p + 1) != '-') {
2767 g_string_append_c(norm_utf7, '+');
2770 g_string_append_c(norm_utf7, '&');
2773 } else if (in_escape && *p == ',') {
2774 g_string_append_c(norm_utf7, '/');
2775 } else if (in_escape && *p == '-') {
2776 g_string_append_c(norm_utf7, '-');
2779 g_string_append_c(norm_utf7, *p);
2783 norm_utf7_p = norm_utf7->str;
2784 norm_utf7_len = norm_utf7->len;
2785 to_len = strlen(mutf7_str) * 5;
2786 to_p = to_str = g_malloc(to_len + 1);
2788 if (iconv(cd, (ICONV_CONST gchar **)&norm_utf7_p, &norm_utf7_len,
2789 &to_p, &to_len) == -1) {
2790 g_warning(_("iconv cannot convert UTF-7 to %s\n"),
2791 conv_get_locale_charset_str());
2792 g_string_free(norm_utf7, TRUE);
2794 return g_strdup(mutf7_str);
2797 /* second iconv() call for flushing */
2798 iconv(cd, NULL, NULL, &to_p, &to_len);
2799 g_string_free(norm_utf7, TRUE);
2805 static gchar *imap_utf8_to_modified_utf7(const gchar *from)
2807 static iconv_t cd = (iconv_t)-1;
2808 static gboolean iconv_ok = TRUE;
2809 gchar *norm_utf7, *norm_utf7_p;
2810 size_t from_len, norm_utf7_len;
2812 gchar *from_tmp, *to, *p;
2813 gboolean in_escape = FALSE;
2815 if (!iconv_ok) return g_strdup(from);
2817 if (cd == (iconv_t)-1) {
2818 cd = iconv_open(CS_UTF_7, CS_INTERNAL);
2819 if (cd == (iconv_t)-1) {
2820 g_warning(_("iconv cannot convert %s to UTF-7\n"),
2823 return g_strdup(from);
2827 /* UTF-8 to normal UTF-7 conversion */
2828 Xstrdup_a(from_tmp, from, return g_strdup(from));
2829 from_len = strlen(from);
2830 norm_utf7_len = from_len * 5;
2831 Xalloca(norm_utf7, norm_utf7_len + 1, return g_strdup(from));
2832 norm_utf7_p = norm_utf7;
2834 #define IS_PRINT(ch) (isprint(ch) && IS_ASCII(ch))
2836 while (from_len > 0) {
2837 if (*from_tmp == '+') {
2838 *norm_utf7_p++ = '+';
2839 *norm_utf7_p++ = '-';
2843 } else if (IS_PRINT(*(guchar *)from_tmp)) {
2844 /* printable ascii char */
2845 *norm_utf7_p = *from_tmp;
2851 size_t conv_len = 0;
2853 /* unprintable char: convert to UTF-7 */
2855 while (!IS_PRINT(*(guchar *)p) && conv_len < from_len) {
2856 conv_len += g_utf8_skip[*(guchar *)p];
2857 p += g_utf8_skip[*(guchar *)p];
2860 from_len -= conv_len;
2861 if (iconv(cd, (ICONV_CONST gchar **)&from_tmp,
2863 &norm_utf7_p, &norm_utf7_len) == -1) {
2864 g_warning(_("iconv cannot convert UTF-8 to UTF-7\n"));
2865 return g_strdup(from);
2868 /* second iconv() call for flushing */
2869 iconv(cd, NULL, NULL, &norm_utf7_p, &norm_utf7_len);
2875 *norm_utf7_p = '\0';
2876 to_str = g_string_new(NULL);
2877 for (p = norm_utf7; p < norm_utf7_p; p++) {
2878 /* replace: '&' -> "&-",
2881 BASE64 '/' -> ',' */
2882 if (!in_escape && *p == '&') {
2883 g_string_append(to_str, "&-");
2884 } else if (!in_escape && *p == '+') {
2885 if (*(p + 1) == '-') {
2886 g_string_append_c(to_str, '+');
2889 g_string_append_c(to_str, '&');
2892 } else if (in_escape && *p == '/') {
2893 g_string_append_c(to_str, ',');
2894 } else if (in_escape && *p == '-') {
2895 g_string_append_c(to_str, '-');
2898 g_string_append_c(to_str, *p);
2904 g_string_append_c(to_str, '-');
2908 g_string_free(to_str, FALSE);
2913 static gboolean imap_rename_folder_func(GNode *node, gpointer data)
2915 FolderItem *item = node->data;
2916 gchar **paths = data;
2917 const gchar *oldpath = paths[0];
2918 const gchar *newpath = paths[1];
2920 gchar *new_itempath;
2923 oldpathlen = strlen(oldpath);
2924 if (strncmp(oldpath, item->path, oldpathlen) != 0) {
2925 g_warning("path doesn't match: %s, %s\n", oldpath, item->path);
2929 base = item->path + oldpathlen;
2930 while (*base == G_DIR_SEPARATOR) base++;
2932 new_itempath = g_strdup(newpath);
2934 new_itempath = g_strconcat(newpath, G_DIR_SEPARATOR_S, base,
2937 item->path = new_itempath;
2942 typedef struct _get_list_uid_data {
2944 IMAPSession *session;
2945 IMAPFolderItem *item;
2946 GSList **msgnum_list;
2948 } get_list_uid_data;
2950 static void *get_list_of_uids_thread(void *data)
2952 get_list_uid_data *stuff = (get_list_uid_data *)data;
2953 Folder *folder = stuff->folder;
2954 IMAPFolderItem *item = stuff->item;
2955 GSList **msgnum_list = stuff->msgnum_list;
2956 gint ok, nummsgs = 0, lastuid_old;
2957 IMAPSession *session;
2958 GSList *uidlist, *elem;
2959 struct mailimap_set * set;
2960 clist * lep_uidlist;
2963 session = stuff->session;
2964 if (session == NULL) {
2966 return GINT_TO_POINTER(-1);
2968 /* no session locking here, it's already locked by caller */
2969 ok = imap_select(session, IMAP_FOLDER(folder), item->item.path,
2970 NULL, NULL, NULL, NULL, TRUE);
2971 if (ok != IMAP_SUCCESS) {
2973 return GINT_TO_POINTER(-1);
2978 set = mailimap_set_new_interval(item->lastuid + 1, 0);
2980 r = imap_threaded_search(folder, IMAP_SEARCH_TYPE_SIMPLE, set,
2982 mailimap_set_free(set);
2984 if (r == MAILIMAP_NO_ERROR) {
2985 GSList * fetchuid_list;
2988 imap_uid_list_from_lep(lep_uidlist);
2989 mailimap_search_result_free(lep_uidlist);
2991 uidlist = g_slist_concat(fetchuid_list, uidlist);
2994 GSList * fetchuid_list;
2995 carray * lep_uidtab;
2997 r = imap_threaded_fetch_uid(folder, item->lastuid + 1,
2999 if (r == MAILIMAP_NO_ERROR) {
3001 imap_uid_list_from_lep_tab(lep_uidtab);
3002 imap_fetch_uid_list_free(lep_uidtab);
3003 uidlist = g_slist_concat(fetchuid_list, uidlist);
3007 lastuid_old = item->lastuid;
3008 *msgnum_list = g_slist_copy(item->uid_list);
3009 nummsgs = g_slist_length(*msgnum_list);
3010 debug_print("Got %d uids from cache\n", g_slist_length(item->uid_list));
3012 for (elem = uidlist; elem != NULL; elem = g_slist_next(elem)) {
3015 msgnum = GPOINTER_TO_INT(elem->data);
3016 if (msgnum > lastuid_old) {
3017 *msgnum_list = g_slist_prepend(*msgnum_list, GINT_TO_POINTER(msgnum));
3018 item->uid_list = g_slist_prepend(item->uid_list, GINT_TO_POINTER(msgnum));
3021 if(msgnum > item->lastuid)
3022 item->lastuid = msgnum;
3025 g_slist_free(uidlist);
3027 return GINT_TO_POINTER(nummsgs);
3030 static gint get_list_of_uids(IMAPSession *session, Folder *folder, IMAPFolderItem *item, GSList **msgnum_list)
3033 get_list_uid_data *data = g_new0(get_list_uid_data, 1);
3035 data->folder = folder;
3037 data->msgnum_list = msgnum_list;
3038 data->session = session;
3039 if (prefs_common.work_offline && !inc_offline_should_override()) {
3044 result = GPOINTER_TO_INT(get_list_of_uids_thread(data));
3050 gint imap_get_num_list(Folder *folder, FolderItem *_item, GSList **msgnum_list, gboolean *old_uids_valid)
3052 IMAPFolderItem *item = (IMAPFolderItem *)_item;
3053 IMAPSession *session;
3054 gint ok, nummsgs = 0, exists, uid_val, uid_next;
3055 GSList *uidlist = NULL;
3057 gboolean selected_folder;
3059 debug_print("get_num_list\n");
3061 g_return_val_if_fail(folder != NULL, -1);
3062 g_return_val_if_fail(item != NULL, -1);
3063 g_return_val_if_fail(item->item.path != NULL, -1);
3064 g_return_val_if_fail(FOLDER_CLASS(folder) == &imap_class, -1);
3065 g_return_val_if_fail(folder->account != NULL, -1);
3067 session = imap_session_get(folder);
3068 g_return_val_if_fail(session != NULL, -1);
3070 statusbar_print_all("Scanning %s...\n", FOLDER_ITEM(item)->path
3071 ? FOLDER_ITEM(item)->path:"");
3073 selected_folder = (session->mbox != NULL) &&
3074 (!strcmp(session->mbox, item->item.path));
3075 if (selected_folder && time(NULL) - item->use_cache < 2) {
3076 ok = imap_cmd_noop(session);
3077 if (ok != IMAP_SUCCESS) {
3078 debug_print("disconnected!\n");
3079 session = imap_reconnect_if_possible(folder, session);
3080 if (session == NULL) {
3081 statusbar_pop_all();
3086 exists = session->exists;
3088 *old_uids_valid = TRUE;
3090 if (item->use_cache && time(NULL) - item->use_cache < 2) {
3091 exists = item->c_messages;
3092 uid_next = item->c_uid_next;
3093 uid_val = item->c_uid_validity;
3095 debug_print("using cache %d %d %d\n", exists, uid_next, uid_val);
3097 ok = imap_status(session, IMAP_FOLDER(folder), item->item.path, item,
3098 &exists, &uid_next, &uid_val, NULL, FALSE);
3100 item->item.last_num = uid_next - 1;
3102 item->use_cache = (time_t)0;
3103 if (ok != IMAP_SUCCESS) {
3104 statusbar_pop_all();
3108 if(item->item.mtime == uid_val)
3109 *old_uids_valid = TRUE;
3111 *old_uids_valid = FALSE;
3113 debug_print("Freeing imap uid cache\n");
3115 g_slist_free(item->uid_list);
3116 item->uid_list = NULL;
3118 item->item.mtime = uid_val;
3120 imap_delete_all_cached_messages((FolderItem *)item);
3124 /* If old uid_next matches new uid_next we can be sure no message
3125 was added to the folder */
3126 debug_print("uid_next is %d and item->uid_next %d \n",
3127 uid_next, item->uid_next);
3128 if (uid_next == item->uid_next) {
3129 nummsgs = g_slist_length(item->uid_list);
3131 /* If number of messages is still the same we
3132 know our caches message numbers are still valid,
3133 otherwise if the number of messages has decrease
3134 we discard our cache to start a new scan to find
3135 out which numbers have been removed */
3136 if (exists == nummsgs) {
3137 debug_print("exists == nummsgs\n");
3138 *msgnum_list = g_slist_copy(item->uid_list);
3139 statusbar_pop_all();
3142 } else if (exists < nummsgs) {
3143 debug_print("Freeing imap uid cache");
3145 g_slist_free(item->uid_list);
3146 item->uid_list = NULL;
3151 *msgnum_list = NULL;
3152 statusbar_pop_all();
3157 nummsgs = get_list_of_uids(session, folder, item, &uidlist);
3160 statusbar_pop_all();
3165 if (nummsgs != exists) {
3166 /* Cache contains more messages then folder, we have cached
3167 an old UID of a message that was removed and new messages
3168 have been added too, otherwise the uid_next check would
3170 debug_print("Freeing imap uid cache");
3172 g_slist_free(item->uid_list);
3173 item->uid_list = NULL;
3175 g_slist_free(*msgnum_list);
3177 nummsgs = get_list_of_uids(session, folder, item, &uidlist);
3180 *msgnum_list = uidlist;
3182 dir = folder_item_get_path((FolderItem *)item);
3183 debug_print("removing old messages from %s\n", dir);
3184 remove_numbered_files_not_in_list(dir, *msgnum_list);
3187 item->uid_next = uid_next;
3189 debug_print("get_num_list - ok - %i\n", nummsgs);
3190 statusbar_pop_all();
3195 static MsgInfo *imap_parse_msg(const gchar *file, FolderItem *item)
3200 flags.perm_flags = MSG_NEW|MSG_UNREAD;
3201 flags.tmp_flags = 0;
3203 g_return_val_if_fail(item != NULL, NULL);
3204 g_return_val_if_fail(file != NULL, NULL);
3206 if (folder_has_parent_of_type(item, F_QUEUE)) {
3207 MSG_SET_TMP_FLAGS(flags, MSG_QUEUED);
3208 } else if (folder_has_parent_of_type(item, F_DRAFT)) {
3209 MSG_SET_TMP_FLAGS(flags, MSG_DRAFT);
3212 msginfo = procheader_parse_file(file, flags, FALSE, FALSE);
3213 if (!msginfo) return NULL;
3215 msginfo->plaintext_file = g_strdup(file);
3216 msginfo->folder = item;
3221 GSList *imap_get_msginfos(Folder *folder, FolderItem *item,
3222 GSList *msgnum_list)
3224 IMAPSession *session;
3225 MsgInfoList *ret = NULL;
3228 debug_print("get_msginfos\n");
3230 g_return_val_if_fail(folder != NULL, NULL);
3231 g_return_val_if_fail(item != NULL, NULL);
3232 g_return_val_if_fail(msgnum_list != NULL, NULL);
3234 session = imap_session_get(folder);
3235 g_return_val_if_fail(session != NULL, NULL);
3237 debug_print("IMAP getting msginfos\n");
3238 ok = imap_select(session, IMAP_FOLDER(folder), item->path,
3239 NULL, NULL, NULL, NULL, FALSE);
3240 if (ok != IMAP_SUCCESS) {
3244 if (!(folder_has_parent_of_type(item, F_DRAFT) ||
3245 folder_has_parent_of_type(item, F_QUEUE))) {
3246 ret = g_slist_concat(ret,
3247 imap_get_uncached_messages(session, item,
3250 MsgNumberList *sorted_list, *elem;
3251 gint startnum, lastnum;
3253 sorted_list = g_slist_sort(g_slist_copy(msgnum_list), g_int_compare);
3255 startnum = lastnum = GPOINTER_TO_INT(sorted_list->data);
3257 for (elem = sorted_list;; elem = g_slist_next(elem)) {
3261 num = GPOINTER_TO_INT(elem->data);
3263 if (num > lastnum + 1 || elem == NULL) {
3265 for (i = startnum; i <= lastnum; ++i) {
3268 file = imap_fetch_msg(folder, item, i);
3270 MsgInfo *msginfo = imap_parse_msg(file, item);
3271 if (msginfo != NULL) {
3272 msginfo->msgnum = i;
3273 ret = g_slist_append(ret, msginfo);
3287 g_slist_free(sorted_list);
3293 MsgInfo *imap_get_msginfo(Folder *folder, FolderItem *item, gint uid)
3295 MsgInfo *msginfo = NULL;
3296 MsgInfoList *msginfolist;
3297 MsgNumberList numlist;
3299 numlist.next = NULL;
3300 numlist.data = GINT_TO_POINTER(uid);
3302 msginfolist = imap_get_msginfos(folder, item, &numlist);
3303 if (msginfolist != NULL) {
3304 msginfo = msginfolist->data;
3305 g_slist_free(msginfolist);
3311 gboolean imap_scan_required(Folder *folder, FolderItem *_item)
3313 IMAPSession *session;
3314 IMAPFolderItem *item = (IMAPFolderItem *)_item;
3315 gint ok, exists = 0, unseen = 0;
3316 guint32 uid_next, uid_val;
3317 gboolean selected_folder;
3319 g_return_val_if_fail(folder != NULL, FALSE);
3320 g_return_val_if_fail(item != NULL, FALSE);
3321 g_return_val_if_fail(item->item.folder != NULL, FALSE);
3322 g_return_val_if_fail(FOLDER_CLASS(item->item.folder) == &imap_class, FALSE);
3324 if (item->item.path == NULL)
3327 session = imap_session_get(folder);
3328 g_return_val_if_fail(session != NULL, FALSE);
3330 selected_folder = (session->mbox != NULL) &&
3331 (!strcmp(session->mbox, item->item.path));
3332 if (selected_folder && time(NULL) - item->use_cache < 2) {
3333 ok = imap_cmd_noop(session);
3334 if (ok != IMAP_SUCCESS) {
3335 debug_print("disconnected!\n");
3336 session = imap_reconnect_if_possible(folder, session);
3337 if (session == NULL)
3342 if (session->folder_content_changed
3343 || session->exists != item->item.total_msgs) {
3348 ok = imap_status(session, IMAP_FOLDER(folder), item->item.path, IMAP_FOLDER_ITEM(item),
3349 &exists, &uid_next, &uid_val, &unseen, FALSE);
3350 if (ok != IMAP_SUCCESS) {
3355 item->use_cache = time(NULL);
3356 item->c_messages = exists;
3357 item->c_uid_next = uid_next;
3358 item->c_uid_validity = uid_val;
3359 item->c_unseen = unseen;
3360 item->item.last_num = uid_next - 1;
3361 debug_print("uidnext %d, item->uid_next %d, exists %d, item->item.total_msgs %d\n",
3362 uid_next, item->uid_next, exists, item->item.total_msgs);
3363 if ((uid_next != item->uid_next) || (exists != item->item.total_msgs)) {
3372 void imap_change_flags(Folder *folder, FolderItem *item, MsgInfo *msginfo, MsgPermFlags newflags)
3374 IMAPSession *session;
3375 IMAPFlags flags_set = 0, flags_unset = 0;
3376 gint ok = IMAP_SUCCESS;
3377 MsgNumberList numlist;
3378 hashtable_data *ht_data = NULL;
3380 g_return_if_fail(folder != NULL);
3381 g_return_if_fail(folder->klass == &imap_class);
3382 g_return_if_fail(item != NULL);
3383 g_return_if_fail(item->folder == folder);
3384 g_return_if_fail(msginfo != NULL);
3385 g_return_if_fail(msginfo->folder == item);
3387 session = imap_session_get(folder);
3392 if ((ok = imap_select(session, IMAP_FOLDER(folder), msginfo->folder->path,
3393 NULL, NULL, NULL, NULL, FALSE)) != IMAP_SUCCESS) {
3398 if (!MSG_IS_MARKED(msginfo->flags) && (newflags & MSG_MARKED))
3399 flags_set |= IMAP_FLAG_FLAGGED;
3400 if ( MSG_IS_MARKED(msginfo->flags) && !(newflags & MSG_MARKED))
3401 flags_unset |= IMAP_FLAG_FLAGGED;
3403 if (!MSG_IS_UNREAD(msginfo->flags) && (newflags & MSG_UNREAD))
3404 flags_unset |= IMAP_FLAG_SEEN;
3405 if ( MSG_IS_UNREAD(msginfo->flags) && !(newflags & MSG_UNREAD))
3406 flags_set |= IMAP_FLAG_SEEN;
3408 if (!MSG_IS_REPLIED(msginfo->flags) && (newflags & MSG_REPLIED))
3409 flags_set |= IMAP_FLAG_ANSWERED;
3410 if ( MSG_IS_REPLIED(msginfo->flags) && !(newflags & MSG_REPLIED))
3411 flags_unset |= IMAP_FLAG_ANSWERED;
3413 if (!MSG_IS_DELETED(msginfo->flags) && (newflags & MSG_DELETED))
3414 flags_set |= IMAP_FLAG_DELETED;
3415 if ( MSG_IS_DELETED(msginfo->flags) && !(newflags & MSG_DELETED))
3416 flags_unset |= IMAP_FLAG_DELETED;
3418 numlist.next = NULL;
3419 numlist.data = GINT_TO_POINTER(msginfo->msgnum);
3421 if (IMAP_FOLDER_ITEM(item)->batching) {
3422 /* instead of performing an UID STORE command for each message change,
3423 * as a lot of them can change "together", we just fill in hashtables
3424 * and defer the treatment so that we're able to send only one
3427 debug_print("IMAP batch mode on, deferring flags change\n");
3429 ht_data = g_hash_table_lookup(IMAP_FOLDER_ITEM(item)->flags_set_table,
3430 GINT_TO_POINTER(flags_set));
3431 if (ht_data == NULL) {
3432 ht_data = g_new0(hashtable_data, 1);
3433 ht_data->session = session;
3434 ht_data->item = IMAP_FOLDER_ITEM(item);
3435 g_hash_table_insert(IMAP_FOLDER_ITEM(item)->flags_set_table,
3436 GINT_TO_POINTER(flags_set), ht_data);
3438 if (!g_slist_find(ht_data->msglist, GINT_TO_POINTER(msginfo->msgnum)))
3439 ht_data->msglist = g_slist_prepend(ht_data->msglist, GINT_TO_POINTER(msginfo->msgnum));
3442 ht_data = g_hash_table_lookup(IMAP_FOLDER_ITEM(item)->flags_unset_table,
3443 GINT_TO_POINTER(flags_unset));
3444 if (ht_data == NULL) {
3445 ht_data = g_new0(hashtable_data, 1);
3446 ht_data->session = session;
3447 ht_data->item = IMAP_FOLDER_ITEM(item);
3448 g_hash_table_insert(IMAP_FOLDER_ITEM(item)->flags_unset_table,
3449 GINT_TO_POINTER(flags_unset), ht_data);
3451 if (!g_slist_find(ht_data->msglist, GINT_TO_POINTER(msginfo->msgnum)))
3452 ht_data->msglist = g_slist_prepend(ht_data->msglist,
3453 GINT_TO_POINTER(msginfo->msgnum));
3456 debug_print("IMAP changing flags\n");
3458 ok = imap_set_message_flags(session, &numlist, flags_set, TRUE);
3459 if (ok != IMAP_SUCCESS) {
3466 ok = imap_set_message_flags(session, &numlist, flags_unset, FALSE);
3467 if (ok != IMAP_SUCCESS) {
3473 msginfo->flags.perm_flags = newflags;
3478 static gint imap_remove_msg(Folder *folder, FolderItem *item, gint uid)
3481 IMAPSession *session;
3483 MsgNumberList numlist;
3485 g_return_val_if_fail(folder != NULL, -1);
3486 g_return_val_if_fail(FOLDER_CLASS(folder) == &imap_class, -1);
3487 g_return_val_if_fail(item != NULL, -1);
3489 session = imap_session_get(folder);
3490 if (!session) return -1;
3492 ok = imap_select(session, IMAP_FOLDER(folder), item->path,
3493 NULL, NULL, NULL, NULL, FALSE);
3494 if (ok != IMAP_SUCCESS) {
3498 numlist.next = NULL;
3499 numlist.data = GINT_TO_POINTER(uid);
3501 ok = imap_set_message_flags
3502 (IMAP_SESSION(REMOTE_FOLDER(folder)->session),
3503 &numlist, IMAP_FLAG_DELETED, TRUE);
3504 if (ok != IMAP_SUCCESS) {
3505 log_warning(_("can't set deleted flags: %d\n"), uid);
3510 if (!session->uidplus) {
3511 ok = imap_cmd_expunge(session);
3515 uidstr = g_strdup_printf("%u", uid);
3516 ok = imap_cmd_expunge(session);
3519 if (ok != IMAP_SUCCESS) {
3520 log_warning(_("can't expunge\n"));
3525 IMAP_FOLDER_ITEM(item)->uid_list = g_slist_remove(
3526 IMAP_FOLDER_ITEM(item)->uid_list, numlist.data);
3527 dir = folder_item_get_path(item);
3528 if (is_dir_exist(dir))
3529 remove_numbered_files(dir, uid, uid);
3532 return IMAP_SUCCESS;
3535 static gint compare_msginfo(gconstpointer a, gconstpointer b)
3537 return ((MsgInfo *)a)->msgnum - ((MsgInfo *)b)->msgnum;
3540 static guint gslist_find_next_num(MsgNumberList **list, guint num)
3544 g_return_val_if_fail(list != NULL, -1);
3546 for (elem = *list; elem != NULL; elem = g_slist_next(elem))
3547 if (GPOINTER_TO_INT(elem->data) >= num)
3550 return elem != NULL ? GPOINTER_TO_INT(elem->data) : (gint)-1;
3554 * NEW and DELETED flags are not syncronized
3555 * - The NEW/RECENT flags in IMAP folders can not really be directly
3556 * modified by Sylpheed
3557 * - The DELETE/DELETED flag in IMAP and Sylpheed don't have the same
3558 * meaning, in IMAP it always removes the messages from the FolderItem
3559 * in Sylpheed it can mean to move the message to trash
3562 typedef struct _get_flags_data {
3565 MsgInfoList *msginfo_list;
3566 GRelation *msgflags;
3567 gboolean full_search;
3571 static /*gint*/ void *imap_get_flags_thread(void *data)
3573 get_flags_data *stuff = (get_flags_data *)data;
3574 Folder *folder = stuff->folder;
3575 FolderItem *item = stuff->item;
3576 MsgInfoList *msginfo_list = stuff->msginfo_list;
3577 GRelation *msgflags = stuff->msgflags;
3578 gboolean full_search = stuff->full_search;
3579 IMAPSession *session;
3580 GSList *sorted_list = NULL;
3581 GSList *unseen = NULL, *answered = NULL, *flagged = NULL, *deleted = NULL;
3582 GSList *p_unseen, *p_answered, *p_flagged, *p_deleted;
3584 GSList *seq_list, *cur;
3585 gboolean reverse_seen = FALSE;
3588 gint exists_cnt, unseen_cnt;
3589 gboolean selected_folder;
3591 if (folder == NULL || item == NULL) {
3593 return GINT_TO_POINTER(-1);
3596 session = imap_session_get(folder);
3597 if (session == NULL) {
3599 return GINT_TO_POINTER(-1);
3602 selected_folder = (session->mbox != NULL) &&
3603 (!strcmp(session->mbox, item->path));
3605 if (!selected_folder) {
3606 ok = imap_select(session, IMAP_FOLDER(folder), item->path,
3607 &exists_cnt, NULL, &unseen_cnt, NULL, TRUE);
3608 if (ok != IMAP_SUCCESS) {
3611 return GINT_TO_POINTER(-1);
3614 if (unseen_cnt > exists_cnt / 2)
3615 reverse_seen = TRUE;
3618 if (item->unread_msgs > item->total_msgs / 2)
3619 reverse_seen = TRUE;
3622 cmd_buf = g_string_new(NULL);
3624 sorted_list = g_slist_sort(g_slist_copy(msginfo_list), compare_msginfo);
3626 seq_list = imap_get_lep_set_from_msglist(msginfo_list);
3628 struct mailimap_set * set;
3629 set = mailimap_set_new_interval(1, 0);
3630 seq_list = g_slist_append(NULL, set);
3633 for (cur = seq_list; cur != NULL; cur = g_slist_next(cur)) {
3634 struct mailimap_set * imapset;
3635 clist * lep_uidlist;
3638 imapset = cur->data;
3640 r = imap_threaded_search(folder, IMAP_SEARCH_TYPE_SEEN,
3641 imapset, &lep_uidlist);
3644 r = imap_threaded_search(folder,
3645 IMAP_SEARCH_TYPE_UNSEEN,
3646 imapset, &lep_uidlist);
3648 if (r == MAILIMAP_NO_ERROR) {
3651 uidlist = imap_uid_list_from_lep(lep_uidlist);
3652 mailimap_search_result_free(lep_uidlist);
3654 unseen = g_slist_concat(unseen, uidlist);
3657 r = imap_threaded_search(folder, IMAP_SEARCH_TYPE_ANSWERED,
3658 imapset, &lep_uidlist);
3659 if (r == MAILIMAP_NO_ERROR) {
3662 uidlist = imap_uid_list_from_lep(lep_uidlist);
3663 mailimap_search_result_free(lep_uidlist);
3665 answered = g_slist_concat(answered, uidlist);
3668 r = imap_threaded_search(folder, IMAP_SEARCH_TYPE_FLAGGED,
3669 imapset, &lep_uidlist);
3670 if (r == MAILIMAP_NO_ERROR) {
3673 uidlist = imap_uid_list_from_lep(lep_uidlist);
3674 mailimap_search_result_free(lep_uidlist);
3676 flagged = g_slist_concat(flagged, uidlist);
3679 r = imap_threaded_search(folder, IMAP_SEARCH_TYPE_DELETED,
3680 imapset, &lep_uidlist);
3681 if (r == MAILIMAP_NO_ERROR) {
3684 uidlist = imap_uid_list_from_lep(lep_uidlist);
3685 mailimap_search_result_free(lep_uidlist);
3687 deleted = g_slist_concat(deleted, uidlist);
3692 p_answered = answered;
3693 p_flagged = flagged;
3694 p_deleted = deleted;
3696 for (elem = sorted_list; elem != NULL; elem = g_slist_next(elem)) {
3701 msginfo = (MsgInfo *) elem->data;
3702 flags = msginfo->flags.perm_flags;
3703 wasnew = (flags & MSG_NEW);
3704 flags &= ~((reverse_seen ? 0 : MSG_UNREAD | MSG_NEW) | MSG_REPLIED | MSG_MARKED);
3706 flags |= MSG_UNREAD | (wasnew ? MSG_NEW : 0);
3707 if (gslist_find_next_num(&p_unseen, msginfo->msgnum) == msginfo->msgnum) {
3708 if (!reverse_seen) {
3709 flags |= MSG_UNREAD | (wasnew ? MSG_NEW : 0);
3711 flags &= ~(MSG_UNREAD | MSG_NEW);
3714 if (gslist_find_next_num(&p_answered, msginfo->msgnum) == msginfo->msgnum)
3715 flags |= MSG_REPLIED;
3717 flags &= ~MSG_REPLIED;
3718 if (gslist_find_next_num(&p_flagged, msginfo->msgnum) == msginfo->msgnum)
3719 flags |= MSG_MARKED;
3721 flags &= ~MSG_MARKED;
3722 if (gslist_find_next_num(&p_deleted, msginfo->msgnum) == msginfo->msgnum)
3723 flags |= MSG_DELETED;
3725 flags &= ~MSG_DELETED;
3726 g_relation_insert(msgflags, msginfo, GINT_TO_POINTER(flags));
3729 imap_lep_set_free(seq_list);
3730 g_slist_free(flagged);
3731 g_slist_free(deleted);
3732 g_slist_free(answered);
3733 g_slist_free(unseen);
3734 g_slist_free(sorted_list);
3735 g_string_free(cmd_buf, TRUE);
3739 return GINT_TO_POINTER(0);
3742 static gint imap_get_flags(Folder *folder, FolderItem *item,
3743 MsgInfoList *msginfo_list, GRelation *msgflags)
3746 get_flags_data *data = g_new0(get_flags_data, 1);
3748 data->folder = folder;
3750 data->msginfo_list = msginfo_list;
3751 data->msgflags = msgflags;
3752 data->full_search = FALSE;
3754 GSList *tmp = NULL, *cur;
3756 if (prefs_common.work_offline && !inc_offline_should_override()) {
3761 tmp = folder_item_get_msg_list(item);
3763 if (g_slist_length(tmp) == g_slist_length(msginfo_list))
3764 data->full_search = TRUE;
3766 for (cur = tmp; cur; cur = cur->next)
3767 procmsg_msginfo_free((MsgInfo *)cur->data);
3771 result = GPOINTER_TO_INT(imap_get_flags_thread(data));
3778 static gboolean process_flags(gpointer key, gpointer value, gpointer user_data)
3780 gboolean flags_set = GPOINTER_TO_INT(user_data);
3781 gint flags_value = GPOINTER_TO_INT(key);
3782 hashtable_data *data = (hashtable_data *)value;
3783 IMAPFolderItem *_item = data->item;
3784 FolderItem *item = (FolderItem *)_item;
3785 gint ok = IMAP_ERROR;
3786 IMAPSession *session = imap_session_get(item->folder);
3788 data->msglist = g_slist_reverse(data->msglist);
3790 debug_print("IMAP %ssetting flags to %d for %d messages\n",
3793 g_slist_length(data->msglist));
3797 ok = imap_select(session, IMAP_FOLDER(item->folder), item->path,
3798 NULL, NULL, NULL, NULL, FALSE);
3800 if (ok == IMAP_SUCCESS) {
3801 imap_set_message_flags(data->session, data->msglist, flags_value, flags_set);
3803 g_warning("can't select mailbox %s\n", item->path);
3807 g_slist_free(data->msglist);
3812 static void process_hashtable(IMAPFolderItem *item)
3814 if (item->flags_set_table) {
3815 g_hash_table_foreach_remove(item->flags_set_table, process_flags, GINT_TO_POINTER(TRUE));
3816 g_hash_table_destroy(item->flags_set_table);
3817 item->flags_set_table = NULL;
3819 if (item->flags_unset_table) {
3820 g_hash_table_foreach_remove(item->flags_unset_table, process_flags, GINT_TO_POINTER(FALSE));
3821 g_hash_table_destroy(item->flags_unset_table);
3822 item->flags_unset_table = NULL;
3826 static void imap_set_batch (Folder *folder, FolderItem *_item, gboolean batch)
3828 IMAPFolderItem *item = (IMAPFolderItem *)_item;
3830 g_return_if_fail(item != NULL);
3832 if (item->batching == batch)
3836 item->batching = TRUE;
3837 debug_print("IMAP switching to batch mode\n");
3838 if (!item->flags_set_table) {
3839 item->flags_set_table = g_hash_table_new(NULL, g_direct_equal);
3841 if (!item->flags_unset_table) {
3842 item->flags_unset_table = g_hash_table_new(NULL, g_direct_equal);
3845 debug_print("IMAP switching away from batch mode\n");
3847 process_hashtable(item);
3848 item->batching = FALSE;
3854 /* data types conversion libetpan <-> sylpheed */
3858 #define ETPAN_IMAP_MB_MARKED 1
3859 #define ETPAN_IMAP_MB_UNMARKED 2
3860 #define ETPAN_IMAP_MB_NOSELECT 4
3861 #define ETPAN_IMAP_MB_NOINFERIORS 8
3863 static int imap_flags_to_flags(struct mailimap_mbx_list_flags * imap_flags)
3869 if (imap_flags->mbf_type == MAILIMAP_MBX_LIST_FLAGS_SFLAG) {
3870 switch (imap_flags->mbf_sflag) {
3871 case MAILIMAP_MBX_LIST_SFLAG_MARKED:
3872 flags |= ETPAN_IMAP_MB_MARKED;
3874 case MAILIMAP_MBX_LIST_SFLAG_NOSELECT:
3875 flags |= ETPAN_IMAP_MB_NOSELECT;
3877 case MAILIMAP_MBX_LIST_SFLAG_UNMARKED:
3878 flags |= ETPAN_IMAP_MB_UNMARKED;
3883 for(cur = clist_begin(imap_flags->mbf_oflags) ; cur != NULL ;
3884 cur = clist_next(cur)) {
3885 struct mailimap_mbx_list_oflag * oflag;
3887 oflag = clist_content(cur);
3889 switch (oflag->of_type) {
3890 case MAILIMAP_MBX_LIST_OFLAG_NOINFERIORS:
3891 flags |= ETPAN_IMAP_MB_NOINFERIORS;
3899 static GSList * imap_list_from_lep(IMAPFolder * folder,
3900 clist * list, const gchar * real_path, gboolean all)
3907 for(iter = clist_begin(list) ; iter != NULL ;
3908 iter = clist_next(iter)) {
3909 struct mailimap_mailbox_list * mb;
3917 FolderItem *new_item;
3919 mb = clist_content(iter);
3925 if (mb->mb_flag != NULL)
3926 flags = imap_flags_to_flags(mb->mb_flag);
3928 delimiter = mb->mb_delimiter;
3931 dup_name = strdup(name);
3932 if (delimiter != '\0')
3933 subst_char(dup_name, delimiter, '/');
3935 base = g_path_get_basename(dup_name);
3936 if (base[0] == '.') {
3942 if (!all && strcmp(dup_name, real_path) == 0) {
3948 if (!all && dup_name[strlen(dup_name)-1] == '/') {
3954 loc_name = imap_modified_utf7_to_utf8(base);
3955 loc_path = imap_modified_utf7_to_utf8(dup_name);
3957 new_item = folder_item_new(FOLDER(folder), loc_name, loc_path);
3958 if ((flags & ETPAN_IMAP_MB_NOINFERIORS) != 0)
3959 new_item->no_sub = TRUE;
3960 if (strcmp(dup_name, "INBOX") != 0 &&
3961 ((flags & ETPAN_IMAP_MB_NOSELECT) != 0))
3962 new_item->no_select = TRUE;
3964 item_list = g_slist_append(item_list, new_item);
3966 debug_print("folder '%s' found.\n", loc_path);
3977 static GSList * imap_get_lep_set_from_numlist(MsgNumberList *numlist)
3979 GSList *sorted_list, *cur;
3980 guint first, last, next;
3981 GSList *ret_list = NULL;
3983 struct mailimap_set * current_set;
3984 unsigned int item_count;
3986 if (numlist == NULL)
3990 current_set = mailimap_set_new_empty();
3992 sorted_list = g_slist_copy(numlist);
3993 sorted_list = g_slist_sort(sorted_list, g_int_compare);
3995 first = GPOINTER_TO_INT(sorted_list->data);
3998 for (cur = sorted_list; cur != NULL; cur = g_slist_next(cur)) {
3999 if (GPOINTER_TO_INT(cur->data) == 0)
4004 last = GPOINTER_TO_INT(cur->data);
4006 next = GPOINTER_TO_INT(cur->next->data);
4010 if (last + 1 != next || next == 0) {
4012 struct mailimap_set_item * item;
4013 item = mailimap_set_item_new(first, last);
4014 mailimap_set_add(current_set, item);
4019 if (count >= IMAP_SET_MAX_COUNT) {
4020 ret_list = g_slist_append(ret_list,
4022 current_set = mailimap_set_new_empty();
4029 if (clist_count(current_set->set_list) > 0) {
4030 ret_list = g_slist_append(ret_list,
4034 g_slist_free(sorted_list);
4039 static GSList * imap_get_lep_set_from_msglist(MsgInfoList *msglist)
4041 MsgNumberList *numlist = NULL;
4045 for (cur = msglist; cur != NULL; cur = g_slist_next(cur)) {
4046 MsgInfo *msginfo = (MsgInfo *) cur->data;
4048 numlist = g_slist_append(numlist, GINT_TO_POINTER(msginfo->msgnum));
4050 seq_list = imap_get_lep_set_from_numlist(numlist);
4051 g_slist_free(numlist);
4056 static GSList * imap_uid_list_from_lep(clist * list)
4063 for(iter = clist_begin(list) ; iter != NULL ;
4064 iter = clist_next(iter)) {
4067 puid = clist_content(iter);
4068 result = g_slist_append(result, GINT_TO_POINTER(* puid));
4074 static GSList * imap_uid_list_from_lep_tab(carray * list)
4081 for(i = 0 ; i < carray_count(list) ; i ++) {
4084 puid = carray_get(list, i);
4085 result = g_slist_append(result, GINT_TO_POINTER(* puid));
4091 static MsgInfo *imap_envelope_from_lep(struct imap_fetch_env_info * info,
4094 MsgInfo *msginfo = NULL;
4097 MsgFlags flags = {0, 0};
4099 if (info->headers == NULL)
4102 MSG_SET_TMP_FLAGS(flags, MSG_IMAP);
4103 if (folder_has_parent_of_type(item, F_QUEUE)) {
4104 MSG_SET_TMP_FLAGS(flags, MSG_QUEUED);
4105 } else if (folder_has_parent_of_type(item, F_DRAFT)) {
4106 MSG_SET_TMP_FLAGS(flags, MSG_DRAFT);
4108 flags.perm_flags = info->flags;
4112 msginfo = procheader_parse_str(info->headers, flags, FALSE, FALSE);
4115 msginfo->msgnum = uid;
4116 msginfo->size = size;
4122 static void imap_lep_set_free(GSList *seq_list)
4126 for(cur = seq_list ; cur != NULL ; cur = g_slist_next(cur)) {
4127 struct mailimap_set * imapset;
4129 imapset = cur->data;
4130 mailimap_set_free(imapset);
4132 g_slist_free(seq_list);
4135 static struct mailimap_flag_list * imap_flag_to_lep(IMAPFlags flags)
4137 struct mailimap_flag_list * flag_list;
4139 flag_list = mailimap_flag_list_new_empty();
4141 if (IMAP_IS_SEEN(flags))
4142 mailimap_flag_list_add(flag_list,
4143 mailimap_flag_new_seen());
4144 if (IMAP_IS_ANSWERED(flags))
4145 mailimap_flag_list_add(flag_list,
4146 mailimap_flag_new_answered());
4147 if (IMAP_IS_FLAGGED(flags))
4148 mailimap_flag_list_add(flag_list,
4149 mailimap_flag_new_flagged());
4150 if (IMAP_IS_DELETED(flags))
4151 mailimap_flag_list_add(flag_list,
4152 mailimap_flag_new_deleted());
4153 if (IMAP_IS_DRAFT(flags))
4154 mailimap_flag_list_add(flag_list,
4155 mailimap_flag_new_draft());
4160 guint imap_folder_get_refcnt(Folder *folder)
4162 return ((IMAPFolder *)folder)->refcnt;
4165 void imap_folder_ref(Folder *folder)
4167 ((IMAPFolder *)folder)->refcnt++;
4170 void imap_folder_unref(Folder *folder)
4172 if (((IMAPFolder *)folder)->refcnt > 0)
4173 ((IMAPFolder *)folder)->refcnt--;
4176 #else /* HAVE_LIBETPAN */
4178 static FolderClass imap_class;
4180 static Folder *imap_folder_new (const gchar *name,
4185 static gint imap_create_tree (Folder *folder)
4189 static FolderItem *imap_create_folder (Folder *folder,
4195 static gint imap_rename_folder (Folder *folder,
4202 FolderClass *imap_get_class(void)
4204 if (imap_class.idstr == NULL) {
4205 imap_class.type = F_IMAP;
4206 imap_class.idstr = "imap";
4207 imap_class.uistr = "IMAP4";
4209 imap_class.new_folder = imap_folder_new;
4210 imap_class.create_tree = imap_create_tree;
4211 imap_class.create_folder = imap_create_folder;
4212 imap_class.rename_folder = imap_rename_folder;
4213 /* nothing implemented */
4220 void imap_synchronise(FolderItem *item)
4222 imap_gtk_synchronise(item);