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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, 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 alertpanel_error(_("Connection to %s failed: login refused.%s"),
611 SESSION(session)->server, ext_info);
613 last_login_err = time(NULL);
618 static IMAPSession *imap_reconnect_if_possible(Folder *folder, IMAPSession *session)
620 RemoteFolder *rfolder = REMOTE_FOLDER(folder);
621 /* Check if this is the first try to establish a
622 connection, if yes we don't try to reconnect */
623 debug_print("reconnecting\n");
624 if (rfolder->session == NULL) {
625 log_warning(_("Connecting to %s failed"),
626 folder->account->recv_server);
627 session_destroy(SESSION(session));
630 log_warning(_("IMAP4 connection to %s has been"
631 " disconnected. Reconnecting...\n"),
632 folder->account->recv_server);
633 statusbar_print_all(_("IMAP4 connection to %s has been"
634 " disconnected. Reconnecting...\n"),
635 folder->account->recv_server);
636 SESSION(session)->state = SESSION_DISCONNECTED;
637 session_destroy(SESSION(session));
638 /* Clear folders session to make imap_session_get create
639 a new session, because of rfolder->session == NULL
640 it will not try to reconnect again and so avoid an
642 rfolder->session = NULL;
643 session = imap_session_get(folder);
644 rfolder->session = SESSION(session);
650 static IMAPSession *imap_session_get(Folder *folder)
652 RemoteFolder *rfolder = REMOTE_FOLDER(folder);
653 IMAPSession *session = NULL;
655 g_return_val_if_fail(folder != NULL, NULL);
656 g_return_val_if_fail(FOLDER_CLASS(folder) == &imap_class, NULL);
657 g_return_val_if_fail(folder->account != NULL, NULL);
659 if (prefs_common.work_offline && !inc_offline_should_override()) {
663 /* Make sure we have a session */
664 if (rfolder->session != NULL) {
665 session = IMAP_SESSION(rfolder->session);
667 imap_reset_uid_lists(folder);
668 session = imap_session_new(folder, folder->account);
673 /* Make sure session is authenticated */
674 if (!IMAP_SESSION(session)->authenticated)
675 imap_session_authenticate(IMAP_SESSION(session), folder->account);
677 if (!IMAP_SESSION(session)->authenticated) {
678 session_destroy(SESSION(session));
679 rfolder->session = NULL;
684 /* Make sure we have parsed the IMAP namespace */
685 imap_parse_namespace(IMAP_SESSION(session),
686 IMAP_FOLDER(folder));
689 /* I think the point of this code is to avoid sending a
690 * keepalive if we've used the session recently and therefore
691 * think it's still alive. Unfortunately, most of the code
692 * does not yet check for errors on the socket, and so if the
693 * connection drops we don't notice until the timeout expires.
694 * A better solution than sending a NOOP every time would be
695 * for every command to be prepared to retry until it is
696 * successfully sent. -- mbp */
697 if (time(NULL) - SESSION(session)->last_access_time > SESSION_TIMEOUT_INTERVAL) {
698 /* verify that the session is still alive */
699 if (imap_cmd_noop(session) != IMAP_SUCCESS) {
700 debug_print("disconnected!\n");
701 session = imap_reconnect_if_possible(folder, session);
705 rfolder->session = SESSION(session);
707 return IMAP_SESSION(session);
710 static IMAPSession *imap_session_new(Folder * folder,
711 const PrefsAccount *account)
713 IMAPSession *session;
719 /* FIXME: IMAP over SSL only... */
722 port = account->set_imapport ? account->imapport
723 : account->ssl_imap == SSL_TUNNEL ? IMAPS_PORT : IMAP4_PORT;
724 ssl_type = account->ssl_imap;
726 if (account->ssl_imap != SSL_NONE) {
727 if (alertpanel_full(_("Insecure connection"),
728 _("This connection is configured to be secured "
729 "using SSL, but SSL is not available in this "
730 "build of Sylpheed-Claws. \n\n"
731 "Do you want to continue connecting to this "
732 "server? The communication would not be "
734 _("Continue connecting"),
735 GTK_STOCK_CANCEL, NULL,
736 FALSE, NULL, ALERT_WARNING,
737 G_ALERTALTERNATE) != G_ALERTDEFAULT)
740 port = account->set_imapport ? account->imapport
745 statusbar_print_all(_("Connecting to IMAP4 server: %s..."), folder->account->recv_server);
746 if (account->set_tunnelcmd) {
747 r = imap_threaded_connect_cmd(folder,
749 account->recv_server,
754 if (ssl_type == SSL_TUNNEL) {
755 r = imap_threaded_connect_ssl(folder,
756 account->recv_server,
762 r = imap_threaded_connect(folder,
763 account->recv_server,
769 if (r == MAILIMAP_NO_ERROR_AUTHENTICATED) {
770 authenticated = TRUE;
772 else if (r == MAILIMAP_NO_ERROR_NON_AUTHENTICATED) {
773 authenticated = FALSE;
776 if(!prefs_common.no_recv_err_panel) {
777 alertpanel_error(_("Can't connect to IMAP4 server: %s:%d"),
778 account->recv_server, port);
780 log_error(_("Can't connect to IMAP4 server: %s:%d"),
781 account->recv_server, port);
787 session = g_new0(IMAPSession, 1);
788 session_init(SESSION(session));
789 SESSION(session)->type = SESSION_IMAP;
790 SESSION(session)->server = g_strdup(account->recv_server);
791 SESSION(session)->sock = NULL;
793 SESSION(session)->destroy = imap_session_destroy;
795 session->capability = NULL;
797 session->authenticated = authenticated;
798 session->mbox = NULL;
799 session->cmd_count = 0;
800 session->folder = folder;
801 IMAP_FOLDER(session->folder)->last_seen_separator = 0;
804 if (account->ssl_imap == SSL_STARTTLS) {
807 ok = imap_cmd_starttls(session);
808 if (ok != IMAP_SUCCESS) {
809 log_warning(_("Can't start TLS session.\n"));
810 session_destroy(SESSION(session));
814 imap_free_capabilities(session);
815 session->authenticated = FALSE;
816 session->uidplus = FALSE;
817 session->cmd_count = 1;
820 log_message("IMAP connection is %s-authenticated\n",
821 (session->authenticated) ? "pre" : "un");
826 static void imap_session_authenticate(IMAPSession *session,
827 const PrefsAccount *account)
831 g_return_if_fail(account->userid != NULL);
833 pass = account->passwd;
836 tmp_pass = input_dialog_query_password(account->recv_server, account->userid);
838 tmp_pass = g_strdup(""); /* allow empty password */
839 Xstrdup_a(pass, tmp_pass, {g_free(tmp_pass); return;});
842 statusbar_print_all(_("Connecting to IMAP4 server %s...\n"),
843 account->recv_server);
844 if (imap_auth(session, account->userid, pass, account->imap_auth_type) != IMAP_SUCCESS) {
845 imap_threaded_disconnect(session->folder);
846 imap_cmd_logout(session);
852 session->authenticated = TRUE;
855 static void imap_session_destroy(Session *session)
857 if (session->state != SESSION_DISCONNECTED)
858 imap_threaded_disconnect(IMAP_SESSION(session)->folder);
860 imap_free_capabilities(IMAP_SESSION(session));
861 g_free(IMAP_SESSION(session)->mbox);
862 sock_close(session->sock);
863 session->sock = NULL;
866 static gchar *imap_fetch_msg(Folder *folder, FolderItem *item, gint uid)
868 return imap_fetch_msg_full(folder, item, uid, TRUE, TRUE);
871 static guint get_size_with_crs(MsgInfo *info)
880 fp = procmsg_open_message(info);
884 while (fgets(buf, sizeof (buf), fp) != NULL) {
886 if (!strstr(buf, "\r") && strstr(buf, "\n"))
894 static gchar *imap_fetch_msg_full(Folder *folder, FolderItem *item, gint uid,
895 gboolean headers, gboolean body)
897 gchar *path, *filename;
898 IMAPSession *session;
901 g_return_val_if_fail(folder != NULL, NULL);
902 g_return_val_if_fail(item != NULL, NULL);
907 path = folder_item_get_path(item);
908 if (!is_dir_exist(path))
910 filename = g_strconcat(path, G_DIR_SEPARATOR_S, itos(uid), NULL);
913 if (is_file_exist(filename)) {
914 /* see whether the local file represents the whole message
915 * or not. As the IMAP server reports size with \r chars,
916 * we have to update the local file (UNIX \n only) size */
917 MsgInfo *msginfo = imap_parse_msg(filename, item);
918 MsgInfo *cached = msgcache_get_msg(item->cache,uid);
919 guint have_size = get_size_with_crs(msginfo);
922 debug_print("message %d has been already %scached (%d/%d).\n", uid,
923 have_size == cached->size ? "fully ":"",
924 have_size, (int)cached->size);
926 if (cached && (cached->size == have_size || !body)) {
927 procmsg_msginfo_free(cached);
928 procmsg_msginfo_free(msginfo);
929 file_strip_crs(filename);
932 procmsg_msginfo_free(cached);
933 procmsg_msginfo_free(msginfo);
937 session = imap_session_get(folder);
943 debug_print("IMAP fetching messages\n");
944 ok = imap_select(session, IMAP_FOLDER(folder), item->path,
945 NULL, NULL, NULL, NULL, FALSE);
946 if (ok != IMAP_SUCCESS) {
947 g_warning("can't select mailbox %s\n", item->path);
952 debug_print("getting message %d...\n", uid);
953 ok = imap_cmd_fetch(session, (guint32)uid, filename, headers, body);
955 if (ok != IMAP_SUCCESS) {
956 g_warning("can't fetch message %d\n", uid);
961 file_strip_crs(filename);
965 static gint imap_add_msg(Folder *folder, FolderItem *dest,
966 const gchar *file, MsgFlags *flags)
970 MsgFileInfo fileinfo;
972 g_return_val_if_fail(file != NULL, -1);
974 fileinfo.msginfo = NULL;
975 fileinfo.file = (gchar *)file;
976 fileinfo.flags = flags;
977 file_list.data = &fileinfo;
978 file_list.next = NULL;
980 ret = imap_add_msgs(folder, dest, &file_list, NULL);
984 static gint imap_add_msgs(Folder *folder, FolderItem *dest, GSList *file_list,
988 IMAPSession *session;
989 guint32 last_uid = 0;
991 MsgFileInfo *fileinfo;
995 g_return_val_if_fail(folder != NULL, -1);
996 g_return_val_if_fail(dest != NULL, -1);
997 g_return_val_if_fail(file_list != NULL, -1);
999 session = imap_session_get(folder);
1003 destdir = imap_get_real_path(IMAP_FOLDER(folder), dest->path);
1005 for (cur = file_list; cur != NULL; cur = cur->next) {
1006 IMAPFlags iflags = 0;
1007 guint32 new_uid = 0;
1008 gchar *real_file = NULL;
1009 gboolean file_is_tmp = FALSE;
1010 fileinfo = (MsgFileInfo *)cur->data;
1012 if (fileinfo->flags) {
1013 if (MSG_IS_MARKED(*fileinfo->flags))
1014 iflags |= IMAP_FLAG_FLAGGED;
1015 if (MSG_IS_REPLIED(*fileinfo->flags))
1016 iflags |= IMAP_FLAG_ANSWERED;
1017 if (!MSG_IS_UNREAD(*fileinfo->flags))
1018 iflags |= IMAP_FLAG_SEEN;
1021 if (fileinfo->flags) {
1022 if ((MSG_IS_QUEUED(*fileinfo->flags)
1023 || MSG_IS_DRAFT(*fileinfo->flags))
1024 && !folder_has_parent_of_type(dest, F_QUEUE)
1025 && !folder_has_parent_of_type(dest, F_DRAFT)) {
1026 real_file = get_tmp_file();
1028 if (procmsg_remove_special_headers(
1037 if (real_file == NULL)
1038 real_file = g_strdup(fileinfo->file);
1040 if (folder_has_parent_of_type(dest, F_QUEUE) ||
1041 folder_has_parent_of_type(dest, F_OUTBOX) ||
1042 folder_has_parent_of_type(dest, F_DRAFT) ||
1043 folder_has_parent_of_type(dest, F_TRASH))
1044 iflags |= IMAP_FLAG_SEEN;
1046 ok = imap_cmd_append(session, destdir, real_file, iflags,
1049 if (ok != IMAP_SUCCESS) {
1050 g_warning("can't append message %s\n", real_file);
1052 g_unlink(real_file);
1058 if (relation != NULL)
1059 g_relation_insert(relation, fileinfo->msginfo != NULL ?
1060 (gpointer) fileinfo->msginfo : (gpointer) fileinfo,
1061 GINT_TO_POINTER(dest->last_num + 1));
1062 if (last_uid < new_uid)
1065 g_unlink(real_file);
1073 static gint imap_do_copy_msgs(Folder *folder, FolderItem *dest,
1074 MsgInfoList *msglist, GRelation *relation)
1078 GSList *seq_list, *cur;
1080 IMAPSession *session;
1081 gint ok = IMAP_SUCCESS;
1082 GRelation *uid_mapping;
1085 g_return_val_if_fail(folder != NULL, -1);
1086 g_return_val_if_fail(dest != NULL, -1);
1087 g_return_val_if_fail(msglist != NULL, -1);
1089 session = imap_session_get(folder);
1094 msginfo = (MsgInfo *)msglist->data;
1096 src = msginfo->folder;
1098 g_warning("the src folder is identical to the dest.\n");
1102 ok = imap_select(session, IMAP_FOLDER(folder), msginfo->folder->path,
1103 NULL, NULL, NULL, NULL, FALSE);
1104 if (ok != IMAP_SUCCESS) {
1108 destdir = imap_get_real_path(IMAP_FOLDER(folder), dest->path);
1109 seq_list = imap_get_lep_set_from_msglist(msglist);
1110 uid_mapping = g_relation_new(2);
1111 g_relation_index(uid_mapping, 0, g_direct_hash, g_direct_equal);
1113 for (cur = seq_list; cur != NULL; cur = g_slist_next(cur)) {
1114 struct mailimap_set * seq_set;
1116 seq_set = cur->data;
1118 debug_print("Copying messages from %s to %s ...\n",
1119 src->path, destdir);
1121 ok = imap_cmd_copy(session, seq_set, destdir, uid_mapping);
1122 if (ok != IMAP_SUCCESS) {
1123 g_relation_destroy(uid_mapping);
1124 imap_lep_set_free(seq_list);
1129 for (cur = msglist; cur != NULL; cur = g_slist_next(cur)) {
1130 MsgInfo *msginfo = (MsgInfo *)cur->data;
1133 tuples = g_relation_select(uid_mapping,
1134 GINT_TO_POINTER(msginfo->msgnum),
1136 if (tuples->len > 0) {
1137 gint num = GPOINTER_TO_INT(g_tuples_index(tuples, 0, 1));
1138 g_relation_insert(relation, msginfo,
1139 GPOINTER_TO_INT(num));
1143 g_relation_insert(relation, msginfo,
1144 GPOINTER_TO_INT(0));
1145 g_tuples_destroy(tuples);
1148 g_relation_destroy(uid_mapping);
1149 imap_lep_set_free(seq_list);
1153 IMAP_FOLDER_ITEM(dest)->lastuid = 0;
1154 IMAP_FOLDER_ITEM(dest)->uid_next = 0;
1155 g_slist_free(IMAP_FOLDER_ITEM(dest)->uid_list);
1156 IMAP_FOLDER_ITEM(dest)->uid_list = NULL;
1158 if (ok == IMAP_SUCCESS)
1164 static gint imap_copy_msg(Folder *folder, FolderItem *dest, MsgInfo *msginfo)
1168 g_return_val_if_fail(msginfo != NULL, -1);
1170 msglist.data = msginfo;
1171 msglist.next = NULL;
1173 return imap_copy_msgs(folder, dest, &msglist, NULL);
1176 static gint imap_copy_msgs(Folder *folder, FolderItem *dest,
1177 MsgInfoList *msglist, GRelation *relation)
1183 g_return_val_if_fail(folder != NULL, -1);
1184 g_return_val_if_fail(dest != NULL, -1);
1185 g_return_val_if_fail(msglist != NULL, -1);
1187 msginfo = (MsgInfo *)msglist->data;
1188 g_return_val_if_fail(msginfo->folder != NULL, -1);
1190 if (folder == msginfo->folder->folder &&
1191 !folder_has_parent_of_type(msginfo->folder, F_DRAFT) &&
1192 !folder_has_parent_of_type(msginfo->folder, F_QUEUE)) {
1193 ret = imap_do_copy_msgs(folder, dest, msglist, relation);
1197 file_list = procmsg_get_message_file_list(msglist);
1198 g_return_val_if_fail(file_list != NULL, -1);
1200 ret = imap_add_msgs(folder, dest, file_list, relation);
1202 procmsg_message_file_list_free(file_list);
1208 static gint imap_do_remove_msgs(Folder *folder, FolderItem *dest,
1209 MsgInfoList *msglist, GRelation *relation)
1212 GSList *seq_list = NULL, *cur;
1214 IMAPSession *session;
1215 gint ok = IMAP_SUCCESS;
1216 GRelation *uid_mapping;
1218 g_return_val_if_fail(folder != NULL, -1);
1219 g_return_val_if_fail(dest != NULL, -1);
1220 g_return_val_if_fail(msglist != NULL, -1);
1222 session = imap_session_get(folder);
1226 msginfo = (MsgInfo *)msglist->data;
1228 ok = imap_select(session, IMAP_FOLDER(folder), msginfo->folder->path,
1229 NULL, NULL, NULL, NULL, FALSE);
1230 if (ok != IMAP_SUCCESS) {
1234 destdir = imap_get_real_path(IMAP_FOLDER(folder), dest->path);
1235 for (cur = msglist; cur; cur = cur->next) {
1236 msginfo = (MsgInfo *)cur->data;
1237 seq_list = g_slist_append(seq_list, GINT_TO_POINTER(msginfo->msgnum));
1240 uid_mapping = g_relation_new(2);
1241 g_relation_index(uid_mapping, 0, g_direct_hash, g_direct_equal);
1243 ok = imap_set_message_flags
1244 (IMAP_SESSION(REMOTE_FOLDER(folder)->session),
1245 seq_list, IMAP_FLAG_DELETED, TRUE);
1246 if (ok != IMAP_SUCCESS) {
1247 log_warning(_("can't set deleted flags\n"));
1250 ok = imap_cmd_expunge(session);
1251 if (ok != IMAP_SUCCESS) {
1252 log_warning(_("can't expunge\n"));
1256 g_relation_destroy(uid_mapping);
1257 g_slist_free(seq_list);
1261 if (ok == IMAP_SUCCESS)
1267 static gint imap_remove_msgs(Folder *folder, FolderItem *dest,
1268 MsgInfoList *msglist, GRelation *relation)
1272 g_return_val_if_fail(folder != NULL, -1);
1273 g_return_val_if_fail(dest != NULL, -1);
1274 if (msglist == NULL)
1277 msginfo = (MsgInfo *)msglist->data;
1278 g_return_val_if_fail(msginfo->folder != NULL, -1);
1280 return imap_do_remove_msgs(folder, dest, msglist, relation);
1283 static gint imap_remove_all_msg(Folder *folder, FolderItem *item)
1285 GSList *list = folder_item_get_msg_list(item);
1286 gint res = imap_remove_msgs(folder, item, list, NULL);
1287 procmsg_msg_list_free(list);
1291 static gboolean imap_is_msg_changed(Folder *folder, FolderItem *item,
1294 /* TODO: properly implement this method */
1298 static gint imap_close(Folder *folder, FolderItem *item)
1303 static gint imap_scan_tree(Folder *folder)
1305 FolderItem *item = NULL;
1306 IMAPSession *session;
1307 gchar *root_folder = NULL;
1309 g_return_val_if_fail(folder != NULL, -1);
1310 g_return_val_if_fail(folder->account != NULL, -1);
1312 session = imap_session_get(folder);
1314 if (!folder->node) {
1315 folder_tree_destroy(folder);
1316 item = folder_item_new(folder, folder->name, NULL);
1317 item->folder = folder;
1318 folder->node = item->node = g_node_new(item);
1323 if (folder->account->imap_dir && *folder->account->imap_dir) {
1328 Xstrdup_a(root_folder, folder->account->imap_dir, {return -1;});
1329 extract_quote(root_folder, '"');
1330 subst_char(root_folder,
1331 imap_get_path_separator(IMAP_FOLDER(folder),
1334 strtailchomp(root_folder, '/');
1335 real_path = imap_get_real_path
1336 (IMAP_FOLDER(folder), root_folder);
1337 debug_print("IMAP root directory: %s\n", real_path);
1339 /* check if root directory exist */
1341 r = imap_threaded_list(session->folder, "", real_path,
1343 if ((r != MAILIMAP_NO_ERROR) || (clist_count(lep_list) == 0)) {
1344 if (!folder->node) {
1345 item = folder_item_new(folder, folder->name, NULL);
1346 item->folder = folder;
1347 folder->node = item->node = g_node_new(item);
1352 mailimap_list_result_free(lep_list);
1358 item = FOLDER_ITEM(folder->node->data);
1359 if (!item || ((item->path || root_folder) &&
1360 strcmp2(item->path, root_folder) != 0)) {
1361 folder_tree_destroy(folder);
1362 item = folder_item_new(folder, folder->name, root_folder);
1363 item->folder = folder;
1364 folder->node = item->node = g_node_new(item);
1367 imap_scan_tree_recursive(session, FOLDER_ITEM(folder->node->data));
1368 imap_create_missing_folders(folder);
1373 static gint imap_scan_tree_recursive(IMAPSession *session, FolderItem *item)
1376 IMAPFolder *imapfolder;
1377 FolderItem *new_item;
1378 GSList *item_list, *cur;
1381 gchar *wildcard_path;
1387 g_return_val_if_fail(item != NULL, -1);
1388 g_return_val_if_fail(item->folder != NULL, -1);
1389 g_return_val_if_fail(item->no_sub == FALSE, -1);
1391 folder = item->folder;
1392 imapfolder = IMAP_FOLDER(folder);
1394 separator = imap_get_path_separator(imapfolder, item->path);
1396 if (folder->ui_func)
1397 folder->ui_func(folder, item, folder->ui_func_data);
1400 wildcard[0] = separator;
1403 real_path = imap_get_real_path(imapfolder, item->path);
1407 real_path = g_strdup("");
1410 Xstrcat_a(wildcard_path, real_path, wildcard,
1411 {g_free(real_path); return IMAP_ERROR;});
1413 r = imap_threaded_list(folder, "", wildcard_path, &lep_list);
1414 if (r != MAILIMAP_NO_ERROR) {
1418 item_list = imap_list_from_lep(imapfolder,
1419 lep_list, real_path);
1420 mailimap_list_result_free(lep_list);
1425 node = item->node->children;
1426 while (node != NULL) {
1427 FolderItem *old_item = FOLDER_ITEM(node->data);
1428 GNode *next = node->next;
1431 for (cur = item_list; cur != NULL; cur = cur->next) {
1432 FolderItem *cur_item = FOLDER_ITEM(cur->data);
1433 if (!strcmp2(old_item->path, cur_item->path)) {
1434 new_item = cur_item;
1439 debug_print("folder '%s' not found. removing...\n",
1441 folder_item_remove(old_item);
1443 old_item->no_sub = new_item->no_sub;
1444 old_item->no_select = new_item->no_select;
1445 if (old_item->no_sub == TRUE && node->children) {
1446 debug_print("folder '%s' doesn't have "
1447 "subfolders. removing...\n",
1449 folder_item_remove_children(old_item);
1456 for (cur = item_list; cur != NULL; cur = cur->next) {
1457 FolderItem *cur_item = FOLDER_ITEM(cur->data);
1459 for (node = item->node->children; node != NULL;
1460 node = node->next) {
1461 if (!strcmp2(FOLDER_ITEM(node->data)->path,
1463 new_item = FOLDER_ITEM(node->data);
1464 folder_item_destroy(cur_item);
1470 new_item = cur_item;
1471 debug_print("new folder '%s' found.\n", new_item->path);
1472 folder_item_append(item, new_item);
1475 if (!strcmp(new_item->path, "INBOX")) {
1476 new_item->stype = F_INBOX;
1477 folder->inbox = new_item;
1478 } else if (!folder_item_parent(item) || item->stype == F_INBOX) {
1481 base = g_path_get_basename(new_item->path);
1483 if (!folder->outbox && !g_ascii_strcasecmp(base, "Sent")) {
1484 new_item->stype = F_OUTBOX;
1485 folder->outbox = new_item;
1486 } else if (!folder->draft && !g_ascii_strcasecmp(base, "Drafts")) {
1487 new_item->stype = F_DRAFT;
1488 folder->draft = new_item;
1489 } else if (!folder->queue && !g_ascii_strcasecmp(base, "Queue")) {
1490 new_item->stype = F_QUEUE;
1491 folder->queue = new_item;
1492 } else if (!folder->trash && !g_ascii_strcasecmp(base, "Trash")) {
1493 new_item->stype = F_TRASH;
1494 folder->trash = new_item;
1499 if (new_item->no_sub == FALSE)
1500 imap_scan_tree_recursive(session, new_item);
1503 g_slist_free(item_list);
1505 return IMAP_SUCCESS;
1508 static gint imap_create_tree(Folder *folder)
1510 g_return_val_if_fail(folder != NULL, -1);
1511 g_return_val_if_fail(folder->node != NULL, -1);
1512 g_return_val_if_fail(folder->node->data != NULL, -1);
1513 g_return_val_if_fail(folder->account != NULL, -1);
1515 imap_scan_tree(folder);
1516 imap_create_missing_folders(folder);
1521 static void imap_create_missing_folders(Folder *folder)
1523 g_return_if_fail(folder != NULL);
1526 folder->inbox = imap_create_special_folder
1527 (folder, F_INBOX, "INBOX");
1529 folder->trash = imap_create_special_folder
1530 (folder, F_TRASH, "Trash");
1532 folder->queue = imap_create_special_folder
1533 (folder, F_QUEUE, "Queue");
1534 if (!folder->outbox)
1535 folder->outbox = imap_create_special_folder
1536 (folder, F_OUTBOX, "Sent");
1538 folder->draft = imap_create_special_folder
1539 (folder, F_DRAFT, "Drafts");
1542 static FolderItem *imap_create_special_folder(Folder *folder,
1543 SpecialFolderItemType stype,
1547 FolderItem *new_item;
1549 g_return_val_if_fail(folder != NULL, NULL);
1550 g_return_val_if_fail(folder->node != NULL, NULL);
1551 g_return_val_if_fail(folder->node->data != NULL, NULL);
1552 g_return_val_if_fail(folder->account != NULL, NULL);
1553 g_return_val_if_fail(name != NULL, NULL);
1555 item = FOLDER_ITEM(folder->node->data);
1556 new_item = imap_create_folder(folder, item, name);
1559 g_warning("Can't create '%s'\n", name);
1560 if (!folder->inbox) return NULL;
1562 new_item = imap_create_folder(folder, folder->inbox, name);
1564 g_warning("Can't create '%s' under INBOX\n", name);
1566 new_item->stype = stype;
1568 new_item->stype = stype;
1573 static gchar *imap_folder_get_path(Folder *folder)
1577 g_return_val_if_fail(folder != NULL, NULL);
1578 g_return_val_if_fail(folder->account != NULL, NULL);
1580 folder_path = g_strconcat(get_imap_cache_dir(),
1582 folder->account->recv_server,
1584 folder->account->userid,
1590 static gchar *imap_item_get_path(Folder *folder, FolderItem *item)
1592 gchar *folder_path, *path;
1594 g_return_val_if_fail(folder != NULL, NULL);
1595 g_return_val_if_fail(item != NULL, NULL);
1596 folder_path = imap_folder_get_path(folder);
1598 g_return_val_if_fail(folder_path != NULL, NULL);
1599 if (folder_path[0] == G_DIR_SEPARATOR) {
1601 path = g_strconcat(folder_path, G_DIR_SEPARATOR_S,
1604 path = g_strdup(folder_path);
1607 path = g_strconcat(get_home_dir(), G_DIR_SEPARATOR_S,
1608 folder_path, G_DIR_SEPARATOR_S,
1611 path = g_strconcat(get_home_dir(), G_DIR_SEPARATOR_S,
1614 g_free(folder_path);
1619 static FolderItem *imap_create_folder(Folder *folder, FolderItem *parent,
1622 gchar *dirpath, *imap_path;
1623 IMAPSession *session;
1624 FolderItem *new_item;
1630 g_return_val_if_fail(folder != NULL, NULL);
1631 g_return_val_if_fail(folder->account != NULL, NULL);
1632 g_return_val_if_fail(parent != NULL, NULL);
1633 g_return_val_if_fail(name != NULL, NULL);
1635 session = imap_session_get(folder);
1640 if (!folder_item_parent(parent) && strcmp(name, "INBOX") == 0)
1641 dirpath = g_strdup(name);
1642 else if (parent->path)
1643 dirpath = g_strconcat(parent->path, "/", name, NULL);
1644 else if ((p = strchr(name, '/')) != NULL && *(p + 1) != '\0')
1645 dirpath = g_strdup(name);
1646 else if (folder->account->imap_dir && *folder->account->imap_dir) {
1649 Xstrdup_a(imap_dir, folder->account->imap_dir, {return NULL;});
1650 strtailchomp(imap_dir, '/');
1651 dirpath = g_strconcat(imap_dir, "/", name, NULL);
1653 dirpath = g_strdup(name);
1655 /* keep trailing directory separator to create a folder that contains
1657 imap_path = imap_utf8_to_modified_utf7(dirpath);
1658 strtailchomp(dirpath, '/');
1659 Xstrdup_a(new_name, name, {
1663 strtailchomp(new_name, '/');
1664 separator = imap_get_path_separator(IMAP_FOLDER(folder), imap_path);
1665 imap_path_separator_subst(imap_path, separator);
1666 subst_char(new_name, '/', separator);
1668 if (strcmp(name, "INBOX") != 0) {
1670 gboolean exist = FALSE;
1674 argbuf = g_ptr_array_new();
1675 r = imap_threaded_list(folder, "", imap_path, &lep_list);
1676 if (r != MAILIMAP_NO_ERROR) {
1677 log_warning(_("can't create mailbox: LIST failed\n"));
1680 ptr_array_free_strings(argbuf);
1681 g_ptr_array_free(argbuf, TRUE);
1685 if (clist_count(lep_list) > 0)
1689 ok = imap_cmd_create(session, imap_path);
1690 if (ok != IMAP_SUCCESS) {
1691 log_warning(_("can't create mailbox\n"));
1699 new_item = folder_item_new(folder, new_name, dirpath);
1700 folder_item_append(parent, new_item);
1704 dirpath = folder_item_get_path(new_item);
1705 if (!is_dir_exist(dirpath))
1706 make_dir_hier(dirpath);
1712 static gint imap_rename_folder(Folder *folder, FolderItem *item,
1717 gchar *real_oldpath;
1718 gchar *real_newpath;
1720 gchar *old_cache_dir;
1721 gchar *new_cache_dir;
1722 IMAPSession *session;
1725 gint exists, recent, unseen;
1726 guint32 uid_validity;
1728 g_return_val_if_fail(folder != NULL, -1);
1729 g_return_val_if_fail(item != NULL, -1);
1730 g_return_val_if_fail(item->path != NULL, -1);
1731 g_return_val_if_fail(name != NULL, -1);
1733 if (strchr(name, imap_get_path_separator(IMAP_FOLDER(folder), item->path)) != NULL) {
1734 g_warning(_("New folder name must not contain the namespace "
1739 session = imap_session_get(folder);
1743 real_oldpath = imap_get_real_path(IMAP_FOLDER(folder), item->path);
1745 g_free(session->mbox);
1746 session->mbox = NULL;
1747 ok = imap_cmd_examine(session, "INBOX",
1748 &exists, &recent, &unseen, &uid_validity, FALSE);
1749 if (ok != IMAP_SUCCESS) {
1750 g_free(real_oldpath);
1754 separator = imap_get_path_separator(IMAP_FOLDER(folder), item->path);
1755 if (strchr(item->path, G_DIR_SEPARATOR)) {
1756 dirpath = g_path_get_dirname(item->path);
1757 newpath = g_strconcat(dirpath, G_DIR_SEPARATOR_S, name, NULL);
1760 newpath = g_strdup(name);
1762 real_newpath = imap_utf8_to_modified_utf7(newpath);
1763 imap_path_separator_subst(real_newpath, separator);
1765 ok = imap_cmd_rename(session, real_oldpath, real_newpath);
1766 if (ok != IMAP_SUCCESS) {
1767 log_warning(_("can't rename mailbox: %s to %s\n"),
1768 real_oldpath, real_newpath);
1769 g_free(real_oldpath);
1771 g_free(real_newpath);
1776 item->name = g_strdup(name);
1778 old_cache_dir = folder_item_get_path(item);
1780 paths[0] = g_strdup(item->path);
1782 g_node_traverse(item->node, G_PRE_ORDER, G_TRAVERSE_ALL, -1,
1783 imap_rename_folder_func, paths);
1785 if (is_dir_exist(old_cache_dir)) {
1786 new_cache_dir = folder_item_get_path(item);
1787 if (rename(old_cache_dir, new_cache_dir) < 0) {
1788 FILE_OP_ERROR(old_cache_dir, "rename");
1790 g_free(new_cache_dir);
1793 g_free(old_cache_dir);
1796 g_free(real_oldpath);
1797 g_free(real_newpath);
1802 static gint imap_remove_folder_real(Folder *folder, FolderItem *item)
1805 IMAPSession *session;
1808 gint exists, recent, unseen;
1809 guint32 uid_validity;
1811 g_return_val_if_fail(folder != NULL, -1);
1812 g_return_val_if_fail(item != NULL, -1);
1813 g_return_val_if_fail(item->path != NULL, -1);
1815 session = imap_session_get(folder);
1819 path = imap_get_real_path(IMAP_FOLDER(folder), item->path);
1821 ok = imap_cmd_examine(session, "INBOX",
1822 &exists, &recent, &unseen, &uid_validity, FALSE);
1823 if (ok != IMAP_SUCCESS) {
1828 ok = imap_cmd_delete(session, path);
1829 if (ok != IMAP_SUCCESS) {
1830 log_warning(_("can't delete mailbox\n"));
1836 cache_dir = folder_item_get_path(item);
1837 if (is_dir_exist(cache_dir) && remove_dir_recursive(cache_dir) < 0)
1838 g_warning("can't remove directory '%s'\n", cache_dir);
1840 folder_item_remove(item);
1845 static gint imap_remove_folder(Folder *folder, FolderItem *item)
1849 g_return_val_if_fail(item != NULL, -1);
1850 g_return_val_if_fail(item->folder != NULL, -1);
1851 g_return_val_if_fail(item->node != NULL, -1);
1853 node = item->node->children;
1854 while (node != NULL) {
1856 if (imap_remove_folder(folder, FOLDER_ITEM(node->data)) < 0)
1860 debug_print("IMAP removing %s\n", item->path);
1862 if (imap_remove_all_msg(folder, item) < 0)
1864 return imap_remove_folder_real(folder, item);
1867 typedef struct _uncached_data {
1868 IMAPSession *session;
1870 MsgNumberList *numlist;
1876 static void *imap_get_uncached_messages_thread(void *data)
1878 uncached_data *stuff = (uncached_data *)data;
1879 IMAPSession *session = stuff->session;
1880 FolderItem *item = stuff->item;
1881 MsgNumberList *numlist = stuff->numlist;
1883 GSList *newlist = NULL;
1884 GSList *llast = NULL;
1885 GSList *seq_list, *cur;
1887 debug_print("uncached_messages\n");
1889 if (session == NULL || item == NULL || item->folder == NULL
1890 || FOLDER_CLASS(item->folder) != &imap_class) {
1895 seq_list = imap_get_lep_set_from_numlist(numlist);
1896 debug_print("get msgs info\n");
1897 for (cur = seq_list; cur != NULL; cur = g_slist_next(cur)) {
1898 struct mailimap_set * imapset;
1904 imapset = cur->data;
1906 r = imap_threaded_fetch_env(session->folder,
1907 imapset, &env_list);
1908 if (r != MAILIMAP_NO_ERROR)
1912 for(i = 0 ; i < carray_count(env_list) ; i ++) {
1913 struct imap_fetch_env_info * info;
1916 info = carray_get(env_list, i);
1917 msginfo = imap_envelope_from_lep(info, item);
1918 msginfo->folder = item;
1920 llast = newlist = g_slist_append(newlist, msginfo);
1922 llast = g_slist_append(llast, msginfo);
1923 llast = llast->next;
1928 imap_fetch_env_free(env_list);
1931 session_set_access_time(SESSION(session));
1936 #define MAX_MSG_NUM 50
1938 static GSList *imap_get_uncached_messages(IMAPSession *session,
1940 MsgNumberList *numlist)
1942 GSList *result = NULL;
1944 uncached_data *data = g_new0(uncached_data, 1);
1949 data->total = g_slist_length(numlist);
1950 debug_print("messages list : %i\n", data->total);
1952 while (cur != NULL) {
1953 GSList * partial_result;
1961 while (count < MAX_MSG_NUM) {
1966 if (newlist == NULL)
1967 llast = newlist = g_slist_append(newlist, p);
1969 llast = g_slist_append(llast, p);
1970 llast = llast->next;
1980 data->session = session;
1982 data->numlist = newlist;
1985 if (prefs_common.work_offline && !inc_offline_should_override()) {
1991 (GSList *)imap_get_uncached_messages_thread(data);
1993 statusbar_progress_all(data->cur,data->total, 1);
1995 g_slist_free(newlist);
1997 result = g_slist_concat(result, partial_result);
2001 statusbar_progress_all(0,0,0);
2002 statusbar_pop_all();
2007 static void imap_delete_all_cached_messages(FolderItem *item)
2011 g_return_if_fail(item != NULL);
2012 g_return_if_fail(item->folder != NULL);
2013 g_return_if_fail(FOLDER_CLASS(item->folder) == &imap_class);
2015 debug_print("Deleting all cached messages...\n");
2017 dir = folder_item_get_path(item);
2018 if (is_dir_exist(dir))
2019 remove_all_numbered_files(dir);
2022 debug_print("done.\n");
2025 static IMAPNameSpace *imap_find_namespace_from_list(GList *ns_list,
2028 IMAPNameSpace *namespace = NULL;
2029 gchar *tmp_path, *name;
2031 if (!path) path = "";
2033 for (; ns_list != NULL; ns_list = ns_list->next) {
2034 IMAPNameSpace *tmp_ns = ns_list->data;
2036 Xstrcat_a(tmp_path, path, "/", return namespace);
2037 Xstrdup_a(name, tmp_ns->name, return namespace);
2038 if (tmp_ns->separator && tmp_ns->separator != '/') {
2039 subst_char(tmp_path, tmp_ns->separator, '/');
2040 subst_char(name, tmp_ns->separator, '/');
2042 if (strncmp(tmp_path, name, strlen(name)) == 0)
2049 static IMAPNameSpace *imap_find_namespace(IMAPFolder *folder,
2052 IMAPNameSpace *namespace;
2054 g_return_val_if_fail(folder != NULL, NULL);
2056 namespace = imap_find_namespace_from_list(folder->ns_personal, path);
2057 if (namespace) return namespace;
2058 namespace = imap_find_namespace_from_list(folder->ns_others, path);
2059 if (namespace) return namespace;
2060 namespace = imap_find_namespace_from_list(folder->ns_shared, path);
2061 if (namespace) return namespace;
2067 static gchar imap_get_path_separator(IMAPFolder *folder, const gchar *path)
2069 IMAPNameSpace *namespace;
2070 gchar separator = '/';
2072 if (folder->last_seen_separator == 0) {
2074 int r = imap_threaded_list((Folder *)folder, "", "", &lep_list);
2075 if (r != MAILIMAP_NO_ERROR) {
2076 log_warning(_("LIST failed\n"));
2080 if (clist_count(lep_list) > 0) {
2081 clistiter * iter = clist_begin(lep_list);
2082 struct mailimap_mailbox_list * mb;
2083 mb = clist_content(iter);
2085 folder->last_seen_separator = mb->mb_delimiter;
2086 debug_print("got separator: %c\n", folder->last_seen_separator);
2088 mailimap_list_result_free(lep_list);
2091 if (folder->last_seen_separator != 0) {
2092 debug_print("using separator: %c\n", folder->last_seen_separator);
2093 return folder->last_seen_separator;
2096 namespace = imap_find_namespace(folder, path);
2097 if (namespace && namespace->separator)
2098 separator = namespace->separator;
2103 static gchar *imap_get_real_path(IMAPFolder *folder, const gchar *path)
2108 g_return_val_if_fail(folder != NULL, NULL);
2109 g_return_val_if_fail(path != NULL, NULL);
2111 real_path = imap_utf8_to_modified_utf7(path);
2112 separator = imap_get_path_separator(folder, path);
2113 imap_path_separator_subst(real_path, separator);
2118 static gint imap_set_message_flags(IMAPSession *session,
2119 MsgNumberList *numlist,
2127 seq_list = imap_get_lep_set_from_numlist(numlist);
2129 for(cur = seq_list ; cur != NULL ; cur = g_slist_next(cur)) {
2130 struct mailimap_set * imapset;
2132 imapset = cur->data;
2134 ok = imap_cmd_store(session, imapset,
2138 imap_lep_set_free(seq_list);
2140 return IMAP_SUCCESS;
2143 typedef struct _select_data {
2144 IMAPSession *session;
2149 guint32 *uid_validity;
2153 static gint imap_select(IMAPSession *session, IMAPFolder *folder,
2155 gint *exists, gint *recent, gint *unseen,
2156 guint32 *uid_validity, gboolean block)
2160 gint exists_, recent_, unseen_;
2161 guint32 uid_validity_;
2163 if (!exists || !recent || !unseen || !uid_validity) {
2164 if (session->mbox && strcmp(session->mbox, path) == 0)
2165 return IMAP_SUCCESS;
2169 uid_validity = &uid_validity_;
2172 g_free(session->mbox);
2173 session->mbox = NULL;
2175 real_path = imap_get_real_path(folder, path);
2177 ok = imap_cmd_select(session, real_path,
2178 exists, recent, unseen, uid_validity, block);
2179 if (ok != IMAP_SUCCESS)
2180 log_warning(_("can't select folder: %s\n"), real_path);
2182 session->mbox = g_strdup(path);
2183 session->folder_content_changed = FALSE;
2190 static gint imap_status(IMAPSession *session, IMAPFolder *folder,
2192 gint *messages, gint *recent,
2193 guint32 *uid_next, guint32 *uid_validity,
2194 gint *unseen, gboolean block)
2198 struct mailimap_mailbox_data_status * data_status;
2202 real_path = imap_get_real_path(folder, path);
2204 r = imap_threaded_status(FOLDER(folder), real_path, &data_status);
2206 if (r != MAILIMAP_NO_ERROR) {
2207 debug_print("status err %d\n", r);
2211 if (data_status->st_info_list == NULL) {
2212 mailimap_mailbox_data_status_free(data_status);
2213 debug_print("status->st_info_list == NULL\n");
2218 for(iter = clist_begin(data_status->st_info_list) ; iter != NULL ;
2219 iter = clist_next(iter)) {
2220 struct mailimap_status_info * info;
2222 info = clist_content(iter);
2223 switch (info->st_att) {
2224 case MAILIMAP_STATUS_ATT_MESSAGES:
2225 * messages = info->st_value;
2226 got_values |= 1 << 0;
2229 case MAILIMAP_STATUS_ATT_RECENT:
2230 * recent = info->st_value;
2231 got_values |= 1 << 1;
2234 case MAILIMAP_STATUS_ATT_UIDNEXT:
2235 * uid_next = info->st_value;
2236 got_values |= 1 << 2;
2239 case MAILIMAP_STATUS_ATT_UIDVALIDITY:
2240 * uid_validity = info->st_value;
2241 got_values |= 1 << 3;
2244 case MAILIMAP_STATUS_ATT_UNSEEN:
2245 * unseen = info->st_value;
2246 got_values |= 1 << 4;
2250 mailimap_mailbox_data_status_free(data_status);
2252 if (got_values != ((1 << 4) + (1 << 3) +
2253 (1 << 2) + (1 << 1) + (1 << 0))) {
2254 debug_print("status: incomplete values received (%d)\n", got_values);
2257 return IMAP_SUCCESS;
2260 static void imap_free_capabilities(IMAPSession *session)
2262 slist_free_strings(session->capability);
2263 g_slist_free(session->capability);
2264 session->capability = NULL;
2267 /* low-level IMAP4rev1 commands */
2270 static gint imap_cmd_authenticate(IMAPSession *session, const gchar *user,
2271 const gchar *pass, IMAPAuthType type)
2278 gchar hexdigest[33];
2282 auth_type = "CRAM-MD5";
2284 imap_gen_send(session, "AUTHENTICATE %s", auth_type);
2285 ok = imap_gen_recv(session, &buf);
2286 if (ok != IMAP_SUCCESS || buf[0] != '+' || buf[1] != ' ') {
2291 challenge = g_malloc(strlen(buf + 2) + 1);
2292 challenge_len = base64_decode(challenge, buf + 2, -1);
2293 challenge[challenge_len] = '\0';
2296 md5_hex_hmac(hexdigest, challenge, challenge_len, pass, strlen(pass));
2299 response = g_strdup_printf("%s %s", user, hexdigest);
2300 response64 = g_malloc((strlen(response) + 3) * 2 + 1);
2301 base64_encode(response64, response, strlen(response));
2304 sock_puts(SESSION(session)->sock, response64);
2305 ok = imap_cmd_ok(session, NULL);
2306 if (ok != IMAP_SUCCESS)
2307 log_warning(_("IMAP4 authentication failed.\n"));
2313 static gint imap_cmd_login(IMAPSession *session,
2314 const gchar *user, const gchar *pass,
2320 log_print("IMAP4> Logging %s to %s using %s\n",
2322 SESSION(session)->server,
2324 r = imap_threaded_login(session->folder, user, pass, type);
2325 if (r != MAILIMAP_NO_ERROR) {
2326 log_error("IMAP4< Error logging in to %s\n",
2327 SESSION(session)->server);
2335 static gint imap_cmd_logout(IMAPSession *session)
2337 imap_threaded_disconnect(session->folder);
2339 return IMAP_SUCCESS;
2342 static gint imap_cmd_noop(IMAPSession *session)
2345 unsigned int exists;
2347 r = imap_threaded_noop(session->folder, &exists);
2348 if (r != MAILIMAP_NO_ERROR) {
2349 debug_print("noop err %d\n", r);
2352 session->exists = exists;
2353 session_set_access_time(SESSION(session));
2355 return IMAP_SUCCESS;
2359 static gint imap_cmd_starttls(IMAPSession *session)
2363 r = imap_threaded_starttls(session->folder);
2364 if (r != MAILIMAP_NO_ERROR) {
2365 debug_print("starttls err %d\n", r);
2368 return IMAP_SUCCESS;
2372 static gint imap_cmd_select(IMAPSession *session, const gchar *folder,
2373 gint *exists, gint *recent, gint *unseen,
2374 guint32 *uid_validity, gboolean block)
2378 r = imap_threaded_select(session->folder, folder,
2379 exists, recent, unseen, uid_validity);
2380 if (r != MAILIMAP_NO_ERROR) {
2381 debug_print("select err %d\n", r);
2384 return IMAP_SUCCESS;
2387 static gint imap_cmd_examine(IMAPSession *session, const gchar *folder,
2388 gint *exists, gint *recent, gint *unseen,
2389 guint32 *uid_validity, gboolean block)
2393 r = imap_threaded_examine(session->folder, folder,
2394 exists, recent, unseen, uid_validity);
2395 if (r != MAILIMAP_NO_ERROR) {
2396 debug_print("examine err %d\n", r);
2400 return IMAP_SUCCESS;
2403 static gint imap_cmd_create(IMAPSession *session, const gchar *folder)
2407 r = imap_threaded_create(session->folder, folder);
2408 if (r != MAILIMAP_NO_ERROR) {
2413 return IMAP_SUCCESS;
2416 static gint imap_cmd_rename(IMAPSession *session, const gchar *old_folder,
2417 const gchar *new_folder)
2421 r = imap_threaded_rename(session->folder, old_folder,
2423 if (r != MAILIMAP_NO_ERROR) {
2428 return IMAP_SUCCESS;
2431 static gint imap_cmd_delete(IMAPSession *session, const gchar *folder)
2436 r = imap_threaded_delete(session->folder, folder);
2437 if (r != MAILIMAP_NO_ERROR) {
2442 return IMAP_SUCCESS;
2445 typedef struct _fetch_data {
2446 IMAPSession *session;
2448 const gchar *filename;
2454 static void *imap_cmd_fetch_thread(void *data)
2456 fetch_data *stuff = (fetch_data *)data;
2457 IMAPSession *session = stuff->session;
2458 guint32 uid = stuff->uid;
2459 const gchar *filename = stuff->filename;
2463 r = imap_threaded_fetch_content(session->folder,
2467 r = imap_threaded_fetch_content(session->folder,
2470 if (r != MAILIMAP_NO_ERROR) {
2471 debug_print("fetch err %d\n", r);
2472 return GINT_TO_POINTER(IMAP_ERROR);
2474 return GINT_TO_POINTER(IMAP_SUCCESS);
2477 static gint imap_cmd_fetch(IMAPSession *session, guint32 uid,
2478 const gchar *filename, gboolean headers,
2481 fetch_data *data = g_new0(fetch_data, 1);
2484 data->session = session;
2486 data->filename = filename;
2487 data->headers = headers;
2490 if (prefs_common.work_offline && !inc_offline_should_override()) {
2494 statusbar_print_all(_("Fetching message..."));
2495 result = GPOINTER_TO_INT(imap_cmd_fetch_thread(data));
2496 statusbar_pop_all();
2502 static gint imap_cmd_append(IMAPSession *session, const gchar *destfolder,
2503 const gchar *file, IMAPFlags flags,
2506 struct mailimap_flag_list * flag_list;
2509 g_return_val_if_fail(file != NULL, IMAP_ERROR);
2511 flag_list = imap_flag_to_lep(flags);
2512 statusbar_print_all(_("Adding messages..."));
2513 r = imap_threaded_append(session->folder, destfolder,
2515 statusbar_pop_all();
2516 if (new_uid != NULL)
2519 if (r != MAILIMAP_NO_ERROR) {
2520 debug_print("append err %d\n", r);
2523 return IMAP_SUCCESS;
2526 static gint imap_cmd_copy(IMAPSession *session, struct mailimap_set * set,
2527 const gchar *destfolder, GRelation *uid_mapping)
2531 g_return_val_if_fail(session != NULL, IMAP_ERROR);
2532 g_return_val_if_fail(set != NULL, IMAP_ERROR);
2533 g_return_val_if_fail(destfolder != NULL, IMAP_ERROR);
2535 statusbar_print_all(_("Copying messages..."));
2536 r = imap_threaded_copy(session->folder, set, destfolder);
2537 statusbar_pop_all();
2538 if (r != MAILIMAP_NO_ERROR) {
2543 return IMAP_SUCCESS;
2546 static gint imap_cmd_store(IMAPSession *session, struct mailimap_set * set,
2547 IMAPFlags flags, int do_add)
2550 struct mailimap_flag_list * flag_list;
2551 struct mailimap_store_att_flags * store_att_flags;
2553 flag_list = imap_flag_to_lep(flags);
2557 mailimap_store_att_flags_new_add_flags_silent(flag_list);
2560 mailimap_store_att_flags_new_remove_flags_silent(flag_list);
2562 r = imap_threaded_store(session->folder, set, store_att_flags);
2563 if (r != MAILIMAP_NO_ERROR) {
2568 return IMAP_SUCCESS;
2571 static gint imap_cmd_expunge(IMAPSession *session)
2575 if (prefs_common.work_offline && !inc_offline_should_override()) {
2579 r = imap_threaded_expunge(session->folder);
2580 if (r != MAILIMAP_NO_ERROR) {
2585 return IMAP_SUCCESS;
2588 static void imap_path_separator_subst(gchar *str, gchar separator)
2591 gboolean in_escape = FALSE;
2593 if (!separator || separator == '/') return;
2595 for (p = str; *p != '\0'; p++) {
2596 if (*p == '/' && !in_escape)
2598 else if (*p == '&' && *(p + 1) != '-' && !in_escape)
2600 else if (*p == '-' && in_escape)
2605 static gchar *imap_modified_utf7_to_utf8(const gchar *mutf7_str)
2607 static iconv_t cd = (iconv_t)-1;
2608 static gboolean iconv_ok = TRUE;
2611 size_t norm_utf7_len;
2613 gchar *to_str, *to_p;
2615 gboolean in_escape = FALSE;
2617 if (!iconv_ok) return g_strdup(mutf7_str);
2619 if (cd == (iconv_t)-1) {
2620 cd = iconv_open(CS_INTERNAL, CS_UTF_7);
2621 if (cd == (iconv_t)-1) {
2622 g_warning("iconv cannot convert UTF-7 to %s\n",
2625 return g_strdup(mutf7_str);
2629 /* modified UTF-7 to normal UTF-7 conversion */
2630 norm_utf7 = g_string_new(NULL);
2632 for (p = mutf7_str; *p != '\0'; p++) {
2633 /* replace: '&' -> '+',
2635 escaped ',' -> '/' */
2636 if (!in_escape && *p == '&') {
2637 if (*(p + 1) != '-') {
2638 g_string_append_c(norm_utf7, '+');
2641 g_string_append_c(norm_utf7, '&');
2644 } else if (in_escape && *p == ',') {
2645 g_string_append_c(norm_utf7, '/');
2646 } else if (in_escape && *p == '-') {
2647 g_string_append_c(norm_utf7, '-');
2650 g_string_append_c(norm_utf7, *p);
2654 norm_utf7_p = norm_utf7->str;
2655 norm_utf7_len = norm_utf7->len;
2656 to_len = strlen(mutf7_str) * 5;
2657 to_p = to_str = g_malloc(to_len + 1);
2659 if (iconv(cd, (ICONV_CONST gchar **)&norm_utf7_p, &norm_utf7_len,
2660 &to_p, &to_len) == -1) {
2661 g_warning(_("iconv cannot convert UTF-7 to %s\n"),
2662 conv_get_locale_charset_str());
2663 g_string_free(norm_utf7, TRUE);
2665 return g_strdup(mutf7_str);
2668 /* second iconv() call for flushing */
2669 iconv(cd, NULL, NULL, &to_p, &to_len);
2670 g_string_free(norm_utf7, TRUE);
2676 static gchar *imap_utf8_to_modified_utf7(const gchar *from)
2678 static iconv_t cd = (iconv_t)-1;
2679 static gboolean iconv_ok = TRUE;
2680 gchar *norm_utf7, *norm_utf7_p;
2681 size_t from_len, norm_utf7_len;
2683 gchar *from_tmp, *to, *p;
2684 gboolean in_escape = FALSE;
2686 if (!iconv_ok) return g_strdup(from);
2688 if (cd == (iconv_t)-1) {
2689 cd = iconv_open(CS_UTF_7, CS_INTERNAL);
2690 if (cd == (iconv_t)-1) {
2691 g_warning(_("iconv cannot convert %s to UTF-7\n"),
2694 return g_strdup(from);
2698 /* UTF-8 to normal UTF-7 conversion */
2699 Xstrdup_a(from_tmp, from, return g_strdup(from));
2700 from_len = strlen(from);
2701 norm_utf7_len = from_len * 5;
2702 Xalloca(norm_utf7, norm_utf7_len + 1, return g_strdup(from));
2703 norm_utf7_p = norm_utf7;
2705 #define IS_PRINT(ch) (isprint(ch) && IS_ASCII(ch))
2707 while (from_len > 0) {
2708 if (*from_tmp == '+') {
2709 *norm_utf7_p++ = '+';
2710 *norm_utf7_p++ = '-';
2714 } else if (IS_PRINT(*(guchar *)from_tmp)) {
2715 /* printable ascii char */
2716 *norm_utf7_p = *from_tmp;
2722 size_t conv_len = 0;
2724 /* unprintable char: convert to UTF-7 */
2726 while (!IS_PRINT(*(guchar *)p) && conv_len < from_len) {
2727 conv_len += g_utf8_skip[*(guchar *)p];
2728 p += g_utf8_skip[*(guchar *)p];
2731 from_len -= conv_len;
2732 if (iconv(cd, (ICONV_CONST gchar **)&from_tmp,
2734 &norm_utf7_p, &norm_utf7_len) == -1) {
2735 g_warning(_("iconv cannot convert UTF-8 to UTF-7\n"));
2736 return g_strdup(from);
2739 /* second iconv() call for flushing */
2740 iconv(cd, NULL, NULL, &norm_utf7_p, &norm_utf7_len);
2746 *norm_utf7_p = '\0';
2747 to_str = g_string_new(NULL);
2748 for (p = norm_utf7; p < norm_utf7_p; p++) {
2749 /* replace: '&' -> "&-",
2752 BASE64 '/' -> ',' */
2753 if (!in_escape && *p == '&') {
2754 g_string_append(to_str, "&-");
2755 } else if (!in_escape && *p == '+') {
2756 if (*(p + 1) == '-') {
2757 g_string_append_c(to_str, '+');
2760 g_string_append_c(to_str, '&');
2763 } else if (in_escape && *p == '/') {
2764 g_string_append_c(to_str, ',');
2765 } else if (in_escape && *p == '-') {
2766 g_string_append_c(to_str, '-');
2769 g_string_append_c(to_str, *p);
2775 g_string_append_c(to_str, '-');
2779 g_string_free(to_str, FALSE);
2784 static gboolean imap_rename_folder_func(GNode *node, gpointer data)
2786 FolderItem *item = node->data;
2787 gchar **paths = data;
2788 const gchar *oldpath = paths[0];
2789 const gchar *newpath = paths[1];
2791 gchar *new_itempath;
2794 oldpathlen = strlen(oldpath);
2795 if (strncmp(oldpath, item->path, oldpathlen) != 0) {
2796 g_warning("path doesn't match: %s, %s\n", oldpath, item->path);
2800 base = item->path + oldpathlen;
2801 while (*base == G_DIR_SEPARATOR) base++;
2803 new_itempath = g_strdup(newpath);
2805 new_itempath = g_strconcat(newpath, G_DIR_SEPARATOR_S, base,
2808 item->path = new_itempath;
2813 typedef struct _get_list_uid_data {
2815 IMAPFolderItem *item;
2816 GSList **msgnum_list;
2818 } get_list_uid_data;
2820 static void *get_list_of_uids_thread(void *data)
2822 get_list_uid_data *stuff = (get_list_uid_data *)data;
2823 Folder *folder = stuff->folder;
2824 IMAPFolderItem *item = stuff->item;
2825 GSList **msgnum_list = stuff->msgnum_list;
2826 gint ok, nummsgs = 0, lastuid_old;
2827 IMAPSession *session;
2828 GSList *uidlist, *elem;
2829 struct mailimap_set * set;
2830 clist * lep_uidlist;
2833 session = imap_session_get(folder);
2834 if (session == NULL) {
2836 return GINT_TO_POINTER(-1);
2839 ok = imap_select(session, IMAP_FOLDER(folder), item->item.path,
2840 NULL, NULL, NULL, NULL, TRUE);
2841 if (ok != IMAP_SUCCESS) {
2843 return GINT_TO_POINTER(-1);
2848 set = mailimap_set_new_interval(item->lastuid + 1, 0);
2849 r = imap_threaded_search(folder, IMAP_SEARCH_TYPE_SIMPLE, set,
2851 if (r == MAILIMAP_NO_ERROR) {
2852 GSList * fetchuid_list;
2855 imap_uid_list_from_lep(lep_uidlist);
2856 uidlist = g_slist_concat(fetchuid_list, uidlist);
2859 GSList * fetchuid_list;
2860 carray * lep_uidtab;
2862 r = imap_threaded_fetch_uid(folder, item->lastuid + 1,
2864 if (r == MAILIMAP_NO_ERROR) {
2866 imap_uid_list_from_lep_tab(lep_uidtab);
2867 uidlist = g_slist_concat(fetchuid_list, uidlist);
2871 lastuid_old = item->lastuid;
2872 *msgnum_list = g_slist_copy(item->uid_list);
2873 nummsgs = g_slist_length(*msgnum_list);
2874 debug_print("Got %d uids from cache\n", g_slist_length(item->uid_list));
2876 for (elem = uidlist; elem != NULL; elem = g_slist_next(elem)) {
2879 msgnum = GPOINTER_TO_INT(elem->data);
2880 if (msgnum > lastuid_old) {
2881 *msgnum_list = g_slist_prepend(*msgnum_list, GINT_TO_POINTER(msgnum));
2882 item->uid_list = g_slist_prepend(item->uid_list, GINT_TO_POINTER(msgnum));
2885 if(msgnum > item->lastuid)
2886 item->lastuid = msgnum;
2889 g_slist_free(uidlist);
2892 return GINT_TO_POINTER(nummsgs);
2895 static gint get_list_of_uids(Folder *folder, IMAPFolderItem *item, GSList **msgnum_list)
2898 get_list_uid_data *data = g_new0(get_list_uid_data, 1);
2900 data->folder = folder;
2902 data->msgnum_list = msgnum_list;
2904 if (prefs_common.work_offline && !inc_offline_should_override()) {
2909 result = GPOINTER_TO_INT(get_list_of_uids_thread(data));
2915 gint imap_get_num_list(Folder *folder, FolderItem *_item, GSList **msgnum_list, gboolean *old_uids_valid)
2917 IMAPFolderItem *item = (IMAPFolderItem *)_item;
2918 IMAPSession *session;
2919 gint ok, nummsgs = 0, exists, recent, uid_val, uid_next, unseen;
2920 GSList *uidlist = NULL;
2922 gboolean selected_folder;
2924 debug_print("get_num_list\n");
2926 g_return_val_if_fail(folder != NULL, -1);
2927 g_return_val_if_fail(item != NULL, -1);
2928 g_return_val_if_fail(item->item.path != NULL, -1);
2929 g_return_val_if_fail(FOLDER_CLASS(folder) == &imap_class, -1);
2930 g_return_val_if_fail(folder->account != NULL, -1);
2932 session = imap_session_get(folder);
2933 g_return_val_if_fail(session != NULL, -1);
2935 statusbar_print_all("Scanning %s...\n", FOLDER_ITEM(item)->path
2936 ? FOLDER_ITEM(item)->path:"");
2938 selected_folder = (session->mbox != NULL) &&
2939 (!strcmp(session->mbox, item->item.path));
2940 if (selected_folder) {
2941 ok = imap_cmd_noop(session);
2942 if (ok != IMAP_SUCCESS) {
2943 debug_print("disconnected!\n");
2944 session = imap_reconnect_if_possible(folder, session);
2945 if (session == NULL) {
2946 statusbar_pop_all();
2950 exists = session->exists;
2952 *old_uids_valid = TRUE;
2954 ok = imap_status(session, IMAP_FOLDER(folder), item->item.path,
2955 &exists, &recent, &uid_next, &uid_val, &unseen, FALSE);
2956 if (ok != IMAP_SUCCESS) {
2957 statusbar_pop_all();
2960 if(item->item.mtime == uid_val)
2961 *old_uids_valid = TRUE;
2963 *old_uids_valid = FALSE;
2965 debug_print("Freeing imap uid cache\n");
2967 g_slist_free(item->uid_list);
2968 item->uid_list = NULL;
2970 item->item.mtime = uid_val;
2972 imap_delete_all_cached_messages((FolderItem *)item);
2976 if (!selected_folder)
2977 item->uid_next = uid_next;
2979 /* If old uid_next matches new uid_next we can be sure no message
2980 was added to the folder */
2981 if (( selected_folder && !session->folder_content_changed) ||
2982 (!selected_folder && uid_next == item->uid_next)) {
2983 nummsgs = g_slist_length(item->uid_list);
2985 /* If number of messages is still the same we
2986 know our caches message numbers are still valid,
2987 otherwise if the number of messages has decrease
2988 we discard our cache to start a new scan to find
2989 out which numbers have been removed */
2990 if (exists == nummsgs) {
2991 *msgnum_list = g_slist_copy(item->uid_list);
2992 statusbar_pop_all();
2994 } else if (exists < nummsgs) {
2995 debug_print("Freeing imap uid cache");
2997 g_slist_free(item->uid_list);
2998 item->uid_list = NULL;
3003 *msgnum_list = NULL;
3004 statusbar_pop_all();
3008 nummsgs = get_list_of_uids(folder, item, &uidlist);
3011 statusbar_pop_all();
3015 if (nummsgs != exists) {
3016 /* Cache contains more messages then folder, we have cached
3017 an old UID of a message that was removed and new messages
3018 have been added too, otherwise the uid_next check would
3020 debug_print("Freeing imap uid cache");
3022 g_slist_free(item->uid_list);
3023 item->uid_list = NULL;
3025 g_slist_free(*msgnum_list);
3027 nummsgs = get_list_of_uids(folder, item, &uidlist);
3030 *msgnum_list = uidlist;
3032 dir = folder_item_get_path((FolderItem *)item);
3033 debug_print("removing old messages from %s\n", dir);
3034 remove_numbered_files_not_in_list(dir, *msgnum_list);
3037 debug_print("get_num_list - ok - %i\n", nummsgs);
3038 statusbar_pop_all();
3042 static MsgInfo *imap_parse_msg(const gchar *file, FolderItem *item)
3047 flags.perm_flags = MSG_NEW|MSG_UNREAD;
3048 flags.tmp_flags = 0;
3050 g_return_val_if_fail(item != NULL, NULL);
3051 g_return_val_if_fail(file != NULL, NULL);
3053 if (folder_has_parent_of_type(item, F_QUEUE)) {
3054 MSG_SET_TMP_FLAGS(flags, MSG_QUEUED);
3055 } else if (folder_has_parent_of_type(item, F_DRAFT)) {
3056 MSG_SET_TMP_FLAGS(flags, MSG_DRAFT);
3059 msginfo = procheader_parse_file(file, flags, FALSE, FALSE);
3060 if (!msginfo) return NULL;
3062 msginfo->plaintext_file = g_strdup(file);
3063 msginfo->folder = item;
3068 GSList *imap_get_msginfos(Folder *folder, FolderItem *item,
3069 GSList *msgnum_list)
3071 IMAPSession *session;
3072 MsgInfoList *ret = NULL;
3075 debug_print("get_msginfos\n");
3077 g_return_val_if_fail(folder != NULL, NULL);
3078 g_return_val_if_fail(item != NULL, NULL);
3079 g_return_val_if_fail(msgnum_list != NULL, NULL);
3081 session = imap_session_get(folder);
3082 g_return_val_if_fail(session != NULL, NULL);
3084 debug_print("IMAP getting msginfos\n");
3085 ok = imap_select(session, IMAP_FOLDER(folder), item->path,
3086 NULL, NULL, NULL, NULL, FALSE);
3087 if (ok != IMAP_SUCCESS)
3090 if (!(folder_has_parent_of_type(item, F_DRAFT) ||
3091 folder_has_parent_of_type(item, F_QUEUE))) {
3092 ret = g_slist_concat(ret,
3093 imap_get_uncached_messages(session, item,
3096 MsgNumberList *sorted_list, *elem;
3097 gint startnum, lastnum;
3099 sorted_list = g_slist_sort(g_slist_copy(msgnum_list), g_int_compare);
3101 startnum = lastnum = GPOINTER_TO_INT(sorted_list->data);
3103 for (elem = sorted_list;; elem = g_slist_next(elem)) {
3107 num = GPOINTER_TO_INT(elem->data);
3109 if (num > lastnum + 1 || elem == NULL) {
3111 for (i = startnum; i <= lastnum; ++i) {
3114 file = imap_fetch_msg(folder, item, i);
3116 MsgInfo *msginfo = imap_parse_msg(file, item);
3117 if (msginfo != NULL) {
3118 msginfo->msgnum = i;
3119 ret = g_slist_append(ret, msginfo);
3133 g_slist_free(sorted_list);
3139 MsgInfo *imap_get_msginfo(Folder *folder, FolderItem *item, gint uid)
3141 MsgInfo *msginfo = NULL;
3142 MsgInfoList *msginfolist;
3143 MsgNumberList numlist;
3145 numlist.next = NULL;
3146 numlist.data = GINT_TO_POINTER(uid);
3148 msginfolist = imap_get_msginfos(folder, item, &numlist);
3149 if (msginfolist != NULL) {
3150 msginfo = msginfolist->data;
3151 g_slist_free(msginfolist);
3157 gboolean imap_scan_required(Folder *folder, FolderItem *_item)
3159 IMAPSession *session;
3160 IMAPFolderItem *item = (IMAPFolderItem *)_item;
3161 gint ok, exists = 0, recent = 0, unseen = 0;
3162 guint32 uid_next, uid_val = 0;
3163 gboolean selected_folder;
3165 g_return_val_if_fail(folder != NULL, FALSE);
3166 g_return_val_if_fail(item != NULL, FALSE);
3167 g_return_val_if_fail(item->item.folder != NULL, FALSE);
3168 g_return_val_if_fail(FOLDER_CLASS(item->item.folder) == &imap_class, FALSE);
3170 if (item->item.path == NULL)
3173 session = imap_session_get(folder);
3174 g_return_val_if_fail(session != NULL, FALSE);
3176 selected_folder = (session->mbox != NULL) &&
3177 (!strcmp(session->mbox, item->item.path));
3178 if (selected_folder) {
3179 ok = imap_cmd_noop(session);
3180 if (ok != IMAP_SUCCESS) {
3181 debug_print("disconnected!\n");
3182 session = imap_reconnect_if_possible(folder, session);
3183 if (session == NULL)
3187 if (session->folder_content_changed
3188 || session->exists != item->item.total_msgs)
3191 ok = imap_status(session, IMAP_FOLDER(folder), item->item.path,
3192 &exists, &recent, &uid_next, &uid_val, &unseen, FALSE);
3193 if (ok != IMAP_SUCCESS)
3196 if ((uid_next != item->uid_next) || (exists != item->item.total_msgs))
3203 void imap_change_flags(Folder *folder, FolderItem *item, MsgInfo *msginfo, MsgPermFlags newflags)
3205 IMAPSession *session;
3206 IMAPFlags flags_set = 0, flags_unset = 0;
3207 gint ok = IMAP_SUCCESS;
3208 MsgNumberList numlist;
3209 hashtable_data *ht_data = NULL;
3211 g_return_if_fail(folder != NULL);
3212 g_return_if_fail(folder->klass == &imap_class);
3213 g_return_if_fail(item != NULL);
3214 g_return_if_fail(item->folder == folder);
3215 g_return_if_fail(msginfo != NULL);
3216 g_return_if_fail(msginfo->folder == item);
3218 session = imap_session_get(folder);
3222 if ((ok = imap_select(session, IMAP_FOLDER(folder), msginfo->folder->path,
3223 NULL, NULL, NULL, NULL, FALSE)) != IMAP_SUCCESS) {
3227 if (!MSG_IS_MARKED(msginfo->flags) && (newflags & MSG_MARKED))
3228 flags_set |= IMAP_FLAG_FLAGGED;
3229 if ( MSG_IS_MARKED(msginfo->flags) && !(newflags & MSG_MARKED))
3230 flags_unset |= IMAP_FLAG_FLAGGED;
3232 if (!MSG_IS_UNREAD(msginfo->flags) && (newflags & MSG_UNREAD))
3233 flags_unset |= IMAP_FLAG_SEEN;
3234 if ( MSG_IS_UNREAD(msginfo->flags) && !(newflags & MSG_UNREAD))
3235 flags_set |= IMAP_FLAG_SEEN;
3237 if (!MSG_IS_REPLIED(msginfo->flags) && (newflags & MSG_REPLIED))
3238 flags_set |= IMAP_FLAG_ANSWERED;
3239 if ( MSG_IS_REPLIED(msginfo->flags) && !(newflags & MSG_REPLIED))
3240 flags_unset |= IMAP_FLAG_ANSWERED;
3242 if (!MSG_IS_DELETED(msginfo->flags) && (newflags & MSG_DELETED))
3243 flags_set |= IMAP_FLAG_DELETED;
3244 if ( MSG_IS_DELETED(msginfo->flags) && !(newflags & MSG_DELETED))
3245 flags_unset |= IMAP_FLAG_DELETED;
3247 numlist.next = NULL;
3248 numlist.data = GINT_TO_POINTER(msginfo->msgnum);
3250 if (IMAP_FOLDER_ITEM(item)->batching) {
3251 /* instead of performing an UID STORE command for each message change,
3252 * as a lot of them can change "together", we just fill in hashtables
3253 * and defer the treatment so that we're able to send only one
3256 debug_print("IMAP batch mode on, deferring flags change\n");
3258 ht_data = g_hash_table_lookup(flags_set_table, GINT_TO_POINTER(flags_set));
3259 if (ht_data == NULL) {
3260 ht_data = g_new0(hashtable_data, 1);
3261 ht_data->session = session;
3262 g_hash_table_insert(flags_set_table, GINT_TO_POINTER(flags_set), ht_data);
3264 if (!g_slist_find(ht_data->msglist, GINT_TO_POINTER(msginfo->msgnum)))
3265 ht_data->msglist = g_slist_prepend(ht_data->msglist, GINT_TO_POINTER(msginfo->msgnum));
3268 ht_data = g_hash_table_lookup(flags_unset_table, GINT_TO_POINTER(flags_unset));
3269 if (ht_data == NULL) {
3270 ht_data = g_new0(hashtable_data, 1);
3271 ht_data->session = session;
3272 g_hash_table_insert(flags_unset_table, GINT_TO_POINTER(flags_unset), ht_data);
3274 if (!g_slist_find(ht_data->msglist, GINT_TO_POINTER(msginfo->msgnum)))
3275 ht_data->msglist = g_slist_prepend(ht_data->msglist, GINT_TO_POINTER(msginfo->msgnum));
3278 debug_print("IMAP changing flags\n");
3280 ok = imap_set_message_flags(session, &numlist, flags_set, TRUE);
3281 if (ok != IMAP_SUCCESS) {
3287 ok = imap_set_message_flags(session, &numlist, flags_unset, FALSE);
3288 if (ok != IMAP_SUCCESS) {
3293 msginfo->flags.perm_flags = newflags;
3298 static gint imap_remove_msg(Folder *folder, FolderItem *item, gint uid)
3301 IMAPSession *session;
3303 MsgNumberList numlist;
3305 g_return_val_if_fail(folder != NULL, -1);
3306 g_return_val_if_fail(FOLDER_CLASS(folder) == &imap_class, -1);
3307 g_return_val_if_fail(item != NULL, -1);
3309 session = imap_session_get(folder);
3310 if (!session) return -1;
3312 ok = imap_select(session, IMAP_FOLDER(folder), item->path,
3313 NULL, NULL, NULL, NULL, FALSE);
3314 if (ok != IMAP_SUCCESS)
3317 numlist.next = NULL;
3318 numlist.data = GINT_TO_POINTER(uid);
3320 ok = imap_set_message_flags
3321 (IMAP_SESSION(REMOTE_FOLDER(folder)->session),
3322 &numlist, IMAP_FLAG_DELETED, TRUE);
3323 if (ok != IMAP_SUCCESS) {
3324 log_warning(_("can't set deleted flags: %d\n"), uid);
3328 if (!session->uidplus) {
3329 ok = imap_cmd_expunge(session);
3333 uidstr = g_strdup_printf("%u", uid);
3334 ok = imap_cmd_expunge(session);
3337 if (ok != IMAP_SUCCESS) {
3338 log_warning(_("can't expunge\n"));
3342 IMAP_FOLDER_ITEM(item)->uid_list = g_slist_remove(
3343 IMAP_FOLDER_ITEM(item)->uid_list, numlist.data);
3344 dir = folder_item_get_path(item);
3345 if (is_dir_exist(dir))
3346 remove_numbered_files(dir, uid, uid);
3349 return IMAP_SUCCESS;
3352 static gint compare_msginfo(gconstpointer a, gconstpointer b)
3354 return ((MsgInfo *)a)->msgnum - ((MsgInfo *)b)->msgnum;
3357 static guint gslist_find_next_num(MsgNumberList **list, guint num)
3361 g_return_val_if_fail(list != NULL, -1);
3363 for (elem = *list; elem != NULL; elem = g_slist_next(elem))
3364 if (GPOINTER_TO_INT(elem->data) >= num)
3367 return elem != NULL ? GPOINTER_TO_INT(elem->data) : (gint)-1;
3371 * NEW and DELETED flags are not syncronized
3372 * - The NEW/RECENT flags in IMAP folders can not really be directly
3373 * modified by Sylpheed
3374 * - The DELETE/DELETED flag in IMAP and Sylpheed don't have the same
3375 * meaning, in IMAP it always removes the messages from the FolderItem
3376 * in Sylpheed it can mean to move the message to trash
3379 typedef struct _get_flags_data {
3382 MsgInfoList *msginfo_list;
3383 GRelation *msgflags;
3387 static /*gint*/ void *imap_get_flags_thread(void *data)
3389 get_flags_data *stuff = (get_flags_data *)data;
3390 Folder *folder = stuff->folder;
3391 FolderItem *item = stuff->item;
3392 MsgInfoList *msginfo_list = stuff->msginfo_list;
3393 GRelation *msgflags = stuff->msgflags;
3394 IMAPSession *session;
3395 GSList *sorted_list;
3396 GSList *unseen = NULL, *answered = NULL, *flagged = NULL, *deleted = NULL;
3397 GSList *p_unseen, *p_answered, *p_flagged, *p_deleted;
3399 GSList *seq_list, *cur;
3400 gboolean reverse_seen = FALSE;
3403 gint exists_cnt, recent_cnt, unseen_cnt, uid_next;
3404 guint32 uidvalidity;
3405 gboolean selected_folder;
3407 if (folder == NULL || item == NULL) {
3409 return GINT_TO_POINTER(-1);
3411 if (msginfo_list == NULL) {
3413 return GINT_TO_POINTER(0);
3416 session = imap_session_get(folder);
3417 if (session == NULL) {
3419 return GINT_TO_POINTER(-1);
3422 selected_folder = (session->mbox != NULL) &&
3423 (!strcmp(session->mbox, item->path));
3425 if (!selected_folder) {
3426 ok = imap_status(session, IMAP_FOLDER(folder), item->path,
3427 &exists_cnt, &recent_cnt, &uid_next, &uidvalidity, &unseen_cnt, TRUE);
3428 ok = imap_select(session, IMAP_FOLDER(folder), item->path,
3429 NULL, NULL, NULL, NULL, TRUE);
3430 if (ok != IMAP_SUCCESS) {
3432 return GINT_TO_POINTER(-1);
3435 if (unseen_cnt > exists_cnt / 2)
3436 reverse_seen = TRUE;
3439 if (item->unread_msgs > item->total_msgs / 2)
3440 reverse_seen = TRUE;
3443 cmd_buf = g_string_new(NULL);
3445 sorted_list = g_slist_sort(g_slist_copy(msginfo_list), compare_msginfo);
3447 seq_list = imap_get_lep_set_from_msglist(msginfo_list);
3449 for (cur = seq_list; cur != NULL; cur = g_slist_next(cur)) {
3450 struct mailimap_set * imapset;
3451 clist * lep_uidlist;
3454 imapset = cur->data;
3456 r = imap_threaded_search(folder, IMAP_SEARCH_TYPE_SEEN,
3457 imapset, &lep_uidlist);
3460 r = imap_threaded_search(folder,
3461 IMAP_SEARCH_TYPE_UNSEEN,
3462 imapset, &lep_uidlist);
3464 if (r == MAILIMAP_NO_ERROR) {
3467 uidlist = imap_uid_list_from_lep(lep_uidlist);
3468 mailimap_search_result_free(lep_uidlist);
3470 unseen = g_slist_concat(unseen, uidlist);
3473 r = imap_threaded_search(folder, IMAP_SEARCH_TYPE_ANSWERED,
3474 imapset, &lep_uidlist);
3475 if (r == MAILIMAP_NO_ERROR) {
3478 uidlist = imap_uid_list_from_lep(lep_uidlist);
3479 mailimap_search_result_free(lep_uidlist);
3481 answered = g_slist_concat(answered, uidlist);
3484 r = imap_threaded_search(folder, IMAP_SEARCH_TYPE_FLAGGED,
3485 imapset, &lep_uidlist);
3486 if (r == MAILIMAP_NO_ERROR) {
3489 uidlist = imap_uid_list_from_lep(lep_uidlist);
3490 mailimap_search_result_free(lep_uidlist);
3492 flagged = g_slist_concat(flagged, uidlist);
3495 r = imap_threaded_search(folder, IMAP_SEARCH_TYPE_DELETED,
3496 imapset, &lep_uidlist);
3497 if (r == MAILIMAP_NO_ERROR) {
3500 uidlist = imap_uid_list_from_lep(lep_uidlist);
3501 mailimap_search_result_free(lep_uidlist);
3503 deleted = g_slist_concat(deleted, uidlist);
3508 p_answered = answered;
3509 p_flagged = flagged;
3510 p_deleted = deleted;
3512 for (elem = sorted_list; elem != NULL; elem = g_slist_next(elem)) {
3517 msginfo = (MsgInfo *) elem->data;
3518 flags = msginfo->flags.perm_flags;
3519 wasnew = (flags & MSG_NEW);
3520 flags &= ~((reverse_seen ? 0 : MSG_UNREAD | MSG_NEW) | MSG_REPLIED | MSG_MARKED);
3522 flags |= MSG_UNREAD | (wasnew ? MSG_NEW : 0);
3523 if (gslist_find_next_num(&p_unseen, msginfo->msgnum) == msginfo->msgnum) {
3524 if (!reverse_seen) {
3525 flags |= MSG_UNREAD | (wasnew ? MSG_NEW : 0);
3527 flags &= ~(MSG_UNREAD | MSG_NEW);
3530 if (gslist_find_next_num(&p_answered, msginfo->msgnum) == msginfo->msgnum)
3531 flags |= MSG_REPLIED;
3533 flags &= ~MSG_REPLIED;
3534 if (gslist_find_next_num(&p_flagged, msginfo->msgnum) == msginfo->msgnum)
3535 flags |= MSG_MARKED;
3537 flags &= ~MSG_MARKED;
3538 if (gslist_find_next_num(&p_deleted, msginfo->msgnum) == msginfo->msgnum)
3539 flags |= MSG_DELETED;
3541 flags &= ~MSG_DELETED;
3542 g_relation_insert(msgflags, msginfo, GINT_TO_POINTER(flags));
3545 imap_lep_set_free(seq_list);
3546 g_slist_free(flagged);
3547 g_slist_free(deleted);
3548 g_slist_free(answered);
3549 g_slist_free(unseen);
3550 g_slist_free(sorted_list);
3551 g_string_free(cmd_buf, TRUE);
3554 return GINT_TO_POINTER(0);
3557 static gint imap_get_flags(Folder *folder, FolderItem *item,
3558 MsgInfoList *msginfo_list, GRelation *msgflags)
3561 get_flags_data *data = g_new0(get_flags_data, 1);
3563 data->folder = folder;
3565 data->msginfo_list = msginfo_list;
3566 data->msgflags = msgflags;
3568 if (prefs_common.work_offline && !inc_offline_should_override()) {
3573 result = GPOINTER_TO_INT(imap_get_flags_thread(data));
3580 static gboolean process_flags(gpointer key, gpointer value, gpointer user_data)
3582 gboolean flags_set = GPOINTER_TO_INT(user_data);
3583 gint flags_value = GPOINTER_TO_INT(key);
3584 hashtable_data *data = (hashtable_data *)value;
3586 data->msglist = g_slist_reverse(data->msglist);
3588 debug_print("IMAP %ssetting flags to %d for %d messages\n",
3591 g_slist_length(data->msglist));
3592 imap_set_message_flags(data->session, data->msglist, flags_value, flags_set);
3594 g_slist_free(data->msglist);
3599 static void process_hashtable(void)
3601 if (flags_set_table) {
3602 g_hash_table_foreach_remove(flags_set_table, process_flags, GINT_TO_POINTER(TRUE));
3603 g_free(flags_set_table);
3604 flags_set_table = NULL;
3606 if (flags_unset_table) {
3607 g_hash_table_foreach_remove(flags_unset_table, process_flags, GINT_TO_POINTER(FALSE));
3608 g_free(flags_unset_table);
3609 flags_unset_table = NULL;
3613 static IMAPFolderItem *batching_item = NULL;
3615 static void imap_set_batch (Folder *folder, FolderItem *_item, gboolean batch)
3617 IMAPFolderItem *item = (IMAPFolderItem *)_item;
3619 g_return_if_fail(item != NULL);
3621 if (batch && batching_item != NULL) {
3622 g_warning("already batching on %s\n", batching_item->item.path);
3626 if (item->batching == batch)
3629 item->batching = batch;
3631 batching_item = batch?item:NULL;
3634 debug_print("IMAP switching to batch mode\n");
3635 if (flags_set_table) {
3636 g_warning("flags_set_table non-null but we just entered batch mode!\n");
3637 flags_set_table = NULL;
3639 if (flags_unset_table) {
3640 g_warning("flags_unset_table non-null but we just entered batch mode!\n");
3641 flags_unset_table = NULL;
3643 flags_set_table = g_hash_table_new(NULL, g_direct_equal);
3644 flags_unset_table = g_hash_table_new(NULL, g_direct_equal);
3646 debug_print("IMAP switching away from batch mode\n");
3648 process_hashtable();
3654 /* data types conversion libetpan <-> sylpheed */
3658 #define ETPAN_IMAP_MB_MARKED 1
3659 #define ETPAN_IMAP_MB_UNMARKED 2
3660 #define ETPAN_IMAP_MB_NOSELECT 4
3661 #define ETPAN_IMAP_MB_NOINFERIORS 8
3663 static int imap_flags_to_flags(struct mailimap_mbx_list_flags * imap_flags)
3669 if (imap_flags->mbf_type == MAILIMAP_MBX_LIST_FLAGS_SFLAG) {
3670 switch (imap_flags->mbf_sflag) {
3671 case MAILIMAP_MBX_LIST_SFLAG_MARKED:
3672 flags |= ETPAN_IMAP_MB_MARKED;
3674 case MAILIMAP_MBX_LIST_SFLAG_NOSELECT:
3675 flags |= ETPAN_IMAP_MB_NOSELECT;
3677 case MAILIMAP_MBX_LIST_SFLAG_UNMARKED:
3678 flags |= ETPAN_IMAP_MB_UNMARKED;
3683 for(cur = clist_begin(imap_flags->mbf_oflags) ; cur != NULL ;
3684 cur = clist_next(cur)) {
3685 struct mailimap_mbx_list_oflag * oflag;
3687 oflag = clist_content(cur);
3689 switch (oflag->of_type) {
3690 case MAILIMAP_MBX_LIST_OFLAG_NOINFERIORS:
3691 flags |= ETPAN_IMAP_MB_NOINFERIORS;
3699 static GSList * imap_list_from_lep(IMAPFolder * folder,
3700 clist * list, const gchar * real_path)
3707 for(iter = clist_begin(list) ; iter != NULL ;
3708 iter = clist_next(iter)) {
3709 struct mailimap_mailbox_list * mb;
3717 FolderItem *new_item;
3719 mb = clist_content(iter);
3722 if (mb->mb_flag != NULL)
3723 flags = imap_flags_to_flags(mb->mb_flag);
3725 delimiter = mb->mb_delimiter;
3728 dup_name = strdup(name);
3729 if (delimiter != '\0')
3730 subst_char(dup_name, delimiter, '/');
3732 base = g_path_get_basename(dup_name);
3733 if (base[0] == '.') {
3739 if (strcmp(dup_name, real_path) == 0) {
3745 if (dup_name[strlen(dup_name)-1] == '/') {
3751 loc_name = imap_modified_utf7_to_utf8(base);
3752 loc_path = imap_modified_utf7_to_utf8(dup_name);
3754 new_item = folder_item_new(FOLDER(folder), loc_name, loc_path);
3755 if ((flags & ETPAN_IMAP_MB_NOINFERIORS) != 0)
3756 new_item->no_sub = TRUE;
3757 if (strcmp(dup_name, "INBOX") != 0 &&
3758 ((flags & ETPAN_IMAP_MB_NOSELECT) != 0))
3759 new_item->no_select = TRUE;
3761 item_list = g_slist_append(item_list, new_item);
3763 debug_print("folder '%s' found.\n", loc_path);
3774 static GSList * imap_get_lep_set_from_numlist(MsgNumberList *numlist)
3776 GSList *sorted_list, *cur;
3777 guint first, last, next;
3778 GSList *ret_list = NULL;
3780 struct mailimap_set * current_set;
3781 unsigned int item_count;
3783 if (numlist == NULL)
3787 current_set = mailimap_set_new_empty();
3789 sorted_list = g_slist_copy(numlist);
3790 sorted_list = g_slist_sort(sorted_list, g_int_compare);
3792 first = GPOINTER_TO_INT(sorted_list->data);
3795 for (cur = sorted_list; cur != NULL; cur = g_slist_next(cur)) {
3796 if (GPOINTER_TO_INT(cur->data) == 0)
3801 last = GPOINTER_TO_INT(cur->data);
3803 next = GPOINTER_TO_INT(cur->next->data);
3807 if (last + 1 != next || next == 0) {
3809 struct mailimap_set_item * item;
3810 item = mailimap_set_item_new(first, last);
3811 mailimap_set_add(current_set, item);
3816 if (count >= IMAP_SET_MAX_COUNT) {
3817 ret_list = g_slist_append(ret_list,
3819 current_set = mailimap_set_new_empty();
3826 if (clist_count(current_set->set_list) > 0) {
3827 ret_list = g_slist_append(ret_list,
3831 g_slist_free(sorted_list);
3836 static GSList * imap_get_lep_set_from_msglist(MsgInfoList *msglist)
3838 MsgNumberList *numlist = NULL;
3842 for (cur = msglist; cur != NULL; cur = g_slist_next(cur)) {
3843 MsgInfo *msginfo = (MsgInfo *) cur->data;
3845 numlist = g_slist_append(numlist, GINT_TO_POINTER(msginfo->msgnum));
3847 seq_list = imap_get_lep_set_from_numlist(numlist);
3848 g_slist_free(numlist);
3853 static GSList * imap_uid_list_from_lep(clist * list)
3860 for(iter = clist_begin(list) ; iter != NULL ;
3861 iter = clist_next(iter)) {
3864 puid = clist_content(iter);
3865 result = g_slist_append(result, GINT_TO_POINTER(* puid));
3871 static GSList * imap_uid_list_from_lep_tab(carray * list)
3878 for(i = 0 ; i < carray_count(list) ; i ++) {
3881 puid = carray_get(list, i);
3882 result = g_slist_append(result, GINT_TO_POINTER(* puid));
3888 static MsgInfo *imap_envelope_from_lep(struct imap_fetch_env_info * info,
3891 MsgInfo *msginfo = NULL;
3894 MsgFlags flags = {0, 0};
3896 MSG_SET_TMP_FLAGS(flags, MSG_IMAP);
3897 if (folder_has_parent_of_type(item, F_QUEUE)) {
3898 MSG_SET_TMP_FLAGS(flags, MSG_QUEUED);
3899 } else if (folder_has_parent_of_type(item, F_DRAFT)) {
3900 MSG_SET_TMP_FLAGS(flags, MSG_DRAFT);
3902 flags.perm_flags = info->flags;
3906 msginfo = procheader_parse_str(info->headers, flags, FALSE, FALSE);
3909 msginfo->msgnum = uid;
3910 msginfo->size = size;
3916 static void imap_lep_set_free(GSList *seq_list)
3920 for(cur = seq_list ; cur != NULL ; cur = g_slist_next(cur)) {
3921 struct mailimap_set * imapset;
3923 imapset = cur->data;
3924 mailimap_set_free(imapset);
3926 g_slist_free(seq_list);
3929 static struct mailimap_flag_list * imap_flag_to_lep(IMAPFlags flags)
3931 struct mailimap_flag_list * flag_list;
3933 flag_list = mailimap_flag_list_new_empty();
3935 if (IMAP_IS_SEEN(flags))
3936 mailimap_flag_list_add(flag_list,
3937 mailimap_flag_new_seen());
3938 if (IMAP_IS_ANSWERED(flags))
3939 mailimap_flag_list_add(flag_list,
3940 mailimap_flag_new_answered());
3941 if (IMAP_IS_FLAGGED(flags))
3942 mailimap_flag_list_add(flag_list,
3943 mailimap_flag_new_flagged());
3944 if (IMAP_IS_DELETED(flags))
3945 mailimap_flag_list_add(flag_list,
3946 mailimap_flag_new_deleted());
3947 if (IMAP_IS_DRAFT(flags))
3948 mailimap_flag_list_add(flag_list,
3949 mailimap_flag_new_draft());
3954 guint imap_folder_get_refcnt(Folder *folder)
3956 return ((IMAPFolder *)folder)->refcnt;
3959 void imap_folder_ref(Folder *folder)
3961 ((IMAPFolder *)folder)->refcnt++;
3964 void imap_folder_unref(Folder *folder)
3966 if (((IMAPFolder *)folder)->refcnt > 0)
3967 ((IMAPFolder *)folder)->refcnt--;
3970 #else /* HAVE_LIBETPAN */
3972 static FolderClass imap_class;
3974 static Folder *imap_folder_new (const gchar *name,
3979 static gint imap_create_tree (Folder *folder)
3983 static FolderItem *imap_create_folder (Folder *folder,
3989 static gint imap_rename_folder (Folder *folder,
3996 FolderClass *imap_get_class(void)
3998 if (imap_class.idstr == NULL) {
3999 imap_class.type = F_IMAP;
4000 imap_class.idstr = "imap";
4001 imap_class.uistr = "IMAP4";
4003 imap_class.new_folder = imap_folder_new;
4004 imap_class.create_tree = imap_create_tree;
4005 imap_class.create_folder = imap_create_folder;
4006 imap_class.rename_folder = imap_rename_folder;
4007 /* nothing implemented */
4014 void imap_synchronise(FolderItem *item)
4016 imap_gtk_synchronise(item);