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
163 guint32 c_uid_validity;
167 static void imap_folder_init (Folder *folder,
171 static Folder *imap_folder_new (const gchar *name,
173 static void imap_folder_destroy (Folder *folder);
175 static IMAPSession *imap_session_new (Folder *folder,
176 const PrefsAccount *account);
177 static void imap_session_authenticate(IMAPSession *session,
178 const PrefsAccount *account);
179 static void imap_session_destroy (Session *session);
181 static gchar *imap_fetch_msg (Folder *folder,
184 static gchar *imap_fetch_msg_full (Folder *folder,
189 static gint imap_add_msg (Folder *folder,
193 static gint imap_add_msgs (Folder *folder,
196 GRelation *relation);
198 static gint imap_copy_msg (Folder *folder,
201 static gint imap_copy_msgs (Folder *folder,
203 MsgInfoList *msglist,
204 GRelation *relation);
206 static gint imap_remove_msg (Folder *folder,
209 static gint imap_remove_msgs (Folder *folder,
211 MsgInfoList *msglist,
212 GRelation *relation);
213 static gint imap_remove_all_msg (Folder *folder,
216 static gboolean imap_is_msg_changed (Folder *folder,
220 static gint imap_close (Folder *folder,
223 static gint imap_scan_tree (Folder *folder);
225 static gint imap_create_tree (Folder *folder);
227 static FolderItem *imap_create_folder (Folder *folder,
230 static gint imap_rename_folder (Folder *folder,
233 static gint imap_remove_folder (Folder *folder,
236 static FolderItem *imap_folder_item_new (Folder *folder);
237 static void imap_folder_item_destroy (Folder *folder,
240 static IMAPSession *imap_session_get (Folder *folder);
242 static gint imap_auth (IMAPSession *session,
247 static gint imap_scan_tree_recursive (IMAPSession *session,
250 static void imap_create_missing_folders (Folder *folder);
251 static FolderItem *imap_create_special_folder
253 SpecialFolderItemType stype,
256 static gint imap_do_copy_msgs (Folder *folder,
258 MsgInfoList *msglist,
259 GRelation *relation);
261 static void imap_delete_all_cached_messages (FolderItem *item);
262 static void imap_set_batch (Folder *folder,
265 static gint imap_set_message_flags (IMAPSession *session,
266 MsgNumberList *numlist,
269 static gint imap_select (IMAPSession *session,
275 guint32 *uid_validity,
277 static gint imap_status (IMAPSession *session,
280 IMAPFolderItem *item,
283 guint32 *uid_validity,
287 static IMAPNameSpace *imap_find_namespace (IMAPFolder *folder,
289 static gchar imap_get_path_separator (IMAPFolder *folder,
291 static gchar *imap_get_real_path (IMAPFolder *folder,
293 static void imap_synchronise (FolderItem *item);
295 static void imap_free_capabilities (IMAPSession *session);
297 /* low-level IMAP4rev1 commands */
298 static gint imap_cmd_login (IMAPSession *session,
302 static gint imap_cmd_logout (IMAPSession *session);
303 static gint imap_cmd_noop (IMAPSession *session);
305 static gint imap_cmd_starttls (IMAPSession *session);
307 static gint imap_cmd_select (IMAPSession *session,
312 guint32 *uid_validity,
314 static gint imap_cmd_examine (IMAPSession *session,
319 guint32 *uid_validity,
321 static gint imap_cmd_create (IMAPSession *sock,
322 const gchar *folder);
323 static gint imap_cmd_rename (IMAPSession *sock,
324 const gchar *oldfolder,
325 const gchar *newfolder);
326 static gint imap_cmd_delete (IMAPSession *session,
327 const gchar *folder);
328 static gint imap_cmd_fetch (IMAPSession *sock,
330 const gchar *filename,
333 static gint imap_cmd_append (IMAPSession *session,
334 const gchar *destfolder,
338 static gint imap_cmd_copy (IMAPSession *session,
339 struct mailimap_set * set,
340 const gchar *destfolder,
341 GRelation *uid_mapping);
342 static gint imap_cmd_store (IMAPSession *session,
343 struct mailimap_set * set,
346 static gint imap_cmd_expunge (IMAPSession *session);
348 static void imap_path_separator_subst (gchar *str,
351 static gchar *imap_utf8_to_modified_utf7 (const gchar *from);
352 static gchar *imap_modified_utf7_to_utf8 (const gchar *mutf7_str);
354 static gboolean imap_rename_folder_func (GNode *node,
356 static gint imap_get_num_list (Folder *folder,
359 gboolean *old_uids_valid);
360 static GSList *imap_get_msginfos (Folder *folder,
362 GSList *msgnum_list);
363 static MsgInfo *imap_get_msginfo (Folder *folder,
366 static gboolean imap_scan_required (Folder *folder,
368 static void imap_change_flags (Folder *folder,
371 MsgPermFlags newflags);
372 static gint imap_get_flags (Folder *folder,
374 MsgInfoList *msglist,
375 GRelation *msgflags);
376 static gchar *imap_folder_get_path (Folder *folder);
377 static gchar *imap_item_get_path (Folder *folder,
379 static MsgInfo *imap_parse_msg(const gchar *file, FolderItem *item);
382 /* data types conversion libetpan <-> sylpheed */
383 static GSList * imap_list_from_lep(IMAPFolder * folder,
384 clist * list, const gchar * real_path, gboolean all);
385 static GSList * imap_get_lep_set_from_numlist(MsgNumberList *numlist);
386 static GSList * imap_get_lep_set_from_msglist(MsgInfoList *msglist);
387 static GSList * imap_uid_list_from_lep(clist * list);
388 static GSList * imap_uid_list_from_lep_tab(carray * list);
389 static MsgInfo *imap_envelope_from_lep(struct imap_fetch_env_info * info,
391 static void imap_lep_set_free(GSList *seq_list);
392 static struct mailimap_flag_list * imap_flag_to_lep(IMAPFlags flags);
395 static GHashTable *flags_set_table = NULL;
396 static GHashTable *flags_unset_table = NULL;
397 typedef struct _hashtable_data {
398 IMAPSession *session;
402 static FolderClass imap_class;
404 typedef struct _thread_data {
414 FolderClass *imap_get_class(void)
416 if (imap_class.idstr == NULL) {
417 imap_class.type = F_IMAP;
418 imap_class.idstr = "imap";
419 imap_class.uistr = "IMAP4";
421 /* Folder functions */
422 imap_class.new_folder = imap_folder_new;
423 imap_class.destroy_folder = imap_folder_destroy;
424 imap_class.scan_tree = imap_scan_tree;
425 imap_class.create_tree = imap_create_tree;
427 /* FolderItem functions */
428 imap_class.item_new = imap_folder_item_new;
429 imap_class.item_destroy = imap_folder_item_destroy;
430 imap_class.item_get_path = imap_item_get_path;
431 imap_class.create_folder = imap_create_folder;
432 imap_class.rename_folder = imap_rename_folder;
433 imap_class.remove_folder = imap_remove_folder;
434 imap_class.close = imap_close;
435 imap_class.get_num_list = imap_get_num_list;
436 imap_class.scan_required = imap_scan_required;
438 /* Message functions */
439 imap_class.get_msginfo = imap_get_msginfo;
440 imap_class.get_msginfos = imap_get_msginfos;
441 imap_class.fetch_msg = imap_fetch_msg;
442 imap_class.fetch_msg_full = imap_fetch_msg_full;
443 imap_class.add_msg = imap_add_msg;
444 imap_class.add_msgs = imap_add_msgs;
445 imap_class.copy_msg = imap_copy_msg;
446 imap_class.copy_msgs = imap_copy_msgs;
447 imap_class.remove_msg = imap_remove_msg;
448 imap_class.remove_msgs = imap_remove_msgs;
449 imap_class.remove_all_msg = imap_remove_all_msg;
450 imap_class.is_msg_changed = imap_is_msg_changed;
451 imap_class.change_flags = imap_change_flags;
452 imap_class.get_flags = imap_get_flags;
453 imap_class.set_batch = imap_set_batch;
454 imap_class.synchronise = imap_synchronise;
456 pthread_mutex_init(&imap_mutex, NULL);
463 static Folder *imap_folder_new(const gchar *name, const gchar *path)
467 folder = (Folder *)g_new0(IMAPFolder, 1);
468 folder->klass = &imap_class;
469 imap_folder_init(folder, name, path);
474 static void imap_folder_destroy(Folder *folder)
478 while (imap_folder_get_refcnt(folder) > 0)
479 gtk_main_iteration();
481 dir = imap_folder_get_path(folder);
482 if (is_dir_exist(dir))
483 remove_dir_recursive(dir);
486 folder_remote_folder_destroy(REMOTE_FOLDER(folder));
490 static void imap_folder_init(Folder *folder, const gchar *name,
493 folder_remote_folder_init((Folder *)folder, name, path);
496 static FolderItem *imap_folder_item_new(Folder *folder)
498 IMAPFolderItem *item;
500 item = g_new0(IMAPFolderItem, 1);
503 item->uid_list = NULL;
505 return (FolderItem *)item;
508 static void imap_folder_item_destroy(Folder *folder, FolderItem *_item)
510 IMAPFolderItem *item = (IMAPFolderItem *)_item;
512 g_return_if_fail(item != NULL);
513 g_slist_free(item->uid_list);
518 static gboolean imap_reset_uid_lists_func(GNode *node, gpointer data)
520 IMAPFolderItem *item = (IMAPFolderItem *)node->data;
524 g_slist_free(item->uid_list);
525 item->uid_list = NULL;
530 static void imap_reset_uid_lists(Folder *folder)
532 if(folder->node == NULL)
535 /* Destroy all uid lists and rest last uid */
536 g_node_traverse(folder->node, G_IN_ORDER, G_TRAVERSE_ALL, -1, imap_reset_uid_lists_func, NULL);
539 void imap_get_capabilities(IMAPSession *session)
541 struct mailimap_capability_data *capabilities = NULL;
544 if (session->capability != NULL)
547 capabilities = imap_threaded_capability(session->folder);
549 if (capabilities == NULL)
552 for(cur = clist_begin(capabilities->cap_list) ; cur != NULL ;
553 cur = clist_next(cur)) {
554 struct mailimap_capability * cap =
556 if (!cap || cap->cap_data.cap_name == NULL)
558 session->capability = g_slist_append
559 (session->capability,
560 g_strdup(cap->cap_data.cap_name));
561 debug_print("got capa %s\n", cap->cap_data.cap_name);
563 mailimap_capability_data_free(capabilities);
566 gboolean imap_has_capability(IMAPSession *session, const gchar *cap)
569 for (cur = session->capability; cur; cur = cur->next) {
570 if (!g_ascii_strcasecmp(cur->data, cap))
576 static gint imap_auth(IMAPSession *session, const gchar *user, const gchar *pass,
579 gint ok = IMAP_ERROR;
580 static time_t last_login_err = 0;
582 imap_get_capabilities(session);
585 case IMAP_AUTH_CRAM_MD5:
586 ok = imap_cmd_login(session, user, pass, "CRAM-MD5");
588 case IMAP_AUTH_LOGIN:
589 ok = imap_cmd_login(session, user, pass, "LOGIN");
592 debug_print("capabilities:\n"
595 imap_has_capability(session, "CRAM-MD5"),
596 imap_has_capability(session, "LOGIN"));
597 if (imap_has_capability(session, "CRAM-MD5"))
598 ok = imap_cmd_login(session, user, pass, "CRAM-MD5");
599 if (ok == IMAP_ERROR) /* we always try LOGIN before giving up */
600 ok = imap_cmd_login(session, user, pass, "LOGIN");
602 if (ok == IMAP_SUCCESS)
603 session->authenticated = TRUE;
605 gchar *ext_info = NULL;
607 if (type == IMAP_AUTH_CRAM_MD5) {
608 ext_info = _("\n\nCRAM-MD5 logins only work if libetpan has been "
609 "compiled with SASL support and the "
610 "CRAM-MD5 SASL plugin is installed.");
615 if (time(NULL) - last_login_err > 10) {
616 if (!prefs_common.no_recv_err_panel) {
617 alertpanel_error(_("Connection to %s failed: "
619 SESSION(session)->server, ext_info);
621 log_error(_("Connection to %s failed: "
622 "login refused.%s\n"),
623 SESSION(session)->server, ext_info);
626 last_login_err = time(NULL);
631 static IMAPSession *imap_reconnect_if_possible(Folder *folder, IMAPSession *session)
633 RemoteFolder *rfolder = REMOTE_FOLDER(folder);
634 /* Check if this is the first try to establish a
635 connection, if yes we don't try to reconnect */
636 debug_print("reconnecting\n");
637 if (rfolder->session == NULL) {
638 log_warning(_("Connecting to %s failed"),
639 folder->account->recv_server);
640 session_destroy(SESSION(session));
643 log_warning(_("IMAP4 connection to %s has been"
644 " disconnected. Reconnecting...\n"),
645 folder->account->recv_server);
646 statusbar_print_all(_("IMAP4 connection to %s has been"
647 " disconnected. Reconnecting...\n"),
648 folder->account->recv_server);
649 SESSION(session)->state = SESSION_DISCONNECTED;
650 session_destroy(SESSION(session));
651 /* Clear folders session to make imap_session_get create
652 a new session, because of rfolder->session == NULL
653 it will not try to reconnect again and so avoid an
655 rfolder->session = NULL;
656 session = imap_session_get(folder);
657 rfolder->session = SESSION(session);
663 static IMAPSession *imap_session_get(Folder *folder)
665 RemoteFolder *rfolder = REMOTE_FOLDER(folder);
666 IMAPSession *session = NULL;
668 g_return_val_if_fail(folder != NULL, NULL);
669 g_return_val_if_fail(FOLDER_CLASS(folder) == &imap_class, NULL);
670 g_return_val_if_fail(folder->account != NULL, NULL);
672 if (prefs_common.work_offline && !inc_offline_should_override()) {
676 /* Make sure we have a session */
677 if (rfolder->session != NULL) {
678 session = IMAP_SESSION(rfolder->session);
680 imap_reset_uid_lists(folder);
681 session = imap_session_new(folder, folder->account);
686 /* Make sure session is authenticated */
687 if (!IMAP_SESSION(session)->authenticated)
688 imap_session_authenticate(IMAP_SESSION(session), folder->account);
690 if (!IMAP_SESSION(session)->authenticated) {
691 session_destroy(SESSION(session));
692 rfolder->session = NULL;
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 _("Con_tinue 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;
1000 gint curnum = 0, total = 0;
1003 g_return_val_if_fail(folder != NULL, -1);
1004 g_return_val_if_fail(dest != NULL, -1);
1005 g_return_val_if_fail(file_list != NULL, -1);
1007 session = imap_session_get(folder);
1011 destdir = imap_get_real_path(IMAP_FOLDER(folder), dest->path);
1013 statusbar_print_all(_("Adding messages..."));
1014 total = g_slist_length(file_list);
1015 for (cur = file_list; cur != NULL; cur = cur->next) {
1016 IMAPFlags iflags = 0;
1017 guint32 new_uid = 0;
1018 gchar *real_file = NULL;
1019 gboolean file_is_tmp = FALSE;
1020 fileinfo = (MsgFileInfo *)cur->data;
1022 statusbar_progress_all(curnum, total, 1);
1025 if (fileinfo->flags) {
1026 if (MSG_IS_MARKED(*fileinfo->flags))
1027 iflags |= IMAP_FLAG_FLAGGED;
1028 if (MSG_IS_REPLIED(*fileinfo->flags))
1029 iflags |= IMAP_FLAG_ANSWERED;
1030 if (!MSG_IS_UNREAD(*fileinfo->flags))
1031 iflags |= IMAP_FLAG_SEEN;
1034 if (fileinfo->flags) {
1035 if ((MSG_IS_QUEUED(*fileinfo->flags)
1036 || MSG_IS_DRAFT(*fileinfo->flags))
1037 && !folder_has_parent_of_type(dest, F_QUEUE)
1038 && !folder_has_parent_of_type(dest, F_DRAFT)) {
1039 real_file = get_tmp_file();
1041 if (procmsg_remove_special_headers(
1050 if (real_file == NULL)
1051 real_file = g_strdup(fileinfo->file);
1053 if (folder_has_parent_of_type(dest, F_QUEUE) ||
1054 folder_has_parent_of_type(dest, F_OUTBOX) ||
1055 folder_has_parent_of_type(dest, F_DRAFT) ||
1056 folder_has_parent_of_type(dest, F_TRASH))
1057 iflags |= IMAP_FLAG_SEEN;
1059 ok = imap_cmd_append(session, destdir, real_file, iflags,
1062 if (ok != IMAP_SUCCESS) {
1063 g_warning("can't append message %s\n", real_file);
1065 g_unlink(real_file);
1071 if (relation != NULL)
1072 g_relation_insert(relation, fileinfo->msginfo != NULL ?
1073 (gpointer) fileinfo->msginfo : (gpointer) fileinfo,
1074 GINT_TO_POINTER(dest->last_num + 1));
1075 if (last_uid < new_uid)
1078 g_unlink(real_file);
1080 statusbar_progress_all(0,0,0);
1081 statusbar_pop_all();
1088 static gint imap_do_copy_msgs(Folder *folder, FolderItem *dest,
1089 MsgInfoList *msglist, GRelation *relation)
1093 GSList *seq_list, *cur;
1095 IMAPSession *session;
1096 gint ok = IMAP_SUCCESS;
1097 GRelation *uid_mapping;
1100 g_return_val_if_fail(folder != NULL, -1);
1101 g_return_val_if_fail(dest != NULL, -1);
1102 g_return_val_if_fail(msglist != NULL, -1);
1104 session = imap_session_get(folder);
1109 msginfo = (MsgInfo *)msglist->data;
1111 src = msginfo->folder;
1113 g_warning("the src folder is identical to the dest.\n");
1117 ok = imap_select(session, IMAP_FOLDER(folder), msginfo->folder->path,
1118 NULL, NULL, NULL, NULL, FALSE);
1119 if (ok != IMAP_SUCCESS) {
1123 destdir = imap_get_real_path(IMAP_FOLDER(folder), dest->path);
1124 seq_list = imap_get_lep_set_from_msglist(msglist);
1125 uid_mapping = g_relation_new(2);
1126 g_relation_index(uid_mapping, 0, g_direct_hash, g_direct_equal);
1128 statusbar_print_all(_("Copying messages..."));
1129 for (cur = seq_list; cur != NULL; cur = g_slist_next(cur)) {
1130 struct mailimap_set * seq_set;
1131 seq_set = cur->data;
1133 debug_print("Copying messages from %s to %s ...\n",
1134 src->path, destdir);
1136 ok = imap_cmd_copy(session, seq_set, destdir, uid_mapping);
1137 if (ok != IMAP_SUCCESS) {
1138 g_relation_destroy(uid_mapping);
1139 imap_lep_set_free(seq_list);
1144 for (cur = msglist; cur != NULL; cur = g_slist_next(cur)) {
1145 MsgInfo *msginfo = (MsgInfo *)cur->data;
1148 tuples = g_relation_select(uid_mapping,
1149 GINT_TO_POINTER(msginfo->msgnum),
1151 if (tuples->len > 0) {
1152 gint num = GPOINTER_TO_INT(g_tuples_index(tuples, 0, 1));
1153 g_relation_insert(relation, msginfo,
1154 GPOINTER_TO_INT(num));
1158 g_relation_insert(relation, msginfo,
1159 GPOINTER_TO_INT(0));
1160 g_tuples_destroy(tuples);
1162 statusbar_pop_all();
1164 g_relation_destroy(uid_mapping);
1165 imap_lep_set_free(seq_list);
1169 IMAP_FOLDER_ITEM(dest)->lastuid = 0;
1170 IMAP_FOLDER_ITEM(dest)->uid_next = 0;
1171 g_slist_free(IMAP_FOLDER_ITEM(dest)->uid_list);
1172 IMAP_FOLDER_ITEM(dest)->uid_list = NULL;
1174 if (ok == IMAP_SUCCESS)
1180 static gint imap_copy_msg(Folder *folder, FolderItem *dest, MsgInfo *msginfo)
1184 g_return_val_if_fail(msginfo != NULL, -1);
1186 msglist.data = msginfo;
1187 msglist.next = NULL;
1189 return imap_copy_msgs(folder, dest, &msglist, NULL);
1192 static gint imap_copy_msgs(Folder *folder, FolderItem *dest,
1193 MsgInfoList *msglist, GRelation *relation)
1199 g_return_val_if_fail(folder != NULL, -1);
1200 g_return_val_if_fail(dest != NULL, -1);
1201 g_return_val_if_fail(msglist != NULL, -1);
1203 msginfo = (MsgInfo *)msglist->data;
1204 g_return_val_if_fail(msginfo->folder != NULL, -1);
1206 if (folder == msginfo->folder->folder &&
1207 !folder_has_parent_of_type(msginfo->folder, F_DRAFT) &&
1208 !folder_has_parent_of_type(msginfo->folder, F_QUEUE)) {
1209 ret = imap_do_copy_msgs(folder, dest, msglist, relation);
1213 file_list = procmsg_get_message_file_list(msglist);
1214 g_return_val_if_fail(file_list != NULL, -1);
1216 ret = imap_add_msgs(folder, dest, file_list, relation);
1218 procmsg_message_file_list_free(file_list);
1224 static gint imap_do_remove_msgs(Folder *folder, FolderItem *dest,
1225 MsgInfoList *msglist, GRelation *relation)
1228 GSList *numlist = NULL, *cur;
1230 IMAPSession *session;
1231 gint ok = IMAP_SUCCESS;
1232 GRelation *uid_mapping;
1234 g_return_val_if_fail(folder != NULL, -1);
1235 g_return_val_if_fail(dest != NULL, -1);
1236 g_return_val_if_fail(msglist != NULL, -1);
1238 session = imap_session_get(folder);
1242 msginfo = (MsgInfo *)msglist->data;
1244 ok = imap_select(session, IMAP_FOLDER(folder), msginfo->folder->path,
1245 NULL, NULL, NULL, NULL, FALSE);
1246 if (ok != IMAP_SUCCESS) {
1250 destdir = imap_get_real_path(IMAP_FOLDER(folder), dest->path);
1251 for (cur = msglist; cur; cur = cur->next) {
1252 msginfo = (MsgInfo *)cur->data;
1253 if (!MSG_IS_DELETED(msginfo->flags))
1254 numlist = g_slist_append(numlist, GINT_TO_POINTER(msginfo->msgnum));
1257 uid_mapping = g_relation_new(2);
1258 g_relation_index(uid_mapping, 0, g_direct_hash, g_direct_equal);
1260 ok = imap_set_message_flags
1261 (IMAP_SESSION(REMOTE_FOLDER(folder)->session),
1262 numlist, IMAP_FLAG_DELETED, TRUE);
1263 if (ok != IMAP_SUCCESS) {
1264 log_warning(_("can't set deleted flags\n"));
1267 ok = imap_cmd_expunge(session);
1268 if (ok != IMAP_SUCCESS) {
1269 log_warning(_("can't expunge\n"));
1273 g_relation_destroy(uid_mapping);
1274 g_slist_free(numlist);
1278 if (ok == IMAP_SUCCESS)
1284 static gint imap_remove_msgs(Folder *folder, FolderItem *dest,
1285 MsgInfoList *msglist, GRelation *relation)
1289 g_return_val_if_fail(folder != NULL, -1);
1290 g_return_val_if_fail(dest != NULL, -1);
1291 if (msglist == NULL)
1294 msginfo = (MsgInfo *)msglist->data;
1295 g_return_val_if_fail(msginfo->folder != NULL, -1);
1297 return imap_do_remove_msgs(folder, dest, msglist, relation);
1300 static gint imap_remove_all_msg(Folder *folder, FolderItem *item)
1302 GSList *list = folder_item_get_msg_list(item);
1303 gint res = imap_remove_msgs(folder, item, list, NULL);
1304 procmsg_msg_list_free(list);
1308 static gboolean imap_is_msg_changed(Folder *folder, FolderItem *item,
1311 /* TODO: properly implement this method */
1315 static gint imap_close(Folder *folder, FolderItem *item)
1320 static gint imap_scan_tree(Folder *folder)
1322 FolderItem *item = NULL;
1323 IMAPSession *session;
1324 gchar *root_folder = NULL;
1326 g_return_val_if_fail(folder != NULL, -1);
1327 g_return_val_if_fail(folder->account != NULL, -1);
1329 session = imap_session_get(folder);
1331 if (!folder->node) {
1332 folder_tree_destroy(folder);
1333 item = folder_item_new(folder, folder->name, NULL);
1334 item->folder = folder;
1335 folder->node = item->node = g_node_new(item);
1340 if (folder->account->imap_dir && *folder->account->imap_dir) {
1345 Xstrdup_a(root_folder, folder->account->imap_dir, {return -1;});
1346 extract_quote(root_folder, '"');
1347 subst_char(root_folder,
1348 imap_get_path_separator(IMAP_FOLDER(folder),
1351 strtailchomp(root_folder, '/');
1352 real_path = imap_get_real_path
1353 (IMAP_FOLDER(folder), root_folder);
1354 debug_print("IMAP root directory: %s\n", real_path);
1356 /* check if root directory exist */
1358 r = imap_threaded_list(session->folder, "", real_path,
1360 if ((r != MAILIMAP_NO_ERROR) || (clist_count(lep_list) == 0)) {
1361 if (!folder->node) {
1362 item = folder_item_new(folder, folder->name, NULL);
1363 item->folder = folder;
1364 folder->node = item->node = g_node_new(item);
1368 mailimap_list_result_free(lep_list);
1374 item = FOLDER_ITEM(folder->node->data);
1375 if (!item || ((item->path || root_folder) &&
1376 strcmp2(item->path, root_folder) != 0)) {
1377 folder_tree_destroy(folder);
1378 item = folder_item_new(folder, folder->name, root_folder);
1379 item->folder = folder;
1380 folder->node = item->node = g_node_new(item);
1383 imap_scan_tree_recursive(session, FOLDER_ITEM(folder->node->data));
1384 imap_create_missing_folders(folder);
1389 static gint imap_scan_tree_recursive(IMAPSession *session, FolderItem *item)
1392 IMAPFolder *imapfolder;
1393 FolderItem *new_item;
1394 GSList *item_list, *cur;
1397 gchar *wildcard_path;
1403 g_return_val_if_fail(item != NULL, -1);
1404 g_return_val_if_fail(item->folder != NULL, -1);
1405 g_return_val_if_fail(item->no_sub == FALSE, -1);
1407 folder = item->folder;
1408 imapfolder = IMAP_FOLDER(folder);
1410 separator = imap_get_path_separator(imapfolder, item->path);
1412 if (folder->ui_func)
1413 folder->ui_func(folder, item, folder->ui_func_data);
1416 wildcard[0] = separator;
1419 real_path = imap_get_real_path(imapfolder, item->path);
1423 real_path = g_strdup("");
1426 Xstrcat_a(wildcard_path, real_path, wildcard,
1427 {g_free(real_path); return IMAP_ERROR;});
1429 r = imap_threaded_list(folder, "", wildcard_path, &lep_list);
1430 if (r != MAILIMAP_NO_ERROR) {
1434 item_list = imap_list_from_lep(imapfolder,
1435 lep_list, real_path, FALSE);
1436 mailimap_list_result_free(lep_list);
1441 node = item->node->children;
1442 while (node != NULL) {
1443 FolderItem *old_item = FOLDER_ITEM(node->data);
1444 GNode *next = node->next;
1447 for (cur = item_list; cur != NULL; cur = cur->next) {
1448 FolderItem *cur_item = FOLDER_ITEM(cur->data);
1449 if (!strcmp2(old_item->path, cur_item->path)) {
1450 new_item = cur_item;
1455 debug_print("folder '%s' not found. removing...\n",
1457 folder_item_remove(old_item);
1459 old_item->no_sub = new_item->no_sub;
1460 old_item->no_select = new_item->no_select;
1461 if (old_item->no_sub == TRUE && node->children) {
1462 debug_print("folder '%s' doesn't have "
1463 "subfolders. removing...\n",
1465 folder_item_remove_children(old_item);
1472 for (cur = item_list; cur != NULL; cur = cur->next) {
1473 FolderItem *cur_item = FOLDER_ITEM(cur->data);
1476 for (node = item->node->children; node != NULL;
1477 node = node->next) {
1478 if (!strcmp2(FOLDER_ITEM(node->data)->path,
1480 new_item = FOLDER_ITEM(node->data);
1481 folder_item_destroy(cur_item);
1487 new_item = cur_item;
1488 debug_print("new folder '%s' found.\n", new_item->path);
1489 folder_item_append(item, new_item);
1492 if (!strcmp(new_item->path, "INBOX")) {
1493 new_item->stype = F_INBOX;
1494 folder->inbox = new_item;
1495 } else if (!folder_item_parent(item) || item->stype == F_INBOX) {
1498 base = g_path_get_basename(new_item->path);
1500 if (!folder->outbox && !g_ascii_strcasecmp(base, "Sent")) {
1501 new_item->stype = F_OUTBOX;
1502 folder->outbox = new_item;
1503 } else if (!folder->draft && !g_ascii_strcasecmp(base, "Drafts")) {
1504 new_item->stype = F_DRAFT;
1505 folder->draft = new_item;
1506 } else if (!folder->queue && !g_ascii_strcasecmp(base, "Queue")) {
1507 new_item->stype = F_QUEUE;
1508 folder->queue = new_item;
1509 } else if (!folder->trash && !g_ascii_strcasecmp(base, "Trash")) {
1510 new_item->stype = F_TRASH;
1511 folder->trash = new_item;
1516 if (new_item->no_sub == FALSE)
1517 imap_scan_tree_recursive(session, new_item);
1520 g_slist_free(item_list);
1522 return IMAP_SUCCESS;
1525 static gint imap_create_tree(Folder *folder)
1527 g_return_val_if_fail(folder != NULL, -1);
1528 g_return_val_if_fail(folder->node != NULL, -1);
1529 g_return_val_if_fail(folder->node->data != NULL, -1);
1530 g_return_val_if_fail(folder->account != NULL, -1);
1532 imap_scan_tree(folder);
1533 imap_create_missing_folders(folder);
1538 static void imap_create_missing_folders(Folder *folder)
1540 g_return_if_fail(folder != NULL);
1543 folder->inbox = imap_create_special_folder
1544 (folder, F_INBOX, "INBOX");
1546 folder->trash = imap_create_special_folder
1547 (folder, F_TRASH, "Trash");
1549 folder->queue = imap_create_special_folder
1550 (folder, F_QUEUE, "Queue");
1551 if (!folder->outbox)
1552 folder->outbox = imap_create_special_folder
1553 (folder, F_OUTBOX, "Sent");
1555 folder->draft = imap_create_special_folder
1556 (folder, F_DRAFT, "Drafts");
1559 static FolderItem *imap_create_special_folder(Folder *folder,
1560 SpecialFolderItemType stype,
1564 FolderItem *new_item;
1566 g_return_val_if_fail(folder != NULL, NULL);
1567 g_return_val_if_fail(folder->node != NULL, NULL);
1568 g_return_val_if_fail(folder->node->data != NULL, NULL);
1569 g_return_val_if_fail(folder->account != NULL, NULL);
1570 g_return_val_if_fail(name != NULL, NULL);
1572 item = FOLDER_ITEM(folder->node->data);
1573 new_item = imap_create_folder(folder, item, name);
1576 g_warning("Can't create '%s'\n", name);
1577 if (!folder->inbox) return NULL;
1579 new_item = imap_create_folder(folder, folder->inbox, name);
1581 g_warning("Can't create '%s' under INBOX\n", name);
1583 new_item->stype = stype;
1585 new_item->stype = stype;
1590 static gchar *imap_folder_get_path(Folder *folder)
1594 g_return_val_if_fail(folder != NULL, NULL);
1595 g_return_val_if_fail(folder->account != NULL, NULL);
1597 folder_path = g_strconcat(get_imap_cache_dir(),
1599 folder->account->recv_server,
1601 folder->account->userid,
1607 static gchar *imap_item_get_path(Folder *folder, FolderItem *item)
1609 gchar *folder_path, *path;
1611 g_return_val_if_fail(folder != NULL, NULL);
1612 g_return_val_if_fail(item != NULL, NULL);
1613 folder_path = imap_folder_get_path(folder);
1615 g_return_val_if_fail(folder_path != NULL, NULL);
1616 if (folder_path[0] == G_DIR_SEPARATOR) {
1618 path = g_strconcat(folder_path, G_DIR_SEPARATOR_S,
1621 path = g_strdup(folder_path);
1624 path = g_strconcat(get_home_dir(), G_DIR_SEPARATOR_S,
1625 folder_path, G_DIR_SEPARATOR_S,
1628 path = g_strconcat(get_home_dir(), G_DIR_SEPARATOR_S,
1631 g_free(folder_path);
1636 static FolderItem *imap_create_folder(Folder *folder, FolderItem *parent,
1639 gchar *dirpath, *imap_path;
1640 IMAPSession *session;
1641 FolderItem *new_item;
1646 gboolean no_select = FALSE, no_sub = FALSE;
1648 g_return_val_if_fail(folder != NULL, NULL);
1649 g_return_val_if_fail(folder->account != NULL, NULL);
1650 g_return_val_if_fail(parent != NULL, NULL);
1651 g_return_val_if_fail(name != NULL, NULL);
1653 session = imap_session_get(folder);
1658 if (!folder_item_parent(parent) && strcmp(name, "INBOX") == 0) {
1659 dirpath = g_strdup(name);
1660 }else if (parent->path)
1661 dirpath = g_strconcat(parent->path, "/", name, NULL);
1662 else if ((p = strchr(name, '/')) != NULL && *(p + 1) != '\0')
1663 dirpath = g_strdup(name);
1664 else if (folder->account->imap_dir && *folder->account->imap_dir) {
1667 Xstrdup_a(imap_dir, folder->account->imap_dir, {return NULL;});
1668 strtailchomp(imap_dir, '/');
1669 dirpath = g_strconcat(imap_dir, "/", name, NULL);
1671 dirpath = g_strdup(name);
1675 /* keep trailing directory separator to create a folder that contains
1677 imap_path = imap_utf8_to_modified_utf7(dirpath);
1679 strtailchomp(dirpath, '/');
1680 Xstrdup_a(new_name, name, {
1684 separator = imap_get_path_separator(IMAP_FOLDER(folder), imap_path);
1685 imap_path_separator_subst(imap_path, separator);
1686 /* remove trailing / for display */
1687 strtailchomp(new_name, '/');
1689 if (strcmp(dirpath, "INBOX") != 0) {
1691 gboolean exist = FALSE;
1695 argbuf = g_ptr_array_new();
1696 r = imap_threaded_list(folder, "", imap_path, &lep_list);
1697 if (r != MAILIMAP_NO_ERROR) {
1698 log_warning(_("can't create mailbox: LIST failed\n"));
1701 ptr_array_free_strings(argbuf);
1702 g_ptr_array_free(argbuf, TRUE);
1706 if (clist_count(lep_list) > 0)
1708 mailimap_list_result_free(lep_list);
1711 ok = imap_cmd_create(session, imap_path);
1712 if (ok != IMAP_SUCCESS) {
1713 log_warning(_("can't create mailbox\n"));
1718 r = imap_threaded_list(folder, "", imap_path, &lep_list);
1719 if (r == MAILIMAP_NO_ERROR) {
1720 GSList *item_list = imap_list_from_lep(IMAP_FOLDER(folder),
1721 lep_list, dirpath, TRUE);
1723 FolderItem *cur_item = FOLDER_ITEM(item_list->data);
1724 no_select = cur_item->no_select;
1725 no_sub = cur_item->no_sub;
1726 g_slist_free(item_list);
1728 mailimap_list_result_free(lep_list);
1735 /* just get flags */
1736 r = imap_threaded_list(folder, "", "INBOX", &lep_list);
1737 if (r == MAILIMAP_NO_ERROR) {
1738 GSList *item_list = imap_list_from_lep(IMAP_FOLDER(folder),
1739 lep_list, dirpath, TRUE);
1741 FolderItem *cur_item = FOLDER_ITEM(item_list->data);
1742 no_select = cur_item->no_select;
1743 no_sub = cur_item->no_sub;
1744 g_slist_free(item_list);
1746 mailimap_list_result_free(lep_list);
1750 new_item = folder_item_new(folder, new_name, dirpath);
1751 new_item->no_select = no_select;
1752 new_item->no_sub = no_sub;
1753 folder_item_append(parent, new_item);
1757 dirpath = folder_item_get_path(new_item);
1758 if (!is_dir_exist(dirpath))
1759 make_dir_hier(dirpath);
1765 static gint imap_rename_folder(Folder *folder, FolderItem *item,
1770 gchar *real_oldpath;
1771 gchar *real_newpath;
1773 gchar *old_cache_dir;
1774 gchar *new_cache_dir;
1775 IMAPSession *session;
1778 gint exists, recent, unseen;
1779 guint32 uid_validity;
1781 g_return_val_if_fail(folder != NULL, -1);
1782 g_return_val_if_fail(item != NULL, -1);
1783 g_return_val_if_fail(item->path != NULL, -1);
1784 g_return_val_if_fail(name != NULL, -1);
1786 if (strchr(name, imap_get_path_separator(IMAP_FOLDER(folder), item->path)) != NULL) {
1787 g_warning(_("New folder name must not contain the namespace "
1792 session = imap_session_get(folder);
1796 real_oldpath = imap_get_real_path(IMAP_FOLDER(folder), item->path);
1798 g_free(session->mbox);
1799 session->mbox = NULL;
1800 ok = imap_cmd_examine(session, "INBOX",
1801 &exists, &recent, &unseen, &uid_validity, FALSE);
1802 if (ok != IMAP_SUCCESS) {
1803 g_free(real_oldpath);
1807 separator = imap_get_path_separator(IMAP_FOLDER(folder), item->path);
1808 if (strchr(item->path, G_DIR_SEPARATOR)) {
1809 dirpath = g_path_get_dirname(item->path);
1810 newpath = g_strconcat(dirpath, G_DIR_SEPARATOR_S, name, NULL);
1813 newpath = g_strdup(name);
1815 real_newpath = imap_utf8_to_modified_utf7(newpath);
1816 imap_path_separator_subst(real_newpath, separator);
1818 ok = imap_cmd_rename(session, real_oldpath, real_newpath);
1819 if (ok != IMAP_SUCCESS) {
1820 log_warning(_("can't rename mailbox: %s to %s\n"),
1821 real_oldpath, real_newpath);
1822 g_free(real_oldpath);
1824 g_free(real_newpath);
1829 item->name = g_strdup(name);
1831 old_cache_dir = folder_item_get_path(item);
1833 paths[0] = g_strdup(item->path);
1835 g_node_traverse(item->node, G_PRE_ORDER, G_TRAVERSE_ALL, -1,
1836 imap_rename_folder_func, paths);
1838 if (is_dir_exist(old_cache_dir)) {
1839 new_cache_dir = folder_item_get_path(item);
1840 if (rename(old_cache_dir, new_cache_dir) < 0) {
1841 FILE_OP_ERROR(old_cache_dir, "rename");
1843 g_free(new_cache_dir);
1846 g_free(old_cache_dir);
1849 g_free(real_oldpath);
1850 g_free(real_newpath);
1855 static gint imap_remove_folder_real(Folder *folder, FolderItem *item)
1858 IMAPSession *session;
1861 gint exists, recent, unseen;
1862 guint32 uid_validity;
1864 g_return_val_if_fail(folder != NULL, -1);
1865 g_return_val_if_fail(item != NULL, -1);
1866 g_return_val_if_fail(item->path != NULL, -1);
1868 session = imap_session_get(folder);
1872 path = imap_get_real_path(IMAP_FOLDER(folder), item->path);
1874 ok = imap_cmd_examine(session, "INBOX",
1875 &exists, &recent, &unseen, &uid_validity, FALSE);
1876 if (ok != IMAP_SUCCESS) {
1881 ok = imap_cmd_delete(session, path);
1882 if (ok != IMAP_SUCCESS) {
1883 gchar *tmp = g_strdup_printf("%s%c", path,
1884 imap_get_path_separator(IMAP_FOLDER(folder), path));
1887 ok = imap_cmd_delete(session, path);
1890 if (ok != IMAP_SUCCESS) {
1891 log_warning(_("can't delete mailbox\n"));
1897 cache_dir = folder_item_get_path(item);
1898 if (is_dir_exist(cache_dir) && remove_dir_recursive(cache_dir) < 0)
1899 g_warning("can't remove directory '%s'\n", cache_dir);
1901 folder_item_remove(item);
1906 static gint imap_remove_folder(Folder *folder, FolderItem *item)
1910 g_return_val_if_fail(item != NULL, -1);
1911 g_return_val_if_fail(item->folder != NULL, -1);
1912 g_return_val_if_fail(item->node != NULL, -1);
1914 node = item->node->children;
1915 while (node != NULL) {
1917 if (imap_remove_folder(folder, FOLDER_ITEM(node->data)) < 0)
1921 debug_print("IMAP removing %s\n", item->path);
1923 if (imap_remove_all_msg(folder, item) < 0)
1925 return imap_remove_folder_real(folder, item);
1928 typedef struct _uncached_data {
1929 IMAPSession *session;
1931 MsgNumberList *numlist;
1937 static void *imap_get_uncached_messages_thread(void *data)
1939 uncached_data *stuff = (uncached_data *)data;
1940 IMAPSession *session = stuff->session;
1941 FolderItem *item = stuff->item;
1942 MsgNumberList *numlist = stuff->numlist;
1944 GSList *newlist = NULL;
1945 GSList *llast = NULL;
1946 GSList *seq_list, *cur;
1948 debug_print("uncached_messages\n");
1950 if (session == NULL || item == NULL || item->folder == NULL
1951 || FOLDER_CLASS(item->folder) != &imap_class) {
1956 seq_list = imap_get_lep_set_from_numlist(numlist);
1957 debug_print("get msgs info\n");
1958 for (cur = seq_list; cur != NULL; cur = g_slist_next(cur)) {
1959 struct mailimap_set * imapset;
1965 imapset = cur->data;
1967 r = imap_threaded_fetch_env(session->folder,
1968 imapset, &env_list);
1969 if (r != MAILIMAP_NO_ERROR)
1973 for(i = 0 ; i < carray_count(env_list) ; i ++) {
1974 struct imap_fetch_env_info * info;
1977 info = carray_get(env_list, i);
1978 msginfo = imap_envelope_from_lep(info, item);
1979 msginfo->folder = item;
1981 llast = newlist = g_slist_append(newlist, msginfo);
1983 llast = g_slist_append(llast, msginfo);
1984 llast = llast->next;
1989 imap_fetch_env_free(env_list);
1992 session_set_access_time(SESSION(session));
1997 #define MAX_MSG_NUM 50
1999 static GSList *imap_get_uncached_messages(IMAPSession *session,
2001 MsgNumberList *numlist)
2003 GSList *result = NULL;
2005 uncached_data *data = g_new0(uncached_data, 1);
2010 data->total = g_slist_length(numlist);
2011 debug_print("messages list : %i\n", data->total);
2013 while (cur != NULL) {
2014 GSList * partial_result;
2022 while (count < MAX_MSG_NUM) {
2027 if (newlist == NULL)
2028 llast = newlist = g_slist_append(newlist, p);
2030 llast = g_slist_append(llast, p);
2031 llast = llast->next;
2041 data->session = session;
2043 data->numlist = newlist;
2046 if (prefs_common.work_offline && !inc_offline_should_override()) {
2052 (GSList *)imap_get_uncached_messages_thread(data);
2054 statusbar_progress_all(data->cur,data->total, 1);
2056 g_slist_free(newlist);
2058 result = g_slist_concat(result, partial_result);
2062 statusbar_progress_all(0,0,0);
2063 statusbar_pop_all();
2068 static void imap_delete_all_cached_messages(FolderItem *item)
2072 g_return_if_fail(item != NULL);
2073 g_return_if_fail(item->folder != NULL);
2074 g_return_if_fail(FOLDER_CLASS(item->folder) == &imap_class);
2076 debug_print("Deleting all cached messages...\n");
2078 dir = folder_item_get_path(item);
2079 if (is_dir_exist(dir))
2080 remove_all_numbered_files(dir);
2083 debug_print("done.\n");
2086 static IMAPNameSpace *imap_find_namespace_from_list(GList *ns_list,
2089 IMAPNameSpace *namespace = NULL;
2090 gchar *tmp_path, *name;
2092 if (!path) path = "";
2094 for (; ns_list != NULL; ns_list = ns_list->next) {
2095 IMAPNameSpace *tmp_ns = ns_list->data;
2097 Xstrcat_a(tmp_path, path, "/", return namespace);
2098 Xstrdup_a(name, tmp_ns->name, return namespace);
2099 if (tmp_ns->separator && tmp_ns->separator != '/') {
2100 subst_char(tmp_path, tmp_ns->separator, '/');
2101 subst_char(name, tmp_ns->separator, '/');
2103 if (strncmp(tmp_path, name, strlen(name)) == 0)
2110 static IMAPNameSpace *imap_find_namespace(IMAPFolder *folder,
2113 IMAPNameSpace *namespace;
2115 g_return_val_if_fail(folder != NULL, NULL);
2117 namespace = imap_find_namespace_from_list(folder->ns_personal, path);
2118 if (namespace) return namespace;
2119 namespace = imap_find_namespace_from_list(folder->ns_others, path);
2120 if (namespace) return namespace;
2121 namespace = imap_find_namespace_from_list(folder->ns_shared, path);
2122 if (namespace) return namespace;
2128 static gchar imap_get_path_separator(IMAPFolder *folder, const gchar *path)
2130 IMAPNameSpace *namespace;
2131 gchar separator = '/';
2133 if (folder->last_seen_separator == 0) {
2135 int r = imap_threaded_list((Folder *)folder, "", "", &lep_list);
2136 if (r != MAILIMAP_NO_ERROR) {
2137 log_warning(_("LIST failed\n"));
2141 if (clist_count(lep_list) > 0) {
2142 clistiter * iter = clist_begin(lep_list);
2143 struct mailimap_mailbox_list * mb;
2144 mb = clist_content(iter);
2146 folder->last_seen_separator = mb->mb_delimiter;
2147 debug_print("got separator: %c\n", folder->last_seen_separator);
2149 mailimap_list_result_free(lep_list);
2152 if (folder->last_seen_separator != 0) {
2153 debug_print("using separator: %c\n", folder->last_seen_separator);
2154 return folder->last_seen_separator;
2157 namespace = imap_find_namespace(folder, path);
2158 if (namespace && namespace->separator)
2159 separator = namespace->separator;
2164 static gchar *imap_get_real_path(IMAPFolder *folder, const gchar *path)
2169 g_return_val_if_fail(folder != NULL, NULL);
2170 g_return_val_if_fail(path != NULL, NULL);
2172 real_path = imap_utf8_to_modified_utf7(path);
2173 separator = imap_get_path_separator(folder, path);
2174 imap_path_separator_subst(real_path, separator);
2179 static gint imap_set_message_flags(IMAPSession *session,
2180 MsgNumberList *numlist,
2188 seq_list = imap_get_lep_set_from_numlist(numlist);
2190 for(cur = seq_list ; cur != NULL ; cur = g_slist_next(cur)) {
2191 struct mailimap_set * imapset;
2193 imapset = cur->data;
2195 ok = imap_cmd_store(session, imapset,
2199 imap_lep_set_free(seq_list);
2201 return IMAP_SUCCESS;
2204 typedef struct _select_data {
2205 IMAPSession *session;
2210 guint32 *uid_validity;
2214 static gint imap_select(IMAPSession *session, IMAPFolder *folder,
2216 gint *exists, gint *recent, gint *unseen,
2217 guint32 *uid_validity, gboolean block)
2221 gint exists_, recent_, unseen_;
2222 guint32 uid_validity_;
2224 if (!exists && !recent && !unseen && !uid_validity) {
2225 if (session->mbox && strcmp(session->mbox, path) == 0)
2226 return IMAP_SUCCESS;
2235 uid_validity = &uid_validity_;
2237 g_free(session->mbox);
2238 session->mbox = NULL;
2240 real_path = imap_get_real_path(folder, path);
2242 ok = imap_cmd_select(session, real_path,
2243 exists, recent, unseen, uid_validity, block);
2244 if (ok != IMAP_SUCCESS)
2245 log_warning(_("can't select folder: %s\n"), real_path);
2247 session->mbox = g_strdup(path);
2248 session->folder_content_changed = FALSE;
2255 static gint imap_status(IMAPSession *session, IMAPFolder *folder,
2256 const gchar *path, IMAPFolderItem *item,
2258 guint32 *uid_next, guint32 *uid_validity,
2259 gint *unseen, gboolean block)
2263 struct mailimap_mailbox_data_status * data_status;
2268 real_path = imap_get_real_path(folder, path);
2271 if (time(NULL) - item->last_update >= 5 && item->last_update != 1) {
2272 /* do the full stuff */
2273 item->last_update = 1; /* force update */
2274 debug_print("updating everything\n");
2275 r = imap_status(session, folder, path, item,
2276 &item->c_messages, &item->c_uid_next,
2277 &item->c_uid_validity, &item->c_unseen, block);
2278 if (r != MAILIMAP_NO_ERROR) {
2279 debug_print("status err %d\n", r);
2282 item->last_update = time(NULL);
2284 *messages = item->c_messages;
2286 *uid_next = item->c_uid_next;
2288 *uid_validity = item->c_uid_validity;
2290 *unseen = item->c_unseen;
2292 } else if (time(NULL) - item->last_update < 5) {
2293 /* return cached stuff */
2294 debug_print("using cache\n");
2296 *messages = item->c_messages;
2298 *uid_next = item->c_uid_next;
2300 *uid_validity = item->c_uid_validity;
2302 *unseen = item->c_unseen;
2307 /* if we get there, we're updating cache */
2321 r = imap_threaded_status(FOLDER(folder), real_path,
2322 &data_status, mask);
2325 if (r != MAILIMAP_NO_ERROR) {
2326 debug_print("status err %d\n", r);
2330 if (data_status->st_info_list == NULL) {
2331 mailimap_mailbox_data_status_free(data_status);
2332 debug_print("status->st_info_list == NULL\n");
2337 for(iter = clist_begin(data_status->st_info_list) ; iter != NULL ;
2338 iter = clist_next(iter)) {
2339 struct mailimap_status_info * info;
2341 info = clist_content(iter);
2342 switch (info->st_att) {
2343 case MAILIMAP_STATUS_ATT_MESSAGES:
2344 * messages = info->st_value;
2345 got_values |= 1 << 0;
2348 case MAILIMAP_STATUS_ATT_UIDNEXT:
2349 * uid_next = info->st_value;
2350 got_values |= 1 << 2;
2353 case MAILIMAP_STATUS_ATT_UIDVALIDITY:
2354 * uid_validity = info->st_value;
2355 got_values |= 1 << 3;
2358 case MAILIMAP_STATUS_ATT_UNSEEN:
2359 * unseen = info->st_value;
2360 got_values |= 1 << 4;
2364 mailimap_mailbox_data_status_free(data_status);
2366 if (got_values != mask) {
2367 debug_print("status: incomplete values received (%d)\n", got_values);
2370 return IMAP_SUCCESS;
2373 static void imap_free_capabilities(IMAPSession *session)
2375 slist_free_strings(session->capability);
2376 g_slist_free(session->capability);
2377 session->capability = NULL;
2380 /* low-level IMAP4rev1 commands */
2383 static gint imap_cmd_authenticate(IMAPSession *session, const gchar *user,
2384 const gchar *pass, IMAPAuthType type)
2391 gchar hexdigest[33];
2395 auth_type = "CRAM-MD5";
2397 imap_gen_send(session, "AUTHENTICATE %s", auth_type);
2398 ok = imap_gen_recv(session, &buf);
2399 if (ok != IMAP_SUCCESS || buf[0] != '+' || buf[1] != ' ') {
2404 challenge = g_malloc(strlen(buf + 2) + 1);
2405 challenge_len = base64_decode(challenge, buf + 2, -1);
2406 challenge[challenge_len] = '\0';
2409 md5_hex_hmac(hexdigest, challenge, challenge_len, pass, strlen(pass));
2412 response = g_strdup_printf("%s %s", user, hexdigest);
2413 response64 = g_malloc((strlen(response) + 3) * 2 + 1);
2414 base64_encode(response64, response, strlen(response));
2417 sock_puts(SESSION(session)->sock, response64);
2418 ok = imap_cmd_ok(session, NULL);
2419 if (ok != IMAP_SUCCESS)
2420 log_warning(_("IMAP4 authentication failed.\n"));
2426 static gint imap_cmd_login(IMAPSession *session,
2427 const gchar *user, const gchar *pass,
2433 log_print("IMAP4> Logging %s to %s using %s\n",
2435 SESSION(session)->server,
2437 r = imap_threaded_login(session->folder, user, pass, type);
2438 if (r != MAILIMAP_NO_ERROR) {
2439 log_error("IMAP4< Error logging in to %s\n",
2440 SESSION(session)->server);
2448 static gint imap_cmd_logout(IMAPSession *session)
2450 imap_threaded_disconnect(session->folder);
2452 return IMAP_SUCCESS;
2455 static gint imap_cmd_noop(IMAPSession *session)
2458 unsigned int exists;
2460 r = imap_threaded_noop(session->folder, &exists);
2461 if (r != MAILIMAP_NO_ERROR) {
2462 debug_print("noop err %d\n", r);
2465 session->exists = exists;
2466 session_set_access_time(SESSION(session));
2468 return IMAP_SUCCESS;
2472 static gint imap_cmd_starttls(IMAPSession *session)
2476 r = imap_threaded_starttls(session->folder);
2477 if (r != MAILIMAP_NO_ERROR) {
2478 debug_print("starttls err %d\n", r);
2481 return IMAP_SUCCESS;
2485 static gint imap_cmd_select(IMAPSession *session, const gchar *folder,
2486 gint *exists, gint *recent, gint *unseen,
2487 guint32 *uid_validity, gboolean block)
2491 r = imap_threaded_select(session->folder, folder,
2492 exists, recent, unseen, uid_validity);
2493 if (r != MAILIMAP_NO_ERROR) {
2494 debug_print("select err %d\n", r);
2497 return IMAP_SUCCESS;
2500 static gint imap_cmd_examine(IMAPSession *session, const gchar *folder,
2501 gint *exists, gint *recent, gint *unseen,
2502 guint32 *uid_validity, gboolean block)
2506 r = imap_threaded_examine(session->folder, folder,
2507 exists, recent, unseen, uid_validity);
2508 if (r != MAILIMAP_NO_ERROR) {
2509 debug_print("examine err %d\n", r);
2513 return IMAP_SUCCESS;
2516 static gint imap_cmd_create(IMAPSession *session, const gchar *folder)
2520 r = imap_threaded_create(session->folder, folder);
2521 if (r != MAILIMAP_NO_ERROR) {
2526 return IMAP_SUCCESS;
2529 static gint imap_cmd_rename(IMAPSession *session, const gchar *old_folder,
2530 const gchar *new_folder)
2534 r = imap_threaded_rename(session->folder, old_folder,
2536 if (r != MAILIMAP_NO_ERROR) {
2541 return IMAP_SUCCESS;
2544 static gint imap_cmd_delete(IMAPSession *session, const gchar *folder)
2549 r = imap_threaded_delete(session->folder, folder);
2550 if (r != MAILIMAP_NO_ERROR) {
2555 return IMAP_SUCCESS;
2558 typedef struct _fetch_data {
2559 IMAPSession *session;
2561 const gchar *filename;
2567 static void *imap_cmd_fetch_thread(void *data)
2569 fetch_data *stuff = (fetch_data *)data;
2570 IMAPSession *session = stuff->session;
2571 guint32 uid = stuff->uid;
2572 const gchar *filename = stuff->filename;
2576 r = imap_threaded_fetch_content(session->folder,
2580 r = imap_threaded_fetch_content(session->folder,
2583 if (r != MAILIMAP_NO_ERROR) {
2584 debug_print("fetch err %d\n", r);
2585 return GINT_TO_POINTER(IMAP_ERROR);
2587 return GINT_TO_POINTER(IMAP_SUCCESS);
2590 static gint imap_cmd_fetch(IMAPSession *session, guint32 uid,
2591 const gchar *filename, gboolean headers,
2594 fetch_data *data = g_new0(fetch_data, 1);
2597 data->session = session;
2599 data->filename = filename;
2600 data->headers = headers;
2603 if (prefs_common.work_offline && !inc_offline_should_override()) {
2607 statusbar_print_all(_("Fetching message..."));
2608 result = GPOINTER_TO_INT(imap_cmd_fetch_thread(data));
2609 statusbar_pop_all();
2615 static gint imap_cmd_append(IMAPSession *session, const gchar *destfolder,
2616 const gchar *file, IMAPFlags flags,
2619 struct mailimap_flag_list * flag_list;
2622 g_return_val_if_fail(file != NULL, IMAP_ERROR);
2624 flag_list = imap_flag_to_lep(flags);
2625 r = imap_threaded_append(session->folder, destfolder,
2627 if (new_uid != NULL)
2630 if (r != MAILIMAP_NO_ERROR) {
2631 debug_print("append err %d\n", r);
2634 return IMAP_SUCCESS;
2637 static gint imap_cmd_copy(IMAPSession *session, struct mailimap_set * set,
2638 const gchar *destfolder, GRelation *uid_mapping)
2642 g_return_val_if_fail(session != NULL, IMAP_ERROR);
2643 g_return_val_if_fail(set != NULL, IMAP_ERROR);
2644 g_return_val_if_fail(destfolder != NULL, IMAP_ERROR);
2646 r = imap_threaded_copy(session->folder, set, destfolder);
2647 if (r != MAILIMAP_NO_ERROR) {
2652 return IMAP_SUCCESS;
2655 static gint imap_cmd_store(IMAPSession *session, struct mailimap_set * set,
2656 IMAPFlags flags, int do_add)
2659 struct mailimap_flag_list * flag_list;
2660 struct mailimap_store_att_flags * store_att_flags;
2662 flag_list = imap_flag_to_lep(flags);
2666 mailimap_store_att_flags_new_add_flags_silent(flag_list);
2669 mailimap_store_att_flags_new_remove_flags_silent(flag_list);
2671 r = imap_threaded_store(session->folder, set, store_att_flags);
2672 if (r != MAILIMAP_NO_ERROR) {
2677 return IMAP_SUCCESS;
2680 static gint imap_cmd_expunge(IMAPSession *session)
2684 if (prefs_common.work_offline && !inc_offline_should_override()) {
2688 r = imap_threaded_expunge(session->folder);
2689 if (r != MAILIMAP_NO_ERROR) {
2694 return IMAP_SUCCESS;
2697 static void imap_path_separator_subst(gchar *str, gchar separator)
2700 gboolean in_escape = FALSE;
2702 if (!separator || separator == '/') return;
2704 for (p = str; *p != '\0'; p++) {
2705 if (*p == '/' && !in_escape)
2707 else if (*p == '&' && *(p + 1) != '-' && !in_escape)
2709 else if (*p == '-' && in_escape)
2714 static gchar *imap_modified_utf7_to_utf8(const gchar *mutf7_str)
2716 static iconv_t cd = (iconv_t)-1;
2717 static gboolean iconv_ok = TRUE;
2720 size_t norm_utf7_len;
2722 gchar *to_str, *to_p;
2724 gboolean in_escape = FALSE;
2726 if (!iconv_ok) return g_strdup(mutf7_str);
2728 if (cd == (iconv_t)-1) {
2729 cd = iconv_open(CS_INTERNAL, CS_UTF_7);
2730 if (cd == (iconv_t)-1) {
2731 g_warning("iconv cannot convert UTF-7 to %s\n",
2734 return g_strdup(mutf7_str);
2738 /* modified UTF-7 to normal UTF-7 conversion */
2739 norm_utf7 = g_string_new(NULL);
2741 for (p = mutf7_str; *p != '\0'; p++) {
2742 /* replace: '&' -> '+',
2744 escaped ',' -> '/' */
2745 if (!in_escape && *p == '&') {
2746 if (*(p + 1) != '-') {
2747 g_string_append_c(norm_utf7, '+');
2750 g_string_append_c(norm_utf7, '&');
2753 } else if (in_escape && *p == ',') {
2754 g_string_append_c(norm_utf7, '/');
2755 } else if (in_escape && *p == '-') {
2756 g_string_append_c(norm_utf7, '-');
2759 g_string_append_c(norm_utf7, *p);
2763 norm_utf7_p = norm_utf7->str;
2764 norm_utf7_len = norm_utf7->len;
2765 to_len = strlen(mutf7_str) * 5;
2766 to_p = to_str = g_malloc(to_len + 1);
2768 if (iconv(cd, (ICONV_CONST gchar **)&norm_utf7_p, &norm_utf7_len,
2769 &to_p, &to_len) == -1) {
2770 g_warning(_("iconv cannot convert UTF-7 to %s\n"),
2771 conv_get_locale_charset_str());
2772 g_string_free(norm_utf7, TRUE);
2774 return g_strdup(mutf7_str);
2777 /* second iconv() call for flushing */
2778 iconv(cd, NULL, NULL, &to_p, &to_len);
2779 g_string_free(norm_utf7, TRUE);
2785 static gchar *imap_utf8_to_modified_utf7(const gchar *from)
2787 static iconv_t cd = (iconv_t)-1;
2788 static gboolean iconv_ok = TRUE;
2789 gchar *norm_utf7, *norm_utf7_p;
2790 size_t from_len, norm_utf7_len;
2792 gchar *from_tmp, *to, *p;
2793 gboolean in_escape = FALSE;
2795 if (!iconv_ok) return g_strdup(from);
2797 if (cd == (iconv_t)-1) {
2798 cd = iconv_open(CS_UTF_7, CS_INTERNAL);
2799 if (cd == (iconv_t)-1) {
2800 g_warning(_("iconv cannot convert %s to UTF-7\n"),
2803 return g_strdup(from);
2807 /* UTF-8 to normal UTF-7 conversion */
2808 Xstrdup_a(from_tmp, from, return g_strdup(from));
2809 from_len = strlen(from);
2810 norm_utf7_len = from_len * 5;
2811 Xalloca(norm_utf7, norm_utf7_len + 1, return g_strdup(from));
2812 norm_utf7_p = norm_utf7;
2814 #define IS_PRINT(ch) (isprint(ch) && IS_ASCII(ch))
2816 while (from_len > 0) {
2817 if (*from_tmp == '+') {
2818 *norm_utf7_p++ = '+';
2819 *norm_utf7_p++ = '-';
2823 } else if (IS_PRINT(*(guchar *)from_tmp)) {
2824 /* printable ascii char */
2825 *norm_utf7_p = *from_tmp;
2831 size_t conv_len = 0;
2833 /* unprintable char: convert to UTF-7 */
2835 while (!IS_PRINT(*(guchar *)p) && conv_len < from_len) {
2836 conv_len += g_utf8_skip[*(guchar *)p];
2837 p += g_utf8_skip[*(guchar *)p];
2840 from_len -= conv_len;
2841 if (iconv(cd, (ICONV_CONST gchar **)&from_tmp,
2843 &norm_utf7_p, &norm_utf7_len) == -1) {
2844 g_warning(_("iconv cannot convert UTF-8 to UTF-7\n"));
2845 return g_strdup(from);
2848 /* second iconv() call for flushing */
2849 iconv(cd, NULL, NULL, &norm_utf7_p, &norm_utf7_len);
2855 *norm_utf7_p = '\0';
2856 to_str = g_string_new(NULL);
2857 for (p = norm_utf7; p < norm_utf7_p; p++) {
2858 /* replace: '&' -> "&-",
2861 BASE64 '/' -> ',' */
2862 if (!in_escape && *p == '&') {
2863 g_string_append(to_str, "&-");
2864 } else if (!in_escape && *p == '+') {
2865 if (*(p + 1) == '-') {
2866 g_string_append_c(to_str, '+');
2869 g_string_append_c(to_str, '&');
2872 } else if (in_escape && *p == '/') {
2873 g_string_append_c(to_str, ',');
2874 } else if (in_escape && *p == '-') {
2875 g_string_append_c(to_str, '-');
2878 g_string_append_c(to_str, *p);
2884 g_string_append_c(to_str, '-');
2888 g_string_free(to_str, FALSE);
2893 static gboolean imap_rename_folder_func(GNode *node, gpointer data)
2895 FolderItem *item = node->data;
2896 gchar **paths = data;
2897 const gchar *oldpath = paths[0];
2898 const gchar *newpath = paths[1];
2900 gchar *new_itempath;
2903 oldpathlen = strlen(oldpath);
2904 if (strncmp(oldpath, item->path, oldpathlen) != 0) {
2905 g_warning("path doesn't match: %s, %s\n", oldpath, item->path);
2909 base = item->path + oldpathlen;
2910 while (*base == G_DIR_SEPARATOR) base++;
2912 new_itempath = g_strdup(newpath);
2914 new_itempath = g_strconcat(newpath, G_DIR_SEPARATOR_S, base,
2917 item->path = new_itempath;
2922 typedef struct _get_list_uid_data {
2924 IMAPFolderItem *item;
2925 GSList **msgnum_list;
2927 } get_list_uid_data;
2929 static void *get_list_of_uids_thread(void *data)
2931 get_list_uid_data *stuff = (get_list_uid_data *)data;
2932 Folder *folder = stuff->folder;
2933 IMAPFolderItem *item = stuff->item;
2934 GSList **msgnum_list = stuff->msgnum_list;
2935 gint ok, nummsgs = 0, lastuid_old;
2936 IMAPSession *session;
2937 GSList *uidlist, *elem;
2938 struct mailimap_set * set;
2939 clist * lep_uidlist;
2942 session = imap_session_get(folder);
2943 if (session == NULL) {
2945 return GINT_TO_POINTER(-1);
2948 ok = imap_select(session, IMAP_FOLDER(folder), item->item.path,
2949 NULL, NULL, NULL, NULL, TRUE);
2950 if (ok != IMAP_SUCCESS) {
2952 return GINT_TO_POINTER(-1);
2957 set = mailimap_set_new_interval(item->lastuid + 1, 0);
2959 r = imap_threaded_search(folder, IMAP_SEARCH_TYPE_SIMPLE, set,
2961 if (r == MAILIMAP_NO_ERROR) {
2962 GSList * fetchuid_list;
2965 imap_uid_list_from_lep(lep_uidlist);
2966 uidlist = g_slist_concat(fetchuid_list, uidlist);
2969 GSList * fetchuid_list;
2970 carray * lep_uidtab;
2972 r = imap_threaded_fetch_uid(folder, item->lastuid + 1,
2974 if (r == MAILIMAP_NO_ERROR) {
2976 imap_uid_list_from_lep_tab(lep_uidtab);
2977 uidlist = g_slist_concat(fetchuid_list, uidlist);
2981 lastuid_old = item->lastuid;
2982 *msgnum_list = g_slist_copy(item->uid_list);
2983 nummsgs = g_slist_length(*msgnum_list);
2984 debug_print("Got %d uids from cache\n", g_slist_length(item->uid_list));
2986 for (elem = uidlist; elem != NULL; elem = g_slist_next(elem)) {
2989 msgnum = GPOINTER_TO_INT(elem->data);
2990 if (msgnum > lastuid_old) {
2991 *msgnum_list = g_slist_prepend(*msgnum_list, GINT_TO_POINTER(msgnum));
2992 item->uid_list = g_slist_prepend(item->uid_list, GINT_TO_POINTER(msgnum));
2995 if(msgnum > item->lastuid)
2996 item->lastuid = msgnum;
2999 g_slist_free(uidlist);
3002 return GINT_TO_POINTER(nummsgs);
3005 static gint get_list_of_uids(Folder *folder, IMAPFolderItem *item, GSList **msgnum_list)
3008 get_list_uid_data *data = g_new0(get_list_uid_data, 1);
3010 data->folder = folder;
3012 data->msgnum_list = msgnum_list;
3014 if (prefs_common.work_offline && !inc_offline_should_override()) {
3019 result = GPOINTER_TO_INT(get_list_of_uids_thread(data));
3025 gint imap_get_num_list(Folder *folder, FolderItem *_item, GSList **msgnum_list, gboolean *old_uids_valid)
3027 IMAPFolderItem *item = (IMAPFolderItem *)_item;
3028 IMAPSession *session;
3029 gint ok, nummsgs = 0, exists, uid_val, uid_next;
3030 GSList *uidlist = NULL;
3032 gboolean selected_folder;
3034 debug_print("get_num_list\n");
3036 g_return_val_if_fail(folder != NULL, -1);
3037 g_return_val_if_fail(item != NULL, -1);
3038 g_return_val_if_fail(item->item.path != NULL, -1);
3039 g_return_val_if_fail(FOLDER_CLASS(folder) == &imap_class, -1);
3040 g_return_val_if_fail(folder->account != NULL, -1);
3042 session = imap_session_get(folder);
3043 g_return_val_if_fail(session != NULL, -1);
3045 statusbar_print_all("Scanning %s...\n", FOLDER_ITEM(item)->path
3046 ? FOLDER_ITEM(item)->path:"");
3048 selected_folder = (session->mbox != NULL) &&
3049 (!strcmp(session->mbox, item->item.path));
3050 if (selected_folder) {
3051 ok = imap_cmd_noop(session);
3052 if (ok != IMAP_SUCCESS) {
3053 debug_print("disconnected!\n");
3054 session = imap_reconnect_if_possible(folder, session);
3055 if (session == NULL) {
3056 statusbar_pop_all();
3060 exists = session->exists;
3062 *old_uids_valid = TRUE;
3064 if (item->use_cache && time(NULL) - item->use_cache < 2) {
3065 exists = item->c_messages;
3066 uid_next = item->c_uid_next;
3067 uid_val = item->c_uid_validity;
3069 debug_print("using cache %d %d %d\n", exists, uid_next, uid_val);
3071 ok = imap_status(session, IMAP_FOLDER(folder), item->item.path, item,
3072 &exists, &uid_next, &uid_val, NULL, FALSE);
3074 item->use_cache = (time_t)0;
3075 if (ok != IMAP_SUCCESS) {
3076 statusbar_pop_all();
3079 if(item->item.mtime == uid_val)
3080 *old_uids_valid = TRUE;
3082 *old_uids_valid = FALSE;
3084 debug_print("Freeing imap uid cache\n");
3086 g_slist_free(item->uid_list);
3087 item->uid_list = NULL;
3089 item->item.mtime = uid_val;
3091 imap_delete_all_cached_messages((FolderItem *)item);
3095 if (!selected_folder)
3096 item->uid_next = uid_next;
3098 /* If old uid_next matches new uid_next we can be sure no message
3099 was added to the folder */
3100 if (( selected_folder && !session->folder_content_changed) ||
3101 (!selected_folder && uid_next == item->uid_next)) {
3102 nummsgs = g_slist_length(item->uid_list);
3104 /* If number of messages is still the same we
3105 know our caches message numbers are still valid,
3106 otherwise if the number of messages has decrease
3107 we discard our cache to start a new scan to find
3108 out which numbers have been removed */
3109 if (exists == nummsgs) {
3110 *msgnum_list = g_slist_copy(item->uid_list);
3111 statusbar_pop_all();
3113 } else if (exists < nummsgs) {
3114 debug_print("Freeing imap uid cache");
3116 g_slist_free(item->uid_list);
3117 item->uid_list = NULL;
3122 *msgnum_list = NULL;
3123 statusbar_pop_all();
3127 nummsgs = get_list_of_uids(folder, item, &uidlist);
3130 statusbar_pop_all();
3134 if (nummsgs != exists) {
3135 /* Cache contains more messages then folder, we have cached
3136 an old UID of a message that was removed and new messages
3137 have been added too, otherwise the uid_next check would
3139 debug_print("Freeing imap uid cache");
3141 g_slist_free(item->uid_list);
3142 item->uid_list = NULL;
3144 g_slist_free(*msgnum_list);
3146 nummsgs = get_list_of_uids(folder, item, &uidlist);
3149 *msgnum_list = uidlist;
3151 dir = folder_item_get_path((FolderItem *)item);
3152 debug_print("removing old messages from %s\n", dir);
3153 remove_numbered_files_not_in_list(dir, *msgnum_list);
3156 debug_print("get_num_list - ok - %i\n", nummsgs);
3157 statusbar_pop_all();
3161 static MsgInfo *imap_parse_msg(const gchar *file, FolderItem *item)
3166 flags.perm_flags = MSG_NEW|MSG_UNREAD;
3167 flags.tmp_flags = 0;
3169 g_return_val_if_fail(item != NULL, NULL);
3170 g_return_val_if_fail(file != NULL, NULL);
3172 if (folder_has_parent_of_type(item, F_QUEUE)) {
3173 MSG_SET_TMP_FLAGS(flags, MSG_QUEUED);
3174 } else if (folder_has_parent_of_type(item, F_DRAFT)) {
3175 MSG_SET_TMP_FLAGS(flags, MSG_DRAFT);
3178 msginfo = procheader_parse_file(file, flags, FALSE, FALSE);
3179 if (!msginfo) return NULL;
3181 msginfo->plaintext_file = g_strdup(file);
3182 msginfo->folder = item;
3187 GSList *imap_get_msginfos(Folder *folder, FolderItem *item,
3188 GSList *msgnum_list)
3190 IMAPSession *session;
3191 MsgInfoList *ret = NULL;
3194 debug_print("get_msginfos\n");
3196 g_return_val_if_fail(folder != NULL, NULL);
3197 g_return_val_if_fail(item != NULL, NULL);
3198 g_return_val_if_fail(msgnum_list != NULL, NULL);
3200 session = imap_session_get(folder);
3201 g_return_val_if_fail(session != NULL, NULL);
3203 debug_print("IMAP getting msginfos\n");
3204 ok = imap_select(session, IMAP_FOLDER(folder), item->path,
3205 NULL, NULL, NULL, NULL, FALSE);
3206 if (ok != IMAP_SUCCESS)
3209 if (!(folder_has_parent_of_type(item, F_DRAFT) ||
3210 folder_has_parent_of_type(item, F_QUEUE))) {
3211 ret = g_slist_concat(ret,
3212 imap_get_uncached_messages(session, item,
3215 MsgNumberList *sorted_list, *elem;
3216 gint startnum, lastnum;
3218 sorted_list = g_slist_sort(g_slist_copy(msgnum_list), g_int_compare);
3220 startnum = lastnum = GPOINTER_TO_INT(sorted_list->data);
3222 for (elem = sorted_list;; elem = g_slist_next(elem)) {
3226 num = GPOINTER_TO_INT(elem->data);
3228 if (num > lastnum + 1 || elem == NULL) {
3230 for (i = startnum; i <= lastnum; ++i) {
3233 file = imap_fetch_msg(folder, item, i);
3235 MsgInfo *msginfo = imap_parse_msg(file, item);
3236 if (msginfo != NULL) {
3237 msginfo->msgnum = i;
3238 ret = g_slist_append(ret, msginfo);
3252 g_slist_free(sorted_list);
3258 MsgInfo *imap_get_msginfo(Folder *folder, FolderItem *item, gint uid)
3260 MsgInfo *msginfo = NULL;
3261 MsgInfoList *msginfolist;
3262 MsgNumberList numlist;
3264 numlist.next = NULL;
3265 numlist.data = GINT_TO_POINTER(uid);
3267 msginfolist = imap_get_msginfos(folder, item, &numlist);
3268 if (msginfolist != NULL) {
3269 msginfo = msginfolist->data;
3270 g_slist_free(msginfolist);
3276 gboolean imap_scan_required(Folder *folder, FolderItem *_item)
3278 IMAPSession *session;
3279 IMAPFolderItem *item = (IMAPFolderItem *)_item;
3280 gint ok, exists = 0, unseen = 0;
3281 guint32 uid_next, uid_val;
3282 gboolean selected_folder;
3284 g_return_val_if_fail(folder != NULL, FALSE);
3285 g_return_val_if_fail(item != NULL, FALSE);
3286 g_return_val_if_fail(item->item.folder != NULL, FALSE);
3287 g_return_val_if_fail(FOLDER_CLASS(item->item.folder) == &imap_class, FALSE);
3289 if (item->item.path == NULL)
3292 session = imap_session_get(folder);
3293 g_return_val_if_fail(session != NULL, FALSE);
3295 selected_folder = (session->mbox != NULL) &&
3296 (!strcmp(session->mbox, item->item.path));
3297 if (selected_folder) {
3298 ok = imap_cmd_noop(session);
3299 if (ok != IMAP_SUCCESS) {
3300 debug_print("disconnected!\n");
3301 session = imap_reconnect_if_possible(folder, session);
3302 if (session == NULL)
3306 if (session->folder_content_changed
3307 || session->exists != item->item.total_msgs)
3310 ok = imap_status(session, IMAP_FOLDER(folder), item->item.path, IMAP_FOLDER_ITEM(item),
3311 &exists, &uid_next, &uid_val, &unseen, FALSE);
3312 if (ok != IMAP_SUCCESS)
3315 item->use_cache = time(NULL);
3316 item->c_messages = exists;
3317 item->c_uid_next = uid_next;
3318 item->c_uid_validity = uid_val;
3319 item->c_unseen = unseen;
3321 if ((uid_next != item->uid_next) || (exists != item->item.total_msgs))
3328 void imap_change_flags(Folder *folder, FolderItem *item, MsgInfo *msginfo, MsgPermFlags newflags)
3330 IMAPSession *session;
3331 IMAPFlags flags_set = 0, flags_unset = 0;
3332 gint ok = IMAP_SUCCESS;
3333 MsgNumberList numlist;
3334 hashtable_data *ht_data = NULL;
3336 g_return_if_fail(folder != NULL);
3337 g_return_if_fail(folder->klass == &imap_class);
3338 g_return_if_fail(item != NULL);
3339 g_return_if_fail(item->folder == folder);
3340 g_return_if_fail(msginfo != NULL);
3341 g_return_if_fail(msginfo->folder == item);
3343 session = imap_session_get(folder);
3347 if ((ok = imap_select(session, IMAP_FOLDER(folder), msginfo->folder->path,
3348 NULL, NULL, NULL, NULL, FALSE)) != IMAP_SUCCESS) {
3352 if (!MSG_IS_MARKED(msginfo->flags) && (newflags & MSG_MARKED))
3353 flags_set |= IMAP_FLAG_FLAGGED;
3354 if ( MSG_IS_MARKED(msginfo->flags) && !(newflags & MSG_MARKED))
3355 flags_unset |= IMAP_FLAG_FLAGGED;
3357 if (!MSG_IS_UNREAD(msginfo->flags) && (newflags & MSG_UNREAD))
3358 flags_unset |= IMAP_FLAG_SEEN;
3359 if ( MSG_IS_UNREAD(msginfo->flags) && !(newflags & MSG_UNREAD))
3360 flags_set |= IMAP_FLAG_SEEN;
3362 if (!MSG_IS_REPLIED(msginfo->flags) && (newflags & MSG_REPLIED))
3363 flags_set |= IMAP_FLAG_ANSWERED;
3364 if ( MSG_IS_REPLIED(msginfo->flags) && !(newflags & MSG_REPLIED))
3365 flags_unset |= IMAP_FLAG_ANSWERED;
3367 if (!MSG_IS_DELETED(msginfo->flags) && (newflags & MSG_DELETED))
3368 flags_set |= IMAP_FLAG_DELETED;
3369 if ( MSG_IS_DELETED(msginfo->flags) && !(newflags & MSG_DELETED))
3370 flags_unset |= IMAP_FLAG_DELETED;
3372 numlist.next = NULL;
3373 numlist.data = GINT_TO_POINTER(msginfo->msgnum);
3375 if (IMAP_FOLDER_ITEM(item)->batching) {
3376 /* instead of performing an UID STORE command for each message change,
3377 * as a lot of them can change "together", we just fill in hashtables
3378 * and defer the treatment so that we're able to send only one
3381 debug_print("IMAP batch mode on, deferring flags change\n");
3383 ht_data = g_hash_table_lookup(flags_set_table, GINT_TO_POINTER(flags_set));
3384 if (ht_data == NULL) {
3385 ht_data = g_new0(hashtable_data, 1);
3386 ht_data->session = session;
3387 g_hash_table_insert(flags_set_table, GINT_TO_POINTER(flags_set), ht_data);
3389 if (!g_slist_find(ht_data->msglist, GINT_TO_POINTER(msginfo->msgnum)))
3390 ht_data->msglist = g_slist_prepend(ht_data->msglist, GINT_TO_POINTER(msginfo->msgnum));
3393 ht_data = g_hash_table_lookup(flags_unset_table, GINT_TO_POINTER(flags_unset));
3394 if (ht_data == NULL) {
3395 ht_data = g_new0(hashtable_data, 1);
3396 ht_data->session = session;
3397 g_hash_table_insert(flags_unset_table, GINT_TO_POINTER(flags_unset), ht_data);
3399 if (!g_slist_find(ht_data->msglist, GINT_TO_POINTER(msginfo->msgnum)))
3400 ht_data->msglist = g_slist_prepend(ht_data->msglist, GINT_TO_POINTER(msginfo->msgnum));
3403 debug_print("IMAP changing flags\n");
3405 ok = imap_set_message_flags(session, &numlist, flags_set, TRUE);
3406 if (ok != IMAP_SUCCESS) {
3412 ok = imap_set_message_flags(session, &numlist, flags_unset, FALSE);
3413 if (ok != IMAP_SUCCESS) {
3418 msginfo->flags.perm_flags = newflags;
3423 static gint imap_remove_msg(Folder *folder, FolderItem *item, gint uid)
3426 IMAPSession *session;
3428 MsgNumberList numlist;
3430 g_return_val_if_fail(folder != NULL, -1);
3431 g_return_val_if_fail(FOLDER_CLASS(folder) == &imap_class, -1);
3432 g_return_val_if_fail(item != NULL, -1);
3434 session = imap_session_get(folder);
3435 if (!session) return -1;
3437 ok = imap_select(session, IMAP_FOLDER(folder), item->path,
3438 NULL, NULL, NULL, NULL, FALSE);
3439 if (ok != IMAP_SUCCESS)
3442 numlist.next = NULL;
3443 numlist.data = GINT_TO_POINTER(uid);
3445 ok = imap_set_message_flags
3446 (IMAP_SESSION(REMOTE_FOLDER(folder)->session),
3447 &numlist, IMAP_FLAG_DELETED, TRUE);
3448 if (ok != IMAP_SUCCESS) {
3449 log_warning(_("can't set deleted flags: %d\n"), uid);
3453 if (!session->uidplus) {
3454 ok = imap_cmd_expunge(session);
3458 uidstr = g_strdup_printf("%u", uid);
3459 ok = imap_cmd_expunge(session);
3462 if (ok != IMAP_SUCCESS) {
3463 log_warning(_("can't expunge\n"));
3467 IMAP_FOLDER_ITEM(item)->uid_list = g_slist_remove(
3468 IMAP_FOLDER_ITEM(item)->uid_list, numlist.data);
3469 dir = folder_item_get_path(item);
3470 if (is_dir_exist(dir))
3471 remove_numbered_files(dir, uid, uid);
3474 return IMAP_SUCCESS;
3477 static gint compare_msginfo(gconstpointer a, gconstpointer b)
3479 return ((MsgInfo *)a)->msgnum - ((MsgInfo *)b)->msgnum;
3482 static guint gslist_find_next_num(MsgNumberList **list, guint num)
3486 g_return_val_if_fail(list != NULL, -1);
3488 for (elem = *list; elem != NULL; elem = g_slist_next(elem))
3489 if (GPOINTER_TO_INT(elem->data) >= num)
3492 return elem != NULL ? GPOINTER_TO_INT(elem->data) : (gint)-1;
3496 * NEW and DELETED flags are not syncronized
3497 * - The NEW/RECENT flags in IMAP folders can not really be directly
3498 * modified by Sylpheed
3499 * - The DELETE/DELETED flag in IMAP and Sylpheed don't have the same
3500 * meaning, in IMAP it always removes the messages from the FolderItem
3501 * in Sylpheed it can mean to move the message to trash
3504 typedef struct _get_flags_data {
3507 MsgInfoList *msginfo_list;
3508 GRelation *msgflags;
3509 gboolean full_search;
3513 static /*gint*/ void *imap_get_flags_thread(void *data)
3515 get_flags_data *stuff = (get_flags_data *)data;
3516 Folder *folder = stuff->folder;
3517 FolderItem *item = stuff->item;
3518 MsgInfoList *msginfo_list = stuff->msginfo_list;
3519 GRelation *msgflags = stuff->msgflags;
3520 gboolean full_search = stuff->full_search;
3521 IMAPSession *session;
3522 GSList *sorted_list = NULL;
3523 GSList *unseen = NULL, *answered = NULL, *flagged = NULL, *deleted = NULL;
3524 GSList *p_unseen, *p_answered, *p_flagged, *p_deleted;
3526 GSList *seq_list, *cur;
3527 gboolean reverse_seen = FALSE;
3530 gint exists_cnt, unseen_cnt;
3531 gboolean selected_folder;
3533 if (folder == NULL || item == NULL) {
3535 return GINT_TO_POINTER(-1);
3538 session = imap_session_get(folder);
3539 if (session == NULL) {
3541 return GINT_TO_POINTER(-1);
3544 selected_folder = (session->mbox != NULL) &&
3545 (!strcmp(session->mbox, item->path));
3547 if (!selected_folder) {
3548 ok = imap_select(session, IMAP_FOLDER(folder), item->path,
3549 &exists_cnt, NULL, &unseen_cnt, NULL, TRUE);
3550 if (ok != IMAP_SUCCESS) {
3552 return GINT_TO_POINTER(-1);
3555 if (unseen_cnt > exists_cnt / 2)
3556 reverse_seen = TRUE;
3559 if (item->unread_msgs > item->total_msgs / 2)
3560 reverse_seen = TRUE;
3563 cmd_buf = g_string_new(NULL);
3565 sorted_list = g_slist_sort(g_slist_copy(msginfo_list), compare_msginfo);
3567 seq_list = imap_get_lep_set_from_msglist(msginfo_list);
3569 struct mailimap_set * set;
3570 set = mailimap_set_new_interval(1, 0);
3571 seq_list = g_slist_append(NULL, set);
3574 for (cur = seq_list; cur != NULL; cur = g_slist_next(cur)) {
3575 struct mailimap_set * imapset;
3576 clist * lep_uidlist;
3579 imapset = cur->data;
3581 r = imap_threaded_search(folder, IMAP_SEARCH_TYPE_SEEN,
3582 imapset, &lep_uidlist);
3585 r = imap_threaded_search(folder,
3586 IMAP_SEARCH_TYPE_UNSEEN,
3587 imapset, &lep_uidlist);
3589 if (r == MAILIMAP_NO_ERROR) {
3592 uidlist = imap_uid_list_from_lep(lep_uidlist);
3593 mailimap_search_result_free(lep_uidlist);
3595 unseen = g_slist_concat(unseen, uidlist);
3598 r = imap_threaded_search(folder, IMAP_SEARCH_TYPE_ANSWERED,
3599 imapset, &lep_uidlist);
3600 if (r == MAILIMAP_NO_ERROR) {
3603 uidlist = imap_uid_list_from_lep(lep_uidlist);
3604 mailimap_search_result_free(lep_uidlist);
3606 answered = g_slist_concat(answered, uidlist);
3609 r = imap_threaded_search(folder, IMAP_SEARCH_TYPE_FLAGGED,
3610 imapset, &lep_uidlist);
3611 if (r == MAILIMAP_NO_ERROR) {
3614 uidlist = imap_uid_list_from_lep(lep_uidlist);
3615 mailimap_search_result_free(lep_uidlist);
3617 flagged = g_slist_concat(flagged, uidlist);
3620 r = imap_threaded_search(folder, IMAP_SEARCH_TYPE_DELETED,
3621 imapset, &lep_uidlist);
3622 if (r == MAILIMAP_NO_ERROR) {
3625 uidlist = imap_uid_list_from_lep(lep_uidlist);
3626 mailimap_search_result_free(lep_uidlist);
3628 deleted = g_slist_concat(deleted, uidlist);
3633 p_answered = answered;
3634 p_flagged = flagged;
3635 p_deleted = deleted;
3637 for (elem = sorted_list; elem != NULL; elem = g_slist_next(elem)) {
3642 msginfo = (MsgInfo *) elem->data;
3643 flags = msginfo->flags.perm_flags;
3644 wasnew = (flags & MSG_NEW);
3645 flags &= ~((reverse_seen ? 0 : MSG_UNREAD | MSG_NEW) | MSG_REPLIED | MSG_MARKED);
3647 flags |= MSG_UNREAD | (wasnew ? MSG_NEW : 0);
3648 if (gslist_find_next_num(&p_unseen, msginfo->msgnum) == msginfo->msgnum) {
3649 if (!reverse_seen) {
3650 flags |= MSG_UNREAD | (wasnew ? MSG_NEW : 0);
3652 flags &= ~(MSG_UNREAD | MSG_NEW);
3655 if (gslist_find_next_num(&p_answered, msginfo->msgnum) == msginfo->msgnum)
3656 flags |= MSG_REPLIED;
3658 flags &= ~MSG_REPLIED;
3659 if (gslist_find_next_num(&p_flagged, msginfo->msgnum) == msginfo->msgnum)
3660 flags |= MSG_MARKED;
3662 flags &= ~MSG_MARKED;
3663 if (gslist_find_next_num(&p_deleted, msginfo->msgnum) == msginfo->msgnum)
3664 flags |= MSG_DELETED;
3666 flags &= ~MSG_DELETED;
3667 g_relation_insert(msgflags, msginfo, GINT_TO_POINTER(flags));
3670 imap_lep_set_free(seq_list);
3671 g_slist_free(flagged);
3672 g_slist_free(deleted);
3673 g_slist_free(answered);
3674 g_slist_free(unseen);
3675 g_slist_free(sorted_list);
3676 g_string_free(cmd_buf, TRUE);
3679 return GINT_TO_POINTER(0);
3682 static gint imap_get_flags(Folder *folder, FolderItem *item,
3683 MsgInfoList *msginfo_list, GRelation *msgflags)
3686 get_flags_data *data = g_new0(get_flags_data, 1);
3688 data->folder = folder;
3690 data->msginfo_list = msginfo_list;
3691 data->msgflags = msgflags;
3692 data->full_search = FALSE;
3694 GSList *tmp = NULL, *cur;
3696 if (prefs_common.work_offline && !inc_offline_should_override()) {
3701 tmp = folder_item_get_msg_list(item);
3703 if (g_slist_length(tmp) == g_slist_length(msginfo_list))
3704 data->full_search = TRUE;
3706 for (cur = tmp; cur; cur = cur->next)
3707 procmsg_msginfo_free((MsgInfo *)cur->data);
3711 result = GPOINTER_TO_INT(imap_get_flags_thread(data));
3718 static gboolean process_flags(gpointer key, gpointer value, gpointer user_data)
3720 gboolean flags_set = GPOINTER_TO_INT(user_data);
3721 gint flags_value = GPOINTER_TO_INT(key);
3722 hashtable_data *data = (hashtable_data *)value;
3724 data->msglist = g_slist_reverse(data->msglist);
3726 debug_print("IMAP %ssetting flags to %d for %d messages\n",
3729 g_slist_length(data->msglist));
3730 imap_set_message_flags(data->session, data->msglist, flags_value, flags_set);
3732 g_slist_free(data->msglist);
3737 static void process_hashtable(void)
3739 if (flags_set_table) {
3740 g_hash_table_foreach_remove(flags_set_table, process_flags, GINT_TO_POINTER(TRUE));
3741 g_free(flags_set_table);
3742 flags_set_table = NULL;
3744 if (flags_unset_table) {
3745 g_hash_table_foreach_remove(flags_unset_table, process_flags, GINT_TO_POINTER(FALSE));
3746 g_free(flags_unset_table);
3747 flags_unset_table = NULL;
3751 static IMAPFolderItem *batching_item = NULL;
3753 static void imap_set_batch (Folder *folder, FolderItem *_item, gboolean batch)
3755 IMAPFolderItem *item = (IMAPFolderItem *)_item;
3757 g_return_if_fail(item != NULL);
3759 if (batch && batching_item != NULL) {
3760 g_warning("already batching on %s\n", batching_item->item.path);
3764 if (item->batching == batch)
3767 item->batching = batch;
3769 batching_item = batch?item:NULL;
3772 debug_print("IMAP switching to batch mode\n");
3773 if (flags_set_table) {
3774 g_warning("flags_set_table non-null but we just entered batch mode!\n");
3775 flags_set_table = NULL;
3777 if (flags_unset_table) {
3778 g_warning("flags_unset_table non-null but we just entered batch mode!\n");
3779 flags_unset_table = NULL;
3781 flags_set_table = g_hash_table_new(NULL, g_direct_equal);
3782 flags_unset_table = g_hash_table_new(NULL, g_direct_equal);
3784 debug_print("IMAP switching away from batch mode\n");
3786 process_hashtable();
3792 /* data types conversion libetpan <-> sylpheed */
3796 #define ETPAN_IMAP_MB_MARKED 1
3797 #define ETPAN_IMAP_MB_UNMARKED 2
3798 #define ETPAN_IMAP_MB_NOSELECT 4
3799 #define ETPAN_IMAP_MB_NOINFERIORS 8
3801 static int imap_flags_to_flags(struct mailimap_mbx_list_flags * imap_flags)
3807 if (imap_flags->mbf_type == MAILIMAP_MBX_LIST_FLAGS_SFLAG) {
3808 switch (imap_flags->mbf_sflag) {
3809 case MAILIMAP_MBX_LIST_SFLAG_MARKED:
3810 flags |= ETPAN_IMAP_MB_MARKED;
3812 case MAILIMAP_MBX_LIST_SFLAG_NOSELECT:
3813 flags |= ETPAN_IMAP_MB_NOSELECT;
3815 case MAILIMAP_MBX_LIST_SFLAG_UNMARKED:
3816 flags |= ETPAN_IMAP_MB_UNMARKED;
3821 for(cur = clist_begin(imap_flags->mbf_oflags) ; cur != NULL ;
3822 cur = clist_next(cur)) {
3823 struct mailimap_mbx_list_oflag * oflag;
3825 oflag = clist_content(cur);
3827 switch (oflag->of_type) {
3828 case MAILIMAP_MBX_LIST_OFLAG_NOINFERIORS:
3829 flags |= ETPAN_IMAP_MB_NOINFERIORS;
3837 static GSList * imap_list_from_lep(IMAPFolder * folder,
3838 clist * list, const gchar * real_path, gboolean all)
3845 for(iter = clist_begin(list) ; iter != NULL ;
3846 iter = clist_next(iter)) {
3847 struct mailimap_mailbox_list * mb;
3855 FolderItem *new_item;
3857 mb = clist_content(iter);
3863 if (mb->mb_flag != NULL)
3864 flags = imap_flags_to_flags(mb->mb_flag);
3866 delimiter = mb->mb_delimiter;
3869 dup_name = strdup(name);
3870 if (delimiter != '\0')
3871 subst_char(dup_name, delimiter, '/');
3873 base = g_path_get_basename(dup_name);
3874 if (base[0] == '.') {
3880 if (!all && strcmp(dup_name, real_path) == 0) {
3886 if (!all && dup_name[strlen(dup_name)-1] == '/') {
3892 loc_name = imap_modified_utf7_to_utf8(base);
3893 loc_path = imap_modified_utf7_to_utf8(dup_name);
3895 new_item = folder_item_new(FOLDER(folder), loc_name, loc_path);
3896 if ((flags & ETPAN_IMAP_MB_NOINFERIORS) != 0)
3897 new_item->no_sub = TRUE;
3898 if (strcmp(dup_name, "INBOX") != 0 &&
3899 ((flags & ETPAN_IMAP_MB_NOSELECT) != 0))
3900 new_item->no_select = TRUE;
3902 item_list = g_slist_append(item_list, new_item);
3904 debug_print("folder '%s' found.\n", loc_path);
3915 static GSList * imap_get_lep_set_from_numlist(MsgNumberList *numlist)
3917 GSList *sorted_list, *cur;
3918 guint first, last, next;
3919 GSList *ret_list = NULL;
3921 struct mailimap_set * current_set;
3922 unsigned int item_count;
3924 if (numlist == NULL)
3928 current_set = mailimap_set_new_empty();
3930 sorted_list = g_slist_copy(numlist);
3931 sorted_list = g_slist_sort(sorted_list, g_int_compare);
3933 first = GPOINTER_TO_INT(sorted_list->data);
3936 for (cur = sorted_list; cur != NULL; cur = g_slist_next(cur)) {
3937 if (GPOINTER_TO_INT(cur->data) == 0)
3942 last = GPOINTER_TO_INT(cur->data);
3944 next = GPOINTER_TO_INT(cur->next->data);
3948 if (last + 1 != next || next == 0) {
3950 struct mailimap_set_item * item;
3951 item = mailimap_set_item_new(first, last);
3952 mailimap_set_add(current_set, item);
3957 if (count >= IMAP_SET_MAX_COUNT) {
3958 ret_list = g_slist_append(ret_list,
3960 current_set = mailimap_set_new_empty();
3967 if (clist_count(current_set->set_list) > 0) {
3968 ret_list = g_slist_append(ret_list,
3972 g_slist_free(sorted_list);
3977 static GSList * imap_get_lep_set_from_msglist(MsgInfoList *msglist)
3979 MsgNumberList *numlist = NULL;
3983 for (cur = msglist; cur != NULL; cur = g_slist_next(cur)) {
3984 MsgInfo *msginfo = (MsgInfo *) cur->data;
3986 numlist = g_slist_append(numlist, GINT_TO_POINTER(msginfo->msgnum));
3988 seq_list = imap_get_lep_set_from_numlist(numlist);
3989 g_slist_free(numlist);
3994 static GSList * imap_uid_list_from_lep(clist * list)
4001 for(iter = clist_begin(list) ; iter != NULL ;
4002 iter = clist_next(iter)) {
4005 puid = clist_content(iter);
4006 result = g_slist_append(result, GINT_TO_POINTER(* puid));
4012 static GSList * imap_uid_list_from_lep_tab(carray * list)
4019 for(i = 0 ; i < carray_count(list) ; i ++) {
4022 puid = carray_get(list, i);
4023 result = g_slist_append(result, GINT_TO_POINTER(* puid));
4029 static MsgInfo *imap_envelope_from_lep(struct imap_fetch_env_info * info,
4032 MsgInfo *msginfo = NULL;
4035 MsgFlags flags = {0, 0};
4037 MSG_SET_TMP_FLAGS(flags, MSG_IMAP);
4038 if (folder_has_parent_of_type(item, F_QUEUE)) {
4039 MSG_SET_TMP_FLAGS(flags, MSG_QUEUED);
4040 } else if (folder_has_parent_of_type(item, F_DRAFT)) {
4041 MSG_SET_TMP_FLAGS(flags, MSG_DRAFT);
4043 flags.perm_flags = info->flags;
4047 msginfo = procheader_parse_str(info->headers, flags, FALSE, FALSE);
4050 msginfo->msgnum = uid;
4051 msginfo->size = size;
4057 static void imap_lep_set_free(GSList *seq_list)
4061 for(cur = seq_list ; cur != NULL ; cur = g_slist_next(cur)) {
4062 struct mailimap_set * imapset;
4064 imapset = cur->data;
4065 mailimap_set_free(imapset);
4067 g_slist_free(seq_list);
4070 static struct mailimap_flag_list * imap_flag_to_lep(IMAPFlags flags)
4072 struct mailimap_flag_list * flag_list;
4074 flag_list = mailimap_flag_list_new_empty();
4076 if (IMAP_IS_SEEN(flags))
4077 mailimap_flag_list_add(flag_list,
4078 mailimap_flag_new_seen());
4079 if (IMAP_IS_ANSWERED(flags))
4080 mailimap_flag_list_add(flag_list,
4081 mailimap_flag_new_answered());
4082 if (IMAP_IS_FLAGGED(flags))
4083 mailimap_flag_list_add(flag_list,
4084 mailimap_flag_new_flagged());
4085 if (IMAP_IS_DELETED(flags))
4086 mailimap_flag_list_add(flag_list,
4087 mailimap_flag_new_deleted());
4088 if (IMAP_IS_DRAFT(flags))
4089 mailimap_flag_list_add(flag_list,
4090 mailimap_flag_new_draft());
4095 guint imap_folder_get_refcnt(Folder *folder)
4097 return ((IMAPFolder *)folder)->refcnt;
4100 void imap_folder_ref(Folder *folder)
4102 ((IMAPFolder *)folder)->refcnt++;
4105 void imap_folder_unref(Folder *folder)
4107 if (((IMAPFolder *)folder)->refcnt > 0)
4108 ((IMAPFolder *)folder)->refcnt--;
4111 #else /* HAVE_LIBETPAN */
4113 static FolderClass imap_class;
4115 static Folder *imap_folder_new (const gchar *name,
4120 static gint imap_create_tree (Folder *folder)
4124 static FolderItem *imap_create_folder (Folder *folder,
4130 static gint imap_rename_folder (Folder *folder,
4137 FolderClass *imap_get_class(void)
4139 if (imap_class.idstr == NULL) {
4140 imap_class.type = F_IMAP;
4141 imap_class.idstr = "imap";
4142 imap_class.uistr = "IMAP4";
4144 imap_class.new_folder = imap_folder_new;
4145 imap_class.create_tree = imap_create_tree;
4146 imap_class.create_folder = imap_create_folder;
4147 imap_class.rename_folder = imap_rename_folder;
4148 /* nothing implemented */
4155 void imap_synchronise(FolderItem *item)
4157 imap_gtk_synchronise(item);