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;
112 struct _IMAPNameSpace
118 #define IMAP_SUCCESS 0
119 #define IMAP_SOCKET 2
120 #define IMAP_AUTHFAIL 3
121 #define IMAP_PROTOCOL 4
122 #define IMAP_SYNTAX 5
126 #define IMAPBUFSIZE 8192
130 IMAP_FLAG_SEEN = 1 << 0,
131 IMAP_FLAG_ANSWERED = 1 << 1,
132 IMAP_FLAG_FLAGGED = 1 << 2,
133 IMAP_FLAG_DELETED = 1 << 3,
134 IMAP_FLAG_DRAFT = 1 << 4
137 #define IMAP_IS_SEEN(flags) ((flags & IMAP_FLAG_SEEN) != 0)
138 #define IMAP_IS_ANSWERED(flags) ((flags & IMAP_FLAG_ANSWERED) != 0)
139 #define IMAP_IS_FLAGGED(flags) ((flags & IMAP_FLAG_FLAGGED) != 0)
140 #define IMAP_IS_DELETED(flags) ((flags & IMAP_FLAG_DELETED) != 0)
141 #define IMAP_IS_DRAFT(flags) ((flags & IMAP_FLAG_DRAFT) != 0)
144 #define IMAP4_PORT 143
146 #define IMAPS_PORT 993
149 #define IMAP_CMD_LIMIT 1000
151 struct _IMAPFolderItem
161 static void imap_folder_init (Folder *folder,
165 static Folder *imap_folder_new (const gchar *name,
167 static void imap_folder_destroy (Folder *folder);
169 static IMAPSession *imap_session_new (Folder *folder,
170 const PrefsAccount *account);
171 static void imap_session_authenticate(IMAPSession *session,
172 const PrefsAccount *account);
173 static void imap_session_destroy (Session *session);
175 static gchar *imap_fetch_msg (Folder *folder,
178 static gchar *imap_fetch_msg_full (Folder *folder,
183 static gint imap_add_msg (Folder *folder,
187 static gint imap_add_msgs (Folder *folder,
190 GRelation *relation);
192 static gint imap_copy_msg (Folder *folder,
195 static gint imap_copy_msgs (Folder *folder,
197 MsgInfoList *msglist,
198 GRelation *relation);
200 static gint imap_remove_msg (Folder *folder,
203 static gint imap_remove_msgs (Folder *folder,
205 MsgInfoList *msglist,
206 GRelation *relation);
207 static gint imap_remove_all_msg (Folder *folder,
210 static gboolean imap_is_msg_changed (Folder *folder,
214 static gint imap_close (Folder *folder,
217 static gint imap_scan_tree (Folder *folder);
219 static gint imap_create_tree (Folder *folder);
221 static FolderItem *imap_create_folder (Folder *folder,
224 static gint imap_rename_folder (Folder *folder,
227 static gint imap_remove_folder (Folder *folder,
230 static FolderItem *imap_folder_item_new (Folder *folder);
231 static void imap_folder_item_destroy (Folder *folder,
234 static IMAPSession *imap_session_get (Folder *folder);
236 static gint imap_auth (IMAPSession *session,
241 static gint imap_scan_tree_recursive (IMAPSession *session,
244 static void imap_create_missing_folders (Folder *folder);
245 static FolderItem *imap_create_special_folder
247 SpecialFolderItemType stype,
250 static gint imap_do_copy_msgs (Folder *folder,
252 MsgInfoList *msglist,
253 GRelation *relation);
255 static void imap_delete_all_cached_messages (FolderItem *item);
256 static void imap_set_batch (Folder *folder,
259 static gint imap_set_message_flags (IMAPSession *session,
260 MsgNumberList *numlist,
263 static gint imap_select (IMAPSession *session,
269 guint32 *uid_validity,
271 static gint imap_status (IMAPSession *session,
277 guint32 *uid_validity,
281 static IMAPNameSpace *imap_find_namespace (IMAPFolder *folder,
283 static gchar imap_get_path_separator (IMAPFolder *folder,
285 static gchar *imap_get_real_path (IMAPFolder *folder,
287 static void imap_synchronise (FolderItem *item);
289 static void imap_free_capabilities (IMAPSession *session);
291 /* low-level IMAP4rev1 commands */
292 static gint imap_cmd_login (IMAPSession *session,
296 static gint imap_cmd_logout (IMAPSession *session);
297 static gint imap_cmd_noop (IMAPSession *session);
299 static gint imap_cmd_starttls (IMAPSession *session);
301 static gint imap_cmd_select (IMAPSession *session,
306 guint32 *uid_validity,
308 static gint imap_cmd_examine (IMAPSession *session,
313 guint32 *uid_validity,
315 static gint imap_cmd_create (IMAPSession *sock,
316 const gchar *folder);
317 static gint imap_cmd_rename (IMAPSession *sock,
318 const gchar *oldfolder,
319 const gchar *newfolder);
320 static gint imap_cmd_delete (IMAPSession *session,
321 const gchar *folder);
322 static gint imap_cmd_fetch (IMAPSession *sock,
324 const gchar *filename,
327 static gint imap_cmd_append (IMAPSession *session,
328 const gchar *destfolder,
332 static gint imap_cmd_copy (IMAPSession *session,
333 struct mailimap_set * set,
334 const gchar *destfolder,
335 GRelation *uid_mapping);
336 static gint imap_cmd_store (IMAPSession *session,
337 struct mailimap_set * set,
340 static gint imap_cmd_expunge (IMAPSession *session);
342 static void imap_path_separator_subst (gchar *str,
345 static gchar *imap_utf8_to_modified_utf7 (const gchar *from);
346 static gchar *imap_modified_utf7_to_utf8 (const gchar *mutf7_str);
348 static gboolean imap_rename_folder_func (GNode *node,
350 static gint imap_get_num_list (Folder *folder,
353 gboolean *old_uids_valid);
354 static GSList *imap_get_msginfos (Folder *folder,
356 GSList *msgnum_list);
357 static MsgInfo *imap_get_msginfo (Folder *folder,
360 static gboolean imap_scan_required (Folder *folder,
362 static void imap_change_flags (Folder *folder,
365 MsgPermFlags newflags);
366 static gint imap_get_flags (Folder *folder,
368 MsgInfoList *msglist,
369 GRelation *msgflags);
370 static gchar *imap_folder_get_path (Folder *folder);
371 static gchar *imap_item_get_path (Folder *folder,
373 static MsgInfo *imap_parse_msg(const gchar *file, FolderItem *item);
376 /* data types conversion libetpan <-> sylpheed */
377 static GSList * imap_list_from_lep(IMAPFolder * folder,
378 clist * list, const gchar * real_path);
379 static GSList * imap_get_lep_set_from_numlist(MsgNumberList *numlist);
380 static GSList * imap_get_lep_set_from_msglist(MsgInfoList *msglist);
381 static GSList * imap_uid_list_from_lep(clist * list);
382 static GSList * imap_uid_list_from_lep_tab(carray * list);
383 static MsgInfo *imap_envelope_from_lep(struct imap_fetch_env_info * info,
385 static void imap_lep_set_free(GSList *seq_list);
386 static struct mailimap_flag_list * imap_flag_to_lep(IMAPFlags flags);
389 static GHashTable *flags_set_table = NULL;
390 static GHashTable *flags_unset_table = NULL;
391 typedef struct _hashtable_data {
392 IMAPSession *session;
396 static FolderClass imap_class;
398 typedef struct _thread_data {
408 FolderClass *imap_get_class(void)
410 if (imap_class.idstr == NULL) {
411 imap_class.type = F_IMAP;
412 imap_class.idstr = "imap";
413 imap_class.uistr = "IMAP4";
415 /* Folder functions */
416 imap_class.new_folder = imap_folder_new;
417 imap_class.destroy_folder = imap_folder_destroy;
418 imap_class.scan_tree = imap_scan_tree;
419 imap_class.create_tree = imap_create_tree;
421 /* FolderItem functions */
422 imap_class.item_new = imap_folder_item_new;
423 imap_class.item_destroy = imap_folder_item_destroy;
424 imap_class.item_get_path = imap_item_get_path;
425 imap_class.create_folder = imap_create_folder;
426 imap_class.rename_folder = imap_rename_folder;
427 imap_class.remove_folder = imap_remove_folder;
428 imap_class.close = imap_close;
429 imap_class.get_num_list = imap_get_num_list;
430 imap_class.scan_required = imap_scan_required;
432 /* Message functions */
433 imap_class.get_msginfo = imap_get_msginfo;
434 imap_class.get_msginfos = imap_get_msginfos;
435 imap_class.fetch_msg = imap_fetch_msg;
436 imap_class.fetch_msg_full = imap_fetch_msg_full;
437 imap_class.add_msg = imap_add_msg;
438 imap_class.add_msgs = imap_add_msgs;
439 imap_class.copy_msg = imap_copy_msg;
440 imap_class.copy_msgs = imap_copy_msgs;
441 imap_class.remove_msg = imap_remove_msg;
442 imap_class.remove_msgs = imap_remove_msgs;
443 imap_class.remove_all_msg = imap_remove_all_msg;
444 imap_class.is_msg_changed = imap_is_msg_changed;
445 imap_class.change_flags = imap_change_flags;
446 imap_class.get_flags = imap_get_flags;
447 imap_class.set_batch = imap_set_batch;
448 imap_class.synchronise = imap_synchronise;
450 pthread_mutex_init(&imap_mutex, NULL);
457 static Folder *imap_folder_new(const gchar *name, const gchar *path)
461 folder = (Folder *)g_new0(IMAPFolder, 1);
462 folder->klass = &imap_class;
463 imap_folder_init(folder, name, path);
468 static void imap_folder_destroy(Folder *folder)
472 while (imap_folder_get_refcnt(folder) > 0)
473 gtk_main_iteration();
475 dir = imap_folder_get_path(folder);
476 if (is_dir_exist(dir))
477 remove_dir_recursive(dir);
480 folder_remote_folder_destroy(REMOTE_FOLDER(folder));
484 static void imap_folder_init(Folder *folder, const gchar *name,
487 folder_remote_folder_init((Folder *)folder, name, path);
490 static FolderItem *imap_folder_item_new(Folder *folder)
492 IMAPFolderItem *item;
494 item = g_new0(IMAPFolderItem, 1);
497 item->uid_list = NULL;
499 return (FolderItem *)item;
502 static void imap_folder_item_destroy(Folder *folder, FolderItem *_item)
504 IMAPFolderItem *item = (IMAPFolderItem *)_item;
506 g_return_if_fail(item != NULL);
507 g_slist_free(item->uid_list);
512 static gboolean imap_reset_uid_lists_func(GNode *node, gpointer data)
514 IMAPFolderItem *item = (IMAPFolderItem *)node->data;
518 g_slist_free(item->uid_list);
519 item->uid_list = NULL;
524 static void imap_reset_uid_lists(Folder *folder)
526 if(folder->node == NULL)
529 /* Destroy all uid lists and rest last uid */
530 g_node_traverse(folder->node, G_IN_ORDER, G_TRAVERSE_ALL, -1, imap_reset_uid_lists_func, NULL);
533 void imap_get_capabilities(IMAPSession *session)
535 struct mailimap_capability_data *capabilities = NULL;
538 if (session->capability != NULL)
541 capabilities = imap_threaded_capability(session->folder);
543 if (capabilities == NULL)
546 for(cur = clist_begin(capabilities->cap_list) ; cur != NULL ;
547 cur = clist_next(cur)) {
548 struct mailimap_capability * cap =
550 if (!cap || cap->cap_data.cap_name == NULL)
552 session->capability = g_slist_append
553 (session->capability,
554 g_strdup(cap->cap_data.cap_name));
555 debug_print("got capa %s\n", cap->cap_data.cap_name);
557 mailimap_capability_data_free(capabilities);
560 gboolean imap_has_capability(IMAPSession *session, const gchar *cap)
563 for (cur = session->capability; cur; cur = cur->next) {
564 if (!g_ascii_strcasecmp(cur->data, cap))
570 static gint imap_auth(IMAPSession *session, const gchar *user, const gchar *pass,
573 gint ok = IMAP_ERROR;
574 static time_t last_login_err = 0;
576 imap_get_capabilities(session);
579 case IMAP_AUTH_CRAM_MD5:
580 ok = imap_cmd_login(session, user, pass, "CRAM-MD5");
582 case IMAP_AUTH_LOGIN:
583 ok = imap_cmd_login(session, user, pass, "LOGIN");
586 debug_print("capabilities:\n"
589 imap_has_capability(session, "CRAM-MD5"),
590 imap_has_capability(session, "LOGIN"));
591 if (imap_has_capability(session, "CRAM-MD5"))
592 ok = imap_cmd_login(session, user, pass, "CRAM-MD5");
593 if (ok == IMAP_ERROR) /* we always try LOGIN before giving up */
594 ok = imap_cmd_login(session, user, pass, "LOGIN");
596 if (ok == IMAP_SUCCESS)
597 session->authenticated = TRUE;
599 gchar *ext_info = NULL;
601 if (type == IMAP_AUTH_CRAM_MD5) {
602 ext_info = _("\n\nCRAM-MD5 logins only work if libetpan has been "
603 "compiled with SASL support and the "
604 "CRAM-MD5 SASL plugin is installed.");
609 if (time(NULL) - last_login_err > 10) {
610 if (!prefs_common.no_recv_err_panel) {
611 alertpanel_error(_("Connection to %s failed: "
613 SESSION(session)->server, ext_info);
615 log_error(_("Connection to %s failed: "
616 "login refused.%s\n"),
617 SESSION(session)->server, ext_info);
620 last_login_err = time(NULL);
625 static IMAPSession *imap_reconnect_if_possible(Folder *folder, IMAPSession *session)
627 RemoteFolder *rfolder = REMOTE_FOLDER(folder);
628 /* Check if this is the first try to establish a
629 connection, if yes we don't try to reconnect */
630 debug_print("reconnecting\n");
631 if (rfolder->session == NULL) {
632 log_warning(_("Connecting to %s failed"),
633 folder->account->recv_server);
634 session_destroy(SESSION(session));
637 log_warning(_("IMAP4 connection to %s has been"
638 " disconnected. Reconnecting...\n"),
639 folder->account->recv_server);
640 statusbar_print_all(_("IMAP4 connection to %s has been"
641 " disconnected. Reconnecting...\n"),
642 folder->account->recv_server);
643 SESSION(session)->state = SESSION_DISCONNECTED;
644 session_destroy(SESSION(session));
645 /* Clear folders session to make imap_session_get create
646 a new session, because of rfolder->session == NULL
647 it will not try to reconnect again and so avoid an
649 rfolder->session = NULL;
650 session = imap_session_get(folder);
651 rfolder->session = SESSION(session);
657 static IMAPSession *imap_session_get(Folder *folder)
659 RemoteFolder *rfolder = REMOTE_FOLDER(folder);
660 IMAPSession *session = NULL;
662 g_return_val_if_fail(folder != NULL, NULL);
663 g_return_val_if_fail(FOLDER_CLASS(folder) == &imap_class, NULL);
664 g_return_val_if_fail(folder->account != NULL, NULL);
666 if (prefs_common.work_offline && !inc_offline_should_override()) {
670 /* Make sure we have a session */
671 if (rfolder->session != NULL) {
672 session = IMAP_SESSION(rfolder->session);
674 imap_reset_uid_lists(folder);
675 session = imap_session_new(folder, folder->account);
680 /* Make sure session is authenticated */
681 if (!IMAP_SESSION(session)->authenticated)
682 imap_session_authenticate(IMAP_SESSION(session), folder->account);
684 if (!IMAP_SESSION(session)->authenticated) {
685 session_destroy(SESSION(session));
686 rfolder->session = NULL;
691 /* Make sure we have parsed the IMAP namespace */
692 imap_parse_namespace(IMAP_SESSION(session),
693 IMAP_FOLDER(folder));
696 /* I think the point of this code is to avoid sending a
697 * keepalive if we've used the session recently and therefore
698 * think it's still alive. Unfortunately, most of the code
699 * does not yet check for errors on the socket, and so if the
700 * connection drops we don't notice until the timeout expires.
701 * A better solution than sending a NOOP every time would be
702 * for every command to be prepared to retry until it is
703 * successfully sent. -- mbp */
704 if (time(NULL) - SESSION(session)->last_access_time > SESSION_TIMEOUT_INTERVAL) {
705 /* verify that the session is still alive */
706 if (imap_cmd_noop(session) != IMAP_SUCCESS) {
707 debug_print("disconnected!\n");
708 session = imap_reconnect_if_possible(folder, session);
712 rfolder->session = SESSION(session);
714 return IMAP_SESSION(session);
717 static IMAPSession *imap_session_new(Folder * folder,
718 const PrefsAccount *account)
720 IMAPSession *session;
726 /* FIXME: IMAP over SSL only... */
729 port = account->set_imapport ? account->imapport
730 : account->ssl_imap == SSL_TUNNEL ? IMAPS_PORT : IMAP4_PORT;
731 ssl_type = account->ssl_imap;
733 if (account->ssl_imap != SSL_NONE) {
734 if (alertpanel_full(_("Insecure connection"),
735 _("This connection is configured to be secured "
736 "using SSL, but SSL is not available in this "
737 "build of Sylpheed-Claws. \n\n"
738 "Do you want to continue connecting to this "
739 "server? The communication would not be "
741 _("Continue connecting"),
742 GTK_STOCK_CANCEL, NULL,
743 FALSE, NULL, ALERT_WARNING,
744 G_ALERTALTERNATE) != G_ALERTDEFAULT)
747 port = account->set_imapport ? account->imapport
752 statusbar_print_all(_("Connecting to IMAP4 server: %s..."), folder->account->recv_server);
753 if (account->set_tunnelcmd) {
754 r = imap_threaded_connect_cmd(folder,
756 account->recv_server,
761 if (ssl_type == SSL_TUNNEL) {
762 r = imap_threaded_connect_ssl(folder,
763 account->recv_server,
769 r = imap_threaded_connect(folder,
770 account->recv_server,
776 if (r == MAILIMAP_NO_ERROR_AUTHENTICATED) {
777 authenticated = TRUE;
779 else if (r == MAILIMAP_NO_ERROR_NON_AUTHENTICATED) {
780 authenticated = FALSE;
783 if(!prefs_common.no_recv_err_panel) {
784 alertpanel_error(_("Can't connect to IMAP4 server: %s:%d"),
785 account->recv_server, port);
787 log_error(_("Can't connect to IMAP4 server: %s:%d\n"),
788 account->recv_server, port);
794 session = g_new0(IMAPSession, 1);
795 session_init(SESSION(session));
796 SESSION(session)->type = SESSION_IMAP;
797 SESSION(session)->server = g_strdup(account->recv_server);
798 SESSION(session)->sock = NULL;
800 SESSION(session)->destroy = imap_session_destroy;
802 session->capability = NULL;
804 session->authenticated = authenticated;
805 session->mbox = NULL;
806 session->cmd_count = 0;
807 session->folder = folder;
808 IMAP_FOLDER(session->folder)->last_seen_separator = 0;
811 if (account->ssl_imap == SSL_STARTTLS) {
814 ok = imap_cmd_starttls(session);
815 if (ok != IMAP_SUCCESS) {
816 log_warning(_("Can't start TLS session.\n"));
817 session_destroy(SESSION(session));
821 imap_free_capabilities(session);
822 session->authenticated = FALSE;
823 session->uidplus = FALSE;
824 session->cmd_count = 1;
827 log_message("IMAP connection is %s-authenticated\n",
828 (session->authenticated) ? "pre" : "un");
833 static void imap_session_authenticate(IMAPSession *session,
834 const PrefsAccount *account)
838 g_return_if_fail(account->userid != NULL);
840 pass = account->passwd;
843 tmp_pass = input_dialog_query_password(account->recv_server, account->userid);
845 tmp_pass = g_strdup(""); /* allow empty password */
846 Xstrdup_a(pass, tmp_pass, {g_free(tmp_pass); return;});
849 statusbar_print_all(_("Connecting to IMAP4 server %s...\n"),
850 account->recv_server);
851 if (imap_auth(session, account->userid, pass, account->imap_auth_type) != IMAP_SUCCESS) {
852 imap_threaded_disconnect(session->folder);
853 imap_cmd_logout(session);
859 session->authenticated = TRUE;
862 static void imap_session_destroy(Session *session)
864 if (session->state != SESSION_DISCONNECTED)
865 imap_threaded_disconnect(IMAP_SESSION(session)->folder);
867 imap_free_capabilities(IMAP_SESSION(session));
868 g_free(IMAP_SESSION(session)->mbox);
869 sock_close(session->sock);
870 session->sock = NULL;
873 static gchar *imap_fetch_msg(Folder *folder, FolderItem *item, gint uid)
875 return imap_fetch_msg_full(folder, item, uid, TRUE, TRUE);
878 static guint get_size_with_crs(MsgInfo *info)
887 fp = procmsg_open_message(info);
891 while (fgets(buf, sizeof (buf), fp) != NULL) {
893 if (!strstr(buf, "\r") && strstr(buf, "\n"))
901 static gchar *imap_fetch_msg_full(Folder *folder, FolderItem *item, gint uid,
902 gboolean headers, gboolean body)
904 gchar *path, *filename;
905 IMAPSession *session;
908 g_return_val_if_fail(folder != NULL, NULL);
909 g_return_val_if_fail(item != NULL, NULL);
914 path = folder_item_get_path(item);
915 if (!is_dir_exist(path))
917 filename = g_strconcat(path, G_DIR_SEPARATOR_S, itos(uid), NULL);
920 if (is_file_exist(filename)) {
921 /* see whether the local file represents the whole message
922 * or not. As the IMAP server reports size with \r chars,
923 * we have to update the local file (UNIX \n only) size */
924 MsgInfo *msginfo = imap_parse_msg(filename, item);
925 MsgInfo *cached = msgcache_get_msg(item->cache,uid);
926 guint have_size = get_size_with_crs(msginfo);
929 debug_print("message %d has been already %scached (%d/%d).\n", uid,
930 have_size == cached->size ? "fully ":"",
931 have_size, (int)cached->size);
933 if (cached && (cached->size == have_size || !body)) {
934 procmsg_msginfo_free(cached);
935 procmsg_msginfo_free(msginfo);
936 file_strip_crs(filename);
939 procmsg_msginfo_free(cached);
940 procmsg_msginfo_free(msginfo);
944 session = imap_session_get(folder);
950 debug_print("IMAP fetching messages\n");
951 ok = imap_select(session, IMAP_FOLDER(folder), item->path,
952 NULL, NULL, NULL, NULL, FALSE);
953 if (ok != IMAP_SUCCESS) {
954 g_warning("can't select mailbox %s\n", item->path);
959 debug_print("getting message %d...\n", uid);
960 ok = imap_cmd_fetch(session, (guint32)uid, filename, headers, body);
962 if (ok != IMAP_SUCCESS) {
963 g_warning("can't fetch message %d\n", uid);
968 file_strip_crs(filename);
972 static gint imap_add_msg(Folder *folder, FolderItem *dest,
973 const gchar *file, MsgFlags *flags)
977 MsgFileInfo fileinfo;
979 g_return_val_if_fail(file != NULL, -1);
981 fileinfo.msginfo = NULL;
982 fileinfo.file = (gchar *)file;
983 fileinfo.flags = flags;
984 file_list.data = &fileinfo;
985 file_list.next = NULL;
987 ret = imap_add_msgs(folder, dest, &file_list, NULL);
991 static gint imap_add_msgs(Folder *folder, FolderItem *dest, GSList *file_list,
995 IMAPSession *session;
996 guint32 last_uid = 0;
998 MsgFileInfo *fileinfo;
1002 g_return_val_if_fail(folder != NULL, -1);
1003 g_return_val_if_fail(dest != NULL, -1);
1004 g_return_val_if_fail(file_list != NULL, -1);
1006 session = imap_session_get(folder);
1010 destdir = imap_get_real_path(IMAP_FOLDER(folder), dest->path);
1012 for (cur = file_list; cur != NULL; cur = cur->next) {
1013 IMAPFlags iflags = 0;
1014 guint32 new_uid = 0;
1015 gchar *real_file = NULL;
1016 gboolean file_is_tmp = FALSE;
1017 fileinfo = (MsgFileInfo *)cur->data;
1019 if (fileinfo->flags) {
1020 if (MSG_IS_MARKED(*fileinfo->flags))
1021 iflags |= IMAP_FLAG_FLAGGED;
1022 if (MSG_IS_REPLIED(*fileinfo->flags))
1023 iflags |= IMAP_FLAG_ANSWERED;
1024 if (!MSG_IS_UNREAD(*fileinfo->flags))
1025 iflags |= IMAP_FLAG_SEEN;
1028 if (fileinfo->flags) {
1029 if ((MSG_IS_QUEUED(*fileinfo->flags)
1030 || MSG_IS_DRAFT(*fileinfo->flags))
1031 && !folder_has_parent_of_type(dest, F_QUEUE)
1032 && !folder_has_parent_of_type(dest, F_DRAFT)) {
1033 real_file = get_tmp_file();
1035 if (procmsg_remove_special_headers(
1044 if (real_file == NULL)
1045 real_file = g_strdup(fileinfo->file);
1047 if (folder_has_parent_of_type(dest, F_QUEUE) ||
1048 folder_has_parent_of_type(dest, F_OUTBOX) ||
1049 folder_has_parent_of_type(dest, F_DRAFT) ||
1050 folder_has_parent_of_type(dest, F_TRASH))
1051 iflags |= IMAP_FLAG_SEEN;
1053 ok = imap_cmd_append(session, destdir, real_file, iflags,
1056 if (ok != IMAP_SUCCESS) {
1057 g_warning("can't append message %s\n", real_file);
1059 g_unlink(real_file);
1065 if (relation != NULL)
1066 g_relation_insert(relation, fileinfo->msginfo != NULL ?
1067 (gpointer) fileinfo->msginfo : (gpointer) fileinfo,
1068 GINT_TO_POINTER(dest->last_num + 1));
1069 if (last_uid < new_uid)
1072 g_unlink(real_file);
1080 static gint imap_do_copy_msgs(Folder *folder, FolderItem *dest,
1081 MsgInfoList *msglist, GRelation *relation)
1085 GSList *seq_list, *cur;
1087 IMAPSession *session;
1088 gint ok = IMAP_SUCCESS;
1089 GRelation *uid_mapping;
1092 g_return_val_if_fail(folder != NULL, -1);
1093 g_return_val_if_fail(dest != NULL, -1);
1094 g_return_val_if_fail(msglist != NULL, -1);
1096 session = imap_session_get(folder);
1101 msginfo = (MsgInfo *)msglist->data;
1103 src = msginfo->folder;
1105 g_warning("the src folder is identical to the dest.\n");
1109 ok = imap_select(session, IMAP_FOLDER(folder), msginfo->folder->path,
1110 NULL, NULL, NULL, NULL, FALSE);
1111 if (ok != IMAP_SUCCESS) {
1115 destdir = imap_get_real_path(IMAP_FOLDER(folder), dest->path);
1116 seq_list = imap_get_lep_set_from_msglist(msglist);
1117 uid_mapping = g_relation_new(2);
1118 g_relation_index(uid_mapping, 0, g_direct_hash, g_direct_equal);
1120 for (cur = seq_list; cur != NULL; cur = g_slist_next(cur)) {
1121 struct mailimap_set * seq_set;
1123 seq_set = cur->data;
1125 debug_print("Copying messages from %s to %s ...\n",
1126 src->path, destdir);
1128 ok = imap_cmd_copy(session, seq_set, destdir, uid_mapping);
1129 if (ok != IMAP_SUCCESS) {
1130 g_relation_destroy(uid_mapping);
1131 imap_lep_set_free(seq_list);
1136 for (cur = msglist; cur != NULL; cur = g_slist_next(cur)) {
1137 MsgInfo *msginfo = (MsgInfo *)cur->data;
1140 tuples = g_relation_select(uid_mapping,
1141 GINT_TO_POINTER(msginfo->msgnum),
1143 if (tuples->len > 0) {
1144 gint num = GPOINTER_TO_INT(g_tuples_index(tuples, 0, 1));
1145 g_relation_insert(relation, msginfo,
1146 GPOINTER_TO_INT(num));
1150 g_relation_insert(relation, msginfo,
1151 GPOINTER_TO_INT(0));
1152 g_tuples_destroy(tuples);
1155 g_relation_destroy(uid_mapping);
1156 imap_lep_set_free(seq_list);
1160 IMAP_FOLDER_ITEM(dest)->lastuid = 0;
1161 IMAP_FOLDER_ITEM(dest)->uid_next = 0;
1162 g_slist_free(IMAP_FOLDER_ITEM(dest)->uid_list);
1163 IMAP_FOLDER_ITEM(dest)->uid_list = NULL;
1165 if (ok == IMAP_SUCCESS)
1171 static gint imap_copy_msg(Folder *folder, FolderItem *dest, MsgInfo *msginfo)
1175 g_return_val_if_fail(msginfo != NULL, -1);
1177 msglist.data = msginfo;
1178 msglist.next = NULL;
1180 return imap_copy_msgs(folder, dest, &msglist, NULL);
1183 static gint imap_copy_msgs(Folder *folder, FolderItem *dest,
1184 MsgInfoList *msglist, GRelation *relation)
1190 g_return_val_if_fail(folder != NULL, -1);
1191 g_return_val_if_fail(dest != NULL, -1);
1192 g_return_val_if_fail(msglist != NULL, -1);
1194 msginfo = (MsgInfo *)msglist->data;
1195 g_return_val_if_fail(msginfo->folder != NULL, -1);
1197 if (folder == msginfo->folder->folder &&
1198 !folder_has_parent_of_type(msginfo->folder, F_DRAFT) &&
1199 !folder_has_parent_of_type(msginfo->folder, F_QUEUE)) {
1200 ret = imap_do_copy_msgs(folder, dest, msglist, relation);
1204 file_list = procmsg_get_message_file_list(msglist);
1205 g_return_val_if_fail(file_list != NULL, -1);
1207 ret = imap_add_msgs(folder, dest, file_list, relation);
1209 procmsg_message_file_list_free(file_list);
1215 static gint imap_do_remove_msgs(Folder *folder, FolderItem *dest,
1216 MsgInfoList *msglist, GRelation *relation)
1219 GSList *seq_list = NULL, *cur;
1221 IMAPSession *session;
1222 gint ok = IMAP_SUCCESS;
1223 GRelation *uid_mapping;
1225 g_return_val_if_fail(folder != NULL, -1);
1226 g_return_val_if_fail(dest != NULL, -1);
1227 g_return_val_if_fail(msglist != NULL, -1);
1229 session = imap_session_get(folder);
1233 msginfo = (MsgInfo *)msglist->data;
1235 ok = imap_select(session, IMAP_FOLDER(folder), msginfo->folder->path,
1236 NULL, NULL, NULL, NULL, FALSE);
1237 if (ok != IMAP_SUCCESS) {
1241 destdir = imap_get_real_path(IMAP_FOLDER(folder), dest->path);
1242 for (cur = msglist; cur; cur = cur->next) {
1243 msginfo = (MsgInfo *)cur->data;
1244 seq_list = g_slist_append(seq_list, GINT_TO_POINTER(msginfo->msgnum));
1247 uid_mapping = g_relation_new(2);
1248 g_relation_index(uid_mapping, 0, g_direct_hash, g_direct_equal);
1250 ok = imap_set_message_flags
1251 (IMAP_SESSION(REMOTE_FOLDER(folder)->session),
1252 seq_list, IMAP_FLAG_DELETED, TRUE);
1253 if (ok != IMAP_SUCCESS) {
1254 log_warning(_("can't set deleted flags\n"));
1257 ok = imap_cmd_expunge(session);
1258 if (ok != IMAP_SUCCESS) {
1259 log_warning(_("can't expunge\n"));
1263 g_relation_destroy(uid_mapping);
1264 g_slist_free(seq_list);
1268 if (ok == IMAP_SUCCESS)
1274 static gint imap_remove_msgs(Folder *folder, FolderItem *dest,
1275 MsgInfoList *msglist, GRelation *relation)
1279 g_return_val_if_fail(folder != NULL, -1);
1280 g_return_val_if_fail(dest != NULL, -1);
1281 if (msglist == NULL)
1284 msginfo = (MsgInfo *)msglist->data;
1285 g_return_val_if_fail(msginfo->folder != NULL, -1);
1287 return imap_do_remove_msgs(folder, dest, msglist, relation);
1290 static gint imap_remove_all_msg(Folder *folder, FolderItem *item)
1292 GSList *list = folder_item_get_msg_list(item);
1293 gint res = imap_remove_msgs(folder, item, list, NULL);
1294 procmsg_msg_list_free(list);
1298 static gboolean imap_is_msg_changed(Folder *folder, FolderItem *item,
1301 /* TODO: properly implement this method */
1305 static gint imap_close(Folder *folder, FolderItem *item)
1310 static gint imap_scan_tree(Folder *folder)
1312 FolderItem *item = NULL;
1313 IMAPSession *session;
1314 gchar *root_folder = NULL;
1316 g_return_val_if_fail(folder != NULL, -1);
1317 g_return_val_if_fail(folder->account != NULL, -1);
1319 session = imap_session_get(folder);
1321 if (!folder->node) {
1322 folder_tree_destroy(folder);
1323 item = folder_item_new(folder, folder->name, NULL);
1324 item->folder = folder;
1325 folder->node = item->node = g_node_new(item);
1330 if (folder->account->imap_dir && *folder->account->imap_dir) {
1335 Xstrdup_a(root_folder, folder->account->imap_dir, {return -1;});
1336 extract_quote(root_folder, '"');
1337 subst_char(root_folder,
1338 imap_get_path_separator(IMAP_FOLDER(folder),
1341 strtailchomp(root_folder, '/');
1342 real_path = imap_get_real_path
1343 (IMAP_FOLDER(folder), root_folder);
1344 debug_print("IMAP root directory: %s\n", real_path);
1346 /* check if root directory exist */
1348 r = imap_threaded_list(session->folder, "", real_path,
1350 if ((r != MAILIMAP_NO_ERROR) || (clist_count(lep_list) == 0)) {
1351 if (!folder->node) {
1352 item = folder_item_new(folder, folder->name, NULL);
1353 item->folder = folder;
1354 folder->node = item->node = g_node_new(item);
1359 mailimap_list_result_free(lep_list);
1365 item = FOLDER_ITEM(folder->node->data);
1366 if (!item || ((item->path || root_folder) &&
1367 strcmp2(item->path, root_folder) != 0)) {
1368 folder_tree_destroy(folder);
1369 item = folder_item_new(folder, folder->name, root_folder);
1370 item->folder = folder;
1371 folder->node = item->node = g_node_new(item);
1374 imap_scan_tree_recursive(session, FOLDER_ITEM(folder->node->data));
1375 imap_create_missing_folders(folder);
1380 static gint imap_scan_tree_recursive(IMAPSession *session, FolderItem *item)
1383 IMAPFolder *imapfolder;
1384 FolderItem *new_item;
1385 GSList *item_list, *cur;
1388 gchar *wildcard_path;
1394 g_return_val_if_fail(item != NULL, -1);
1395 g_return_val_if_fail(item->folder != NULL, -1);
1396 g_return_val_if_fail(item->no_sub == FALSE, -1);
1398 folder = item->folder;
1399 imapfolder = IMAP_FOLDER(folder);
1401 separator = imap_get_path_separator(imapfolder, item->path);
1403 if (folder->ui_func)
1404 folder->ui_func(folder, item, folder->ui_func_data);
1407 wildcard[0] = separator;
1410 real_path = imap_get_real_path(imapfolder, item->path);
1414 real_path = g_strdup("");
1417 Xstrcat_a(wildcard_path, real_path, wildcard,
1418 {g_free(real_path); return IMAP_ERROR;});
1420 r = imap_threaded_list(folder, "", wildcard_path, &lep_list);
1421 if (r != MAILIMAP_NO_ERROR) {
1425 item_list = imap_list_from_lep(imapfolder,
1426 lep_list, real_path);
1427 mailimap_list_result_free(lep_list);
1432 node = item->node->children;
1433 while (node != NULL) {
1434 FolderItem *old_item = FOLDER_ITEM(node->data);
1435 GNode *next = node->next;
1438 for (cur = item_list; cur != NULL; cur = cur->next) {
1439 FolderItem *cur_item = FOLDER_ITEM(cur->data);
1440 if (!strcmp2(old_item->path, cur_item->path)) {
1441 new_item = cur_item;
1446 debug_print("folder '%s' not found. removing...\n",
1448 folder_item_remove(old_item);
1450 old_item->no_sub = new_item->no_sub;
1451 old_item->no_select = new_item->no_select;
1452 if (old_item->no_sub == TRUE && node->children) {
1453 debug_print("folder '%s' doesn't have "
1454 "subfolders. removing...\n",
1456 folder_item_remove_children(old_item);
1463 for (cur = item_list; cur != NULL; cur = cur->next) {
1464 FolderItem *cur_item = FOLDER_ITEM(cur->data);
1466 for (node = item->node->children; node != NULL;
1467 node = node->next) {
1468 if (!strcmp2(FOLDER_ITEM(node->data)->path,
1470 new_item = FOLDER_ITEM(node->data);
1471 folder_item_destroy(cur_item);
1477 new_item = cur_item;
1478 debug_print("new folder '%s' found.\n", new_item->path);
1479 folder_item_append(item, new_item);
1482 if (!strcmp(new_item->path, "INBOX")) {
1483 new_item->stype = F_INBOX;
1484 folder->inbox = new_item;
1485 } else if (!folder_item_parent(item) || item->stype == F_INBOX) {
1488 base = g_path_get_basename(new_item->path);
1490 if (!folder->outbox && !g_ascii_strcasecmp(base, "Sent")) {
1491 new_item->stype = F_OUTBOX;
1492 folder->outbox = new_item;
1493 } else if (!folder->draft && !g_ascii_strcasecmp(base, "Drafts")) {
1494 new_item->stype = F_DRAFT;
1495 folder->draft = new_item;
1496 } else if (!folder->queue && !g_ascii_strcasecmp(base, "Queue")) {
1497 new_item->stype = F_QUEUE;
1498 folder->queue = new_item;
1499 } else if (!folder->trash && !g_ascii_strcasecmp(base, "Trash")) {
1500 new_item->stype = F_TRASH;
1501 folder->trash = new_item;
1506 if (new_item->no_sub == FALSE)
1507 imap_scan_tree_recursive(session, new_item);
1510 g_slist_free(item_list);
1512 return IMAP_SUCCESS;
1515 static gint imap_create_tree(Folder *folder)
1517 g_return_val_if_fail(folder != NULL, -1);
1518 g_return_val_if_fail(folder->node != NULL, -1);
1519 g_return_val_if_fail(folder->node->data != NULL, -1);
1520 g_return_val_if_fail(folder->account != NULL, -1);
1522 imap_scan_tree(folder);
1523 imap_create_missing_folders(folder);
1528 static void imap_create_missing_folders(Folder *folder)
1530 g_return_if_fail(folder != NULL);
1533 folder->inbox = imap_create_special_folder
1534 (folder, F_INBOX, "INBOX");
1536 folder->trash = imap_create_special_folder
1537 (folder, F_TRASH, "Trash");
1539 folder->queue = imap_create_special_folder
1540 (folder, F_QUEUE, "Queue");
1541 if (!folder->outbox)
1542 folder->outbox = imap_create_special_folder
1543 (folder, F_OUTBOX, "Sent");
1545 folder->draft = imap_create_special_folder
1546 (folder, F_DRAFT, "Drafts");
1549 static FolderItem *imap_create_special_folder(Folder *folder,
1550 SpecialFolderItemType stype,
1554 FolderItem *new_item;
1556 g_return_val_if_fail(folder != NULL, NULL);
1557 g_return_val_if_fail(folder->node != NULL, NULL);
1558 g_return_val_if_fail(folder->node->data != NULL, NULL);
1559 g_return_val_if_fail(folder->account != NULL, NULL);
1560 g_return_val_if_fail(name != NULL, NULL);
1562 item = FOLDER_ITEM(folder->node->data);
1563 new_item = imap_create_folder(folder, item, name);
1566 g_warning("Can't create '%s'\n", name);
1567 if (!folder->inbox) return NULL;
1569 new_item = imap_create_folder(folder, folder->inbox, name);
1571 g_warning("Can't create '%s' under INBOX\n", name);
1573 new_item->stype = stype;
1575 new_item->stype = stype;
1580 static gchar *imap_folder_get_path(Folder *folder)
1584 g_return_val_if_fail(folder != NULL, NULL);
1585 g_return_val_if_fail(folder->account != NULL, NULL);
1587 folder_path = g_strconcat(get_imap_cache_dir(),
1589 folder->account->recv_server,
1591 folder->account->userid,
1597 static gchar *imap_item_get_path(Folder *folder, FolderItem *item)
1599 gchar *folder_path, *path;
1601 g_return_val_if_fail(folder != NULL, NULL);
1602 g_return_val_if_fail(item != NULL, NULL);
1603 folder_path = imap_folder_get_path(folder);
1605 g_return_val_if_fail(folder_path != NULL, NULL);
1606 if (folder_path[0] == G_DIR_SEPARATOR) {
1608 path = g_strconcat(folder_path, G_DIR_SEPARATOR_S,
1611 path = g_strdup(folder_path);
1614 path = g_strconcat(get_home_dir(), G_DIR_SEPARATOR_S,
1615 folder_path, G_DIR_SEPARATOR_S,
1618 path = g_strconcat(get_home_dir(), G_DIR_SEPARATOR_S,
1621 g_free(folder_path);
1626 static FolderItem *imap_create_folder(Folder *folder, FolderItem *parent,
1629 gchar *dirpath, *imap_path;
1630 IMAPSession *session;
1631 FolderItem *new_item;
1637 g_return_val_if_fail(folder != NULL, NULL);
1638 g_return_val_if_fail(folder->account != NULL, NULL);
1639 g_return_val_if_fail(parent != NULL, NULL);
1640 g_return_val_if_fail(name != NULL, NULL);
1642 session = imap_session_get(folder);
1647 if (!folder_item_parent(parent) && strcmp(name, "INBOX") == 0)
1648 dirpath = g_strdup(name);
1649 else if (parent->path)
1650 dirpath = g_strconcat(parent->path, "/", name, NULL);
1651 else if ((p = strchr(name, '/')) != NULL && *(p + 1) != '\0')
1652 dirpath = g_strdup(name);
1653 else if (folder->account->imap_dir && *folder->account->imap_dir) {
1656 Xstrdup_a(imap_dir, folder->account->imap_dir, {return NULL;});
1657 strtailchomp(imap_dir, '/');
1658 dirpath = g_strconcat(imap_dir, "/", name, NULL);
1660 dirpath = g_strdup(name);
1662 /* keep trailing directory separator to create a folder that contains
1664 imap_path = imap_utf8_to_modified_utf7(dirpath);
1665 strtailchomp(dirpath, '/');
1666 Xstrdup_a(new_name, name, {
1670 strtailchomp(new_name, '/');
1671 separator = imap_get_path_separator(IMAP_FOLDER(folder), imap_path);
1672 imap_path_separator_subst(imap_path, separator);
1673 subst_char(new_name, '/', separator);
1675 if (strcmp(name, "INBOX") != 0) {
1677 gboolean exist = FALSE;
1681 argbuf = g_ptr_array_new();
1682 r = imap_threaded_list(folder, "", imap_path, &lep_list);
1683 if (r != MAILIMAP_NO_ERROR) {
1684 log_warning(_("can't create mailbox: LIST failed\n"));
1687 ptr_array_free_strings(argbuf);
1688 g_ptr_array_free(argbuf, TRUE);
1692 if (clist_count(lep_list) > 0)
1696 ok = imap_cmd_create(session, imap_path);
1697 if (ok != IMAP_SUCCESS) {
1698 log_warning(_("can't create mailbox\n"));
1706 new_item = folder_item_new(folder, new_name, dirpath);
1707 folder_item_append(parent, new_item);
1711 dirpath = folder_item_get_path(new_item);
1712 if (!is_dir_exist(dirpath))
1713 make_dir_hier(dirpath);
1719 static gint imap_rename_folder(Folder *folder, FolderItem *item,
1724 gchar *real_oldpath;
1725 gchar *real_newpath;
1727 gchar *old_cache_dir;
1728 gchar *new_cache_dir;
1729 IMAPSession *session;
1732 gint exists, recent, unseen;
1733 guint32 uid_validity;
1735 g_return_val_if_fail(folder != NULL, -1);
1736 g_return_val_if_fail(item != NULL, -1);
1737 g_return_val_if_fail(item->path != NULL, -1);
1738 g_return_val_if_fail(name != NULL, -1);
1740 if (strchr(name, imap_get_path_separator(IMAP_FOLDER(folder), item->path)) != NULL) {
1741 g_warning(_("New folder name must not contain the namespace "
1746 session = imap_session_get(folder);
1750 real_oldpath = imap_get_real_path(IMAP_FOLDER(folder), item->path);
1752 g_free(session->mbox);
1753 session->mbox = NULL;
1754 ok = imap_cmd_examine(session, "INBOX",
1755 &exists, &recent, &unseen, &uid_validity, FALSE);
1756 if (ok != IMAP_SUCCESS) {
1757 g_free(real_oldpath);
1761 separator = imap_get_path_separator(IMAP_FOLDER(folder), item->path);
1762 if (strchr(item->path, G_DIR_SEPARATOR)) {
1763 dirpath = g_path_get_dirname(item->path);
1764 newpath = g_strconcat(dirpath, G_DIR_SEPARATOR_S, name, NULL);
1767 newpath = g_strdup(name);
1769 real_newpath = imap_utf8_to_modified_utf7(newpath);
1770 imap_path_separator_subst(real_newpath, separator);
1772 ok = imap_cmd_rename(session, real_oldpath, real_newpath);
1773 if (ok != IMAP_SUCCESS) {
1774 log_warning(_("can't rename mailbox: %s to %s\n"),
1775 real_oldpath, real_newpath);
1776 g_free(real_oldpath);
1778 g_free(real_newpath);
1783 item->name = g_strdup(name);
1785 old_cache_dir = folder_item_get_path(item);
1787 paths[0] = g_strdup(item->path);
1789 g_node_traverse(item->node, G_PRE_ORDER, G_TRAVERSE_ALL, -1,
1790 imap_rename_folder_func, paths);
1792 if (is_dir_exist(old_cache_dir)) {
1793 new_cache_dir = folder_item_get_path(item);
1794 if (rename(old_cache_dir, new_cache_dir) < 0) {
1795 FILE_OP_ERROR(old_cache_dir, "rename");
1797 g_free(new_cache_dir);
1800 g_free(old_cache_dir);
1803 g_free(real_oldpath);
1804 g_free(real_newpath);
1809 static gint imap_remove_folder_real(Folder *folder, FolderItem *item)
1812 IMAPSession *session;
1815 gint exists, recent, unseen;
1816 guint32 uid_validity;
1818 g_return_val_if_fail(folder != NULL, -1);
1819 g_return_val_if_fail(item != NULL, -1);
1820 g_return_val_if_fail(item->path != NULL, -1);
1822 session = imap_session_get(folder);
1826 path = imap_get_real_path(IMAP_FOLDER(folder), item->path);
1828 ok = imap_cmd_examine(session, "INBOX",
1829 &exists, &recent, &unseen, &uid_validity, FALSE);
1830 if (ok != IMAP_SUCCESS) {
1835 ok = imap_cmd_delete(session, path);
1836 if (ok != IMAP_SUCCESS) {
1837 log_warning(_("can't delete mailbox\n"));
1843 cache_dir = folder_item_get_path(item);
1844 if (is_dir_exist(cache_dir) && remove_dir_recursive(cache_dir) < 0)
1845 g_warning("can't remove directory '%s'\n", cache_dir);
1847 folder_item_remove(item);
1852 static gint imap_remove_folder(Folder *folder, FolderItem *item)
1856 g_return_val_if_fail(item != NULL, -1);
1857 g_return_val_if_fail(item->folder != NULL, -1);
1858 g_return_val_if_fail(item->node != NULL, -1);
1860 node = item->node->children;
1861 while (node != NULL) {
1863 if (imap_remove_folder(folder, FOLDER_ITEM(node->data)) < 0)
1867 debug_print("IMAP removing %s\n", item->path);
1869 if (imap_remove_all_msg(folder, item) < 0)
1871 return imap_remove_folder_real(folder, item);
1874 typedef struct _uncached_data {
1875 IMAPSession *session;
1877 MsgNumberList *numlist;
1883 static void *imap_get_uncached_messages_thread(void *data)
1885 uncached_data *stuff = (uncached_data *)data;
1886 IMAPSession *session = stuff->session;
1887 FolderItem *item = stuff->item;
1888 MsgNumberList *numlist = stuff->numlist;
1890 GSList *newlist = NULL;
1891 GSList *llast = NULL;
1892 GSList *seq_list, *cur;
1894 debug_print("uncached_messages\n");
1896 if (session == NULL || item == NULL || item->folder == NULL
1897 || FOLDER_CLASS(item->folder) != &imap_class) {
1902 seq_list = imap_get_lep_set_from_numlist(numlist);
1903 debug_print("get msgs info\n");
1904 for (cur = seq_list; cur != NULL; cur = g_slist_next(cur)) {
1905 struct mailimap_set * imapset;
1911 imapset = cur->data;
1913 r = imap_threaded_fetch_env(session->folder,
1914 imapset, &env_list);
1915 if (r != MAILIMAP_NO_ERROR)
1919 for(i = 0 ; i < carray_count(env_list) ; i ++) {
1920 struct imap_fetch_env_info * info;
1923 info = carray_get(env_list, i);
1924 msginfo = imap_envelope_from_lep(info, item);
1925 msginfo->folder = item;
1927 llast = newlist = g_slist_append(newlist, msginfo);
1929 llast = g_slist_append(llast, msginfo);
1930 llast = llast->next;
1935 imap_fetch_env_free(env_list);
1938 session_set_access_time(SESSION(session));
1943 #define MAX_MSG_NUM 50
1945 static GSList *imap_get_uncached_messages(IMAPSession *session,
1947 MsgNumberList *numlist)
1949 GSList *result = NULL;
1951 uncached_data *data = g_new0(uncached_data, 1);
1956 data->total = g_slist_length(numlist);
1957 debug_print("messages list : %i\n", data->total);
1959 while (cur != NULL) {
1960 GSList * partial_result;
1968 while (count < MAX_MSG_NUM) {
1973 if (newlist == NULL)
1974 llast = newlist = g_slist_append(newlist, p);
1976 llast = g_slist_append(llast, p);
1977 llast = llast->next;
1987 data->session = session;
1989 data->numlist = newlist;
1992 if (prefs_common.work_offline && !inc_offline_should_override()) {
1998 (GSList *)imap_get_uncached_messages_thread(data);
2000 statusbar_progress_all(data->cur,data->total, 1);
2002 g_slist_free(newlist);
2004 result = g_slist_concat(result, partial_result);
2008 statusbar_progress_all(0,0,0);
2009 statusbar_pop_all();
2014 static void imap_delete_all_cached_messages(FolderItem *item)
2018 g_return_if_fail(item != NULL);
2019 g_return_if_fail(item->folder != NULL);
2020 g_return_if_fail(FOLDER_CLASS(item->folder) == &imap_class);
2022 debug_print("Deleting all cached messages...\n");
2024 dir = folder_item_get_path(item);
2025 if (is_dir_exist(dir))
2026 remove_all_numbered_files(dir);
2029 debug_print("done.\n");
2032 static IMAPNameSpace *imap_find_namespace_from_list(GList *ns_list,
2035 IMAPNameSpace *namespace = NULL;
2036 gchar *tmp_path, *name;
2038 if (!path) path = "";
2040 for (; ns_list != NULL; ns_list = ns_list->next) {
2041 IMAPNameSpace *tmp_ns = ns_list->data;
2043 Xstrcat_a(tmp_path, path, "/", return namespace);
2044 Xstrdup_a(name, tmp_ns->name, return namespace);
2045 if (tmp_ns->separator && tmp_ns->separator != '/') {
2046 subst_char(tmp_path, tmp_ns->separator, '/');
2047 subst_char(name, tmp_ns->separator, '/');
2049 if (strncmp(tmp_path, name, strlen(name)) == 0)
2056 static IMAPNameSpace *imap_find_namespace(IMAPFolder *folder,
2059 IMAPNameSpace *namespace;
2061 g_return_val_if_fail(folder != NULL, NULL);
2063 namespace = imap_find_namespace_from_list(folder->ns_personal, path);
2064 if (namespace) return namespace;
2065 namespace = imap_find_namespace_from_list(folder->ns_others, path);
2066 if (namespace) return namespace;
2067 namespace = imap_find_namespace_from_list(folder->ns_shared, path);
2068 if (namespace) return namespace;
2074 static gchar imap_get_path_separator(IMAPFolder *folder, const gchar *path)
2076 IMAPNameSpace *namespace;
2077 gchar separator = '/';
2079 if (folder->last_seen_separator == 0) {
2081 int r = imap_threaded_list((Folder *)folder, "", "", &lep_list);
2082 if (r != MAILIMAP_NO_ERROR) {
2083 log_warning(_("LIST failed\n"));
2087 if (clist_count(lep_list) > 0) {
2088 clistiter * iter = clist_begin(lep_list);
2089 struct mailimap_mailbox_list * mb;
2090 mb = clist_content(iter);
2092 folder->last_seen_separator = mb->mb_delimiter;
2093 debug_print("got separator: %c\n", folder->last_seen_separator);
2095 mailimap_list_result_free(lep_list);
2098 if (folder->last_seen_separator != 0) {
2099 debug_print("using separator: %c\n", folder->last_seen_separator);
2100 return folder->last_seen_separator;
2103 namespace = imap_find_namespace(folder, path);
2104 if (namespace && namespace->separator)
2105 separator = namespace->separator;
2110 static gchar *imap_get_real_path(IMAPFolder *folder, const gchar *path)
2115 g_return_val_if_fail(folder != NULL, NULL);
2116 g_return_val_if_fail(path != NULL, NULL);
2118 real_path = imap_utf8_to_modified_utf7(path);
2119 separator = imap_get_path_separator(folder, path);
2120 imap_path_separator_subst(real_path, separator);
2125 static gint imap_set_message_flags(IMAPSession *session,
2126 MsgNumberList *numlist,
2134 seq_list = imap_get_lep_set_from_numlist(numlist);
2136 for(cur = seq_list ; cur != NULL ; cur = g_slist_next(cur)) {
2137 struct mailimap_set * imapset;
2139 imapset = cur->data;
2141 ok = imap_cmd_store(session, imapset,
2145 imap_lep_set_free(seq_list);
2147 return IMAP_SUCCESS;
2150 typedef struct _select_data {
2151 IMAPSession *session;
2156 guint32 *uid_validity;
2160 static gint imap_select(IMAPSession *session, IMAPFolder *folder,
2162 gint *exists, gint *recent, gint *unseen,
2163 guint32 *uid_validity, gboolean block)
2167 gint exists_, recent_, unseen_;
2168 guint32 uid_validity_;
2170 if (!exists || !recent || !unseen || !uid_validity) {
2171 if (session->mbox && strcmp(session->mbox, path) == 0)
2172 return IMAP_SUCCESS;
2176 uid_validity = &uid_validity_;
2179 g_free(session->mbox);
2180 session->mbox = NULL;
2182 real_path = imap_get_real_path(folder, path);
2184 ok = imap_cmd_select(session, real_path,
2185 exists, recent, unseen, uid_validity, block);
2186 if (ok != IMAP_SUCCESS)
2187 log_warning(_("can't select folder: %s\n"), real_path);
2189 session->mbox = g_strdup(path);
2190 session->folder_content_changed = FALSE;
2197 static gint imap_status(IMAPSession *session, IMAPFolder *folder,
2199 gint *messages, gint *recent,
2200 guint32 *uid_next, guint32 *uid_validity,
2201 gint *unseen, gboolean block)
2205 struct mailimap_mailbox_data_status * data_status;
2209 real_path = imap_get_real_path(folder, path);
2211 r = imap_threaded_status(FOLDER(folder), real_path, &data_status);
2213 if (r != MAILIMAP_NO_ERROR) {
2214 debug_print("status err %d\n", r);
2218 if (data_status->st_info_list == NULL) {
2219 mailimap_mailbox_data_status_free(data_status);
2220 debug_print("status->st_info_list == NULL\n");
2225 for(iter = clist_begin(data_status->st_info_list) ; iter != NULL ;
2226 iter = clist_next(iter)) {
2227 struct mailimap_status_info * info;
2229 info = clist_content(iter);
2230 switch (info->st_att) {
2231 case MAILIMAP_STATUS_ATT_MESSAGES:
2232 * messages = info->st_value;
2233 got_values |= 1 << 0;
2236 case MAILIMAP_STATUS_ATT_RECENT:
2237 * recent = info->st_value;
2238 got_values |= 1 << 1;
2241 case MAILIMAP_STATUS_ATT_UIDNEXT:
2242 * uid_next = info->st_value;
2243 got_values |= 1 << 2;
2246 case MAILIMAP_STATUS_ATT_UIDVALIDITY:
2247 * uid_validity = info->st_value;
2248 got_values |= 1 << 3;
2251 case MAILIMAP_STATUS_ATT_UNSEEN:
2252 * unseen = info->st_value;
2253 got_values |= 1 << 4;
2257 mailimap_mailbox_data_status_free(data_status);
2259 if (got_values != ((1 << 4) + (1 << 3) +
2260 (1 << 2) + (1 << 1) + (1 << 0))) {
2261 debug_print("status: incomplete values received (%d)\n", got_values);
2264 return IMAP_SUCCESS;
2267 static void imap_free_capabilities(IMAPSession *session)
2269 slist_free_strings(session->capability);
2270 g_slist_free(session->capability);
2271 session->capability = NULL;
2274 /* low-level IMAP4rev1 commands */
2277 static gint imap_cmd_authenticate(IMAPSession *session, const gchar *user,
2278 const gchar *pass, IMAPAuthType type)
2285 gchar hexdigest[33];
2289 auth_type = "CRAM-MD5";
2291 imap_gen_send(session, "AUTHENTICATE %s", auth_type);
2292 ok = imap_gen_recv(session, &buf);
2293 if (ok != IMAP_SUCCESS || buf[0] != '+' || buf[1] != ' ') {
2298 challenge = g_malloc(strlen(buf + 2) + 1);
2299 challenge_len = base64_decode(challenge, buf + 2, -1);
2300 challenge[challenge_len] = '\0';
2303 md5_hex_hmac(hexdigest, challenge, challenge_len, pass, strlen(pass));
2306 response = g_strdup_printf("%s %s", user, hexdigest);
2307 response64 = g_malloc((strlen(response) + 3) * 2 + 1);
2308 base64_encode(response64, response, strlen(response));
2311 sock_puts(SESSION(session)->sock, response64);
2312 ok = imap_cmd_ok(session, NULL);
2313 if (ok != IMAP_SUCCESS)
2314 log_warning(_("IMAP4 authentication failed.\n"));
2320 static gint imap_cmd_login(IMAPSession *session,
2321 const gchar *user, const gchar *pass,
2327 log_print("IMAP4> Logging %s to %s using %s\n",
2329 SESSION(session)->server,
2331 r = imap_threaded_login(session->folder, user, pass, type);
2332 if (r != MAILIMAP_NO_ERROR) {
2333 log_error("IMAP4< Error logging in to %s\n",
2334 SESSION(session)->server);
2342 static gint imap_cmd_logout(IMAPSession *session)
2344 imap_threaded_disconnect(session->folder);
2346 return IMAP_SUCCESS;
2349 static gint imap_cmd_noop(IMAPSession *session)
2352 unsigned int exists;
2354 r = imap_threaded_noop(session->folder, &exists);
2355 if (r != MAILIMAP_NO_ERROR) {
2356 debug_print("noop err %d\n", r);
2359 session->exists = exists;
2360 session_set_access_time(SESSION(session));
2362 return IMAP_SUCCESS;
2366 static gint imap_cmd_starttls(IMAPSession *session)
2370 r = imap_threaded_starttls(session->folder);
2371 if (r != MAILIMAP_NO_ERROR) {
2372 debug_print("starttls err %d\n", r);
2375 return IMAP_SUCCESS;
2379 static gint imap_cmd_select(IMAPSession *session, const gchar *folder,
2380 gint *exists, gint *recent, gint *unseen,
2381 guint32 *uid_validity, gboolean block)
2385 r = imap_threaded_select(session->folder, folder,
2386 exists, recent, unseen, uid_validity);
2387 if (r != MAILIMAP_NO_ERROR) {
2388 debug_print("select err %d\n", r);
2391 return IMAP_SUCCESS;
2394 static gint imap_cmd_examine(IMAPSession *session, const gchar *folder,
2395 gint *exists, gint *recent, gint *unseen,
2396 guint32 *uid_validity, gboolean block)
2400 r = imap_threaded_examine(session->folder, folder,
2401 exists, recent, unseen, uid_validity);
2402 if (r != MAILIMAP_NO_ERROR) {
2403 debug_print("examine err %d\n", r);
2407 return IMAP_SUCCESS;
2410 static gint imap_cmd_create(IMAPSession *session, const gchar *folder)
2414 r = imap_threaded_create(session->folder, folder);
2415 if (r != MAILIMAP_NO_ERROR) {
2420 return IMAP_SUCCESS;
2423 static gint imap_cmd_rename(IMAPSession *session, const gchar *old_folder,
2424 const gchar *new_folder)
2428 r = imap_threaded_rename(session->folder, old_folder,
2430 if (r != MAILIMAP_NO_ERROR) {
2435 return IMAP_SUCCESS;
2438 static gint imap_cmd_delete(IMAPSession *session, const gchar *folder)
2443 r = imap_threaded_delete(session->folder, folder);
2444 if (r != MAILIMAP_NO_ERROR) {
2449 return IMAP_SUCCESS;
2452 typedef struct _fetch_data {
2453 IMAPSession *session;
2455 const gchar *filename;
2461 static void *imap_cmd_fetch_thread(void *data)
2463 fetch_data *stuff = (fetch_data *)data;
2464 IMAPSession *session = stuff->session;
2465 guint32 uid = stuff->uid;
2466 const gchar *filename = stuff->filename;
2470 r = imap_threaded_fetch_content(session->folder,
2474 r = imap_threaded_fetch_content(session->folder,
2477 if (r != MAILIMAP_NO_ERROR) {
2478 debug_print("fetch err %d\n", r);
2479 return GINT_TO_POINTER(IMAP_ERROR);
2481 return GINT_TO_POINTER(IMAP_SUCCESS);
2484 static gint imap_cmd_fetch(IMAPSession *session, guint32 uid,
2485 const gchar *filename, gboolean headers,
2488 fetch_data *data = g_new0(fetch_data, 1);
2491 data->session = session;
2493 data->filename = filename;
2494 data->headers = headers;
2497 if (prefs_common.work_offline && !inc_offline_should_override()) {
2501 statusbar_print_all(_("Fetching message..."));
2502 result = GPOINTER_TO_INT(imap_cmd_fetch_thread(data));
2503 statusbar_pop_all();
2509 static gint imap_cmd_append(IMAPSession *session, const gchar *destfolder,
2510 const gchar *file, IMAPFlags flags,
2513 struct mailimap_flag_list * flag_list;
2516 g_return_val_if_fail(file != NULL, IMAP_ERROR);
2518 flag_list = imap_flag_to_lep(flags);
2519 statusbar_print_all(_("Adding messages..."));
2520 r = imap_threaded_append(session->folder, destfolder,
2522 statusbar_pop_all();
2523 if (new_uid != NULL)
2526 if (r != MAILIMAP_NO_ERROR) {
2527 debug_print("append err %d\n", r);
2530 return IMAP_SUCCESS;
2533 static gint imap_cmd_copy(IMAPSession *session, struct mailimap_set * set,
2534 const gchar *destfolder, GRelation *uid_mapping)
2538 g_return_val_if_fail(session != NULL, IMAP_ERROR);
2539 g_return_val_if_fail(set != NULL, IMAP_ERROR);
2540 g_return_val_if_fail(destfolder != NULL, IMAP_ERROR);
2542 statusbar_print_all(_("Copying messages..."));
2543 r = imap_threaded_copy(session->folder, set, destfolder);
2544 statusbar_pop_all();
2545 if (r != MAILIMAP_NO_ERROR) {
2550 return IMAP_SUCCESS;
2553 static gint imap_cmd_store(IMAPSession *session, struct mailimap_set * set,
2554 IMAPFlags flags, int do_add)
2557 struct mailimap_flag_list * flag_list;
2558 struct mailimap_store_att_flags * store_att_flags;
2560 flag_list = imap_flag_to_lep(flags);
2564 mailimap_store_att_flags_new_add_flags_silent(flag_list);
2567 mailimap_store_att_flags_new_remove_flags_silent(flag_list);
2569 r = imap_threaded_store(session->folder, set, store_att_flags);
2570 if (r != MAILIMAP_NO_ERROR) {
2575 return IMAP_SUCCESS;
2578 static gint imap_cmd_expunge(IMAPSession *session)
2582 if (prefs_common.work_offline && !inc_offline_should_override()) {
2586 r = imap_threaded_expunge(session->folder);
2587 if (r != MAILIMAP_NO_ERROR) {
2592 return IMAP_SUCCESS;
2595 static void imap_path_separator_subst(gchar *str, gchar separator)
2598 gboolean in_escape = FALSE;
2600 if (!separator || separator == '/') return;
2602 for (p = str; *p != '\0'; p++) {
2603 if (*p == '/' && !in_escape)
2605 else if (*p == '&' && *(p + 1) != '-' && !in_escape)
2607 else if (*p == '-' && in_escape)
2612 static gchar *imap_modified_utf7_to_utf8(const gchar *mutf7_str)
2614 static iconv_t cd = (iconv_t)-1;
2615 static gboolean iconv_ok = TRUE;
2618 size_t norm_utf7_len;
2620 gchar *to_str, *to_p;
2622 gboolean in_escape = FALSE;
2624 if (!iconv_ok) return g_strdup(mutf7_str);
2626 if (cd == (iconv_t)-1) {
2627 cd = iconv_open(CS_INTERNAL, CS_UTF_7);
2628 if (cd == (iconv_t)-1) {
2629 g_warning("iconv cannot convert UTF-7 to %s\n",
2632 return g_strdup(mutf7_str);
2636 /* modified UTF-7 to normal UTF-7 conversion */
2637 norm_utf7 = g_string_new(NULL);
2639 for (p = mutf7_str; *p != '\0'; p++) {
2640 /* replace: '&' -> '+',
2642 escaped ',' -> '/' */
2643 if (!in_escape && *p == '&') {
2644 if (*(p + 1) != '-') {
2645 g_string_append_c(norm_utf7, '+');
2648 g_string_append_c(norm_utf7, '&');
2651 } else if (in_escape && *p == ',') {
2652 g_string_append_c(norm_utf7, '/');
2653 } else if (in_escape && *p == '-') {
2654 g_string_append_c(norm_utf7, '-');
2657 g_string_append_c(norm_utf7, *p);
2661 norm_utf7_p = norm_utf7->str;
2662 norm_utf7_len = norm_utf7->len;
2663 to_len = strlen(mutf7_str) * 5;
2664 to_p = to_str = g_malloc(to_len + 1);
2666 if (iconv(cd, (ICONV_CONST gchar **)&norm_utf7_p, &norm_utf7_len,
2667 &to_p, &to_len) == -1) {
2668 g_warning(_("iconv cannot convert UTF-7 to %s\n"),
2669 conv_get_locale_charset_str());
2670 g_string_free(norm_utf7, TRUE);
2672 return g_strdup(mutf7_str);
2675 /* second iconv() call for flushing */
2676 iconv(cd, NULL, NULL, &to_p, &to_len);
2677 g_string_free(norm_utf7, TRUE);
2683 static gchar *imap_utf8_to_modified_utf7(const gchar *from)
2685 static iconv_t cd = (iconv_t)-1;
2686 static gboolean iconv_ok = TRUE;
2687 gchar *norm_utf7, *norm_utf7_p;
2688 size_t from_len, norm_utf7_len;
2690 gchar *from_tmp, *to, *p;
2691 gboolean in_escape = FALSE;
2693 if (!iconv_ok) return g_strdup(from);
2695 if (cd == (iconv_t)-1) {
2696 cd = iconv_open(CS_UTF_7, CS_INTERNAL);
2697 if (cd == (iconv_t)-1) {
2698 g_warning(_("iconv cannot convert %s to UTF-7\n"),
2701 return g_strdup(from);
2705 /* UTF-8 to normal UTF-7 conversion */
2706 Xstrdup_a(from_tmp, from, return g_strdup(from));
2707 from_len = strlen(from);
2708 norm_utf7_len = from_len * 5;
2709 Xalloca(norm_utf7, norm_utf7_len + 1, return g_strdup(from));
2710 norm_utf7_p = norm_utf7;
2712 #define IS_PRINT(ch) (isprint(ch) && IS_ASCII(ch))
2714 while (from_len > 0) {
2715 if (*from_tmp == '+') {
2716 *norm_utf7_p++ = '+';
2717 *norm_utf7_p++ = '-';
2721 } else if (IS_PRINT(*(guchar *)from_tmp)) {
2722 /* printable ascii char */
2723 *norm_utf7_p = *from_tmp;
2729 size_t conv_len = 0;
2731 /* unprintable char: convert to UTF-7 */
2733 while (!IS_PRINT(*(guchar *)p) && conv_len < from_len) {
2734 conv_len += g_utf8_skip[*(guchar *)p];
2735 p += g_utf8_skip[*(guchar *)p];
2738 from_len -= conv_len;
2739 if (iconv(cd, (ICONV_CONST gchar **)&from_tmp,
2741 &norm_utf7_p, &norm_utf7_len) == -1) {
2742 g_warning(_("iconv cannot convert UTF-8 to UTF-7\n"));
2743 return g_strdup(from);
2746 /* second iconv() call for flushing */
2747 iconv(cd, NULL, NULL, &norm_utf7_p, &norm_utf7_len);
2753 *norm_utf7_p = '\0';
2754 to_str = g_string_new(NULL);
2755 for (p = norm_utf7; p < norm_utf7_p; p++) {
2756 /* replace: '&' -> "&-",
2759 BASE64 '/' -> ',' */
2760 if (!in_escape && *p == '&') {
2761 g_string_append(to_str, "&-");
2762 } else if (!in_escape && *p == '+') {
2763 if (*(p + 1) == '-') {
2764 g_string_append_c(to_str, '+');
2767 g_string_append_c(to_str, '&');
2770 } else if (in_escape && *p == '/') {
2771 g_string_append_c(to_str, ',');
2772 } else if (in_escape && *p == '-') {
2773 g_string_append_c(to_str, '-');
2776 g_string_append_c(to_str, *p);
2782 g_string_append_c(to_str, '-');
2786 g_string_free(to_str, FALSE);
2791 static gboolean imap_rename_folder_func(GNode *node, gpointer data)
2793 FolderItem *item = node->data;
2794 gchar **paths = data;
2795 const gchar *oldpath = paths[0];
2796 const gchar *newpath = paths[1];
2798 gchar *new_itempath;
2801 oldpathlen = strlen(oldpath);
2802 if (strncmp(oldpath, item->path, oldpathlen) != 0) {
2803 g_warning("path doesn't match: %s, %s\n", oldpath, item->path);
2807 base = item->path + oldpathlen;
2808 while (*base == G_DIR_SEPARATOR) base++;
2810 new_itempath = g_strdup(newpath);
2812 new_itempath = g_strconcat(newpath, G_DIR_SEPARATOR_S, base,
2815 item->path = new_itempath;
2820 typedef struct _get_list_uid_data {
2822 IMAPFolderItem *item;
2823 GSList **msgnum_list;
2825 } get_list_uid_data;
2827 static void *get_list_of_uids_thread(void *data)
2829 get_list_uid_data *stuff = (get_list_uid_data *)data;
2830 Folder *folder = stuff->folder;
2831 IMAPFolderItem *item = stuff->item;
2832 GSList **msgnum_list = stuff->msgnum_list;
2833 gint ok, nummsgs = 0, lastuid_old;
2834 IMAPSession *session;
2835 GSList *uidlist, *elem;
2836 struct mailimap_set * set;
2837 clist * lep_uidlist;
2840 session = imap_session_get(folder);
2841 if (session == NULL) {
2843 return GINT_TO_POINTER(-1);
2846 ok = imap_select(session, IMAP_FOLDER(folder), item->item.path,
2847 NULL, NULL, NULL, NULL, TRUE);
2848 if (ok != IMAP_SUCCESS) {
2850 return GINT_TO_POINTER(-1);
2855 set = mailimap_set_new_interval(item->lastuid + 1, 0);
2856 r = imap_threaded_search(folder, IMAP_SEARCH_TYPE_SIMPLE, set,
2858 if (r == MAILIMAP_NO_ERROR) {
2859 GSList * fetchuid_list;
2862 imap_uid_list_from_lep(lep_uidlist);
2863 uidlist = g_slist_concat(fetchuid_list, uidlist);
2866 GSList * fetchuid_list;
2867 carray * lep_uidtab;
2869 r = imap_threaded_fetch_uid(folder, item->lastuid + 1,
2871 if (r == MAILIMAP_NO_ERROR) {
2873 imap_uid_list_from_lep_tab(lep_uidtab);
2874 uidlist = g_slist_concat(fetchuid_list, uidlist);
2878 lastuid_old = item->lastuid;
2879 *msgnum_list = g_slist_copy(item->uid_list);
2880 nummsgs = g_slist_length(*msgnum_list);
2881 debug_print("Got %d uids from cache\n", g_slist_length(item->uid_list));
2883 for (elem = uidlist; elem != NULL; elem = g_slist_next(elem)) {
2886 msgnum = GPOINTER_TO_INT(elem->data);
2887 if (msgnum > lastuid_old) {
2888 *msgnum_list = g_slist_prepend(*msgnum_list, GINT_TO_POINTER(msgnum));
2889 item->uid_list = g_slist_prepend(item->uid_list, GINT_TO_POINTER(msgnum));
2892 if(msgnum > item->lastuid)
2893 item->lastuid = msgnum;
2896 g_slist_free(uidlist);
2899 return GINT_TO_POINTER(nummsgs);
2902 static gint get_list_of_uids(Folder *folder, IMAPFolderItem *item, GSList **msgnum_list)
2905 get_list_uid_data *data = g_new0(get_list_uid_data, 1);
2907 data->folder = folder;
2909 data->msgnum_list = msgnum_list;
2911 if (prefs_common.work_offline && !inc_offline_should_override()) {
2916 result = GPOINTER_TO_INT(get_list_of_uids_thread(data));
2922 gint imap_get_num_list(Folder *folder, FolderItem *_item, GSList **msgnum_list, gboolean *old_uids_valid)
2924 IMAPFolderItem *item = (IMAPFolderItem *)_item;
2925 IMAPSession *session;
2926 gint ok, nummsgs = 0, exists, recent, uid_val, uid_next, unseen;
2927 GSList *uidlist = NULL;
2929 gboolean selected_folder;
2931 debug_print("get_num_list\n");
2933 g_return_val_if_fail(folder != NULL, -1);
2934 g_return_val_if_fail(item != NULL, -1);
2935 g_return_val_if_fail(item->item.path != NULL, -1);
2936 g_return_val_if_fail(FOLDER_CLASS(folder) == &imap_class, -1);
2937 g_return_val_if_fail(folder->account != NULL, -1);
2939 session = imap_session_get(folder);
2940 g_return_val_if_fail(session != NULL, -1);
2942 statusbar_print_all("Scanning %s...\n", FOLDER_ITEM(item)->path
2943 ? FOLDER_ITEM(item)->path:"");
2945 selected_folder = (session->mbox != NULL) &&
2946 (!strcmp(session->mbox, item->item.path));
2947 if (selected_folder) {
2948 ok = imap_cmd_noop(session);
2949 if (ok != IMAP_SUCCESS) {
2950 debug_print("disconnected!\n");
2951 session = imap_reconnect_if_possible(folder, session);
2952 if (session == NULL) {
2953 statusbar_pop_all();
2957 exists = session->exists;
2959 *old_uids_valid = TRUE;
2961 ok = imap_status(session, IMAP_FOLDER(folder), item->item.path,
2962 &exists, &recent, &uid_next, &uid_val, &unseen, FALSE);
2963 if (ok != IMAP_SUCCESS) {
2964 statusbar_pop_all();
2967 if(item->item.mtime == uid_val)
2968 *old_uids_valid = TRUE;
2970 *old_uids_valid = FALSE;
2972 debug_print("Freeing imap uid cache\n");
2974 g_slist_free(item->uid_list);
2975 item->uid_list = NULL;
2977 item->item.mtime = uid_val;
2979 imap_delete_all_cached_messages((FolderItem *)item);
2983 if (!selected_folder)
2984 item->uid_next = uid_next;
2986 /* If old uid_next matches new uid_next we can be sure no message
2987 was added to the folder */
2988 if (( selected_folder && !session->folder_content_changed) ||
2989 (!selected_folder && uid_next == item->uid_next)) {
2990 nummsgs = g_slist_length(item->uid_list);
2992 /* If number of messages is still the same we
2993 know our caches message numbers are still valid,
2994 otherwise if the number of messages has decrease
2995 we discard our cache to start a new scan to find
2996 out which numbers have been removed */
2997 if (exists == nummsgs) {
2998 *msgnum_list = g_slist_copy(item->uid_list);
2999 statusbar_pop_all();
3001 } else if (exists < nummsgs) {
3002 debug_print("Freeing imap uid cache");
3004 g_slist_free(item->uid_list);
3005 item->uid_list = NULL;
3010 *msgnum_list = NULL;
3011 statusbar_pop_all();
3015 nummsgs = get_list_of_uids(folder, item, &uidlist);
3018 statusbar_pop_all();
3022 if (nummsgs != exists) {
3023 /* Cache contains more messages then folder, we have cached
3024 an old UID of a message that was removed and new messages
3025 have been added too, otherwise the uid_next check would
3027 debug_print("Freeing imap uid cache");
3029 g_slist_free(item->uid_list);
3030 item->uid_list = NULL;
3032 g_slist_free(*msgnum_list);
3034 nummsgs = get_list_of_uids(folder, item, &uidlist);
3037 *msgnum_list = uidlist;
3039 dir = folder_item_get_path((FolderItem *)item);
3040 debug_print("removing old messages from %s\n", dir);
3041 remove_numbered_files_not_in_list(dir, *msgnum_list);
3044 debug_print("get_num_list - ok - %i\n", nummsgs);
3045 statusbar_pop_all();
3049 static MsgInfo *imap_parse_msg(const gchar *file, FolderItem *item)
3054 flags.perm_flags = MSG_NEW|MSG_UNREAD;
3055 flags.tmp_flags = 0;
3057 g_return_val_if_fail(item != NULL, NULL);
3058 g_return_val_if_fail(file != NULL, NULL);
3060 if (folder_has_parent_of_type(item, F_QUEUE)) {
3061 MSG_SET_TMP_FLAGS(flags, MSG_QUEUED);
3062 } else if (folder_has_parent_of_type(item, F_DRAFT)) {
3063 MSG_SET_TMP_FLAGS(flags, MSG_DRAFT);
3066 msginfo = procheader_parse_file(file, flags, FALSE, FALSE);
3067 if (!msginfo) return NULL;
3069 msginfo->plaintext_file = g_strdup(file);
3070 msginfo->folder = item;
3075 GSList *imap_get_msginfos(Folder *folder, FolderItem *item,
3076 GSList *msgnum_list)
3078 IMAPSession *session;
3079 MsgInfoList *ret = NULL;
3082 debug_print("get_msginfos\n");
3084 g_return_val_if_fail(folder != NULL, NULL);
3085 g_return_val_if_fail(item != NULL, NULL);
3086 g_return_val_if_fail(msgnum_list != NULL, NULL);
3088 session = imap_session_get(folder);
3089 g_return_val_if_fail(session != NULL, NULL);
3091 debug_print("IMAP getting msginfos\n");
3092 ok = imap_select(session, IMAP_FOLDER(folder), item->path,
3093 NULL, NULL, NULL, NULL, FALSE);
3094 if (ok != IMAP_SUCCESS)
3097 if (!(folder_has_parent_of_type(item, F_DRAFT) ||
3098 folder_has_parent_of_type(item, F_QUEUE))) {
3099 ret = g_slist_concat(ret,
3100 imap_get_uncached_messages(session, item,
3103 MsgNumberList *sorted_list, *elem;
3104 gint startnum, lastnum;
3106 sorted_list = g_slist_sort(g_slist_copy(msgnum_list), g_int_compare);
3108 startnum = lastnum = GPOINTER_TO_INT(sorted_list->data);
3110 for (elem = sorted_list;; elem = g_slist_next(elem)) {
3114 num = GPOINTER_TO_INT(elem->data);
3116 if (num > lastnum + 1 || elem == NULL) {
3118 for (i = startnum; i <= lastnum; ++i) {
3121 file = imap_fetch_msg(folder, item, i);
3123 MsgInfo *msginfo = imap_parse_msg(file, item);
3124 if (msginfo != NULL) {
3125 msginfo->msgnum = i;
3126 ret = g_slist_append(ret, msginfo);
3140 g_slist_free(sorted_list);
3146 MsgInfo *imap_get_msginfo(Folder *folder, FolderItem *item, gint uid)
3148 MsgInfo *msginfo = NULL;
3149 MsgInfoList *msginfolist;
3150 MsgNumberList numlist;
3152 numlist.next = NULL;
3153 numlist.data = GINT_TO_POINTER(uid);
3155 msginfolist = imap_get_msginfos(folder, item, &numlist);
3156 if (msginfolist != NULL) {
3157 msginfo = msginfolist->data;
3158 g_slist_free(msginfolist);
3164 gboolean imap_scan_required(Folder *folder, FolderItem *_item)
3166 IMAPSession *session;
3167 IMAPFolderItem *item = (IMAPFolderItem *)_item;
3168 gint ok, exists = 0, recent = 0, unseen = 0;
3169 guint32 uid_next, uid_val = 0;
3170 gboolean selected_folder;
3172 g_return_val_if_fail(folder != NULL, FALSE);
3173 g_return_val_if_fail(item != NULL, FALSE);
3174 g_return_val_if_fail(item->item.folder != NULL, FALSE);
3175 g_return_val_if_fail(FOLDER_CLASS(item->item.folder) == &imap_class, FALSE);
3177 if (item->item.path == NULL)
3180 session = imap_session_get(folder);
3181 g_return_val_if_fail(session != NULL, FALSE);
3183 selected_folder = (session->mbox != NULL) &&
3184 (!strcmp(session->mbox, item->item.path));
3185 if (selected_folder) {
3186 ok = imap_cmd_noop(session);
3187 if (ok != IMAP_SUCCESS) {
3188 debug_print("disconnected!\n");
3189 session = imap_reconnect_if_possible(folder, session);
3190 if (session == NULL)
3194 if (session->folder_content_changed
3195 || session->exists != item->item.total_msgs)
3198 ok = imap_status(session, IMAP_FOLDER(folder), item->item.path,
3199 &exists, &recent, &uid_next, &uid_val, &unseen, FALSE);
3200 if (ok != IMAP_SUCCESS)
3203 if ((uid_next != item->uid_next) || (exists != item->item.total_msgs))
3210 void imap_change_flags(Folder *folder, FolderItem *item, MsgInfo *msginfo, MsgPermFlags newflags)
3212 IMAPSession *session;
3213 IMAPFlags flags_set = 0, flags_unset = 0;
3214 gint ok = IMAP_SUCCESS;
3215 MsgNumberList numlist;
3216 hashtable_data *ht_data = NULL;
3218 g_return_if_fail(folder != NULL);
3219 g_return_if_fail(folder->klass == &imap_class);
3220 g_return_if_fail(item != NULL);
3221 g_return_if_fail(item->folder == folder);
3222 g_return_if_fail(msginfo != NULL);
3223 g_return_if_fail(msginfo->folder == item);
3225 session = imap_session_get(folder);
3229 if ((ok = imap_select(session, IMAP_FOLDER(folder), msginfo->folder->path,
3230 NULL, NULL, NULL, NULL, FALSE)) != IMAP_SUCCESS) {
3234 if (!MSG_IS_MARKED(msginfo->flags) && (newflags & MSG_MARKED))
3235 flags_set |= IMAP_FLAG_FLAGGED;
3236 if ( MSG_IS_MARKED(msginfo->flags) && !(newflags & MSG_MARKED))
3237 flags_unset |= IMAP_FLAG_FLAGGED;
3239 if (!MSG_IS_UNREAD(msginfo->flags) && (newflags & MSG_UNREAD))
3240 flags_unset |= IMAP_FLAG_SEEN;
3241 if ( MSG_IS_UNREAD(msginfo->flags) && !(newflags & MSG_UNREAD))
3242 flags_set |= IMAP_FLAG_SEEN;
3244 if (!MSG_IS_REPLIED(msginfo->flags) && (newflags & MSG_REPLIED))
3245 flags_set |= IMAP_FLAG_ANSWERED;
3246 if ( MSG_IS_REPLIED(msginfo->flags) && !(newflags & MSG_REPLIED))
3247 flags_unset |= IMAP_FLAG_ANSWERED;
3249 if (!MSG_IS_DELETED(msginfo->flags) && (newflags & MSG_DELETED))
3250 flags_set |= IMAP_FLAG_DELETED;
3251 if ( MSG_IS_DELETED(msginfo->flags) && !(newflags & MSG_DELETED))
3252 flags_unset |= IMAP_FLAG_DELETED;
3254 numlist.next = NULL;
3255 numlist.data = GINT_TO_POINTER(msginfo->msgnum);
3257 if (IMAP_FOLDER_ITEM(item)->batching) {
3258 /* instead of performing an UID STORE command for each message change,
3259 * as a lot of them can change "together", we just fill in hashtables
3260 * and defer the treatment so that we're able to send only one
3263 debug_print("IMAP batch mode on, deferring flags change\n");
3265 ht_data = g_hash_table_lookup(flags_set_table, GINT_TO_POINTER(flags_set));
3266 if (ht_data == NULL) {
3267 ht_data = g_new0(hashtable_data, 1);
3268 ht_data->session = session;
3269 g_hash_table_insert(flags_set_table, GINT_TO_POINTER(flags_set), ht_data);
3271 if (!g_slist_find(ht_data->msglist, GINT_TO_POINTER(msginfo->msgnum)))
3272 ht_data->msglist = g_slist_prepend(ht_data->msglist, GINT_TO_POINTER(msginfo->msgnum));
3275 ht_data = g_hash_table_lookup(flags_unset_table, GINT_TO_POINTER(flags_unset));
3276 if (ht_data == NULL) {
3277 ht_data = g_new0(hashtable_data, 1);
3278 ht_data->session = session;
3279 g_hash_table_insert(flags_unset_table, GINT_TO_POINTER(flags_unset), ht_data);
3281 if (!g_slist_find(ht_data->msglist, GINT_TO_POINTER(msginfo->msgnum)))
3282 ht_data->msglist = g_slist_prepend(ht_data->msglist, GINT_TO_POINTER(msginfo->msgnum));
3285 debug_print("IMAP changing flags\n");
3287 ok = imap_set_message_flags(session, &numlist, flags_set, TRUE);
3288 if (ok != IMAP_SUCCESS) {
3294 ok = imap_set_message_flags(session, &numlist, flags_unset, FALSE);
3295 if (ok != IMAP_SUCCESS) {
3300 msginfo->flags.perm_flags = newflags;
3305 static gint imap_remove_msg(Folder *folder, FolderItem *item, gint uid)
3308 IMAPSession *session;
3310 MsgNumberList numlist;
3312 g_return_val_if_fail(folder != NULL, -1);
3313 g_return_val_if_fail(FOLDER_CLASS(folder) == &imap_class, -1);
3314 g_return_val_if_fail(item != NULL, -1);
3316 session = imap_session_get(folder);
3317 if (!session) return -1;
3319 ok = imap_select(session, IMAP_FOLDER(folder), item->path,
3320 NULL, NULL, NULL, NULL, FALSE);
3321 if (ok != IMAP_SUCCESS)
3324 numlist.next = NULL;
3325 numlist.data = GINT_TO_POINTER(uid);
3327 ok = imap_set_message_flags
3328 (IMAP_SESSION(REMOTE_FOLDER(folder)->session),
3329 &numlist, IMAP_FLAG_DELETED, TRUE);
3330 if (ok != IMAP_SUCCESS) {
3331 log_warning(_("can't set deleted flags: %d\n"), uid);
3335 if (!session->uidplus) {
3336 ok = imap_cmd_expunge(session);
3340 uidstr = g_strdup_printf("%u", uid);
3341 ok = imap_cmd_expunge(session);
3344 if (ok != IMAP_SUCCESS) {
3345 log_warning(_("can't expunge\n"));
3349 IMAP_FOLDER_ITEM(item)->uid_list = g_slist_remove(
3350 IMAP_FOLDER_ITEM(item)->uid_list, numlist.data);
3351 dir = folder_item_get_path(item);
3352 if (is_dir_exist(dir))
3353 remove_numbered_files(dir, uid, uid);
3356 return IMAP_SUCCESS;
3359 static gint compare_msginfo(gconstpointer a, gconstpointer b)
3361 return ((MsgInfo *)a)->msgnum - ((MsgInfo *)b)->msgnum;
3364 static guint gslist_find_next_num(MsgNumberList **list, guint num)
3368 g_return_val_if_fail(list != NULL, -1);
3370 for (elem = *list; elem != NULL; elem = g_slist_next(elem))
3371 if (GPOINTER_TO_INT(elem->data) >= num)
3374 return elem != NULL ? GPOINTER_TO_INT(elem->data) : (gint)-1;
3378 * NEW and DELETED flags are not syncronized
3379 * - The NEW/RECENT flags in IMAP folders can not really be directly
3380 * modified by Sylpheed
3381 * - The DELETE/DELETED flag in IMAP and Sylpheed don't have the same
3382 * meaning, in IMAP it always removes the messages from the FolderItem
3383 * in Sylpheed it can mean to move the message to trash
3386 typedef struct _get_flags_data {
3389 MsgInfoList *msginfo_list;
3390 GRelation *msgflags;
3394 static /*gint*/ void *imap_get_flags_thread(void *data)
3396 get_flags_data *stuff = (get_flags_data *)data;
3397 Folder *folder = stuff->folder;
3398 FolderItem *item = stuff->item;
3399 MsgInfoList *msginfo_list = stuff->msginfo_list;
3400 GRelation *msgflags = stuff->msgflags;
3401 IMAPSession *session;
3402 GSList *sorted_list;
3403 GSList *unseen = NULL, *answered = NULL, *flagged = NULL, *deleted = NULL;
3404 GSList *p_unseen, *p_answered, *p_flagged, *p_deleted;
3406 GSList *seq_list, *cur;
3407 gboolean reverse_seen = FALSE;
3410 gint exists_cnt, recent_cnt, unseen_cnt, uid_next;
3411 guint32 uidvalidity;
3412 gboolean selected_folder;
3414 if (folder == NULL || item == NULL) {
3416 return GINT_TO_POINTER(-1);
3418 if (msginfo_list == NULL) {
3420 return GINT_TO_POINTER(0);
3423 session = imap_session_get(folder);
3424 if (session == NULL) {
3426 return GINT_TO_POINTER(-1);
3429 selected_folder = (session->mbox != NULL) &&
3430 (!strcmp(session->mbox, item->path));
3432 if (!selected_folder) {
3433 ok = imap_status(session, IMAP_FOLDER(folder), item->path,
3434 &exists_cnt, &recent_cnt, &uid_next, &uidvalidity, &unseen_cnt, TRUE);
3435 ok = imap_select(session, IMAP_FOLDER(folder), item->path,
3436 NULL, NULL, NULL, NULL, TRUE);
3437 if (ok != IMAP_SUCCESS) {
3439 return GINT_TO_POINTER(-1);
3442 if (unseen_cnt > exists_cnt / 2)
3443 reverse_seen = TRUE;
3446 if (item->unread_msgs > item->total_msgs / 2)
3447 reverse_seen = TRUE;
3450 cmd_buf = g_string_new(NULL);
3452 sorted_list = g_slist_sort(g_slist_copy(msginfo_list), compare_msginfo);
3454 seq_list = imap_get_lep_set_from_msglist(msginfo_list);
3456 for (cur = seq_list; cur != NULL; cur = g_slist_next(cur)) {
3457 struct mailimap_set * imapset;
3458 clist * lep_uidlist;
3461 imapset = cur->data;
3463 r = imap_threaded_search(folder, IMAP_SEARCH_TYPE_SEEN,
3464 imapset, &lep_uidlist);
3467 r = imap_threaded_search(folder,
3468 IMAP_SEARCH_TYPE_UNSEEN,
3469 imapset, &lep_uidlist);
3471 if (r == MAILIMAP_NO_ERROR) {
3474 uidlist = imap_uid_list_from_lep(lep_uidlist);
3475 mailimap_search_result_free(lep_uidlist);
3477 unseen = g_slist_concat(unseen, uidlist);
3480 r = imap_threaded_search(folder, IMAP_SEARCH_TYPE_ANSWERED,
3481 imapset, &lep_uidlist);
3482 if (r == MAILIMAP_NO_ERROR) {
3485 uidlist = imap_uid_list_from_lep(lep_uidlist);
3486 mailimap_search_result_free(lep_uidlist);
3488 answered = g_slist_concat(answered, uidlist);
3491 r = imap_threaded_search(folder, IMAP_SEARCH_TYPE_FLAGGED,
3492 imapset, &lep_uidlist);
3493 if (r == MAILIMAP_NO_ERROR) {
3496 uidlist = imap_uid_list_from_lep(lep_uidlist);
3497 mailimap_search_result_free(lep_uidlist);
3499 flagged = g_slist_concat(flagged, uidlist);
3502 r = imap_threaded_search(folder, IMAP_SEARCH_TYPE_DELETED,
3503 imapset, &lep_uidlist);
3504 if (r == MAILIMAP_NO_ERROR) {
3507 uidlist = imap_uid_list_from_lep(lep_uidlist);
3508 mailimap_search_result_free(lep_uidlist);
3510 deleted = g_slist_concat(deleted, uidlist);
3515 p_answered = answered;
3516 p_flagged = flagged;
3517 p_deleted = deleted;
3519 for (elem = sorted_list; elem != NULL; elem = g_slist_next(elem)) {
3524 msginfo = (MsgInfo *) elem->data;
3525 flags = msginfo->flags.perm_flags;
3526 wasnew = (flags & MSG_NEW);
3527 flags &= ~((reverse_seen ? 0 : MSG_UNREAD | MSG_NEW) | MSG_REPLIED | MSG_MARKED);
3529 flags |= MSG_UNREAD | (wasnew ? MSG_NEW : 0);
3530 if (gslist_find_next_num(&p_unseen, msginfo->msgnum) == msginfo->msgnum) {
3531 if (!reverse_seen) {
3532 flags |= MSG_UNREAD | (wasnew ? MSG_NEW : 0);
3534 flags &= ~(MSG_UNREAD | MSG_NEW);
3537 if (gslist_find_next_num(&p_answered, msginfo->msgnum) == msginfo->msgnum)
3538 flags |= MSG_REPLIED;
3540 flags &= ~MSG_REPLIED;
3541 if (gslist_find_next_num(&p_flagged, msginfo->msgnum) == msginfo->msgnum)
3542 flags |= MSG_MARKED;
3544 flags &= ~MSG_MARKED;
3545 if (gslist_find_next_num(&p_deleted, msginfo->msgnum) == msginfo->msgnum)
3546 flags |= MSG_DELETED;
3548 flags &= ~MSG_DELETED;
3549 g_relation_insert(msgflags, msginfo, GINT_TO_POINTER(flags));
3552 imap_lep_set_free(seq_list);
3553 g_slist_free(flagged);
3554 g_slist_free(deleted);
3555 g_slist_free(answered);
3556 g_slist_free(unseen);
3557 g_slist_free(sorted_list);
3558 g_string_free(cmd_buf, TRUE);
3561 return GINT_TO_POINTER(0);
3564 static gint imap_get_flags(Folder *folder, FolderItem *item,
3565 MsgInfoList *msginfo_list, GRelation *msgflags)
3568 get_flags_data *data = g_new0(get_flags_data, 1);
3570 data->folder = folder;
3572 data->msginfo_list = msginfo_list;
3573 data->msgflags = msgflags;
3575 if (prefs_common.work_offline && !inc_offline_should_override()) {
3580 result = GPOINTER_TO_INT(imap_get_flags_thread(data));
3587 static gboolean process_flags(gpointer key, gpointer value, gpointer user_data)
3589 gboolean flags_set = GPOINTER_TO_INT(user_data);
3590 gint flags_value = GPOINTER_TO_INT(key);
3591 hashtable_data *data = (hashtable_data *)value;
3593 data->msglist = g_slist_reverse(data->msglist);
3595 debug_print("IMAP %ssetting flags to %d for %d messages\n",
3598 g_slist_length(data->msglist));
3599 imap_set_message_flags(data->session, data->msglist, flags_value, flags_set);
3601 g_slist_free(data->msglist);
3606 static void process_hashtable(void)
3608 if (flags_set_table) {
3609 g_hash_table_foreach_remove(flags_set_table, process_flags, GINT_TO_POINTER(TRUE));
3610 g_free(flags_set_table);
3611 flags_set_table = NULL;
3613 if (flags_unset_table) {
3614 g_hash_table_foreach_remove(flags_unset_table, process_flags, GINT_TO_POINTER(FALSE));
3615 g_free(flags_unset_table);
3616 flags_unset_table = NULL;
3620 static IMAPFolderItem *batching_item = NULL;
3622 static void imap_set_batch (Folder *folder, FolderItem *_item, gboolean batch)
3624 IMAPFolderItem *item = (IMAPFolderItem *)_item;
3626 g_return_if_fail(item != NULL);
3628 if (batch && batching_item != NULL) {
3629 g_warning("already batching on %s\n", batching_item->item.path);
3633 if (item->batching == batch)
3636 item->batching = batch;
3638 batching_item = batch?item:NULL;
3641 debug_print("IMAP switching to batch mode\n");
3642 if (flags_set_table) {
3643 g_warning("flags_set_table non-null but we just entered batch mode!\n");
3644 flags_set_table = NULL;
3646 if (flags_unset_table) {
3647 g_warning("flags_unset_table non-null but we just entered batch mode!\n");
3648 flags_unset_table = NULL;
3650 flags_set_table = g_hash_table_new(NULL, g_direct_equal);
3651 flags_unset_table = g_hash_table_new(NULL, g_direct_equal);
3653 debug_print("IMAP switching away from batch mode\n");
3655 process_hashtable();
3661 /* data types conversion libetpan <-> sylpheed */
3665 #define ETPAN_IMAP_MB_MARKED 1
3666 #define ETPAN_IMAP_MB_UNMARKED 2
3667 #define ETPAN_IMAP_MB_NOSELECT 4
3668 #define ETPAN_IMAP_MB_NOINFERIORS 8
3670 static int imap_flags_to_flags(struct mailimap_mbx_list_flags * imap_flags)
3676 if (imap_flags->mbf_type == MAILIMAP_MBX_LIST_FLAGS_SFLAG) {
3677 switch (imap_flags->mbf_sflag) {
3678 case MAILIMAP_MBX_LIST_SFLAG_MARKED:
3679 flags |= ETPAN_IMAP_MB_MARKED;
3681 case MAILIMAP_MBX_LIST_SFLAG_NOSELECT:
3682 flags |= ETPAN_IMAP_MB_NOSELECT;
3684 case MAILIMAP_MBX_LIST_SFLAG_UNMARKED:
3685 flags |= ETPAN_IMAP_MB_UNMARKED;
3690 for(cur = clist_begin(imap_flags->mbf_oflags) ; cur != NULL ;
3691 cur = clist_next(cur)) {
3692 struct mailimap_mbx_list_oflag * oflag;
3694 oflag = clist_content(cur);
3696 switch (oflag->of_type) {
3697 case MAILIMAP_MBX_LIST_OFLAG_NOINFERIORS:
3698 flags |= ETPAN_IMAP_MB_NOINFERIORS;
3706 static GSList * imap_list_from_lep(IMAPFolder * folder,
3707 clist * list, const gchar * real_path)
3714 for(iter = clist_begin(list) ; iter != NULL ;
3715 iter = clist_next(iter)) {
3716 struct mailimap_mailbox_list * mb;
3724 FolderItem *new_item;
3726 mb = clist_content(iter);
3729 if (mb->mb_flag != NULL)
3730 flags = imap_flags_to_flags(mb->mb_flag);
3732 delimiter = mb->mb_delimiter;
3735 dup_name = strdup(name);
3736 if (delimiter != '\0')
3737 subst_char(dup_name, delimiter, '/');
3739 base = g_path_get_basename(dup_name);
3740 if (base[0] == '.') {
3746 if (strcmp(dup_name, real_path) == 0) {
3752 if (dup_name[strlen(dup_name)-1] == '/') {
3758 loc_name = imap_modified_utf7_to_utf8(base);
3759 loc_path = imap_modified_utf7_to_utf8(dup_name);
3761 new_item = folder_item_new(FOLDER(folder), loc_name, loc_path);
3762 if ((flags & ETPAN_IMAP_MB_NOINFERIORS) != 0)
3763 new_item->no_sub = TRUE;
3764 if (strcmp(dup_name, "INBOX") != 0 &&
3765 ((flags & ETPAN_IMAP_MB_NOSELECT) != 0))
3766 new_item->no_select = TRUE;
3768 item_list = g_slist_append(item_list, new_item);
3770 debug_print("folder '%s' found.\n", loc_path);
3781 static GSList * imap_get_lep_set_from_numlist(MsgNumberList *numlist)
3783 GSList *sorted_list, *cur;
3784 guint first, last, next;
3785 GSList *ret_list = NULL;
3787 struct mailimap_set * current_set;
3788 unsigned int item_count;
3790 if (numlist == NULL)
3794 current_set = mailimap_set_new_empty();
3796 sorted_list = g_slist_copy(numlist);
3797 sorted_list = g_slist_sort(sorted_list, g_int_compare);
3799 first = GPOINTER_TO_INT(sorted_list->data);
3802 for (cur = sorted_list; cur != NULL; cur = g_slist_next(cur)) {
3803 if (GPOINTER_TO_INT(cur->data) == 0)
3808 last = GPOINTER_TO_INT(cur->data);
3810 next = GPOINTER_TO_INT(cur->next->data);
3814 if (last + 1 != next || next == 0) {
3816 struct mailimap_set_item * item;
3817 item = mailimap_set_item_new(first, last);
3818 mailimap_set_add(current_set, item);
3823 if (count >= IMAP_SET_MAX_COUNT) {
3824 ret_list = g_slist_append(ret_list,
3826 current_set = mailimap_set_new_empty();
3833 if (clist_count(current_set->set_list) > 0) {
3834 ret_list = g_slist_append(ret_list,
3838 g_slist_free(sorted_list);
3843 static GSList * imap_get_lep_set_from_msglist(MsgInfoList *msglist)
3845 MsgNumberList *numlist = NULL;
3849 for (cur = msglist; cur != NULL; cur = g_slist_next(cur)) {
3850 MsgInfo *msginfo = (MsgInfo *) cur->data;
3852 numlist = g_slist_append(numlist, GINT_TO_POINTER(msginfo->msgnum));
3854 seq_list = imap_get_lep_set_from_numlist(numlist);
3855 g_slist_free(numlist);
3860 static GSList * imap_uid_list_from_lep(clist * list)
3867 for(iter = clist_begin(list) ; iter != NULL ;
3868 iter = clist_next(iter)) {
3871 puid = clist_content(iter);
3872 result = g_slist_append(result, GINT_TO_POINTER(* puid));
3878 static GSList * imap_uid_list_from_lep_tab(carray * list)
3885 for(i = 0 ; i < carray_count(list) ; i ++) {
3888 puid = carray_get(list, i);
3889 result = g_slist_append(result, GINT_TO_POINTER(* puid));
3895 static MsgInfo *imap_envelope_from_lep(struct imap_fetch_env_info * info,
3898 MsgInfo *msginfo = NULL;
3901 MsgFlags flags = {0, 0};
3903 MSG_SET_TMP_FLAGS(flags, MSG_IMAP);
3904 if (folder_has_parent_of_type(item, F_QUEUE)) {
3905 MSG_SET_TMP_FLAGS(flags, MSG_QUEUED);
3906 } else if (folder_has_parent_of_type(item, F_DRAFT)) {
3907 MSG_SET_TMP_FLAGS(flags, MSG_DRAFT);
3909 flags.perm_flags = info->flags;
3913 msginfo = procheader_parse_str(info->headers, flags, FALSE, FALSE);
3916 msginfo->msgnum = uid;
3917 msginfo->size = size;
3923 static void imap_lep_set_free(GSList *seq_list)
3927 for(cur = seq_list ; cur != NULL ; cur = g_slist_next(cur)) {
3928 struct mailimap_set * imapset;
3930 imapset = cur->data;
3931 mailimap_set_free(imapset);
3933 g_slist_free(seq_list);
3936 static struct mailimap_flag_list * imap_flag_to_lep(IMAPFlags flags)
3938 struct mailimap_flag_list * flag_list;
3940 flag_list = mailimap_flag_list_new_empty();
3942 if (IMAP_IS_SEEN(flags))
3943 mailimap_flag_list_add(flag_list,
3944 mailimap_flag_new_seen());
3945 if (IMAP_IS_ANSWERED(flags))
3946 mailimap_flag_list_add(flag_list,
3947 mailimap_flag_new_answered());
3948 if (IMAP_IS_FLAGGED(flags))
3949 mailimap_flag_list_add(flag_list,
3950 mailimap_flag_new_flagged());
3951 if (IMAP_IS_DELETED(flags))
3952 mailimap_flag_list_add(flag_list,
3953 mailimap_flag_new_deleted());
3954 if (IMAP_IS_DRAFT(flags))
3955 mailimap_flag_list_add(flag_list,
3956 mailimap_flag_new_draft());
3961 guint imap_folder_get_refcnt(Folder *folder)
3963 return ((IMAPFolder *)folder)->refcnt;
3966 void imap_folder_ref(Folder *folder)
3968 ((IMAPFolder *)folder)->refcnt++;
3971 void imap_folder_unref(Folder *folder)
3973 if (((IMAPFolder *)folder)->refcnt > 0)
3974 ((IMAPFolder *)folder)->refcnt--;
3977 #else /* HAVE_LIBETPAN */
3979 static FolderClass imap_class;
3981 static Folder *imap_folder_new (const gchar *name,
3986 static gint imap_create_tree (Folder *folder)
3990 static FolderItem *imap_create_folder (Folder *folder,
3996 static gint imap_rename_folder (Folder *folder,
4003 FolderClass *imap_get_class(void)
4005 if (imap_class.idstr == NULL) {
4006 imap_class.type = F_IMAP;
4007 imap_class.idstr = "imap";
4008 imap_class.uistr = "IMAP4";
4010 imap_class.new_folder = imap_folder_new;
4011 imap_class.create_tree = imap_create_tree;
4012 imap_class.create_folder = imap_create_folder;
4013 imap_class.rename_folder = imap_rename_folder;
4014 /* nothing implemented */
4021 void imap_synchronise(FolderItem *item)
4023 imap_gtk_synchronise(item);