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)
486 while (imap_folder_get_refcnt(folder) > 0)
487 gtk_main_iteration();
489 folder_remote_folder_destroy(REMOTE_FOLDER(folder));
493 static void imap_folder_init(Folder *folder, const gchar *name,
496 folder_remote_folder_init((Folder *)folder, name, path);
499 static FolderItem *imap_folder_item_new(Folder *folder)
501 IMAPFolderItem *item;
503 item = g_new0(IMAPFolderItem, 1);
506 item->uid_list = NULL;
508 return (FolderItem *)item;
511 static void imap_folder_item_destroy(Folder *folder, FolderItem *_item)
513 IMAPFolderItem *item = (IMAPFolderItem *)_item;
515 g_return_if_fail(item != NULL);
516 g_slist_free(item->uid_list);
521 static gboolean imap_reset_uid_lists_func(GNode *node, gpointer data)
523 IMAPFolderItem *item = (IMAPFolderItem *)node->data;
526 g_slist_free(item->uid_list);
527 item->uid_list = NULL;
532 static void imap_reset_uid_lists(Folder *folder)
534 if(folder->node == NULL)
537 /* Destroy all uid lists and rest last uid */
538 g_node_traverse(folder->node, G_IN_ORDER, G_TRAVERSE_ALL, -1, imap_reset_uid_lists_func, NULL);
541 void imap_get_capabilities(IMAPSession *session)
543 struct mailimap_capability_data *capabilities = NULL;
546 if (session->capability != NULL)
549 capabilities = imap_threaded_capability(session->folder);
551 if (capabilities == NULL)
554 for(cur = clist_begin(capabilities->cap_list) ; cur != NULL ;
555 cur = clist_next(cur)) {
556 struct mailimap_capability * cap =
558 if (!cap || cap->cap_data.cap_name == NULL)
560 session->capability = g_slist_append
561 (session->capability,
562 g_strdup(cap->cap_data.cap_name));
563 debug_print("got capa %s\n", cap->cap_data.cap_name);
565 mailimap_capability_data_free(capabilities);
568 gboolean imap_has_capability(IMAPSession *session, const gchar *cap)
571 for (cur = session->capability; cur; cur = cur->next) {
572 if (!g_ascii_strcasecmp(cur->data, cap))
578 static gint imap_auth(IMAPSession *session, const gchar *user, const gchar *pass,
581 gint ok = IMAP_ERROR;
582 static time_t last_login_err = 0;
584 imap_get_capabilities(session);
588 ok = imap_cmd_login(session, user, pass, "ANONYMOUS");
590 case IMAP_AUTH_CRAM_MD5:
591 ok = imap_cmd_login(session, user, pass, "CRAM-MD5");
593 case IMAP_AUTH_LOGIN:
594 ok = imap_cmd_login(session, user, pass, "LOGIN");
597 debug_print("capabilities:\n"
601 imap_has_capability(session, "ANONYMOUS"),
602 imap_has_capability(session, "CRAM-MD5"),
603 imap_has_capability(session, "LOGIN"));
604 if (imap_has_capability(session, "CRAM-MD5"))
605 ok = imap_cmd_login(session, user, pass, "CRAM-MD5");
606 if (ok == IMAP_ERROR) /* we always try LOGIN before giving up */
607 ok = imap_cmd_login(session, user, pass, "LOGIN");
609 if (ok == IMAP_SUCCESS)
610 session->authenticated = TRUE;
612 gchar *ext_info = NULL;
614 if (type == IMAP_AUTH_CRAM_MD5) {
615 ext_info = _("\n\nCRAM-MD5 logins only work if libetpan has been "
616 "compiled with SASL support and the "
617 "CRAM-MD5 SASL plugin is installed.");
622 if (time(NULL) - last_login_err > 10) {
623 if (!prefs_common.no_recv_err_panel) {
624 alertpanel_error(_("Connection to %s failed: "
626 SESSION(session)->server, ext_info);
628 log_error(_("Connection to %s failed: "
629 "login refused.%s\n"),
630 SESSION(session)->server, ext_info);
633 last_login_err = time(NULL);
638 static IMAPSession *imap_reconnect_if_possible(Folder *folder, IMAPSession *session)
640 RemoteFolder *rfolder = REMOTE_FOLDER(folder);
641 /* Check if this is the first try to establish a
642 connection, if yes we don't try to reconnect */
643 debug_print("reconnecting\n");
644 if (rfolder->session == NULL) {
645 log_warning(_("Connecting to %s failed"),
646 folder->account->recv_server);
647 session_destroy(SESSION(session));
650 log_warning(_("IMAP4 connection to %s has been"
651 " disconnected. Reconnecting...\n"),
652 folder->account->recv_server);
653 statusbar_print_all(_("IMAP4 connection to %s has been"
654 " disconnected. Reconnecting...\n"),
655 folder->account->recv_server);
656 SESSION(session)->state = SESSION_DISCONNECTED;
657 session_destroy(SESSION(session));
658 /* Clear folders session to make imap_session_get create
659 a new session, because of rfolder->session == NULL
660 it will not try to reconnect again and so avoid an
662 rfolder->session = NULL;
663 session = imap_session_get(folder);
664 rfolder->session = SESSION(session);
670 #define lock_session() {\
671 debug_print("locking session\n"); \
672 session->busy = TRUE;\
675 #define unlock_session() {\
676 debug_print("unlocking session\n"); \
677 session->busy = FALSE;\
680 static IMAPSession *imap_session_get(Folder *folder)
682 RemoteFolder *rfolder = REMOTE_FOLDER(folder);
683 IMAPSession *session = NULL;
684 static time_t last_failure = 0;
686 g_return_val_if_fail(folder != NULL, NULL);
687 g_return_val_if_fail(FOLDER_CLASS(folder) == &imap_class, NULL);
688 g_return_val_if_fail(folder->account != NULL, NULL);
690 if (prefs_common.work_offline &&
691 !inc_offline_should_override(
692 _("Sylpheed-Claws needs network access in order "
693 "to access the IMAP server."))) {
697 /* Make sure we have a session */
698 if (rfolder->session != NULL) {
699 session = IMAP_SESSION(rfolder->session);
700 /* don't do that yet...
705 imap_reset_uid_lists(folder);
706 if (time(NULL) - last_failure <= 2)
708 session = imap_session_new(folder, folder->account);
710 if(session == NULL) {
711 last_failure = time(NULL);
715 /* Make sure session is authenticated */
716 if (!IMAP_SESSION(session)->authenticated)
717 imap_session_authenticate(IMAP_SESSION(session), folder->account);
719 if (!IMAP_SESSION(session)->authenticated) {
720 session_destroy(SESSION(session));
721 rfolder->session = NULL;
722 last_failure = time(NULL);
726 /* I think the point of this code is to avoid sending a
727 * keepalive if we've used the session recently and therefore
728 * think it's still alive. Unfortunately, most of the code
729 * does not yet check for errors on the socket, and so if the
730 * connection drops we don't notice until the timeout expires.
731 * A better solution than sending a NOOP every time would be
732 * for every command to be prepared to retry until it is
733 * successfully sent. -- mbp */
734 if (time(NULL) - SESSION(session)->last_access_time > SESSION_TIMEOUT_INTERVAL) {
735 /* verify that the session is still alive */
736 if (imap_cmd_noop(session) != IMAP_SUCCESS) {
737 debug_print("disconnected!\n");
738 session = imap_reconnect_if_possible(folder, session);
742 rfolder->session = SESSION(session);
744 return IMAP_SESSION(session);
747 static IMAPSession *imap_session_new(Folder * folder,
748 const PrefsAccount *account)
750 IMAPSession *session;
756 /* FIXME: IMAP over SSL only... */
759 port = account->set_imapport ? account->imapport
760 : account->ssl_imap == SSL_TUNNEL ? IMAPS_PORT : IMAP4_PORT;
761 ssl_type = account->ssl_imap;
763 if (account->ssl_imap != SSL_NONE) {
764 if (alertpanel_full(_("Insecure connection"),
765 _("This connection is configured to be secured "
766 "using SSL, but SSL is not available in this "
767 "build of Sylpheed-Claws. \n\n"
768 "Do you want to continue connecting to this "
769 "server? The communication would not be "
771 GTK_STOCK_CANCEL, _("Con_tinue connecting"),
772 NULL, FALSE, NULL, ALERT_WARNING,
773 G_ALERTDEFAULT) != G_ALERTALTERNATE)
776 port = account->set_imapport ? account->imapport
781 statusbar_print_all(_("Connecting to IMAP4 server: %s..."), folder->account->recv_server);
782 if (account->set_tunnelcmd) {
783 r = imap_threaded_connect_cmd(folder,
785 account->recv_server,
790 if (ssl_type == SSL_TUNNEL) {
791 r = imap_threaded_connect_ssl(folder,
792 account->recv_server,
798 r = imap_threaded_connect(folder,
799 account->recv_server,
805 if (r == MAILIMAP_NO_ERROR_AUTHENTICATED) {
806 authenticated = TRUE;
808 else if (r == MAILIMAP_NO_ERROR_NON_AUTHENTICATED) {
809 authenticated = FALSE;
812 if(!prefs_common.no_recv_err_panel) {
813 alertpanel_error(_("Can't connect to IMAP4 server: %s:%d"),
814 account->recv_server, port);
816 log_error(_("Can't connect to IMAP4 server: %s:%d\n"),
817 account->recv_server, port);
823 session = g_new0(IMAPSession, 1);
824 session_init(SESSION(session));
825 SESSION(session)->type = SESSION_IMAP;
826 SESSION(session)->server = g_strdup(account->recv_server);
827 SESSION(session)->sock = NULL;
829 SESSION(session)->destroy = imap_session_destroy;
831 session->capability = NULL;
833 session->authenticated = authenticated;
834 session->mbox = NULL;
835 session->cmd_count = 0;
836 session->folder = folder;
837 IMAP_FOLDER(session->folder)->last_seen_separator = 0;
840 if (account->ssl_imap == SSL_STARTTLS) {
843 ok = imap_cmd_starttls(session);
844 if (ok != IMAP_SUCCESS) {
845 log_warning(_("Can't start TLS session.\n"));
846 session_destroy(SESSION(session));
850 imap_free_capabilities(session);
851 session->authenticated = FALSE;
852 session->uidplus = FALSE;
853 session->cmd_count = 1;
856 log_message("IMAP connection is %s-authenticated\n",
857 (session->authenticated) ? "pre" : "un");
862 static void imap_session_authenticate(IMAPSession *session,
863 const PrefsAccount *account)
867 g_return_if_fail(account->userid != NULL);
869 pass = account->passwd;
870 if (!pass && account->imap_auth_type != IMAP_AUTH_ANON) {
872 tmp_pass = input_dialog_query_password(account->recv_server, account->userid);
875 Xstrdup_a(pass, tmp_pass, {g_free(tmp_pass); return;});
877 } else if (account->imap_auth_type == IMAP_AUTH_ANON) {
880 statusbar_print_all(_("Connecting to IMAP4 server %s...\n"),
881 account->recv_server);
882 if (imap_auth(session, account->userid, pass, account->imap_auth_type) != IMAP_SUCCESS) {
883 imap_threaded_disconnect(session->folder);
884 imap_cmd_logout(session);
890 session->authenticated = TRUE;
893 static void imap_session_destroy(Session *session)
895 if (session->state != SESSION_DISCONNECTED)
896 imap_threaded_disconnect(IMAP_SESSION(session)->folder);
898 imap_free_capabilities(IMAP_SESSION(session));
899 g_free(IMAP_SESSION(session)->mbox);
900 sock_close(session->sock);
901 session->sock = NULL;
904 static gchar *imap_fetch_msg(Folder *folder, FolderItem *item, gint uid)
906 return imap_fetch_msg_full(folder, item, uid, TRUE, TRUE);
909 static guint get_size_with_crs(MsgInfo *info)
918 fp = procmsg_open_message(info);
922 while (fgets(buf, sizeof (buf), fp) != NULL) {
924 if (!strstr(buf, "\r") && strstr(buf, "\n"))
932 static gchar *imap_fetch_msg_full(Folder *folder, FolderItem *item, gint uid,
933 gboolean headers, gboolean body)
935 gchar *path, *filename;
936 IMAPSession *session;
939 g_return_val_if_fail(folder != NULL, NULL);
940 g_return_val_if_fail(item != NULL, NULL);
945 path = folder_item_get_path(item);
946 if (!is_dir_exist(path))
948 filename = g_strconcat(path, G_DIR_SEPARATOR_S, itos(uid), NULL);
950 debug_print("trying to fetch cached %s\n", filename);
951 if (is_file_exist(filename)) {
952 /* see whether the local file represents the whole message
953 * or not. As the IMAP server reports size with \r chars,
954 * we have to update the local file (UNIX \n only) size */
955 MsgInfo *msginfo = imap_parse_msg(filename, item);
956 MsgInfo *cached = msgcache_get_msg(item->cache,uid);
957 guint have_size = get_size_with_crs(msginfo);
960 debug_print("message %d has been already %scached (%d/%d).\n", uid,
961 have_size >= cached->size ? "fully ":"",
962 have_size, (int)cached->size);
964 if (cached && (cached->size <= have_size || !body)) {
965 procmsg_msginfo_free(cached);
966 procmsg_msginfo_free(msginfo);
967 file_strip_crs(filename);
969 } else if (!cached) {
970 debug_print("message not cached, considering file complete\n");
971 procmsg_msginfo_free(msginfo);
972 file_strip_crs(filename);
975 procmsg_msginfo_free(cached);
976 procmsg_msginfo_free(msginfo);
980 session = imap_session_get(folder);
989 debug_print("IMAP fetching messages\n");
990 ok = imap_select(session, IMAP_FOLDER(folder), item->path,
991 NULL, NULL, NULL, NULL, FALSE);
992 if (ok != IMAP_SUCCESS) {
993 g_warning("can't select mailbox %s\n", item->path);
999 debug_print("getting message %d...\n", uid);
1000 ok = imap_cmd_fetch(session, (guint32)uid, filename, headers, body);
1002 if (ok != IMAP_SUCCESS) {
1003 g_warning("can't fetch message %d\n", uid);
1010 file_strip_crs(filename);
1014 static gint imap_add_msg(Folder *folder, FolderItem *dest,
1015 const gchar *file, MsgFlags *flags)
1019 MsgFileInfo fileinfo;
1021 g_return_val_if_fail(file != NULL, -1);
1023 fileinfo.msginfo = NULL;
1024 fileinfo.file = (gchar *)file;
1025 fileinfo.flags = flags;
1026 file_list.data = &fileinfo;
1027 file_list.next = NULL;
1029 ret = imap_add_msgs(folder, dest, &file_list, NULL);
1033 static gint imap_add_msgs(Folder *folder, FolderItem *dest, GSList *file_list,
1034 GRelation *relation)
1037 IMAPSession *session;
1038 guint32 last_uid = 0;
1040 MsgFileInfo *fileinfo;
1042 gint curnum = 0, total = 0;
1045 g_return_val_if_fail(folder != NULL, -1);
1046 g_return_val_if_fail(dest != NULL, -1);
1047 g_return_val_if_fail(file_list != NULL, -1);
1049 session = imap_session_get(folder);
1054 destdir = imap_get_real_path(IMAP_FOLDER(folder), dest->path);
1056 statusbar_print_all(_("Adding messages..."));
1057 total = g_slist_length(file_list);
1058 for (cur = file_list; cur != NULL; cur = cur->next) {
1059 IMAPFlags iflags = 0;
1060 guint32 new_uid = 0;
1061 gchar *real_file = NULL;
1062 fileinfo = (MsgFileInfo *)cur->data;
1064 statusbar_progress_all(curnum, total, 1);
1067 if (fileinfo->flags) {
1068 if (MSG_IS_MARKED(*fileinfo->flags))
1069 iflags |= IMAP_FLAG_FLAGGED;
1070 if (MSG_IS_REPLIED(*fileinfo->flags))
1071 iflags |= IMAP_FLAG_ANSWERED;
1072 if (!MSG_IS_UNREAD(*fileinfo->flags))
1073 iflags |= IMAP_FLAG_SEEN;
1076 if (real_file == NULL)
1077 real_file = g_strdup(fileinfo->file);
1079 if (folder_has_parent_of_type(dest, F_QUEUE) ||
1080 folder_has_parent_of_type(dest, F_OUTBOX) ||
1081 folder_has_parent_of_type(dest, F_DRAFT) ||
1082 folder_has_parent_of_type(dest, F_TRASH))
1083 iflags |= IMAP_FLAG_SEEN;
1085 ok = imap_cmd_append(session, destdir, real_file, iflags,
1088 if (ok != IMAP_SUCCESS) {
1089 g_warning("can't append message %s\n", real_file);
1093 statusbar_progress_all(0,0,0);
1094 statusbar_pop_all();
1097 debug_print("appended new message as %d\n", new_uid);
1098 /* put the local file in the imapcache, so that we don't
1099 * have to fetch it back later. */
1101 gchar *cache_path = folder_item_get_path(dest);
1102 if (!is_dir_exist(cache_path))
1103 make_dir_hier(cache_path);
1104 if (is_dir_exist(cache_path)) {
1105 gchar *cache_file = g_strconcat(
1106 cache_path, G_DIR_SEPARATOR_S,
1107 itos(new_uid), NULL);
1108 copy_file(real_file, cache_file, TRUE);
1109 debug_print("copied to cache: %s\n", cache_file);
1116 if (relation != NULL)
1117 g_relation_insert(relation, fileinfo->msginfo != NULL ?
1118 (gpointer) fileinfo->msginfo : (gpointer) fileinfo,
1119 GINT_TO_POINTER(dest->last_num + 1));
1121 new_uid = dest->last_num+1;
1123 if (last_uid < new_uid) {
1129 statusbar_progress_all(0,0,0);
1130 statusbar_pop_all();
1132 imap_cmd_expunge(session);
1140 static gint imap_do_copy_msgs(Folder *folder, FolderItem *dest,
1141 MsgInfoList *msglist, GRelation *relation)
1145 GSList *seq_list, *cur;
1147 IMAPSession *session;
1148 gint ok = IMAP_SUCCESS;
1149 GRelation *uid_mapping;
1152 g_return_val_if_fail(folder != NULL, -1);
1153 g_return_val_if_fail(dest != NULL, -1);
1154 g_return_val_if_fail(msglist != NULL, -1);
1156 session = imap_session_get(folder);
1162 msginfo = (MsgInfo *)msglist->data;
1164 src = msginfo->folder;
1166 g_warning("the src folder is identical to the dest.\n");
1171 if (src->folder != dest->folder) {
1172 GSList *infolist = NULL, *cur;
1174 for (cur = msglist; cur; cur = cur->next) {
1175 msginfo = (MsgInfo *)cur->data;
1176 MsgFileInfo *fileinfo = g_new0(MsgFileInfo, 1);
1177 fileinfo->file = procmsg_get_message_file(msginfo);
1178 fileinfo->flags = &(msginfo->flags);
1179 infolist = g_slist_prepend(infolist, fileinfo);
1181 infolist = g_slist_reverse(infolist);
1182 res = folder_item_add_msgs(dest, infolist, FALSE);
1183 for (cur = infolist; cur; cur = cur->next) {
1184 MsgFileInfo *info = (MsgFileInfo *)cur->data;
1188 g_slist_free(infolist);
1192 ok = imap_select(session, IMAP_FOLDER(folder), msginfo->folder->path,
1193 NULL, NULL, NULL, NULL, FALSE);
1194 if (ok != IMAP_SUCCESS) {
1199 destdir = imap_get_real_path(IMAP_FOLDER(folder), dest->path);
1200 seq_list = imap_get_lep_set_from_msglist(msglist);
1201 uid_mapping = g_relation_new(2);
1202 g_relation_index(uid_mapping, 0, g_direct_hash, g_direct_equal);
1204 statusbar_print_all(_("Copying messages..."));
1205 for (cur = seq_list; cur != NULL; cur = g_slist_next(cur)) {
1206 struct mailimap_set * seq_set;
1207 seq_set = cur->data;
1209 debug_print("Copying messages from %s to %s ...\n",
1210 src->path, destdir);
1212 ok = imap_cmd_copy(session, seq_set, destdir, uid_mapping);
1213 if (ok != IMAP_SUCCESS) {
1214 g_relation_destroy(uid_mapping);
1215 imap_lep_set_free(seq_list);
1221 for (cur = msglist; cur != NULL; cur = g_slist_next(cur)) {
1222 MsgInfo *msginfo = (MsgInfo *)cur->data;
1225 tuples = g_relation_select(uid_mapping,
1226 GINT_TO_POINTER(msginfo->msgnum),
1228 if (tuples->len > 0) {
1229 gint num = GPOINTER_TO_INT(g_tuples_index(tuples, 0, 1));
1230 g_relation_insert(relation, msginfo,
1231 GPOINTER_TO_INT(num));
1235 g_relation_insert(relation, msginfo,
1236 GPOINTER_TO_INT(0));
1237 g_tuples_destroy(tuples);
1239 statusbar_pop_all();
1241 g_relation_destroy(uid_mapping);
1242 imap_lep_set_free(seq_list);
1246 IMAP_FOLDER_ITEM(dest)->lastuid = 0;
1247 IMAP_FOLDER_ITEM(dest)->uid_next = 0;
1248 g_slist_free(IMAP_FOLDER_ITEM(dest)->uid_list);
1249 IMAP_FOLDER_ITEM(dest)->uid_list = NULL;
1252 if (ok == IMAP_SUCCESS)
1258 static gint imap_copy_msg(Folder *folder, FolderItem *dest, MsgInfo *msginfo)
1262 g_return_val_if_fail(msginfo != NULL, -1);
1264 msglist.data = msginfo;
1265 msglist.next = NULL;
1267 return imap_copy_msgs(folder, dest, &msglist, NULL);
1270 static gint imap_copy_msgs(Folder *folder, FolderItem *dest,
1271 MsgInfoList *msglist, GRelation *relation)
1276 g_return_val_if_fail(folder != NULL, -1);
1277 g_return_val_if_fail(dest != NULL, -1);
1278 g_return_val_if_fail(msglist != NULL, -1);
1280 msginfo = (MsgInfo *)msglist->data;
1281 g_return_val_if_fail(msginfo->folder != NULL, -1);
1283 ret = imap_do_copy_msgs(folder, dest, msglist, relation);
1288 static gint imap_do_remove_msgs(Folder *folder, FolderItem *dest,
1289 MsgInfoList *msglist, GRelation *relation)
1291 gchar *destdir, *dir;
1292 GSList *numlist = NULL, *cur;
1294 IMAPSession *session;
1295 gint ok = IMAP_SUCCESS;
1296 GRelation *uid_mapping;
1298 g_return_val_if_fail(folder != NULL, -1);
1299 g_return_val_if_fail(dest != NULL, -1);
1300 g_return_val_if_fail(msglist != NULL, -1);
1302 session = imap_session_get(folder);
1307 msginfo = (MsgInfo *)msglist->data;
1309 ok = imap_select(session, IMAP_FOLDER(folder), msginfo->folder->path,
1310 NULL, NULL, NULL, NULL, FALSE);
1311 if (ok != IMAP_SUCCESS) {
1316 destdir = imap_get_real_path(IMAP_FOLDER(folder), dest->path);
1317 for (cur = msglist; cur; cur = cur->next) {
1318 msginfo = (MsgInfo *)cur->data;
1319 if (!MSG_IS_DELETED(msginfo->flags))
1320 numlist = g_slist_prepend(numlist, GINT_TO_POINTER(msginfo->msgnum));
1322 numlist = g_slist_reverse(numlist);
1324 uid_mapping = g_relation_new(2);
1325 g_relation_index(uid_mapping, 0, g_direct_hash, g_direct_equal);
1327 ok = imap_set_message_flags
1328 (IMAP_SESSION(REMOTE_FOLDER(folder)->session),
1329 numlist, IMAP_FLAG_DELETED, TRUE);
1330 if (ok != IMAP_SUCCESS) {
1331 log_warning(_("can't set deleted flags\n"));
1335 ok = imap_cmd_expunge(session);
1336 if (ok != IMAP_SUCCESS) {
1337 log_warning(_("can't expunge\n"));
1342 dir = folder_item_get_path(msginfo->folder);
1343 if (is_dir_exist(dir)) {
1344 for (cur = msglist; cur; cur = cur->next) {
1345 msginfo = (MsgInfo *)cur->data;
1346 remove_numbered_files(dir, msginfo->msgnum, msginfo->msgnum);
1351 g_relation_destroy(uid_mapping);
1352 g_slist_free(numlist);
1356 if (ok == IMAP_SUCCESS)
1362 static gint imap_remove_msgs(Folder *folder, FolderItem *dest,
1363 MsgInfoList *msglist, GRelation *relation)
1367 g_return_val_if_fail(folder != NULL, -1);
1368 g_return_val_if_fail(dest != NULL, -1);
1369 if (msglist == NULL)
1372 msginfo = (MsgInfo *)msglist->data;
1373 g_return_val_if_fail(msginfo->folder != NULL, -1);
1375 return imap_do_remove_msgs(folder, dest, msglist, relation);
1378 static gint imap_remove_all_msg(Folder *folder, FolderItem *item)
1380 GSList *list = folder_item_get_msg_list(item);
1381 gint res = imap_remove_msgs(folder, item, list, NULL);
1382 procmsg_msg_list_free(list);
1386 static gboolean imap_is_msg_changed(Folder *folder, FolderItem *item,
1389 /* TODO: properly implement this method */
1393 static gint imap_close(Folder *folder, FolderItem *item)
1398 static gint imap_scan_tree(Folder *folder)
1400 FolderItem *item = NULL;
1401 IMAPSession *session;
1402 gchar *root_folder = NULL;
1404 g_return_val_if_fail(folder != NULL, -1);
1405 g_return_val_if_fail(folder->account != NULL, -1);
1407 session = imap_session_get(folder);
1409 if (!folder->node) {
1410 folder_tree_destroy(folder);
1411 item = folder_item_new(folder, folder->name, NULL);
1412 item->folder = folder;
1413 folder->node = item->node = g_node_new(item);
1419 if (folder->account->imap_dir && *folder->account->imap_dir) {
1424 Xstrdup_a(root_folder, folder->account->imap_dir, {unlock_session();return -1;});
1425 extract_quote(root_folder, '"');
1426 subst_char(root_folder,
1427 imap_get_path_separator(IMAP_FOLDER(folder),
1430 strtailchomp(root_folder, '/');
1431 real_path = imap_get_real_path
1432 (IMAP_FOLDER(folder), root_folder);
1433 debug_print("IMAP root directory: %s\n", real_path);
1435 /* check if root directory exist */
1437 r = imap_threaded_list(session->folder, "", real_path,
1439 if ((r != MAILIMAP_NO_ERROR) || (clist_count(lep_list) == 0)) {
1440 if (!folder->node) {
1441 item = folder_item_new(folder, folder->name, NULL);
1442 item->folder = folder;
1443 folder->node = item->node = g_node_new(item);
1448 mailimap_list_result_free(lep_list);
1454 item = FOLDER_ITEM(folder->node->data);
1455 if (!item || ((item->path || root_folder) &&
1456 strcmp2(item->path, root_folder) != 0)) {
1457 folder_tree_destroy(folder);
1458 item = folder_item_new(folder, folder->name, root_folder);
1459 item->folder = folder;
1460 folder->node = item->node = g_node_new(item);
1463 imap_scan_tree_recursive(session, FOLDER_ITEM(folder->node->data));
1464 imap_create_missing_folders(folder);
1470 static gint imap_scan_tree_recursive(IMAPSession *session, FolderItem *item)
1473 IMAPFolder *imapfolder;
1474 FolderItem *new_item;
1475 GSList *item_list, *cur;
1478 gchar *wildcard_path;
1484 g_return_val_if_fail(item != NULL, -1);
1485 g_return_val_if_fail(item->folder != NULL, -1);
1486 g_return_val_if_fail(item->no_sub == FALSE, -1);
1488 folder = item->folder;
1489 imapfolder = IMAP_FOLDER(folder);
1491 separator = imap_get_path_separator(imapfolder, item->path);
1493 if (folder->ui_func)
1494 folder->ui_func(folder, item, folder->ui_func_data);
1497 wildcard[0] = separator;
1500 real_path = imap_get_real_path(imapfolder, item->path);
1504 real_path = g_strdup("");
1507 Xstrcat_a(wildcard_path, real_path, wildcard,
1508 {g_free(real_path); return IMAP_ERROR;});
1510 r = imap_threaded_list(folder, "", wildcard_path, &lep_list);
1511 if (r != MAILIMAP_NO_ERROR) {
1515 item_list = imap_list_from_lep(imapfolder,
1516 lep_list, real_path, FALSE);
1517 mailimap_list_result_free(lep_list);
1522 node = item->node->children;
1523 while (node != NULL) {
1524 FolderItem *old_item = FOLDER_ITEM(node->data);
1525 GNode *next = node->next;
1528 for (cur = item_list; cur != NULL; cur = cur->next) {
1529 FolderItem *cur_item = FOLDER_ITEM(cur->data);
1530 if (!strcmp2(old_item->path, cur_item->path)) {
1531 new_item = cur_item;
1536 debug_print("folder '%s' not found. removing...\n",
1538 folder_item_remove(old_item);
1540 old_item->no_sub = new_item->no_sub;
1541 old_item->no_select = new_item->no_select;
1542 if (old_item->no_sub == TRUE && node->children) {
1543 debug_print("folder '%s' doesn't have "
1544 "subfolders. removing...\n",
1546 folder_item_remove_children(old_item);
1553 for (cur = item_list; cur != NULL; cur = cur->next) {
1554 FolderItem *cur_item = FOLDER_ITEM(cur->data);
1557 for (node = item->node->children; node != NULL;
1558 node = node->next) {
1559 if (!strcmp2(FOLDER_ITEM(node->data)->path,
1561 new_item = FOLDER_ITEM(node->data);
1562 folder_item_destroy(cur_item);
1568 new_item = cur_item;
1569 debug_print("new folder '%s' found.\n", new_item->path);
1570 folder_item_append(item, new_item);
1573 if (!strcmp(new_item->path, "INBOX")) {
1574 new_item->stype = F_INBOX;
1575 folder->inbox = new_item;
1576 } else if (!folder_item_parent(item) || item->stype == F_INBOX) {
1579 base = g_path_get_basename(new_item->path);
1581 if (!folder->outbox && !g_ascii_strcasecmp(base, "Sent")) {
1582 new_item->stype = F_OUTBOX;
1583 folder->outbox = new_item;
1584 } else if (!folder->draft && !g_ascii_strcasecmp(base, "Drafts")) {
1585 new_item->stype = F_DRAFT;
1586 folder->draft = new_item;
1587 } else if (!folder->queue && !g_ascii_strcasecmp(base, "Queue")) {
1588 new_item->stype = F_QUEUE;
1589 folder->queue = new_item;
1590 } else if (!folder->trash && !g_ascii_strcasecmp(base, "Trash")) {
1591 new_item->stype = F_TRASH;
1592 folder->trash = new_item;
1597 if (new_item->no_sub == FALSE)
1598 imap_scan_tree_recursive(session, new_item);
1601 g_slist_free(item_list);
1603 return IMAP_SUCCESS;
1606 static gint imap_create_tree(Folder *folder)
1608 g_return_val_if_fail(folder != NULL, -1);
1609 g_return_val_if_fail(folder->node != NULL, -1);
1610 g_return_val_if_fail(folder->node->data != NULL, -1);
1611 g_return_val_if_fail(folder->account != NULL, -1);
1613 imap_scan_tree(folder);
1614 imap_create_missing_folders(folder);
1619 static void imap_create_missing_folders(Folder *folder)
1621 g_return_if_fail(folder != NULL);
1624 folder->inbox = imap_create_special_folder
1625 (folder, F_INBOX, "INBOX");
1627 folder->trash = imap_create_special_folder
1628 (folder, F_TRASH, "Trash");
1630 folder->queue = imap_create_special_folder
1631 (folder, F_QUEUE, "Queue");
1632 if (!folder->outbox)
1633 folder->outbox = imap_create_special_folder
1634 (folder, F_OUTBOX, "Sent");
1636 folder->draft = imap_create_special_folder
1637 (folder, F_DRAFT, "Drafts");
1640 static FolderItem *imap_create_special_folder(Folder *folder,
1641 SpecialFolderItemType stype,
1645 FolderItem *new_item;
1647 g_return_val_if_fail(folder != NULL, NULL);
1648 g_return_val_if_fail(folder->node != NULL, NULL);
1649 g_return_val_if_fail(folder->node->data != NULL, NULL);
1650 g_return_val_if_fail(folder->account != NULL, NULL);
1651 g_return_val_if_fail(name != NULL, NULL);
1653 item = FOLDER_ITEM(folder->node->data);
1654 new_item = imap_create_folder(folder, item, name);
1657 g_warning("Can't create '%s'\n", name);
1658 if (!folder->inbox) return NULL;
1660 new_item = imap_create_folder(folder, folder->inbox, name);
1662 g_warning("Can't create '%s' under INBOX\n", name);
1664 new_item->stype = stype;
1666 new_item->stype = stype;
1671 static gchar *imap_folder_get_path(Folder *folder)
1675 g_return_val_if_fail(folder != NULL, NULL);
1676 g_return_val_if_fail(folder->account != NULL, NULL);
1678 folder_path = g_strconcat(get_imap_cache_dir(),
1680 folder->account->recv_server,
1682 folder->account->userid,
1688 static gchar *imap_item_get_path(Folder *folder, FolderItem *item)
1690 gchar *folder_path, *path;
1692 g_return_val_if_fail(folder != NULL, NULL);
1693 g_return_val_if_fail(item != NULL, NULL);
1694 folder_path = imap_folder_get_path(folder);
1696 g_return_val_if_fail(folder_path != NULL, NULL);
1697 if (folder_path[0] == G_DIR_SEPARATOR) {
1699 path = g_strconcat(folder_path, G_DIR_SEPARATOR_S,
1702 path = g_strdup(folder_path);
1705 path = g_strconcat(get_home_dir(), G_DIR_SEPARATOR_S,
1706 folder_path, G_DIR_SEPARATOR_S,
1709 path = g_strconcat(get_home_dir(), G_DIR_SEPARATOR_S,
1712 g_free(folder_path);
1717 static FolderItem *imap_create_folder(Folder *folder, FolderItem *parent,
1720 gchar *dirpath, *imap_path;
1721 IMAPSession *session;
1722 FolderItem *new_item;
1727 gboolean no_select = FALSE, no_sub = FALSE;
1729 g_return_val_if_fail(folder != NULL, NULL);
1730 g_return_val_if_fail(folder->account != NULL, NULL);
1731 g_return_val_if_fail(parent != NULL, NULL);
1732 g_return_val_if_fail(name != NULL, NULL);
1734 session = imap_session_get(folder);
1740 if (!folder_item_parent(parent) && strcmp(name, "INBOX") == 0) {
1741 dirpath = g_strdup(name);
1742 }else if (parent->path)
1743 dirpath = g_strconcat(parent->path, "/", name, NULL);
1744 else if ((p = strchr(name, '/')) != NULL && *(p + 1) != '\0')
1745 dirpath = g_strdup(name);
1746 else if (folder->account->imap_dir && *folder->account->imap_dir) {
1749 Xstrdup_a(imap_dir, folder->account->imap_dir, {unlock_session();return NULL;});
1750 strtailchomp(imap_dir, '/');
1751 dirpath = g_strconcat(imap_dir, "/", name, NULL);
1753 dirpath = g_strdup(name);
1757 /* keep trailing directory separator to create a folder that contains
1759 imap_path = imap_utf8_to_modified_utf7(dirpath);
1761 strtailchomp(dirpath, '/');
1762 Xstrdup_a(new_name, name, {
1767 separator = imap_get_path_separator(IMAP_FOLDER(folder), imap_path);
1768 imap_path_separator_subst(imap_path, separator);
1769 /* remove trailing / for display */
1770 strtailchomp(new_name, '/');
1772 if (strcmp(dirpath, "INBOX") != 0) {
1774 gboolean exist = FALSE;
1778 argbuf = g_ptr_array_new();
1779 r = imap_threaded_list(folder, "", imap_path, &lep_list);
1780 if (r != MAILIMAP_NO_ERROR) {
1781 log_warning(_("can't create mailbox: LIST failed\n"));
1784 ptr_array_free_strings(argbuf);
1785 g_ptr_array_free(argbuf, TRUE);
1790 if (clist_count(lep_list) > 0)
1792 mailimap_list_result_free(lep_list);
1795 ok = imap_cmd_create(session, imap_path);
1796 if (ok != IMAP_SUCCESS) {
1797 log_warning(_("can't create mailbox\n"));
1803 r = imap_threaded_list(folder, "", imap_path, &lep_list);
1804 if (r == MAILIMAP_NO_ERROR) {
1805 GSList *item_list = imap_list_from_lep(IMAP_FOLDER(folder),
1806 lep_list, dirpath, TRUE);
1808 FolderItem *cur_item = FOLDER_ITEM(item_list->data);
1809 no_select = cur_item->no_select;
1810 no_sub = cur_item->no_sub;
1811 g_slist_free(item_list);
1813 mailimap_list_result_free(lep_list);
1820 /* just get flags */
1821 r = imap_threaded_list(folder, "", "INBOX", &lep_list);
1822 if (r == MAILIMAP_NO_ERROR) {
1823 GSList *item_list = imap_list_from_lep(IMAP_FOLDER(folder),
1824 lep_list, dirpath, TRUE);
1826 FolderItem *cur_item = FOLDER_ITEM(item_list->data);
1827 no_select = cur_item->no_select;
1828 no_sub = cur_item->no_sub;
1829 g_slist_free(item_list);
1831 mailimap_list_result_free(lep_list);
1835 new_item = folder_item_new(folder, new_name, dirpath);
1836 new_item->no_select = no_select;
1837 new_item->no_sub = no_sub;
1838 folder_item_append(parent, new_item);
1842 dirpath = folder_item_get_path(new_item);
1843 if (!is_dir_exist(dirpath))
1844 make_dir_hier(dirpath);
1850 static gint imap_rename_folder(Folder *folder, FolderItem *item,
1855 gchar *real_oldpath;
1856 gchar *real_newpath;
1858 gchar *old_cache_dir;
1859 gchar *new_cache_dir;
1860 IMAPSession *session;
1863 gint exists, recent, unseen;
1864 guint32 uid_validity;
1866 g_return_val_if_fail(folder != NULL, -1);
1867 g_return_val_if_fail(item != NULL, -1);
1868 g_return_val_if_fail(item->path != NULL, -1);
1869 g_return_val_if_fail(name != NULL, -1);
1871 session = imap_session_get(folder);
1877 if (strchr(name, imap_get_path_separator(IMAP_FOLDER(folder), item->path)) != NULL) {
1878 g_warning(_("New folder name must not contain the namespace "
1884 real_oldpath = imap_get_real_path(IMAP_FOLDER(folder), item->path);
1886 g_free(session->mbox);
1887 session->mbox = NULL;
1888 ok = imap_cmd_examine(session, "INBOX",
1889 &exists, &recent, &unseen, &uid_validity, FALSE);
1890 if (ok != IMAP_SUCCESS) {
1891 g_free(real_oldpath);
1896 separator = imap_get_path_separator(IMAP_FOLDER(folder), item->path);
1897 if (strchr(item->path, G_DIR_SEPARATOR)) {
1898 dirpath = g_path_get_dirname(item->path);
1899 newpath = g_strconcat(dirpath, G_DIR_SEPARATOR_S, name, NULL);
1902 newpath = g_strdup(name);
1904 real_newpath = imap_utf8_to_modified_utf7(newpath);
1905 imap_path_separator_subst(real_newpath, separator);
1907 ok = imap_cmd_rename(session, real_oldpath, real_newpath);
1908 if (ok != IMAP_SUCCESS) {
1909 log_warning(_("can't rename mailbox: %s to %s\n"),
1910 real_oldpath, real_newpath);
1911 g_free(real_oldpath);
1913 g_free(real_newpath);
1919 item->name = g_strdup(name);
1921 old_cache_dir = folder_item_get_path(item);
1923 paths[0] = g_strdup(item->path);
1925 g_node_traverse(item->node, G_PRE_ORDER, G_TRAVERSE_ALL, -1,
1926 imap_rename_folder_func, paths);
1928 if (is_dir_exist(old_cache_dir)) {
1929 new_cache_dir = folder_item_get_path(item);
1930 if (rename(old_cache_dir, new_cache_dir) < 0) {
1931 FILE_OP_ERROR(old_cache_dir, "rename");
1933 g_free(new_cache_dir);
1936 g_free(old_cache_dir);
1939 g_free(real_oldpath);
1940 g_free(real_newpath);
1945 static gint imap_remove_folder_real(Folder *folder, FolderItem *item)
1948 IMAPSession *session;
1952 g_return_val_if_fail(folder != NULL, -1);
1953 g_return_val_if_fail(item != NULL, -1);
1954 g_return_val_if_fail(item->path != NULL, -1);
1956 session = imap_session_get(folder);
1961 path = imap_get_real_path(IMAP_FOLDER(folder), item->path);
1963 ok = imap_cmd_delete(session, path);
1964 if (ok != IMAP_SUCCESS) {
1965 gchar *tmp = g_strdup_printf("%s%c", path,
1966 imap_get_path_separator(IMAP_FOLDER(folder), path));
1969 ok = imap_cmd_delete(session, path);
1972 if (ok != IMAP_SUCCESS) {
1973 log_warning(_("can't delete mailbox\n"));
1980 cache_dir = folder_item_get_path(item);
1981 if (is_dir_exist(cache_dir) && remove_dir_recursive(cache_dir) < 0)
1982 g_warning("can't remove directory '%s'\n", cache_dir);
1984 folder_item_remove(item);
1989 static gint imap_remove_folder(Folder *folder, FolderItem *item)
1993 g_return_val_if_fail(item != NULL, -1);
1994 g_return_val_if_fail(item->folder != NULL, -1);
1995 g_return_val_if_fail(item->node != NULL, -1);
1997 node = item->node->children;
1998 while (node != NULL) {
2000 if (imap_remove_folder(folder, FOLDER_ITEM(node->data)) < 0)
2004 debug_print("IMAP removing %s\n", item->path);
2006 if (imap_remove_all_msg(folder, item) < 0)
2008 return imap_remove_folder_real(folder, item);
2011 typedef struct _uncached_data {
2012 IMAPSession *session;
2014 MsgNumberList *numlist;
2020 static void *imap_get_uncached_messages_thread(void *data)
2022 uncached_data *stuff = (uncached_data *)data;
2023 IMAPSession *session = stuff->session;
2024 FolderItem *item = stuff->item;
2025 MsgNumberList *numlist = stuff->numlist;
2027 GSList *newlist = NULL;
2028 GSList *llast = NULL;
2029 GSList *seq_list, *cur;
2031 debug_print("uncached_messages\n");
2033 if (session == NULL || item == NULL || item->folder == NULL
2034 || FOLDER_CLASS(item->folder) != &imap_class) {
2039 seq_list = imap_get_lep_set_from_numlist(numlist);
2040 debug_print("get msgs info\n");
2041 for (cur = seq_list; cur != NULL; cur = g_slist_next(cur)) {
2042 struct mailimap_set * imapset;
2048 imapset = cur->data;
2050 r = imap_threaded_fetch_env(session->folder,
2051 imapset, &env_list);
2052 if (r != MAILIMAP_NO_ERROR)
2055 session_set_access_time(SESSION(session));
2058 for(i = 0 ; i < carray_count(env_list) ; i ++) {
2059 struct imap_fetch_env_info * info;
2062 info = carray_get(env_list, i);
2063 msginfo = imap_envelope_from_lep(info, item);
2064 if (msginfo == NULL)
2066 msginfo->folder = item;
2068 llast = newlist = g_slist_append(newlist, msginfo);
2070 llast = g_slist_append(llast, msginfo);
2071 llast = llast->next;
2076 imap_fetch_env_free(env_list);
2079 for (cur = seq_list; cur != NULL; cur = g_slist_next(cur)) {
2080 struct mailimap_set * imapset;
2082 imapset = cur->data;
2083 mailimap_set_free(imapset);
2086 session_set_access_time(SESSION(session));
2091 #define MAX_MSG_NUM 50
2093 static GSList *imap_get_uncached_messages(IMAPSession *session,
2095 MsgNumberList *numlist)
2097 GSList *result = NULL;
2099 uncached_data *data = g_new0(uncached_data, 1);
2104 data->total = g_slist_length(numlist);
2105 debug_print("messages list : %i\n", data->total);
2107 while (cur != NULL) {
2108 GSList * partial_result;
2116 while (count < MAX_MSG_NUM) {
2121 if (newlist == NULL)
2122 llast = newlist = g_slist_append(newlist, p);
2124 llast = g_slist_append(llast, p);
2125 llast = llast->next;
2135 data->session = session;
2137 data->numlist = newlist;
2140 if (prefs_common.work_offline &&
2141 !inc_offline_should_override(
2142 _("Sylpheed-Claws needs network access in order "
2143 "to access the IMAP server."))) {
2149 (GSList *)imap_get_uncached_messages_thread(data);
2151 statusbar_progress_all(data->cur,data->total, 1);
2153 g_slist_free(newlist);
2155 result = g_slist_concat(result, partial_result);
2159 statusbar_progress_all(0,0,0);
2160 statusbar_pop_all();
2165 static void imap_delete_all_cached_messages(FolderItem *item)
2169 g_return_if_fail(item != NULL);
2170 g_return_if_fail(item->folder != NULL);
2171 g_return_if_fail(FOLDER_CLASS(item->folder) == &imap_class);
2173 debug_print("Deleting all cached messages...\n");
2175 dir = folder_item_get_path(item);
2176 if (is_dir_exist(dir))
2177 remove_all_numbered_files(dir);
2180 debug_print("done.\n");
2183 static IMAPNameSpace *imap_find_namespace_from_list(GList *ns_list,
2186 IMAPNameSpace *namespace = NULL;
2187 gchar *tmp_path, *name;
2189 if (!path) path = "";
2191 for (; ns_list != NULL; ns_list = ns_list->next) {
2192 IMAPNameSpace *tmp_ns = ns_list->data;
2194 Xstrcat_a(tmp_path, path, "/", return namespace);
2195 Xstrdup_a(name, tmp_ns->name, return namespace);
2196 if (tmp_ns->separator && tmp_ns->separator != '/') {
2197 subst_char(tmp_path, tmp_ns->separator, '/');
2198 subst_char(name, tmp_ns->separator, '/');
2200 if (strncmp(tmp_path, name, strlen(name)) == 0)
2207 static IMAPNameSpace *imap_find_namespace(IMAPFolder *folder,
2210 IMAPNameSpace *namespace;
2212 g_return_val_if_fail(folder != NULL, NULL);
2214 namespace = imap_find_namespace_from_list(folder->ns_personal, path);
2215 if (namespace) return namespace;
2216 namespace = imap_find_namespace_from_list(folder->ns_others, path);
2217 if (namespace) return namespace;
2218 namespace = imap_find_namespace_from_list(folder->ns_shared, path);
2219 if (namespace) return namespace;
2224 gchar imap_get_path_separator_for_item(FolderItem *item)
2226 Folder *folder = NULL;
2227 IMAPFolder *imap_folder = NULL;
2230 folder = item->folder;
2235 imap_folder = IMAP_FOLDER(folder);
2240 return imap_get_path_separator(imap_folder, item->path);
2243 static gchar imap_get_path_separator(IMAPFolder *folder, const gchar *path)
2245 IMAPNameSpace *namespace;
2246 gchar separator = '/';
2247 IMAPSession *session = imap_session_get(FOLDER(folder));
2248 g_return_val_if_fail(session != NULL, '/');
2250 if (folder->last_seen_separator == 0) {
2252 int r = imap_threaded_list((Folder *)folder, "", "", &lep_list);
2253 if (r != MAILIMAP_NO_ERROR) {
2254 log_warning(_("LIST failed\n"));
2258 if (clist_count(lep_list) > 0) {
2259 clistiter * iter = clist_begin(lep_list);
2260 struct mailimap_mailbox_list * mb;
2261 mb = clist_content(iter);
2263 folder->last_seen_separator = mb->mb_delimiter;
2264 debug_print("got separator: %c\n", folder->last_seen_separator);
2266 mailimap_list_result_free(lep_list);
2269 if (folder->last_seen_separator != 0) {
2270 debug_print("using separator: %c\n", folder->last_seen_separator);
2271 return folder->last_seen_separator;
2274 namespace = imap_find_namespace(folder, path);
2275 if (namespace && namespace->separator)
2276 separator = namespace->separator;
2281 static gchar *imap_get_real_path(IMAPFolder *folder, const gchar *path)
2286 g_return_val_if_fail(folder != NULL, NULL);
2287 g_return_val_if_fail(path != NULL, NULL);
2289 real_path = imap_utf8_to_modified_utf7(path);
2290 separator = imap_get_path_separator(folder, path);
2291 imap_path_separator_subst(real_path, separator);
2296 static gint imap_set_message_flags(IMAPSession *session,
2297 MsgNumberList *numlist,
2305 seq_list = imap_get_lep_set_from_numlist(numlist);
2307 for(cur = seq_list ; cur != NULL ; cur = g_slist_next(cur)) {
2308 struct mailimap_set * imapset;
2310 imapset = cur->data;
2312 ok = imap_cmd_store(session, imapset,
2316 imap_lep_set_free(seq_list);
2318 return IMAP_SUCCESS;
2321 typedef struct _select_data {
2322 IMAPSession *session;
2327 guint32 *uid_validity;
2331 static gint imap_select(IMAPSession *session, IMAPFolder *folder,
2333 gint *exists, gint *recent, gint *unseen,
2334 guint32 *uid_validity, gboolean block)
2338 gint exists_, recent_, unseen_;
2339 guint32 uid_validity_;
2341 if (!exists && !recent && !unseen && !uid_validity) {
2342 if (session->mbox && strcmp(session->mbox, path) == 0)
2343 return IMAP_SUCCESS;
2352 uid_validity = &uid_validity_;
2354 g_free(session->mbox);
2355 session->mbox = NULL;
2357 real_path = imap_get_real_path(folder, path);
2359 ok = imap_cmd_select(session, real_path,
2360 exists, recent, unseen, uid_validity, block);
2361 if (ok != IMAP_SUCCESS)
2362 log_warning(_("can't select folder: %s\n"), real_path);
2364 session->mbox = g_strdup(path);
2365 session->folder_content_changed = FALSE;
2372 static gint imap_status(IMAPSession *session, IMAPFolder *folder,
2373 const gchar *path, IMAPFolderItem *item,
2375 guint32 *uid_next, guint32 *uid_validity,
2376 gint *unseen, gboolean block)
2380 struct mailimap_mailbox_data_status * data_status;
2385 real_path = imap_get_real_path(folder, path);
2399 r = imap_threaded_status(FOLDER(folder), real_path,
2400 &data_status, mask);
2403 if (r != MAILIMAP_NO_ERROR) {
2404 debug_print("status err %d\n", r);
2408 if (data_status->st_info_list == NULL) {
2409 mailimap_mailbox_data_status_free(data_status);
2410 debug_print("status->st_info_list == NULL\n");
2415 for(iter = clist_begin(data_status->st_info_list) ; iter != NULL ;
2416 iter = clist_next(iter)) {
2417 struct mailimap_status_info * info;
2419 info = clist_content(iter);
2420 switch (info->st_att) {
2421 case MAILIMAP_STATUS_ATT_MESSAGES:
2422 * messages = info->st_value;
2423 got_values |= 1 << 0;
2426 case MAILIMAP_STATUS_ATT_UIDNEXT:
2427 * uid_next = info->st_value;
2428 got_values |= 1 << 2;
2431 case MAILIMAP_STATUS_ATT_UIDVALIDITY:
2432 * uid_validity = info->st_value;
2433 got_values |= 1 << 3;
2436 case MAILIMAP_STATUS_ATT_UNSEEN:
2437 * unseen = info->st_value;
2438 got_values |= 1 << 4;
2442 mailimap_mailbox_data_status_free(data_status);
2444 if (got_values != mask) {
2445 debug_print("status: incomplete values received (%d)\n", got_values);
2448 return IMAP_SUCCESS;
2451 static void imap_free_capabilities(IMAPSession *session)
2453 slist_free_strings(session->capability);
2454 g_slist_free(session->capability);
2455 session->capability = NULL;
2458 /* low-level IMAP4rev1 commands */
2460 static gint imap_cmd_login(IMAPSession *session,
2461 const gchar *user, const gchar *pass,
2467 log_print("IMAP4> Logging %s to %s using %s\n",
2469 SESSION(session)->server,
2471 r = imap_threaded_login(session->folder, user, pass, type);
2472 if (r != MAILIMAP_NO_ERROR) {
2473 log_print("IMAP4< Error logging in to %s\n",
2474 SESSION(session)->server);
2477 log_print("IMAP4< Login to %s successful\n",
2478 SESSION(session)->server);
2484 static gint imap_cmd_logout(IMAPSession *session)
2486 imap_threaded_disconnect(session->folder);
2488 return IMAP_SUCCESS;
2491 static gint imap_cmd_noop(IMAPSession *session)
2494 unsigned int exists;
2496 r = imap_threaded_noop(session->folder, &exists);
2497 if (r != MAILIMAP_NO_ERROR) {
2498 debug_print("noop err %d\n", r);
2501 session->exists = exists;
2502 session_set_access_time(SESSION(session));
2504 return IMAP_SUCCESS;
2508 static gint imap_cmd_starttls(IMAPSession *session)
2512 r = imap_threaded_starttls(session->folder,
2513 SESSION(session)->server, SESSION(session)->port);
2514 if (r != MAILIMAP_NO_ERROR) {
2515 debug_print("starttls err %d\n", r);
2518 return IMAP_SUCCESS;
2522 static gint imap_cmd_select(IMAPSession *session, const gchar *folder,
2523 gint *exists, gint *recent, gint *unseen,
2524 guint32 *uid_validity, gboolean block)
2528 r = imap_threaded_select(session->folder, folder,
2529 exists, recent, unseen, uid_validity);
2530 if (r != MAILIMAP_NO_ERROR) {
2531 debug_print("select err %d\n", r);
2534 return IMAP_SUCCESS;
2537 static gint imap_cmd_examine(IMAPSession *session, const gchar *folder,
2538 gint *exists, gint *recent, gint *unseen,
2539 guint32 *uid_validity, gboolean block)
2543 r = imap_threaded_examine(session->folder, folder,
2544 exists, recent, unseen, uid_validity);
2545 if (r != MAILIMAP_NO_ERROR) {
2546 debug_print("examine err %d\n", r);
2550 return IMAP_SUCCESS;
2553 static gint imap_cmd_create(IMAPSession *session, const gchar *folder)
2557 r = imap_threaded_create(session->folder, folder);
2558 if (r != MAILIMAP_NO_ERROR) {
2563 return IMAP_SUCCESS;
2566 static gint imap_cmd_rename(IMAPSession *session, const gchar *old_folder,
2567 const gchar *new_folder)
2571 r = imap_threaded_rename(session->folder, old_folder,
2573 if (r != MAILIMAP_NO_ERROR) {
2578 return IMAP_SUCCESS;
2581 static gint imap_cmd_delete(IMAPSession *session, const gchar *folder)
2586 r = imap_threaded_delete(session->folder, folder);
2587 if (r != MAILIMAP_NO_ERROR) {
2592 return IMAP_SUCCESS;
2595 typedef struct _fetch_data {
2596 IMAPSession *session;
2598 const gchar *filename;
2604 static void *imap_cmd_fetch_thread(void *data)
2606 fetch_data *stuff = (fetch_data *)data;
2607 IMAPSession *session = stuff->session;
2608 guint32 uid = stuff->uid;
2609 const gchar *filename = stuff->filename;
2613 r = imap_threaded_fetch_content(session->folder,
2617 r = imap_threaded_fetch_content(session->folder,
2620 if (r != MAILIMAP_NO_ERROR) {
2621 debug_print("fetch err %d\n", r);
2622 return GINT_TO_POINTER(IMAP_ERROR);
2624 return GINT_TO_POINTER(IMAP_SUCCESS);
2627 static gint imap_cmd_fetch(IMAPSession *session, guint32 uid,
2628 const gchar *filename, gboolean headers,
2631 fetch_data *data = g_new0(fetch_data, 1);
2634 data->session = session;
2636 data->filename = filename;
2637 data->headers = headers;
2640 if (prefs_common.work_offline &&
2641 !inc_offline_should_override(
2642 _("Sylpheed-Claws needs network access in order "
2643 "to access the IMAP server."))) {
2647 statusbar_print_all(_("Fetching message..."));
2648 result = GPOINTER_TO_INT(imap_cmd_fetch_thread(data));
2649 statusbar_pop_all();
2655 static gint imap_cmd_append(IMAPSession *session, const gchar *destfolder,
2656 const gchar *file, IMAPFlags flags,
2659 struct mailimap_flag_list * flag_list;
2662 g_return_val_if_fail(file != NULL, IMAP_ERROR);
2664 flag_list = imap_flag_to_lep(flags);
2665 r = imap_threaded_append(session->folder, destfolder,
2666 file, flag_list, new_uid);
2667 mailimap_flag_list_free(flag_list);
2669 if (r != MAILIMAP_NO_ERROR) {
2670 debug_print("append err %d\n", r);
2673 return IMAP_SUCCESS;
2676 static gint imap_cmd_copy(IMAPSession *session, struct mailimap_set * set,
2677 const gchar *destfolder, GRelation *uid_mapping)
2681 g_return_val_if_fail(session != NULL, IMAP_ERROR);
2682 g_return_val_if_fail(set != NULL, IMAP_ERROR);
2683 g_return_val_if_fail(destfolder != NULL, IMAP_ERROR);
2685 r = imap_threaded_copy(session->folder, set, destfolder);
2686 if (r != MAILIMAP_NO_ERROR) {
2691 return IMAP_SUCCESS;
2694 static gint imap_cmd_store(IMAPSession *session, struct mailimap_set * set,
2695 IMAPFlags flags, int do_add)
2698 struct mailimap_flag_list * flag_list;
2699 struct mailimap_store_att_flags * store_att_flags;
2701 flag_list = imap_flag_to_lep(flags);
2705 mailimap_store_att_flags_new_add_flags_silent(flag_list);
2708 mailimap_store_att_flags_new_remove_flags_silent(flag_list);
2710 r = imap_threaded_store(session->folder, set, store_att_flags);
2711 mailimap_store_att_flags_free(store_att_flags);
2712 if (r != MAILIMAP_NO_ERROR) {
2717 return IMAP_SUCCESS;
2720 static gint imap_cmd_expunge(IMAPSession *session)
2724 if (prefs_common.work_offline &&
2725 !inc_offline_should_override(
2726 _("Sylpheed-Claws needs network access in order "
2727 "to access the IMAP server."))) {
2731 r = imap_threaded_expunge(session->folder);
2732 if (r != MAILIMAP_NO_ERROR) {
2737 return IMAP_SUCCESS;
2740 static void imap_path_separator_subst(gchar *str, gchar separator)
2743 gboolean in_escape = FALSE;
2745 if (!separator || separator == '/') return;
2747 for (p = str; *p != '\0'; p++) {
2748 if (*p == '/' && !in_escape)
2750 else if (*p == '&' && *(p + 1) != '-' && !in_escape)
2752 else if (*p == '-' && in_escape)
2757 static gchar *imap_modified_utf7_to_utf8(const gchar *mutf7_str)
2759 static iconv_t cd = (iconv_t)-1;
2760 static gboolean iconv_ok = TRUE;
2763 size_t norm_utf7_len;
2765 gchar *to_str, *to_p;
2767 gboolean in_escape = FALSE;
2769 if (!iconv_ok) return g_strdup(mutf7_str);
2771 if (cd == (iconv_t)-1) {
2772 cd = iconv_open(CS_INTERNAL, CS_UTF_7);
2773 if (cd == (iconv_t)-1) {
2774 g_warning("iconv cannot convert UTF-7 to %s\n",
2777 return g_strdup(mutf7_str);
2781 /* modified UTF-7 to normal UTF-7 conversion */
2782 norm_utf7 = g_string_new(NULL);
2784 for (p = mutf7_str; *p != '\0'; p++) {
2785 /* replace: '&' -> '+',
2787 escaped ',' -> '/' */
2788 if (!in_escape && *p == '&') {
2789 if (*(p + 1) != '-') {
2790 g_string_append_c(norm_utf7, '+');
2793 g_string_append_c(norm_utf7, '&');
2796 } else if (in_escape && *p == ',') {
2797 g_string_append_c(norm_utf7, '/');
2798 } else if (in_escape && *p == '-') {
2799 g_string_append_c(norm_utf7, '-');
2802 g_string_append_c(norm_utf7, *p);
2806 norm_utf7_p = norm_utf7->str;
2807 norm_utf7_len = norm_utf7->len;
2808 to_len = strlen(mutf7_str) * 5;
2809 to_p = to_str = g_malloc(to_len + 1);
2811 if (iconv(cd, (ICONV_CONST gchar **)&norm_utf7_p, &norm_utf7_len,
2812 &to_p, &to_len) == -1) {
2813 g_warning(_("iconv cannot convert UTF-7 to %s\n"),
2814 conv_get_locale_charset_str());
2815 g_string_free(norm_utf7, TRUE);
2817 return g_strdup(mutf7_str);
2820 /* second iconv() call for flushing */
2821 iconv(cd, NULL, NULL, &to_p, &to_len);
2822 g_string_free(norm_utf7, TRUE);
2828 static gchar *imap_utf8_to_modified_utf7(const gchar *from)
2830 static iconv_t cd = (iconv_t)-1;
2831 static gboolean iconv_ok = TRUE;
2832 gchar *norm_utf7, *norm_utf7_p;
2833 size_t from_len, norm_utf7_len;
2835 gchar *from_tmp, *to, *p;
2836 gboolean in_escape = FALSE;
2838 if (!iconv_ok) return g_strdup(from);
2840 if (cd == (iconv_t)-1) {
2841 cd = iconv_open(CS_UTF_7, CS_INTERNAL);
2842 if (cd == (iconv_t)-1) {
2843 g_warning(_("iconv cannot convert %s to UTF-7\n"),
2846 return g_strdup(from);
2850 /* UTF-8 to normal UTF-7 conversion */
2851 Xstrdup_a(from_tmp, from, return g_strdup(from));
2852 from_len = strlen(from);
2853 norm_utf7_len = from_len * 5;
2854 Xalloca(norm_utf7, norm_utf7_len + 1, return g_strdup(from));
2855 norm_utf7_p = norm_utf7;
2857 #define IS_PRINT(ch) (isprint(ch) && IS_ASCII(ch))
2859 while (from_len > 0) {
2860 if (*from_tmp == '+') {
2861 *norm_utf7_p++ = '+';
2862 *norm_utf7_p++ = '-';
2866 } else if (IS_PRINT(*(guchar *)from_tmp)) {
2867 /* printable ascii char */
2868 *norm_utf7_p = *from_tmp;
2874 size_t conv_len = 0;
2876 /* unprintable char: convert to UTF-7 */
2878 while (!IS_PRINT(*(guchar *)p) && conv_len < from_len) {
2879 conv_len += g_utf8_skip[*(guchar *)p];
2880 p += g_utf8_skip[*(guchar *)p];
2883 from_len -= conv_len;
2884 if (iconv(cd, (ICONV_CONST gchar **)&from_tmp,
2886 &norm_utf7_p, &norm_utf7_len) == -1) {
2887 g_warning(_("iconv cannot convert UTF-8 to UTF-7\n"));
2888 return g_strdup(from);
2891 /* second iconv() call for flushing */
2892 iconv(cd, NULL, NULL, &norm_utf7_p, &norm_utf7_len);
2898 *norm_utf7_p = '\0';
2899 to_str = g_string_new(NULL);
2900 for (p = norm_utf7; p < norm_utf7_p; p++) {
2901 /* replace: '&' -> "&-",
2904 BASE64 '/' -> ',' */
2905 if (!in_escape && *p == '&') {
2906 g_string_append(to_str, "&-");
2907 } else if (!in_escape && *p == '+') {
2908 if (*(p + 1) == '-') {
2909 g_string_append_c(to_str, '+');
2912 g_string_append_c(to_str, '&');
2915 } else if (in_escape && *p == '/') {
2916 g_string_append_c(to_str, ',');
2917 } else if (in_escape && *p == '-') {
2918 g_string_append_c(to_str, '-');
2921 g_string_append_c(to_str, *p);
2927 g_string_append_c(to_str, '-');
2931 g_string_free(to_str, FALSE);
2936 static gboolean imap_rename_folder_func(GNode *node, gpointer data)
2938 FolderItem *item = node->data;
2939 gchar **paths = data;
2940 const gchar *oldpath = paths[0];
2941 const gchar *newpath = paths[1];
2943 gchar *new_itempath;
2946 oldpathlen = strlen(oldpath);
2947 if (strncmp(oldpath, item->path, oldpathlen) != 0) {
2948 g_warning("path doesn't match: %s, %s\n", oldpath, item->path);
2952 base = item->path + oldpathlen;
2953 while (*base == G_DIR_SEPARATOR) base++;
2955 new_itempath = g_strdup(newpath);
2957 new_itempath = g_strconcat(newpath, G_DIR_SEPARATOR_S, base,
2960 item->path = new_itempath;
2965 typedef struct _get_list_uid_data {
2967 IMAPSession *session;
2968 IMAPFolderItem *item;
2969 GSList **msgnum_list;
2971 } get_list_uid_data;
2973 static void *get_list_of_uids_thread(void *data)
2975 get_list_uid_data *stuff = (get_list_uid_data *)data;
2976 Folder *folder = stuff->folder;
2977 IMAPFolderItem *item = stuff->item;
2978 GSList **msgnum_list = stuff->msgnum_list;
2979 gint ok, nummsgs = 0, lastuid_old;
2980 IMAPSession *session;
2981 GSList *uidlist, *elem;
2982 struct mailimap_set * set;
2983 clist * lep_uidlist;
2986 session = stuff->session;
2987 if (session == NULL) {
2989 return GINT_TO_POINTER(-1);
2991 /* no session locking here, it's already locked by caller */
2992 ok = imap_select(session, IMAP_FOLDER(folder), item->item.path,
2993 NULL, NULL, NULL, NULL, TRUE);
2994 if (ok != IMAP_SUCCESS) {
2996 return GINT_TO_POINTER(-1);
3001 set = mailimap_set_new_interval(item->lastuid + 1, 0);
3003 r = imap_threaded_search(folder, IMAP_SEARCH_TYPE_SIMPLE, set,
3005 mailimap_set_free(set);
3007 if (r == MAILIMAP_NO_ERROR) {
3008 GSList * fetchuid_list;
3011 imap_uid_list_from_lep(lep_uidlist);
3012 mailimap_search_result_free(lep_uidlist);
3014 uidlist = g_slist_concat(fetchuid_list, uidlist);
3017 GSList * fetchuid_list;
3018 carray * lep_uidtab;
3020 r = imap_threaded_fetch_uid(folder, item->lastuid + 1,
3022 if (r == MAILIMAP_NO_ERROR) {
3024 imap_uid_list_from_lep_tab(lep_uidtab);
3025 imap_fetch_uid_list_free(lep_uidtab);
3026 uidlist = g_slist_concat(fetchuid_list, uidlist);
3030 lastuid_old = item->lastuid;
3031 *msgnum_list = g_slist_copy(item->uid_list);
3032 nummsgs = g_slist_length(*msgnum_list);
3033 debug_print("Got %d uids from cache\n", g_slist_length(item->uid_list));
3035 for (elem = uidlist; elem != NULL; elem = g_slist_next(elem)) {
3038 msgnum = GPOINTER_TO_INT(elem->data);
3039 if (msgnum > lastuid_old) {
3040 *msgnum_list = g_slist_prepend(*msgnum_list, GINT_TO_POINTER(msgnum));
3041 item->uid_list = g_slist_prepend(item->uid_list, GINT_TO_POINTER(msgnum));
3044 if(msgnum > item->lastuid)
3045 item->lastuid = msgnum;
3048 g_slist_free(uidlist);
3050 return GINT_TO_POINTER(nummsgs);
3053 static gint get_list_of_uids(IMAPSession *session, Folder *folder, IMAPFolderItem *item, GSList **msgnum_list)
3056 get_list_uid_data *data = g_new0(get_list_uid_data, 1);
3058 data->folder = folder;
3060 data->msgnum_list = msgnum_list;
3061 data->session = session;
3062 if (prefs_common.work_offline &&
3063 !inc_offline_should_override(
3064 _("Sylpheed-Claws needs network access in order "
3065 "to access the IMAP server."))) {
3070 result = GPOINTER_TO_INT(get_list_of_uids_thread(data));
3076 gint imap_get_num_list(Folder *folder, FolderItem *_item, GSList **msgnum_list, gboolean *old_uids_valid)
3078 IMAPFolderItem *item = (IMAPFolderItem *)_item;
3079 IMAPSession *session;
3080 gint ok, nummsgs = 0, exists, uid_val, uid_next = 0;
3081 GSList *uidlist = NULL;
3083 gboolean selected_folder;
3085 debug_print("get_num_list\n");
3087 g_return_val_if_fail(folder != NULL, -1);
3088 g_return_val_if_fail(item != NULL, -1);
3089 g_return_val_if_fail(item->item.path != NULL, -1);
3090 g_return_val_if_fail(FOLDER_CLASS(folder) == &imap_class, -1);
3091 g_return_val_if_fail(folder->account != NULL, -1);
3093 session = imap_session_get(folder);
3094 g_return_val_if_fail(session != NULL, -1);
3097 if (FOLDER_ITEM(item)->path)
3098 statusbar_print_all(_("Scanning folder %s%c%s ..."),
3099 FOLDER_ITEM(item)->folder->name,
3101 FOLDER_ITEM(item)->path);
3103 statusbar_print_all(_("Scanning folder %s ..."),
3104 FOLDER_ITEM(item)->folder->name);
3106 selected_folder = (session->mbox != NULL) &&
3107 (!strcmp(session->mbox, item->item.path));
3108 if (selected_folder && time(NULL) - item->use_cache < 2) {
3109 ok = imap_cmd_noop(session);
3110 if (ok != IMAP_SUCCESS) {
3111 debug_print("disconnected!\n");
3112 session = imap_reconnect_if_possible(folder, session);
3113 if (session == NULL) {
3114 statusbar_pop_all();
3119 exists = session->exists;
3121 uid_next = item->c_uid_next;
3122 uid_val = item->c_uid_validity;
3123 *old_uids_valid = TRUE;
3125 if (item->use_cache && time(NULL) - item->use_cache < 2) {
3126 exists = item->c_messages;
3127 uid_next = item->c_uid_next;
3128 uid_val = item->c_uid_validity;
3130 debug_print("using cache %d %d %d\n", exists, uid_next, uid_val);
3132 ok = imap_status(session, IMAP_FOLDER(folder), item->item.path, item,
3133 &exists, &uid_next, &uid_val, NULL, FALSE);
3135 item->item.last_num = uid_next - 1;
3137 item->use_cache = (time_t)0;
3138 if (ok != IMAP_SUCCESS) {
3139 statusbar_pop_all();
3143 if(item->item.mtime == uid_val)
3144 *old_uids_valid = TRUE;
3146 *old_uids_valid = FALSE;
3148 debug_print("Freeing imap uid cache (%d != %d)\n",
3149 item->item.mtime, uid_val);
3151 g_slist_free(item->uid_list);
3152 item->uid_list = NULL;
3154 item->item.mtime = uid_val;
3156 imap_delete_all_cached_messages((FolderItem *)item);
3160 /* If old uid_next matches new uid_next we can be sure no message
3161 was added to the folder */
3162 debug_print("uid_next is %d and item->uid_next %d \n",
3163 uid_next, item->uid_next);
3164 if (uid_next == item->uid_next) {
3165 nummsgs = g_slist_length(item->uid_list);
3167 /* If number of messages is still the same we
3168 know our caches message numbers are still valid,
3169 otherwise if the number of messages has decrease
3170 we discard our cache to start a new scan to find
3171 out which numbers have been removed */
3172 if (exists == nummsgs) {
3173 debug_print("exists == nummsgs\n");
3174 *msgnum_list = g_slist_copy(item->uid_list);
3175 statusbar_pop_all();
3178 } else if (exists < nummsgs) {
3179 debug_print("Freeing imap uid cache");
3181 g_slist_free(item->uid_list);
3182 item->uid_list = NULL;
3187 *msgnum_list = NULL;
3188 statusbar_pop_all();
3193 nummsgs = get_list_of_uids(session, folder, item, &uidlist);
3196 statusbar_pop_all();
3201 if (nummsgs != exists) {
3202 /* Cache contains more messages then folder, we have cached
3203 an old UID of a message that was removed and new messages
3204 have been added too, otherwise the uid_next check would
3206 debug_print("Freeing imap uid cache");
3208 g_slist_free(item->uid_list);
3209 item->uid_list = NULL;
3211 g_slist_free(*msgnum_list);
3213 nummsgs = get_list_of_uids(session, folder, item, &uidlist);
3216 *msgnum_list = uidlist;
3218 dir = folder_item_get_path((FolderItem *)item);
3219 debug_print("removing old messages from %s\n", dir);
3220 remove_numbered_files_not_in_list(dir, *msgnum_list);
3223 item->uid_next = uid_next;
3225 debug_print("get_num_list - ok - %i\n", nummsgs);
3226 statusbar_pop_all();
3231 static MsgInfo *imap_parse_msg(const gchar *file, FolderItem *item)
3236 flags.perm_flags = MSG_NEW|MSG_UNREAD;
3237 flags.tmp_flags = 0;
3239 g_return_val_if_fail(item != NULL, NULL);
3240 g_return_val_if_fail(file != NULL, NULL);
3242 if (folder_has_parent_of_type(item, F_QUEUE)) {
3243 MSG_SET_TMP_FLAGS(flags, MSG_QUEUED);
3244 } else if (folder_has_parent_of_type(item, F_DRAFT)) {
3245 MSG_SET_TMP_FLAGS(flags, MSG_DRAFT);
3248 msginfo = procheader_parse_file(file, flags, FALSE, FALSE);
3249 if (!msginfo) return NULL;
3251 msginfo->plaintext_file = g_strdup(file);
3252 msginfo->folder = item;
3257 GSList *imap_get_msginfos(Folder *folder, FolderItem *item,
3258 GSList *msgnum_list)
3260 IMAPSession *session;
3261 MsgInfoList *ret = NULL;
3264 debug_print("get_msginfos\n");
3266 g_return_val_if_fail(folder != NULL, NULL);
3267 g_return_val_if_fail(item != NULL, NULL);
3268 g_return_val_if_fail(msgnum_list != NULL, NULL);
3270 session = imap_session_get(folder);
3271 g_return_val_if_fail(session != NULL, NULL);
3273 debug_print("IMAP getting msginfos\n");
3274 ok = imap_select(session, IMAP_FOLDER(folder), item->path,
3275 NULL, NULL, NULL, NULL, FALSE);
3276 if (ok != IMAP_SUCCESS) {
3280 if (!(folder_has_parent_of_type(item, F_DRAFT) ||
3281 folder_has_parent_of_type(item, F_QUEUE))) {
3282 ret = g_slist_concat(ret,
3283 imap_get_uncached_messages(session, item,
3286 MsgNumberList *sorted_list, *elem, *llast = NULL;
3287 gint startnum, lastnum;
3289 sorted_list = g_slist_sort(g_slist_copy(msgnum_list), g_int_compare);
3291 startnum = lastnum = GPOINTER_TO_INT(sorted_list->data);
3293 llast = g_slist_last(ret);
3294 for (elem = sorted_list;; elem = g_slist_next(elem)) {
3298 num = GPOINTER_TO_INT(elem->data);
3300 if (num > lastnum + 1 || elem == NULL) {
3302 for (i = startnum; i <= lastnum; ++i) {
3305 file = imap_fetch_msg(folder, item, i);
3307 MsgInfo *msginfo = imap_parse_msg(file, item);
3308 if (msginfo != NULL) {
3309 msginfo->msgnum = i;
3311 llast = ret = g_slist_append(ret, msginfo);
3313 llast = g_slist_append(llast, msginfo);
3314 llast = llast->next;
3319 session_set_access_time(SESSION(session));
3330 g_slist_free(sorted_list);
3336 MsgInfo *imap_get_msginfo(Folder *folder, FolderItem *item, gint uid)
3338 MsgInfo *msginfo = NULL;
3339 MsgInfoList *msginfolist;
3340 MsgNumberList numlist;
3342 numlist.next = NULL;
3343 numlist.data = GINT_TO_POINTER(uid);
3345 msginfolist = imap_get_msginfos(folder, item, &numlist);
3346 if (msginfolist != NULL) {
3347 msginfo = msginfolist->data;
3348 g_slist_free(msginfolist);
3354 gboolean imap_scan_required(Folder *folder, FolderItem *_item)
3356 IMAPSession *session;
3357 IMAPFolderItem *item = (IMAPFolderItem *)_item;
3358 gint ok, exists = 0, unseen = 0;
3359 guint32 uid_next, uid_val;
3360 gboolean selected_folder;
3362 g_return_val_if_fail(folder != NULL, FALSE);
3363 g_return_val_if_fail(item != NULL, FALSE);
3364 g_return_val_if_fail(item->item.folder != NULL, FALSE);
3365 g_return_val_if_fail(FOLDER_CLASS(item->item.folder) == &imap_class, FALSE);
3367 if (item->item.path == NULL)
3370 session = imap_session_get(folder);
3371 g_return_val_if_fail(session != NULL, FALSE);
3373 selected_folder = (session->mbox != NULL) &&
3374 (!strcmp(session->mbox, item->item.path));
3375 if (selected_folder && time(NULL) - item->use_cache < 2) {
3376 ok = imap_cmd_noop(session);
3377 if (ok != IMAP_SUCCESS) {
3378 debug_print("disconnected!\n");
3379 session = imap_reconnect_if_possible(folder, session);
3380 if (session == NULL)
3385 if (session->folder_content_changed
3386 || session->exists != item->item.total_msgs) {
3391 ok = imap_status(session, IMAP_FOLDER(folder), item->item.path, IMAP_FOLDER_ITEM(item),
3392 &exists, &uid_next, &uid_val, &unseen, FALSE);
3393 if (ok != IMAP_SUCCESS) {
3398 item->use_cache = time(NULL);
3399 item->c_messages = exists;
3400 item->c_uid_next = uid_next;
3401 item->c_uid_validity = uid_val;
3402 item->c_unseen = unseen;
3403 item->item.last_num = uid_next - 1;
3404 debug_print("uidnext %d, item->uid_next %d, exists %d, item->item.total_msgs %d\n",
3405 uid_next, item->uid_next, exists, item->item.total_msgs);
3406 if ((uid_next != item->uid_next) || (exists != item->item.total_msgs)
3407 || unseen != item->item.unread_msgs || uid_val != item->item.mtime) {
3416 void imap_change_flags(Folder *folder, FolderItem *item, MsgInfo *msginfo, MsgPermFlags newflags)
3418 IMAPSession *session;
3419 IMAPFlags flags_set = 0, flags_unset = 0;
3420 gint ok = IMAP_SUCCESS;
3421 MsgNumberList numlist;
3422 hashtable_data *ht_data = NULL;
3424 g_return_if_fail(folder != NULL);
3425 g_return_if_fail(folder->klass == &imap_class);
3426 g_return_if_fail(item != NULL);
3427 g_return_if_fail(item->folder == folder);
3428 g_return_if_fail(msginfo != NULL);
3429 g_return_if_fail(msginfo->folder == item);
3431 session = imap_session_get(folder);
3436 if (!MSG_IS_MARKED(msginfo->flags) && (newflags & MSG_MARKED))
3437 flags_set |= IMAP_FLAG_FLAGGED;
3438 if ( MSG_IS_MARKED(msginfo->flags) && !(newflags & MSG_MARKED))
3439 flags_unset |= IMAP_FLAG_FLAGGED;
3441 if (!MSG_IS_UNREAD(msginfo->flags) && (newflags & MSG_UNREAD))
3442 flags_unset |= IMAP_FLAG_SEEN;
3443 if ( MSG_IS_UNREAD(msginfo->flags) && !(newflags & MSG_UNREAD))
3444 flags_set |= IMAP_FLAG_SEEN;
3446 if (!MSG_IS_REPLIED(msginfo->flags) && (newflags & MSG_REPLIED))
3447 flags_set |= IMAP_FLAG_ANSWERED;
3448 if ( MSG_IS_REPLIED(msginfo->flags) && !(newflags & MSG_REPLIED))
3449 flags_unset |= IMAP_FLAG_ANSWERED;
3451 if (!MSG_IS_DELETED(msginfo->flags) && (newflags & MSG_DELETED))
3452 flags_set |= IMAP_FLAG_DELETED;
3453 if ( MSG_IS_DELETED(msginfo->flags) && !(newflags & MSG_DELETED))
3454 flags_unset |= IMAP_FLAG_DELETED;
3456 if (!flags_set && !flags_unset) {
3457 /* the changed flags were not translatable to IMAP-speak.
3458 * like MSG_POSTFILTERED, so just apply. */
3459 msginfo->flags.perm_flags = newflags;
3464 if ((ok = imap_select(session, IMAP_FOLDER(folder), msginfo->folder->path,
3465 NULL, NULL, NULL, NULL, FALSE)) != IMAP_SUCCESS) {
3469 numlist.next = NULL;
3470 numlist.data = GINT_TO_POINTER(msginfo->msgnum);
3472 if (IMAP_FOLDER_ITEM(item)->batching) {
3473 /* instead of performing an UID STORE command for each message change,
3474 * as a lot of them can change "together", we just fill in hashtables
3475 * and defer the treatment so that we're able to send only one
3478 debug_print("IMAP batch mode on, deferring flags change\n");
3480 ht_data = g_hash_table_lookup(IMAP_FOLDER_ITEM(item)->flags_set_table,
3481 GINT_TO_POINTER(flags_set));
3482 if (ht_data == NULL) {
3483 ht_data = g_new0(hashtable_data, 1);
3484 ht_data->session = session;
3485 ht_data->item = IMAP_FOLDER_ITEM(item);
3486 g_hash_table_insert(IMAP_FOLDER_ITEM(item)->flags_set_table,
3487 GINT_TO_POINTER(flags_set), ht_data);
3489 if (!g_slist_find(ht_data->msglist, GINT_TO_POINTER(msginfo->msgnum)))
3490 ht_data->msglist = g_slist_prepend(ht_data->msglist, GINT_TO_POINTER(msginfo->msgnum));
3493 ht_data = g_hash_table_lookup(IMAP_FOLDER_ITEM(item)->flags_unset_table,
3494 GINT_TO_POINTER(flags_unset));
3495 if (ht_data == NULL) {
3496 ht_data = g_new0(hashtable_data, 1);
3497 ht_data->session = session;
3498 ht_data->item = IMAP_FOLDER_ITEM(item);
3499 g_hash_table_insert(IMAP_FOLDER_ITEM(item)->flags_unset_table,
3500 GINT_TO_POINTER(flags_unset), ht_data);
3502 if (!g_slist_find(ht_data->msglist, GINT_TO_POINTER(msginfo->msgnum)))
3503 ht_data->msglist = g_slist_prepend(ht_data->msglist,
3504 GINT_TO_POINTER(msginfo->msgnum));
3507 debug_print("IMAP changing flags\n");
3509 ok = imap_set_message_flags(session, &numlist, flags_set, TRUE);
3510 if (ok != IMAP_SUCCESS) {
3517 ok = imap_set_message_flags(session, &numlist, flags_unset, FALSE);
3518 if (ok != IMAP_SUCCESS) {
3524 msginfo->flags.perm_flags = newflags;
3529 static gint imap_remove_msg(Folder *folder, FolderItem *item, gint uid)
3532 IMAPSession *session;
3534 MsgNumberList numlist;
3536 g_return_val_if_fail(folder != NULL, -1);
3537 g_return_val_if_fail(FOLDER_CLASS(folder) == &imap_class, -1);
3538 g_return_val_if_fail(item != NULL, -1);
3540 session = imap_session_get(folder);
3541 if (!session) return -1;
3543 ok = imap_select(session, IMAP_FOLDER(folder), item->path,
3544 NULL, NULL, NULL, NULL, FALSE);
3545 if (ok != IMAP_SUCCESS) {
3549 numlist.next = NULL;
3550 numlist.data = GINT_TO_POINTER(uid);
3552 ok = imap_set_message_flags
3553 (IMAP_SESSION(REMOTE_FOLDER(folder)->session),
3554 &numlist, IMAP_FLAG_DELETED, TRUE);
3555 if (ok != IMAP_SUCCESS) {
3556 log_warning(_("can't set deleted flags: %d\n"), uid);
3561 if (!session->uidplus) {
3562 ok = imap_cmd_expunge(session);
3566 uidstr = g_strdup_printf("%u", uid);
3567 ok = imap_cmd_expunge(session);
3570 if (ok != IMAP_SUCCESS) {
3571 log_warning(_("can't expunge\n"));
3576 IMAP_FOLDER_ITEM(item)->uid_list = g_slist_remove(
3577 IMAP_FOLDER_ITEM(item)->uid_list, numlist.data);
3578 dir = folder_item_get_path(item);
3579 if (is_dir_exist(dir))
3580 remove_numbered_files(dir, uid, uid);
3583 return IMAP_SUCCESS;
3586 static gint compare_msginfo(gconstpointer a, gconstpointer b)
3588 return ((MsgInfo *)a)->msgnum - ((MsgInfo *)b)->msgnum;
3591 static guint gslist_find_next_num(MsgNumberList **list, guint num)
3595 g_return_val_if_fail(list != NULL, -1);
3597 for (elem = *list; elem != NULL; elem = g_slist_next(elem))
3598 if (GPOINTER_TO_INT(elem->data) >= num)
3601 return elem != NULL ? GPOINTER_TO_INT(elem->data) : (gint)-1;
3605 * NEW and DELETED flags are not syncronized
3606 * - The NEW/RECENT flags in IMAP folders can not really be directly
3607 * modified by Sylpheed
3608 * - The DELETE/DELETED flag in IMAP and Sylpheed don't have the same
3609 * meaning, in IMAP it always removes the messages from the FolderItem
3610 * in Sylpheed it can mean to move the message to trash
3613 typedef struct _get_flags_data {
3616 MsgInfoList *msginfo_list;
3617 GRelation *msgflags;
3618 gboolean full_search;
3622 static /*gint*/ void *imap_get_flags_thread(void *data)
3624 get_flags_data *stuff = (get_flags_data *)data;
3625 Folder *folder = stuff->folder;
3626 FolderItem *item = stuff->item;
3627 MsgInfoList *msginfo_list = stuff->msginfo_list;
3628 GRelation *msgflags = stuff->msgflags;
3629 gboolean full_search = stuff->full_search;
3630 IMAPSession *session;
3631 GSList *sorted_list = NULL;
3632 GSList *unseen = NULL, *answered = NULL, *flagged = NULL, *deleted = NULL;
3633 GSList *p_unseen, *p_answered, *p_flagged, *p_deleted;
3635 GSList *seq_list, *cur;
3636 gboolean reverse_seen = FALSE;
3639 gint exists_cnt, unseen_cnt;
3640 gboolean selected_folder;
3642 if (folder == NULL || item == NULL) {
3644 return GINT_TO_POINTER(-1);
3647 session = imap_session_get(folder);
3648 if (session == NULL) {
3650 return GINT_TO_POINTER(-1);
3653 selected_folder = (session->mbox != NULL) &&
3654 (!strcmp(session->mbox, item->path));
3656 if (!selected_folder) {
3657 ok = imap_select(session, IMAP_FOLDER(folder), item->path,
3658 &exists_cnt, NULL, &unseen_cnt, NULL, TRUE);
3659 if (ok != IMAP_SUCCESS) {
3662 return GINT_TO_POINTER(-1);
3665 if (unseen_cnt > exists_cnt / 2)
3666 reverse_seen = TRUE;
3669 if (item->unread_msgs > item->total_msgs / 2)
3670 reverse_seen = TRUE;
3673 cmd_buf = g_string_new(NULL);
3675 sorted_list = g_slist_sort(g_slist_copy(msginfo_list), compare_msginfo);
3677 seq_list = imap_get_lep_set_from_msglist(msginfo_list);
3679 struct mailimap_set * set;
3680 set = mailimap_set_new_interval(1, 0);
3681 seq_list = g_slist_append(NULL, set);
3684 for (cur = seq_list; cur != NULL; cur = g_slist_next(cur)) {
3685 struct mailimap_set * imapset;
3686 clist * lep_uidlist;
3689 imapset = cur->data;
3691 r = imap_threaded_search(folder, IMAP_SEARCH_TYPE_SEEN,
3692 full_search ? NULL:imapset, &lep_uidlist);
3695 r = imap_threaded_search(folder,
3696 IMAP_SEARCH_TYPE_UNSEEN,
3697 full_search ? NULL:imapset, &lep_uidlist);
3699 if (r == MAILIMAP_NO_ERROR) {
3702 uidlist = imap_uid_list_from_lep(lep_uidlist);
3703 mailimap_search_result_free(lep_uidlist);
3705 unseen = g_slist_concat(unseen, uidlist);
3708 r = imap_threaded_search(folder, IMAP_SEARCH_TYPE_FLAGGED,
3709 full_search ? NULL:imapset, &lep_uidlist);
3710 if (r == MAILIMAP_NO_ERROR) {
3713 uidlist = imap_uid_list_from_lep(lep_uidlist);
3714 mailimap_search_result_free(lep_uidlist);
3716 flagged = g_slist_concat(flagged, uidlist);
3719 if (item->opened || item->processing_pending || item == folder->inbox) {
3720 r = imap_threaded_search(folder, IMAP_SEARCH_TYPE_ANSWERED,
3721 full_search ? NULL:imapset, &lep_uidlist);
3722 if (r == MAILIMAP_NO_ERROR) {
3725 uidlist = imap_uid_list_from_lep(lep_uidlist);
3726 mailimap_search_result_free(lep_uidlist);
3728 answered = g_slist_concat(answered, uidlist);
3731 r = imap_threaded_search(folder, IMAP_SEARCH_TYPE_DELETED,
3732 full_search ? NULL:imapset, &lep_uidlist);
3733 if (r == MAILIMAP_NO_ERROR) {
3736 uidlist = imap_uid_list_from_lep(lep_uidlist);
3737 mailimap_search_result_free(lep_uidlist);
3739 deleted = g_slist_concat(deleted, uidlist);
3745 p_answered = answered;
3746 p_flagged = flagged;
3747 p_deleted = deleted;
3749 for (elem = sorted_list; elem != NULL; elem = g_slist_next(elem)) {
3754 msginfo = (MsgInfo *) elem->data;
3755 flags = msginfo->flags.perm_flags;
3756 wasnew = (flags & MSG_NEW);
3757 if (item->opened || item->processing_pending || item == folder->inbox) {
3758 flags &= ~((reverse_seen ? 0 : MSG_UNREAD | MSG_NEW) | MSG_REPLIED | MSG_MARKED);
3760 flags &= ~((reverse_seen ? 0 : MSG_UNREAD | MSG_NEW | MSG_MARKED));
3763 flags |= MSG_UNREAD | (wasnew ? MSG_NEW : 0);
3764 if (gslist_find_next_num(&p_unseen, msginfo->msgnum) == msginfo->msgnum) {
3765 if (!reverse_seen) {
3766 flags |= MSG_UNREAD | (wasnew ? MSG_NEW : 0);
3768 flags &= ~(MSG_UNREAD | MSG_NEW);
3772 if (gslist_find_next_num(&p_flagged, msginfo->msgnum) == msginfo->msgnum)
3773 flags |= MSG_MARKED;
3775 flags &= ~MSG_MARKED;
3777 if (item->opened || item->processing_pending || item == folder->inbox) {
3778 if (gslist_find_next_num(&p_answered, msginfo->msgnum) == msginfo->msgnum)
3779 flags |= MSG_REPLIED;
3781 flags &= ~MSG_REPLIED;
3782 if (gslist_find_next_num(&p_deleted, msginfo->msgnum) == msginfo->msgnum)
3783 flags |= MSG_DELETED;
3785 flags &= ~MSG_DELETED;
3787 g_relation_insert(msgflags, msginfo, GINT_TO_POINTER(flags));
3790 imap_lep_set_free(seq_list);
3791 g_slist_free(flagged);
3792 g_slist_free(deleted);
3793 g_slist_free(answered);
3794 g_slist_free(unseen);
3795 g_slist_free(sorted_list);
3796 g_string_free(cmd_buf, TRUE);
3800 return GINT_TO_POINTER(0);
3803 static gint imap_get_flags(Folder *folder, FolderItem *item,
3804 MsgInfoList *msginfo_list, GRelation *msgflags)
3807 get_flags_data *data = g_new0(get_flags_data, 1);
3809 data->folder = folder;
3811 data->msginfo_list = msginfo_list;
3812 data->msgflags = msgflags;
3813 data->full_search = FALSE;
3815 GSList *tmp = NULL, *cur;
3817 if (prefs_common.work_offline &&
3818 !inc_offline_should_override(
3819 _("Sylpheed-Claws needs network access in order "
3820 "to access the IMAP server."))) {
3825 tmp = folder_item_get_msg_list(item);
3827 if (g_slist_length(tmp) <= g_slist_length(msginfo_list))
3828 data->full_search = TRUE;
3830 for (cur = tmp; cur; cur = cur->next)
3831 procmsg_msginfo_free((MsgInfo *)cur->data);
3835 result = GPOINTER_TO_INT(imap_get_flags_thread(data));
3842 static gboolean process_flags(gpointer key, gpointer value, gpointer user_data)
3844 gboolean flags_set = GPOINTER_TO_INT(user_data);
3845 gint flags_value = GPOINTER_TO_INT(key);
3846 hashtable_data *data = (hashtable_data *)value;
3847 IMAPFolderItem *_item = data->item;
3848 FolderItem *item = (FolderItem *)_item;
3849 gint ok = IMAP_ERROR;
3850 IMAPSession *session = imap_session_get(item->folder);
3852 data->msglist = g_slist_reverse(data->msglist);
3854 debug_print("IMAP %ssetting flags to %d for %d messages\n",
3857 g_slist_length(data->msglist));
3861 ok = imap_select(session, IMAP_FOLDER(item->folder), item->path,
3862 NULL, NULL, NULL, NULL, FALSE);
3864 if (ok == IMAP_SUCCESS) {
3865 imap_set_message_flags(data->session, data->msglist, flags_value, flags_set);
3867 g_warning("can't select mailbox %s\n", item->path);
3871 g_slist_free(data->msglist);
3876 static void process_hashtable(IMAPFolderItem *item)
3878 if (item->flags_set_table) {
3879 g_hash_table_foreach_remove(item->flags_set_table, process_flags, GINT_TO_POINTER(TRUE));
3880 g_hash_table_destroy(item->flags_set_table);
3881 item->flags_set_table = NULL;
3883 if (item->flags_unset_table) {
3884 g_hash_table_foreach_remove(item->flags_unset_table, process_flags, GINT_TO_POINTER(FALSE));
3885 g_hash_table_destroy(item->flags_unset_table);
3886 item->flags_unset_table = NULL;
3890 static void imap_set_batch (Folder *folder, FolderItem *_item, gboolean batch)
3892 IMAPFolderItem *item = (IMAPFolderItem *)_item;
3894 g_return_if_fail(item != NULL);
3896 if (item->batching == batch)
3900 item->batching = TRUE;
3901 debug_print("IMAP switching to batch mode\n");
3902 if (!item->flags_set_table) {
3903 item->flags_set_table = g_hash_table_new(NULL, g_direct_equal);
3905 if (!item->flags_unset_table) {
3906 item->flags_unset_table = g_hash_table_new(NULL, g_direct_equal);
3909 debug_print("IMAP switching away from batch mode\n");
3911 process_hashtable(item);
3912 item->batching = FALSE;
3918 /* data types conversion libetpan <-> sylpheed */
3922 #define ETPAN_IMAP_MB_MARKED 1
3923 #define ETPAN_IMAP_MB_UNMARKED 2
3924 #define ETPAN_IMAP_MB_NOSELECT 4
3925 #define ETPAN_IMAP_MB_NOINFERIORS 8
3927 static int imap_flags_to_flags(struct mailimap_mbx_list_flags * imap_flags)
3933 if (imap_flags->mbf_type == MAILIMAP_MBX_LIST_FLAGS_SFLAG) {
3934 switch (imap_flags->mbf_sflag) {
3935 case MAILIMAP_MBX_LIST_SFLAG_MARKED:
3936 flags |= ETPAN_IMAP_MB_MARKED;
3938 case MAILIMAP_MBX_LIST_SFLAG_NOSELECT:
3939 flags |= ETPAN_IMAP_MB_NOSELECT;
3941 case MAILIMAP_MBX_LIST_SFLAG_UNMARKED:
3942 flags |= ETPAN_IMAP_MB_UNMARKED;
3947 for(cur = clist_begin(imap_flags->mbf_oflags) ; cur != NULL ;
3948 cur = clist_next(cur)) {
3949 struct mailimap_mbx_list_oflag * oflag;
3951 oflag = clist_content(cur);
3953 switch (oflag->of_type) {
3954 case MAILIMAP_MBX_LIST_OFLAG_NOINFERIORS:
3955 flags |= ETPAN_IMAP_MB_NOINFERIORS;
3963 static GSList * imap_list_from_lep(IMAPFolder * folder,
3964 clist * list, const gchar * real_path, gboolean all)
3967 GSList * item_list = NULL, *llast = NULL;
3969 for(iter = clist_begin(list) ; iter != NULL ;
3970 iter = clist_next(iter)) {
3971 struct mailimap_mailbox_list * mb;
3979 FolderItem *new_item;
3981 mb = clist_content(iter);
3987 if (mb->mb_flag != NULL)
3988 flags = imap_flags_to_flags(mb->mb_flag);
3990 delimiter = mb->mb_delimiter;
3993 dup_name = strdup(name);
3994 if (delimiter != '\0')
3995 subst_char(dup_name, delimiter, '/');
3997 base = g_path_get_basename(dup_name);
3998 if (base[0] == '.') {
4004 if (!all && strcmp(dup_name, real_path) == 0) {
4010 if (!all && dup_name[strlen(dup_name)-1] == '/') {
4016 loc_name = imap_modified_utf7_to_utf8(base);
4017 loc_path = imap_modified_utf7_to_utf8(dup_name);
4019 new_item = folder_item_new(FOLDER(folder), loc_name, loc_path);
4020 if ((flags & ETPAN_IMAP_MB_NOINFERIORS) != 0)
4021 new_item->no_sub = TRUE;
4022 if (strcmp(dup_name, "INBOX") != 0 &&
4023 ((flags & ETPAN_IMAP_MB_NOSELECT) != 0))
4024 new_item->no_select = TRUE;
4026 if (item_list == NULL)
4027 llast = item_list = g_slist_append(item_list, new_item);
4029 llast = g_slist_append(llast, new_item);
4030 llast = llast->next;
4032 debug_print("folder '%s' found.\n", loc_path);
4043 static GSList * imap_get_lep_set_from_numlist(MsgNumberList *numlist)
4045 GSList *sorted_list, *cur;
4046 guint first, last, next;
4047 GSList *ret_list = NULL, *llast = NULL;
4049 struct mailimap_set * current_set;
4050 unsigned int item_count;
4052 if (numlist == NULL)
4056 current_set = mailimap_set_new_empty();
4058 sorted_list = g_slist_copy(numlist);
4059 sorted_list = g_slist_sort(sorted_list, g_int_compare);
4061 first = GPOINTER_TO_INT(sorted_list->data);
4064 for (cur = sorted_list; cur != NULL; cur = g_slist_next(cur)) {
4065 if (GPOINTER_TO_INT(cur->data) == 0)
4070 last = GPOINTER_TO_INT(cur->data);
4072 next = GPOINTER_TO_INT(cur->next->data);
4076 if (last + 1 != next || next == 0) {
4078 struct mailimap_set_item * item;
4079 item = mailimap_set_item_new(first, last);
4080 mailimap_set_add(current_set, item);
4085 if (count >= IMAP_SET_MAX_COUNT) {
4086 if (ret_list == NULL)
4087 llast = ret_list = g_slist_append(ret_list,
4090 llast = g_slist_append(llast, current_set);
4091 llast = llast->next;
4093 current_set = mailimap_set_new_empty();
4100 if (clist_count(current_set->set_list) > 0) {
4101 ret_list = g_slist_append(ret_list,
4105 g_slist_free(sorted_list);
4110 static GSList * imap_get_lep_set_from_msglist(MsgInfoList *msglist)
4112 MsgNumberList *numlist = NULL;
4116 for (cur = msglist; cur != NULL; cur = g_slist_next(cur)) {
4117 MsgInfo *msginfo = (MsgInfo *) cur->data;
4119 numlist = g_slist_prepend(numlist, GINT_TO_POINTER(msginfo->msgnum));
4121 numlist = g_slist_reverse(numlist);
4122 seq_list = imap_get_lep_set_from_numlist(numlist);
4123 g_slist_free(numlist);
4128 static GSList * imap_uid_list_from_lep(clist * list)
4135 for(iter = clist_begin(list) ; iter != NULL ;
4136 iter = clist_next(iter)) {
4139 puid = clist_content(iter);
4140 result = g_slist_prepend(result, GINT_TO_POINTER(* puid));
4143 result = g_slist_reverse(result);
4147 static GSList * imap_uid_list_from_lep_tab(carray * list)
4154 for(i = 0 ; i < carray_count(list) ; i ++) {
4157 puid = carray_get(list, i);
4158 result = g_slist_prepend(result, GINT_TO_POINTER(* puid));
4160 result = g_slist_reverse(result);
4164 static MsgInfo *imap_envelope_from_lep(struct imap_fetch_env_info * info,
4167 MsgInfo *msginfo = NULL;
4170 MsgFlags flags = {0, 0};
4172 if (info->headers == NULL)
4175 MSG_SET_TMP_FLAGS(flags, MSG_IMAP);
4176 if (folder_has_parent_of_type(item, F_QUEUE)) {
4177 MSG_SET_TMP_FLAGS(flags, MSG_QUEUED);
4178 } else if (folder_has_parent_of_type(item, F_DRAFT)) {
4179 MSG_SET_TMP_FLAGS(flags, MSG_DRAFT);
4181 flags.perm_flags = info->flags;
4185 msginfo = procheader_parse_str(info->headers, flags, FALSE, FALSE);
4188 msginfo->msgnum = uid;
4189 msginfo->size = size;
4195 static void imap_lep_set_free(GSList *seq_list)
4199 for(cur = seq_list ; cur != NULL ; cur = g_slist_next(cur)) {
4200 struct mailimap_set * imapset;
4202 imapset = cur->data;
4203 mailimap_set_free(imapset);
4205 g_slist_free(seq_list);
4208 static struct mailimap_flag_list * imap_flag_to_lep(IMAPFlags flags)
4210 struct mailimap_flag_list * flag_list;
4212 flag_list = mailimap_flag_list_new_empty();
4214 if (IMAP_IS_SEEN(flags))
4215 mailimap_flag_list_add(flag_list,
4216 mailimap_flag_new_seen());
4217 if (IMAP_IS_ANSWERED(flags))
4218 mailimap_flag_list_add(flag_list,
4219 mailimap_flag_new_answered());
4220 if (IMAP_IS_FLAGGED(flags))
4221 mailimap_flag_list_add(flag_list,
4222 mailimap_flag_new_flagged());
4223 if (IMAP_IS_DELETED(flags))
4224 mailimap_flag_list_add(flag_list,
4225 mailimap_flag_new_deleted());
4226 if (IMAP_IS_DRAFT(flags))
4227 mailimap_flag_list_add(flag_list,
4228 mailimap_flag_new_draft());
4233 guint imap_folder_get_refcnt(Folder *folder)
4235 return ((IMAPFolder *)folder)->refcnt;
4238 void imap_folder_ref(Folder *folder)
4240 ((IMAPFolder *)folder)->refcnt++;
4243 void imap_folder_unref(Folder *folder)
4245 if (((IMAPFolder *)folder)->refcnt > 0)
4246 ((IMAPFolder *)folder)->refcnt--;
4249 #else /* HAVE_LIBETPAN */
4251 static FolderClass imap_class;
4253 static XMLTag *imap_item_get_xml(Folder *folder, FolderItem *item);
4254 static void imap_item_set_xml(Folder *folder, FolderItem *item, XMLTag *tag);
4256 static Folder *imap_folder_new (const gchar *name,
4261 static gint imap_create_tree (Folder *folder)
4265 static FolderItem *imap_create_folder (Folder *folder,
4271 static gint imap_rename_folder (Folder *folder,
4278 gchar imap_get_path_separator_for_item(FolderItem *item)
4283 FolderClass *imap_get_class(void)
4285 if (imap_class.idstr == NULL) {
4286 imap_class.type = F_IMAP;
4287 imap_class.idstr = "imap";
4288 imap_class.uistr = "IMAP4";
4290 imap_class.new_folder = imap_folder_new;
4291 imap_class.create_tree = imap_create_tree;
4292 imap_class.create_folder = imap_create_folder;
4293 imap_class.rename_folder = imap_rename_folder;
4295 imap_class.set_xml = folder_set_xml;
4296 imap_class.get_xml = folder_get_xml;
4297 imap_class.item_set_xml = imap_item_set_xml;
4298 imap_class.item_get_xml = imap_item_get_xml;
4299 /* nothing implemented */
4306 void imap_synchronise(FolderItem *item)
4308 imap_gtk_synchronise(item);
4311 static void imap_item_set_xml(Folder *folder, FolderItem *item, XMLTag *tag)
4313 #ifdef HAVE_LIBETPAN
4316 folder_item_set_xml(folder, item, tag);
4318 #ifdef HAVE_LIBETPAN
4319 for (cur = tag->attr; cur != NULL; cur = g_list_next(cur)) {
4320 XMLAttr *attr = (XMLAttr *) cur->data;
4322 if (!attr || !attr->name || !attr->value) continue;
4323 if (!strcmp(attr->name, "uidnext"))
4324 IMAP_FOLDER_ITEM(item)->uid_next = atoi(attr->value);
4329 static XMLTag *imap_item_get_xml(Folder *folder, FolderItem *item)
4333 tag = folder_item_get_xml(folder, item);
4335 #ifdef HAVE_LIBETPAN
4336 xml_tag_add_attr(tag, xml_attr_new_int("uidnext",
4337 IMAP_FOLDER_ITEM(item)->uid_next));