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 if (msginfo == NULL)
2032 msginfo->folder = item;
2034 llast = newlist = g_slist_append(newlist, msginfo);
2036 llast = g_slist_append(llast, msginfo);
2037 llast = llast->next;
2042 imap_fetch_env_free(env_list);
2045 session_set_access_time(SESSION(session));
2050 #define MAX_MSG_NUM 50
2052 static GSList *imap_get_uncached_messages(IMAPSession *session,
2054 MsgNumberList *numlist)
2056 GSList *result = NULL;
2058 uncached_data *data = g_new0(uncached_data, 1);
2063 data->total = g_slist_length(numlist);
2064 debug_print("messages list : %i\n", data->total);
2066 while (cur != NULL) {
2067 GSList * partial_result;
2075 while (count < MAX_MSG_NUM) {
2080 if (newlist == NULL)
2081 llast = newlist = g_slist_append(newlist, p);
2083 llast = g_slist_append(llast, p);
2084 llast = llast->next;
2094 data->session = session;
2096 data->numlist = newlist;
2099 if (prefs_common.work_offline && !inc_offline_should_override()) {
2105 (GSList *)imap_get_uncached_messages_thread(data);
2107 statusbar_progress_all(data->cur,data->total, 1);
2109 g_slist_free(newlist);
2111 result = g_slist_concat(result, partial_result);
2115 statusbar_progress_all(0,0,0);
2116 statusbar_pop_all();
2121 static void imap_delete_all_cached_messages(FolderItem *item)
2125 g_return_if_fail(item != NULL);
2126 g_return_if_fail(item->folder != NULL);
2127 g_return_if_fail(FOLDER_CLASS(item->folder) == &imap_class);
2129 debug_print("Deleting all cached messages...\n");
2131 dir = folder_item_get_path(item);
2132 if (is_dir_exist(dir))
2133 remove_all_numbered_files(dir);
2136 debug_print("done.\n");
2139 static IMAPNameSpace *imap_find_namespace_from_list(GList *ns_list,
2142 IMAPNameSpace *namespace = NULL;
2143 gchar *tmp_path, *name;
2145 if (!path) path = "";
2147 for (; ns_list != NULL; ns_list = ns_list->next) {
2148 IMAPNameSpace *tmp_ns = ns_list->data;
2150 Xstrcat_a(tmp_path, path, "/", return namespace);
2151 Xstrdup_a(name, tmp_ns->name, return namespace);
2152 if (tmp_ns->separator && tmp_ns->separator != '/') {
2153 subst_char(tmp_path, tmp_ns->separator, '/');
2154 subst_char(name, tmp_ns->separator, '/');
2156 if (strncmp(tmp_path, name, strlen(name)) == 0)
2163 static IMAPNameSpace *imap_find_namespace(IMAPFolder *folder,
2166 IMAPNameSpace *namespace;
2168 g_return_val_if_fail(folder != NULL, NULL);
2170 namespace = imap_find_namespace_from_list(folder->ns_personal, path);
2171 if (namespace) return namespace;
2172 namespace = imap_find_namespace_from_list(folder->ns_others, path);
2173 if (namespace) return namespace;
2174 namespace = imap_find_namespace_from_list(folder->ns_shared, path);
2175 if (namespace) return namespace;
2181 static gchar imap_get_path_separator(IMAPFolder *folder, const gchar *path)
2183 IMAPNameSpace *namespace;
2184 gchar separator = '/';
2186 if (folder->last_seen_separator == 0) {
2188 int r = imap_threaded_list((Folder *)folder, "", "", &lep_list);
2189 if (r != MAILIMAP_NO_ERROR) {
2190 log_warning(_("LIST failed\n"));
2194 if (clist_count(lep_list) > 0) {
2195 clistiter * iter = clist_begin(lep_list);
2196 struct mailimap_mailbox_list * mb;
2197 mb = clist_content(iter);
2199 folder->last_seen_separator = mb->mb_delimiter;
2200 debug_print("got separator: %c\n", folder->last_seen_separator);
2202 mailimap_list_result_free(lep_list);
2205 if (folder->last_seen_separator != 0) {
2206 debug_print("using separator: %c\n", folder->last_seen_separator);
2207 return folder->last_seen_separator;
2210 namespace = imap_find_namespace(folder, path);
2211 if (namespace && namespace->separator)
2212 separator = namespace->separator;
2217 static gchar *imap_get_real_path(IMAPFolder *folder, const gchar *path)
2222 g_return_val_if_fail(folder != NULL, NULL);
2223 g_return_val_if_fail(path != NULL, NULL);
2225 real_path = imap_utf8_to_modified_utf7(path);
2226 separator = imap_get_path_separator(folder, path);
2227 imap_path_separator_subst(real_path, separator);
2232 static gint imap_set_message_flags(IMAPSession *session,
2233 MsgNumberList *numlist,
2241 seq_list = imap_get_lep_set_from_numlist(numlist);
2243 for(cur = seq_list ; cur != NULL ; cur = g_slist_next(cur)) {
2244 struct mailimap_set * imapset;
2246 imapset = cur->data;
2248 ok = imap_cmd_store(session, imapset,
2252 imap_lep_set_free(seq_list);
2254 return IMAP_SUCCESS;
2257 typedef struct _select_data {
2258 IMAPSession *session;
2263 guint32 *uid_validity;
2267 static gint imap_select(IMAPSession *session, IMAPFolder *folder,
2269 gint *exists, gint *recent, gint *unseen,
2270 guint32 *uid_validity, gboolean block)
2274 gint exists_, recent_, unseen_;
2275 guint32 uid_validity_;
2277 if (!exists && !recent && !unseen && !uid_validity) {
2278 if (session->mbox && strcmp(session->mbox, path) == 0)
2279 return IMAP_SUCCESS;
2288 uid_validity = &uid_validity_;
2290 g_free(session->mbox);
2291 session->mbox = NULL;
2293 real_path = imap_get_real_path(folder, path);
2295 ok = imap_cmd_select(session, real_path,
2296 exists, recent, unseen, uid_validity, block);
2297 if (ok != IMAP_SUCCESS)
2298 log_warning(_("can't select folder: %s\n"), real_path);
2300 session->mbox = g_strdup(path);
2301 session->folder_content_changed = FALSE;
2308 static gint imap_status(IMAPSession *session, IMAPFolder *folder,
2309 const gchar *path, IMAPFolderItem *item,
2311 guint32 *uid_next, guint32 *uid_validity,
2312 gint *unseen, gboolean block)
2316 struct mailimap_mailbox_data_status * data_status;
2321 real_path = imap_get_real_path(folder, path);
2324 if (time(NULL) - item->last_update >= 5 && item->last_update != 1) {
2325 /* do the full stuff */
2326 item->last_update = 1; /* force update */
2327 debug_print("updating everything\n");
2328 r = imap_status(session, folder, path, item,
2329 &item->c_messages, &item->c_uid_next,
2330 &item->c_uid_validity, &item->c_unseen, block);
2331 if (r != MAILIMAP_NO_ERROR) {
2332 debug_print("status err %d\n", r);
2335 item->last_update = time(NULL);
2337 *messages = item->c_messages;
2339 *uid_next = item->c_uid_next;
2341 *uid_validity = item->c_uid_validity;
2343 *unseen = item->c_unseen;
2345 } else if (time(NULL) - item->last_update < 5) {
2346 /* return cached stuff */
2347 debug_print("using cache\n");
2349 *messages = item->c_messages;
2351 *uid_next = item->c_uid_next;
2353 *uid_validity = item->c_uid_validity;
2355 *unseen = item->c_unseen;
2360 /* if we get there, we're updating cache */
2374 r = imap_threaded_status(FOLDER(folder), real_path,
2375 &data_status, mask);
2378 if (r != MAILIMAP_NO_ERROR) {
2379 debug_print("status err %d\n", r);
2383 if (data_status->st_info_list == NULL) {
2384 mailimap_mailbox_data_status_free(data_status);
2385 debug_print("status->st_info_list == NULL\n");
2390 for(iter = clist_begin(data_status->st_info_list) ; iter != NULL ;
2391 iter = clist_next(iter)) {
2392 struct mailimap_status_info * info;
2394 info = clist_content(iter);
2395 switch (info->st_att) {
2396 case MAILIMAP_STATUS_ATT_MESSAGES:
2397 * messages = info->st_value;
2398 got_values |= 1 << 0;
2401 case MAILIMAP_STATUS_ATT_UIDNEXT:
2402 * uid_next = info->st_value;
2403 got_values |= 1 << 2;
2406 case MAILIMAP_STATUS_ATT_UIDVALIDITY:
2407 * uid_validity = info->st_value;
2408 got_values |= 1 << 3;
2411 case MAILIMAP_STATUS_ATT_UNSEEN:
2412 * unseen = info->st_value;
2413 got_values |= 1 << 4;
2417 mailimap_mailbox_data_status_free(data_status);
2419 if (got_values != mask) {
2420 debug_print("status: incomplete values received (%d)\n", got_values);
2423 return IMAP_SUCCESS;
2426 static void imap_free_capabilities(IMAPSession *session)
2428 slist_free_strings(session->capability);
2429 g_slist_free(session->capability);
2430 session->capability = NULL;
2433 /* low-level IMAP4rev1 commands */
2436 static gint imap_cmd_authenticate(IMAPSession *session, const gchar *user,
2437 const gchar *pass, IMAPAuthType type)
2444 gchar hexdigest[33];
2448 auth_type = "CRAM-MD5";
2450 imap_gen_send(session, "AUTHENTICATE %s", auth_type);
2451 ok = imap_gen_recv(session, &buf);
2452 if (ok != IMAP_SUCCESS || buf[0] != '+' || buf[1] != ' ') {
2457 challenge = g_malloc(strlen(buf + 2) + 1);
2458 challenge_len = base64_decode(challenge, buf + 2, -1);
2459 challenge[challenge_len] = '\0';
2462 md5_hex_hmac(hexdigest, challenge, challenge_len, pass, strlen(pass));
2465 response = g_strdup_printf("%s %s", user, hexdigest);
2466 response64 = g_malloc((strlen(response) + 3) * 2 + 1);
2467 base64_encode(response64, response, strlen(response));
2470 sock_puts(SESSION(session)->sock, response64);
2471 ok = imap_cmd_ok(session, NULL);
2472 if (ok != IMAP_SUCCESS)
2473 log_warning(_("IMAP4 authentication failed.\n"));
2479 static gint imap_cmd_login(IMAPSession *session,
2480 const gchar *user, const gchar *pass,
2486 log_print("IMAP4> Logging %s to %s using %s\n",
2488 SESSION(session)->server,
2490 r = imap_threaded_login(session->folder, user, pass, type);
2491 if (r != MAILIMAP_NO_ERROR) {
2492 log_error("IMAP4< Error logging in to %s\n",
2493 SESSION(session)->server);
2501 static gint imap_cmd_logout(IMAPSession *session)
2503 imap_threaded_disconnect(session->folder);
2505 return IMAP_SUCCESS;
2508 static gint imap_cmd_noop(IMAPSession *session)
2511 unsigned int exists;
2513 r = imap_threaded_noop(session->folder, &exists);
2514 if (r != MAILIMAP_NO_ERROR) {
2515 debug_print("noop err %d\n", r);
2518 session->exists = exists;
2519 session_set_access_time(SESSION(session));
2521 return IMAP_SUCCESS;
2525 static gint imap_cmd_starttls(IMAPSession *session)
2529 r = imap_threaded_starttls(session->folder);
2530 if (r != MAILIMAP_NO_ERROR) {
2531 debug_print("starttls err %d\n", r);
2534 return IMAP_SUCCESS;
2538 static gint imap_cmd_select(IMAPSession *session, const gchar *folder,
2539 gint *exists, gint *recent, gint *unseen,
2540 guint32 *uid_validity, gboolean block)
2544 r = imap_threaded_select(session->folder, folder,
2545 exists, recent, unseen, uid_validity);
2546 if (r != MAILIMAP_NO_ERROR) {
2547 debug_print("select err %d\n", r);
2550 return IMAP_SUCCESS;
2553 static gint imap_cmd_examine(IMAPSession *session, const gchar *folder,
2554 gint *exists, gint *recent, gint *unseen,
2555 guint32 *uid_validity, gboolean block)
2559 r = imap_threaded_examine(session->folder, folder,
2560 exists, recent, unseen, uid_validity);
2561 if (r != MAILIMAP_NO_ERROR) {
2562 debug_print("examine err %d\n", r);
2566 return IMAP_SUCCESS;
2569 static gint imap_cmd_create(IMAPSession *session, const gchar *folder)
2573 r = imap_threaded_create(session->folder, folder);
2574 if (r != MAILIMAP_NO_ERROR) {
2579 return IMAP_SUCCESS;
2582 static gint imap_cmd_rename(IMAPSession *session, const gchar *old_folder,
2583 const gchar *new_folder)
2587 r = imap_threaded_rename(session->folder, old_folder,
2589 if (r != MAILIMAP_NO_ERROR) {
2594 return IMAP_SUCCESS;
2597 static gint imap_cmd_delete(IMAPSession *session, const gchar *folder)
2602 r = imap_threaded_delete(session->folder, folder);
2603 if (r != MAILIMAP_NO_ERROR) {
2608 return IMAP_SUCCESS;
2611 typedef struct _fetch_data {
2612 IMAPSession *session;
2614 const gchar *filename;
2620 static void *imap_cmd_fetch_thread(void *data)
2622 fetch_data *stuff = (fetch_data *)data;
2623 IMAPSession *session = stuff->session;
2624 guint32 uid = stuff->uid;
2625 const gchar *filename = stuff->filename;
2629 r = imap_threaded_fetch_content(session->folder,
2633 r = imap_threaded_fetch_content(session->folder,
2636 if (r != MAILIMAP_NO_ERROR) {
2637 debug_print("fetch err %d\n", r);
2638 return GINT_TO_POINTER(IMAP_ERROR);
2640 return GINT_TO_POINTER(IMAP_SUCCESS);
2643 static gint imap_cmd_fetch(IMAPSession *session, guint32 uid,
2644 const gchar *filename, gboolean headers,
2647 fetch_data *data = g_new0(fetch_data, 1);
2650 data->session = session;
2652 data->filename = filename;
2653 data->headers = headers;
2656 if (prefs_common.work_offline && !inc_offline_should_override()) {
2660 statusbar_print_all(_("Fetching message..."));
2661 result = GPOINTER_TO_INT(imap_cmd_fetch_thread(data));
2662 statusbar_pop_all();
2668 static gint imap_cmd_append(IMAPSession *session, const gchar *destfolder,
2669 const gchar *file, IMAPFlags flags,
2672 struct mailimap_flag_list * flag_list;
2675 g_return_val_if_fail(file != NULL, IMAP_ERROR);
2677 flag_list = imap_flag_to_lep(flags);
2678 r = imap_threaded_append(session->folder, destfolder,
2680 if (new_uid != NULL)
2683 if (r != MAILIMAP_NO_ERROR) {
2684 debug_print("append err %d\n", r);
2687 return IMAP_SUCCESS;
2690 static gint imap_cmd_copy(IMAPSession *session, struct mailimap_set * set,
2691 const gchar *destfolder, GRelation *uid_mapping)
2695 g_return_val_if_fail(session != NULL, IMAP_ERROR);
2696 g_return_val_if_fail(set != NULL, IMAP_ERROR);
2697 g_return_val_if_fail(destfolder != NULL, IMAP_ERROR);
2699 r = imap_threaded_copy(session->folder, set, destfolder);
2700 if (r != MAILIMAP_NO_ERROR) {
2705 return IMAP_SUCCESS;
2708 static gint imap_cmd_store(IMAPSession *session, struct mailimap_set * set,
2709 IMAPFlags flags, int do_add)
2712 struct mailimap_flag_list * flag_list;
2713 struct mailimap_store_att_flags * store_att_flags;
2715 flag_list = imap_flag_to_lep(flags);
2719 mailimap_store_att_flags_new_add_flags_silent(flag_list);
2722 mailimap_store_att_flags_new_remove_flags_silent(flag_list);
2724 r = imap_threaded_store(session->folder, set, store_att_flags);
2725 if (r != MAILIMAP_NO_ERROR) {
2730 return IMAP_SUCCESS;
2733 static gint imap_cmd_expunge(IMAPSession *session)
2737 if (prefs_common.work_offline && !inc_offline_should_override()) {
2741 r = imap_threaded_expunge(session->folder);
2742 if (r != MAILIMAP_NO_ERROR) {
2747 return IMAP_SUCCESS;
2750 static void imap_path_separator_subst(gchar *str, gchar separator)
2753 gboolean in_escape = FALSE;
2755 if (!separator || separator == '/') return;
2757 for (p = str; *p != '\0'; p++) {
2758 if (*p == '/' && !in_escape)
2760 else if (*p == '&' && *(p + 1) != '-' && !in_escape)
2762 else if (*p == '-' && in_escape)
2767 static gchar *imap_modified_utf7_to_utf8(const gchar *mutf7_str)
2769 static iconv_t cd = (iconv_t)-1;
2770 static gboolean iconv_ok = TRUE;
2773 size_t norm_utf7_len;
2775 gchar *to_str, *to_p;
2777 gboolean in_escape = FALSE;
2779 if (!iconv_ok) return g_strdup(mutf7_str);
2781 if (cd == (iconv_t)-1) {
2782 cd = iconv_open(CS_INTERNAL, CS_UTF_7);
2783 if (cd == (iconv_t)-1) {
2784 g_warning("iconv cannot convert UTF-7 to %s\n",
2787 return g_strdup(mutf7_str);
2791 /* modified UTF-7 to normal UTF-7 conversion */
2792 norm_utf7 = g_string_new(NULL);
2794 for (p = mutf7_str; *p != '\0'; p++) {
2795 /* replace: '&' -> '+',
2797 escaped ',' -> '/' */
2798 if (!in_escape && *p == '&') {
2799 if (*(p + 1) != '-') {
2800 g_string_append_c(norm_utf7, '+');
2803 g_string_append_c(norm_utf7, '&');
2806 } else if (in_escape && *p == ',') {
2807 g_string_append_c(norm_utf7, '/');
2808 } else if (in_escape && *p == '-') {
2809 g_string_append_c(norm_utf7, '-');
2812 g_string_append_c(norm_utf7, *p);
2816 norm_utf7_p = norm_utf7->str;
2817 norm_utf7_len = norm_utf7->len;
2818 to_len = strlen(mutf7_str) * 5;
2819 to_p = to_str = g_malloc(to_len + 1);
2821 if (iconv(cd, (ICONV_CONST gchar **)&norm_utf7_p, &norm_utf7_len,
2822 &to_p, &to_len) == -1) {
2823 g_warning(_("iconv cannot convert UTF-7 to %s\n"),
2824 conv_get_locale_charset_str());
2825 g_string_free(norm_utf7, TRUE);
2827 return g_strdup(mutf7_str);
2830 /* second iconv() call for flushing */
2831 iconv(cd, NULL, NULL, &to_p, &to_len);
2832 g_string_free(norm_utf7, TRUE);
2838 static gchar *imap_utf8_to_modified_utf7(const gchar *from)
2840 static iconv_t cd = (iconv_t)-1;
2841 static gboolean iconv_ok = TRUE;
2842 gchar *norm_utf7, *norm_utf7_p;
2843 size_t from_len, norm_utf7_len;
2845 gchar *from_tmp, *to, *p;
2846 gboolean in_escape = FALSE;
2848 if (!iconv_ok) return g_strdup(from);
2850 if (cd == (iconv_t)-1) {
2851 cd = iconv_open(CS_UTF_7, CS_INTERNAL);
2852 if (cd == (iconv_t)-1) {
2853 g_warning(_("iconv cannot convert %s to UTF-7\n"),
2856 return g_strdup(from);
2860 /* UTF-8 to normal UTF-7 conversion */
2861 Xstrdup_a(from_tmp, from, return g_strdup(from));
2862 from_len = strlen(from);
2863 norm_utf7_len = from_len * 5;
2864 Xalloca(norm_utf7, norm_utf7_len + 1, return g_strdup(from));
2865 norm_utf7_p = norm_utf7;
2867 #define IS_PRINT(ch) (isprint(ch) && IS_ASCII(ch))
2869 while (from_len > 0) {
2870 if (*from_tmp == '+') {
2871 *norm_utf7_p++ = '+';
2872 *norm_utf7_p++ = '-';
2876 } else if (IS_PRINT(*(guchar *)from_tmp)) {
2877 /* printable ascii char */
2878 *norm_utf7_p = *from_tmp;
2884 size_t conv_len = 0;
2886 /* unprintable char: convert to UTF-7 */
2888 while (!IS_PRINT(*(guchar *)p) && conv_len < from_len) {
2889 conv_len += g_utf8_skip[*(guchar *)p];
2890 p += g_utf8_skip[*(guchar *)p];
2893 from_len -= conv_len;
2894 if (iconv(cd, (ICONV_CONST gchar **)&from_tmp,
2896 &norm_utf7_p, &norm_utf7_len) == -1) {
2897 g_warning(_("iconv cannot convert UTF-8 to UTF-7\n"));
2898 return g_strdup(from);
2901 /* second iconv() call for flushing */
2902 iconv(cd, NULL, NULL, &norm_utf7_p, &norm_utf7_len);
2908 *norm_utf7_p = '\0';
2909 to_str = g_string_new(NULL);
2910 for (p = norm_utf7; p < norm_utf7_p; p++) {
2911 /* replace: '&' -> "&-",
2914 BASE64 '/' -> ',' */
2915 if (!in_escape && *p == '&') {
2916 g_string_append(to_str, "&-");
2917 } else if (!in_escape && *p == '+') {
2918 if (*(p + 1) == '-') {
2919 g_string_append_c(to_str, '+');
2922 g_string_append_c(to_str, '&');
2925 } else if (in_escape && *p == '/') {
2926 g_string_append_c(to_str, ',');
2927 } else if (in_escape && *p == '-') {
2928 g_string_append_c(to_str, '-');
2931 g_string_append_c(to_str, *p);
2937 g_string_append_c(to_str, '-');
2941 g_string_free(to_str, FALSE);
2946 static gboolean imap_rename_folder_func(GNode *node, gpointer data)
2948 FolderItem *item = node->data;
2949 gchar **paths = data;
2950 const gchar *oldpath = paths[0];
2951 const gchar *newpath = paths[1];
2953 gchar *new_itempath;
2956 oldpathlen = strlen(oldpath);
2957 if (strncmp(oldpath, item->path, oldpathlen) != 0) {
2958 g_warning("path doesn't match: %s, %s\n", oldpath, item->path);
2962 base = item->path + oldpathlen;
2963 while (*base == G_DIR_SEPARATOR) base++;
2965 new_itempath = g_strdup(newpath);
2967 new_itempath = g_strconcat(newpath, G_DIR_SEPARATOR_S, base,
2970 item->path = new_itempath;
2975 typedef struct _get_list_uid_data {
2977 IMAPSession *session;
2978 IMAPFolderItem *item;
2979 GSList **msgnum_list;
2981 } get_list_uid_data;
2983 static void *get_list_of_uids_thread(void *data)
2985 get_list_uid_data *stuff = (get_list_uid_data *)data;
2986 Folder *folder = stuff->folder;
2987 IMAPFolderItem *item = stuff->item;
2988 GSList **msgnum_list = stuff->msgnum_list;
2989 gint ok, nummsgs = 0, lastuid_old;
2990 IMAPSession *session;
2991 GSList *uidlist, *elem;
2992 struct mailimap_set * set;
2993 clist * lep_uidlist;
2996 session = stuff->session;
2997 if (session == NULL) {
2999 return GINT_TO_POINTER(-1);
3001 /* no session locking here, it's already locked by caller */
3002 ok = imap_select(session, IMAP_FOLDER(folder), item->item.path,
3003 NULL, NULL, NULL, NULL, TRUE);
3004 if (ok != IMAP_SUCCESS) {
3006 return GINT_TO_POINTER(-1);
3011 set = mailimap_set_new_interval(item->lastuid + 1, 0);
3013 r = imap_threaded_search(folder, IMAP_SEARCH_TYPE_SIMPLE, set,
3015 if (r == MAILIMAP_NO_ERROR) {
3016 GSList * fetchuid_list;
3019 imap_uid_list_from_lep(lep_uidlist);
3020 uidlist = g_slist_concat(fetchuid_list, uidlist);
3023 GSList * fetchuid_list;
3024 carray * lep_uidtab;
3026 r = imap_threaded_fetch_uid(folder, item->lastuid + 1,
3028 if (r == MAILIMAP_NO_ERROR) {
3030 imap_uid_list_from_lep_tab(lep_uidtab);
3031 uidlist = g_slist_concat(fetchuid_list, uidlist);
3035 lastuid_old = item->lastuid;
3036 *msgnum_list = g_slist_copy(item->uid_list);
3037 nummsgs = g_slist_length(*msgnum_list);
3038 debug_print("Got %d uids from cache\n", g_slist_length(item->uid_list));
3040 for (elem = uidlist; elem != NULL; elem = g_slist_next(elem)) {
3043 msgnum = GPOINTER_TO_INT(elem->data);
3044 if (msgnum > lastuid_old) {
3045 *msgnum_list = g_slist_prepend(*msgnum_list, GINT_TO_POINTER(msgnum));
3046 item->uid_list = g_slist_prepend(item->uid_list, GINT_TO_POINTER(msgnum));
3049 if(msgnum > item->lastuid)
3050 item->lastuid = msgnum;
3053 g_slist_free(uidlist);
3055 return GINT_TO_POINTER(nummsgs);
3058 static gint get_list_of_uids(IMAPSession *session, Folder *folder, IMAPFolderItem *item, GSList **msgnum_list)
3061 get_list_uid_data *data = g_new0(get_list_uid_data, 1);
3063 data->folder = folder;
3065 data->msgnum_list = msgnum_list;
3066 data->session = session;
3067 if (prefs_common.work_offline && !inc_offline_should_override()) {
3072 result = GPOINTER_TO_INT(get_list_of_uids_thread(data));
3078 gint imap_get_num_list(Folder *folder, FolderItem *_item, GSList **msgnum_list, gboolean *old_uids_valid)
3080 IMAPFolderItem *item = (IMAPFolderItem *)_item;
3081 IMAPSession *session;
3082 gint ok, nummsgs = 0, exists, uid_val, uid_next;
3083 GSList *uidlist = NULL;
3085 gboolean selected_folder;
3087 debug_print("get_num_list\n");
3089 g_return_val_if_fail(folder != NULL, -1);
3090 g_return_val_if_fail(item != NULL, -1);
3091 g_return_val_if_fail(item->item.path != NULL, -1);
3092 g_return_val_if_fail(FOLDER_CLASS(folder) == &imap_class, -1);
3093 g_return_val_if_fail(folder->account != NULL, -1);
3095 session = imap_session_get(folder);
3096 g_return_val_if_fail(session != NULL, -1);
3098 statusbar_print_all("Scanning %s...\n", FOLDER_ITEM(item)->path
3099 ? FOLDER_ITEM(item)->path:"");
3101 selected_folder = (session->mbox != NULL) &&
3102 (!strcmp(session->mbox, item->item.path));
3103 if (selected_folder) {
3104 ok = imap_cmd_noop(session);
3105 if (ok != IMAP_SUCCESS) {
3106 debug_print("disconnected!\n");
3107 session = imap_reconnect_if_possible(folder, session);
3108 if (session == NULL) {
3109 statusbar_pop_all();
3114 exists = session->exists;
3116 *old_uids_valid = TRUE;
3118 if (item->use_cache && time(NULL) - item->use_cache < 2) {
3119 exists = item->c_messages;
3120 uid_next = item->c_uid_next;
3121 uid_val = item->c_uid_validity;
3123 debug_print("using cache %d %d %d\n", exists, uid_next, uid_val);
3125 ok = imap_status(session, IMAP_FOLDER(folder), item->item.path, item,
3126 &exists, &uid_next, &uid_val, NULL, FALSE);
3128 item->use_cache = (time_t)0;
3129 if (ok != IMAP_SUCCESS) {
3130 statusbar_pop_all();
3134 if(item->item.mtime == uid_val)
3135 *old_uids_valid = TRUE;
3137 *old_uids_valid = FALSE;
3139 debug_print("Freeing imap uid cache\n");
3141 g_slist_free(item->uid_list);
3142 item->uid_list = NULL;
3144 item->item.mtime = uid_val;
3146 imap_delete_all_cached_messages((FolderItem *)item);
3150 if (!selected_folder)
3151 item->uid_next = uid_next;
3153 /* If old uid_next matches new uid_next we can be sure no message
3154 was added to the folder */
3155 if (( selected_folder && !session->folder_content_changed) ||
3156 (!selected_folder && uid_next == item->uid_next)) {
3157 nummsgs = g_slist_length(item->uid_list);
3159 /* If number of messages is still the same we
3160 know our caches message numbers are still valid,
3161 otherwise if the number of messages has decrease
3162 we discard our cache to start a new scan to find
3163 out which numbers have been removed */
3164 if (exists == nummsgs) {
3165 *msgnum_list = g_slist_copy(item->uid_list);
3166 statusbar_pop_all();
3169 } else if (exists < nummsgs) {
3170 debug_print("Freeing imap uid cache");
3172 g_slist_free(item->uid_list);
3173 item->uid_list = NULL;
3178 *msgnum_list = NULL;
3179 statusbar_pop_all();
3184 nummsgs = get_list_of_uids(session, folder, item, &uidlist);
3187 statusbar_pop_all();
3192 if (nummsgs != exists) {
3193 /* Cache contains more messages then folder, we have cached
3194 an old UID of a message that was removed and new messages
3195 have been added too, otherwise the uid_next check would
3197 debug_print("Freeing imap uid cache");
3199 g_slist_free(item->uid_list);
3200 item->uid_list = NULL;
3202 g_slist_free(*msgnum_list);
3204 nummsgs = get_list_of_uids(session, folder, item, &uidlist);
3207 *msgnum_list = uidlist;
3209 dir = folder_item_get_path((FolderItem *)item);
3210 debug_print("removing old messages from %s\n", dir);
3211 remove_numbered_files_not_in_list(dir, *msgnum_list);
3214 debug_print("get_num_list - ok - %i\n", nummsgs);
3215 statusbar_pop_all();
3220 static MsgInfo *imap_parse_msg(const gchar *file, FolderItem *item)
3225 flags.perm_flags = MSG_NEW|MSG_UNREAD;
3226 flags.tmp_flags = 0;
3228 g_return_val_if_fail(item != NULL, NULL);
3229 g_return_val_if_fail(file != NULL, NULL);
3231 if (folder_has_parent_of_type(item, F_QUEUE)) {
3232 MSG_SET_TMP_FLAGS(flags, MSG_QUEUED);
3233 } else if (folder_has_parent_of_type(item, F_DRAFT)) {
3234 MSG_SET_TMP_FLAGS(flags, MSG_DRAFT);
3237 msginfo = procheader_parse_file(file, flags, FALSE, FALSE);
3238 if (!msginfo) return NULL;
3240 msginfo->plaintext_file = g_strdup(file);
3241 msginfo->folder = item;
3246 GSList *imap_get_msginfos(Folder *folder, FolderItem *item,
3247 GSList *msgnum_list)
3249 IMAPSession *session;
3250 MsgInfoList *ret = NULL;
3253 debug_print("get_msginfos\n");
3255 g_return_val_if_fail(folder != NULL, NULL);
3256 g_return_val_if_fail(item != NULL, NULL);
3257 g_return_val_if_fail(msgnum_list != NULL, NULL);
3259 session = imap_session_get(folder);
3260 g_return_val_if_fail(session != NULL, NULL);
3262 debug_print("IMAP getting msginfos\n");
3263 ok = imap_select(session, IMAP_FOLDER(folder), item->path,
3264 NULL, NULL, NULL, NULL, FALSE);
3265 if (ok != IMAP_SUCCESS) {
3269 if (!(folder_has_parent_of_type(item, F_DRAFT) ||
3270 folder_has_parent_of_type(item, F_QUEUE))) {
3271 ret = g_slist_concat(ret,
3272 imap_get_uncached_messages(session, item,
3275 MsgNumberList *sorted_list, *elem;
3276 gint startnum, lastnum;
3278 sorted_list = g_slist_sort(g_slist_copy(msgnum_list), g_int_compare);
3280 startnum = lastnum = GPOINTER_TO_INT(sorted_list->data);
3282 for (elem = sorted_list;; elem = g_slist_next(elem)) {
3286 num = GPOINTER_TO_INT(elem->data);
3288 if (num > lastnum + 1 || elem == NULL) {
3290 for (i = startnum; i <= lastnum; ++i) {
3293 file = imap_fetch_msg(folder, item, i);
3295 MsgInfo *msginfo = imap_parse_msg(file, item);
3296 if (msginfo != NULL) {
3297 msginfo->msgnum = i;
3298 ret = g_slist_append(ret, msginfo);
3312 g_slist_free(sorted_list);
3318 MsgInfo *imap_get_msginfo(Folder *folder, FolderItem *item, gint uid)
3320 MsgInfo *msginfo = NULL;
3321 MsgInfoList *msginfolist;
3322 MsgNumberList numlist;
3324 numlist.next = NULL;
3325 numlist.data = GINT_TO_POINTER(uid);
3327 msginfolist = imap_get_msginfos(folder, item, &numlist);
3328 if (msginfolist != NULL) {
3329 msginfo = msginfolist->data;
3330 g_slist_free(msginfolist);
3336 gboolean imap_scan_required(Folder *folder, FolderItem *_item)
3338 IMAPSession *session;
3339 IMAPFolderItem *item = (IMAPFolderItem *)_item;
3340 gint ok, exists = 0, unseen = 0;
3341 guint32 uid_next, uid_val;
3342 gboolean selected_folder;
3344 g_return_val_if_fail(folder != NULL, FALSE);
3345 g_return_val_if_fail(item != NULL, FALSE);
3346 g_return_val_if_fail(item->item.folder != NULL, FALSE);
3347 g_return_val_if_fail(FOLDER_CLASS(item->item.folder) == &imap_class, FALSE);
3349 if (item->item.path == NULL)
3352 session = imap_session_get(folder);
3353 g_return_val_if_fail(session != NULL, FALSE);
3355 selected_folder = (session->mbox != NULL) &&
3356 (!strcmp(session->mbox, item->item.path));
3357 if (selected_folder) {
3358 ok = imap_cmd_noop(session);
3359 if (ok != IMAP_SUCCESS) {
3360 debug_print("disconnected!\n");
3361 session = imap_reconnect_if_possible(folder, session);
3362 if (session == NULL)
3367 if (session->folder_content_changed
3368 || session->exists != item->item.total_msgs) {
3373 ok = imap_status(session, IMAP_FOLDER(folder), item->item.path, IMAP_FOLDER_ITEM(item),
3374 &exists, &uid_next, &uid_val, &unseen, FALSE);
3375 if (ok != IMAP_SUCCESS) {
3380 item->use_cache = time(NULL);
3381 item->c_messages = exists;
3382 item->c_uid_next = uid_next;
3383 item->c_uid_validity = uid_val;
3384 item->c_unseen = unseen;
3386 if ((uid_next != item->uid_next) || (exists != item->item.total_msgs)) {
3395 void imap_change_flags(Folder *folder, FolderItem *item, MsgInfo *msginfo, MsgPermFlags newflags)
3397 IMAPSession *session;
3398 IMAPFlags flags_set = 0, flags_unset = 0;
3399 gint ok = IMAP_SUCCESS;
3400 MsgNumberList numlist;
3401 hashtable_data *ht_data = NULL;
3403 g_return_if_fail(folder != NULL);
3404 g_return_if_fail(folder->klass == &imap_class);
3405 g_return_if_fail(item != NULL);
3406 g_return_if_fail(item->folder == folder);
3407 g_return_if_fail(msginfo != NULL);
3408 g_return_if_fail(msginfo->folder == item);
3410 session = imap_session_get(folder);
3415 if ((ok = imap_select(session, IMAP_FOLDER(folder), msginfo->folder->path,
3416 NULL, NULL, NULL, NULL, FALSE)) != IMAP_SUCCESS) {
3421 if (!MSG_IS_MARKED(msginfo->flags) && (newflags & MSG_MARKED))
3422 flags_set |= IMAP_FLAG_FLAGGED;
3423 if ( MSG_IS_MARKED(msginfo->flags) && !(newflags & MSG_MARKED))
3424 flags_unset |= IMAP_FLAG_FLAGGED;
3426 if (!MSG_IS_UNREAD(msginfo->flags) && (newflags & MSG_UNREAD))
3427 flags_unset |= IMAP_FLAG_SEEN;
3428 if ( MSG_IS_UNREAD(msginfo->flags) && !(newflags & MSG_UNREAD))
3429 flags_set |= IMAP_FLAG_SEEN;
3431 if (!MSG_IS_REPLIED(msginfo->flags) && (newflags & MSG_REPLIED))
3432 flags_set |= IMAP_FLAG_ANSWERED;
3433 if ( MSG_IS_REPLIED(msginfo->flags) && !(newflags & MSG_REPLIED))
3434 flags_unset |= IMAP_FLAG_ANSWERED;
3436 if (!MSG_IS_DELETED(msginfo->flags) && (newflags & MSG_DELETED))
3437 flags_set |= IMAP_FLAG_DELETED;
3438 if ( MSG_IS_DELETED(msginfo->flags) && !(newflags & MSG_DELETED))
3439 flags_unset |= IMAP_FLAG_DELETED;
3441 numlist.next = NULL;
3442 numlist.data = GINT_TO_POINTER(msginfo->msgnum);
3444 if (IMAP_FOLDER_ITEM(item)->batching) {
3445 /* instead of performing an UID STORE command for each message change,
3446 * as a lot of them can change "together", we just fill in hashtables
3447 * and defer the treatment so that we're able to send only one
3450 debug_print("IMAP batch mode on, deferring flags change\n");
3452 ht_data = g_hash_table_lookup(IMAP_FOLDER_ITEM(item)->flags_set_table,
3453 GINT_TO_POINTER(flags_set));
3454 if (ht_data == NULL) {
3455 ht_data = g_new0(hashtable_data, 1);
3456 ht_data->session = session;
3457 ht_data->item = IMAP_FOLDER_ITEM(item);
3458 g_hash_table_insert(IMAP_FOLDER_ITEM(item)->flags_set_table,
3459 GINT_TO_POINTER(flags_set), ht_data);
3461 if (!g_slist_find(ht_data->msglist, GINT_TO_POINTER(msginfo->msgnum)))
3462 ht_data->msglist = g_slist_prepend(ht_data->msglist, GINT_TO_POINTER(msginfo->msgnum));
3465 ht_data = g_hash_table_lookup(IMAP_FOLDER_ITEM(item)->flags_unset_table,
3466 GINT_TO_POINTER(flags_unset));
3467 if (ht_data == NULL) {
3468 ht_data = g_new0(hashtable_data, 1);
3469 ht_data->session = session;
3470 ht_data->item = IMAP_FOLDER_ITEM(item);
3471 g_hash_table_insert(IMAP_FOLDER_ITEM(item)->flags_unset_table,
3472 GINT_TO_POINTER(flags_unset), ht_data);
3474 if (!g_slist_find(ht_data->msglist, GINT_TO_POINTER(msginfo->msgnum)))
3475 ht_data->msglist = g_slist_prepend(ht_data->msglist,
3476 GINT_TO_POINTER(msginfo->msgnum));
3479 debug_print("IMAP changing flags\n");
3481 ok = imap_set_message_flags(session, &numlist, flags_set, TRUE);
3482 if (ok != IMAP_SUCCESS) {
3489 ok = imap_set_message_flags(session, &numlist, flags_unset, FALSE);
3490 if (ok != IMAP_SUCCESS) {
3496 msginfo->flags.perm_flags = newflags;
3501 static gint imap_remove_msg(Folder *folder, FolderItem *item, gint uid)
3504 IMAPSession *session;
3506 MsgNumberList numlist;
3508 g_return_val_if_fail(folder != NULL, -1);
3509 g_return_val_if_fail(FOLDER_CLASS(folder) == &imap_class, -1);
3510 g_return_val_if_fail(item != NULL, -1);
3512 session = imap_session_get(folder);
3513 if (!session) return -1;
3515 ok = imap_select(session, IMAP_FOLDER(folder), item->path,
3516 NULL, NULL, NULL, NULL, FALSE);
3517 if (ok != IMAP_SUCCESS) {
3521 numlist.next = NULL;
3522 numlist.data = GINT_TO_POINTER(uid);
3524 ok = imap_set_message_flags
3525 (IMAP_SESSION(REMOTE_FOLDER(folder)->session),
3526 &numlist, IMAP_FLAG_DELETED, TRUE);
3527 if (ok != IMAP_SUCCESS) {
3528 log_warning(_("can't set deleted flags: %d\n"), uid);
3533 if (!session->uidplus) {
3534 ok = imap_cmd_expunge(session);
3538 uidstr = g_strdup_printf("%u", uid);
3539 ok = imap_cmd_expunge(session);
3542 if (ok != IMAP_SUCCESS) {
3543 log_warning(_("can't expunge\n"));
3548 IMAP_FOLDER_ITEM(item)->uid_list = g_slist_remove(
3549 IMAP_FOLDER_ITEM(item)->uid_list, numlist.data);
3550 dir = folder_item_get_path(item);
3551 if (is_dir_exist(dir))
3552 remove_numbered_files(dir, uid, uid);
3555 return IMAP_SUCCESS;
3558 static gint compare_msginfo(gconstpointer a, gconstpointer b)
3560 return ((MsgInfo *)a)->msgnum - ((MsgInfo *)b)->msgnum;
3563 static guint gslist_find_next_num(MsgNumberList **list, guint num)
3567 g_return_val_if_fail(list != NULL, -1);
3569 for (elem = *list; elem != NULL; elem = g_slist_next(elem))
3570 if (GPOINTER_TO_INT(elem->data) >= num)
3573 return elem != NULL ? GPOINTER_TO_INT(elem->data) : (gint)-1;
3577 * NEW and DELETED flags are not syncronized
3578 * - The NEW/RECENT flags in IMAP folders can not really be directly
3579 * modified by Sylpheed
3580 * - The DELETE/DELETED flag in IMAP and Sylpheed don't have the same
3581 * meaning, in IMAP it always removes the messages from the FolderItem
3582 * in Sylpheed it can mean to move the message to trash
3585 typedef struct _get_flags_data {
3588 MsgInfoList *msginfo_list;
3589 GRelation *msgflags;
3590 gboolean full_search;
3594 static /*gint*/ void *imap_get_flags_thread(void *data)
3596 get_flags_data *stuff = (get_flags_data *)data;
3597 Folder *folder = stuff->folder;
3598 FolderItem *item = stuff->item;
3599 MsgInfoList *msginfo_list = stuff->msginfo_list;
3600 GRelation *msgflags = stuff->msgflags;
3601 gboolean full_search = stuff->full_search;
3602 IMAPSession *session;
3603 GSList *sorted_list = NULL;
3604 GSList *unseen = NULL, *answered = NULL, *flagged = NULL, *deleted = NULL;
3605 GSList *p_unseen, *p_answered, *p_flagged, *p_deleted;
3607 GSList *seq_list, *cur;
3608 gboolean reverse_seen = FALSE;
3611 gint exists_cnt, unseen_cnt;
3612 gboolean selected_folder;
3614 if (folder == NULL || item == NULL) {
3616 return GINT_TO_POINTER(-1);
3619 session = imap_session_get(folder);
3620 if (session == NULL) {
3622 return GINT_TO_POINTER(-1);
3625 selected_folder = (session->mbox != NULL) &&
3626 (!strcmp(session->mbox, item->path));
3628 if (!selected_folder) {
3629 ok = imap_select(session, IMAP_FOLDER(folder), item->path,
3630 &exists_cnt, NULL, &unseen_cnt, NULL, TRUE);
3631 if (ok != IMAP_SUCCESS) {
3634 return GINT_TO_POINTER(-1);
3637 if (unseen_cnt > exists_cnt / 2)
3638 reverse_seen = TRUE;
3641 if (item->unread_msgs > item->total_msgs / 2)
3642 reverse_seen = TRUE;
3645 cmd_buf = g_string_new(NULL);
3647 sorted_list = g_slist_sort(g_slist_copy(msginfo_list), compare_msginfo);
3649 seq_list = imap_get_lep_set_from_msglist(msginfo_list);
3651 struct mailimap_set * set;
3652 set = mailimap_set_new_interval(1, 0);
3653 seq_list = g_slist_append(NULL, set);
3656 for (cur = seq_list; cur != NULL; cur = g_slist_next(cur)) {
3657 struct mailimap_set * imapset;
3658 clist * lep_uidlist;
3661 imapset = cur->data;
3663 r = imap_threaded_search(folder, IMAP_SEARCH_TYPE_SEEN,
3664 imapset, &lep_uidlist);
3667 r = imap_threaded_search(folder,
3668 IMAP_SEARCH_TYPE_UNSEEN,
3669 imapset, &lep_uidlist);
3671 if (r == MAILIMAP_NO_ERROR) {
3674 uidlist = imap_uid_list_from_lep(lep_uidlist);
3675 mailimap_search_result_free(lep_uidlist);
3677 unseen = g_slist_concat(unseen, uidlist);
3680 r = imap_threaded_search(folder, IMAP_SEARCH_TYPE_ANSWERED,
3681 imapset, &lep_uidlist);
3682 if (r == MAILIMAP_NO_ERROR) {
3685 uidlist = imap_uid_list_from_lep(lep_uidlist);
3686 mailimap_search_result_free(lep_uidlist);
3688 answered = g_slist_concat(answered, uidlist);
3691 r = imap_threaded_search(folder, IMAP_SEARCH_TYPE_FLAGGED,
3692 imapset, &lep_uidlist);
3693 if (r == MAILIMAP_NO_ERROR) {
3696 uidlist = imap_uid_list_from_lep(lep_uidlist);
3697 mailimap_search_result_free(lep_uidlist);
3699 flagged = g_slist_concat(flagged, uidlist);
3702 r = imap_threaded_search(folder, IMAP_SEARCH_TYPE_DELETED,
3703 imapset, &lep_uidlist);
3704 if (r == MAILIMAP_NO_ERROR) {
3707 uidlist = imap_uid_list_from_lep(lep_uidlist);
3708 mailimap_search_result_free(lep_uidlist);
3710 deleted = g_slist_concat(deleted, uidlist);
3715 p_answered = answered;
3716 p_flagged = flagged;
3717 p_deleted = deleted;
3719 for (elem = sorted_list; elem != NULL; elem = g_slist_next(elem)) {
3724 msginfo = (MsgInfo *) elem->data;
3725 flags = msginfo->flags.perm_flags;
3726 wasnew = (flags & MSG_NEW);
3727 flags &= ~((reverse_seen ? 0 : MSG_UNREAD | MSG_NEW) | MSG_REPLIED | MSG_MARKED);
3729 flags |= MSG_UNREAD | (wasnew ? MSG_NEW : 0);
3730 if (gslist_find_next_num(&p_unseen, msginfo->msgnum) == msginfo->msgnum) {
3731 if (!reverse_seen) {
3732 flags |= MSG_UNREAD | (wasnew ? MSG_NEW : 0);
3734 flags &= ~(MSG_UNREAD | MSG_NEW);
3737 if (gslist_find_next_num(&p_answered, msginfo->msgnum) == msginfo->msgnum)
3738 flags |= MSG_REPLIED;
3740 flags &= ~MSG_REPLIED;
3741 if (gslist_find_next_num(&p_flagged, msginfo->msgnum) == msginfo->msgnum)
3742 flags |= MSG_MARKED;
3744 flags &= ~MSG_MARKED;
3745 if (gslist_find_next_num(&p_deleted, msginfo->msgnum) == msginfo->msgnum)
3746 flags |= MSG_DELETED;
3748 flags &= ~MSG_DELETED;
3749 g_relation_insert(msgflags, msginfo, GINT_TO_POINTER(flags));
3752 imap_lep_set_free(seq_list);
3753 g_slist_free(flagged);
3754 g_slist_free(deleted);
3755 g_slist_free(answered);
3756 g_slist_free(unseen);
3757 g_slist_free(sorted_list);
3758 g_string_free(cmd_buf, TRUE);
3762 return GINT_TO_POINTER(0);
3765 static gint imap_get_flags(Folder *folder, FolderItem *item,
3766 MsgInfoList *msginfo_list, GRelation *msgflags)
3769 get_flags_data *data = g_new0(get_flags_data, 1);
3771 data->folder = folder;
3773 data->msginfo_list = msginfo_list;
3774 data->msgflags = msgflags;
3775 data->full_search = FALSE;
3777 GSList *tmp = NULL, *cur;
3779 if (prefs_common.work_offline && !inc_offline_should_override()) {
3784 tmp = folder_item_get_msg_list(item);
3786 if (g_slist_length(tmp) == g_slist_length(msginfo_list))
3787 data->full_search = TRUE;
3789 for (cur = tmp; cur; cur = cur->next)
3790 procmsg_msginfo_free((MsgInfo *)cur->data);
3794 result = GPOINTER_TO_INT(imap_get_flags_thread(data));
3801 static gboolean process_flags(gpointer key, gpointer value, gpointer user_data)
3803 gboolean flags_set = GPOINTER_TO_INT(user_data);
3804 gint flags_value = GPOINTER_TO_INT(key);
3805 hashtable_data *data = (hashtable_data *)value;
3806 IMAPFolderItem *_item = data->item;
3807 FolderItem *item = (FolderItem *)_item;
3808 gint ok = IMAP_ERROR;
3809 IMAPSession *session = imap_session_get(item->folder);
3811 data->msglist = g_slist_reverse(data->msglist);
3813 debug_print("IMAP %ssetting flags to %d for %d messages\n",
3816 g_slist_length(data->msglist));
3820 ok = imap_select(session, IMAP_FOLDER(item->folder), item->path,
3821 NULL, NULL, NULL, NULL, FALSE);
3823 if (ok == IMAP_SUCCESS) {
3824 imap_set_message_flags(data->session, data->msglist, flags_value, flags_set);
3826 g_warning("can't select mailbox %s\n", item->path);
3830 g_slist_free(data->msglist);
3835 static void process_hashtable(IMAPFolderItem *item)
3837 if (item->flags_set_table) {
3838 g_hash_table_foreach_remove(item->flags_set_table, process_flags, GINT_TO_POINTER(TRUE));
3839 g_free(item->flags_set_table);
3840 item->flags_set_table = NULL;
3842 if (item->flags_unset_table) {
3843 g_hash_table_foreach_remove(item->flags_unset_table, process_flags, GINT_TO_POINTER(FALSE));
3844 g_free(item->flags_unset_table);
3845 item->flags_unset_table = NULL;
3849 static void imap_set_batch (Folder *folder, FolderItem *_item, gboolean batch)
3851 IMAPFolderItem *item = (IMAPFolderItem *)_item;
3853 g_return_if_fail(item != NULL);
3855 if (item->batching == batch)
3859 item->batching = TRUE;
3860 debug_print("IMAP switching to batch mode\n");
3861 if (!item->flags_set_table) {
3862 item->flags_set_table = g_hash_table_new(NULL, g_direct_equal);
3864 if (!item->flags_unset_table) {
3865 item->flags_unset_table = g_hash_table_new(NULL, g_direct_equal);
3868 debug_print("IMAP switching away from batch mode\n");
3870 process_hashtable(item);
3871 item->batching = FALSE;
3877 /* data types conversion libetpan <-> sylpheed */
3881 #define ETPAN_IMAP_MB_MARKED 1
3882 #define ETPAN_IMAP_MB_UNMARKED 2
3883 #define ETPAN_IMAP_MB_NOSELECT 4
3884 #define ETPAN_IMAP_MB_NOINFERIORS 8
3886 static int imap_flags_to_flags(struct mailimap_mbx_list_flags * imap_flags)
3892 if (imap_flags->mbf_type == MAILIMAP_MBX_LIST_FLAGS_SFLAG) {
3893 switch (imap_flags->mbf_sflag) {
3894 case MAILIMAP_MBX_LIST_SFLAG_MARKED:
3895 flags |= ETPAN_IMAP_MB_MARKED;
3897 case MAILIMAP_MBX_LIST_SFLAG_NOSELECT:
3898 flags |= ETPAN_IMAP_MB_NOSELECT;
3900 case MAILIMAP_MBX_LIST_SFLAG_UNMARKED:
3901 flags |= ETPAN_IMAP_MB_UNMARKED;
3906 for(cur = clist_begin(imap_flags->mbf_oflags) ; cur != NULL ;
3907 cur = clist_next(cur)) {
3908 struct mailimap_mbx_list_oflag * oflag;
3910 oflag = clist_content(cur);
3912 switch (oflag->of_type) {
3913 case MAILIMAP_MBX_LIST_OFLAG_NOINFERIORS:
3914 flags |= ETPAN_IMAP_MB_NOINFERIORS;
3922 static GSList * imap_list_from_lep(IMAPFolder * folder,
3923 clist * list, const gchar * real_path, gboolean all)
3930 for(iter = clist_begin(list) ; iter != NULL ;
3931 iter = clist_next(iter)) {
3932 struct mailimap_mailbox_list * mb;
3940 FolderItem *new_item;
3942 mb = clist_content(iter);
3948 if (mb->mb_flag != NULL)
3949 flags = imap_flags_to_flags(mb->mb_flag);
3951 delimiter = mb->mb_delimiter;
3954 dup_name = strdup(name);
3955 if (delimiter != '\0')
3956 subst_char(dup_name, delimiter, '/');
3958 base = g_path_get_basename(dup_name);
3959 if (base[0] == '.') {
3965 if (!all && strcmp(dup_name, real_path) == 0) {
3971 if (!all && dup_name[strlen(dup_name)-1] == '/') {
3977 loc_name = imap_modified_utf7_to_utf8(base);
3978 loc_path = imap_modified_utf7_to_utf8(dup_name);
3980 new_item = folder_item_new(FOLDER(folder), loc_name, loc_path);
3981 if ((flags & ETPAN_IMAP_MB_NOINFERIORS) != 0)
3982 new_item->no_sub = TRUE;
3983 if (strcmp(dup_name, "INBOX") != 0 &&
3984 ((flags & ETPAN_IMAP_MB_NOSELECT) != 0))
3985 new_item->no_select = TRUE;
3987 item_list = g_slist_append(item_list, new_item);
3989 debug_print("folder '%s' found.\n", loc_path);
4000 static GSList * imap_get_lep_set_from_numlist(MsgNumberList *numlist)
4002 GSList *sorted_list, *cur;
4003 guint first, last, next;
4004 GSList *ret_list = NULL;
4006 struct mailimap_set * current_set;
4007 unsigned int item_count;
4009 if (numlist == NULL)
4013 current_set = mailimap_set_new_empty();
4015 sorted_list = g_slist_copy(numlist);
4016 sorted_list = g_slist_sort(sorted_list, g_int_compare);
4018 first = GPOINTER_TO_INT(sorted_list->data);
4021 for (cur = sorted_list; cur != NULL; cur = g_slist_next(cur)) {
4022 if (GPOINTER_TO_INT(cur->data) == 0)
4027 last = GPOINTER_TO_INT(cur->data);
4029 next = GPOINTER_TO_INT(cur->next->data);
4033 if (last + 1 != next || next == 0) {
4035 struct mailimap_set_item * item;
4036 item = mailimap_set_item_new(first, last);
4037 mailimap_set_add(current_set, item);
4042 if (count >= IMAP_SET_MAX_COUNT) {
4043 ret_list = g_slist_append(ret_list,
4045 current_set = mailimap_set_new_empty();
4052 if (clist_count(current_set->set_list) > 0) {
4053 ret_list = g_slist_append(ret_list,
4057 g_slist_free(sorted_list);
4062 static GSList * imap_get_lep_set_from_msglist(MsgInfoList *msglist)
4064 MsgNumberList *numlist = NULL;
4068 for (cur = msglist; cur != NULL; cur = g_slist_next(cur)) {
4069 MsgInfo *msginfo = (MsgInfo *) cur->data;
4071 numlist = g_slist_append(numlist, GINT_TO_POINTER(msginfo->msgnum));
4073 seq_list = imap_get_lep_set_from_numlist(numlist);
4074 g_slist_free(numlist);
4079 static GSList * imap_uid_list_from_lep(clist * list)
4086 for(iter = clist_begin(list) ; iter != NULL ;
4087 iter = clist_next(iter)) {
4090 puid = clist_content(iter);
4091 result = g_slist_append(result, GINT_TO_POINTER(* puid));
4097 static GSList * imap_uid_list_from_lep_tab(carray * list)
4104 for(i = 0 ; i < carray_count(list) ; i ++) {
4107 puid = carray_get(list, i);
4108 result = g_slist_append(result, GINT_TO_POINTER(* puid));
4114 static MsgInfo *imap_envelope_from_lep(struct imap_fetch_env_info * info,
4117 MsgInfo *msginfo = NULL;
4120 MsgFlags flags = {0, 0};
4122 if (info->headers == NULL)
4125 MSG_SET_TMP_FLAGS(flags, MSG_IMAP);
4126 if (folder_has_parent_of_type(item, F_QUEUE)) {
4127 MSG_SET_TMP_FLAGS(flags, MSG_QUEUED);
4128 } else if (folder_has_parent_of_type(item, F_DRAFT)) {
4129 MSG_SET_TMP_FLAGS(flags, MSG_DRAFT);
4131 flags.perm_flags = info->flags;
4135 msginfo = procheader_parse_str(info->headers, flags, FALSE, FALSE);
4138 msginfo->msgnum = uid;
4139 msginfo->size = size;
4145 static void imap_lep_set_free(GSList *seq_list)
4149 for(cur = seq_list ; cur != NULL ; cur = g_slist_next(cur)) {
4150 struct mailimap_set * imapset;
4152 imapset = cur->data;
4153 mailimap_set_free(imapset);
4155 g_slist_free(seq_list);
4158 static struct mailimap_flag_list * imap_flag_to_lep(IMAPFlags flags)
4160 struct mailimap_flag_list * flag_list;
4162 flag_list = mailimap_flag_list_new_empty();
4164 if (IMAP_IS_SEEN(flags))
4165 mailimap_flag_list_add(flag_list,
4166 mailimap_flag_new_seen());
4167 if (IMAP_IS_ANSWERED(flags))
4168 mailimap_flag_list_add(flag_list,
4169 mailimap_flag_new_answered());
4170 if (IMAP_IS_FLAGGED(flags))
4171 mailimap_flag_list_add(flag_list,
4172 mailimap_flag_new_flagged());
4173 if (IMAP_IS_DELETED(flags))
4174 mailimap_flag_list_add(flag_list,
4175 mailimap_flag_new_deleted());
4176 if (IMAP_IS_DRAFT(flags))
4177 mailimap_flag_list_add(flag_list,
4178 mailimap_flag_new_draft());
4183 guint imap_folder_get_refcnt(Folder *folder)
4185 return ((IMAPFolder *)folder)->refcnt;
4188 void imap_folder_ref(Folder *folder)
4190 ((IMAPFolder *)folder)->refcnt++;
4193 void imap_folder_unref(Folder *folder)
4195 if (((IMAPFolder *)folder)->refcnt > 0)
4196 ((IMAPFolder *)folder)->refcnt--;
4199 #else /* HAVE_LIBETPAN */
4201 static FolderClass imap_class;
4203 static Folder *imap_folder_new (const gchar *name,
4208 static gint imap_create_tree (Folder *folder)
4212 static FolderItem *imap_create_folder (Folder *folder,
4218 static gint imap_rename_folder (Folder *folder,
4225 FolderClass *imap_get_class(void)
4227 if (imap_class.idstr == NULL) {
4228 imap_class.type = F_IMAP;
4229 imap_class.idstr = "imap";
4230 imap_class.uistr = "IMAP4";
4232 imap_class.new_folder = imap_folder_new;
4233 imap_class.create_tree = imap_create_tree;
4234 imap_class.create_folder = imap_create_folder;
4235 imap_class.rename_folder = imap_rename_folder;
4236 /* nothing implemented */
4243 void imap_synchronise(FolderItem *item)
4245 imap_gtk_synchronise(item);