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;
2252 static gchar imap_get_path_separator(IMAPFolder *folder, const gchar *path)
2254 IMAPNameSpace *namespace;
2255 gchar separator = '/';
2257 if (folder->last_seen_separator == 0) {
2259 int r = imap_threaded_list((Folder *)folder, "", "", &lep_list);
2260 if (r != MAILIMAP_NO_ERROR) {
2261 log_warning(_("LIST failed\n"));
2265 if (clist_count(lep_list) > 0) {
2266 clistiter * iter = clist_begin(lep_list);
2267 struct mailimap_mailbox_list * mb;
2268 mb = clist_content(iter);
2270 folder->last_seen_separator = mb->mb_delimiter;
2271 debug_print("got separator: %c\n", folder->last_seen_separator);
2273 mailimap_list_result_free(lep_list);
2276 if (folder->last_seen_separator != 0) {
2277 debug_print("using separator: %c\n", folder->last_seen_separator);
2278 return folder->last_seen_separator;
2281 namespace = imap_find_namespace(folder, path);
2282 if (namespace && namespace->separator)
2283 separator = namespace->separator;
2288 static gchar *imap_get_real_path(IMAPFolder *folder, const gchar *path)
2293 g_return_val_if_fail(folder != NULL, NULL);
2294 g_return_val_if_fail(path != NULL, NULL);
2296 real_path = imap_utf8_to_modified_utf7(path);
2297 separator = imap_get_path_separator(folder, path);
2298 imap_path_separator_subst(real_path, separator);
2303 static gint imap_set_message_flags(IMAPSession *session,
2304 MsgNumberList *numlist,
2312 seq_list = imap_get_lep_set_from_numlist(numlist);
2314 for(cur = seq_list ; cur != NULL ; cur = g_slist_next(cur)) {
2315 struct mailimap_set * imapset;
2317 imapset = cur->data;
2319 ok = imap_cmd_store(session, imapset,
2323 imap_lep_set_free(seq_list);
2325 return IMAP_SUCCESS;
2328 typedef struct _select_data {
2329 IMAPSession *session;
2334 guint32 *uid_validity;
2338 static gint imap_select(IMAPSession *session, IMAPFolder *folder,
2340 gint *exists, gint *recent, gint *unseen,
2341 guint32 *uid_validity, gboolean block)
2345 gint exists_, recent_, unseen_;
2346 guint32 uid_validity_;
2348 if (!exists && !recent && !unseen && !uid_validity) {
2349 if (session->mbox && strcmp(session->mbox, path) == 0)
2350 return IMAP_SUCCESS;
2359 uid_validity = &uid_validity_;
2361 g_free(session->mbox);
2362 session->mbox = NULL;
2364 real_path = imap_get_real_path(folder, path);
2366 ok = imap_cmd_select(session, real_path,
2367 exists, recent, unseen, uid_validity, block);
2368 if (ok != IMAP_SUCCESS)
2369 log_warning(_("can't select folder: %s\n"), real_path);
2371 session->mbox = g_strdup(path);
2372 session->folder_content_changed = FALSE;
2379 static gint imap_status(IMAPSession *session, IMAPFolder *folder,
2380 const gchar *path, IMAPFolderItem *item,
2382 guint32 *uid_next, guint32 *uid_validity,
2383 gint *unseen, gboolean block)
2387 struct mailimap_mailbox_data_status * data_status;
2392 real_path = imap_get_real_path(folder, path);
2406 r = imap_threaded_status(FOLDER(folder), real_path,
2407 &data_status, mask);
2410 if (r != MAILIMAP_NO_ERROR) {
2411 debug_print("status err %d\n", r);
2415 if (data_status->st_info_list == NULL) {
2416 mailimap_mailbox_data_status_free(data_status);
2417 debug_print("status->st_info_list == NULL\n");
2422 for(iter = clist_begin(data_status->st_info_list) ; iter != NULL ;
2423 iter = clist_next(iter)) {
2424 struct mailimap_status_info * info;
2426 info = clist_content(iter);
2427 switch (info->st_att) {
2428 case MAILIMAP_STATUS_ATT_MESSAGES:
2429 * messages = info->st_value;
2430 got_values |= 1 << 0;
2433 case MAILIMAP_STATUS_ATT_UIDNEXT:
2434 * uid_next = info->st_value;
2435 got_values |= 1 << 2;
2438 case MAILIMAP_STATUS_ATT_UIDVALIDITY:
2439 * uid_validity = info->st_value;
2440 got_values |= 1 << 3;
2443 case MAILIMAP_STATUS_ATT_UNSEEN:
2444 * unseen = info->st_value;
2445 got_values |= 1 << 4;
2449 mailimap_mailbox_data_status_free(data_status);
2451 if (got_values != mask) {
2452 debug_print("status: incomplete values received (%d)\n", got_values);
2455 return IMAP_SUCCESS;
2458 static void imap_free_capabilities(IMAPSession *session)
2460 slist_free_strings(session->capability);
2461 g_slist_free(session->capability);
2462 session->capability = NULL;
2465 /* low-level IMAP4rev1 commands */
2467 static gint imap_cmd_login(IMAPSession *session,
2468 const gchar *user, const gchar *pass,
2474 log_print("IMAP4> Logging %s to %s using %s\n",
2476 SESSION(session)->server,
2478 r = imap_threaded_login(session->folder, user, pass, type);
2479 if (r != MAILIMAP_NO_ERROR) {
2480 log_print("IMAP4< Error logging in to %s\n",
2481 SESSION(session)->server);
2484 log_print("IMAP4< Login to %s successful\n",
2485 SESSION(session)->server);
2491 static gint imap_cmd_logout(IMAPSession *session)
2493 imap_threaded_disconnect(session->folder);
2495 return IMAP_SUCCESS;
2498 static gint imap_cmd_noop(IMAPSession *session)
2501 unsigned int exists;
2503 r = imap_threaded_noop(session->folder, &exists);
2504 if (r != MAILIMAP_NO_ERROR) {
2505 debug_print("noop err %d\n", r);
2508 session->exists = exists;
2509 session_set_access_time(SESSION(session));
2511 return IMAP_SUCCESS;
2515 static gint imap_cmd_starttls(IMAPSession *session)
2519 r = imap_threaded_starttls(session->folder,
2520 SESSION(session)->server, SESSION(session)->port);
2521 if (r != MAILIMAP_NO_ERROR) {
2522 debug_print("starttls err %d\n", r);
2525 return IMAP_SUCCESS;
2529 static gint imap_cmd_select(IMAPSession *session, const gchar *folder,
2530 gint *exists, gint *recent, gint *unseen,
2531 guint32 *uid_validity, gboolean block)
2535 r = imap_threaded_select(session->folder, folder,
2536 exists, recent, unseen, uid_validity);
2537 if (r != MAILIMAP_NO_ERROR) {
2538 debug_print("select err %d\n", r);
2541 return IMAP_SUCCESS;
2544 static gint imap_cmd_examine(IMAPSession *session, const gchar *folder,
2545 gint *exists, gint *recent, gint *unseen,
2546 guint32 *uid_validity, gboolean block)
2550 r = imap_threaded_examine(session->folder, folder,
2551 exists, recent, unseen, uid_validity);
2552 if (r != MAILIMAP_NO_ERROR) {
2553 debug_print("examine err %d\n", r);
2557 return IMAP_SUCCESS;
2560 static gint imap_cmd_create(IMAPSession *session, const gchar *folder)
2564 r = imap_threaded_create(session->folder, folder);
2565 if (r != MAILIMAP_NO_ERROR) {
2570 return IMAP_SUCCESS;
2573 static gint imap_cmd_rename(IMAPSession *session, const gchar *old_folder,
2574 const gchar *new_folder)
2578 r = imap_threaded_rename(session->folder, old_folder,
2580 if (r != MAILIMAP_NO_ERROR) {
2585 return IMAP_SUCCESS;
2588 static gint imap_cmd_delete(IMAPSession *session, const gchar *folder)
2593 r = imap_threaded_delete(session->folder, folder);
2594 if (r != MAILIMAP_NO_ERROR) {
2599 return IMAP_SUCCESS;
2602 typedef struct _fetch_data {
2603 IMAPSession *session;
2605 const gchar *filename;
2611 static void *imap_cmd_fetch_thread(void *data)
2613 fetch_data *stuff = (fetch_data *)data;
2614 IMAPSession *session = stuff->session;
2615 guint32 uid = stuff->uid;
2616 const gchar *filename = stuff->filename;
2620 r = imap_threaded_fetch_content(session->folder,
2624 r = imap_threaded_fetch_content(session->folder,
2627 if (r != MAILIMAP_NO_ERROR) {
2628 debug_print("fetch err %d\n", r);
2629 return GINT_TO_POINTER(IMAP_ERROR);
2631 return GINT_TO_POINTER(IMAP_SUCCESS);
2634 static gint imap_cmd_fetch(IMAPSession *session, guint32 uid,
2635 const gchar *filename, gboolean headers,
2638 fetch_data *data = g_new0(fetch_data, 1);
2641 data->session = session;
2643 data->filename = filename;
2644 data->headers = headers;
2647 if (prefs_common.work_offline &&
2648 !inc_offline_should_override(
2649 _("Sylpheed-Claws needs network access in order "
2650 "to access the IMAP server."))) {
2654 statusbar_print_all(_("Fetching message..."));
2655 result = GPOINTER_TO_INT(imap_cmd_fetch_thread(data));
2656 statusbar_pop_all();
2662 static gint imap_cmd_append(IMAPSession *session, const gchar *destfolder,
2663 const gchar *file, IMAPFlags flags,
2666 struct mailimap_flag_list * flag_list;
2669 g_return_val_if_fail(file != NULL, IMAP_ERROR);
2671 flag_list = imap_flag_to_lep(flags);
2672 r = imap_threaded_append(session->folder, destfolder,
2673 file, flag_list, new_uid);
2674 mailimap_flag_list_free(flag_list);
2676 if (r != MAILIMAP_NO_ERROR) {
2677 debug_print("append err %d\n", r);
2680 return IMAP_SUCCESS;
2683 static gint imap_cmd_copy(IMAPSession *session, struct mailimap_set * set,
2684 const gchar *destfolder, GRelation *uid_mapping)
2688 g_return_val_if_fail(session != NULL, IMAP_ERROR);
2689 g_return_val_if_fail(set != NULL, IMAP_ERROR);
2690 g_return_val_if_fail(destfolder != NULL, IMAP_ERROR);
2692 r = imap_threaded_copy(session->folder, set, destfolder);
2693 if (r != MAILIMAP_NO_ERROR) {
2698 return IMAP_SUCCESS;
2701 static gint imap_cmd_store(IMAPSession *session, struct mailimap_set * set,
2702 IMAPFlags flags, int do_add)
2705 struct mailimap_flag_list * flag_list;
2706 struct mailimap_store_att_flags * store_att_flags;
2708 flag_list = imap_flag_to_lep(flags);
2712 mailimap_store_att_flags_new_add_flags_silent(flag_list);
2715 mailimap_store_att_flags_new_remove_flags_silent(flag_list);
2717 r = imap_threaded_store(session->folder, set, store_att_flags);
2718 mailimap_store_att_flags_free(store_att_flags);
2719 if (r != MAILIMAP_NO_ERROR) {
2724 return IMAP_SUCCESS;
2727 static gint imap_cmd_expunge(IMAPSession *session)
2731 if (prefs_common.work_offline &&
2732 !inc_offline_should_override(
2733 _("Sylpheed-Claws needs network access in order "
2734 "to access the IMAP server."))) {
2738 r = imap_threaded_expunge(session->folder);
2739 if (r != MAILIMAP_NO_ERROR) {
2744 return IMAP_SUCCESS;
2747 static void imap_path_separator_subst(gchar *str, gchar separator)
2750 gboolean in_escape = FALSE;
2752 if (!separator || separator == '/') return;
2754 for (p = str; *p != '\0'; p++) {
2755 if (*p == '/' && !in_escape)
2757 else if (*p == '&' && *(p + 1) != '-' && !in_escape)
2759 else if (*p == '-' && in_escape)
2764 static gchar *imap_modified_utf7_to_utf8(const gchar *mutf7_str)
2766 static iconv_t cd = (iconv_t)-1;
2767 static gboolean iconv_ok = TRUE;
2770 size_t norm_utf7_len;
2772 gchar *to_str, *to_p;
2774 gboolean in_escape = FALSE;
2776 if (!iconv_ok) return g_strdup(mutf7_str);
2778 if (cd == (iconv_t)-1) {
2779 cd = iconv_open(CS_INTERNAL, CS_UTF_7);
2780 if (cd == (iconv_t)-1) {
2781 g_warning("iconv cannot convert UTF-7 to %s\n",
2784 return g_strdup(mutf7_str);
2788 /* modified UTF-7 to normal UTF-7 conversion */
2789 norm_utf7 = g_string_new(NULL);
2791 for (p = mutf7_str; *p != '\0'; p++) {
2792 /* replace: '&' -> '+',
2794 escaped ',' -> '/' */
2795 if (!in_escape && *p == '&') {
2796 if (*(p + 1) != '-') {
2797 g_string_append_c(norm_utf7, '+');
2800 g_string_append_c(norm_utf7, '&');
2803 } else if (in_escape && *p == ',') {
2804 g_string_append_c(norm_utf7, '/');
2805 } else if (in_escape && *p == '-') {
2806 g_string_append_c(norm_utf7, '-');
2809 g_string_append_c(norm_utf7, *p);
2813 norm_utf7_p = norm_utf7->str;
2814 norm_utf7_len = norm_utf7->len;
2815 to_len = strlen(mutf7_str) * 5;
2816 to_p = to_str = g_malloc(to_len + 1);
2818 if (iconv(cd, (ICONV_CONST gchar **)&norm_utf7_p, &norm_utf7_len,
2819 &to_p, &to_len) == -1) {
2820 g_warning(_("iconv cannot convert UTF-7 to %s\n"),
2821 conv_get_locale_charset_str());
2822 g_string_free(norm_utf7, TRUE);
2824 return g_strdup(mutf7_str);
2827 /* second iconv() call for flushing */
2828 iconv(cd, NULL, NULL, &to_p, &to_len);
2829 g_string_free(norm_utf7, TRUE);
2835 static gchar *imap_utf8_to_modified_utf7(const gchar *from)
2837 static iconv_t cd = (iconv_t)-1;
2838 static gboolean iconv_ok = TRUE;
2839 gchar *norm_utf7, *norm_utf7_p;
2840 size_t from_len, norm_utf7_len;
2842 gchar *from_tmp, *to, *p;
2843 gboolean in_escape = FALSE;
2845 if (!iconv_ok) return g_strdup(from);
2847 if (cd == (iconv_t)-1) {
2848 cd = iconv_open(CS_UTF_7, CS_INTERNAL);
2849 if (cd == (iconv_t)-1) {
2850 g_warning(_("iconv cannot convert %s to UTF-7\n"),
2853 return g_strdup(from);
2857 /* UTF-8 to normal UTF-7 conversion */
2858 Xstrdup_a(from_tmp, from, return g_strdup(from));
2859 from_len = strlen(from);
2860 norm_utf7_len = from_len * 5;
2861 Xalloca(norm_utf7, norm_utf7_len + 1, return g_strdup(from));
2862 norm_utf7_p = norm_utf7;
2864 #define IS_PRINT(ch) (isprint(ch) && IS_ASCII(ch))
2866 while (from_len > 0) {
2867 if (*from_tmp == '+') {
2868 *norm_utf7_p++ = '+';
2869 *norm_utf7_p++ = '-';
2873 } else if (IS_PRINT(*(guchar *)from_tmp)) {
2874 /* printable ascii char */
2875 *norm_utf7_p = *from_tmp;
2881 size_t conv_len = 0;
2883 /* unprintable char: convert to UTF-7 */
2885 while (!IS_PRINT(*(guchar *)p) && conv_len < from_len) {
2886 conv_len += g_utf8_skip[*(guchar *)p];
2887 p += g_utf8_skip[*(guchar *)p];
2890 from_len -= conv_len;
2891 if (iconv(cd, (ICONV_CONST gchar **)&from_tmp,
2893 &norm_utf7_p, &norm_utf7_len) == -1) {
2894 g_warning(_("iconv cannot convert UTF-8 to UTF-7\n"));
2895 return g_strdup(from);
2898 /* second iconv() call for flushing */
2899 iconv(cd, NULL, NULL, &norm_utf7_p, &norm_utf7_len);
2905 *norm_utf7_p = '\0';
2906 to_str = g_string_new(NULL);
2907 for (p = norm_utf7; p < norm_utf7_p; p++) {
2908 /* replace: '&' -> "&-",
2911 BASE64 '/' -> ',' */
2912 if (!in_escape && *p == '&') {
2913 g_string_append(to_str, "&-");
2914 } else if (!in_escape && *p == '+') {
2915 if (*(p + 1) == '-') {
2916 g_string_append_c(to_str, '+');
2919 g_string_append_c(to_str, '&');
2922 } else if (in_escape && *p == '/') {
2923 g_string_append_c(to_str, ',');
2924 } else if (in_escape && *p == '-') {
2925 g_string_append_c(to_str, '-');
2928 g_string_append_c(to_str, *p);
2934 g_string_append_c(to_str, '-');
2938 g_string_free(to_str, FALSE);
2943 static gboolean imap_rename_folder_func(GNode *node, gpointer data)
2945 FolderItem *item = node->data;
2946 gchar **paths = data;
2947 const gchar *oldpath = paths[0];
2948 const gchar *newpath = paths[1];
2950 gchar *new_itempath;
2953 oldpathlen = strlen(oldpath);
2954 if (strncmp(oldpath, item->path, oldpathlen) != 0) {
2955 g_warning("path doesn't match: %s, %s\n", oldpath, item->path);
2959 base = item->path + oldpathlen;
2960 while (*base == G_DIR_SEPARATOR) base++;
2962 new_itempath = g_strdup(newpath);
2964 new_itempath = g_strconcat(newpath, G_DIR_SEPARATOR_S, base,
2967 item->path = new_itempath;
2972 typedef struct _get_list_uid_data {
2974 IMAPSession *session;
2975 IMAPFolderItem *item;
2976 GSList **msgnum_list;
2978 } get_list_uid_data;
2980 static void *get_list_of_uids_thread(void *data)
2982 get_list_uid_data *stuff = (get_list_uid_data *)data;
2983 Folder *folder = stuff->folder;
2984 IMAPFolderItem *item = stuff->item;
2985 GSList **msgnum_list = stuff->msgnum_list;
2986 gint ok, nummsgs = 0, lastuid_old;
2987 IMAPSession *session;
2988 GSList *uidlist, *elem;
2989 struct mailimap_set * set;
2990 clist * lep_uidlist;
2993 session = stuff->session;
2994 if (session == NULL) {
2996 return GINT_TO_POINTER(-1);
2998 /* no session locking here, it's already locked by caller */
2999 ok = imap_select(session, IMAP_FOLDER(folder), item->item.path,
3000 NULL, NULL, NULL, NULL, TRUE);
3001 if (ok != IMAP_SUCCESS) {
3003 return GINT_TO_POINTER(-1);
3008 set = mailimap_set_new_interval(item->lastuid + 1, 0);
3010 r = imap_threaded_search(folder, IMAP_SEARCH_TYPE_SIMPLE, set,
3012 mailimap_set_free(set);
3014 if (r == MAILIMAP_NO_ERROR) {
3015 GSList * fetchuid_list;
3018 imap_uid_list_from_lep(lep_uidlist);
3019 mailimap_search_result_free(lep_uidlist);
3021 uidlist = g_slist_concat(fetchuid_list, uidlist);
3024 GSList * fetchuid_list;
3025 carray * lep_uidtab;
3027 r = imap_threaded_fetch_uid(folder, item->lastuid + 1,
3029 if (r == MAILIMAP_NO_ERROR) {
3031 imap_uid_list_from_lep_tab(lep_uidtab);
3032 imap_fetch_uid_list_free(lep_uidtab);
3033 uidlist = g_slist_concat(fetchuid_list, uidlist);
3037 lastuid_old = item->lastuid;
3038 *msgnum_list = g_slist_copy(item->uid_list);
3039 nummsgs = g_slist_length(*msgnum_list);
3040 debug_print("Got %d uids from cache\n", g_slist_length(item->uid_list));
3042 for (elem = uidlist; elem != NULL; elem = g_slist_next(elem)) {
3045 msgnum = GPOINTER_TO_INT(elem->data);
3046 if (msgnum > lastuid_old) {
3047 *msgnum_list = g_slist_prepend(*msgnum_list, GINT_TO_POINTER(msgnum));
3048 item->uid_list = g_slist_prepend(item->uid_list, GINT_TO_POINTER(msgnum));
3051 if(msgnum > item->lastuid)
3052 item->lastuid = msgnum;
3055 g_slist_free(uidlist);
3057 return GINT_TO_POINTER(nummsgs);
3060 static gint get_list_of_uids(IMAPSession *session, Folder *folder, IMAPFolderItem *item, GSList **msgnum_list)
3063 get_list_uid_data *data = g_new0(get_list_uid_data, 1);
3065 data->folder = folder;
3067 data->msgnum_list = msgnum_list;
3068 data->session = session;
3069 if (prefs_common.work_offline &&
3070 !inc_offline_should_override(
3071 _("Sylpheed-Claws needs network access in order "
3072 "to access the IMAP server."))) {
3077 result = GPOINTER_TO_INT(get_list_of_uids_thread(data));
3083 gint imap_get_num_list(Folder *folder, FolderItem *_item, GSList **msgnum_list, gboolean *old_uids_valid)
3085 IMAPFolderItem *item = (IMAPFolderItem *)_item;
3086 IMAPSession *session;
3087 gint ok, nummsgs = 0, exists, uid_val, uid_next;
3088 GSList *uidlist = NULL;
3090 gboolean selected_folder;
3092 debug_print("get_num_list\n");
3094 g_return_val_if_fail(folder != NULL, -1);
3095 g_return_val_if_fail(item != NULL, -1);
3096 g_return_val_if_fail(item->item.path != NULL, -1);
3097 g_return_val_if_fail(FOLDER_CLASS(folder) == &imap_class, -1);
3098 g_return_val_if_fail(folder->account != NULL, -1);
3100 session = imap_session_get(folder);
3101 g_return_val_if_fail(session != NULL, -1);
3104 if (FOLDER_ITEM(item)->path)
3105 statusbar_print_all(_("Scanning folder %s%c%s ..."),
3106 FOLDER_ITEM(item)->folder->name,
3108 FOLDER_ITEM(item)->path);
3110 statusbar_print_all(_("Scanning folder %s ..."),
3111 FOLDER_ITEM(item)->folder->name);
3113 selected_folder = (session->mbox != NULL) &&
3114 (!strcmp(session->mbox, item->item.path));
3115 if (selected_folder && time(NULL) - item->use_cache < 2) {
3116 ok = imap_cmd_noop(session);
3117 if (ok != IMAP_SUCCESS) {
3118 debug_print("disconnected!\n");
3119 session = imap_reconnect_if_possible(folder, session);
3120 if (session == NULL) {
3121 statusbar_pop_all();
3126 exists = session->exists;
3128 *old_uids_valid = TRUE;
3130 if (item->use_cache && time(NULL) - item->use_cache < 2) {
3131 exists = item->c_messages;
3132 uid_next = item->c_uid_next;
3133 uid_val = item->c_uid_validity;
3135 debug_print("using cache %d %d %d\n", exists, uid_next, uid_val);
3137 ok = imap_status(session, IMAP_FOLDER(folder), item->item.path, item,
3138 &exists, &uid_next, &uid_val, NULL, FALSE);
3140 item->item.last_num = uid_next - 1;
3142 item->use_cache = (time_t)0;
3143 if (ok != IMAP_SUCCESS) {
3144 statusbar_pop_all();
3148 if(item->item.mtime == uid_val)
3149 *old_uids_valid = TRUE;
3151 *old_uids_valid = FALSE;
3153 debug_print("Freeing imap uid cache\n");
3155 g_slist_free(item->uid_list);
3156 item->uid_list = NULL;
3158 item->item.mtime = uid_val;
3160 imap_delete_all_cached_messages((FolderItem *)item);
3164 /* If old uid_next matches new uid_next we can be sure no message
3165 was added to the folder */
3166 debug_print("uid_next is %d and item->uid_next %d \n",
3167 uid_next, item->uid_next);
3168 if (uid_next == item->uid_next) {
3169 nummsgs = g_slist_length(item->uid_list);
3171 /* If number of messages is still the same we
3172 know our caches message numbers are still valid,
3173 otherwise if the number of messages has decrease
3174 we discard our cache to start a new scan to find
3175 out which numbers have been removed */
3176 if (exists == nummsgs) {
3177 debug_print("exists == nummsgs\n");
3178 *msgnum_list = g_slist_copy(item->uid_list);
3179 statusbar_pop_all();
3182 } else if (exists < nummsgs) {
3183 debug_print("Freeing imap uid cache");
3185 g_slist_free(item->uid_list);
3186 item->uid_list = NULL;
3191 *msgnum_list = NULL;
3192 statusbar_pop_all();
3197 nummsgs = get_list_of_uids(session, folder, item, &uidlist);
3200 statusbar_pop_all();
3205 if (nummsgs != exists) {
3206 /* Cache contains more messages then folder, we have cached
3207 an old UID of a message that was removed and new messages
3208 have been added too, otherwise the uid_next check would
3210 debug_print("Freeing imap uid cache");
3212 g_slist_free(item->uid_list);
3213 item->uid_list = NULL;
3215 g_slist_free(*msgnum_list);
3217 nummsgs = get_list_of_uids(session, folder, item, &uidlist);
3220 *msgnum_list = uidlist;
3222 dir = folder_item_get_path((FolderItem *)item);
3223 debug_print("removing old messages from %s\n", dir);
3224 remove_numbered_files_not_in_list(dir, *msgnum_list);
3227 item->uid_next = uid_next;
3229 debug_print("get_num_list - ok - %i\n", nummsgs);
3230 statusbar_pop_all();
3235 static MsgInfo *imap_parse_msg(const gchar *file, FolderItem *item)
3240 flags.perm_flags = MSG_NEW|MSG_UNREAD;
3241 flags.tmp_flags = 0;
3243 g_return_val_if_fail(item != NULL, NULL);
3244 g_return_val_if_fail(file != NULL, NULL);
3246 if (folder_has_parent_of_type(item, F_QUEUE)) {
3247 MSG_SET_TMP_FLAGS(flags, MSG_QUEUED);
3248 } else if (folder_has_parent_of_type(item, F_DRAFT)) {
3249 MSG_SET_TMP_FLAGS(flags, MSG_DRAFT);
3252 msginfo = procheader_parse_file(file, flags, FALSE, FALSE);
3253 if (!msginfo) return NULL;
3255 msginfo->plaintext_file = g_strdup(file);
3256 msginfo->folder = item;
3261 GSList *imap_get_msginfos(Folder *folder, FolderItem *item,
3262 GSList *msgnum_list)
3264 IMAPSession *session;
3265 MsgInfoList *ret = NULL;
3268 debug_print("get_msginfos\n");
3270 g_return_val_if_fail(folder != NULL, NULL);
3271 g_return_val_if_fail(item != NULL, NULL);
3272 g_return_val_if_fail(msgnum_list != NULL, NULL);
3274 session = imap_session_get(folder);
3275 g_return_val_if_fail(session != NULL, NULL);
3277 debug_print("IMAP getting msginfos\n");
3278 ok = imap_select(session, IMAP_FOLDER(folder), item->path,
3279 NULL, NULL, NULL, NULL, FALSE);
3280 if (ok != IMAP_SUCCESS) {
3284 if (!(folder_has_parent_of_type(item, F_DRAFT) ||
3285 folder_has_parent_of_type(item, F_QUEUE))) {
3286 ret = g_slist_concat(ret,
3287 imap_get_uncached_messages(session, item,
3290 MsgNumberList *sorted_list, *elem;
3291 gint startnum, lastnum;
3293 sorted_list = g_slist_sort(g_slist_copy(msgnum_list), g_int_compare);
3295 startnum = lastnum = GPOINTER_TO_INT(sorted_list->data);
3297 for (elem = sorted_list;; elem = g_slist_next(elem)) {
3301 num = GPOINTER_TO_INT(elem->data);
3303 if (num > lastnum + 1 || elem == NULL) {
3305 for (i = startnum; i <= lastnum; ++i) {
3308 file = imap_fetch_msg(folder, item, i);
3310 MsgInfo *msginfo = imap_parse_msg(file, item);
3311 if (msginfo != NULL) {
3312 msginfo->msgnum = i;
3313 ret = g_slist_append(ret, msginfo);
3317 session_set_access_time(SESSION(session));
3328 g_slist_free(sorted_list);
3334 MsgInfo *imap_get_msginfo(Folder *folder, FolderItem *item, gint uid)
3336 MsgInfo *msginfo = NULL;
3337 MsgInfoList *msginfolist;
3338 MsgNumberList numlist;
3340 numlist.next = NULL;
3341 numlist.data = GINT_TO_POINTER(uid);
3343 msginfolist = imap_get_msginfos(folder, item, &numlist);
3344 if (msginfolist != NULL) {
3345 msginfo = msginfolist->data;
3346 g_slist_free(msginfolist);
3352 gboolean imap_scan_required(Folder *folder, FolderItem *_item)
3354 IMAPSession *session;
3355 IMAPFolderItem *item = (IMAPFolderItem *)_item;
3356 gint ok, exists = 0, unseen = 0;
3357 guint32 uid_next, uid_val;
3358 gboolean selected_folder;
3360 g_return_val_if_fail(folder != NULL, FALSE);
3361 g_return_val_if_fail(item != NULL, FALSE);
3362 g_return_val_if_fail(item->item.folder != NULL, FALSE);
3363 g_return_val_if_fail(FOLDER_CLASS(item->item.folder) == &imap_class, FALSE);
3365 if (item->item.path == NULL)
3368 session = imap_session_get(folder);
3369 g_return_val_if_fail(session != NULL, FALSE);
3371 selected_folder = (session->mbox != NULL) &&
3372 (!strcmp(session->mbox, item->item.path));
3373 if (selected_folder && time(NULL) - item->use_cache < 2) {
3374 ok = imap_cmd_noop(session);
3375 if (ok != IMAP_SUCCESS) {
3376 debug_print("disconnected!\n");
3377 session = imap_reconnect_if_possible(folder, session);
3378 if (session == NULL)
3383 if (session->folder_content_changed
3384 || session->exists != item->item.total_msgs) {
3389 ok = imap_status(session, IMAP_FOLDER(folder), item->item.path, IMAP_FOLDER_ITEM(item),
3390 &exists, &uid_next, &uid_val, &unseen, FALSE);
3391 if (ok != IMAP_SUCCESS) {
3396 item->use_cache = time(NULL);
3397 item->c_messages = exists;
3398 item->c_uid_next = uid_next;
3399 item->c_uid_validity = uid_val;
3400 item->c_unseen = unseen;
3401 item->item.last_num = uid_next - 1;
3402 debug_print("uidnext %d, item->uid_next %d, exists %d, item->item.total_msgs %d\n",
3403 uid_next, item->uid_next, exists, item->item.total_msgs);
3404 if ((uid_next != item->uid_next) || (exists != item->item.total_msgs)
3405 || unseen != item->item.unread_msgs || uid_val != item->item.mtime) {
3414 void imap_change_flags(Folder *folder, FolderItem *item, MsgInfo *msginfo, MsgPermFlags newflags)
3416 IMAPSession *session;
3417 IMAPFlags flags_set = 0, flags_unset = 0;
3418 gint ok = IMAP_SUCCESS;
3419 MsgNumberList numlist;
3420 hashtable_data *ht_data = NULL;
3422 g_return_if_fail(folder != NULL);
3423 g_return_if_fail(folder->klass == &imap_class);
3424 g_return_if_fail(item != NULL);
3425 g_return_if_fail(item->folder == folder);
3426 g_return_if_fail(msginfo != NULL);
3427 g_return_if_fail(msginfo->folder == item);
3429 session = imap_session_get(folder);
3434 if ((ok = imap_select(session, IMAP_FOLDER(folder), msginfo->folder->path,
3435 NULL, NULL, NULL, NULL, FALSE)) != IMAP_SUCCESS) {
3440 if (!MSG_IS_MARKED(msginfo->flags) && (newflags & MSG_MARKED))
3441 flags_set |= IMAP_FLAG_FLAGGED;
3442 if ( MSG_IS_MARKED(msginfo->flags) && !(newflags & MSG_MARKED))
3443 flags_unset |= IMAP_FLAG_FLAGGED;
3445 if (!MSG_IS_UNREAD(msginfo->flags) && (newflags & MSG_UNREAD))
3446 flags_unset |= IMAP_FLAG_SEEN;
3447 if ( MSG_IS_UNREAD(msginfo->flags) && !(newflags & MSG_UNREAD))
3448 flags_set |= IMAP_FLAG_SEEN;
3450 if (!MSG_IS_REPLIED(msginfo->flags) && (newflags & MSG_REPLIED))
3451 flags_set |= IMAP_FLAG_ANSWERED;
3452 if ( MSG_IS_REPLIED(msginfo->flags) && !(newflags & MSG_REPLIED))
3453 flags_unset |= IMAP_FLAG_ANSWERED;
3455 if (!MSG_IS_DELETED(msginfo->flags) && (newflags & MSG_DELETED))
3456 flags_set |= IMAP_FLAG_DELETED;
3457 if ( MSG_IS_DELETED(msginfo->flags) && !(newflags & MSG_DELETED))
3458 flags_unset |= IMAP_FLAG_DELETED;
3460 numlist.next = NULL;
3461 numlist.data = GINT_TO_POINTER(msginfo->msgnum);
3463 if (IMAP_FOLDER_ITEM(item)->batching) {
3464 /* instead of performing an UID STORE command for each message change,
3465 * as a lot of them can change "together", we just fill in hashtables
3466 * and defer the treatment so that we're able to send only one
3469 debug_print("IMAP batch mode on, deferring flags change\n");
3471 ht_data = g_hash_table_lookup(IMAP_FOLDER_ITEM(item)->flags_set_table,
3472 GINT_TO_POINTER(flags_set));
3473 if (ht_data == NULL) {
3474 ht_data = g_new0(hashtable_data, 1);
3475 ht_data->session = session;
3476 ht_data->item = IMAP_FOLDER_ITEM(item);
3477 g_hash_table_insert(IMAP_FOLDER_ITEM(item)->flags_set_table,
3478 GINT_TO_POINTER(flags_set), ht_data);
3480 if (!g_slist_find(ht_data->msglist, GINT_TO_POINTER(msginfo->msgnum)))
3481 ht_data->msglist = g_slist_prepend(ht_data->msglist, GINT_TO_POINTER(msginfo->msgnum));
3484 ht_data = g_hash_table_lookup(IMAP_FOLDER_ITEM(item)->flags_unset_table,
3485 GINT_TO_POINTER(flags_unset));
3486 if (ht_data == NULL) {
3487 ht_data = g_new0(hashtable_data, 1);
3488 ht_data->session = session;
3489 ht_data->item = IMAP_FOLDER_ITEM(item);
3490 g_hash_table_insert(IMAP_FOLDER_ITEM(item)->flags_unset_table,
3491 GINT_TO_POINTER(flags_unset), ht_data);
3493 if (!g_slist_find(ht_data->msglist, GINT_TO_POINTER(msginfo->msgnum)))
3494 ht_data->msglist = g_slist_prepend(ht_data->msglist,
3495 GINT_TO_POINTER(msginfo->msgnum));
3498 debug_print("IMAP changing flags\n");
3500 ok = imap_set_message_flags(session, &numlist, flags_set, TRUE);
3501 if (ok != IMAP_SUCCESS) {
3508 ok = imap_set_message_flags(session, &numlist, flags_unset, FALSE);
3509 if (ok != IMAP_SUCCESS) {
3515 msginfo->flags.perm_flags = newflags;
3520 static gint imap_remove_msg(Folder *folder, FolderItem *item, gint uid)
3523 IMAPSession *session;
3525 MsgNumberList numlist;
3527 g_return_val_if_fail(folder != NULL, -1);
3528 g_return_val_if_fail(FOLDER_CLASS(folder) == &imap_class, -1);
3529 g_return_val_if_fail(item != NULL, -1);
3531 session = imap_session_get(folder);
3532 if (!session) return -1;
3534 ok = imap_select(session, IMAP_FOLDER(folder), item->path,
3535 NULL, NULL, NULL, NULL, FALSE);
3536 if (ok != IMAP_SUCCESS) {
3540 numlist.next = NULL;
3541 numlist.data = GINT_TO_POINTER(uid);
3543 ok = imap_set_message_flags
3544 (IMAP_SESSION(REMOTE_FOLDER(folder)->session),
3545 &numlist, IMAP_FLAG_DELETED, TRUE);
3546 if (ok != IMAP_SUCCESS) {
3547 log_warning(_("can't set deleted flags: %d\n"), uid);
3552 if (!session->uidplus) {
3553 ok = imap_cmd_expunge(session);
3557 uidstr = g_strdup_printf("%u", uid);
3558 ok = imap_cmd_expunge(session);
3561 if (ok != IMAP_SUCCESS) {
3562 log_warning(_("can't expunge\n"));
3567 IMAP_FOLDER_ITEM(item)->uid_list = g_slist_remove(
3568 IMAP_FOLDER_ITEM(item)->uid_list, numlist.data);
3569 dir = folder_item_get_path(item);
3570 if (is_dir_exist(dir))
3571 remove_numbered_files(dir, uid, uid);
3574 return IMAP_SUCCESS;
3577 static gint compare_msginfo(gconstpointer a, gconstpointer b)
3579 return ((MsgInfo *)a)->msgnum - ((MsgInfo *)b)->msgnum;
3582 static guint gslist_find_next_num(MsgNumberList **list, guint num)
3586 g_return_val_if_fail(list != NULL, -1);
3588 for (elem = *list; elem != NULL; elem = g_slist_next(elem))
3589 if (GPOINTER_TO_INT(elem->data) >= num)
3592 return elem != NULL ? GPOINTER_TO_INT(elem->data) : (gint)-1;
3596 * NEW and DELETED flags are not syncronized
3597 * - The NEW/RECENT flags in IMAP folders can not really be directly
3598 * modified by Sylpheed
3599 * - The DELETE/DELETED flag in IMAP and Sylpheed don't have the same
3600 * meaning, in IMAP it always removes the messages from the FolderItem
3601 * in Sylpheed it can mean to move the message to trash
3604 typedef struct _get_flags_data {
3607 MsgInfoList *msginfo_list;
3608 GRelation *msgflags;
3609 gboolean full_search;
3613 static /*gint*/ void *imap_get_flags_thread(void *data)
3615 get_flags_data *stuff = (get_flags_data *)data;
3616 Folder *folder = stuff->folder;
3617 FolderItem *item = stuff->item;
3618 MsgInfoList *msginfo_list = stuff->msginfo_list;
3619 GRelation *msgflags = stuff->msgflags;
3620 gboolean full_search = stuff->full_search;
3621 IMAPSession *session;
3622 GSList *sorted_list = NULL;
3623 GSList *unseen = NULL, *answered = NULL, *flagged = NULL, *deleted = NULL;
3624 GSList *p_unseen, *p_answered, *p_flagged, *p_deleted;
3626 GSList *seq_list, *cur;
3627 gboolean reverse_seen = FALSE;
3630 gint exists_cnt, unseen_cnt;
3631 gboolean selected_folder;
3633 if (folder == NULL || item == NULL) {
3635 return GINT_TO_POINTER(-1);
3638 session = imap_session_get(folder);
3639 if (session == NULL) {
3641 return GINT_TO_POINTER(-1);
3644 selected_folder = (session->mbox != NULL) &&
3645 (!strcmp(session->mbox, item->path));
3647 if (!selected_folder) {
3648 ok = imap_select(session, IMAP_FOLDER(folder), item->path,
3649 &exists_cnt, NULL, &unseen_cnt, NULL, TRUE);
3650 if (ok != IMAP_SUCCESS) {
3653 return GINT_TO_POINTER(-1);
3656 if (unseen_cnt > exists_cnt / 2)
3657 reverse_seen = TRUE;
3660 if (item->unread_msgs > item->total_msgs / 2)
3661 reverse_seen = TRUE;
3664 cmd_buf = g_string_new(NULL);
3666 sorted_list = g_slist_sort(g_slist_copy(msginfo_list), compare_msginfo);
3668 seq_list = imap_get_lep_set_from_msglist(msginfo_list);
3670 struct mailimap_set * set;
3671 set = mailimap_set_new_interval(1, 0);
3672 seq_list = g_slist_append(NULL, set);
3675 for (cur = seq_list; cur != NULL; cur = g_slist_next(cur)) {
3676 struct mailimap_set * imapset;
3677 clist * lep_uidlist;
3680 imapset = cur->data;
3682 r = imap_threaded_search(folder, IMAP_SEARCH_TYPE_SEEN,
3683 imapset, &lep_uidlist);
3686 r = imap_threaded_search(folder,
3687 IMAP_SEARCH_TYPE_UNSEEN,
3688 imapset, &lep_uidlist);
3690 if (r == MAILIMAP_NO_ERROR) {
3693 uidlist = imap_uid_list_from_lep(lep_uidlist);
3694 mailimap_search_result_free(lep_uidlist);
3696 unseen = g_slist_concat(unseen, uidlist);
3699 r = imap_threaded_search(folder, IMAP_SEARCH_TYPE_FLAGGED,
3700 imapset, &lep_uidlist);
3701 if (r == MAILIMAP_NO_ERROR) {
3704 uidlist = imap_uid_list_from_lep(lep_uidlist);
3705 mailimap_search_result_free(lep_uidlist);
3707 flagged = g_slist_concat(flagged, uidlist);
3710 if (item->opened || item->processing_pending || item == folder->inbox) {
3711 r = imap_threaded_search(folder, IMAP_SEARCH_TYPE_ANSWERED,
3712 imapset, &lep_uidlist);
3713 if (r == MAILIMAP_NO_ERROR) {
3716 uidlist = imap_uid_list_from_lep(lep_uidlist);
3717 mailimap_search_result_free(lep_uidlist);
3719 answered = g_slist_concat(answered, uidlist);
3722 r = imap_threaded_search(folder, IMAP_SEARCH_TYPE_DELETED,
3723 imapset, &lep_uidlist);
3724 if (r == MAILIMAP_NO_ERROR) {
3727 uidlist = imap_uid_list_from_lep(lep_uidlist);
3728 mailimap_search_result_free(lep_uidlist);
3730 deleted = g_slist_concat(deleted, uidlist);
3736 p_answered = answered;
3737 p_flagged = flagged;
3738 p_deleted = deleted;
3740 for (elem = sorted_list; elem != NULL; elem = g_slist_next(elem)) {
3745 msginfo = (MsgInfo *) elem->data;
3746 flags = msginfo->flags.perm_flags;
3747 wasnew = (flags & MSG_NEW);
3748 if (item->opened || item->processing_pending || item == folder->inbox) {
3749 flags &= ~((reverse_seen ? 0 : MSG_UNREAD | MSG_NEW) | MSG_REPLIED | MSG_MARKED);
3751 flags &= ~((reverse_seen ? 0 : MSG_UNREAD | MSG_NEW | MSG_MARKED));
3754 flags |= MSG_UNREAD | (wasnew ? MSG_NEW : 0);
3755 if (gslist_find_next_num(&p_unseen, msginfo->msgnum) == msginfo->msgnum) {
3756 if (!reverse_seen) {
3757 flags |= MSG_UNREAD | (wasnew ? MSG_NEW : 0);
3759 flags &= ~(MSG_UNREAD | MSG_NEW);
3763 if (gslist_find_next_num(&p_flagged, msginfo->msgnum) == msginfo->msgnum)
3764 flags |= MSG_MARKED;
3766 flags &= ~MSG_MARKED;
3768 if (item->opened || item->processing_pending || item == folder->inbox) {
3769 if (gslist_find_next_num(&p_answered, msginfo->msgnum) == msginfo->msgnum)
3770 flags |= MSG_REPLIED;
3772 flags &= ~MSG_REPLIED;
3773 if (gslist_find_next_num(&p_deleted, msginfo->msgnum) == msginfo->msgnum)
3774 flags |= MSG_DELETED;
3776 flags &= ~MSG_DELETED;
3778 g_relation_insert(msgflags, msginfo, GINT_TO_POINTER(flags));
3781 imap_lep_set_free(seq_list);
3782 g_slist_free(flagged);
3783 g_slist_free(deleted);
3784 g_slist_free(answered);
3785 g_slist_free(unseen);
3786 g_slist_free(sorted_list);
3787 g_string_free(cmd_buf, TRUE);
3791 return GINT_TO_POINTER(0);
3794 static gint imap_get_flags(Folder *folder, FolderItem *item,
3795 MsgInfoList *msginfo_list, GRelation *msgflags)
3798 get_flags_data *data = g_new0(get_flags_data, 1);
3800 data->folder = folder;
3802 data->msginfo_list = msginfo_list;
3803 data->msgflags = msgflags;
3804 data->full_search = FALSE;
3806 GSList *tmp = NULL, *cur;
3808 if (prefs_common.work_offline &&
3809 !inc_offline_should_override(
3810 _("Sylpheed-Claws needs network access in order "
3811 "to access the IMAP server."))) {
3816 tmp = folder_item_get_msg_list(item);
3818 if (g_slist_length(tmp) <= g_slist_length(msginfo_list))
3819 data->full_search = TRUE;
3821 for (cur = tmp; cur; cur = cur->next)
3822 procmsg_msginfo_free((MsgInfo *)cur->data);
3826 result = GPOINTER_TO_INT(imap_get_flags_thread(data));
3833 static gboolean process_flags(gpointer key, gpointer value, gpointer user_data)
3835 gboolean flags_set = GPOINTER_TO_INT(user_data);
3836 gint flags_value = GPOINTER_TO_INT(key);
3837 hashtable_data *data = (hashtable_data *)value;
3838 IMAPFolderItem *_item = data->item;
3839 FolderItem *item = (FolderItem *)_item;
3840 gint ok = IMAP_ERROR;
3841 IMAPSession *session = imap_session_get(item->folder);
3843 data->msglist = g_slist_reverse(data->msglist);
3845 debug_print("IMAP %ssetting flags to %d for %d messages\n",
3848 g_slist_length(data->msglist));
3852 ok = imap_select(session, IMAP_FOLDER(item->folder), item->path,
3853 NULL, NULL, NULL, NULL, FALSE);
3855 if (ok == IMAP_SUCCESS) {
3856 imap_set_message_flags(data->session, data->msglist, flags_value, flags_set);
3858 g_warning("can't select mailbox %s\n", item->path);
3862 g_slist_free(data->msglist);
3867 static void process_hashtable(IMAPFolderItem *item)
3869 if (item->flags_set_table) {
3870 g_hash_table_foreach_remove(item->flags_set_table, process_flags, GINT_TO_POINTER(TRUE));
3871 g_hash_table_destroy(item->flags_set_table);
3872 item->flags_set_table = NULL;
3874 if (item->flags_unset_table) {
3875 g_hash_table_foreach_remove(item->flags_unset_table, process_flags, GINT_TO_POINTER(FALSE));
3876 g_hash_table_destroy(item->flags_unset_table);
3877 item->flags_unset_table = NULL;
3881 static void imap_set_batch (Folder *folder, FolderItem *_item, gboolean batch)
3883 IMAPFolderItem *item = (IMAPFolderItem *)_item;
3885 g_return_if_fail(item != NULL);
3887 if (item->batching == batch)
3891 item->batching = TRUE;
3892 debug_print("IMAP switching to batch mode\n");
3893 if (!item->flags_set_table) {
3894 item->flags_set_table = g_hash_table_new(NULL, g_direct_equal);
3896 if (!item->flags_unset_table) {
3897 item->flags_unset_table = g_hash_table_new(NULL, g_direct_equal);
3900 debug_print("IMAP switching away from batch mode\n");
3902 process_hashtable(item);
3903 item->batching = FALSE;
3909 /* data types conversion libetpan <-> sylpheed */
3913 #define ETPAN_IMAP_MB_MARKED 1
3914 #define ETPAN_IMAP_MB_UNMARKED 2
3915 #define ETPAN_IMAP_MB_NOSELECT 4
3916 #define ETPAN_IMAP_MB_NOINFERIORS 8
3918 static int imap_flags_to_flags(struct mailimap_mbx_list_flags * imap_flags)
3924 if (imap_flags->mbf_type == MAILIMAP_MBX_LIST_FLAGS_SFLAG) {
3925 switch (imap_flags->mbf_sflag) {
3926 case MAILIMAP_MBX_LIST_SFLAG_MARKED:
3927 flags |= ETPAN_IMAP_MB_MARKED;
3929 case MAILIMAP_MBX_LIST_SFLAG_NOSELECT:
3930 flags |= ETPAN_IMAP_MB_NOSELECT;
3932 case MAILIMAP_MBX_LIST_SFLAG_UNMARKED:
3933 flags |= ETPAN_IMAP_MB_UNMARKED;
3938 for(cur = clist_begin(imap_flags->mbf_oflags) ; cur != NULL ;
3939 cur = clist_next(cur)) {
3940 struct mailimap_mbx_list_oflag * oflag;
3942 oflag = clist_content(cur);
3944 switch (oflag->of_type) {
3945 case MAILIMAP_MBX_LIST_OFLAG_NOINFERIORS:
3946 flags |= ETPAN_IMAP_MB_NOINFERIORS;
3954 static GSList * imap_list_from_lep(IMAPFolder * folder,
3955 clist * list, const gchar * real_path, gboolean all)
3962 for(iter = clist_begin(list) ; iter != NULL ;
3963 iter = clist_next(iter)) {
3964 struct mailimap_mailbox_list * mb;
3972 FolderItem *new_item;
3974 mb = clist_content(iter);
3980 if (mb->mb_flag != NULL)
3981 flags = imap_flags_to_flags(mb->mb_flag);
3983 delimiter = mb->mb_delimiter;
3986 dup_name = strdup(name);
3987 if (delimiter != '\0')
3988 subst_char(dup_name, delimiter, '/');
3990 base = g_path_get_basename(dup_name);
3991 if (base[0] == '.') {
3997 if (!all && strcmp(dup_name, real_path) == 0) {
4003 if (!all && dup_name[strlen(dup_name)-1] == '/') {
4009 loc_name = imap_modified_utf7_to_utf8(base);
4010 loc_path = imap_modified_utf7_to_utf8(dup_name);
4012 new_item = folder_item_new(FOLDER(folder), loc_name, loc_path);
4013 if ((flags & ETPAN_IMAP_MB_NOINFERIORS) != 0)
4014 new_item->no_sub = TRUE;
4015 if (strcmp(dup_name, "INBOX") != 0 &&
4016 ((flags & ETPAN_IMAP_MB_NOSELECT) != 0))
4017 new_item->no_select = TRUE;
4019 item_list = g_slist_append(item_list, new_item);
4021 debug_print("folder '%s' found.\n", loc_path);
4032 static GSList * imap_get_lep_set_from_numlist(MsgNumberList *numlist)
4034 GSList *sorted_list, *cur;
4035 guint first, last, next;
4036 GSList *ret_list = NULL;
4038 struct mailimap_set * current_set;
4039 unsigned int item_count;
4041 if (numlist == NULL)
4045 current_set = mailimap_set_new_empty();
4047 sorted_list = g_slist_copy(numlist);
4048 sorted_list = g_slist_sort(sorted_list, g_int_compare);
4050 first = GPOINTER_TO_INT(sorted_list->data);
4053 for (cur = sorted_list; cur != NULL; cur = g_slist_next(cur)) {
4054 if (GPOINTER_TO_INT(cur->data) == 0)
4059 last = GPOINTER_TO_INT(cur->data);
4061 next = GPOINTER_TO_INT(cur->next->data);
4065 if (last + 1 != next || next == 0) {
4067 struct mailimap_set_item * item;
4068 item = mailimap_set_item_new(first, last);
4069 mailimap_set_add(current_set, item);
4074 if (count >= IMAP_SET_MAX_COUNT) {
4075 ret_list = g_slist_append(ret_list,
4077 current_set = mailimap_set_new_empty();
4084 if (clist_count(current_set->set_list) > 0) {
4085 ret_list = g_slist_append(ret_list,
4089 g_slist_free(sorted_list);
4094 static GSList * imap_get_lep_set_from_msglist(MsgInfoList *msglist)
4096 MsgNumberList *numlist = NULL;
4100 for (cur = msglist; cur != NULL; cur = g_slist_next(cur)) {
4101 MsgInfo *msginfo = (MsgInfo *) cur->data;
4103 numlist = g_slist_append(numlist, GINT_TO_POINTER(msginfo->msgnum));
4105 seq_list = imap_get_lep_set_from_numlist(numlist);
4106 g_slist_free(numlist);
4111 static GSList * imap_uid_list_from_lep(clist * list)
4118 for(iter = clist_begin(list) ; iter != NULL ;
4119 iter = clist_next(iter)) {
4122 puid = clist_content(iter);
4123 result = g_slist_append(result, GINT_TO_POINTER(* puid));
4129 static GSList * imap_uid_list_from_lep_tab(carray * list)
4136 for(i = 0 ; i < carray_count(list) ; i ++) {
4139 puid = carray_get(list, i);
4140 result = g_slist_append(result, GINT_TO_POINTER(* puid));
4146 static MsgInfo *imap_envelope_from_lep(struct imap_fetch_env_info * info,
4149 MsgInfo *msginfo = NULL;
4152 MsgFlags flags = {0, 0};
4154 if (info->headers == NULL)
4157 MSG_SET_TMP_FLAGS(flags, MSG_IMAP);
4158 if (folder_has_parent_of_type(item, F_QUEUE)) {
4159 MSG_SET_TMP_FLAGS(flags, MSG_QUEUED);
4160 } else if (folder_has_parent_of_type(item, F_DRAFT)) {
4161 MSG_SET_TMP_FLAGS(flags, MSG_DRAFT);
4163 flags.perm_flags = info->flags;
4167 msginfo = procheader_parse_str(info->headers, flags, FALSE, FALSE);
4170 msginfo->msgnum = uid;
4171 msginfo->size = size;
4177 static void imap_lep_set_free(GSList *seq_list)
4181 for(cur = seq_list ; cur != NULL ; cur = g_slist_next(cur)) {
4182 struct mailimap_set * imapset;
4184 imapset = cur->data;
4185 mailimap_set_free(imapset);
4187 g_slist_free(seq_list);
4190 static struct mailimap_flag_list * imap_flag_to_lep(IMAPFlags flags)
4192 struct mailimap_flag_list * flag_list;
4194 flag_list = mailimap_flag_list_new_empty();
4196 if (IMAP_IS_SEEN(flags))
4197 mailimap_flag_list_add(flag_list,
4198 mailimap_flag_new_seen());
4199 if (IMAP_IS_ANSWERED(flags))
4200 mailimap_flag_list_add(flag_list,
4201 mailimap_flag_new_answered());
4202 if (IMAP_IS_FLAGGED(flags))
4203 mailimap_flag_list_add(flag_list,
4204 mailimap_flag_new_flagged());
4205 if (IMAP_IS_DELETED(flags))
4206 mailimap_flag_list_add(flag_list,
4207 mailimap_flag_new_deleted());
4208 if (IMAP_IS_DRAFT(flags))
4209 mailimap_flag_list_add(flag_list,
4210 mailimap_flag_new_draft());
4215 guint imap_folder_get_refcnt(Folder *folder)
4217 return ((IMAPFolder *)folder)->refcnt;
4220 void imap_folder_ref(Folder *folder)
4222 ((IMAPFolder *)folder)->refcnt++;
4225 void imap_folder_unref(Folder *folder)
4227 if (((IMAPFolder *)folder)->refcnt > 0)
4228 ((IMAPFolder *)folder)->refcnt--;
4231 #else /* HAVE_LIBETPAN */
4233 static FolderClass imap_class;
4235 static XMLTag *imap_item_get_xml(Folder *folder, FolderItem *item);
4236 static void imap_item_set_xml(Folder *folder, FolderItem *item, XMLTag *tag);
4238 static Folder *imap_folder_new (const gchar *name,
4243 static gint imap_create_tree (Folder *folder)
4247 static FolderItem *imap_create_folder (Folder *folder,
4253 static gint imap_rename_folder (Folder *folder,
4260 FolderClass *imap_get_class(void)
4262 if (imap_class.idstr == NULL) {
4263 imap_class.type = F_IMAP;
4264 imap_class.idstr = "imap";
4265 imap_class.uistr = "IMAP4";
4267 imap_class.new_folder = imap_folder_new;
4268 imap_class.create_tree = imap_create_tree;
4269 imap_class.create_folder = imap_create_folder;
4270 imap_class.rename_folder = imap_rename_folder;
4272 imap_class.set_xml = folder_set_xml;
4273 imap_class.get_xml = folder_get_xml;
4274 imap_class.item_set_xml = imap_item_set_xml;
4275 imap_class.item_get_xml = imap_item_get_xml;
4276 /* nothing implemented */
4283 void imap_synchronise(FolderItem *item)
4285 imap_gtk_synchronise(item);
4288 static void imap_item_set_xml(Folder *folder, FolderItem *item, XMLTag *tag)
4292 folder_item_set_xml(folder, item, tag);
4294 for (cur = tag->attr; cur != NULL; cur = g_list_next(cur)) {
4295 XMLAttr *attr = (XMLAttr *) cur->data;
4297 if (!attr || !attr->name || !attr->value) continue;
4298 if (!strcmp(attr->name, "uidnext"))
4299 IMAP_FOLDER_ITEM(item)->uid_next = atoi(attr->value);
4303 static XMLTag *imap_item_get_xml(Folder *folder, FolderItem *item)
4307 tag = folder_item_get_xml(folder, item);
4309 xml_tag_add_attr(tag, xml_attr_new_int("uidnext",
4310 IMAP_FOLDER_ITEM(item)->uid_next));