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)
1128 statusbar_progress_all(0,0,0);
1129 statusbar_pop_all();
1131 imap_cmd_expunge(session);
1139 static gint imap_do_copy_msgs(Folder *folder, FolderItem *dest,
1140 MsgInfoList *msglist, GRelation *relation)
1144 GSList *seq_list, *cur;
1146 IMAPSession *session;
1147 gint ok = IMAP_SUCCESS;
1148 GRelation *uid_mapping;
1151 g_return_val_if_fail(folder != NULL, -1);
1152 g_return_val_if_fail(dest != NULL, -1);
1153 g_return_val_if_fail(msglist != NULL, -1);
1155 session = imap_session_get(folder);
1161 msginfo = (MsgInfo *)msglist->data;
1163 src = msginfo->folder;
1165 g_warning("the src folder is identical to the dest.\n");
1170 if (src->folder != dest->folder) {
1171 GSList *infolist = NULL, *cur;
1173 for (cur = msglist; cur; cur = cur->next) {
1174 msginfo = (MsgInfo *)cur->data;
1175 MsgFileInfo *fileinfo = g_new0(MsgFileInfo, 1);
1176 fileinfo->file = procmsg_get_message_file(msginfo);
1177 fileinfo->flags = &(msginfo->flags);
1178 infolist = g_slist_prepend(infolist, fileinfo);
1180 infolist = g_slist_reverse(infolist);
1181 res = folder_item_add_msgs(dest, infolist, FALSE);
1182 for (cur = infolist; cur; cur = cur->next) {
1183 MsgFileInfo *info = (MsgFileInfo *)cur->data;
1187 g_slist_free(infolist);
1191 ok = imap_select(session, IMAP_FOLDER(folder), msginfo->folder->path,
1192 NULL, NULL, NULL, NULL, FALSE);
1193 if (ok != IMAP_SUCCESS) {
1198 destdir = imap_get_real_path(IMAP_FOLDER(folder), dest->path);
1199 seq_list = imap_get_lep_set_from_msglist(msglist);
1200 uid_mapping = g_relation_new(2);
1201 g_relation_index(uid_mapping, 0, g_direct_hash, g_direct_equal);
1203 statusbar_print_all(_("Copying messages..."));
1204 for (cur = seq_list; cur != NULL; cur = g_slist_next(cur)) {
1205 struct mailimap_set * seq_set;
1206 seq_set = cur->data;
1208 debug_print("Copying messages from %s to %s ...\n",
1209 src->path, destdir);
1211 ok = imap_cmd_copy(session, seq_set, destdir, uid_mapping);
1212 if (ok != IMAP_SUCCESS) {
1213 g_relation_destroy(uid_mapping);
1214 imap_lep_set_free(seq_list);
1220 for (cur = msglist; cur != NULL; cur = g_slist_next(cur)) {
1221 MsgInfo *msginfo = (MsgInfo *)cur->data;
1224 tuples = g_relation_select(uid_mapping,
1225 GINT_TO_POINTER(msginfo->msgnum),
1227 if (tuples->len > 0) {
1228 gint num = GPOINTER_TO_INT(g_tuples_index(tuples, 0, 1));
1229 g_relation_insert(relation, msginfo,
1230 GPOINTER_TO_INT(num));
1234 g_relation_insert(relation, msginfo,
1235 GPOINTER_TO_INT(0));
1236 g_tuples_destroy(tuples);
1238 statusbar_pop_all();
1240 g_relation_destroy(uid_mapping);
1241 imap_lep_set_free(seq_list);
1245 IMAP_FOLDER_ITEM(dest)->lastuid = 0;
1246 IMAP_FOLDER_ITEM(dest)->uid_next = 0;
1247 g_slist_free(IMAP_FOLDER_ITEM(dest)->uid_list);
1248 IMAP_FOLDER_ITEM(dest)->uid_list = NULL;
1251 if (ok == IMAP_SUCCESS)
1257 static gint imap_copy_msg(Folder *folder, FolderItem *dest, MsgInfo *msginfo)
1261 g_return_val_if_fail(msginfo != NULL, -1);
1263 msglist.data = msginfo;
1264 msglist.next = NULL;
1266 return imap_copy_msgs(folder, dest, &msglist, NULL);
1269 static gint imap_copy_msgs(Folder *folder, FolderItem *dest,
1270 MsgInfoList *msglist, GRelation *relation)
1275 g_return_val_if_fail(folder != NULL, -1);
1276 g_return_val_if_fail(dest != NULL, -1);
1277 g_return_val_if_fail(msglist != NULL, -1);
1279 msginfo = (MsgInfo *)msglist->data;
1280 g_return_val_if_fail(msginfo->folder != NULL, -1);
1282 ret = imap_do_copy_msgs(folder, dest, msglist, relation);
1287 static gint imap_do_remove_msgs(Folder *folder, FolderItem *dest,
1288 MsgInfoList *msglist, GRelation *relation)
1290 gchar *destdir, *dir;
1291 GSList *numlist = NULL, *cur;
1293 IMAPSession *session;
1294 gint ok = IMAP_SUCCESS;
1295 GRelation *uid_mapping;
1297 g_return_val_if_fail(folder != NULL, -1);
1298 g_return_val_if_fail(dest != NULL, -1);
1299 g_return_val_if_fail(msglist != NULL, -1);
1301 session = imap_session_get(folder);
1306 msginfo = (MsgInfo *)msglist->data;
1308 ok = imap_select(session, IMAP_FOLDER(folder), msginfo->folder->path,
1309 NULL, NULL, NULL, NULL, FALSE);
1310 if (ok != IMAP_SUCCESS) {
1315 destdir = imap_get_real_path(IMAP_FOLDER(folder), dest->path);
1316 for (cur = msglist; cur; cur = cur->next) {
1317 msginfo = (MsgInfo *)cur->data;
1318 if (!MSG_IS_DELETED(msginfo->flags))
1319 numlist = g_slist_prepend(numlist, GINT_TO_POINTER(msginfo->msgnum));
1321 numlist = g_slist_reverse(numlist);
1323 uid_mapping = g_relation_new(2);
1324 g_relation_index(uid_mapping, 0, g_direct_hash, g_direct_equal);
1326 ok = imap_set_message_flags
1327 (IMAP_SESSION(REMOTE_FOLDER(folder)->session),
1328 numlist, IMAP_FLAG_DELETED, TRUE);
1329 if (ok != IMAP_SUCCESS) {
1330 log_warning(_("can't set deleted flags\n"));
1334 ok = imap_cmd_expunge(session);
1335 if (ok != IMAP_SUCCESS) {
1336 log_warning(_("can't expunge\n"));
1341 dir = folder_item_get_path(msginfo->folder);
1342 if (is_dir_exist(dir)) {
1343 for (cur = msglist; cur; cur = cur->next) {
1344 msginfo = (MsgInfo *)cur->data;
1345 remove_numbered_files(dir, msginfo->msgnum, msginfo->msgnum);
1350 g_relation_destroy(uid_mapping);
1351 g_slist_free(numlist);
1355 if (ok == IMAP_SUCCESS)
1361 static gint imap_remove_msgs(Folder *folder, FolderItem *dest,
1362 MsgInfoList *msglist, GRelation *relation)
1366 g_return_val_if_fail(folder != NULL, -1);
1367 g_return_val_if_fail(dest != NULL, -1);
1368 if (msglist == NULL)
1371 msginfo = (MsgInfo *)msglist->data;
1372 g_return_val_if_fail(msginfo->folder != NULL, -1);
1374 return imap_do_remove_msgs(folder, dest, msglist, relation);
1377 static gint imap_remove_all_msg(Folder *folder, FolderItem *item)
1379 GSList *list = folder_item_get_msg_list(item);
1380 gint res = imap_remove_msgs(folder, item, list, NULL);
1381 procmsg_msg_list_free(list);
1385 static gboolean imap_is_msg_changed(Folder *folder, FolderItem *item,
1388 /* TODO: properly implement this method */
1392 static gint imap_close(Folder *folder, FolderItem *item)
1397 static gint imap_scan_tree(Folder *folder)
1399 FolderItem *item = NULL;
1400 IMAPSession *session;
1401 gchar *root_folder = NULL;
1403 g_return_val_if_fail(folder != NULL, -1);
1404 g_return_val_if_fail(folder->account != NULL, -1);
1406 session = imap_session_get(folder);
1408 if (!folder->node) {
1409 folder_tree_destroy(folder);
1410 item = folder_item_new(folder, folder->name, NULL);
1411 item->folder = folder;
1412 folder->node = item->node = g_node_new(item);
1418 if (folder->account->imap_dir && *folder->account->imap_dir) {
1423 Xstrdup_a(root_folder, folder->account->imap_dir, {unlock_session();return -1;});
1424 extract_quote(root_folder, '"');
1425 subst_char(root_folder,
1426 imap_get_path_separator(IMAP_FOLDER(folder),
1429 strtailchomp(root_folder, '/');
1430 real_path = imap_get_real_path
1431 (IMAP_FOLDER(folder), root_folder);
1432 debug_print("IMAP root directory: %s\n", real_path);
1434 /* check if root directory exist */
1436 r = imap_threaded_list(session->folder, "", real_path,
1438 if ((r != MAILIMAP_NO_ERROR) || (clist_count(lep_list) == 0)) {
1439 if (!folder->node) {
1440 item = folder_item_new(folder, folder->name, NULL);
1441 item->folder = folder;
1442 folder->node = item->node = g_node_new(item);
1447 mailimap_list_result_free(lep_list);
1453 item = FOLDER_ITEM(folder->node->data);
1454 if (!item || ((item->path || root_folder) &&
1455 strcmp2(item->path, root_folder) != 0)) {
1456 folder_tree_destroy(folder);
1457 item = folder_item_new(folder, folder->name, root_folder);
1458 item->folder = folder;
1459 folder->node = item->node = g_node_new(item);
1462 imap_scan_tree_recursive(session, FOLDER_ITEM(folder->node->data));
1463 imap_create_missing_folders(folder);
1469 static gint imap_scan_tree_recursive(IMAPSession *session, FolderItem *item)
1472 IMAPFolder *imapfolder;
1473 FolderItem *new_item;
1474 GSList *item_list, *cur;
1477 gchar *wildcard_path;
1483 g_return_val_if_fail(item != NULL, -1);
1484 g_return_val_if_fail(item->folder != NULL, -1);
1485 g_return_val_if_fail(item->no_sub == FALSE, -1);
1487 folder = item->folder;
1488 imapfolder = IMAP_FOLDER(folder);
1490 separator = imap_get_path_separator(imapfolder, item->path);
1492 if (folder->ui_func)
1493 folder->ui_func(folder, item, folder->ui_func_data);
1496 wildcard[0] = separator;
1499 real_path = imap_get_real_path(imapfolder, item->path);
1503 real_path = g_strdup("");
1506 Xstrcat_a(wildcard_path, real_path, wildcard,
1507 {g_free(real_path); return IMAP_ERROR;});
1509 r = imap_threaded_list(folder, "", wildcard_path, &lep_list);
1510 if (r != MAILIMAP_NO_ERROR) {
1514 item_list = imap_list_from_lep(imapfolder,
1515 lep_list, real_path, FALSE);
1516 mailimap_list_result_free(lep_list);
1521 node = item->node->children;
1522 while (node != NULL) {
1523 FolderItem *old_item = FOLDER_ITEM(node->data);
1524 GNode *next = node->next;
1527 for (cur = item_list; cur != NULL; cur = cur->next) {
1528 FolderItem *cur_item = FOLDER_ITEM(cur->data);
1529 if (!strcmp2(old_item->path, cur_item->path)) {
1530 new_item = cur_item;
1535 debug_print("folder '%s' not found. removing...\n",
1537 folder_item_remove(old_item);
1539 old_item->no_sub = new_item->no_sub;
1540 old_item->no_select = new_item->no_select;
1541 if (old_item->no_sub == TRUE && node->children) {
1542 debug_print("folder '%s' doesn't have "
1543 "subfolders. removing...\n",
1545 folder_item_remove_children(old_item);
1552 for (cur = item_list; cur != NULL; cur = cur->next) {
1553 FolderItem *cur_item = FOLDER_ITEM(cur->data);
1556 for (node = item->node->children; node != NULL;
1557 node = node->next) {
1558 if (!strcmp2(FOLDER_ITEM(node->data)->path,
1560 new_item = FOLDER_ITEM(node->data);
1561 folder_item_destroy(cur_item);
1567 new_item = cur_item;
1568 debug_print("new folder '%s' found.\n", new_item->path);
1569 folder_item_append(item, new_item);
1572 if (!strcmp(new_item->path, "INBOX")) {
1573 new_item->stype = F_INBOX;
1574 folder->inbox = new_item;
1575 } else if (!folder_item_parent(item) || item->stype == F_INBOX) {
1578 base = g_path_get_basename(new_item->path);
1580 if (!folder->outbox && !g_ascii_strcasecmp(base, "Sent")) {
1581 new_item->stype = F_OUTBOX;
1582 folder->outbox = new_item;
1583 } else if (!folder->draft && !g_ascii_strcasecmp(base, "Drafts")) {
1584 new_item->stype = F_DRAFT;
1585 folder->draft = new_item;
1586 } else if (!folder->queue && !g_ascii_strcasecmp(base, "Queue")) {
1587 new_item->stype = F_QUEUE;
1588 folder->queue = new_item;
1589 } else if (!folder->trash && !g_ascii_strcasecmp(base, "Trash")) {
1590 new_item->stype = F_TRASH;
1591 folder->trash = new_item;
1596 if (new_item->no_sub == FALSE)
1597 imap_scan_tree_recursive(session, new_item);
1600 g_slist_free(item_list);
1602 return IMAP_SUCCESS;
1605 static gint imap_create_tree(Folder *folder)
1607 g_return_val_if_fail(folder != NULL, -1);
1608 g_return_val_if_fail(folder->node != NULL, -1);
1609 g_return_val_if_fail(folder->node->data != NULL, -1);
1610 g_return_val_if_fail(folder->account != NULL, -1);
1612 imap_scan_tree(folder);
1613 imap_create_missing_folders(folder);
1618 static void imap_create_missing_folders(Folder *folder)
1620 g_return_if_fail(folder != NULL);
1623 folder->inbox = imap_create_special_folder
1624 (folder, F_INBOX, "INBOX");
1626 folder->trash = imap_create_special_folder
1627 (folder, F_TRASH, "Trash");
1629 folder->queue = imap_create_special_folder
1630 (folder, F_QUEUE, "Queue");
1631 if (!folder->outbox)
1632 folder->outbox = imap_create_special_folder
1633 (folder, F_OUTBOX, "Sent");
1635 folder->draft = imap_create_special_folder
1636 (folder, F_DRAFT, "Drafts");
1639 static FolderItem *imap_create_special_folder(Folder *folder,
1640 SpecialFolderItemType stype,
1644 FolderItem *new_item;
1646 g_return_val_if_fail(folder != NULL, NULL);
1647 g_return_val_if_fail(folder->node != NULL, NULL);
1648 g_return_val_if_fail(folder->node->data != NULL, NULL);
1649 g_return_val_if_fail(folder->account != NULL, NULL);
1650 g_return_val_if_fail(name != NULL, NULL);
1652 item = FOLDER_ITEM(folder->node->data);
1653 new_item = imap_create_folder(folder, item, name);
1656 g_warning("Can't create '%s'\n", name);
1657 if (!folder->inbox) return NULL;
1659 new_item = imap_create_folder(folder, folder->inbox, name);
1661 g_warning("Can't create '%s' under INBOX\n", name);
1663 new_item->stype = stype;
1665 new_item->stype = stype;
1670 static gchar *imap_folder_get_path(Folder *folder)
1674 g_return_val_if_fail(folder != NULL, NULL);
1675 g_return_val_if_fail(folder->account != NULL, NULL);
1677 folder_path = g_strconcat(get_imap_cache_dir(),
1679 folder->account->recv_server,
1681 folder->account->userid,
1687 static gchar *imap_item_get_path(Folder *folder, FolderItem *item)
1689 gchar *folder_path, *path;
1691 g_return_val_if_fail(folder != NULL, NULL);
1692 g_return_val_if_fail(item != NULL, NULL);
1693 folder_path = imap_folder_get_path(folder);
1695 g_return_val_if_fail(folder_path != NULL, NULL);
1696 if (folder_path[0] == G_DIR_SEPARATOR) {
1698 path = g_strconcat(folder_path, G_DIR_SEPARATOR_S,
1701 path = g_strdup(folder_path);
1704 path = g_strconcat(get_home_dir(), G_DIR_SEPARATOR_S,
1705 folder_path, G_DIR_SEPARATOR_S,
1708 path = g_strconcat(get_home_dir(), G_DIR_SEPARATOR_S,
1711 g_free(folder_path);
1716 static FolderItem *imap_create_folder(Folder *folder, FolderItem *parent,
1719 gchar *dirpath, *imap_path;
1720 IMAPSession *session;
1721 FolderItem *new_item;
1726 gboolean no_select = FALSE, no_sub = FALSE;
1728 g_return_val_if_fail(folder != NULL, NULL);
1729 g_return_val_if_fail(folder->account != NULL, NULL);
1730 g_return_val_if_fail(parent != NULL, NULL);
1731 g_return_val_if_fail(name != NULL, NULL);
1733 session = imap_session_get(folder);
1739 if (!folder_item_parent(parent) && strcmp(name, "INBOX") == 0) {
1740 dirpath = g_strdup(name);
1741 }else if (parent->path)
1742 dirpath = g_strconcat(parent->path, "/", name, NULL);
1743 else if ((p = strchr(name, '/')) != NULL && *(p + 1) != '\0')
1744 dirpath = g_strdup(name);
1745 else if (folder->account->imap_dir && *folder->account->imap_dir) {
1748 Xstrdup_a(imap_dir, folder->account->imap_dir, {unlock_session();return NULL;});
1749 strtailchomp(imap_dir, '/');
1750 dirpath = g_strconcat(imap_dir, "/", name, NULL);
1752 dirpath = g_strdup(name);
1756 /* keep trailing directory separator to create a folder that contains
1758 imap_path = imap_utf8_to_modified_utf7(dirpath);
1760 strtailchomp(dirpath, '/');
1761 Xstrdup_a(new_name, name, {
1766 separator = imap_get_path_separator(IMAP_FOLDER(folder), imap_path);
1767 imap_path_separator_subst(imap_path, separator);
1768 /* remove trailing / for display */
1769 strtailchomp(new_name, '/');
1771 if (strcmp(dirpath, "INBOX") != 0) {
1773 gboolean exist = FALSE;
1777 argbuf = g_ptr_array_new();
1778 r = imap_threaded_list(folder, "", imap_path, &lep_list);
1779 if (r != MAILIMAP_NO_ERROR) {
1780 log_warning(_("can't create mailbox: LIST failed\n"));
1783 ptr_array_free_strings(argbuf);
1784 g_ptr_array_free(argbuf, TRUE);
1789 if (clist_count(lep_list) > 0)
1791 mailimap_list_result_free(lep_list);
1794 ok = imap_cmd_create(session, imap_path);
1795 if (ok != IMAP_SUCCESS) {
1796 log_warning(_("can't create mailbox\n"));
1802 r = imap_threaded_list(folder, "", imap_path, &lep_list);
1803 if (r == MAILIMAP_NO_ERROR) {
1804 GSList *item_list = imap_list_from_lep(IMAP_FOLDER(folder),
1805 lep_list, dirpath, TRUE);
1807 FolderItem *cur_item = FOLDER_ITEM(item_list->data);
1808 no_select = cur_item->no_select;
1809 no_sub = cur_item->no_sub;
1810 g_slist_free(item_list);
1812 mailimap_list_result_free(lep_list);
1819 /* just get flags */
1820 r = imap_threaded_list(folder, "", "INBOX", &lep_list);
1821 if (r == MAILIMAP_NO_ERROR) {
1822 GSList *item_list = imap_list_from_lep(IMAP_FOLDER(folder),
1823 lep_list, dirpath, TRUE);
1825 FolderItem *cur_item = FOLDER_ITEM(item_list->data);
1826 no_select = cur_item->no_select;
1827 no_sub = cur_item->no_sub;
1828 g_slist_free(item_list);
1830 mailimap_list_result_free(lep_list);
1834 new_item = folder_item_new(folder, new_name, dirpath);
1835 new_item->no_select = no_select;
1836 new_item->no_sub = no_sub;
1837 folder_item_append(parent, new_item);
1841 dirpath = folder_item_get_path(new_item);
1842 if (!is_dir_exist(dirpath))
1843 make_dir_hier(dirpath);
1849 static gint imap_rename_folder(Folder *folder, FolderItem *item,
1854 gchar *real_oldpath;
1855 gchar *real_newpath;
1857 gchar *old_cache_dir;
1858 gchar *new_cache_dir;
1859 IMAPSession *session;
1862 gint exists, recent, unseen;
1863 guint32 uid_validity;
1865 g_return_val_if_fail(folder != NULL, -1);
1866 g_return_val_if_fail(item != NULL, -1);
1867 g_return_val_if_fail(item->path != NULL, -1);
1868 g_return_val_if_fail(name != NULL, -1);
1870 session = imap_session_get(folder);
1876 if (strchr(name, imap_get_path_separator(IMAP_FOLDER(folder), item->path)) != NULL) {
1877 g_warning(_("New folder name must not contain the namespace "
1883 real_oldpath = imap_get_real_path(IMAP_FOLDER(folder), item->path);
1885 g_free(session->mbox);
1886 session->mbox = NULL;
1887 ok = imap_cmd_examine(session, "INBOX",
1888 &exists, &recent, &unseen, &uid_validity, FALSE);
1889 if (ok != IMAP_SUCCESS) {
1890 g_free(real_oldpath);
1895 separator = imap_get_path_separator(IMAP_FOLDER(folder), item->path);
1896 if (strchr(item->path, G_DIR_SEPARATOR)) {
1897 dirpath = g_path_get_dirname(item->path);
1898 newpath = g_strconcat(dirpath, G_DIR_SEPARATOR_S, name, NULL);
1901 newpath = g_strdup(name);
1903 real_newpath = imap_utf8_to_modified_utf7(newpath);
1904 imap_path_separator_subst(real_newpath, separator);
1906 ok = imap_cmd_rename(session, real_oldpath, real_newpath);
1907 if (ok != IMAP_SUCCESS) {
1908 log_warning(_("can't rename mailbox: %s to %s\n"),
1909 real_oldpath, real_newpath);
1910 g_free(real_oldpath);
1912 g_free(real_newpath);
1918 item->name = g_strdup(name);
1920 old_cache_dir = folder_item_get_path(item);
1922 paths[0] = g_strdup(item->path);
1924 g_node_traverse(item->node, G_PRE_ORDER, G_TRAVERSE_ALL, -1,
1925 imap_rename_folder_func, paths);
1927 if (is_dir_exist(old_cache_dir)) {
1928 new_cache_dir = folder_item_get_path(item);
1929 if (rename(old_cache_dir, new_cache_dir) < 0) {
1930 FILE_OP_ERROR(old_cache_dir, "rename");
1932 g_free(new_cache_dir);
1935 g_free(old_cache_dir);
1938 g_free(real_oldpath);
1939 g_free(real_newpath);
1944 static gint imap_remove_folder_real(Folder *folder, FolderItem *item)
1947 IMAPSession *session;
1951 g_return_val_if_fail(folder != NULL, -1);
1952 g_return_val_if_fail(item != NULL, -1);
1953 g_return_val_if_fail(item->path != NULL, -1);
1955 session = imap_session_get(folder);
1960 path = imap_get_real_path(IMAP_FOLDER(folder), item->path);
1962 ok = imap_cmd_delete(session, path);
1963 if (ok != IMAP_SUCCESS) {
1964 gchar *tmp = g_strdup_printf("%s%c", path,
1965 imap_get_path_separator(IMAP_FOLDER(folder), path));
1968 ok = imap_cmd_delete(session, path);
1971 if (ok != IMAP_SUCCESS) {
1972 log_warning(_("can't delete mailbox\n"));
1979 cache_dir = folder_item_get_path(item);
1980 if (is_dir_exist(cache_dir) && remove_dir_recursive(cache_dir) < 0)
1981 g_warning("can't remove directory '%s'\n", cache_dir);
1983 folder_item_remove(item);
1988 static gint imap_remove_folder(Folder *folder, FolderItem *item)
1992 g_return_val_if_fail(item != NULL, -1);
1993 g_return_val_if_fail(item->folder != NULL, -1);
1994 g_return_val_if_fail(item->node != NULL, -1);
1996 node = item->node->children;
1997 while (node != NULL) {
1999 if (imap_remove_folder(folder, FOLDER_ITEM(node->data)) < 0)
2003 debug_print("IMAP removing %s\n", item->path);
2005 if (imap_remove_all_msg(folder, item) < 0)
2007 return imap_remove_folder_real(folder, item);
2010 typedef struct _uncached_data {
2011 IMAPSession *session;
2013 MsgNumberList *numlist;
2019 static void *imap_get_uncached_messages_thread(void *data)
2021 uncached_data *stuff = (uncached_data *)data;
2022 IMAPSession *session = stuff->session;
2023 FolderItem *item = stuff->item;
2024 MsgNumberList *numlist = stuff->numlist;
2026 GSList *newlist = NULL;
2027 GSList *llast = NULL;
2028 GSList *seq_list, *cur;
2030 debug_print("uncached_messages\n");
2032 if (session == NULL || item == NULL || item->folder == NULL
2033 || FOLDER_CLASS(item->folder) != &imap_class) {
2038 seq_list = imap_get_lep_set_from_numlist(numlist);
2039 debug_print("get msgs info\n");
2040 for (cur = seq_list; cur != NULL; cur = g_slist_next(cur)) {
2041 struct mailimap_set * imapset;
2047 imapset = cur->data;
2049 r = imap_threaded_fetch_env(session->folder,
2050 imapset, &env_list);
2051 if (r != MAILIMAP_NO_ERROR)
2054 session_set_access_time(SESSION(session));
2057 for(i = 0 ; i < carray_count(env_list) ; i ++) {
2058 struct imap_fetch_env_info * info;
2061 info = carray_get(env_list, i);
2062 msginfo = imap_envelope_from_lep(info, item);
2063 if (msginfo == NULL)
2065 msginfo->folder = item;
2067 llast = newlist = g_slist_append(newlist, msginfo);
2069 llast = g_slist_append(llast, msginfo);
2070 llast = llast->next;
2075 imap_fetch_env_free(env_list);
2078 for (cur = seq_list; cur != NULL; cur = g_slist_next(cur)) {
2079 struct mailimap_set * imapset;
2081 imapset = cur->data;
2082 mailimap_set_free(imapset);
2085 session_set_access_time(SESSION(session));
2090 #define MAX_MSG_NUM 50
2092 static GSList *imap_get_uncached_messages(IMAPSession *session,
2094 MsgNumberList *numlist)
2096 GSList *result = NULL;
2098 uncached_data *data = g_new0(uncached_data, 1);
2103 data->total = g_slist_length(numlist);
2104 debug_print("messages list : %i\n", data->total);
2106 while (cur != NULL) {
2107 GSList * partial_result;
2115 while (count < MAX_MSG_NUM) {
2120 if (newlist == NULL)
2121 llast = newlist = g_slist_append(newlist, p);
2123 llast = g_slist_append(llast, p);
2124 llast = llast->next;
2134 data->session = session;
2136 data->numlist = newlist;
2139 if (prefs_common.work_offline &&
2140 !inc_offline_should_override(
2141 _("Sylpheed-Claws needs network access in order "
2142 "to access the IMAP server."))) {
2148 (GSList *)imap_get_uncached_messages_thread(data);
2150 statusbar_progress_all(data->cur,data->total, 1);
2152 g_slist_free(newlist);
2154 result = g_slist_concat(result, partial_result);
2158 statusbar_progress_all(0,0,0);
2159 statusbar_pop_all();
2164 static void imap_delete_all_cached_messages(FolderItem *item)
2168 g_return_if_fail(item != NULL);
2169 g_return_if_fail(item->folder != NULL);
2170 g_return_if_fail(FOLDER_CLASS(item->folder) == &imap_class);
2172 debug_print("Deleting all cached messages...\n");
2174 dir = folder_item_get_path(item);
2175 if (is_dir_exist(dir))
2176 remove_all_numbered_files(dir);
2179 debug_print("done.\n");
2182 static IMAPNameSpace *imap_find_namespace_from_list(GList *ns_list,
2185 IMAPNameSpace *namespace = NULL;
2186 gchar *tmp_path, *name;
2188 if (!path) path = "";
2190 for (; ns_list != NULL; ns_list = ns_list->next) {
2191 IMAPNameSpace *tmp_ns = ns_list->data;
2193 Xstrcat_a(tmp_path, path, "/", return namespace);
2194 Xstrdup_a(name, tmp_ns->name, return namespace);
2195 if (tmp_ns->separator && tmp_ns->separator != '/') {
2196 subst_char(tmp_path, tmp_ns->separator, '/');
2197 subst_char(name, tmp_ns->separator, '/');
2199 if (strncmp(tmp_path, name, strlen(name)) == 0)
2206 static IMAPNameSpace *imap_find_namespace(IMAPFolder *folder,
2209 IMAPNameSpace *namespace;
2211 g_return_val_if_fail(folder != NULL, NULL);
2213 namespace = imap_find_namespace_from_list(folder->ns_personal, path);
2214 if (namespace) return namespace;
2215 namespace = imap_find_namespace_from_list(folder->ns_others, path);
2216 if (namespace) return namespace;
2217 namespace = imap_find_namespace_from_list(folder->ns_shared, path);
2218 if (namespace) return namespace;
2223 gchar imap_get_path_separator_for_item(FolderItem *item)
2225 Folder *folder = NULL;
2226 IMAPFolder *imap_folder = NULL;
2229 folder = item->folder;
2234 imap_folder = IMAP_FOLDER(folder);
2239 return imap_get_path_separator(imap_folder, item->path);
2242 static gchar imap_get_path_separator(IMAPFolder *folder, const gchar *path)
2244 IMAPNameSpace *namespace;
2245 gchar separator = '/';
2246 IMAPSession *session = imap_session_get(FOLDER(folder));
2247 g_return_val_if_fail(session != NULL, '/');
2249 if (folder->last_seen_separator == 0) {
2251 int r = imap_threaded_list((Folder *)folder, "", "", &lep_list);
2252 if (r != MAILIMAP_NO_ERROR) {
2253 log_warning(_("LIST failed\n"));
2257 if (clist_count(lep_list) > 0) {
2258 clistiter * iter = clist_begin(lep_list);
2259 struct mailimap_mailbox_list * mb;
2260 mb = clist_content(iter);
2262 folder->last_seen_separator = mb->mb_delimiter;
2263 debug_print("got separator: %c\n", folder->last_seen_separator);
2265 mailimap_list_result_free(lep_list);
2268 if (folder->last_seen_separator != 0) {
2269 debug_print("using separator: %c\n", folder->last_seen_separator);
2270 return folder->last_seen_separator;
2273 namespace = imap_find_namespace(folder, path);
2274 if (namespace && namespace->separator)
2275 separator = namespace->separator;
2280 static gchar *imap_get_real_path(IMAPFolder *folder, const gchar *path)
2285 g_return_val_if_fail(folder != NULL, NULL);
2286 g_return_val_if_fail(path != NULL, NULL);
2288 real_path = imap_utf8_to_modified_utf7(path);
2289 separator = imap_get_path_separator(folder, path);
2290 imap_path_separator_subst(real_path, separator);
2295 static gint imap_set_message_flags(IMAPSession *session,
2296 MsgNumberList *numlist,
2304 seq_list = imap_get_lep_set_from_numlist(numlist);
2306 for(cur = seq_list ; cur != NULL ; cur = g_slist_next(cur)) {
2307 struct mailimap_set * imapset;
2309 imapset = cur->data;
2311 ok = imap_cmd_store(session, imapset,
2315 imap_lep_set_free(seq_list);
2317 return IMAP_SUCCESS;
2320 typedef struct _select_data {
2321 IMAPSession *session;
2326 guint32 *uid_validity;
2330 static gint imap_select(IMAPSession *session, IMAPFolder *folder,
2332 gint *exists, gint *recent, gint *unseen,
2333 guint32 *uid_validity, gboolean block)
2337 gint exists_, recent_, unseen_;
2338 guint32 uid_validity_;
2340 if (!exists && !recent && !unseen && !uid_validity) {
2341 if (session->mbox && strcmp(session->mbox, path) == 0)
2342 return IMAP_SUCCESS;
2351 uid_validity = &uid_validity_;
2353 g_free(session->mbox);
2354 session->mbox = NULL;
2356 real_path = imap_get_real_path(folder, path);
2358 ok = imap_cmd_select(session, real_path,
2359 exists, recent, unseen, uid_validity, block);
2360 if (ok != IMAP_SUCCESS)
2361 log_warning(_("can't select folder: %s\n"), real_path);
2363 session->mbox = g_strdup(path);
2364 session->folder_content_changed = FALSE;
2371 static gint imap_status(IMAPSession *session, IMAPFolder *folder,
2372 const gchar *path, IMAPFolderItem *item,
2374 guint32 *uid_next, guint32 *uid_validity,
2375 gint *unseen, gboolean block)
2379 struct mailimap_mailbox_data_status * data_status;
2384 real_path = imap_get_real_path(folder, path);
2398 r = imap_threaded_status(FOLDER(folder), real_path,
2399 &data_status, mask);
2402 if (r != MAILIMAP_NO_ERROR) {
2403 debug_print("status err %d\n", r);
2407 if (data_status->st_info_list == NULL) {
2408 mailimap_mailbox_data_status_free(data_status);
2409 debug_print("status->st_info_list == NULL\n");
2414 for(iter = clist_begin(data_status->st_info_list) ; iter != NULL ;
2415 iter = clist_next(iter)) {
2416 struct mailimap_status_info * info;
2418 info = clist_content(iter);
2419 switch (info->st_att) {
2420 case MAILIMAP_STATUS_ATT_MESSAGES:
2421 * messages = info->st_value;
2422 got_values |= 1 << 0;
2425 case MAILIMAP_STATUS_ATT_UIDNEXT:
2426 * uid_next = info->st_value;
2427 got_values |= 1 << 2;
2430 case MAILIMAP_STATUS_ATT_UIDVALIDITY:
2431 * uid_validity = info->st_value;
2432 got_values |= 1 << 3;
2435 case MAILIMAP_STATUS_ATT_UNSEEN:
2436 * unseen = info->st_value;
2437 got_values |= 1 << 4;
2441 mailimap_mailbox_data_status_free(data_status);
2443 if (got_values != mask) {
2444 debug_print("status: incomplete values received (%d)\n", got_values);
2447 return IMAP_SUCCESS;
2450 static void imap_free_capabilities(IMAPSession *session)
2452 slist_free_strings(session->capability);
2453 g_slist_free(session->capability);
2454 session->capability = NULL;
2457 /* low-level IMAP4rev1 commands */
2459 static gint imap_cmd_login(IMAPSession *session,
2460 const gchar *user, const gchar *pass,
2466 log_print("IMAP4> Logging %s to %s using %s\n",
2468 SESSION(session)->server,
2470 r = imap_threaded_login(session->folder, user, pass, type);
2471 if (r != MAILIMAP_NO_ERROR) {
2472 log_print("IMAP4< Error logging in to %s\n",
2473 SESSION(session)->server);
2476 log_print("IMAP4< Login to %s successful\n",
2477 SESSION(session)->server);
2483 static gint imap_cmd_logout(IMAPSession *session)
2485 imap_threaded_disconnect(session->folder);
2487 return IMAP_SUCCESS;
2490 static gint imap_cmd_noop(IMAPSession *session)
2493 unsigned int exists;
2495 r = imap_threaded_noop(session->folder, &exists);
2496 if (r != MAILIMAP_NO_ERROR) {
2497 debug_print("noop err %d\n", r);
2500 session->exists = exists;
2501 session_set_access_time(SESSION(session));
2503 return IMAP_SUCCESS;
2507 static gint imap_cmd_starttls(IMAPSession *session)
2511 r = imap_threaded_starttls(session->folder,
2512 SESSION(session)->server, SESSION(session)->port);
2513 if (r != MAILIMAP_NO_ERROR) {
2514 debug_print("starttls err %d\n", r);
2517 return IMAP_SUCCESS;
2521 static gint imap_cmd_select(IMAPSession *session, const gchar *folder,
2522 gint *exists, gint *recent, gint *unseen,
2523 guint32 *uid_validity, gboolean block)
2527 r = imap_threaded_select(session->folder, folder,
2528 exists, recent, unseen, uid_validity);
2529 if (r != MAILIMAP_NO_ERROR) {
2530 debug_print("select err %d\n", r);
2533 return IMAP_SUCCESS;
2536 static gint imap_cmd_examine(IMAPSession *session, const gchar *folder,
2537 gint *exists, gint *recent, gint *unseen,
2538 guint32 *uid_validity, gboolean block)
2542 r = imap_threaded_examine(session->folder, folder,
2543 exists, recent, unseen, uid_validity);
2544 if (r != MAILIMAP_NO_ERROR) {
2545 debug_print("examine err %d\n", r);
2549 return IMAP_SUCCESS;
2552 static gint imap_cmd_create(IMAPSession *session, const gchar *folder)
2556 r = imap_threaded_create(session->folder, folder);
2557 if (r != MAILIMAP_NO_ERROR) {
2562 return IMAP_SUCCESS;
2565 static gint imap_cmd_rename(IMAPSession *session, const gchar *old_folder,
2566 const gchar *new_folder)
2570 r = imap_threaded_rename(session->folder, old_folder,
2572 if (r != MAILIMAP_NO_ERROR) {
2577 return IMAP_SUCCESS;
2580 static gint imap_cmd_delete(IMAPSession *session, const gchar *folder)
2585 r = imap_threaded_delete(session->folder, folder);
2586 if (r != MAILIMAP_NO_ERROR) {
2591 return IMAP_SUCCESS;
2594 typedef struct _fetch_data {
2595 IMAPSession *session;
2597 const gchar *filename;
2603 static void *imap_cmd_fetch_thread(void *data)
2605 fetch_data *stuff = (fetch_data *)data;
2606 IMAPSession *session = stuff->session;
2607 guint32 uid = stuff->uid;
2608 const gchar *filename = stuff->filename;
2612 r = imap_threaded_fetch_content(session->folder,
2616 r = imap_threaded_fetch_content(session->folder,
2619 if (r != MAILIMAP_NO_ERROR) {
2620 debug_print("fetch err %d\n", r);
2621 return GINT_TO_POINTER(IMAP_ERROR);
2623 return GINT_TO_POINTER(IMAP_SUCCESS);
2626 static gint imap_cmd_fetch(IMAPSession *session, guint32 uid,
2627 const gchar *filename, gboolean headers,
2630 fetch_data *data = g_new0(fetch_data, 1);
2633 data->session = session;
2635 data->filename = filename;
2636 data->headers = headers;
2639 if (prefs_common.work_offline &&
2640 !inc_offline_should_override(
2641 _("Sylpheed-Claws needs network access in order "
2642 "to access the IMAP server."))) {
2646 statusbar_print_all(_("Fetching message..."));
2647 result = GPOINTER_TO_INT(imap_cmd_fetch_thread(data));
2648 statusbar_pop_all();
2654 static gint imap_cmd_append(IMAPSession *session, const gchar *destfolder,
2655 const gchar *file, IMAPFlags flags,
2658 struct mailimap_flag_list * flag_list;
2661 g_return_val_if_fail(file != NULL, IMAP_ERROR);
2663 flag_list = imap_flag_to_lep(flags);
2664 r = imap_threaded_append(session->folder, destfolder,
2665 file, flag_list, new_uid);
2666 mailimap_flag_list_free(flag_list);
2668 if (r != MAILIMAP_NO_ERROR) {
2669 debug_print("append err %d\n", r);
2672 return IMAP_SUCCESS;
2675 static gint imap_cmd_copy(IMAPSession *session, struct mailimap_set * set,
2676 const gchar *destfolder, GRelation *uid_mapping)
2680 g_return_val_if_fail(session != NULL, IMAP_ERROR);
2681 g_return_val_if_fail(set != NULL, IMAP_ERROR);
2682 g_return_val_if_fail(destfolder != NULL, IMAP_ERROR);
2684 r = imap_threaded_copy(session->folder, set, destfolder);
2685 if (r != MAILIMAP_NO_ERROR) {
2690 return IMAP_SUCCESS;
2693 static gint imap_cmd_store(IMAPSession *session, struct mailimap_set * set,
2694 IMAPFlags flags, int do_add)
2697 struct mailimap_flag_list * flag_list;
2698 struct mailimap_store_att_flags * store_att_flags;
2700 flag_list = imap_flag_to_lep(flags);
2704 mailimap_store_att_flags_new_add_flags_silent(flag_list);
2707 mailimap_store_att_flags_new_remove_flags_silent(flag_list);
2709 r = imap_threaded_store(session->folder, set, store_att_flags);
2710 mailimap_store_att_flags_free(store_att_flags);
2711 if (r != MAILIMAP_NO_ERROR) {
2716 return IMAP_SUCCESS;
2719 static gint imap_cmd_expunge(IMAPSession *session)
2723 if (prefs_common.work_offline &&
2724 !inc_offline_should_override(
2725 _("Sylpheed-Claws needs network access in order "
2726 "to access the IMAP server."))) {
2730 r = imap_threaded_expunge(session->folder);
2731 if (r != MAILIMAP_NO_ERROR) {
2736 return IMAP_SUCCESS;
2739 static void imap_path_separator_subst(gchar *str, gchar separator)
2742 gboolean in_escape = FALSE;
2744 if (!separator || separator == '/') return;
2746 for (p = str; *p != '\0'; p++) {
2747 if (*p == '/' && !in_escape)
2749 else if (*p == '&' && *(p + 1) != '-' && !in_escape)
2751 else if (*p == '-' && in_escape)
2756 static gchar *imap_modified_utf7_to_utf8(const gchar *mutf7_str)
2758 static iconv_t cd = (iconv_t)-1;
2759 static gboolean iconv_ok = TRUE;
2762 size_t norm_utf7_len;
2764 gchar *to_str, *to_p;
2766 gboolean in_escape = FALSE;
2768 if (!iconv_ok) return g_strdup(mutf7_str);
2770 if (cd == (iconv_t)-1) {
2771 cd = iconv_open(CS_INTERNAL, CS_UTF_7);
2772 if (cd == (iconv_t)-1) {
2773 g_warning("iconv cannot convert UTF-7 to %s\n",
2776 return g_strdup(mutf7_str);
2780 /* modified UTF-7 to normal UTF-7 conversion */
2781 norm_utf7 = g_string_new(NULL);
2783 for (p = mutf7_str; *p != '\0'; p++) {
2784 /* replace: '&' -> '+',
2786 escaped ',' -> '/' */
2787 if (!in_escape && *p == '&') {
2788 if (*(p + 1) != '-') {
2789 g_string_append_c(norm_utf7, '+');
2792 g_string_append_c(norm_utf7, '&');
2795 } else if (in_escape && *p == ',') {
2796 g_string_append_c(norm_utf7, '/');
2797 } else if (in_escape && *p == '-') {
2798 g_string_append_c(norm_utf7, '-');
2801 g_string_append_c(norm_utf7, *p);
2805 norm_utf7_p = norm_utf7->str;
2806 norm_utf7_len = norm_utf7->len;
2807 to_len = strlen(mutf7_str) * 5;
2808 to_p = to_str = g_malloc(to_len + 1);
2810 if (iconv(cd, (ICONV_CONST gchar **)&norm_utf7_p, &norm_utf7_len,
2811 &to_p, &to_len) == -1) {
2812 g_warning(_("iconv cannot convert UTF-7 to %s\n"),
2813 conv_get_locale_charset_str());
2814 g_string_free(norm_utf7, TRUE);
2816 return g_strdup(mutf7_str);
2819 /* second iconv() call for flushing */
2820 iconv(cd, NULL, NULL, &to_p, &to_len);
2821 g_string_free(norm_utf7, TRUE);
2827 static gchar *imap_utf8_to_modified_utf7(const gchar *from)
2829 static iconv_t cd = (iconv_t)-1;
2830 static gboolean iconv_ok = TRUE;
2831 gchar *norm_utf7, *norm_utf7_p;
2832 size_t from_len, norm_utf7_len;
2834 gchar *from_tmp, *to, *p;
2835 gboolean in_escape = FALSE;
2837 if (!iconv_ok) return g_strdup(from);
2839 if (cd == (iconv_t)-1) {
2840 cd = iconv_open(CS_UTF_7, CS_INTERNAL);
2841 if (cd == (iconv_t)-1) {
2842 g_warning(_("iconv cannot convert %s to UTF-7\n"),
2845 return g_strdup(from);
2849 /* UTF-8 to normal UTF-7 conversion */
2850 Xstrdup_a(from_tmp, from, return g_strdup(from));
2851 from_len = strlen(from);
2852 norm_utf7_len = from_len * 5;
2853 Xalloca(norm_utf7, norm_utf7_len + 1, return g_strdup(from));
2854 norm_utf7_p = norm_utf7;
2856 #define IS_PRINT(ch) (isprint(ch) && IS_ASCII(ch))
2858 while (from_len > 0) {
2859 if (*from_tmp == '+') {
2860 *norm_utf7_p++ = '+';
2861 *norm_utf7_p++ = '-';
2865 } else if (IS_PRINT(*(guchar *)from_tmp)) {
2866 /* printable ascii char */
2867 *norm_utf7_p = *from_tmp;
2873 size_t conv_len = 0;
2875 /* unprintable char: convert to UTF-7 */
2877 while (!IS_PRINT(*(guchar *)p) && conv_len < from_len) {
2878 conv_len += g_utf8_skip[*(guchar *)p];
2879 p += g_utf8_skip[*(guchar *)p];
2882 from_len -= conv_len;
2883 if (iconv(cd, (ICONV_CONST gchar **)&from_tmp,
2885 &norm_utf7_p, &norm_utf7_len) == -1) {
2886 g_warning(_("iconv cannot convert UTF-8 to UTF-7\n"));
2887 return g_strdup(from);
2890 /* second iconv() call for flushing */
2891 iconv(cd, NULL, NULL, &norm_utf7_p, &norm_utf7_len);
2897 *norm_utf7_p = '\0';
2898 to_str = g_string_new(NULL);
2899 for (p = norm_utf7; p < norm_utf7_p; p++) {
2900 /* replace: '&' -> "&-",
2903 BASE64 '/' -> ',' */
2904 if (!in_escape && *p == '&') {
2905 g_string_append(to_str, "&-");
2906 } else if (!in_escape && *p == '+') {
2907 if (*(p + 1) == '-') {
2908 g_string_append_c(to_str, '+');
2911 g_string_append_c(to_str, '&');
2914 } else if (in_escape && *p == '/') {
2915 g_string_append_c(to_str, ',');
2916 } else if (in_escape && *p == '-') {
2917 g_string_append_c(to_str, '-');
2920 g_string_append_c(to_str, *p);
2926 g_string_append_c(to_str, '-');
2930 g_string_free(to_str, FALSE);
2935 static gboolean imap_rename_folder_func(GNode *node, gpointer data)
2937 FolderItem *item = node->data;
2938 gchar **paths = data;
2939 const gchar *oldpath = paths[0];
2940 const gchar *newpath = paths[1];
2942 gchar *new_itempath;
2945 oldpathlen = strlen(oldpath);
2946 if (strncmp(oldpath, item->path, oldpathlen) != 0) {
2947 g_warning("path doesn't match: %s, %s\n", oldpath, item->path);
2951 base = item->path + oldpathlen;
2952 while (*base == G_DIR_SEPARATOR) base++;
2954 new_itempath = g_strdup(newpath);
2956 new_itempath = g_strconcat(newpath, G_DIR_SEPARATOR_S, base,
2959 item->path = new_itempath;
2964 typedef struct _get_list_uid_data {
2966 IMAPSession *session;
2967 IMAPFolderItem *item;
2968 GSList **msgnum_list;
2970 } get_list_uid_data;
2972 static void *get_list_of_uids_thread(void *data)
2974 get_list_uid_data *stuff = (get_list_uid_data *)data;
2975 Folder *folder = stuff->folder;
2976 IMAPFolderItem *item = stuff->item;
2977 GSList **msgnum_list = stuff->msgnum_list;
2978 gint ok, nummsgs = 0, lastuid_old;
2979 IMAPSession *session;
2980 GSList *uidlist, *elem;
2981 struct mailimap_set * set;
2982 clist * lep_uidlist;
2985 session = stuff->session;
2986 if (session == NULL) {
2988 return GINT_TO_POINTER(-1);
2990 /* no session locking here, it's already locked by caller */
2991 ok = imap_select(session, IMAP_FOLDER(folder), item->item.path,
2992 NULL, NULL, NULL, NULL, TRUE);
2993 if (ok != IMAP_SUCCESS) {
2995 return GINT_TO_POINTER(-1);
3000 set = mailimap_set_new_interval(item->lastuid + 1, 0);
3002 r = imap_threaded_search(folder, IMAP_SEARCH_TYPE_SIMPLE, set,
3004 mailimap_set_free(set);
3006 if (r == MAILIMAP_NO_ERROR) {
3007 GSList * fetchuid_list;
3010 imap_uid_list_from_lep(lep_uidlist);
3011 mailimap_search_result_free(lep_uidlist);
3013 uidlist = g_slist_concat(fetchuid_list, uidlist);
3016 GSList * fetchuid_list;
3017 carray * lep_uidtab;
3019 r = imap_threaded_fetch_uid(folder, item->lastuid + 1,
3021 if (r == MAILIMAP_NO_ERROR) {
3023 imap_uid_list_from_lep_tab(lep_uidtab);
3024 imap_fetch_uid_list_free(lep_uidtab);
3025 uidlist = g_slist_concat(fetchuid_list, uidlist);
3029 lastuid_old = item->lastuid;
3030 *msgnum_list = g_slist_copy(item->uid_list);
3031 nummsgs = g_slist_length(*msgnum_list);
3032 debug_print("Got %d uids from cache\n", g_slist_length(item->uid_list));
3034 for (elem = uidlist; elem != NULL; elem = g_slist_next(elem)) {
3037 msgnum = GPOINTER_TO_INT(elem->data);
3038 if (msgnum > lastuid_old) {
3039 *msgnum_list = g_slist_prepend(*msgnum_list, GINT_TO_POINTER(msgnum));
3040 item->uid_list = g_slist_prepend(item->uid_list, GINT_TO_POINTER(msgnum));
3043 if(msgnum > item->lastuid)
3044 item->lastuid = msgnum;
3047 g_slist_free(uidlist);
3049 return GINT_TO_POINTER(nummsgs);
3052 static gint get_list_of_uids(IMAPSession *session, Folder *folder, IMAPFolderItem *item, GSList **msgnum_list)
3055 get_list_uid_data *data = g_new0(get_list_uid_data, 1);
3057 data->folder = folder;
3059 data->msgnum_list = msgnum_list;
3060 data->session = session;
3061 if (prefs_common.work_offline &&
3062 !inc_offline_should_override(
3063 _("Sylpheed-Claws needs network access in order "
3064 "to access the IMAP server."))) {
3069 result = GPOINTER_TO_INT(get_list_of_uids_thread(data));
3075 gint imap_get_num_list(Folder *folder, FolderItem *_item, GSList **msgnum_list, gboolean *old_uids_valid)
3077 IMAPFolderItem *item = (IMAPFolderItem *)_item;
3078 IMAPSession *session;
3079 gint ok, nummsgs = 0, exists, uid_val, uid_next = 0;
3080 GSList *uidlist = NULL;
3082 gboolean selected_folder;
3084 debug_print("get_num_list\n");
3086 g_return_val_if_fail(folder != NULL, -1);
3087 g_return_val_if_fail(item != NULL, -1);
3088 g_return_val_if_fail(item->item.path != NULL, -1);
3089 g_return_val_if_fail(FOLDER_CLASS(folder) == &imap_class, -1);
3090 g_return_val_if_fail(folder->account != NULL, -1);
3092 session = imap_session_get(folder);
3093 g_return_val_if_fail(session != NULL, -1);
3096 if (FOLDER_ITEM(item)->path)
3097 statusbar_print_all(_("Scanning folder %s%c%s ..."),
3098 FOLDER_ITEM(item)->folder->name,
3100 FOLDER_ITEM(item)->path);
3102 statusbar_print_all(_("Scanning folder %s ..."),
3103 FOLDER_ITEM(item)->folder->name);
3105 selected_folder = (session->mbox != NULL) &&
3106 (!strcmp(session->mbox, item->item.path));
3107 if (selected_folder && time(NULL) - item->use_cache < 2) {
3108 ok = imap_cmd_noop(session);
3109 if (ok != IMAP_SUCCESS) {
3110 debug_print("disconnected!\n");
3111 session = imap_reconnect_if_possible(folder, session);
3112 if (session == NULL) {
3113 statusbar_pop_all();
3118 exists = session->exists;
3120 uid_next = item->c_uid_next;
3121 uid_val = item->c_uid_validity;
3122 *old_uids_valid = TRUE;
3124 if (item->use_cache && time(NULL) - item->use_cache < 2) {
3125 exists = item->c_messages;
3126 uid_next = item->c_uid_next;
3127 uid_val = item->c_uid_validity;
3129 debug_print("using cache %d %d %d\n", exists, uid_next, uid_val);
3131 ok = imap_status(session, IMAP_FOLDER(folder), item->item.path, item,
3132 &exists, &uid_next, &uid_val, NULL, FALSE);
3134 item->item.last_num = uid_next - 1;
3136 item->use_cache = (time_t)0;
3137 if (ok != IMAP_SUCCESS) {
3138 statusbar_pop_all();
3142 if(item->item.mtime == uid_val)
3143 *old_uids_valid = TRUE;
3145 *old_uids_valid = FALSE;
3147 debug_print("Freeing imap uid cache\n");
3149 g_slist_free(item->uid_list);
3150 item->uid_list = NULL;
3152 item->item.mtime = uid_val;
3154 imap_delete_all_cached_messages((FolderItem *)item);
3158 /* If old uid_next matches new uid_next we can be sure no message
3159 was added to the folder */
3160 debug_print("uid_next is %d and item->uid_next %d \n",
3161 uid_next, item->uid_next);
3162 if (uid_next == item->uid_next) {
3163 nummsgs = g_slist_length(item->uid_list);
3165 /* If number of messages is still the same we
3166 know our caches message numbers are still valid,
3167 otherwise if the number of messages has decrease
3168 we discard our cache to start a new scan to find
3169 out which numbers have been removed */
3170 if (exists == nummsgs) {
3171 debug_print("exists == nummsgs\n");
3172 *msgnum_list = g_slist_copy(item->uid_list);
3173 statusbar_pop_all();
3176 } else if (exists < nummsgs) {
3177 debug_print("Freeing imap uid cache");
3179 g_slist_free(item->uid_list);
3180 item->uid_list = NULL;
3185 *msgnum_list = NULL;
3186 statusbar_pop_all();
3191 nummsgs = get_list_of_uids(session, folder, item, &uidlist);
3194 statusbar_pop_all();
3199 if (nummsgs != exists) {
3200 /* Cache contains more messages then folder, we have cached
3201 an old UID of a message that was removed and new messages
3202 have been added too, otherwise the uid_next check would
3204 debug_print("Freeing imap uid cache");
3206 g_slist_free(item->uid_list);
3207 item->uid_list = NULL;
3209 g_slist_free(*msgnum_list);
3211 nummsgs = get_list_of_uids(session, folder, item, &uidlist);
3214 *msgnum_list = uidlist;
3216 dir = folder_item_get_path((FolderItem *)item);
3217 debug_print("removing old messages from %s\n", dir);
3218 remove_numbered_files_not_in_list(dir, *msgnum_list);
3221 item->uid_next = uid_next;
3223 debug_print("get_num_list - ok - %i\n", nummsgs);
3224 statusbar_pop_all();
3229 static MsgInfo *imap_parse_msg(const gchar *file, FolderItem *item)
3234 flags.perm_flags = MSG_NEW|MSG_UNREAD;
3235 flags.tmp_flags = 0;
3237 g_return_val_if_fail(item != NULL, NULL);
3238 g_return_val_if_fail(file != NULL, NULL);
3240 if (folder_has_parent_of_type(item, F_QUEUE)) {
3241 MSG_SET_TMP_FLAGS(flags, MSG_QUEUED);
3242 } else if (folder_has_parent_of_type(item, F_DRAFT)) {
3243 MSG_SET_TMP_FLAGS(flags, MSG_DRAFT);
3246 msginfo = procheader_parse_file(file, flags, FALSE, FALSE);
3247 if (!msginfo) return NULL;
3249 msginfo->plaintext_file = g_strdup(file);
3250 msginfo->folder = item;
3255 GSList *imap_get_msginfos(Folder *folder, FolderItem *item,
3256 GSList *msgnum_list)
3258 IMAPSession *session;
3259 MsgInfoList *ret = NULL;
3262 debug_print("get_msginfos\n");
3264 g_return_val_if_fail(folder != NULL, NULL);
3265 g_return_val_if_fail(item != NULL, NULL);
3266 g_return_val_if_fail(msgnum_list != NULL, NULL);
3268 session = imap_session_get(folder);
3269 g_return_val_if_fail(session != NULL, NULL);
3271 debug_print("IMAP getting msginfos\n");
3272 ok = imap_select(session, IMAP_FOLDER(folder), item->path,
3273 NULL, NULL, NULL, NULL, FALSE);
3274 if (ok != IMAP_SUCCESS) {
3278 if (!(folder_has_parent_of_type(item, F_DRAFT) ||
3279 folder_has_parent_of_type(item, F_QUEUE))) {
3280 ret = g_slist_concat(ret,
3281 imap_get_uncached_messages(session, item,
3284 MsgNumberList *sorted_list, *elem, *llast = NULL;
3285 gint startnum, lastnum;
3287 sorted_list = g_slist_sort(g_slist_copy(msgnum_list), g_int_compare);
3289 startnum = lastnum = GPOINTER_TO_INT(sorted_list->data);
3291 llast = g_slist_last(ret);
3292 for (elem = sorted_list;; elem = g_slist_next(elem)) {
3296 num = GPOINTER_TO_INT(elem->data);
3298 if (num > lastnum + 1 || elem == NULL) {
3300 for (i = startnum; i <= lastnum; ++i) {
3303 file = imap_fetch_msg(folder, item, i);
3305 MsgInfo *msginfo = imap_parse_msg(file, item);
3306 if (msginfo != NULL) {
3307 msginfo->msgnum = i;
3309 llast = ret = g_slist_append(ret, msginfo);
3311 llast = g_slist_append(llast, msginfo);
3312 llast = llast->next;
3317 session_set_access_time(SESSION(session));
3328 g_slist_free(sorted_list);
3334 MsgInfo *imap_get_msginfo(Folder *folder, FolderItem *item, gint uid)
3336 MsgInfo *msginfo = NULL;
3337 MsgInfoList *msginfolist;
3338 MsgNumberList numlist;
3340 numlist.next = NULL;
3341 numlist.data = GINT_TO_POINTER(uid);
3343 msginfolist = imap_get_msginfos(folder, item, &numlist);
3344 if (msginfolist != NULL) {
3345 msginfo = msginfolist->data;
3346 g_slist_free(msginfolist);
3352 gboolean imap_scan_required(Folder *folder, FolderItem *_item)
3354 IMAPSession *session;
3355 IMAPFolderItem *item = (IMAPFolderItem *)_item;
3356 gint ok, exists = 0, unseen = 0;
3357 guint32 uid_next, uid_val;
3358 gboolean selected_folder;
3360 g_return_val_if_fail(folder != NULL, FALSE);
3361 g_return_val_if_fail(item != NULL, FALSE);
3362 g_return_val_if_fail(item->item.folder != NULL, FALSE);
3363 g_return_val_if_fail(FOLDER_CLASS(item->item.folder) == &imap_class, FALSE);
3365 if (item->item.path == NULL)
3368 session = imap_session_get(folder);
3369 g_return_val_if_fail(session != NULL, FALSE);
3371 selected_folder = (session->mbox != NULL) &&
3372 (!strcmp(session->mbox, item->item.path));
3373 if (selected_folder && time(NULL) - item->use_cache < 2) {
3374 ok = imap_cmd_noop(session);
3375 if (ok != IMAP_SUCCESS) {
3376 debug_print("disconnected!\n");
3377 session = imap_reconnect_if_possible(folder, session);
3378 if (session == NULL)
3383 if (session->folder_content_changed
3384 || session->exists != item->item.total_msgs) {
3389 ok = imap_status(session, IMAP_FOLDER(folder), item->item.path, IMAP_FOLDER_ITEM(item),
3390 &exists, &uid_next, &uid_val, &unseen, FALSE);
3391 if (ok != IMAP_SUCCESS) {
3396 item->use_cache = time(NULL);
3397 item->c_messages = exists;
3398 item->c_uid_next = uid_next;
3399 item->c_uid_validity = uid_val;
3400 item->c_unseen = unseen;
3401 item->item.last_num = uid_next - 1;
3402 debug_print("uidnext %d, item->uid_next %d, exists %d, item->item.total_msgs %d\n",
3403 uid_next, item->uid_next, exists, item->item.total_msgs);
3404 if ((uid_next != item->uid_next) || (exists != item->item.total_msgs)
3405 || unseen != item->item.unread_msgs || uid_val != item->item.mtime) {
3414 void imap_change_flags(Folder *folder, FolderItem *item, MsgInfo *msginfo, MsgPermFlags newflags)
3416 IMAPSession *session;
3417 IMAPFlags flags_set = 0, flags_unset = 0;
3418 gint ok = IMAP_SUCCESS;
3419 MsgNumberList numlist;
3420 hashtable_data *ht_data = NULL;
3422 g_return_if_fail(folder != NULL);
3423 g_return_if_fail(folder->klass == &imap_class);
3424 g_return_if_fail(item != NULL);
3425 g_return_if_fail(item->folder == folder);
3426 g_return_if_fail(msginfo != NULL);
3427 g_return_if_fail(msginfo->folder == item);
3429 session = imap_session_get(folder);
3434 if (!MSG_IS_MARKED(msginfo->flags) && (newflags & MSG_MARKED))
3435 flags_set |= IMAP_FLAG_FLAGGED;
3436 if ( MSG_IS_MARKED(msginfo->flags) && !(newflags & MSG_MARKED))
3437 flags_unset |= IMAP_FLAG_FLAGGED;
3439 if (!MSG_IS_UNREAD(msginfo->flags) && (newflags & MSG_UNREAD))
3440 flags_unset |= IMAP_FLAG_SEEN;
3441 if ( MSG_IS_UNREAD(msginfo->flags) && !(newflags & MSG_UNREAD))
3442 flags_set |= IMAP_FLAG_SEEN;
3444 if (!MSG_IS_REPLIED(msginfo->flags) && (newflags & MSG_REPLIED))
3445 flags_set |= IMAP_FLAG_ANSWERED;
3446 if ( MSG_IS_REPLIED(msginfo->flags) && !(newflags & MSG_REPLIED))
3447 flags_unset |= IMAP_FLAG_ANSWERED;
3449 if (!MSG_IS_DELETED(msginfo->flags) && (newflags & MSG_DELETED))
3450 flags_set |= IMAP_FLAG_DELETED;
3451 if ( MSG_IS_DELETED(msginfo->flags) && !(newflags & MSG_DELETED))
3452 flags_unset |= IMAP_FLAG_DELETED;
3454 if (!flags_set && !flags_unset) {
3455 /* the changed flags were not translatable to IMAP-speak.
3456 * like MSG_POSTFILTERED, so just apply. */
3457 msginfo->flags.perm_flags = newflags;
3462 if ((ok = imap_select(session, IMAP_FOLDER(folder), msginfo->folder->path,
3463 NULL, NULL, NULL, NULL, FALSE)) != IMAP_SUCCESS) {
3467 numlist.next = NULL;
3468 numlist.data = GINT_TO_POINTER(msginfo->msgnum);
3470 if (IMAP_FOLDER_ITEM(item)->batching) {
3471 /* instead of performing an UID STORE command for each message change,
3472 * as a lot of them can change "together", we just fill in hashtables
3473 * and defer the treatment so that we're able to send only one
3476 debug_print("IMAP batch mode on, deferring flags change\n");
3478 ht_data = g_hash_table_lookup(IMAP_FOLDER_ITEM(item)->flags_set_table,
3479 GINT_TO_POINTER(flags_set));
3480 if (ht_data == NULL) {
3481 ht_data = g_new0(hashtable_data, 1);
3482 ht_data->session = session;
3483 ht_data->item = IMAP_FOLDER_ITEM(item);
3484 g_hash_table_insert(IMAP_FOLDER_ITEM(item)->flags_set_table,
3485 GINT_TO_POINTER(flags_set), ht_data);
3487 if (!g_slist_find(ht_data->msglist, GINT_TO_POINTER(msginfo->msgnum)))
3488 ht_data->msglist = g_slist_prepend(ht_data->msglist, GINT_TO_POINTER(msginfo->msgnum));
3491 ht_data = g_hash_table_lookup(IMAP_FOLDER_ITEM(item)->flags_unset_table,
3492 GINT_TO_POINTER(flags_unset));
3493 if (ht_data == NULL) {
3494 ht_data = g_new0(hashtable_data, 1);
3495 ht_data->session = session;
3496 ht_data->item = IMAP_FOLDER_ITEM(item);
3497 g_hash_table_insert(IMAP_FOLDER_ITEM(item)->flags_unset_table,
3498 GINT_TO_POINTER(flags_unset), ht_data);
3500 if (!g_slist_find(ht_data->msglist, GINT_TO_POINTER(msginfo->msgnum)))
3501 ht_data->msglist = g_slist_prepend(ht_data->msglist,
3502 GINT_TO_POINTER(msginfo->msgnum));
3505 debug_print("IMAP changing flags\n");
3507 ok = imap_set_message_flags(session, &numlist, flags_set, TRUE);
3508 if (ok != IMAP_SUCCESS) {
3515 ok = imap_set_message_flags(session, &numlist, flags_unset, FALSE);
3516 if (ok != IMAP_SUCCESS) {
3522 msginfo->flags.perm_flags = newflags;
3527 static gint imap_remove_msg(Folder *folder, FolderItem *item, gint uid)
3530 IMAPSession *session;
3532 MsgNumberList numlist;
3534 g_return_val_if_fail(folder != NULL, -1);
3535 g_return_val_if_fail(FOLDER_CLASS(folder) == &imap_class, -1);
3536 g_return_val_if_fail(item != NULL, -1);
3538 session = imap_session_get(folder);
3539 if (!session) return -1;
3541 ok = imap_select(session, IMAP_FOLDER(folder), item->path,
3542 NULL, NULL, NULL, NULL, FALSE);
3543 if (ok != IMAP_SUCCESS) {
3547 numlist.next = NULL;
3548 numlist.data = GINT_TO_POINTER(uid);
3550 ok = imap_set_message_flags
3551 (IMAP_SESSION(REMOTE_FOLDER(folder)->session),
3552 &numlist, IMAP_FLAG_DELETED, TRUE);
3553 if (ok != IMAP_SUCCESS) {
3554 log_warning(_("can't set deleted flags: %d\n"), uid);
3559 if (!session->uidplus) {
3560 ok = imap_cmd_expunge(session);
3564 uidstr = g_strdup_printf("%u", uid);
3565 ok = imap_cmd_expunge(session);
3568 if (ok != IMAP_SUCCESS) {
3569 log_warning(_("can't expunge\n"));
3574 IMAP_FOLDER_ITEM(item)->uid_list = g_slist_remove(
3575 IMAP_FOLDER_ITEM(item)->uid_list, numlist.data);
3576 dir = folder_item_get_path(item);
3577 if (is_dir_exist(dir))
3578 remove_numbered_files(dir, uid, uid);
3581 return IMAP_SUCCESS;
3584 static gint compare_msginfo(gconstpointer a, gconstpointer b)
3586 return ((MsgInfo *)a)->msgnum - ((MsgInfo *)b)->msgnum;
3589 static guint gslist_find_next_num(MsgNumberList **list, guint num)
3593 g_return_val_if_fail(list != NULL, -1);
3595 for (elem = *list; elem != NULL; elem = g_slist_next(elem))
3596 if (GPOINTER_TO_INT(elem->data) >= num)
3599 return elem != NULL ? GPOINTER_TO_INT(elem->data) : (gint)-1;
3603 * NEW and DELETED flags are not syncronized
3604 * - The NEW/RECENT flags in IMAP folders can not really be directly
3605 * modified by Sylpheed
3606 * - The DELETE/DELETED flag in IMAP and Sylpheed don't have the same
3607 * meaning, in IMAP it always removes the messages from the FolderItem
3608 * in Sylpheed it can mean to move the message to trash
3611 typedef struct _get_flags_data {
3614 MsgInfoList *msginfo_list;
3615 GRelation *msgflags;
3616 gboolean full_search;
3620 static /*gint*/ void *imap_get_flags_thread(void *data)
3622 get_flags_data *stuff = (get_flags_data *)data;
3623 Folder *folder = stuff->folder;
3624 FolderItem *item = stuff->item;
3625 MsgInfoList *msginfo_list = stuff->msginfo_list;
3626 GRelation *msgflags = stuff->msgflags;
3627 gboolean full_search = stuff->full_search;
3628 IMAPSession *session;
3629 GSList *sorted_list = NULL;
3630 GSList *unseen = NULL, *answered = NULL, *flagged = NULL, *deleted = NULL;
3631 GSList *p_unseen, *p_answered, *p_flagged, *p_deleted;
3633 GSList *seq_list, *cur;
3634 gboolean reverse_seen = FALSE;
3637 gint exists_cnt, unseen_cnt;
3638 gboolean selected_folder;
3640 if (folder == NULL || item == NULL) {
3642 return GINT_TO_POINTER(-1);
3645 session = imap_session_get(folder);
3646 if (session == NULL) {
3648 return GINT_TO_POINTER(-1);
3651 selected_folder = (session->mbox != NULL) &&
3652 (!strcmp(session->mbox, item->path));
3654 if (!selected_folder) {
3655 ok = imap_select(session, IMAP_FOLDER(folder), item->path,
3656 &exists_cnt, NULL, &unseen_cnt, NULL, TRUE);
3657 if (ok != IMAP_SUCCESS) {
3660 return GINT_TO_POINTER(-1);
3663 if (unseen_cnt > exists_cnt / 2)
3664 reverse_seen = TRUE;
3667 if (item->unread_msgs > item->total_msgs / 2)
3668 reverse_seen = TRUE;
3671 cmd_buf = g_string_new(NULL);
3673 sorted_list = g_slist_sort(g_slist_copy(msginfo_list), compare_msginfo);
3675 seq_list = imap_get_lep_set_from_msglist(msginfo_list);
3677 struct mailimap_set * set;
3678 set = mailimap_set_new_interval(1, 0);
3679 seq_list = g_slist_append(NULL, set);
3682 for (cur = seq_list; cur != NULL; cur = g_slist_next(cur)) {
3683 struct mailimap_set * imapset;
3684 clist * lep_uidlist;
3687 imapset = cur->data;
3689 r = imap_threaded_search(folder, IMAP_SEARCH_TYPE_SEEN,
3690 imapset, &lep_uidlist);
3693 r = imap_threaded_search(folder,
3694 IMAP_SEARCH_TYPE_UNSEEN,
3695 imapset, &lep_uidlist);
3697 if (r == MAILIMAP_NO_ERROR) {
3700 uidlist = imap_uid_list_from_lep(lep_uidlist);
3701 mailimap_search_result_free(lep_uidlist);
3703 unseen = g_slist_concat(unseen, uidlist);
3706 r = imap_threaded_search(folder, IMAP_SEARCH_TYPE_FLAGGED,
3707 imapset, &lep_uidlist);
3708 if (r == MAILIMAP_NO_ERROR) {
3711 uidlist = imap_uid_list_from_lep(lep_uidlist);
3712 mailimap_search_result_free(lep_uidlist);
3714 flagged = g_slist_concat(flagged, uidlist);
3717 if (item->opened || item->processing_pending || item == folder->inbox) {
3718 r = imap_threaded_search(folder, IMAP_SEARCH_TYPE_ANSWERED,
3719 imapset, &lep_uidlist);
3720 if (r == MAILIMAP_NO_ERROR) {
3723 uidlist = imap_uid_list_from_lep(lep_uidlist);
3724 mailimap_search_result_free(lep_uidlist);
3726 answered = g_slist_concat(answered, uidlist);
3729 r = imap_threaded_search(folder, IMAP_SEARCH_TYPE_DELETED,
3730 imapset, &lep_uidlist);
3731 if (r == MAILIMAP_NO_ERROR) {
3734 uidlist = imap_uid_list_from_lep(lep_uidlist);
3735 mailimap_search_result_free(lep_uidlist);
3737 deleted = g_slist_concat(deleted, uidlist);
3743 p_answered = answered;
3744 p_flagged = flagged;
3745 p_deleted = deleted;
3747 for (elem = sorted_list; elem != NULL; elem = g_slist_next(elem)) {
3752 msginfo = (MsgInfo *) elem->data;
3753 flags = msginfo->flags.perm_flags;
3754 wasnew = (flags & MSG_NEW);
3755 if (item->opened || item->processing_pending || item == folder->inbox) {
3756 flags &= ~((reverse_seen ? 0 : MSG_UNREAD | MSG_NEW) | MSG_REPLIED | MSG_MARKED);
3758 flags &= ~((reverse_seen ? 0 : MSG_UNREAD | MSG_NEW | MSG_MARKED));
3761 flags |= MSG_UNREAD | (wasnew ? MSG_NEW : 0);
3762 if (gslist_find_next_num(&p_unseen, msginfo->msgnum) == msginfo->msgnum) {
3763 if (!reverse_seen) {
3764 flags |= MSG_UNREAD | (wasnew ? MSG_NEW : 0);
3766 flags &= ~(MSG_UNREAD | MSG_NEW);
3770 if (gslist_find_next_num(&p_flagged, msginfo->msgnum) == msginfo->msgnum)
3771 flags |= MSG_MARKED;
3773 flags &= ~MSG_MARKED;
3775 if (item->opened || item->processing_pending || item == folder->inbox) {
3776 if (gslist_find_next_num(&p_answered, msginfo->msgnum) == msginfo->msgnum)
3777 flags |= MSG_REPLIED;
3779 flags &= ~MSG_REPLIED;
3780 if (gslist_find_next_num(&p_deleted, msginfo->msgnum) == msginfo->msgnum)
3781 flags |= MSG_DELETED;
3783 flags &= ~MSG_DELETED;
3785 g_relation_insert(msgflags, msginfo, GINT_TO_POINTER(flags));
3788 imap_lep_set_free(seq_list);
3789 g_slist_free(flagged);
3790 g_slist_free(deleted);
3791 g_slist_free(answered);
3792 g_slist_free(unseen);
3793 g_slist_free(sorted_list);
3794 g_string_free(cmd_buf, TRUE);
3798 return GINT_TO_POINTER(0);
3801 static gint imap_get_flags(Folder *folder, FolderItem *item,
3802 MsgInfoList *msginfo_list, GRelation *msgflags)
3805 get_flags_data *data = g_new0(get_flags_data, 1);
3807 data->folder = folder;
3809 data->msginfo_list = msginfo_list;
3810 data->msgflags = msgflags;
3811 data->full_search = FALSE;
3813 GSList *tmp = NULL, *cur;
3815 if (prefs_common.work_offline &&
3816 !inc_offline_should_override(
3817 _("Sylpheed-Claws needs network access in order "
3818 "to access the IMAP server."))) {
3823 tmp = folder_item_get_msg_list(item);
3825 if (g_slist_length(tmp) <= g_slist_length(msginfo_list))
3826 data->full_search = TRUE;
3828 for (cur = tmp; cur; cur = cur->next)
3829 procmsg_msginfo_free((MsgInfo *)cur->data);
3833 result = GPOINTER_TO_INT(imap_get_flags_thread(data));
3840 static gboolean process_flags(gpointer key, gpointer value, gpointer user_data)
3842 gboolean flags_set = GPOINTER_TO_INT(user_data);
3843 gint flags_value = GPOINTER_TO_INT(key);
3844 hashtable_data *data = (hashtable_data *)value;
3845 IMAPFolderItem *_item = data->item;
3846 FolderItem *item = (FolderItem *)_item;
3847 gint ok = IMAP_ERROR;
3848 IMAPSession *session = imap_session_get(item->folder);
3850 data->msglist = g_slist_reverse(data->msglist);
3852 debug_print("IMAP %ssetting flags to %d for %d messages\n",
3855 g_slist_length(data->msglist));
3859 ok = imap_select(session, IMAP_FOLDER(item->folder), item->path,
3860 NULL, NULL, NULL, NULL, FALSE);
3862 if (ok == IMAP_SUCCESS) {
3863 imap_set_message_flags(data->session, data->msglist, flags_value, flags_set);
3865 g_warning("can't select mailbox %s\n", item->path);
3869 g_slist_free(data->msglist);
3874 static void process_hashtable(IMAPFolderItem *item)
3876 if (item->flags_set_table) {
3877 g_hash_table_foreach_remove(item->flags_set_table, process_flags, GINT_TO_POINTER(TRUE));
3878 g_hash_table_destroy(item->flags_set_table);
3879 item->flags_set_table = NULL;
3881 if (item->flags_unset_table) {
3882 g_hash_table_foreach_remove(item->flags_unset_table, process_flags, GINT_TO_POINTER(FALSE));
3883 g_hash_table_destroy(item->flags_unset_table);
3884 item->flags_unset_table = NULL;
3888 static void imap_set_batch (Folder *folder, FolderItem *_item, gboolean batch)
3890 IMAPFolderItem *item = (IMAPFolderItem *)_item;
3892 g_return_if_fail(item != NULL);
3894 if (item->batching == batch)
3898 item->batching = TRUE;
3899 debug_print("IMAP switching to batch mode\n");
3900 if (!item->flags_set_table) {
3901 item->flags_set_table = g_hash_table_new(NULL, g_direct_equal);
3903 if (!item->flags_unset_table) {
3904 item->flags_unset_table = g_hash_table_new(NULL, g_direct_equal);
3907 debug_print("IMAP switching away from batch mode\n");
3909 process_hashtable(item);
3910 item->batching = FALSE;
3916 /* data types conversion libetpan <-> sylpheed */
3920 #define ETPAN_IMAP_MB_MARKED 1
3921 #define ETPAN_IMAP_MB_UNMARKED 2
3922 #define ETPAN_IMAP_MB_NOSELECT 4
3923 #define ETPAN_IMAP_MB_NOINFERIORS 8
3925 static int imap_flags_to_flags(struct mailimap_mbx_list_flags * imap_flags)
3931 if (imap_flags->mbf_type == MAILIMAP_MBX_LIST_FLAGS_SFLAG) {
3932 switch (imap_flags->mbf_sflag) {
3933 case MAILIMAP_MBX_LIST_SFLAG_MARKED:
3934 flags |= ETPAN_IMAP_MB_MARKED;
3936 case MAILIMAP_MBX_LIST_SFLAG_NOSELECT:
3937 flags |= ETPAN_IMAP_MB_NOSELECT;
3939 case MAILIMAP_MBX_LIST_SFLAG_UNMARKED:
3940 flags |= ETPAN_IMAP_MB_UNMARKED;
3945 for(cur = clist_begin(imap_flags->mbf_oflags) ; cur != NULL ;
3946 cur = clist_next(cur)) {
3947 struct mailimap_mbx_list_oflag * oflag;
3949 oflag = clist_content(cur);
3951 switch (oflag->of_type) {
3952 case MAILIMAP_MBX_LIST_OFLAG_NOINFERIORS:
3953 flags |= ETPAN_IMAP_MB_NOINFERIORS;
3961 static GSList * imap_list_from_lep(IMAPFolder * folder,
3962 clist * list, const gchar * real_path, gboolean all)
3965 GSList * item_list = NULL, *llast = NULL;
3967 for(iter = clist_begin(list) ; iter != NULL ;
3968 iter = clist_next(iter)) {
3969 struct mailimap_mailbox_list * mb;
3977 FolderItem *new_item;
3979 mb = clist_content(iter);
3985 if (mb->mb_flag != NULL)
3986 flags = imap_flags_to_flags(mb->mb_flag);
3988 delimiter = mb->mb_delimiter;
3991 dup_name = strdup(name);
3992 if (delimiter != '\0')
3993 subst_char(dup_name, delimiter, '/');
3995 base = g_path_get_basename(dup_name);
3996 if (base[0] == '.') {
4002 if (!all && strcmp(dup_name, real_path) == 0) {
4008 if (!all && dup_name[strlen(dup_name)-1] == '/') {
4014 loc_name = imap_modified_utf7_to_utf8(base);
4015 loc_path = imap_modified_utf7_to_utf8(dup_name);
4017 new_item = folder_item_new(FOLDER(folder), loc_name, loc_path);
4018 if ((flags & ETPAN_IMAP_MB_NOINFERIORS) != 0)
4019 new_item->no_sub = TRUE;
4020 if (strcmp(dup_name, "INBOX") != 0 &&
4021 ((flags & ETPAN_IMAP_MB_NOSELECT) != 0))
4022 new_item->no_select = TRUE;
4024 if (item_list == NULL)
4025 llast = item_list = g_slist_append(item_list, new_item);
4027 llast = g_slist_append(llast, new_item);
4028 llast = llast->next;
4030 debug_print("folder '%s' found.\n", loc_path);
4041 static GSList * imap_get_lep_set_from_numlist(MsgNumberList *numlist)
4043 GSList *sorted_list, *cur;
4044 guint first, last, next;
4045 GSList *ret_list = NULL, *llast = NULL;
4047 struct mailimap_set * current_set;
4048 unsigned int item_count;
4050 if (numlist == NULL)
4054 current_set = mailimap_set_new_empty();
4056 sorted_list = g_slist_copy(numlist);
4057 sorted_list = g_slist_sort(sorted_list, g_int_compare);
4059 first = GPOINTER_TO_INT(sorted_list->data);
4062 for (cur = sorted_list; cur != NULL; cur = g_slist_next(cur)) {
4063 if (GPOINTER_TO_INT(cur->data) == 0)
4068 last = GPOINTER_TO_INT(cur->data);
4070 next = GPOINTER_TO_INT(cur->next->data);
4074 if (last + 1 != next || next == 0) {
4076 struct mailimap_set_item * item;
4077 item = mailimap_set_item_new(first, last);
4078 mailimap_set_add(current_set, item);
4083 if (count >= IMAP_SET_MAX_COUNT) {
4084 if (ret_list == NULL)
4085 llast = ret_list = g_slist_append(ret_list,
4088 llast = g_slist_append(llast, current_set);
4089 llast = llast->next;
4091 current_set = mailimap_set_new_empty();
4098 if (clist_count(current_set->set_list) > 0) {
4099 ret_list = g_slist_append(ret_list,
4103 g_slist_free(sorted_list);
4108 static GSList * imap_get_lep_set_from_msglist(MsgInfoList *msglist)
4110 MsgNumberList *numlist = NULL;
4114 for (cur = msglist; cur != NULL; cur = g_slist_next(cur)) {
4115 MsgInfo *msginfo = (MsgInfo *) cur->data;
4117 numlist = g_slist_prepend(numlist, GINT_TO_POINTER(msginfo->msgnum));
4119 numlist = g_slist_reverse(numlist);
4120 seq_list = imap_get_lep_set_from_numlist(numlist);
4121 g_slist_free(numlist);
4126 static GSList * imap_uid_list_from_lep(clist * list)
4133 for(iter = clist_begin(list) ; iter != NULL ;
4134 iter = clist_next(iter)) {
4137 puid = clist_content(iter);
4138 result = g_slist_prepend(result, GINT_TO_POINTER(* puid));
4141 result = g_slist_reverse(result);
4145 static GSList * imap_uid_list_from_lep_tab(carray * list)
4152 for(i = 0 ; i < carray_count(list) ; i ++) {
4155 puid = carray_get(list, i);
4156 result = g_slist_prepend(result, GINT_TO_POINTER(* puid));
4158 result = g_slist_reverse(result);
4162 static MsgInfo *imap_envelope_from_lep(struct imap_fetch_env_info * info,
4165 MsgInfo *msginfo = NULL;
4168 MsgFlags flags = {0, 0};
4170 if (info->headers == NULL)
4173 MSG_SET_TMP_FLAGS(flags, MSG_IMAP);
4174 if (folder_has_parent_of_type(item, F_QUEUE)) {
4175 MSG_SET_TMP_FLAGS(flags, MSG_QUEUED);
4176 } else if (folder_has_parent_of_type(item, F_DRAFT)) {
4177 MSG_SET_TMP_FLAGS(flags, MSG_DRAFT);
4179 flags.perm_flags = info->flags;
4183 msginfo = procheader_parse_str(info->headers, flags, FALSE, FALSE);
4186 msginfo->msgnum = uid;
4187 msginfo->size = size;
4193 static void imap_lep_set_free(GSList *seq_list)
4197 for(cur = seq_list ; cur != NULL ; cur = g_slist_next(cur)) {
4198 struct mailimap_set * imapset;
4200 imapset = cur->data;
4201 mailimap_set_free(imapset);
4203 g_slist_free(seq_list);
4206 static struct mailimap_flag_list * imap_flag_to_lep(IMAPFlags flags)
4208 struct mailimap_flag_list * flag_list;
4210 flag_list = mailimap_flag_list_new_empty();
4212 if (IMAP_IS_SEEN(flags))
4213 mailimap_flag_list_add(flag_list,
4214 mailimap_flag_new_seen());
4215 if (IMAP_IS_ANSWERED(flags))
4216 mailimap_flag_list_add(flag_list,
4217 mailimap_flag_new_answered());
4218 if (IMAP_IS_FLAGGED(flags))
4219 mailimap_flag_list_add(flag_list,
4220 mailimap_flag_new_flagged());
4221 if (IMAP_IS_DELETED(flags))
4222 mailimap_flag_list_add(flag_list,
4223 mailimap_flag_new_deleted());
4224 if (IMAP_IS_DRAFT(flags))
4225 mailimap_flag_list_add(flag_list,
4226 mailimap_flag_new_draft());
4231 guint imap_folder_get_refcnt(Folder *folder)
4233 return ((IMAPFolder *)folder)->refcnt;
4236 void imap_folder_ref(Folder *folder)
4238 ((IMAPFolder *)folder)->refcnt++;
4241 void imap_folder_unref(Folder *folder)
4243 if (((IMAPFolder *)folder)->refcnt > 0)
4244 ((IMAPFolder *)folder)->refcnt--;
4247 #else /* HAVE_LIBETPAN */
4249 static FolderClass imap_class;
4251 static XMLTag *imap_item_get_xml(Folder *folder, FolderItem *item);
4252 static void imap_item_set_xml(Folder *folder, FolderItem *item, XMLTag *tag);
4254 static Folder *imap_folder_new (const gchar *name,
4259 static gint imap_create_tree (Folder *folder)
4263 static FolderItem *imap_create_folder (Folder *folder,
4269 static gint imap_rename_folder (Folder *folder,
4276 gchar imap_get_path_separator_for_item(FolderItem *item)
4281 FolderClass *imap_get_class(void)
4283 if (imap_class.idstr == NULL) {
4284 imap_class.type = F_IMAP;
4285 imap_class.idstr = "imap";
4286 imap_class.uistr = "IMAP4";
4288 imap_class.new_folder = imap_folder_new;
4289 imap_class.create_tree = imap_create_tree;
4290 imap_class.create_folder = imap_create_folder;
4291 imap_class.rename_folder = imap_rename_folder;
4293 imap_class.set_xml = folder_set_xml;
4294 imap_class.get_xml = folder_get_xml;
4295 imap_class.item_set_xml = imap_item_set_xml;
4296 imap_class.item_get_xml = imap_item_get_xml;
4297 /* nothing implemented */
4304 void imap_synchronise(FolderItem *item)
4306 imap_gtk_synchronise(item);
4309 static void imap_item_set_xml(Folder *folder, FolderItem *item, XMLTag *tag)
4311 #ifdef HAVE_LIBETPAN
4314 folder_item_set_xml(folder, item, tag);
4316 #ifdef HAVE_LIBETPAN
4317 for (cur = tag->attr; cur != NULL; cur = g_list_next(cur)) {
4318 XMLAttr *attr = (XMLAttr *) cur->data;
4320 if (!attr || !attr->name || !attr->value) continue;
4321 if (!strcmp(attr->name, "uidnext"))
4322 IMAP_FOLDER_ITEM(item)->uid_next = atoi(attr->value);
4327 static XMLTag *imap_item_get_xml(Folder *folder, FolderItem *item)
4331 tag = folder_item_get_xml(folder, item);
4333 #ifdef HAVE_LIBETPAN
4334 xml_tag_add_attr(tag, xml_attr_new_int("uidnext",
4335 IMAP_FOLDER_ITEM(item)->uid_next));