2 * Sylpheed -- a GTK+ based, lightweight, and fast e-mail client
3 * Copyright (C) 1999-2006 Hiroyuki Yamamoto and the Sylpheed-Claws team
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation; either version 2 of the License, or
8 * (at your option) any later version.
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
15 * You should have received a copy of the GNU General Public License
16 * along with this program; if not, write to the Free Software
17 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
27 #include <glib/gi18n.h>
56 #include "procheader.h"
57 #include "prefs_account.h"
62 #include "prefs_common.h"
63 #include "inputdialog.h"
65 #include "remotefolder.h"
66 #include "alertpanel.h"
68 #include "statusbar.h"
70 #include "imap-thread.h"
72 typedef struct _IMAPFolder IMAPFolder;
73 typedef struct _IMAPSession IMAPSession;
74 typedef struct _IMAPNameSpace IMAPNameSpace;
75 typedef struct _IMAPFolderItem IMAPFolderItem;
77 #include "prefs_account.h"
79 #define IMAP_FOLDER(obj) ((IMAPFolder *)obj)
80 #define IMAP_FOLDER_ITEM(obj) ((IMAPFolderItem *)obj)
81 #define IMAP_SESSION(obj) ((IMAPSession *)obj)
87 /* list of IMAPNameSpace */
91 gchar last_seen_separator;
99 gboolean authenticated;
108 gboolean folder_content_changed;
114 struct _IMAPNameSpace
120 #define IMAP_SUCCESS 0
121 #define IMAP_SOCKET 2
122 #define IMAP_AUTHFAIL 3
123 #define IMAP_PROTOCOL 4
124 #define IMAP_SYNTAX 5
128 #define IMAPBUFSIZE 8192
132 IMAP_FLAG_SEEN = 1 << 0,
133 IMAP_FLAG_ANSWERED = 1 << 1,
134 IMAP_FLAG_FLAGGED = 1 << 2,
135 IMAP_FLAG_DELETED = 1 << 3,
136 IMAP_FLAG_DRAFT = 1 << 4
139 #define IMAP_IS_SEEN(flags) ((flags & IMAP_FLAG_SEEN) != 0)
140 #define IMAP_IS_ANSWERED(flags) ((flags & IMAP_FLAG_ANSWERED) != 0)
141 #define IMAP_IS_FLAGGED(flags) ((flags & IMAP_FLAG_FLAGGED) != 0)
142 #define IMAP_IS_DELETED(flags) ((flags & IMAP_FLAG_DELETED) != 0)
143 #define IMAP_IS_DRAFT(flags) ((flags & IMAP_FLAG_DRAFT) != 0)
146 #define IMAP4_PORT 143
148 #define IMAPS_PORT 993
151 #define IMAP_CMD_LIMIT 1000
153 struct _IMAPFolderItem
165 guint32 c_uid_validity;
168 GHashTable *flags_set_table;
169 GHashTable *flags_unset_table;
172 static XMLTag *imap_item_get_xml(Folder *folder, FolderItem *item);
173 static void imap_item_set_xml(Folder *folder, FolderItem *item, XMLTag *tag);
175 static void imap_folder_init (Folder *folder,
179 static Folder *imap_folder_new (const gchar *name,
181 static void imap_folder_destroy (Folder *folder);
183 static IMAPSession *imap_session_new (Folder *folder,
184 const PrefsAccount *account);
185 static void imap_session_authenticate(IMAPSession *session,
186 const PrefsAccount *account);
187 static void imap_session_destroy (Session *session);
189 static gchar *imap_fetch_msg (Folder *folder,
192 static gchar *imap_fetch_msg_full (Folder *folder,
197 static gint imap_add_msg (Folder *folder,
201 static gint imap_add_msgs (Folder *folder,
204 GRelation *relation);
206 static gint imap_copy_msg (Folder *folder,
209 static gint imap_copy_msgs (Folder *folder,
211 MsgInfoList *msglist,
212 GRelation *relation);
214 static gint imap_remove_msg (Folder *folder,
217 static gint imap_remove_msgs (Folder *folder,
219 MsgInfoList *msglist,
220 GRelation *relation);
221 static gint imap_remove_all_msg (Folder *folder,
224 static gboolean imap_is_msg_changed (Folder *folder,
228 static gint imap_close (Folder *folder,
231 static gint imap_scan_tree (Folder *folder);
233 static gint imap_create_tree (Folder *folder);
235 static FolderItem *imap_create_folder (Folder *folder,
238 static gint imap_rename_folder (Folder *folder,
241 static gint imap_remove_folder (Folder *folder,
244 static FolderItem *imap_folder_item_new (Folder *folder);
245 static void imap_folder_item_destroy (Folder *folder,
248 static IMAPSession *imap_session_get (Folder *folder);
250 static gint imap_auth (IMAPSession *session,
255 static gint imap_scan_tree_recursive (IMAPSession *session,
258 static void imap_create_missing_folders (Folder *folder);
259 static FolderItem *imap_create_special_folder
261 SpecialFolderItemType stype,
264 static gint imap_do_copy_msgs (Folder *folder,
266 MsgInfoList *msglist,
267 GRelation *relation);
269 static void imap_delete_all_cached_messages (FolderItem *item);
270 static void imap_set_batch (Folder *folder,
273 static gint imap_set_message_flags (IMAPSession *session,
274 MsgNumberList *numlist,
277 static gint imap_select (IMAPSession *session,
283 guint32 *uid_validity,
285 static gint imap_status (IMAPSession *session,
288 IMAPFolderItem *item,
291 guint32 *uid_validity,
295 static IMAPNameSpace *imap_find_namespace (IMAPFolder *folder,
297 static gchar imap_get_path_separator (IMAPFolder *folder,
299 static gchar *imap_get_real_path (IMAPFolder *folder,
301 static void imap_synchronise (FolderItem *item);
303 static void imap_free_capabilities (IMAPSession *session);
305 /* low-level IMAP4rev1 commands */
306 static gint imap_cmd_login (IMAPSession *session,
310 static gint imap_cmd_logout (IMAPSession *session);
311 static gint imap_cmd_noop (IMAPSession *session);
313 static gint imap_cmd_starttls (IMAPSession *session);
315 static gint imap_cmd_select (IMAPSession *session,
320 guint32 *uid_validity,
322 static gint imap_cmd_examine (IMAPSession *session,
327 guint32 *uid_validity,
329 static gint imap_cmd_create (IMAPSession *sock,
330 const gchar *folder);
331 static gint imap_cmd_rename (IMAPSession *sock,
332 const gchar *oldfolder,
333 const gchar *newfolder);
334 static gint imap_cmd_delete (IMAPSession *session,
335 const gchar *folder);
336 static gint imap_cmd_fetch (IMAPSession *sock,
338 const gchar *filename,
341 static gint imap_cmd_append (IMAPSession *session,
342 const gchar *destfolder,
346 static gint imap_cmd_copy (IMAPSession *session,
347 struct mailimap_set * set,
348 const gchar *destfolder,
349 GRelation *uid_mapping);
350 static gint imap_cmd_store (IMAPSession *session,
351 struct mailimap_set * set,
354 static gint imap_cmd_expunge (IMAPSession *session);
356 static void imap_path_separator_subst (gchar *str,
359 static gchar *imap_utf8_to_modified_utf7 (const gchar *from);
360 static gchar *imap_modified_utf7_to_utf8 (const gchar *mutf7_str);
362 static gboolean imap_rename_folder_func (GNode *node,
364 static gint imap_get_num_list (Folder *folder,
367 gboolean *old_uids_valid);
368 static GSList *imap_get_msginfos (Folder *folder,
370 GSList *msgnum_list);
371 static MsgInfo *imap_get_msginfo (Folder *folder,
374 static gboolean imap_scan_required (Folder *folder,
376 static void imap_change_flags (Folder *folder,
379 MsgPermFlags newflags);
380 static gint imap_get_flags (Folder *folder,
382 MsgInfoList *msglist,
383 GRelation *msgflags);
384 static gchar *imap_folder_get_path (Folder *folder);
385 static gchar *imap_item_get_path (Folder *folder,
387 static MsgInfo *imap_parse_msg(const gchar *file, FolderItem *item);
390 /* data types conversion libetpan <-> sylpheed */
391 static GSList * imap_list_from_lep(IMAPFolder * folder,
392 clist * list, const gchar * real_path, gboolean all);
393 static GSList * imap_get_lep_set_from_numlist(MsgNumberList *numlist);
394 static GSList * imap_get_lep_set_from_msglist(MsgInfoList *msglist);
395 static GSList * imap_uid_list_from_lep(clist * list);
396 static GSList * imap_uid_list_from_lep_tab(carray * list);
397 static MsgInfo *imap_envelope_from_lep(struct imap_fetch_env_info * info,
399 static void imap_lep_set_free(GSList *seq_list);
400 static struct mailimap_flag_list * imap_flag_to_lep(IMAPFlags flags);
402 typedef struct _hashtable_data {
403 IMAPSession *session;
405 IMAPFolderItem *item;
408 static FolderClass imap_class;
410 typedef struct _thread_data {
420 FolderClass *imap_get_class(void)
422 if (imap_class.idstr == NULL) {
423 imap_class.type = F_IMAP;
424 imap_class.idstr = "imap";
425 imap_class.uistr = "IMAP4";
427 /* Folder functions */
428 imap_class.new_folder = imap_folder_new;
429 imap_class.destroy_folder = imap_folder_destroy;
430 imap_class.scan_tree = imap_scan_tree;
431 imap_class.create_tree = imap_create_tree;
433 /* FolderItem functions */
434 imap_class.item_new = imap_folder_item_new;
435 imap_class.item_destroy = imap_folder_item_destroy;
436 imap_class.item_get_path = imap_item_get_path;
437 imap_class.create_folder = imap_create_folder;
438 imap_class.rename_folder = imap_rename_folder;
439 imap_class.remove_folder = imap_remove_folder;
440 imap_class.close = imap_close;
441 imap_class.get_num_list = imap_get_num_list;
442 imap_class.scan_required = imap_scan_required;
443 imap_class.set_xml = folder_set_xml;
444 imap_class.get_xml = folder_get_xml;
445 imap_class.item_set_xml = imap_item_set_xml;
446 imap_class.item_get_xml = imap_item_get_xml;
448 /* Message functions */
449 imap_class.get_msginfo = imap_get_msginfo;
450 imap_class.get_msginfos = imap_get_msginfos;
451 imap_class.fetch_msg = imap_fetch_msg;
452 imap_class.fetch_msg_full = imap_fetch_msg_full;
453 imap_class.add_msg = imap_add_msg;
454 imap_class.add_msgs = imap_add_msgs;
455 imap_class.copy_msg = imap_copy_msg;
456 imap_class.copy_msgs = imap_copy_msgs;
457 imap_class.remove_msg = imap_remove_msg;
458 imap_class.remove_msgs = imap_remove_msgs;
459 imap_class.remove_all_msg = imap_remove_all_msg;
460 imap_class.is_msg_changed = imap_is_msg_changed;
461 imap_class.change_flags = imap_change_flags;
462 imap_class.get_flags = imap_get_flags;
463 imap_class.set_batch = imap_set_batch;
464 imap_class.synchronise = imap_synchronise;
466 pthread_mutex_init(&imap_mutex, NULL);
473 static Folder *imap_folder_new(const gchar *name, const gchar *path)
477 folder = (Folder *)g_new0(IMAPFolder, 1);
478 folder->klass = &imap_class;
479 imap_folder_init(folder, name, path);
484 static void imap_folder_destroy(Folder *folder)
488 while (imap_folder_get_refcnt(folder) > 0)
489 gtk_main_iteration();
491 dir = imap_folder_get_path(folder);
492 if (is_dir_exist(dir))
493 remove_dir_recursive(dir);
496 folder_remote_folder_destroy(REMOTE_FOLDER(folder));
500 static void imap_folder_init(Folder *folder, const gchar *name,
503 folder_remote_folder_init((Folder *)folder, name, path);
506 static FolderItem *imap_folder_item_new(Folder *folder)
508 IMAPFolderItem *item;
510 item = g_new0(IMAPFolderItem, 1);
513 item->uid_list = NULL;
515 return (FolderItem *)item;
518 static void imap_folder_item_destroy(Folder *folder, FolderItem *_item)
520 IMAPFolderItem *item = (IMAPFolderItem *)_item;
522 g_return_if_fail(item != NULL);
523 g_slist_free(item->uid_list);
528 static gboolean imap_reset_uid_lists_func(GNode *node, gpointer data)
530 IMAPFolderItem *item = (IMAPFolderItem *)node->data;
533 g_slist_free(item->uid_list);
534 item->uid_list = NULL;
539 static void imap_reset_uid_lists(Folder *folder)
541 if(folder->node == NULL)
544 /* Destroy all uid lists and rest last uid */
545 g_node_traverse(folder->node, G_IN_ORDER, G_TRAVERSE_ALL, -1, imap_reset_uid_lists_func, NULL);
548 void imap_get_capabilities(IMAPSession *session)
550 struct mailimap_capability_data *capabilities = NULL;
553 if (session->capability != NULL)
556 capabilities = imap_threaded_capability(session->folder);
558 if (capabilities == NULL)
561 for(cur = clist_begin(capabilities->cap_list) ; cur != NULL ;
562 cur = clist_next(cur)) {
563 struct mailimap_capability * cap =
565 if (!cap || cap->cap_data.cap_name == NULL)
567 session->capability = g_slist_append
568 (session->capability,
569 g_strdup(cap->cap_data.cap_name));
570 debug_print("got capa %s\n", cap->cap_data.cap_name);
572 mailimap_capability_data_free(capabilities);
575 gboolean imap_has_capability(IMAPSession *session, const gchar *cap)
578 for (cur = session->capability; cur; cur = cur->next) {
579 if (!g_ascii_strcasecmp(cur->data, cap))
585 static gint imap_auth(IMAPSession *session, const gchar *user, const gchar *pass,
588 gint ok = IMAP_ERROR;
589 static time_t last_login_err = 0;
591 imap_get_capabilities(session);
595 ok = imap_cmd_login(session, user, pass, "ANONYMOUS");
597 case IMAP_AUTH_CRAM_MD5:
598 ok = imap_cmd_login(session, user, pass, "CRAM-MD5");
600 case IMAP_AUTH_LOGIN:
601 ok = imap_cmd_login(session, user, pass, "LOGIN");
604 debug_print("capabilities:\n"
608 imap_has_capability(session, "ANONYMOUS"),
609 imap_has_capability(session, "CRAM-MD5"),
610 imap_has_capability(session, "LOGIN"));
611 if (imap_has_capability(session, "CRAM-MD5"))
612 ok = imap_cmd_login(session, user, pass, "CRAM-MD5");
613 if (ok == IMAP_ERROR) /* we always try LOGIN before giving up */
614 ok = imap_cmd_login(session, user, pass, "LOGIN");
616 if (ok == IMAP_SUCCESS)
617 session->authenticated = TRUE;
619 gchar *ext_info = NULL;
621 if (type == IMAP_AUTH_CRAM_MD5) {
622 ext_info = _("\n\nCRAM-MD5 logins only work if libetpan has been "
623 "compiled with SASL support and the "
624 "CRAM-MD5 SASL plugin is installed.");
629 if (time(NULL) - last_login_err > 10) {
630 if (!prefs_common.no_recv_err_panel) {
631 alertpanel_error(_("Connection to %s failed: "
633 SESSION(session)->server, ext_info);
635 log_error(_("Connection to %s failed: "
636 "login refused.%s\n"),
637 SESSION(session)->server, ext_info);
640 last_login_err = time(NULL);
645 static IMAPSession *imap_reconnect_if_possible(Folder *folder, IMAPSession *session)
647 RemoteFolder *rfolder = REMOTE_FOLDER(folder);
648 /* Check if this is the first try to establish a
649 connection, if yes we don't try to reconnect */
650 debug_print("reconnecting\n");
651 if (rfolder->session == NULL) {
652 log_warning(_("Connecting to %s failed"),
653 folder->account->recv_server);
654 session_destroy(SESSION(session));
657 log_warning(_("IMAP4 connection to %s has been"
658 " disconnected. Reconnecting...\n"),
659 folder->account->recv_server);
660 statusbar_print_all(_("IMAP4 connection to %s has been"
661 " disconnected. Reconnecting...\n"),
662 folder->account->recv_server);
663 SESSION(session)->state = SESSION_DISCONNECTED;
664 session_destroy(SESSION(session));
665 /* Clear folders session to make imap_session_get create
666 a new session, because of rfolder->session == NULL
667 it will not try to reconnect again and so avoid an
669 rfolder->session = NULL;
670 session = imap_session_get(folder);
671 rfolder->session = SESSION(session);
677 #define lock_session() {\
678 debug_print("locking session\n"); \
679 session->busy = TRUE;\
682 #define unlock_session() {\
683 debug_print("unlocking session\n"); \
684 session->busy = FALSE;\
687 static IMAPSession *imap_session_get(Folder *folder)
689 RemoteFolder *rfolder = REMOTE_FOLDER(folder);
690 IMAPSession *session = NULL;
691 static time_t last_failure = 0;
693 g_return_val_if_fail(folder != NULL, NULL);
694 g_return_val_if_fail(FOLDER_CLASS(folder) == &imap_class, NULL);
695 g_return_val_if_fail(folder->account != NULL, NULL);
697 if (prefs_common.work_offline &&
698 !inc_offline_should_override(
699 _("Sylpheed-Claws needs network access in order "
700 "to access the IMAP server."))) {
704 /* Make sure we have a session */
705 if (rfolder->session != NULL) {
706 session = IMAP_SESSION(rfolder->session);
707 /* don't do that yet...
712 imap_reset_uid_lists(folder);
713 if (time(NULL) - last_failure <= 2)
715 session = imap_session_new(folder, folder->account);
717 if(session == NULL) {
718 last_failure = time(NULL);
722 /* Make sure session is authenticated */
723 if (!IMAP_SESSION(session)->authenticated)
724 imap_session_authenticate(IMAP_SESSION(session), folder->account);
726 if (!IMAP_SESSION(session)->authenticated) {
727 session_destroy(SESSION(session));
728 rfolder->session = NULL;
729 last_failure = time(NULL);
733 /* I think the point of this code is to avoid sending a
734 * keepalive if we've used the session recently and therefore
735 * think it's still alive. Unfortunately, most of the code
736 * does not yet check for errors on the socket, and so if the
737 * connection drops we don't notice until the timeout expires.
738 * A better solution than sending a NOOP every time would be
739 * for every command to be prepared to retry until it is
740 * successfully sent. -- mbp */
741 if (time(NULL) - SESSION(session)->last_access_time > SESSION_TIMEOUT_INTERVAL) {
742 /* verify that the session is still alive */
743 if (imap_cmd_noop(session) != IMAP_SUCCESS) {
744 debug_print("disconnected!\n");
745 session = imap_reconnect_if_possible(folder, session);
749 rfolder->session = SESSION(session);
751 return IMAP_SESSION(session);
754 static IMAPSession *imap_session_new(Folder * folder,
755 const PrefsAccount *account)
757 IMAPSession *session;
763 /* FIXME: IMAP over SSL only... */
766 port = account->set_imapport ? account->imapport
767 : account->ssl_imap == SSL_TUNNEL ? IMAPS_PORT : IMAP4_PORT;
768 ssl_type = account->ssl_imap;
770 if (account->ssl_imap != SSL_NONE) {
771 if (alertpanel_full(_("Insecure connection"),
772 _("This connection is configured to be secured "
773 "using SSL, but SSL is not available in this "
774 "build of Sylpheed-Claws. \n\n"
775 "Do you want to continue connecting to this "
776 "server? The communication would not be "
778 _("Con_tinue connecting"),
779 GTK_STOCK_CANCEL, NULL,
780 FALSE, NULL, ALERT_WARNING,
781 G_ALERTALTERNATE) != G_ALERTDEFAULT)
784 port = account->set_imapport ? account->imapport
789 statusbar_print_all(_("Connecting to IMAP4 server: %s..."), folder->account->recv_server);
790 if (account->set_tunnelcmd) {
791 r = imap_threaded_connect_cmd(folder,
793 account->recv_server,
798 if (ssl_type == SSL_TUNNEL) {
799 r = imap_threaded_connect_ssl(folder,
800 account->recv_server,
806 r = imap_threaded_connect(folder,
807 account->recv_server,
813 if (r == MAILIMAP_NO_ERROR_AUTHENTICATED) {
814 authenticated = TRUE;
816 else if (r == MAILIMAP_NO_ERROR_NON_AUTHENTICATED) {
817 authenticated = FALSE;
820 if(!prefs_common.no_recv_err_panel) {
821 alertpanel_error(_("Can't connect to IMAP4 server: %s:%d"),
822 account->recv_server, port);
824 log_error(_("Can't connect to IMAP4 server: %s:%d\n"),
825 account->recv_server, port);
831 session = g_new0(IMAPSession, 1);
832 session_init(SESSION(session));
833 SESSION(session)->type = SESSION_IMAP;
834 SESSION(session)->server = g_strdup(account->recv_server);
835 SESSION(session)->sock = NULL;
837 SESSION(session)->destroy = imap_session_destroy;
839 session->capability = NULL;
841 session->authenticated = authenticated;
842 session->mbox = NULL;
843 session->cmd_count = 0;
844 session->folder = folder;
845 IMAP_FOLDER(session->folder)->last_seen_separator = 0;
848 if (account->ssl_imap == SSL_STARTTLS) {
851 ok = imap_cmd_starttls(session);
852 if (ok != IMAP_SUCCESS) {
853 log_warning(_("Can't start TLS session.\n"));
854 session_destroy(SESSION(session));
858 imap_free_capabilities(session);
859 session->authenticated = FALSE;
860 session->uidplus = FALSE;
861 session->cmd_count = 1;
864 log_message("IMAP connection is %s-authenticated\n",
865 (session->authenticated) ? "pre" : "un");
870 static void imap_session_authenticate(IMAPSession *session,
871 const PrefsAccount *account)
875 g_return_if_fail(account->userid != NULL);
877 pass = account->passwd;
878 if (!pass && account->imap_auth_type != IMAP_AUTH_ANON) {
880 tmp_pass = input_dialog_query_password(account->recv_server, account->userid);
883 Xstrdup_a(pass, tmp_pass, {g_free(tmp_pass); return;});
885 } else if (account->imap_auth_type == IMAP_AUTH_ANON) {
888 statusbar_print_all(_("Connecting to IMAP4 server %s...\n"),
889 account->recv_server);
890 if (imap_auth(session, account->userid, pass, account->imap_auth_type) != IMAP_SUCCESS) {
891 imap_threaded_disconnect(session->folder);
892 imap_cmd_logout(session);
898 session->authenticated = TRUE;
901 static void imap_session_destroy(Session *session)
903 if (session->state != SESSION_DISCONNECTED)
904 imap_threaded_disconnect(IMAP_SESSION(session)->folder);
906 imap_free_capabilities(IMAP_SESSION(session));
907 g_free(IMAP_SESSION(session)->mbox);
908 sock_close(session->sock);
909 session->sock = NULL;
912 static gchar *imap_fetch_msg(Folder *folder, FolderItem *item, gint uid)
914 return imap_fetch_msg_full(folder, item, uid, TRUE, TRUE);
917 static guint get_size_with_crs(MsgInfo *info)
926 fp = procmsg_open_message(info);
930 while (fgets(buf, sizeof (buf), fp) != NULL) {
932 if (!strstr(buf, "\r") && strstr(buf, "\n"))
940 static gchar *imap_fetch_msg_full(Folder *folder, FolderItem *item, gint uid,
941 gboolean headers, gboolean body)
943 gchar *path, *filename;
944 IMAPSession *session;
947 g_return_val_if_fail(folder != NULL, NULL);
948 g_return_val_if_fail(item != NULL, NULL);
953 path = folder_item_get_path(item);
954 if (!is_dir_exist(path))
956 filename = g_strconcat(path, G_DIR_SEPARATOR_S, itos(uid), NULL);
958 debug_print("trying to fetch cached %s\n", filename);
959 if (is_file_exist(filename)) {
960 /* see whether the local file represents the whole message
961 * or not. As the IMAP server reports size with \r chars,
962 * we have to update the local file (UNIX \n only) size */
963 MsgInfo *msginfo = imap_parse_msg(filename, item);
964 MsgInfo *cached = msgcache_get_msg(item->cache,uid);
965 guint have_size = get_size_with_crs(msginfo);
968 debug_print("message %d has been already %scached (%d/%d).\n", uid,
969 have_size == cached->size ? "fully ":"",
970 have_size, (int)cached->size);
972 if (cached && (cached->size == have_size || !body)) {
973 procmsg_msginfo_free(cached);
974 procmsg_msginfo_free(msginfo);
975 file_strip_crs(filename);
977 } else if (!cached) {
978 debug_print("message not cached, considering file complete\n");
979 procmsg_msginfo_free(msginfo);
980 file_strip_crs(filename);
983 procmsg_msginfo_free(cached);
984 procmsg_msginfo_free(msginfo);
988 session = imap_session_get(folder);
997 debug_print("IMAP fetching messages\n");
998 ok = imap_select(session, IMAP_FOLDER(folder), item->path,
999 NULL, NULL, NULL, NULL, FALSE);
1000 if (ok != IMAP_SUCCESS) {
1001 g_warning("can't select mailbox %s\n", item->path);
1007 debug_print("getting message %d...\n", uid);
1008 ok = imap_cmd_fetch(session, (guint32)uid, filename, headers, body);
1010 if (ok != IMAP_SUCCESS) {
1011 g_warning("can't fetch message %d\n", uid);
1018 file_strip_crs(filename);
1022 static gint imap_add_msg(Folder *folder, FolderItem *dest,
1023 const gchar *file, MsgFlags *flags)
1027 MsgFileInfo fileinfo;
1029 g_return_val_if_fail(file != NULL, -1);
1031 fileinfo.msginfo = NULL;
1032 fileinfo.file = (gchar *)file;
1033 fileinfo.flags = flags;
1034 file_list.data = &fileinfo;
1035 file_list.next = NULL;
1037 ret = imap_add_msgs(folder, dest, &file_list, NULL);
1041 static gint imap_add_msgs(Folder *folder, FolderItem *dest, GSList *file_list,
1042 GRelation *relation)
1045 IMAPSession *session;
1046 guint32 last_uid = 0;
1048 MsgFileInfo *fileinfo;
1050 gint curnum = 0, total = 0;
1053 g_return_val_if_fail(folder != NULL, -1);
1054 g_return_val_if_fail(dest != NULL, -1);
1055 g_return_val_if_fail(file_list != NULL, -1);
1057 session = imap_session_get(folder);
1062 destdir = imap_get_real_path(IMAP_FOLDER(folder), dest->path);
1064 statusbar_print_all(_("Adding messages..."));
1065 total = g_slist_length(file_list);
1066 for (cur = file_list; cur != NULL; cur = cur->next) {
1067 IMAPFlags iflags = 0;
1068 guint32 new_uid = 0;
1069 gchar *real_file = NULL;
1070 gboolean file_is_tmp = FALSE;
1071 fileinfo = (MsgFileInfo *)cur->data;
1073 statusbar_progress_all(curnum, total, 1);
1076 if (fileinfo->flags) {
1077 if (MSG_IS_MARKED(*fileinfo->flags))
1078 iflags |= IMAP_FLAG_FLAGGED;
1079 if (MSG_IS_REPLIED(*fileinfo->flags))
1080 iflags |= IMAP_FLAG_ANSWERED;
1081 if (!MSG_IS_UNREAD(*fileinfo->flags))
1082 iflags |= IMAP_FLAG_SEEN;
1085 if (fileinfo->flags) {
1086 if ((MSG_IS_QUEUED(*fileinfo->flags)
1087 || MSG_IS_DRAFT(*fileinfo->flags))
1088 && !folder_has_parent_of_type(dest, F_QUEUE)
1089 && !folder_has_parent_of_type(dest, F_DRAFT)) {
1090 real_file = get_tmp_file();
1092 if (procmsg_remove_special_headers(
1100 } else if (!(MSG_IS_QUEUED(*fileinfo->flags)
1101 || MSG_IS_DRAFT(*fileinfo->flags))
1102 && (folder_has_parent_of_type(dest, F_QUEUE)
1103 || folder_has_parent_of_type(dest, F_DRAFT))) {
1107 if (real_file == NULL)
1108 real_file = g_strdup(fileinfo->file);
1110 if (folder_has_parent_of_type(dest, F_QUEUE) ||
1111 folder_has_parent_of_type(dest, F_OUTBOX) ||
1112 folder_has_parent_of_type(dest, F_DRAFT) ||
1113 folder_has_parent_of_type(dest, F_TRASH))
1114 iflags |= IMAP_FLAG_SEEN;
1116 ok = imap_cmd_append(session, destdir, real_file, iflags,
1119 if (ok != IMAP_SUCCESS) {
1120 g_warning("can't append message %s\n", real_file);
1122 g_unlink(real_file);
1126 statusbar_progress_all(0,0,0);
1127 statusbar_pop_all();
1130 debug_print("appended new message as %d\n", new_uid);
1131 /* put the local file in the imapcache, so that we don't
1132 * have to fetch it back later. */
1134 gchar *cache_path = folder_item_get_path(dest);
1135 if (!is_dir_exist(cache_path))
1136 make_dir_hier(cache_path);
1137 if (is_dir_exist(cache_path)) {
1138 gchar *cache_file = g_strconcat(
1139 cache_path, G_DIR_SEPARATOR_S,
1140 itos(new_uid), NULL);
1141 copy_file(real_file, cache_file, TRUE);
1142 debug_print("copied to cache: %s\n", cache_file);
1149 if (relation != NULL)
1150 g_relation_insert(relation, fileinfo->msginfo != NULL ?
1151 (gpointer) fileinfo->msginfo : (gpointer) fileinfo,
1152 GINT_TO_POINTER(dest->last_num + 1));
1154 new_uid = dest->last_num+1;
1156 if (last_uid < new_uid)
1159 g_unlink(real_file);
1163 statusbar_progress_all(0,0,0);
1164 statusbar_pop_all();
1166 imap_cmd_expunge(session);
1174 static gint imap_do_copy_msgs(Folder *folder, FolderItem *dest,
1175 MsgInfoList *msglist, GRelation *relation)
1179 GSList *seq_list, *cur;
1181 IMAPSession *session;
1182 gint ok = IMAP_SUCCESS;
1183 GRelation *uid_mapping;
1186 g_return_val_if_fail(folder != NULL, -1);
1187 g_return_val_if_fail(dest != NULL, -1);
1188 g_return_val_if_fail(msglist != NULL, -1);
1190 session = imap_session_get(folder);
1196 msginfo = (MsgInfo *)msglist->data;
1198 src = msginfo->folder;
1200 g_warning("the src folder is identical to the dest.\n");
1205 ok = imap_select(session, IMAP_FOLDER(folder), msginfo->folder->path,
1206 NULL, NULL, NULL, NULL, FALSE);
1207 if (ok != IMAP_SUCCESS) {
1212 destdir = imap_get_real_path(IMAP_FOLDER(folder), dest->path);
1213 seq_list = imap_get_lep_set_from_msglist(msglist);
1214 uid_mapping = g_relation_new(2);
1215 g_relation_index(uid_mapping, 0, g_direct_hash, g_direct_equal);
1217 statusbar_print_all(_("Copying messages..."));
1218 for (cur = seq_list; cur != NULL; cur = g_slist_next(cur)) {
1219 struct mailimap_set * seq_set;
1220 seq_set = cur->data;
1222 debug_print("Copying messages from %s to %s ...\n",
1223 src->path, destdir);
1225 ok = imap_cmd_copy(session, seq_set, destdir, uid_mapping);
1226 if (ok != IMAP_SUCCESS) {
1227 g_relation_destroy(uid_mapping);
1228 imap_lep_set_free(seq_list);
1234 for (cur = msglist; cur != NULL; cur = g_slist_next(cur)) {
1235 MsgInfo *msginfo = (MsgInfo *)cur->data;
1238 tuples = g_relation_select(uid_mapping,
1239 GINT_TO_POINTER(msginfo->msgnum),
1241 if (tuples->len > 0) {
1242 gint num = GPOINTER_TO_INT(g_tuples_index(tuples, 0, 1));
1243 g_relation_insert(relation, msginfo,
1244 GPOINTER_TO_INT(num));
1248 g_relation_insert(relation, msginfo,
1249 GPOINTER_TO_INT(0));
1250 g_tuples_destroy(tuples);
1252 statusbar_pop_all();
1254 g_relation_destroy(uid_mapping);
1255 imap_lep_set_free(seq_list);
1259 IMAP_FOLDER_ITEM(dest)->lastuid = 0;
1260 IMAP_FOLDER_ITEM(dest)->uid_next = 0;
1261 g_slist_free(IMAP_FOLDER_ITEM(dest)->uid_list);
1262 IMAP_FOLDER_ITEM(dest)->uid_list = NULL;
1265 if (ok == IMAP_SUCCESS)
1271 static gint imap_copy_msg(Folder *folder, FolderItem *dest, MsgInfo *msginfo)
1275 g_return_val_if_fail(msginfo != NULL, -1);
1277 msglist.data = msginfo;
1278 msglist.next = NULL;
1280 return imap_copy_msgs(folder, dest, &msglist, NULL);
1283 static gint imap_copy_msgs(Folder *folder, FolderItem *dest,
1284 MsgInfoList *msglist, GRelation *relation)
1290 g_return_val_if_fail(folder != NULL, -1);
1291 g_return_val_if_fail(dest != NULL, -1);
1292 g_return_val_if_fail(msglist != NULL, -1);
1294 msginfo = (MsgInfo *)msglist->data;
1295 g_return_val_if_fail(msginfo->folder != NULL, -1);
1297 /* if from/to are the same "type" (with or without extra headers),
1298 * copy them via imap */
1299 if (folder == msginfo->folder->folder &&
1300 !folder_has_parent_of_type(msginfo->folder, F_DRAFT) &&
1301 !folder_has_parent_of_type(msginfo->folder, F_QUEUE) &&
1302 !folder_has_parent_of_type(dest, F_DRAFT) &&
1303 !folder_has_parent_of_type(dest, F_QUEUE)) {
1304 ret = imap_do_copy_msgs(folder, dest, msglist, relation);
1306 } else if (folder == msginfo->folder->folder &&
1307 (folder_has_parent_of_type(msginfo->folder, F_DRAFT) ||
1308 folder_has_parent_of_type(msginfo->folder, F_QUEUE)) &&
1309 (folder_has_parent_of_type(dest, F_DRAFT) ||
1310 folder_has_parent_of_type(dest, F_QUEUE))) {
1311 ret = imap_do_copy_msgs(folder, dest, msglist, relation);
1314 /* else reupload them */
1315 file_list = procmsg_get_message_file_list(msglist);
1316 g_return_val_if_fail(file_list != NULL, -1);
1318 ret = imap_add_msgs(folder, dest, file_list, relation);
1319 procmsg_message_file_list_free(file_list);
1325 static gint imap_do_remove_msgs(Folder *folder, FolderItem *dest,
1326 MsgInfoList *msglist, GRelation *relation)
1329 GSList *numlist = NULL, *cur;
1331 IMAPSession *session;
1332 gint ok = IMAP_SUCCESS;
1333 GRelation *uid_mapping;
1335 g_return_val_if_fail(folder != NULL, -1);
1336 g_return_val_if_fail(dest != NULL, -1);
1337 g_return_val_if_fail(msglist != NULL, -1);
1339 session = imap_session_get(folder);
1344 msginfo = (MsgInfo *)msglist->data;
1346 ok = imap_select(session, IMAP_FOLDER(folder), msginfo->folder->path,
1347 NULL, NULL, NULL, NULL, FALSE);
1348 if (ok != IMAP_SUCCESS) {
1353 destdir = imap_get_real_path(IMAP_FOLDER(folder), dest->path);
1354 for (cur = msglist; cur; cur = cur->next) {
1355 msginfo = (MsgInfo *)cur->data;
1356 if (!MSG_IS_DELETED(msginfo->flags))
1357 numlist = g_slist_append(numlist, GINT_TO_POINTER(msginfo->msgnum));
1360 uid_mapping = g_relation_new(2);
1361 g_relation_index(uid_mapping, 0, g_direct_hash, g_direct_equal);
1363 ok = imap_set_message_flags
1364 (IMAP_SESSION(REMOTE_FOLDER(folder)->session),
1365 numlist, IMAP_FLAG_DELETED, TRUE);
1366 if (ok != IMAP_SUCCESS) {
1367 log_warning(_("can't set deleted flags\n"));
1371 ok = imap_cmd_expunge(session);
1372 if (ok != IMAP_SUCCESS) {
1373 log_warning(_("can't expunge\n"));
1378 g_relation_destroy(uid_mapping);
1379 g_slist_free(numlist);
1383 if (ok == IMAP_SUCCESS)
1389 static gint imap_remove_msgs(Folder *folder, FolderItem *dest,
1390 MsgInfoList *msglist, GRelation *relation)
1394 g_return_val_if_fail(folder != NULL, -1);
1395 g_return_val_if_fail(dest != NULL, -1);
1396 if (msglist == NULL)
1399 msginfo = (MsgInfo *)msglist->data;
1400 g_return_val_if_fail(msginfo->folder != NULL, -1);
1402 return imap_do_remove_msgs(folder, dest, msglist, relation);
1405 static gint imap_remove_all_msg(Folder *folder, FolderItem *item)
1407 GSList *list = folder_item_get_msg_list(item);
1408 gint res = imap_remove_msgs(folder, item, list, NULL);
1409 procmsg_msg_list_free(list);
1413 static gboolean imap_is_msg_changed(Folder *folder, FolderItem *item,
1416 /* TODO: properly implement this method */
1420 static gint imap_close(Folder *folder, FolderItem *item)
1425 static gint imap_scan_tree(Folder *folder)
1427 FolderItem *item = NULL;
1428 IMAPSession *session;
1429 gchar *root_folder = NULL;
1431 g_return_val_if_fail(folder != NULL, -1);
1432 g_return_val_if_fail(folder->account != NULL, -1);
1434 session = imap_session_get(folder);
1436 if (!folder->node) {
1437 folder_tree_destroy(folder);
1438 item = folder_item_new(folder, folder->name, NULL);
1439 item->folder = folder;
1440 folder->node = item->node = g_node_new(item);
1446 if (folder->account->imap_dir && *folder->account->imap_dir) {
1451 Xstrdup_a(root_folder, folder->account->imap_dir, {unlock_session();return -1;});
1452 extract_quote(root_folder, '"');
1453 subst_char(root_folder,
1454 imap_get_path_separator(IMAP_FOLDER(folder),
1457 strtailchomp(root_folder, '/');
1458 real_path = imap_get_real_path
1459 (IMAP_FOLDER(folder), root_folder);
1460 debug_print("IMAP root directory: %s\n", real_path);
1462 /* check if root directory exist */
1464 r = imap_threaded_list(session->folder, "", real_path,
1466 if ((r != MAILIMAP_NO_ERROR) || (clist_count(lep_list) == 0)) {
1467 if (!folder->node) {
1468 item = folder_item_new(folder, folder->name, NULL);
1469 item->folder = folder;
1470 folder->node = item->node = g_node_new(item);
1475 mailimap_list_result_free(lep_list);
1481 item = FOLDER_ITEM(folder->node->data);
1482 if (!item || ((item->path || root_folder) &&
1483 strcmp2(item->path, root_folder) != 0)) {
1484 folder_tree_destroy(folder);
1485 item = folder_item_new(folder, folder->name, root_folder);
1486 item->folder = folder;
1487 folder->node = item->node = g_node_new(item);
1490 imap_scan_tree_recursive(session, FOLDER_ITEM(folder->node->data));
1491 imap_create_missing_folders(folder);
1497 static gint imap_scan_tree_recursive(IMAPSession *session, FolderItem *item)
1500 IMAPFolder *imapfolder;
1501 FolderItem *new_item;
1502 GSList *item_list, *cur;
1505 gchar *wildcard_path;
1511 g_return_val_if_fail(item != NULL, -1);
1512 g_return_val_if_fail(item->folder != NULL, -1);
1513 g_return_val_if_fail(item->no_sub == FALSE, -1);
1515 folder = item->folder;
1516 imapfolder = IMAP_FOLDER(folder);
1518 separator = imap_get_path_separator(imapfolder, item->path);
1520 if (folder->ui_func)
1521 folder->ui_func(folder, item, folder->ui_func_data);
1524 wildcard[0] = separator;
1527 real_path = imap_get_real_path(imapfolder, item->path);
1531 real_path = g_strdup("");
1534 Xstrcat_a(wildcard_path, real_path, wildcard,
1535 {g_free(real_path); return IMAP_ERROR;});
1537 r = imap_threaded_list(folder, "", wildcard_path, &lep_list);
1538 if (r != MAILIMAP_NO_ERROR) {
1542 item_list = imap_list_from_lep(imapfolder,
1543 lep_list, real_path, FALSE);
1544 mailimap_list_result_free(lep_list);
1549 node = item->node->children;
1550 while (node != NULL) {
1551 FolderItem *old_item = FOLDER_ITEM(node->data);
1552 GNode *next = node->next;
1555 for (cur = item_list; cur != NULL; cur = cur->next) {
1556 FolderItem *cur_item = FOLDER_ITEM(cur->data);
1557 if (!strcmp2(old_item->path, cur_item->path)) {
1558 new_item = cur_item;
1563 debug_print("folder '%s' not found. removing...\n",
1565 folder_item_remove(old_item);
1567 old_item->no_sub = new_item->no_sub;
1568 old_item->no_select = new_item->no_select;
1569 if (old_item->no_sub == TRUE && node->children) {
1570 debug_print("folder '%s' doesn't have "
1571 "subfolders. removing...\n",
1573 folder_item_remove_children(old_item);
1580 for (cur = item_list; cur != NULL; cur = cur->next) {
1581 FolderItem *cur_item = FOLDER_ITEM(cur->data);
1584 for (node = item->node->children; node != NULL;
1585 node = node->next) {
1586 if (!strcmp2(FOLDER_ITEM(node->data)->path,
1588 new_item = FOLDER_ITEM(node->data);
1589 folder_item_destroy(cur_item);
1595 new_item = cur_item;
1596 debug_print("new folder '%s' found.\n", new_item->path);
1597 folder_item_append(item, new_item);
1600 if (!strcmp(new_item->path, "INBOX")) {
1601 new_item->stype = F_INBOX;
1602 folder->inbox = new_item;
1603 } else if (!folder_item_parent(item) || item->stype == F_INBOX) {
1606 base = g_path_get_basename(new_item->path);
1608 if (!folder->outbox && !g_ascii_strcasecmp(base, "Sent")) {
1609 new_item->stype = F_OUTBOX;
1610 folder->outbox = new_item;
1611 } else if (!folder->draft && !g_ascii_strcasecmp(base, "Drafts")) {
1612 new_item->stype = F_DRAFT;
1613 folder->draft = new_item;
1614 } else if (!folder->queue && !g_ascii_strcasecmp(base, "Queue")) {
1615 new_item->stype = F_QUEUE;
1616 folder->queue = new_item;
1617 } else if (!folder->trash && !g_ascii_strcasecmp(base, "Trash")) {
1618 new_item->stype = F_TRASH;
1619 folder->trash = new_item;
1624 if (new_item->no_sub == FALSE)
1625 imap_scan_tree_recursive(session, new_item);
1628 g_slist_free(item_list);
1630 return IMAP_SUCCESS;
1633 static gint imap_create_tree(Folder *folder)
1635 g_return_val_if_fail(folder != NULL, -1);
1636 g_return_val_if_fail(folder->node != NULL, -1);
1637 g_return_val_if_fail(folder->node->data != NULL, -1);
1638 g_return_val_if_fail(folder->account != NULL, -1);
1640 imap_scan_tree(folder);
1641 imap_create_missing_folders(folder);
1646 static void imap_create_missing_folders(Folder *folder)
1648 g_return_if_fail(folder != NULL);
1651 folder->inbox = imap_create_special_folder
1652 (folder, F_INBOX, "INBOX");
1654 folder->trash = imap_create_special_folder
1655 (folder, F_TRASH, "Trash");
1657 folder->queue = imap_create_special_folder
1658 (folder, F_QUEUE, "Queue");
1659 if (!folder->outbox)
1660 folder->outbox = imap_create_special_folder
1661 (folder, F_OUTBOX, "Sent");
1663 folder->draft = imap_create_special_folder
1664 (folder, F_DRAFT, "Drafts");
1667 static FolderItem *imap_create_special_folder(Folder *folder,
1668 SpecialFolderItemType stype,
1672 FolderItem *new_item;
1674 g_return_val_if_fail(folder != NULL, NULL);
1675 g_return_val_if_fail(folder->node != NULL, NULL);
1676 g_return_val_if_fail(folder->node->data != NULL, NULL);
1677 g_return_val_if_fail(folder->account != NULL, NULL);
1678 g_return_val_if_fail(name != NULL, NULL);
1680 item = FOLDER_ITEM(folder->node->data);
1681 new_item = imap_create_folder(folder, item, name);
1684 g_warning("Can't create '%s'\n", name);
1685 if (!folder->inbox) return NULL;
1687 new_item = imap_create_folder(folder, folder->inbox, name);
1689 g_warning("Can't create '%s' under INBOX\n", name);
1691 new_item->stype = stype;
1693 new_item->stype = stype;
1698 static gchar *imap_folder_get_path(Folder *folder)
1702 g_return_val_if_fail(folder != NULL, NULL);
1703 g_return_val_if_fail(folder->account != NULL, NULL);
1705 folder_path = g_strconcat(get_imap_cache_dir(),
1707 folder->account->recv_server,
1709 folder->account->userid,
1715 static gchar *imap_item_get_path(Folder *folder, FolderItem *item)
1717 gchar *folder_path, *path;
1719 g_return_val_if_fail(folder != NULL, NULL);
1720 g_return_val_if_fail(item != NULL, NULL);
1721 folder_path = imap_folder_get_path(folder);
1723 g_return_val_if_fail(folder_path != NULL, NULL);
1724 if (folder_path[0] == G_DIR_SEPARATOR) {
1726 path = g_strconcat(folder_path, G_DIR_SEPARATOR_S,
1729 path = g_strdup(folder_path);
1732 path = g_strconcat(get_home_dir(), G_DIR_SEPARATOR_S,
1733 folder_path, G_DIR_SEPARATOR_S,
1736 path = g_strconcat(get_home_dir(), G_DIR_SEPARATOR_S,
1739 g_free(folder_path);
1744 static FolderItem *imap_create_folder(Folder *folder, FolderItem *parent,
1747 gchar *dirpath, *imap_path;
1748 IMAPSession *session;
1749 FolderItem *new_item;
1754 gboolean no_select = FALSE, no_sub = FALSE;
1756 g_return_val_if_fail(folder != NULL, NULL);
1757 g_return_val_if_fail(folder->account != NULL, NULL);
1758 g_return_val_if_fail(parent != NULL, NULL);
1759 g_return_val_if_fail(name != NULL, NULL);
1761 session = imap_session_get(folder);
1767 if (!folder_item_parent(parent) && strcmp(name, "INBOX") == 0) {
1768 dirpath = g_strdup(name);
1769 }else if (parent->path)
1770 dirpath = g_strconcat(parent->path, "/", name, NULL);
1771 else if ((p = strchr(name, '/')) != NULL && *(p + 1) != '\0')
1772 dirpath = g_strdup(name);
1773 else if (folder->account->imap_dir && *folder->account->imap_dir) {
1776 Xstrdup_a(imap_dir, folder->account->imap_dir, {unlock_session();return NULL;});
1777 strtailchomp(imap_dir, '/');
1778 dirpath = g_strconcat(imap_dir, "/", name, NULL);
1780 dirpath = g_strdup(name);
1784 /* keep trailing directory separator to create a folder that contains
1786 imap_path = imap_utf8_to_modified_utf7(dirpath);
1788 strtailchomp(dirpath, '/');
1789 Xstrdup_a(new_name, name, {
1794 separator = imap_get_path_separator(IMAP_FOLDER(folder), imap_path);
1795 imap_path_separator_subst(imap_path, separator);
1796 /* remove trailing / for display */
1797 strtailchomp(new_name, '/');
1799 if (strcmp(dirpath, "INBOX") != 0) {
1801 gboolean exist = FALSE;
1805 argbuf = g_ptr_array_new();
1806 r = imap_threaded_list(folder, "", imap_path, &lep_list);
1807 if (r != MAILIMAP_NO_ERROR) {
1808 log_warning(_("can't create mailbox: LIST failed\n"));
1811 ptr_array_free_strings(argbuf);
1812 g_ptr_array_free(argbuf, TRUE);
1817 if (clist_count(lep_list) > 0)
1819 mailimap_list_result_free(lep_list);
1822 ok = imap_cmd_create(session, imap_path);
1823 if (ok != IMAP_SUCCESS) {
1824 log_warning(_("can't create mailbox\n"));
1830 r = imap_threaded_list(folder, "", imap_path, &lep_list);
1831 if (r == MAILIMAP_NO_ERROR) {
1832 GSList *item_list = imap_list_from_lep(IMAP_FOLDER(folder),
1833 lep_list, dirpath, TRUE);
1835 FolderItem *cur_item = FOLDER_ITEM(item_list->data);
1836 no_select = cur_item->no_select;
1837 no_sub = cur_item->no_sub;
1838 g_slist_free(item_list);
1840 mailimap_list_result_free(lep_list);
1847 /* just get flags */
1848 r = imap_threaded_list(folder, "", "INBOX", &lep_list);
1849 if (r == MAILIMAP_NO_ERROR) {
1850 GSList *item_list = imap_list_from_lep(IMAP_FOLDER(folder),
1851 lep_list, dirpath, TRUE);
1853 FolderItem *cur_item = FOLDER_ITEM(item_list->data);
1854 no_select = cur_item->no_select;
1855 no_sub = cur_item->no_sub;
1856 g_slist_free(item_list);
1858 mailimap_list_result_free(lep_list);
1862 new_item = folder_item_new(folder, new_name, dirpath);
1863 new_item->no_select = no_select;
1864 new_item->no_sub = no_sub;
1865 folder_item_append(parent, new_item);
1869 dirpath = folder_item_get_path(new_item);
1870 if (!is_dir_exist(dirpath))
1871 make_dir_hier(dirpath);
1877 static gint imap_rename_folder(Folder *folder, FolderItem *item,
1882 gchar *real_oldpath;
1883 gchar *real_newpath;
1885 gchar *old_cache_dir;
1886 gchar *new_cache_dir;
1887 IMAPSession *session;
1890 gint exists, recent, unseen;
1891 guint32 uid_validity;
1893 g_return_val_if_fail(folder != NULL, -1);
1894 g_return_val_if_fail(item != NULL, -1);
1895 g_return_val_if_fail(item->path != NULL, -1);
1896 g_return_val_if_fail(name != NULL, -1);
1898 session = imap_session_get(folder);
1904 if (strchr(name, imap_get_path_separator(IMAP_FOLDER(folder), item->path)) != NULL) {
1905 g_warning(_("New folder name must not contain the namespace "
1911 real_oldpath = imap_get_real_path(IMAP_FOLDER(folder), item->path);
1913 g_free(session->mbox);
1914 session->mbox = NULL;
1915 ok = imap_cmd_examine(session, "INBOX",
1916 &exists, &recent, &unseen, &uid_validity, FALSE);
1917 if (ok != IMAP_SUCCESS) {
1918 g_free(real_oldpath);
1923 separator = imap_get_path_separator(IMAP_FOLDER(folder), item->path);
1924 if (strchr(item->path, G_DIR_SEPARATOR)) {
1925 dirpath = g_path_get_dirname(item->path);
1926 newpath = g_strconcat(dirpath, G_DIR_SEPARATOR_S, name, NULL);
1929 newpath = g_strdup(name);
1931 real_newpath = imap_utf8_to_modified_utf7(newpath);
1932 imap_path_separator_subst(real_newpath, separator);
1934 ok = imap_cmd_rename(session, real_oldpath, real_newpath);
1935 if (ok != IMAP_SUCCESS) {
1936 log_warning(_("can't rename mailbox: %s to %s\n"),
1937 real_oldpath, real_newpath);
1938 g_free(real_oldpath);
1940 g_free(real_newpath);
1946 item->name = g_strdup(name);
1948 old_cache_dir = folder_item_get_path(item);
1950 paths[0] = g_strdup(item->path);
1952 g_node_traverse(item->node, G_PRE_ORDER, G_TRAVERSE_ALL, -1,
1953 imap_rename_folder_func, paths);
1955 if (is_dir_exist(old_cache_dir)) {
1956 new_cache_dir = folder_item_get_path(item);
1957 if (rename(old_cache_dir, new_cache_dir) < 0) {
1958 FILE_OP_ERROR(old_cache_dir, "rename");
1960 g_free(new_cache_dir);
1963 g_free(old_cache_dir);
1966 g_free(real_oldpath);
1967 g_free(real_newpath);
1972 static gint imap_remove_folder_real(Folder *folder, FolderItem *item)
1975 IMAPSession *session;
1979 g_return_val_if_fail(folder != NULL, -1);
1980 g_return_val_if_fail(item != NULL, -1);
1981 g_return_val_if_fail(item->path != NULL, -1);
1983 session = imap_session_get(folder);
1988 path = imap_get_real_path(IMAP_FOLDER(folder), item->path);
1990 ok = imap_cmd_delete(session, path);
1991 if (ok != IMAP_SUCCESS) {
1992 gchar *tmp = g_strdup_printf("%s%c", path,
1993 imap_get_path_separator(IMAP_FOLDER(folder), path));
1996 ok = imap_cmd_delete(session, path);
1999 if (ok != IMAP_SUCCESS) {
2000 log_warning(_("can't delete mailbox\n"));
2007 cache_dir = folder_item_get_path(item);
2008 if (is_dir_exist(cache_dir) && remove_dir_recursive(cache_dir) < 0)
2009 g_warning("can't remove directory '%s'\n", cache_dir);
2011 folder_item_remove(item);
2016 static gint imap_remove_folder(Folder *folder, FolderItem *item)
2020 g_return_val_if_fail(item != NULL, -1);
2021 g_return_val_if_fail(item->folder != NULL, -1);
2022 g_return_val_if_fail(item->node != NULL, -1);
2024 node = item->node->children;
2025 while (node != NULL) {
2027 if (imap_remove_folder(folder, FOLDER_ITEM(node->data)) < 0)
2031 debug_print("IMAP removing %s\n", item->path);
2033 if (imap_remove_all_msg(folder, item) < 0)
2035 return imap_remove_folder_real(folder, item);
2038 typedef struct _uncached_data {
2039 IMAPSession *session;
2041 MsgNumberList *numlist;
2047 static void *imap_get_uncached_messages_thread(void *data)
2049 uncached_data *stuff = (uncached_data *)data;
2050 IMAPSession *session = stuff->session;
2051 FolderItem *item = stuff->item;
2052 MsgNumberList *numlist = stuff->numlist;
2054 GSList *newlist = NULL;
2055 GSList *llast = NULL;
2056 GSList *seq_list, *cur;
2058 debug_print("uncached_messages\n");
2060 if (session == NULL || item == NULL || item->folder == NULL
2061 || FOLDER_CLASS(item->folder) != &imap_class) {
2066 seq_list = imap_get_lep_set_from_numlist(numlist);
2067 debug_print("get msgs info\n");
2068 for (cur = seq_list; cur != NULL; cur = g_slist_next(cur)) {
2069 struct mailimap_set * imapset;
2075 imapset = cur->data;
2077 r = imap_threaded_fetch_env(session->folder,
2078 imapset, &env_list);
2079 if (r != MAILIMAP_NO_ERROR)
2082 session_set_access_time(SESSION(session));
2085 for(i = 0 ; i < carray_count(env_list) ; i ++) {
2086 struct imap_fetch_env_info * info;
2089 info = carray_get(env_list, i);
2090 msginfo = imap_envelope_from_lep(info, item);
2091 if (msginfo == NULL)
2093 msginfo->folder = item;
2095 llast = newlist = g_slist_append(newlist, msginfo);
2097 llast = g_slist_append(llast, msginfo);
2098 llast = llast->next;
2103 imap_fetch_env_free(env_list);
2106 for (cur = seq_list; cur != NULL; cur = g_slist_next(cur)) {
2107 struct mailimap_set * imapset;
2109 imapset = cur->data;
2110 mailimap_set_free(imapset);
2113 session_set_access_time(SESSION(session));
2118 #define MAX_MSG_NUM 50
2120 static GSList *imap_get_uncached_messages(IMAPSession *session,
2122 MsgNumberList *numlist)
2124 GSList *result = NULL;
2126 uncached_data *data = g_new0(uncached_data, 1);
2131 data->total = g_slist_length(numlist);
2132 debug_print("messages list : %i\n", data->total);
2134 while (cur != NULL) {
2135 GSList * partial_result;
2143 while (count < MAX_MSG_NUM) {
2148 if (newlist == NULL)
2149 llast = newlist = g_slist_append(newlist, p);
2151 llast = g_slist_append(llast, p);
2152 llast = llast->next;
2162 data->session = session;
2164 data->numlist = newlist;
2167 if (prefs_common.work_offline &&
2168 !inc_offline_should_override(
2169 _("Sylpheed-Claws needs network access in order "
2170 "to access the IMAP server."))) {
2176 (GSList *)imap_get_uncached_messages_thread(data);
2178 statusbar_progress_all(data->cur,data->total, 1);
2180 g_slist_free(newlist);
2182 result = g_slist_concat(result, partial_result);
2186 statusbar_progress_all(0,0,0);
2187 statusbar_pop_all();
2192 static void imap_delete_all_cached_messages(FolderItem *item)
2196 g_return_if_fail(item != NULL);
2197 g_return_if_fail(item->folder != NULL);
2198 g_return_if_fail(FOLDER_CLASS(item->folder) == &imap_class);
2200 debug_print("Deleting all cached messages...\n");
2202 dir = folder_item_get_path(item);
2203 if (is_dir_exist(dir))
2204 remove_all_numbered_files(dir);
2207 debug_print("done.\n");
2210 static IMAPNameSpace *imap_find_namespace_from_list(GList *ns_list,
2213 IMAPNameSpace *namespace = NULL;
2214 gchar *tmp_path, *name;
2216 if (!path) path = "";
2218 for (; ns_list != NULL; ns_list = ns_list->next) {
2219 IMAPNameSpace *tmp_ns = ns_list->data;
2221 Xstrcat_a(tmp_path, path, "/", return namespace);
2222 Xstrdup_a(name, tmp_ns->name, return namespace);
2223 if (tmp_ns->separator && tmp_ns->separator != '/') {
2224 subst_char(tmp_path, tmp_ns->separator, '/');
2225 subst_char(name, tmp_ns->separator, '/');
2227 if (strncmp(tmp_path, name, strlen(name)) == 0)
2234 static IMAPNameSpace *imap_find_namespace(IMAPFolder *folder,
2237 IMAPNameSpace *namespace;
2239 g_return_val_if_fail(folder != NULL, NULL);
2241 namespace = imap_find_namespace_from_list(folder->ns_personal, path);
2242 if (namespace) return namespace;
2243 namespace = imap_find_namespace_from_list(folder->ns_others, path);
2244 if (namespace) return namespace;
2245 namespace = imap_find_namespace_from_list(folder->ns_shared, path);
2246 if (namespace) return namespace;
2251 gchar imap_get_path_separator_for_item(FolderItem *item)
2253 Folder *folder = NULL;
2254 IMAPFolder *imap_folder = NULL;
2257 folder = item->folder;
2262 imap_folder = IMAP_FOLDER(folder);
2267 return imap_get_path_separator(imap_folder, item->path);
2270 static gchar imap_get_path_separator(IMAPFolder *folder, const gchar *path)
2272 IMAPNameSpace *namespace;
2273 gchar separator = '/';
2274 IMAPSession *session = imap_session_get(FOLDER(folder));
2275 g_return_val_if_fail(session != NULL, '/');
2277 if (folder->last_seen_separator == 0) {
2279 int r = imap_threaded_list((Folder *)folder, "", "", &lep_list);
2280 if (r != MAILIMAP_NO_ERROR) {
2281 log_warning(_("LIST failed\n"));
2285 if (clist_count(lep_list) > 0) {
2286 clistiter * iter = clist_begin(lep_list);
2287 struct mailimap_mailbox_list * mb;
2288 mb = clist_content(iter);
2290 folder->last_seen_separator = mb->mb_delimiter;
2291 debug_print("got separator: %c\n", folder->last_seen_separator);
2293 mailimap_list_result_free(lep_list);
2296 if (folder->last_seen_separator != 0) {
2297 debug_print("using separator: %c\n", folder->last_seen_separator);
2298 return folder->last_seen_separator;
2301 namespace = imap_find_namespace(folder, path);
2302 if (namespace && namespace->separator)
2303 separator = namespace->separator;
2308 static gchar *imap_get_real_path(IMAPFolder *folder, const gchar *path)
2313 g_return_val_if_fail(folder != NULL, NULL);
2314 g_return_val_if_fail(path != NULL, NULL);
2316 real_path = imap_utf8_to_modified_utf7(path);
2317 separator = imap_get_path_separator(folder, path);
2318 imap_path_separator_subst(real_path, separator);
2323 static gint imap_set_message_flags(IMAPSession *session,
2324 MsgNumberList *numlist,
2332 seq_list = imap_get_lep_set_from_numlist(numlist);
2334 for(cur = seq_list ; cur != NULL ; cur = g_slist_next(cur)) {
2335 struct mailimap_set * imapset;
2337 imapset = cur->data;
2339 ok = imap_cmd_store(session, imapset,
2343 imap_lep_set_free(seq_list);
2345 return IMAP_SUCCESS;
2348 typedef struct _select_data {
2349 IMAPSession *session;
2354 guint32 *uid_validity;
2358 static gint imap_select(IMAPSession *session, IMAPFolder *folder,
2360 gint *exists, gint *recent, gint *unseen,
2361 guint32 *uid_validity, gboolean block)
2365 gint exists_, recent_, unseen_;
2366 guint32 uid_validity_;
2368 if (!exists && !recent && !unseen && !uid_validity) {
2369 if (session->mbox && strcmp(session->mbox, path) == 0)
2370 return IMAP_SUCCESS;
2379 uid_validity = &uid_validity_;
2381 g_free(session->mbox);
2382 session->mbox = NULL;
2384 real_path = imap_get_real_path(folder, path);
2386 ok = imap_cmd_select(session, real_path,
2387 exists, recent, unseen, uid_validity, block);
2388 if (ok != IMAP_SUCCESS)
2389 log_warning(_("can't select folder: %s\n"), real_path);
2391 session->mbox = g_strdup(path);
2392 session->folder_content_changed = FALSE;
2399 static gint imap_status(IMAPSession *session, IMAPFolder *folder,
2400 const gchar *path, IMAPFolderItem *item,
2402 guint32 *uid_next, guint32 *uid_validity,
2403 gint *unseen, gboolean block)
2407 struct mailimap_mailbox_data_status * data_status;
2412 real_path = imap_get_real_path(folder, path);
2426 r = imap_threaded_status(FOLDER(folder), real_path,
2427 &data_status, mask);
2430 if (r != MAILIMAP_NO_ERROR) {
2431 debug_print("status err %d\n", r);
2435 if (data_status->st_info_list == NULL) {
2436 mailimap_mailbox_data_status_free(data_status);
2437 debug_print("status->st_info_list == NULL\n");
2442 for(iter = clist_begin(data_status->st_info_list) ; iter != NULL ;
2443 iter = clist_next(iter)) {
2444 struct mailimap_status_info * info;
2446 info = clist_content(iter);
2447 switch (info->st_att) {
2448 case MAILIMAP_STATUS_ATT_MESSAGES:
2449 * messages = info->st_value;
2450 got_values |= 1 << 0;
2453 case MAILIMAP_STATUS_ATT_UIDNEXT:
2454 * uid_next = info->st_value;
2455 got_values |= 1 << 2;
2458 case MAILIMAP_STATUS_ATT_UIDVALIDITY:
2459 * uid_validity = info->st_value;
2460 got_values |= 1 << 3;
2463 case MAILIMAP_STATUS_ATT_UNSEEN:
2464 * unseen = info->st_value;
2465 got_values |= 1 << 4;
2469 mailimap_mailbox_data_status_free(data_status);
2471 if (got_values != mask) {
2472 debug_print("status: incomplete values received (%d)\n", got_values);
2475 return IMAP_SUCCESS;
2478 static void imap_free_capabilities(IMAPSession *session)
2480 slist_free_strings(session->capability);
2481 g_slist_free(session->capability);
2482 session->capability = NULL;
2485 /* low-level IMAP4rev1 commands */
2487 static gint imap_cmd_login(IMAPSession *session,
2488 const gchar *user, const gchar *pass,
2494 log_print("IMAP4> Logging %s to %s using %s\n",
2496 SESSION(session)->server,
2498 r = imap_threaded_login(session->folder, user, pass, type);
2499 if (r != MAILIMAP_NO_ERROR) {
2500 log_print("IMAP4< Error logging in to %s\n",
2501 SESSION(session)->server);
2504 log_print("IMAP4< Login to %s successful\n",
2505 SESSION(session)->server);
2511 static gint imap_cmd_logout(IMAPSession *session)
2513 imap_threaded_disconnect(session->folder);
2515 return IMAP_SUCCESS;
2518 static gint imap_cmd_noop(IMAPSession *session)
2521 unsigned int exists;
2523 r = imap_threaded_noop(session->folder, &exists);
2524 if (r != MAILIMAP_NO_ERROR) {
2525 debug_print("noop err %d\n", r);
2528 session->exists = exists;
2529 session_set_access_time(SESSION(session));
2531 return IMAP_SUCCESS;
2535 static gint imap_cmd_starttls(IMAPSession *session)
2539 r = imap_threaded_starttls(session->folder,
2540 SESSION(session)->server, SESSION(session)->port);
2541 if (r != MAILIMAP_NO_ERROR) {
2542 debug_print("starttls err %d\n", r);
2545 return IMAP_SUCCESS;
2549 static gint imap_cmd_select(IMAPSession *session, const gchar *folder,
2550 gint *exists, gint *recent, gint *unseen,
2551 guint32 *uid_validity, gboolean block)
2555 r = imap_threaded_select(session->folder, folder,
2556 exists, recent, unseen, uid_validity);
2557 if (r != MAILIMAP_NO_ERROR) {
2558 debug_print("select err %d\n", r);
2561 return IMAP_SUCCESS;
2564 static gint imap_cmd_examine(IMAPSession *session, const gchar *folder,
2565 gint *exists, gint *recent, gint *unseen,
2566 guint32 *uid_validity, gboolean block)
2570 r = imap_threaded_examine(session->folder, folder,
2571 exists, recent, unseen, uid_validity);
2572 if (r != MAILIMAP_NO_ERROR) {
2573 debug_print("examine err %d\n", r);
2577 return IMAP_SUCCESS;
2580 static gint imap_cmd_create(IMAPSession *session, const gchar *folder)
2584 r = imap_threaded_create(session->folder, folder);
2585 if (r != MAILIMAP_NO_ERROR) {
2590 return IMAP_SUCCESS;
2593 static gint imap_cmd_rename(IMAPSession *session, const gchar *old_folder,
2594 const gchar *new_folder)
2598 r = imap_threaded_rename(session->folder, old_folder,
2600 if (r != MAILIMAP_NO_ERROR) {
2605 return IMAP_SUCCESS;
2608 static gint imap_cmd_delete(IMAPSession *session, const gchar *folder)
2613 r = imap_threaded_delete(session->folder, folder);
2614 if (r != MAILIMAP_NO_ERROR) {
2619 return IMAP_SUCCESS;
2622 typedef struct _fetch_data {
2623 IMAPSession *session;
2625 const gchar *filename;
2631 static void *imap_cmd_fetch_thread(void *data)
2633 fetch_data *stuff = (fetch_data *)data;
2634 IMAPSession *session = stuff->session;
2635 guint32 uid = stuff->uid;
2636 const gchar *filename = stuff->filename;
2640 r = imap_threaded_fetch_content(session->folder,
2644 r = imap_threaded_fetch_content(session->folder,
2647 if (r != MAILIMAP_NO_ERROR) {
2648 debug_print("fetch err %d\n", r);
2649 return GINT_TO_POINTER(IMAP_ERROR);
2651 return GINT_TO_POINTER(IMAP_SUCCESS);
2654 static gint imap_cmd_fetch(IMAPSession *session, guint32 uid,
2655 const gchar *filename, gboolean headers,
2658 fetch_data *data = g_new0(fetch_data, 1);
2661 data->session = session;
2663 data->filename = filename;
2664 data->headers = headers;
2667 if (prefs_common.work_offline &&
2668 !inc_offline_should_override(
2669 _("Sylpheed-Claws needs network access in order "
2670 "to access the IMAP server."))) {
2674 statusbar_print_all(_("Fetching message..."));
2675 result = GPOINTER_TO_INT(imap_cmd_fetch_thread(data));
2676 statusbar_pop_all();
2682 static gint imap_cmd_append(IMAPSession *session, const gchar *destfolder,
2683 const gchar *file, IMAPFlags flags,
2686 struct mailimap_flag_list * flag_list;
2689 g_return_val_if_fail(file != NULL, IMAP_ERROR);
2691 flag_list = imap_flag_to_lep(flags);
2692 r = imap_threaded_append(session->folder, destfolder,
2693 file, flag_list, new_uid);
2694 mailimap_flag_list_free(flag_list);
2696 if (r != MAILIMAP_NO_ERROR) {
2697 debug_print("append err %d\n", r);
2700 return IMAP_SUCCESS;
2703 static gint imap_cmd_copy(IMAPSession *session, struct mailimap_set * set,
2704 const gchar *destfolder, GRelation *uid_mapping)
2708 g_return_val_if_fail(session != NULL, IMAP_ERROR);
2709 g_return_val_if_fail(set != NULL, IMAP_ERROR);
2710 g_return_val_if_fail(destfolder != NULL, IMAP_ERROR);
2712 r = imap_threaded_copy(session->folder, set, destfolder);
2713 if (r != MAILIMAP_NO_ERROR) {
2718 return IMAP_SUCCESS;
2721 static gint imap_cmd_store(IMAPSession *session, struct mailimap_set * set,
2722 IMAPFlags flags, int do_add)
2725 struct mailimap_flag_list * flag_list;
2726 struct mailimap_store_att_flags * store_att_flags;
2728 flag_list = imap_flag_to_lep(flags);
2732 mailimap_store_att_flags_new_add_flags_silent(flag_list);
2735 mailimap_store_att_flags_new_remove_flags_silent(flag_list);
2737 r = imap_threaded_store(session->folder, set, store_att_flags);
2738 mailimap_store_att_flags_free(store_att_flags);
2739 if (r != MAILIMAP_NO_ERROR) {
2744 return IMAP_SUCCESS;
2747 static gint imap_cmd_expunge(IMAPSession *session)
2751 if (prefs_common.work_offline &&
2752 !inc_offline_should_override(
2753 _("Sylpheed-Claws needs network access in order "
2754 "to access the IMAP server."))) {
2758 r = imap_threaded_expunge(session->folder);
2759 if (r != MAILIMAP_NO_ERROR) {
2764 return IMAP_SUCCESS;
2767 static void imap_path_separator_subst(gchar *str, gchar separator)
2770 gboolean in_escape = FALSE;
2772 if (!separator || separator == '/') return;
2774 for (p = str; *p != '\0'; p++) {
2775 if (*p == '/' && !in_escape)
2777 else if (*p == '&' && *(p + 1) != '-' && !in_escape)
2779 else if (*p == '-' && in_escape)
2784 static gchar *imap_modified_utf7_to_utf8(const gchar *mutf7_str)
2786 static iconv_t cd = (iconv_t)-1;
2787 static gboolean iconv_ok = TRUE;
2790 size_t norm_utf7_len;
2792 gchar *to_str, *to_p;
2794 gboolean in_escape = FALSE;
2796 if (!iconv_ok) return g_strdup(mutf7_str);
2798 if (cd == (iconv_t)-1) {
2799 cd = iconv_open(CS_INTERNAL, CS_UTF_7);
2800 if (cd == (iconv_t)-1) {
2801 g_warning("iconv cannot convert UTF-7 to %s\n",
2804 return g_strdup(mutf7_str);
2808 /* modified UTF-7 to normal UTF-7 conversion */
2809 norm_utf7 = g_string_new(NULL);
2811 for (p = mutf7_str; *p != '\0'; p++) {
2812 /* replace: '&' -> '+',
2814 escaped ',' -> '/' */
2815 if (!in_escape && *p == '&') {
2816 if (*(p + 1) != '-') {
2817 g_string_append_c(norm_utf7, '+');
2820 g_string_append_c(norm_utf7, '&');
2823 } else if (in_escape && *p == ',') {
2824 g_string_append_c(norm_utf7, '/');
2825 } else if (in_escape && *p == '-') {
2826 g_string_append_c(norm_utf7, '-');
2829 g_string_append_c(norm_utf7, *p);
2833 norm_utf7_p = norm_utf7->str;
2834 norm_utf7_len = norm_utf7->len;
2835 to_len = strlen(mutf7_str) * 5;
2836 to_p = to_str = g_malloc(to_len + 1);
2838 if (iconv(cd, (ICONV_CONST gchar **)&norm_utf7_p, &norm_utf7_len,
2839 &to_p, &to_len) == -1) {
2840 g_warning(_("iconv cannot convert UTF-7 to %s\n"),
2841 conv_get_locale_charset_str());
2842 g_string_free(norm_utf7, TRUE);
2844 return g_strdup(mutf7_str);
2847 /* second iconv() call for flushing */
2848 iconv(cd, NULL, NULL, &to_p, &to_len);
2849 g_string_free(norm_utf7, TRUE);
2855 static gchar *imap_utf8_to_modified_utf7(const gchar *from)
2857 static iconv_t cd = (iconv_t)-1;
2858 static gboolean iconv_ok = TRUE;
2859 gchar *norm_utf7, *norm_utf7_p;
2860 size_t from_len, norm_utf7_len;
2862 gchar *from_tmp, *to, *p;
2863 gboolean in_escape = FALSE;
2865 if (!iconv_ok) return g_strdup(from);
2867 if (cd == (iconv_t)-1) {
2868 cd = iconv_open(CS_UTF_7, CS_INTERNAL);
2869 if (cd == (iconv_t)-1) {
2870 g_warning(_("iconv cannot convert %s to UTF-7\n"),
2873 return g_strdup(from);
2877 /* UTF-8 to normal UTF-7 conversion */
2878 Xstrdup_a(from_tmp, from, return g_strdup(from));
2879 from_len = strlen(from);
2880 norm_utf7_len = from_len * 5;
2881 Xalloca(norm_utf7, norm_utf7_len + 1, return g_strdup(from));
2882 norm_utf7_p = norm_utf7;
2884 #define IS_PRINT(ch) (isprint(ch) && IS_ASCII(ch))
2886 while (from_len > 0) {
2887 if (*from_tmp == '+') {
2888 *norm_utf7_p++ = '+';
2889 *norm_utf7_p++ = '-';
2893 } else if (IS_PRINT(*(guchar *)from_tmp)) {
2894 /* printable ascii char */
2895 *norm_utf7_p = *from_tmp;
2901 size_t conv_len = 0;
2903 /* unprintable char: convert to UTF-7 */
2905 while (!IS_PRINT(*(guchar *)p) && conv_len < from_len) {
2906 conv_len += g_utf8_skip[*(guchar *)p];
2907 p += g_utf8_skip[*(guchar *)p];
2910 from_len -= conv_len;
2911 if (iconv(cd, (ICONV_CONST gchar **)&from_tmp,
2913 &norm_utf7_p, &norm_utf7_len) == -1) {
2914 g_warning(_("iconv cannot convert UTF-8 to UTF-7\n"));
2915 return g_strdup(from);
2918 /* second iconv() call for flushing */
2919 iconv(cd, NULL, NULL, &norm_utf7_p, &norm_utf7_len);
2925 *norm_utf7_p = '\0';
2926 to_str = g_string_new(NULL);
2927 for (p = norm_utf7; p < norm_utf7_p; p++) {
2928 /* replace: '&' -> "&-",
2931 BASE64 '/' -> ',' */
2932 if (!in_escape && *p == '&') {
2933 g_string_append(to_str, "&-");
2934 } else if (!in_escape && *p == '+') {
2935 if (*(p + 1) == '-') {
2936 g_string_append_c(to_str, '+');
2939 g_string_append_c(to_str, '&');
2942 } else if (in_escape && *p == '/') {
2943 g_string_append_c(to_str, ',');
2944 } else if (in_escape && *p == '-') {
2945 g_string_append_c(to_str, '-');
2948 g_string_append_c(to_str, *p);
2954 g_string_append_c(to_str, '-');
2958 g_string_free(to_str, FALSE);
2963 static gboolean imap_rename_folder_func(GNode *node, gpointer data)
2965 FolderItem *item = node->data;
2966 gchar **paths = data;
2967 const gchar *oldpath = paths[0];
2968 const gchar *newpath = paths[1];
2970 gchar *new_itempath;
2973 oldpathlen = strlen(oldpath);
2974 if (strncmp(oldpath, item->path, oldpathlen) != 0) {
2975 g_warning("path doesn't match: %s, %s\n", oldpath, item->path);
2979 base = item->path + oldpathlen;
2980 while (*base == G_DIR_SEPARATOR) base++;
2982 new_itempath = g_strdup(newpath);
2984 new_itempath = g_strconcat(newpath, G_DIR_SEPARATOR_S, base,
2987 item->path = new_itempath;
2992 typedef struct _get_list_uid_data {
2994 IMAPSession *session;
2995 IMAPFolderItem *item;
2996 GSList **msgnum_list;
2998 } get_list_uid_data;
3000 static void *get_list_of_uids_thread(void *data)
3002 get_list_uid_data *stuff = (get_list_uid_data *)data;
3003 Folder *folder = stuff->folder;
3004 IMAPFolderItem *item = stuff->item;
3005 GSList **msgnum_list = stuff->msgnum_list;
3006 gint ok, nummsgs = 0, lastuid_old;
3007 IMAPSession *session;
3008 GSList *uidlist, *elem;
3009 struct mailimap_set * set;
3010 clist * lep_uidlist;
3013 session = stuff->session;
3014 if (session == NULL) {
3016 return GINT_TO_POINTER(-1);
3018 /* no session locking here, it's already locked by caller */
3019 ok = imap_select(session, IMAP_FOLDER(folder), item->item.path,
3020 NULL, NULL, NULL, NULL, TRUE);
3021 if (ok != IMAP_SUCCESS) {
3023 return GINT_TO_POINTER(-1);
3028 set = mailimap_set_new_interval(item->lastuid + 1, 0);
3030 r = imap_threaded_search(folder, IMAP_SEARCH_TYPE_SIMPLE, set,
3032 mailimap_set_free(set);
3034 if (r == MAILIMAP_NO_ERROR) {
3035 GSList * fetchuid_list;
3038 imap_uid_list_from_lep(lep_uidlist);
3039 mailimap_search_result_free(lep_uidlist);
3041 uidlist = g_slist_concat(fetchuid_list, uidlist);
3044 GSList * fetchuid_list;
3045 carray * lep_uidtab;
3047 r = imap_threaded_fetch_uid(folder, item->lastuid + 1,
3049 if (r == MAILIMAP_NO_ERROR) {
3051 imap_uid_list_from_lep_tab(lep_uidtab);
3052 imap_fetch_uid_list_free(lep_uidtab);
3053 uidlist = g_slist_concat(fetchuid_list, uidlist);
3057 lastuid_old = item->lastuid;
3058 *msgnum_list = g_slist_copy(item->uid_list);
3059 nummsgs = g_slist_length(*msgnum_list);
3060 debug_print("Got %d uids from cache\n", g_slist_length(item->uid_list));
3062 for (elem = uidlist; elem != NULL; elem = g_slist_next(elem)) {
3065 msgnum = GPOINTER_TO_INT(elem->data);
3066 if (msgnum > lastuid_old) {
3067 *msgnum_list = g_slist_prepend(*msgnum_list, GINT_TO_POINTER(msgnum));
3068 item->uid_list = g_slist_prepend(item->uid_list, GINT_TO_POINTER(msgnum));
3071 if(msgnum > item->lastuid)
3072 item->lastuid = msgnum;
3075 g_slist_free(uidlist);
3077 return GINT_TO_POINTER(nummsgs);
3080 static gint get_list_of_uids(IMAPSession *session, Folder *folder, IMAPFolderItem *item, GSList **msgnum_list)
3083 get_list_uid_data *data = g_new0(get_list_uid_data, 1);
3085 data->folder = folder;
3087 data->msgnum_list = msgnum_list;
3088 data->session = session;
3089 if (prefs_common.work_offline &&
3090 !inc_offline_should_override(
3091 _("Sylpheed-Claws needs network access in order "
3092 "to access the IMAP server."))) {
3097 result = GPOINTER_TO_INT(get_list_of_uids_thread(data));
3103 gint imap_get_num_list(Folder *folder, FolderItem *_item, GSList **msgnum_list, gboolean *old_uids_valid)
3105 IMAPFolderItem *item = (IMAPFolderItem *)_item;
3106 IMAPSession *session;
3107 gint ok, nummsgs = 0, exists, uid_val, uid_next = 0;
3108 GSList *uidlist = NULL;
3110 gboolean selected_folder;
3112 debug_print("get_num_list\n");
3114 g_return_val_if_fail(folder != NULL, -1);
3115 g_return_val_if_fail(item != NULL, -1);
3116 g_return_val_if_fail(item->item.path != NULL, -1);
3117 g_return_val_if_fail(FOLDER_CLASS(folder) == &imap_class, -1);
3118 g_return_val_if_fail(folder->account != NULL, -1);
3120 session = imap_session_get(folder);
3121 g_return_val_if_fail(session != NULL, -1);
3124 if (FOLDER_ITEM(item)->path)
3125 statusbar_print_all(_("Scanning folder %s%c%s ..."),
3126 FOLDER_ITEM(item)->folder->name,
3128 FOLDER_ITEM(item)->path);
3130 statusbar_print_all(_("Scanning folder %s ..."),
3131 FOLDER_ITEM(item)->folder->name);
3133 selected_folder = (session->mbox != NULL) &&
3134 (!strcmp(session->mbox, item->item.path));
3135 if (selected_folder && time(NULL) - item->use_cache < 2) {
3136 ok = imap_cmd_noop(session);
3137 if (ok != IMAP_SUCCESS) {
3138 debug_print("disconnected!\n");
3139 session = imap_reconnect_if_possible(folder, session);
3140 if (session == NULL) {
3141 statusbar_pop_all();
3146 exists = session->exists;
3148 uid_next = item->c_uid_next;
3149 uid_val = item->c_uid_validity;
3150 *old_uids_valid = TRUE;
3152 if (item->use_cache && time(NULL) - item->use_cache < 2) {
3153 exists = item->c_messages;
3154 uid_next = item->c_uid_next;
3155 uid_val = item->c_uid_validity;
3157 debug_print("using cache %d %d %d\n", exists, uid_next, uid_val);
3159 ok = imap_status(session, IMAP_FOLDER(folder), item->item.path, item,
3160 &exists, &uid_next, &uid_val, NULL, FALSE);
3162 item->item.last_num = uid_next - 1;
3164 item->use_cache = (time_t)0;
3165 if (ok != IMAP_SUCCESS) {
3166 statusbar_pop_all();
3170 if(item->item.mtime == uid_val)
3171 *old_uids_valid = TRUE;
3173 *old_uids_valid = FALSE;
3175 debug_print("Freeing imap uid cache\n");
3177 g_slist_free(item->uid_list);
3178 item->uid_list = NULL;
3180 item->item.mtime = uid_val;
3182 imap_delete_all_cached_messages((FolderItem *)item);
3186 /* If old uid_next matches new uid_next we can be sure no message
3187 was added to the folder */
3188 debug_print("uid_next is %d and item->uid_next %d \n",
3189 uid_next, item->uid_next);
3190 if (uid_next == item->uid_next) {
3191 nummsgs = g_slist_length(item->uid_list);
3193 /* If number of messages is still the same we
3194 know our caches message numbers are still valid,
3195 otherwise if the number of messages has decrease
3196 we discard our cache to start a new scan to find
3197 out which numbers have been removed */
3198 if (exists == nummsgs) {
3199 debug_print("exists == nummsgs\n");
3200 *msgnum_list = g_slist_copy(item->uid_list);
3201 statusbar_pop_all();
3204 } else if (exists < nummsgs) {
3205 debug_print("Freeing imap uid cache");
3207 g_slist_free(item->uid_list);
3208 item->uid_list = NULL;
3213 *msgnum_list = NULL;
3214 statusbar_pop_all();
3219 nummsgs = get_list_of_uids(session, folder, item, &uidlist);
3222 statusbar_pop_all();
3227 if (nummsgs != exists) {
3228 /* Cache contains more messages then folder, we have cached
3229 an old UID of a message that was removed and new messages
3230 have been added too, otherwise the uid_next check would
3232 debug_print("Freeing imap uid cache");
3234 g_slist_free(item->uid_list);
3235 item->uid_list = NULL;
3237 g_slist_free(*msgnum_list);
3239 nummsgs = get_list_of_uids(session, folder, item, &uidlist);
3242 *msgnum_list = uidlist;
3244 dir = folder_item_get_path((FolderItem *)item);
3245 debug_print("removing old messages from %s\n", dir);
3246 remove_numbered_files_not_in_list(dir, *msgnum_list);
3249 item->uid_next = uid_next;
3251 debug_print("get_num_list - ok - %i\n", nummsgs);
3252 statusbar_pop_all();
3257 static MsgInfo *imap_parse_msg(const gchar *file, FolderItem *item)
3262 flags.perm_flags = MSG_NEW|MSG_UNREAD;
3263 flags.tmp_flags = 0;
3265 g_return_val_if_fail(item != NULL, NULL);
3266 g_return_val_if_fail(file != NULL, NULL);
3268 if (folder_has_parent_of_type(item, F_QUEUE)) {
3269 MSG_SET_TMP_FLAGS(flags, MSG_QUEUED);
3270 } else if (folder_has_parent_of_type(item, F_DRAFT)) {
3271 MSG_SET_TMP_FLAGS(flags, MSG_DRAFT);
3274 msginfo = procheader_parse_file(file, flags, FALSE, FALSE);
3275 if (!msginfo) return NULL;
3277 msginfo->plaintext_file = g_strdup(file);
3278 msginfo->folder = item;
3283 GSList *imap_get_msginfos(Folder *folder, FolderItem *item,
3284 GSList *msgnum_list)
3286 IMAPSession *session;
3287 MsgInfoList *ret = NULL;
3290 debug_print("get_msginfos\n");
3292 g_return_val_if_fail(folder != NULL, NULL);
3293 g_return_val_if_fail(item != NULL, NULL);
3294 g_return_val_if_fail(msgnum_list != NULL, NULL);
3296 session = imap_session_get(folder);
3297 g_return_val_if_fail(session != NULL, NULL);
3299 debug_print("IMAP getting msginfos\n");
3300 ok = imap_select(session, IMAP_FOLDER(folder), item->path,
3301 NULL, NULL, NULL, NULL, FALSE);
3302 if (ok != IMAP_SUCCESS) {
3306 if (!(folder_has_parent_of_type(item, F_DRAFT) ||
3307 folder_has_parent_of_type(item, F_QUEUE))) {
3308 ret = g_slist_concat(ret,
3309 imap_get_uncached_messages(session, item,
3312 MsgNumberList *sorted_list, *elem;
3313 gint startnum, lastnum;
3315 sorted_list = g_slist_sort(g_slist_copy(msgnum_list), g_int_compare);
3317 startnum = lastnum = GPOINTER_TO_INT(sorted_list->data);
3319 for (elem = sorted_list;; elem = g_slist_next(elem)) {
3323 num = GPOINTER_TO_INT(elem->data);
3325 if (num > lastnum + 1 || elem == NULL) {
3327 for (i = startnum; i <= lastnum; ++i) {
3330 file = imap_fetch_msg(folder, item, i);
3332 MsgInfo *msginfo = imap_parse_msg(file, item);
3333 if (msginfo != NULL) {
3334 msginfo->msgnum = i;
3335 ret = g_slist_append(ret, msginfo);
3339 session_set_access_time(SESSION(session));
3350 g_slist_free(sorted_list);
3356 MsgInfo *imap_get_msginfo(Folder *folder, FolderItem *item, gint uid)
3358 MsgInfo *msginfo = NULL;
3359 MsgInfoList *msginfolist;
3360 MsgNumberList numlist;
3362 numlist.next = NULL;
3363 numlist.data = GINT_TO_POINTER(uid);
3365 msginfolist = imap_get_msginfos(folder, item, &numlist);
3366 if (msginfolist != NULL) {
3367 msginfo = msginfolist->data;
3368 g_slist_free(msginfolist);
3374 gboolean imap_scan_required(Folder *folder, FolderItem *_item)
3376 IMAPSession *session;
3377 IMAPFolderItem *item = (IMAPFolderItem *)_item;
3378 gint ok, exists = 0, unseen = 0;
3379 guint32 uid_next, uid_val;
3380 gboolean selected_folder;
3382 g_return_val_if_fail(folder != NULL, FALSE);
3383 g_return_val_if_fail(item != NULL, FALSE);
3384 g_return_val_if_fail(item->item.folder != NULL, FALSE);
3385 g_return_val_if_fail(FOLDER_CLASS(item->item.folder) == &imap_class, FALSE);
3387 if (item->item.path == NULL)
3390 session = imap_session_get(folder);
3391 g_return_val_if_fail(session != NULL, FALSE);
3393 selected_folder = (session->mbox != NULL) &&
3394 (!strcmp(session->mbox, item->item.path));
3395 if (selected_folder && time(NULL) - item->use_cache < 2) {
3396 ok = imap_cmd_noop(session);
3397 if (ok != IMAP_SUCCESS) {
3398 debug_print("disconnected!\n");
3399 session = imap_reconnect_if_possible(folder, session);
3400 if (session == NULL)
3405 if (session->folder_content_changed
3406 || session->exists != item->item.total_msgs) {
3411 ok = imap_status(session, IMAP_FOLDER(folder), item->item.path, IMAP_FOLDER_ITEM(item),
3412 &exists, &uid_next, &uid_val, &unseen, FALSE);
3413 if (ok != IMAP_SUCCESS) {
3418 item->use_cache = time(NULL);
3419 item->c_messages = exists;
3420 item->c_uid_next = uid_next;
3421 item->c_uid_validity = uid_val;
3422 item->c_unseen = unseen;
3423 item->item.last_num = uid_next - 1;
3424 debug_print("uidnext %d, item->uid_next %d, exists %d, item->item.total_msgs %d\n",
3425 uid_next, item->uid_next, exists, item->item.total_msgs);
3426 if ((uid_next != item->uid_next) || (exists != item->item.total_msgs)
3427 || unseen != item->item.unread_msgs || uid_val != item->item.mtime) {
3436 void imap_change_flags(Folder *folder, FolderItem *item, MsgInfo *msginfo, MsgPermFlags newflags)
3438 IMAPSession *session;
3439 IMAPFlags flags_set = 0, flags_unset = 0;
3440 gint ok = IMAP_SUCCESS;
3441 MsgNumberList numlist;
3442 hashtable_data *ht_data = NULL;
3444 g_return_if_fail(folder != NULL);
3445 g_return_if_fail(folder->klass == &imap_class);
3446 g_return_if_fail(item != NULL);
3447 g_return_if_fail(item->folder == folder);
3448 g_return_if_fail(msginfo != NULL);
3449 g_return_if_fail(msginfo->folder == item);
3451 session = imap_session_get(folder);
3456 if (!MSG_IS_MARKED(msginfo->flags) && (newflags & MSG_MARKED))
3457 flags_set |= IMAP_FLAG_FLAGGED;
3458 if ( MSG_IS_MARKED(msginfo->flags) && !(newflags & MSG_MARKED))
3459 flags_unset |= IMAP_FLAG_FLAGGED;
3461 if (!MSG_IS_UNREAD(msginfo->flags) && (newflags & MSG_UNREAD))
3462 flags_unset |= IMAP_FLAG_SEEN;
3463 if ( MSG_IS_UNREAD(msginfo->flags) && !(newflags & MSG_UNREAD))
3464 flags_set |= IMAP_FLAG_SEEN;
3466 if (!MSG_IS_REPLIED(msginfo->flags) && (newflags & MSG_REPLIED))
3467 flags_set |= IMAP_FLAG_ANSWERED;
3468 if ( MSG_IS_REPLIED(msginfo->flags) && !(newflags & MSG_REPLIED))
3469 flags_unset |= IMAP_FLAG_ANSWERED;
3471 if (!MSG_IS_DELETED(msginfo->flags) && (newflags & MSG_DELETED))
3472 flags_set |= IMAP_FLAG_DELETED;
3473 if ( MSG_IS_DELETED(msginfo->flags) && !(newflags & MSG_DELETED))
3474 flags_unset |= IMAP_FLAG_DELETED;
3476 if (!flags_set && !flags_unset) {
3477 /* the changed flags were not translatable to IMAP-speak.
3478 * like MSG_POSTFILTERED */
3483 if ((ok = imap_select(session, IMAP_FOLDER(folder), msginfo->folder->path,
3484 NULL, NULL, NULL, NULL, FALSE)) != IMAP_SUCCESS) {
3488 numlist.next = NULL;
3489 numlist.data = GINT_TO_POINTER(msginfo->msgnum);
3491 if (IMAP_FOLDER_ITEM(item)->batching) {
3492 /* instead of performing an UID STORE command for each message change,
3493 * as a lot of them can change "together", we just fill in hashtables
3494 * and defer the treatment so that we're able to send only one
3497 debug_print("IMAP batch mode on, deferring flags change\n");
3499 ht_data = g_hash_table_lookup(IMAP_FOLDER_ITEM(item)->flags_set_table,
3500 GINT_TO_POINTER(flags_set));
3501 if (ht_data == NULL) {
3502 ht_data = g_new0(hashtable_data, 1);
3503 ht_data->session = session;
3504 ht_data->item = IMAP_FOLDER_ITEM(item);
3505 g_hash_table_insert(IMAP_FOLDER_ITEM(item)->flags_set_table,
3506 GINT_TO_POINTER(flags_set), ht_data);
3508 if (!g_slist_find(ht_data->msglist, GINT_TO_POINTER(msginfo->msgnum)))
3509 ht_data->msglist = g_slist_prepend(ht_data->msglist, GINT_TO_POINTER(msginfo->msgnum));
3512 ht_data = g_hash_table_lookup(IMAP_FOLDER_ITEM(item)->flags_unset_table,
3513 GINT_TO_POINTER(flags_unset));
3514 if (ht_data == NULL) {
3515 ht_data = g_new0(hashtable_data, 1);
3516 ht_data->session = session;
3517 ht_data->item = IMAP_FOLDER_ITEM(item);
3518 g_hash_table_insert(IMAP_FOLDER_ITEM(item)->flags_unset_table,
3519 GINT_TO_POINTER(flags_unset), ht_data);
3521 if (!g_slist_find(ht_data->msglist, GINT_TO_POINTER(msginfo->msgnum)))
3522 ht_data->msglist = g_slist_prepend(ht_data->msglist,
3523 GINT_TO_POINTER(msginfo->msgnum));
3526 debug_print("IMAP changing flags\n");
3528 ok = imap_set_message_flags(session, &numlist, flags_set, TRUE);
3529 if (ok != IMAP_SUCCESS) {
3536 ok = imap_set_message_flags(session, &numlist, flags_unset, FALSE);
3537 if (ok != IMAP_SUCCESS) {
3543 msginfo->flags.perm_flags = newflags;
3548 static gint imap_remove_msg(Folder *folder, FolderItem *item, gint uid)
3551 IMAPSession *session;
3553 MsgNumberList numlist;
3555 g_return_val_if_fail(folder != NULL, -1);
3556 g_return_val_if_fail(FOLDER_CLASS(folder) == &imap_class, -1);
3557 g_return_val_if_fail(item != NULL, -1);
3559 session = imap_session_get(folder);
3560 if (!session) return -1;
3562 ok = imap_select(session, IMAP_FOLDER(folder), item->path,
3563 NULL, NULL, NULL, NULL, FALSE);
3564 if (ok != IMAP_SUCCESS) {
3568 numlist.next = NULL;
3569 numlist.data = GINT_TO_POINTER(uid);
3571 ok = imap_set_message_flags
3572 (IMAP_SESSION(REMOTE_FOLDER(folder)->session),
3573 &numlist, IMAP_FLAG_DELETED, TRUE);
3574 if (ok != IMAP_SUCCESS) {
3575 log_warning(_("can't set deleted flags: %d\n"), uid);
3580 if (!session->uidplus) {
3581 ok = imap_cmd_expunge(session);
3585 uidstr = g_strdup_printf("%u", uid);
3586 ok = imap_cmd_expunge(session);
3589 if (ok != IMAP_SUCCESS) {
3590 log_warning(_("can't expunge\n"));
3595 IMAP_FOLDER_ITEM(item)->uid_list = g_slist_remove(
3596 IMAP_FOLDER_ITEM(item)->uid_list, numlist.data);
3597 dir = folder_item_get_path(item);
3598 if (is_dir_exist(dir))
3599 remove_numbered_files(dir, uid, uid);
3602 return IMAP_SUCCESS;
3605 static gint compare_msginfo(gconstpointer a, gconstpointer b)
3607 return ((MsgInfo *)a)->msgnum - ((MsgInfo *)b)->msgnum;
3610 static guint gslist_find_next_num(MsgNumberList **list, guint num)
3614 g_return_val_if_fail(list != NULL, -1);
3616 for (elem = *list; elem != NULL; elem = g_slist_next(elem))
3617 if (GPOINTER_TO_INT(elem->data) >= num)
3620 return elem != NULL ? GPOINTER_TO_INT(elem->data) : (gint)-1;
3624 * NEW and DELETED flags are not syncronized
3625 * - The NEW/RECENT flags in IMAP folders can not really be directly
3626 * modified by Sylpheed
3627 * - The DELETE/DELETED flag in IMAP and Sylpheed don't have the same
3628 * meaning, in IMAP it always removes the messages from the FolderItem
3629 * in Sylpheed it can mean to move the message to trash
3632 typedef struct _get_flags_data {
3635 MsgInfoList *msginfo_list;
3636 GRelation *msgflags;
3637 gboolean full_search;
3641 static /*gint*/ void *imap_get_flags_thread(void *data)
3643 get_flags_data *stuff = (get_flags_data *)data;
3644 Folder *folder = stuff->folder;
3645 FolderItem *item = stuff->item;
3646 MsgInfoList *msginfo_list = stuff->msginfo_list;
3647 GRelation *msgflags = stuff->msgflags;
3648 gboolean full_search = stuff->full_search;
3649 IMAPSession *session;
3650 GSList *sorted_list = NULL;
3651 GSList *unseen = NULL, *answered = NULL, *flagged = NULL, *deleted = NULL;
3652 GSList *p_unseen, *p_answered, *p_flagged, *p_deleted;
3654 GSList *seq_list, *cur;
3655 gboolean reverse_seen = FALSE;
3658 gint exists_cnt, unseen_cnt;
3659 gboolean selected_folder;
3661 if (folder == NULL || item == NULL) {
3663 return GINT_TO_POINTER(-1);
3666 session = imap_session_get(folder);
3667 if (session == NULL) {
3669 return GINT_TO_POINTER(-1);
3672 selected_folder = (session->mbox != NULL) &&
3673 (!strcmp(session->mbox, item->path));
3675 if (!selected_folder) {
3676 ok = imap_select(session, IMAP_FOLDER(folder), item->path,
3677 &exists_cnt, NULL, &unseen_cnt, NULL, TRUE);
3678 if (ok != IMAP_SUCCESS) {
3681 return GINT_TO_POINTER(-1);
3684 if (unseen_cnt > exists_cnt / 2)
3685 reverse_seen = TRUE;
3688 if (item->unread_msgs > item->total_msgs / 2)
3689 reverse_seen = TRUE;
3692 cmd_buf = g_string_new(NULL);
3694 sorted_list = g_slist_sort(g_slist_copy(msginfo_list), compare_msginfo);
3696 seq_list = imap_get_lep_set_from_msglist(msginfo_list);
3698 struct mailimap_set * set;
3699 set = mailimap_set_new_interval(1, 0);
3700 seq_list = g_slist_append(NULL, set);
3703 for (cur = seq_list; cur != NULL; cur = g_slist_next(cur)) {
3704 struct mailimap_set * imapset;
3705 clist * lep_uidlist;
3708 imapset = cur->data;
3710 r = imap_threaded_search(folder, IMAP_SEARCH_TYPE_SEEN,
3711 imapset, &lep_uidlist);
3714 r = imap_threaded_search(folder,
3715 IMAP_SEARCH_TYPE_UNSEEN,
3716 imapset, &lep_uidlist);
3718 if (r == MAILIMAP_NO_ERROR) {
3721 uidlist = imap_uid_list_from_lep(lep_uidlist);
3722 mailimap_search_result_free(lep_uidlist);
3724 unseen = g_slist_concat(unseen, uidlist);
3727 r = imap_threaded_search(folder, IMAP_SEARCH_TYPE_FLAGGED,
3728 imapset, &lep_uidlist);
3729 if (r == MAILIMAP_NO_ERROR) {
3732 uidlist = imap_uid_list_from_lep(lep_uidlist);
3733 mailimap_search_result_free(lep_uidlist);
3735 flagged = g_slist_concat(flagged, uidlist);
3738 if (item->opened || item->processing_pending || item == folder->inbox) {
3739 r = imap_threaded_search(folder, IMAP_SEARCH_TYPE_ANSWERED,
3740 imapset, &lep_uidlist);
3741 if (r == MAILIMAP_NO_ERROR) {
3744 uidlist = imap_uid_list_from_lep(lep_uidlist);
3745 mailimap_search_result_free(lep_uidlist);
3747 answered = g_slist_concat(answered, uidlist);
3750 r = imap_threaded_search(folder, IMAP_SEARCH_TYPE_DELETED,
3751 imapset, &lep_uidlist);
3752 if (r == MAILIMAP_NO_ERROR) {
3755 uidlist = imap_uid_list_from_lep(lep_uidlist);
3756 mailimap_search_result_free(lep_uidlist);
3758 deleted = g_slist_concat(deleted, uidlist);
3764 p_answered = answered;
3765 p_flagged = flagged;
3766 p_deleted = deleted;
3768 for (elem = sorted_list; elem != NULL; elem = g_slist_next(elem)) {
3773 msginfo = (MsgInfo *) elem->data;
3774 flags = msginfo->flags.perm_flags;
3775 wasnew = (flags & MSG_NEW);
3776 if (item->opened || item->processing_pending || item == folder->inbox) {
3777 flags &= ~((reverse_seen ? 0 : MSG_UNREAD | MSG_NEW) | MSG_REPLIED | MSG_MARKED);
3779 flags &= ~((reverse_seen ? 0 : MSG_UNREAD | MSG_NEW | MSG_MARKED));
3782 flags |= MSG_UNREAD | (wasnew ? MSG_NEW : 0);
3783 if (gslist_find_next_num(&p_unseen, msginfo->msgnum) == msginfo->msgnum) {
3784 if (!reverse_seen) {
3785 flags |= MSG_UNREAD | (wasnew ? MSG_NEW : 0);
3787 flags &= ~(MSG_UNREAD | MSG_NEW);
3791 if (gslist_find_next_num(&p_flagged, msginfo->msgnum) == msginfo->msgnum)
3792 flags |= MSG_MARKED;
3794 flags &= ~MSG_MARKED;
3796 if (item->opened || item->processing_pending || item == folder->inbox) {
3797 if (gslist_find_next_num(&p_answered, msginfo->msgnum) == msginfo->msgnum)
3798 flags |= MSG_REPLIED;
3800 flags &= ~MSG_REPLIED;
3801 if (gslist_find_next_num(&p_deleted, msginfo->msgnum) == msginfo->msgnum)
3802 flags |= MSG_DELETED;
3804 flags &= ~MSG_DELETED;
3806 g_relation_insert(msgflags, msginfo, GINT_TO_POINTER(flags));
3809 imap_lep_set_free(seq_list);
3810 g_slist_free(flagged);
3811 g_slist_free(deleted);
3812 g_slist_free(answered);
3813 g_slist_free(unseen);
3814 g_slist_free(sorted_list);
3815 g_string_free(cmd_buf, TRUE);
3819 return GINT_TO_POINTER(0);
3822 static gint imap_get_flags(Folder *folder, FolderItem *item,
3823 MsgInfoList *msginfo_list, GRelation *msgflags)
3826 get_flags_data *data = g_new0(get_flags_data, 1);
3828 data->folder = folder;
3830 data->msginfo_list = msginfo_list;
3831 data->msgflags = msgflags;
3832 data->full_search = FALSE;
3834 GSList *tmp = NULL, *cur;
3836 if (prefs_common.work_offline &&
3837 !inc_offline_should_override(
3838 _("Sylpheed-Claws needs network access in order "
3839 "to access the IMAP server."))) {
3844 tmp = folder_item_get_msg_list(item);
3846 if (g_slist_length(tmp) <= g_slist_length(msginfo_list))
3847 data->full_search = TRUE;
3849 for (cur = tmp; cur; cur = cur->next)
3850 procmsg_msginfo_free((MsgInfo *)cur->data);
3854 result = GPOINTER_TO_INT(imap_get_flags_thread(data));
3861 static gboolean process_flags(gpointer key, gpointer value, gpointer user_data)
3863 gboolean flags_set = GPOINTER_TO_INT(user_data);
3864 gint flags_value = GPOINTER_TO_INT(key);
3865 hashtable_data *data = (hashtable_data *)value;
3866 IMAPFolderItem *_item = data->item;
3867 FolderItem *item = (FolderItem *)_item;
3868 gint ok = IMAP_ERROR;
3869 IMAPSession *session = imap_session_get(item->folder);
3871 data->msglist = g_slist_reverse(data->msglist);
3873 debug_print("IMAP %ssetting flags to %d for %d messages\n",
3876 g_slist_length(data->msglist));
3880 ok = imap_select(session, IMAP_FOLDER(item->folder), item->path,
3881 NULL, NULL, NULL, NULL, FALSE);
3883 if (ok == IMAP_SUCCESS) {
3884 imap_set_message_flags(data->session, data->msglist, flags_value, flags_set);
3886 g_warning("can't select mailbox %s\n", item->path);
3890 g_slist_free(data->msglist);
3895 static void process_hashtable(IMAPFolderItem *item)
3897 if (item->flags_set_table) {
3898 g_hash_table_foreach_remove(item->flags_set_table, process_flags, GINT_TO_POINTER(TRUE));
3899 g_hash_table_destroy(item->flags_set_table);
3900 item->flags_set_table = NULL;
3902 if (item->flags_unset_table) {
3903 g_hash_table_foreach_remove(item->flags_unset_table, process_flags, GINT_TO_POINTER(FALSE));
3904 g_hash_table_destroy(item->flags_unset_table);
3905 item->flags_unset_table = NULL;
3909 static void imap_set_batch (Folder *folder, FolderItem *_item, gboolean batch)
3911 IMAPFolderItem *item = (IMAPFolderItem *)_item;
3913 g_return_if_fail(item != NULL);
3915 if (item->batching == batch)
3919 item->batching = TRUE;
3920 debug_print("IMAP switching to batch mode\n");
3921 if (!item->flags_set_table) {
3922 item->flags_set_table = g_hash_table_new(NULL, g_direct_equal);
3924 if (!item->flags_unset_table) {
3925 item->flags_unset_table = g_hash_table_new(NULL, g_direct_equal);
3928 debug_print("IMAP switching away from batch mode\n");
3930 process_hashtable(item);
3931 item->batching = FALSE;
3937 /* data types conversion libetpan <-> sylpheed */
3941 #define ETPAN_IMAP_MB_MARKED 1
3942 #define ETPAN_IMAP_MB_UNMARKED 2
3943 #define ETPAN_IMAP_MB_NOSELECT 4
3944 #define ETPAN_IMAP_MB_NOINFERIORS 8
3946 static int imap_flags_to_flags(struct mailimap_mbx_list_flags * imap_flags)
3952 if (imap_flags->mbf_type == MAILIMAP_MBX_LIST_FLAGS_SFLAG) {
3953 switch (imap_flags->mbf_sflag) {
3954 case MAILIMAP_MBX_LIST_SFLAG_MARKED:
3955 flags |= ETPAN_IMAP_MB_MARKED;
3957 case MAILIMAP_MBX_LIST_SFLAG_NOSELECT:
3958 flags |= ETPAN_IMAP_MB_NOSELECT;
3960 case MAILIMAP_MBX_LIST_SFLAG_UNMARKED:
3961 flags |= ETPAN_IMAP_MB_UNMARKED;
3966 for(cur = clist_begin(imap_flags->mbf_oflags) ; cur != NULL ;
3967 cur = clist_next(cur)) {
3968 struct mailimap_mbx_list_oflag * oflag;
3970 oflag = clist_content(cur);
3972 switch (oflag->of_type) {
3973 case MAILIMAP_MBX_LIST_OFLAG_NOINFERIORS:
3974 flags |= ETPAN_IMAP_MB_NOINFERIORS;
3982 static GSList * imap_list_from_lep(IMAPFolder * folder,
3983 clist * list, const gchar * real_path, gboolean all)
3990 for(iter = clist_begin(list) ; iter != NULL ;
3991 iter = clist_next(iter)) {
3992 struct mailimap_mailbox_list * mb;
4000 FolderItem *new_item;
4002 mb = clist_content(iter);
4008 if (mb->mb_flag != NULL)
4009 flags = imap_flags_to_flags(mb->mb_flag);
4011 delimiter = mb->mb_delimiter;
4014 dup_name = strdup(name);
4015 if (delimiter != '\0')
4016 subst_char(dup_name, delimiter, '/');
4018 base = g_path_get_basename(dup_name);
4019 if (base[0] == '.') {
4025 if (!all && strcmp(dup_name, real_path) == 0) {
4031 if (!all && dup_name[strlen(dup_name)-1] == '/') {
4037 loc_name = imap_modified_utf7_to_utf8(base);
4038 loc_path = imap_modified_utf7_to_utf8(dup_name);
4040 new_item = folder_item_new(FOLDER(folder), loc_name, loc_path);
4041 if ((flags & ETPAN_IMAP_MB_NOINFERIORS) != 0)
4042 new_item->no_sub = TRUE;
4043 if (strcmp(dup_name, "INBOX") != 0 &&
4044 ((flags & ETPAN_IMAP_MB_NOSELECT) != 0))
4045 new_item->no_select = TRUE;
4047 item_list = g_slist_append(item_list, new_item);
4049 debug_print("folder '%s' found.\n", loc_path);
4060 static GSList * imap_get_lep_set_from_numlist(MsgNumberList *numlist)
4062 GSList *sorted_list, *cur;
4063 guint first, last, next;
4064 GSList *ret_list = NULL;
4066 struct mailimap_set * current_set;
4067 unsigned int item_count;
4069 if (numlist == NULL)
4073 current_set = mailimap_set_new_empty();
4075 sorted_list = g_slist_copy(numlist);
4076 sorted_list = g_slist_sort(sorted_list, g_int_compare);
4078 first = GPOINTER_TO_INT(sorted_list->data);
4081 for (cur = sorted_list; cur != NULL; cur = g_slist_next(cur)) {
4082 if (GPOINTER_TO_INT(cur->data) == 0)
4087 last = GPOINTER_TO_INT(cur->data);
4089 next = GPOINTER_TO_INT(cur->next->data);
4093 if (last + 1 != next || next == 0) {
4095 struct mailimap_set_item * item;
4096 item = mailimap_set_item_new(first, last);
4097 mailimap_set_add(current_set, item);
4102 if (count >= IMAP_SET_MAX_COUNT) {
4103 ret_list = g_slist_append(ret_list,
4105 current_set = mailimap_set_new_empty();
4112 if (clist_count(current_set->set_list) > 0) {
4113 ret_list = g_slist_append(ret_list,
4117 g_slist_free(sorted_list);
4122 static GSList * imap_get_lep_set_from_msglist(MsgInfoList *msglist)
4124 MsgNumberList *numlist = NULL;
4128 for (cur = msglist; cur != NULL; cur = g_slist_next(cur)) {
4129 MsgInfo *msginfo = (MsgInfo *) cur->data;
4131 numlist = g_slist_append(numlist, GINT_TO_POINTER(msginfo->msgnum));
4133 seq_list = imap_get_lep_set_from_numlist(numlist);
4134 g_slist_free(numlist);
4139 static GSList * imap_uid_list_from_lep(clist * list)
4146 for(iter = clist_begin(list) ; iter != NULL ;
4147 iter = clist_next(iter)) {
4150 puid = clist_content(iter);
4151 result = g_slist_append(result, GINT_TO_POINTER(* puid));
4157 static GSList * imap_uid_list_from_lep_tab(carray * list)
4164 for(i = 0 ; i < carray_count(list) ; i ++) {
4167 puid = carray_get(list, i);
4168 result = g_slist_append(result, GINT_TO_POINTER(* puid));
4174 static MsgInfo *imap_envelope_from_lep(struct imap_fetch_env_info * info,
4177 MsgInfo *msginfo = NULL;
4180 MsgFlags flags = {0, 0};
4182 if (info->headers == NULL)
4185 MSG_SET_TMP_FLAGS(flags, MSG_IMAP);
4186 if (folder_has_parent_of_type(item, F_QUEUE)) {
4187 MSG_SET_TMP_FLAGS(flags, MSG_QUEUED);
4188 } else if (folder_has_parent_of_type(item, F_DRAFT)) {
4189 MSG_SET_TMP_FLAGS(flags, MSG_DRAFT);
4191 flags.perm_flags = info->flags;
4195 msginfo = procheader_parse_str(info->headers, flags, FALSE, FALSE);
4198 msginfo->msgnum = uid;
4199 msginfo->size = size;
4205 static void imap_lep_set_free(GSList *seq_list)
4209 for(cur = seq_list ; cur != NULL ; cur = g_slist_next(cur)) {
4210 struct mailimap_set * imapset;
4212 imapset = cur->data;
4213 mailimap_set_free(imapset);
4215 g_slist_free(seq_list);
4218 static struct mailimap_flag_list * imap_flag_to_lep(IMAPFlags flags)
4220 struct mailimap_flag_list * flag_list;
4222 flag_list = mailimap_flag_list_new_empty();
4224 if (IMAP_IS_SEEN(flags))
4225 mailimap_flag_list_add(flag_list,
4226 mailimap_flag_new_seen());
4227 if (IMAP_IS_ANSWERED(flags))
4228 mailimap_flag_list_add(flag_list,
4229 mailimap_flag_new_answered());
4230 if (IMAP_IS_FLAGGED(flags))
4231 mailimap_flag_list_add(flag_list,
4232 mailimap_flag_new_flagged());
4233 if (IMAP_IS_DELETED(flags))
4234 mailimap_flag_list_add(flag_list,
4235 mailimap_flag_new_deleted());
4236 if (IMAP_IS_DRAFT(flags))
4237 mailimap_flag_list_add(flag_list,
4238 mailimap_flag_new_draft());
4243 guint imap_folder_get_refcnt(Folder *folder)
4245 return ((IMAPFolder *)folder)->refcnt;
4248 void imap_folder_ref(Folder *folder)
4250 ((IMAPFolder *)folder)->refcnt++;
4253 void imap_folder_unref(Folder *folder)
4255 if (((IMAPFolder *)folder)->refcnt > 0)
4256 ((IMAPFolder *)folder)->refcnt--;
4259 #else /* HAVE_LIBETPAN */
4261 static FolderClass imap_class;
4263 static XMLTag *imap_item_get_xml(Folder *folder, FolderItem *item);
4264 static void imap_item_set_xml(Folder *folder, FolderItem *item, XMLTag *tag);
4266 static Folder *imap_folder_new (const gchar *name,
4271 static gint imap_create_tree (Folder *folder)
4275 static FolderItem *imap_create_folder (Folder *folder,
4281 static gint imap_rename_folder (Folder *folder,
4288 gchar imap_get_path_separator_for_item(FolderItem *item)
4293 FolderClass *imap_get_class(void)
4295 if (imap_class.idstr == NULL) {
4296 imap_class.type = F_IMAP;
4297 imap_class.idstr = "imap";
4298 imap_class.uistr = "IMAP4";
4300 imap_class.new_folder = imap_folder_new;
4301 imap_class.create_tree = imap_create_tree;
4302 imap_class.create_folder = imap_create_folder;
4303 imap_class.rename_folder = imap_rename_folder;
4305 imap_class.set_xml = folder_set_xml;
4306 imap_class.get_xml = folder_get_xml;
4307 imap_class.item_set_xml = imap_item_set_xml;
4308 imap_class.item_get_xml = imap_item_get_xml;
4309 /* nothing implemented */
4316 void imap_synchronise(FolderItem *item)
4318 imap_gtk_synchronise(item);
4321 static void imap_item_set_xml(Folder *folder, FolderItem *item, XMLTag *tag)
4323 #ifdef HAVE_LIBETPAN
4326 folder_item_set_xml(folder, item, tag);
4328 #ifdef HAVE_LIBETPAN
4329 for (cur = tag->attr; cur != NULL; cur = g_list_next(cur)) {
4330 XMLAttr *attr = (XMLAttr *) cur->data;
4332 if (!attr || !attr->name || !attr->value) continue;
4333 if (!strcmp(attr->name, "uidnext"))
4334 IMAP_FOLDER_ITEM(item)->uid_next = atoi(attr->value);
4339 static XMLTag *imap_item_get_xml(Folder *folder, FolderItem *item)
4343 tag = folder_item_get_xml(folder, item);
4345 #ifdef HAVE_LIBETPAN
4346 xml_tag_add_attr(tag, xml_attr_new_int("uidnext",
4347 IMAP_FOLDER_ITEM(item)->uid_next));