2 * Sylpheed -- a GTK+ based, lightweight, and fast e-mail client
3 * Copyright (C) 1999-2005 Hiroyuki Yamamoto
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation; either version 2 of the License, or
8 * (at your option) any later version.
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
15 * You should have received a copy of the GNU General Public License
16 * along with this program; if not, write to the Free Software
17 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
27 #include <glib/gi18n.h>
51 #include "procheader.h"
52 #include "prefs_account.h"
57 #include "prefs_common.h"
58 #include "inputdialog.h"
60 #include "remotefolder.h"
61 #include "alertpanel.h"
63 #include "statusbar.h"
65 #include "imap-thread.h"
69 static pthread_mutex_t imap_mutex;
70 static const char *mutex_hold = NULL;
73 #define __FUNCTION__ __FILE__
77 #define MUTEX_TRYLOCK_OR_RETURN() { \
78 debug_print("%s: locking mutex\n", __FUNCTION__); \
79 if (pthread_mutex_trylock(&imap_mutex) == EBUSY) { \
80 g_warning("can't lock mutex (held by %s)\n", \
81 mutex_hold ? mutex_hold:"(nil)"); \
84 mutex_hold = __FUNCTION__; \
87 #define MUTEX_TRYLOCK_OR_RETURN_VAL(retval) { \
88 debug_print("%s: locking mutex\n", __FUNCTION__); \
89 if (pthread_mutex_trylock(&imap_mutex) == EBUSY) { \
90 g_warning("can't lock mutex (held by %s)\n", \
91 mutex_hold ? mutex_hold:"(nil)"); \
94 mutex_hold = __FUNCTION__; \
97 #define MUTEX_UNLOCK() { \
98 debug_print("%s: unlocking mutex\n", __FUNCTION__);\
99 pthread_mutex_unlock(&imap_mutex); \
104 #define MUTEX_TRYLOCK_OR_RETURN() do {} while(0)
105 #define MUTEX_TRYLOCK_OR_RETURN_VAL(retval) do {} while(0)
106 #define MUTEX_UNLOCK() do {} while(0)
110 #define MUTEX_TRYLOCK_OR_RETURN() do {} while(0)
111 #define MUTEX_TRYLOCK_OR_RETURN_VAL(retval) do {} while(0)
112 #define MUTEX_UNLOCK() do {} while(0)
114 typedef struct _IMAPFolder IMAPFolder;
115 typedef struct _IMAPSession IMAPSession;
116 typedef struct _IMAPNameSpace IMAPNameSpace;
117 typedef struct _IMAPFolderItem IMAPFolderItem;
119 #include "prefs_account.h"
121 #define IMAP_FOLDER(obj) ((IMAPFolder *)obj)
122 #define IMAP_FOLDER_ITEM(obj) ((IMAPFolderItem *)obj)
123 #define IMAP_SESSION(obj) ((IMAPSession *)obj)
127 RemoteFolder rfolder;
129 /* list of IMAPNameSpace */
139 gboolean authenticated;
148 gboolean folder_content_changed;
153 struct _IMAPNameSpace
159 #define IMAP_SUCCESS 0
160 #define IMAP_SOCKET 2
161 #define IMAP_AUTHFAIL 3
162 #define IMAP_PROTOCOL 4
163 #define IMAP_SYNTAX 5
167 #define IMAPBUFSIZE 8192
171 IMAP_FLAG_SEEN = 1 << 0,
172 IMAP_FLAG_ANSWERED = 1 << 1,
173 IMAP_FLAG_FLAGGED = 1 << 2,
174 IMAP_FLAG_DELETED = 1 << 3,
175 IMAP_FLAG_DRAFT = 1 << 4
178 #define IMAP_IS_SEEN(flags) ((flags & IMAP_FLAG_SEEN) != 0)
179 #define IMAP_IS_ANSWERED(flags) ((flags & IMAP_FLAG_ANSWERED) != 0)
180 #define IMAP_IS_FLAGGED(flags) ((flags & IMAP_FLAG_FLAGGED) != 0)
181 #define IMAP_IS_DELETED(flags) ((flags & IMAP_FLAG_DELETED) != 0)
182 #define IMAP_IS_DRAFT(flags) ((flags & IMAP_FLAG_DRAFT) != 0)
185 #define IMAP4_PORT 143
187 #define IMAPS_PORT 993
190 #define IMAP_CMD_LIMIT 1000
192 struct _IMAPFolderItem
202 static void imap_folder_init (Folder *folder,
206 static Folder *imap_folder_new (const gchar *name,
208 static void imap_folder_destroy (Folder *folder);
210 static IMAPSession *imap_session_new (Folder *folder,
211 const PrefsAccount *account);
212 static void imap_session_authenticate(IMAPSession *session,
213 const PrefsAccount *account);
214 static void imap_session_destroy (Session *session);
216 static gchar *imap_fetch_msg (Folder *folder,
219 static gchar *imap_fetch_msg_full (Folder *folder,
224 static gint imap_add_msg (Folder *folder,
228 static gint imap_add_msgs (Folder *folder,
231 GRelation *relation);
233 static gint imap_copy_msg (Folder *folder,
236 static gint imap_copy_msgs (Folder *folder,
238 MsgInfoList *msglist,
239 GRelation *relation);
241 static gint imap_remove_msg (Folder *folder,
244 static gint imap_remove_msgs (Folder *folder,
246 MsgInfoList *msglist,
247 GRelation *relation);
248 static gint imap_remove_all_msg (Folder *folder,
251 static gboolean imap_is_msg_changed (Folder *folder,
255 static gint imap_close (Folder *folder,
258 static gint imap_scan_tree (Folder *folder);
260 static gint imap_create_tree (Folder *folder);
262 static FolderItem *imap_create_folder (Folder *folder,
265 static gint imap_rename_folder (Folder *folder,
268 static gint imap_remove_folder (Folder *folder,
271 static FolderItem *imap_folder_item_new (Folder *folder);
272 static void imap_folder_item_destroy (Folder *folder,
275 static IMAPSession *imap_session_get (Folder *folder);
277 static gint imap_auth (IMAPSession *session,
282 static gint imap_scan_tree_recursive (IMAPSession *session,
285 static void imap_create_missing_folders (Folder *folder);
286 static FolderItem *imap_create_special_folder
288 SpecialFolderItemType stype,
291 static gint imap_do_copy_msgs (Folder *folder,
293 MsgInfoList *msglist,
294 GRelation *relation);
296 static void imap_delete_all_cached_messages (FolderItem *item);
297 static void imap_set_batch (Folder *folder,
300 static gint imap_set_message_flags (IMAPSession *session,
301 MsgNumberList *numlist,
304 static gint imap_select (IMAPSession *session,
310 guint32 *uid_validity,
312 static gint imap_status (IMAPSession *session,
318 guint32 *uid_validity,
322 static IMAPNameSpace *imap_find_namespace (IMAPFolder *folder,
324 static gchar imap_get_path_separator (IMAPFolder *folder,
326 static gchar *imap_get_real_path (IMAPFolder *folder,
329 static void imap_free_capabilities (IMAPSession *session);
331 /* low-level IMAP4rev1 commands */
332 static gint imap_cmd_login (IMAPSession *session,
335 static gint imap_cmd_logout (IMAPSession *session);
336 static gint imap_cmd_noop (IMAPSession *session);
338 static gint imap_cmd_starttls (IMAPSession *session);
340 static gint imap_cmd_select (IMAPSession *session,
345 guint32 *uid_validity,
347 static gint imap_cmd_examine (IMAPSession *session,
352 guint32 *uid_validity,
354 static gint imap_cmd_create (IMAPSession *sock,
355 const gchar *folder);
356 static gint imap_cmd_rename (IMAPSession *sock,
357 const gchar *oldfolder,
358 const gchar *newfolder);
359 static gint imap_cmd_delete (IMAPSession *session,
360 const gchar *folder);
361 static gint imap_cmd_fetch (IMAPSession *sock,
363 const gchar *filename,
366 static gint imap_cmd_append (IMAPSession *session,
367 const gchar *destfolder,
371 static gint imap_cmd_copy (IMAPSession *session,
372 struct mailimap_set * set,
373 const gchar *destfolder,
374 GRelation *uid_mapping);
375 static gint imap_cmd_store (IMAPSession *session,
376 struct mailimap_set * set,
379 static gint imap_cmd_expunge (IMAPSession *session);
381 static void imap_path_separator_subst (gchar *str,
384 static gchar *imap_utf8_to_modified_utf7 (const gchar *from);
385 static gchar *imap_modified_utf7_to_utf8 (const gchar *mutf7_str);
387 static gboolean imap_rename_folder_func (GNode *node,
389 static gint imap_get_num_list (Folder *folder,
392 gboolean *old_uids_valid);
393 static GSList *imap_get_msginfos (Folder *folder,
395 GSList *msgnum_list);
396 static MsgInfo *imap_get_msginfo (Folder *folder,
399 static gboolean imap_scan_required (Folder *folder,
401 static void imap_change_flags (Folder *folder,
404 MsgPermFlags newflags);
405 static gint imap_get_flags (Folder *folder,
407 MsgInfoList *msglist,
408 GRelation *msgflags);
409 static gchar *imap_folder_get_path (Folder *folder);
410 static gchar *imap_item_get_path (Folder *folder,
412 static MsgInfo *imap_parse_msg(const gchar *file, FolderItem *item);
415 /* data types conversion libetpan <-> sylpheed */
416 static GSList * imap_list_from_lep(IMAPFolder * folder,
417 clist * list, const gchar * real_path);
418 static GSList * imap_get_lep_set_from_numlist(MsgNumberList *numlist);
419 static GSList * imap_get_lep_set_from_msglist(MsgInfoList *msglist);
420 static GSList * imap_uid_list_from_lep(clist * list);
421 static GSList * imap_uid_list_from_lep_tab(carray * list);
422 static MsgInfo *imap_envelope_from_lep(struct imap_fetch_env_info * info,
424 static void imap_lep_set_free(GSList *seq_list);
425 static struct mailimap_flag_list * imap_flag_to_lep(IMAPFlags flags);
428 static GHashTable *flags_set_table = NULL;
429 static GHashTable *flags_unset_table = NULL;
430 typedef struct _hashtable_data {
431 IMAPSession *session;
435 static FolderClass imap_class;
437 typedef struct _thread_data {
447 FolderClass *imap_get_class(void)
449 if (imap_class.idstr == NULL) {
450 imap_class.type = F_IMAP;
451 imap_class.idstr = "imap";
452 imap_class.uistr = "IMAP4";
454 /* Folder functions */
455 imap_class.new_folder = imap_folder_new;
456 imap_class.destroy_folder = imap_folder_destroy;
457 imap_class.scan_tree = imap_scan_tree;
458 imap_class.create_tree = imap_create_tree;
460 /* FolderItem functions */
461 imap_class.item_new = imap_folder_item_new;
462 imap_class.item_destroy = imap_folder_item_destroy;
463 imap_class.item_get_path = imap_item_get_path;
464 imap_class.create_folder = imap_create_folder;
465 imap_class.rename_folder = imap_rename_folder;
466 imap_class.remove_folder = imap_remove_folder;
467 imap_class.close = imap_close;
468 imap_class.get_num_list = imap_get_num_list;
469 imap_class.scan_required = imap_scan_required;
471 /* Message functions */
472 imap_class.get_msginfo = imap_get_msginfo;
473 imap_class.get_msginfos = imap_get_msginfos;
474 imap_class.fetch_msg = imap_fetch_msg;
475 imap_class.fetch_msg_full = imap_fetch_msg_full;
476 imap_class.add_msg = imap_add_msg;
477 imap_class.add_msgs = imap_add_msgs;
478 imap_class.copy_msg = imap_copy_msg;
479 imap_class.copy_msgs = imap_copy_msgs;
480 imap_class.remove_msg = imap_remove_msg;
481 imap_class.remove_msgs = imap_remove_msgs;
482 imap_class.remove_all_msg = imap_remove_all_msg;
483 imap_class.is_msg_changed = imap_is_msg_changed;
484 imap_class.change_flags = imap_change_flags;
485 imap_class.get_flags = imap_get_flags;
486 imap_class.set_batch = imap_set_batch;
488 pthread_mutex_init(&imap_mutex, NULL);
495 static Folder *imap_folder_new(const gchar *name, const gchar *path)
499 folder = (Folder *)g_new0(IMAPFolder, 1);
500 folder->klass = &imap_class;
501 imap_folder_init(folder, name, path);
506 static void imap_folder_destroy(Folder *folder)
511 dir = imap_folder_get_path(folder);
512 if (is_dir_exist(dir))
513 remove_dir_recursive(dir);
516 folder_remote_folder_destroy(REMOTE_FOLDER(folder));
519 static void imap_folder_init(Folder *folder, const gchar *name,
522 folder_remote_folder_init((Folder *)folder, name, path);
525 static FolderItem *imap_folder_item_new(Folder *folder)
527 IMAPFolderItem *item;
529 item = g_new0(IMAPFolderItem, 1);
532 item->uid_list = NULL;
534 return (FolderItem *)item;
537 static void imap_folder_item_destroy(Folder *folder, FolderItem *_item)
539 IMAPFolderItem *item = (IMAPFolderItem *)_item;
541 g_return_if_fail(item != NULL);
542 g_slist_free(item->uid_list);
547 static gboolean imap_reset_uid_lists_func(GNode *node, gpointer data)
549 IMAPFolderItem *item = (IMAPFolderItem *)node->data;
553 g_slist_free(item->uid_list);
554 item->uid_list = NULL;
559 static void imap_reset_uid_lists(Folder *folder)
561 if(folder->node == NULL)
564 /* Destroy all uid lists and rest last uid */
565 g_node_traverse(folder->node, G_IN_ORDER, G_TRAVERSE_ALL, -1, imap_reset_uid_lists_func, NULL);
568 static gint imap_auth(IMAPSession *session, const gchar *user, const gchar *pass,
573 ok = imap_cmd_login(session, user, pass);
575 if (ok == IMAP_SUCCESS)
576 session->authenticated = TRUE;
581 static IMAPSession *imap_session_get(Folder *folder)
583 RemoteFolder *rfolder = REMOTE_FOLDER(folder);
584 IMAPSession *session = NULL;
586 g_return_val_if_fail(folder != NULL, NULL);
587 g_return_val_if_fail(FOLDER_CLASS(folder) == &imap_class, NULL);
588 g_return_val_if_fail(folder->account != NULL, NULL);
590 if (prefs_common.work_offline && !imap_gtk_should_override()) {
594 /* Make sure we have a session */
595 if (rfolder->session != NULL) {
596 session = IMAP_SESSION(rfolder->session);
598 imap_reset_uid_lists(folder);
599 session = imap_session_new(folder, folder->account);
604 /* Make sure session is authenticated */
605 if (!IMAP_SESSION(session)->authenticated)
606 imap_session_authenticate(IMAP_SESSION(session), folder->account);
608 if (!IMAP_SESSION(session)->authenticated) {
609 session_destroy(SESSION(session));
610 rfolder->session = NULL;
615 /* Make sure we have parsed the IMAP namespace */
616 imap_parse_namespace(IMAP_SESSION(session),
617 IMAP_FOLDER(folder));
620 /* I think the point of this code is to avoid sending a
621 * keepalive if we've used the session recently and therefore
622 * think it's still alive. Unfortunately, most of the code
623 * does not yet check for errors on the socket, and so if the
624 * connection drops we don't notice until the timeout expires.
625 * A better solution than sending a NOOP every time would be
626 * for every command to be prepared to retry until it is
627 * successfully sent. -- mbp */
628 if (time(NULL) - SESSION(session)->last_access_time > SESSION_TIMEOUT_INTERVAL) {
629 /* verify that the session is still alive */
630 if (imap_cmd_noop(session) != IMAP_SUCCESS) {
631 /* Check if this is the first try to establish a
632 connection, if yes we don't try to reconnect */
633 if (rfolder->session == NULL) {
634 log_warning(_("Connecting to %s failed"),
635 folder->account->recv_server);
636 session_destroy(SESSION(session));
639 log_warning(_("IMAP4 connection to %s has been"
640 " disconnected. Reconnecting...\n"),
641 folder->account->recv_server);
642 statusbar_print_all(_("IMAP4 connection to %s has been"
643 " disconnected. Reconnecting...\n"),
644 folder->account->recv_server);
645 session_destroy(SESSION(session));
646 /* Clear folders session to make imap_session_get create
647 a new session, because of rfolder->session == NULL
648 it will not try to reconnect again and so avoid an
650 rfolder->session = NULL;
651 session = imap_session_get(folder);
657 rfolder->session = SESSION(session);
659 return IMAP_SESSION(session);
662 static IMAPSession *imap_session_new(Folder * folder,
663 const PrefsAccount *account)
665 IMAPSession *session;
671 /* FIXME: IMAP over SSL only... */
674 port = account->set_imapport ? account->imapport
675 : account->ssl_imap == SSL_TUNNEL ? IMAPS_PORT : IMAP4_PORT;
676 ssl_type = account->ssl_imap;
678 port = account->set_imapport ? account->imapport
683 statusbar_print_all(_("Connecting to IMAP4 server: %s..."), folder->account->recv_server);
684 if (account->set_tunnelcmd) {
685 r = imap_threaded_connect_cmd(folder,
687 account->recv_server,
692 if (ssl_type == SSL_TUNNEL) {
693 r = imap_threaded_connect_ssl(folder,
694 account->recv_server,
700 r = imap_threaded_connect(folder,
701 account->recv_server,
707 if (r == MAILIMAP_NO_ERROR_AUTHENTICATED) {
708 authenticated = TRUE;
710 else if (r == MAILIMAP_NO_ERROR_NON_AUTHENTICATED) {
711 authenticated = FALSE;
714 if(!prefs_common.no_recv_err_panel) {
715 alertpanel_error(_("Can't connect to IMAP4 server: %s:%d"),
716 account->recv_server, port);
722 session = g_new0(IMAPSession, 1);
723 session_init(SESSION(session));
724 SESSION(session)->type = SESSION_IMAP;
725 SESSION(session)->server = g_strdup(account->recv_server);
726 SESSION(session)->sock = NULL;
728 SESSION(session)->destroy = imap_session_destroy;
730 session->capability = NULL;
732 session->authenticated = authenticated;
733 session->mbox = NULL;
734 session->cmd_count = 0;
735 session->folder = folder;
738 if (account->ssl_imap == SSL_STARTTLS) {
741 ok = imap_cmd_starttls(session);
742 if (ok != IMAP_SUCCESS) {
743 log_warning(_("Can't start TLS session.\n"));
744 session_destroy(SESSION(session));
748 imap_free_capabilities(session);
749 session->authenticated = FALSE;
750 session->uidplus = FALSE;
751 session->cmd_count = 1;
754 log_message("IMAP connection is %s-authenticated\n",
755 (session->authenticated) ? "pre" : "un");
760 static void imap_session_authenticate(IMAPSession *session,
761 const PrefsAccount *account)
765 g_return_if_fail(account->userid != NULL);
767 pass = account->passwd;
770 tmp_pass = input_dialog_query_password(account->recv_server, account->userid);
773 Xstrdup_a(pass, tmp_pass, {g_free(tmp_pass); return;});
776 statusbar_print_all(_("Connecting to IMAP4 server %s...\n"),
777 account->recv_server);
778 if (imap_auth(session, account->userid, pass, account->imap_auth_type) != IMAP_SUCCESS) {
779 imap_threaded_disconnect(session->folder);
780 imap_cmd_logout(session);
786 session->authenticated = TRUE;
789 static void imap_session_destroy(Session *session)
791 imap_threaded_disconnect(IMAP_SESSION(session)->folder);
793 imap_free_capabilities(IMAP_SESSION(session));
794 g_free(IMAP_SESSION(session)->mbox);
795 sock_close(session->sock);
796 session->sock = NULL;
799 static gchar *imap_fetch_msg(Folder *folder, FolderItem *item, gint uid)
801 return imap_fetch_msg_full(folder, item, uid, TRUE, TRUE);
804 static guint get_size_with_lfs(MsgInfo *info)
813 fp = procmsg_open_message(info);
817 while (fgets(buf, sizeof (buf), fp) != NULL) {
819 if (!strstr(buf, "\r") && strstr(buf, "\n"))
827 static gchar *imap_fetch_msg_full(Folder *folder, FolderItem *item, gint uid,
828 gboolean headers, gboolean body)
830 gchar *path, *filename;
831 IMAPSession *session;
834 g_return_val_if_fail(folder != NULL, NULL);
835 g_return_val_if_fail(item != NULL, NULL);
840 path = folder_item_get_path(item);
841 if (!is_dir_exist(path))
843 filename = g_strconcat(path, G_DIR_SEPARATOR_S, itos(uid), NULL);
846 if (is_file_exist(filename)) {
847 /* see whether the local file represents the whole message
848 * or not. As the IMAP server reports size with \r chars,
849 * we have to update the local file (UNIX \n only) size */
850 MsgInfo *msginfo = imap_parse_msg(filename, item);
851 MsgInfo *cached = msgcache_get_msg(item->cache,uid);
852 guint have_size = get_size_with_lfs(msginfo);
853 debug_print("message %d has been already %scached (%d/%d).\n", uid,
854 have_size == cached->size ? "fully ":"",
855 have_size, cached? (int)cached->size : -1);
857 if (cached && (cached->size == have_size || !body)) {
858 procmsg_msginfo_free(cached);
859 procmsg_msginfo_free(msginfo);
862 procmsg_msginfo_free(cached);
863 procmsg_msginfo_free(msginfo);
867 session = imap_session_get(folder);
873 debug_print("IMAP fetching messages\n");
874 ok = imap_select(session, IMAP_FOLDER(folder), item->path,
875 NULL, NULL, NULL, NULL, FALSE);
876 if (ok != IMAP_SUCCESS) {
877 g_warning("can't select mailbox %s\n", item->path);
882 debug_print("getting message %d...\n", uid);
883 ok = imap_cmd_fetch(session, (guint32)uid, filename, headers, body);
885 if (ok != IMAP_SUCCESS) {
886 g_warning("can't fetch message %d\n", uid);
894 static gint imap_add_msg(Folder *folder, FolderItem *dest,
895 const gchar *file, MsgFlags *flags)
899 MsgFileInfo fileinfo;
901 g_return_val_if_fail(file != NULL, -1);
903 fileinfo.msginfo = NULL;
904 fileinfo.file = (gchar *)file;
905 fileinfo.flags = flags;
906 file_list.data = &fileinfo;
907 file_list.next = NULL;
909 ret = imap_add_msgs(folder, dest, &file_list, NULL);
913 static gint imap_add_msgs(Folder *folder, FolderItem *dest, GSList *file_list,
917 IMAPSession *session;
918 guint32 last_uid = 0;
920 MsgFileInfo *fileinfo;
924 g_return_val_if_fail(folder != NULL, -1);
925 g_return_val_if_fail(dest != NULL, -1);
926 g_return_val_if_fail(file_list != NULL, -1);
928 MUTEX_TRYLOCK_OR_RETURN_VAL(-1);
930 session = imap_session_get(folder);
935 destdir = imap_get_real_path(IMAP_FOLDER(folder), dest->path);
937 for (cur = file_list; cur != NULL; cur = cur->next) {
938 IMAPFlags iflags = 0;
941 fileinfo = (MsgFileInfo *)cur->data;
943 if (fileinfo->flags) {
944 if (MSG_IS_MARKED(*fileinfo->flags))
945 iflags |= IMAP_FLAG_FLAGGED;
946 if (MSG_IS_REPLIED(*fileinfo->flags))
947 iflags |= IMAP_FLAG_ANSWERED;
948 if (!MSG_IS_UNREAD(*fileinfo->flags))
949 iflags |= IMAP_FLAG_SEEN;
952 if (dest->stype == F_OUTBOX ||
953 dest->stype == F_QUEUE ||
954 dest->stype == F_DRAFT ||
955 dest->stype == F_TRASH)
956 iflags |= IMAP_FLAG_SEEN;
958 ok = imap_cmd_append(session, destdir, fileinfo->file, iflags,
961 if (ok != IMAP_SUCCESS) {
962 g_warning("can't append message %s\n", fileinfo->file);
968 if (relation != NULL)
969 g_relation_insert(relation, fileinfo->msginfo != NULL ?
970 (gpointer) fileinfo->msginfo : (gpointer) fileinfo,
971 GINT_TO_POINTER(dest->last_num + 1));
972 if (last_uid < new_uid)
982 static gint imap_do_copy_msgs(Folder *folder, FolderItem *dest,
983 MsgInfoList *msglist, GRelation *relation)
987 GSList *seq_list, *cur;
989 IMAPSession *session;
990 gint ok = IMAP_SUCCESS;
991 GRelation *uid_mapping;
994 g_return_val_if_fail(folder != NULL, -1);
995 g_return_val_if_fail(dest != NULL, -1);
996 g_return_val_if_fail(msglist != NULL, -1);
998 MUTEX_TRYLOCK_OR_RETURN_VAL(-1);
1000 session = imap_session_get(folder);
1006 msginfo = (MsgInfo *)msglist->data;
1008 src = msginfo->folder;
1010 g_warning("the src folder is identical to the dest.\n");
1015 ok = imap_select(session, IMAP_FOLDER(folder), msginfo->folder->path,
1016 NULL, NULL, NULL, NULL, FALSE);
1017 if (ok != IMAP_SUCCESS) {
1022 destdir = imap_get_real_path(IMAP_FOLDER(folder), dest->path);
1023 seq_list = imap_get_lep_set_from_msglist(msglist);
1024 uid_mapping = g_relation_new(2);
1025 g_relation_index(uid_mapping, 0, g_direct_hash, g_direct_equal);
1027 for (cur = seq_list; cur != NULL; cur = g_slist_next(cur)) {
1028 struct mailimap_set * seq_set;
1030 seq_set = cur->data;
1032 debug_print("Copying message %s%c[%s] to %s ...\n",
1033 src->path, G_DIR_SEPARATOR,
1036 ok = imap_cmd_copy(session, seq_set, destdir, uid_mapping);
1037 if (ok != IMAP_SUCCESS) {
1038 g_relation_destroy(uid_mapping);
1039 imap_lep_set_free(seq_list);
1044 for (cur = msglist; cur != NULL; cur = g_slist_next(cur)) {
1045 MsgInfo *msginfo = (MsgInfo *)cur->data;
1048 tuples = g_relation_select(uid_mapping,
1049 GINT_TO_POINTER(msginfo->msgnum),
1051 if (tuples->len > 0) {
1052 gint num = GPOINTER_TO_INT(g_tuples_index(tuples, 0, 1));
1053 g_relation_insert(relation, msginfo,
1054 GPOINTER_TO_INT(num));
1058 g_relation_insert(relation, msginfo,
1059 GPOINTER_TO_INT(0));
1060 g_tuples_destroy(tuples);
1063 g_relation_destroy(uid_mapping);
1064 imap_lep_set_free(seq_list);
1068 IMAP_FOLDER_ITEM(dest)->lastuid = 0;
1069 IMAP_FOLDER_ITEM(dest)->uid_next = 0;
1070 g_slist_free(IMAP_FOLDER_ITEM(dest)->uid_list);
1071 IMAP_FOLDER_ITEM(dest)->uid_list = NULL;
1075 if (ok == IMAP_SUCCESS)
1081 static gint imap_copy_msg(Folder *folder, FolderItem *dest, MsgInfo *msginfo)
1085 g_return_val_if_fail(msginfo != NULL, -1);
1087 msglist.data = msginfo;
1088 msglist.next = NULL;
1090 return imap_copy_msgs(folder, dest, &msglist, NULL);
1093 static gint imap_copy_msgs(Folder *folder, FolderItem *dest,
1094 MsgInfoList *msglist, GRelation *relation)
1100 g_return_val_if_fail(folder != NULL, -1);
1101 g_return_val_if_fail(dest != NULL, -1);
1102 g_return_val_if_fail(msglist != NULL, -1);
1104 msginfo = (MsgInfo *)msglist->data;
1105 g_return_val_if_fail(msginfo->folder != NULL, -1);
1107 if (folder == msginfo->folder->folder) {
1108 ret = imap_do_copy_msgs(folder, dest, msglist, relation);
1112 file_list = procmsg_get_message_file_list(msglist);
1113 g_return_val_if_fail(file_list != NULL, -1);
1115 ret = imap_add_msgs(folder, dest, file_list, relation);
1117 procmsg_message_file_list_free(file_list);
1123 static gint imap_do_remove_msgs(Folder *folder, FolderItem *dest,
1124 MsgInfoList *msglist, GRelation *relation)
1127 GSList *seq_list = NULL, *cur;
1129 IMAPSession *session;
1130 gint ok = IMAP_SUCCESS;
1131 GRelation *uid_mapping;
1133 g_return_val_if_fail(folder != NULL, -1);
1134 g_return_val_if_fail(dest != NULL, -1);
1135 g_return_val_if_fail(msglist != NULL, -1);
1137 MUTEX_TRYLOCK_OR_RETURN_VAL(-1);
1139 session = imap_session_get(folder);
1144 msginfo = (MsgInfo *)msglist->data;
1146 ok = imap_select(session, IMAP_FOLDER(folder), msginfo->folder->path,
1147 NULL, NULL, NULL, NULL, FALSE);
1148 if (ok != IMAP_SUCCESS) {
1153 destdir = imap_get_real_path(IMAP_FOLDER(folder), dest->path);
1154 for (cur = msglist; cur; cur = cur->next) {
1155 msginfo = (MsgInfo *)cur->data;
1156 seq_list = g_slist_append(seq_list, GINT_TO_POINTER(msginfo->msgnum));
1159 uid_mapping = g_relation_new(2);
1160 g_relation_index(uid_mapping, 0, g_direct_hash, g_direct_equal);
1162 ok = imap_set_message_flags
1163 (IMAP_SESSION(REMOTE_FOLDER(folder)->session),
1164 seq_list, IMAP_FLAG_DELETED, TRUE);
1165 if (ok != IMAP_SUCCESS) {
1166 log_warning(_("can't set deleted flags\n"));
1170 ok = imap_cmd_expunge(session);
1171 if (ok != IMAP_SUCCESS) {
1172 log_warning(_("can't expunge\n"));
1177 g_relation_destroy(uid_mapping);
1178 g_slist_free(seq_list);
1183 if (ok == IMAP_SUCCESS)
1189 static gint imap_remove_msgs(Folder *folder, FolderItem *dest,
1190 MsgInfoList *msglist, GRelation *relation)
1194 g_return_val_if_fail(folder != NULL, -1);
1195 g_return_val_if_fail(dest != NULL, -1);
1196 if (msglist == NULL)
1199 msginfo = (MsgInfo *)msglist->data;
1200 g_return_val_if_fail(msginfo->folder != NULL, -1);
1202 return imap_do_remove_msgs(folder, dest, msglist, relation);
1205 static gint imap_remove_all_msg(Folder *folder, FolderItem *item)
1207 GSList *list = folder_item_get_msg_list(item);
1208 gint res = imap_remove_msgs(folder, item, list, NULL);
1213 static gboolean imap_is_msg_changed(Folder *folder, FolderItem *item,
1216 /* TODO: properly implement this method */
1220 static gint imap_close(Folder *folder, FolderItem *item)
1225 static gint imap_scan_tree(Folder *folder)
1227 FolderItem *item = NULL;
1228 IMAPSession *session;
1229 gchar *root_folder = NULL;
1231 g_return_val_if_fail(folder != NULL, -1);
1232 g_return_val_if_fail(folder->account != NULL, -1);
1234 MUTEX_TRYLOCK_OR_RETURN_VAL(-1);
1236 session = imap_session_get(folder);
1238 if (!folder->node) {
1239 folder_tree_destroy(folder);
1240 item = folder_item_new(folder, folder->name, NULL);
1241 item->folder = folder;
1242 folder->node = item->node = g_node_new(item);
1248 if (folder->account->imap_dir && *folder->account->imap_dir) {
1253 Xstrdup_a(root_folder, folder->account->imap_dir, {MUTEX_UNLOCK();return -1;});
1254 extract_quote(root_folder, '"');
1255 subst_char(root_folder,
1256 imap_get_path_separator(IMAP_FOLDER(folder),
1259 strtailchomp(root_folder, '/');
1260 real_path = imap_get_real_path
1261 (IMAP_FOLDER(folder), root_folder);
1262 debug_print("IMAP root directory: %s\n", real_path);
1264 /* check if root directory exist */
1266 r = imap_threaded_list(session->folder, "", real_path,
1268 if ((r != MAILIMAP_NO_ERROR) || (clist_count(lep_list) == 0)) {
1269 if (!folder->node) {
1270 item = folder_item_new(folder, folder->name, NULL);
1271 item->folder = folder;
1272 folder->node = item->node = g_node_new(item);
1278 mailimap_list_result_free(lep_list);
1284 item = FOLDER_ITEM(folder->node->data);
1285 if (!item || ((item->path || root_folder) &&
1286 strcmp2(item->path, root_folder) != 0)) {
1287 folder_tree_destroy(folder);
1288 item = folder_item_new(folder, folder->name, root_folder);
1289 item->folder = folder;
1290 folder->node = item->node = g_node_new(item);
1293 imap_scan_tree_recursive(session, FOLDER_ITEM(folder->node->data));
1294 imap_create_missing_folders(folder);
1300 static gint imap_scan_tree_recursive(IMAPSession *session, FolderItem *item)
1303 IMAPFolder *imapfolder;
1304 FolderItem *new_item;
1305 GSList *item_list, *cur;
1308 gchar *wildcard_path;
1314 g_return_val_if_fail(item != NULL, -1);
1315 g_return_val_if_fail(item->folder != NULL, -1);
1316 g_return_val_if_fail(item->no_sub == FALSE, -1);
1318 folder = item->folder;
1319 imapfolder = IMAP_FOLDER(folder);
1321 separator = imap_get_path_separator(imapfolder, item->path);
1323 if (folder->ui_func)
1324 folder->ui_func(folder, item, folder->ui_func_data);
1327 wildcard[0] = separator;
1330 real_path = imap_get_real_path(imapfolder, item->path);
1334 real_path = g_strdup("");
1337 Xstrcat_a(wildcard_path, real_path, wildcard,
1338 {g_free(real_path); return IMAP_ERROR;});
1340 r = imap_threaded_list(folder, "", wildcard_path, &lep_list);
1341 if (r != MAILIMAP_NO_ERROR) {
1345 item_list = imap_list_from_lep(imapfolder,
1346 lep_list, real_path);
1347 mailimap_list_result_free(lep_list);
1352 node = item->node->children;
1353 while (node != NULL) {
1354 FolderItem *old_item = FOLDER_ITEM(node->data);
1355 GNode *next = node->next;
1358 for (cur = item_list; cur != NULL; cur = cur->next) {
1359 FolderItem *cur_item = FOLDER_ITEM(cur->data);
1360 if (!strcmp2(old_item->path, cur_item->path)) {
1361 new_item = cur_item;
1366 debug_print("folder '%s' not found. removing...\n",
1368 folder_item_remove(old_item);
1370 old_item->no_sub = new_item->no_sub;
1371 old_item->no_select = new_item->no_select;
1372 if (old_item->no_sub == TRUE && node->children) {
1373 debug_print("folder '%s' doesn't have "
1374 "subfolders. removing...\n",
1376 folder_item_remove_children(old_item);
1383 for (cur = item_list; cur != NULL; cur = cur->next) {
1384 FolderItem *cur_item = FOLDER_ITEM(cur->data);
1386 for (node = item->node->children; node != NULL;
1387 node = node->next) {
1388 if (!strcmp2(FOLDER_ITEM(node->data)->path,
1390 new_item = FOLDER_ITEM(node->data);
1391 folder_item_destroy(cur_item);
1397 new_item = cur_item;
1398 debug_print("new folder '%s' found.\n", new_item->path);
1399 folder_item_append(item, new_item);
1402 if (!strcmp(new_item->path, "INBOX")) {
1403 new_item->stype = F_INBOX;
1404 folder->inbox = new_item;
1405 } else if (!folder_item_parent(item) || item->stype == F_INBOX) {
1408 base = g_path_get_basename(new_item->path);
1410 if (!folder->outbox && !g_ascii_strcasecmp(base, "Sent")) {
1411 new_item->stype = F_OUTBOX;
1412 folder->outbox = new_item;
1413 } else if (!folder->draft && !g_ascii_strcasecmp(base, "Drafts")) {
1414 new_item->stype = F_DRAFT;
1415 folder->draft = new_item;
1416 } else if (!folder->queue && !g_ascii_strcasecmp(base, "Queue")) {
1417 new_item->stype = F_QUEUE;
1418 folder->queue = new_item;
1419 } else if (!folder->trash && !g_ascii_strcasecmp(base, "Trash")) {
1420 new_item->stype = F_TRASH;
1421 folder->trash = new_item;
1426 if (new_item->no_sub == FALSE)
1427 imap_scan_tree_recursive(session, new_item);
1430 g_slist_free(item_list);
1432 return IMAP_SUCCESS;
1435 static gint imap_create_tree(Folder *folder)
1437 g_return_val_if_fail(folder != NULL, -1);
1438 g_return_val_if_fail(folder->node != NULL, -1);
1439 g_return_val_if_fail(folder->node->data != NULL, -1);
1440 g_return_val_if_fail(folder->account != NULL, -1);
1442 imap_scan_tree(folder);
1443 imap_create_missing_folders(folder);
1448 static void imap_create_missing_folders(Folder *folder)
1450 g_return_if_fail(folder != NULL);
1453 folder->inbox = imap_create_special_folder
1454 (folder, F_INBOX, "INBOX");
1456 folder->trash = imap_create_special_folder
1457 (folder, F_TRASH, "Trash");
1460 static FolderItem *imap_create_special_folder(Folder *folder,
1461 SpecialFolderItemType stype,
1465 FolderItem *new_item;
1467 g_return_val_if_fail(folder != NULL, NULL);
1468 g_return_val_if_fail(folder->node != NULL, NULL);
1469 g_return_val_if_fail(folder->node->data != NULL, NULL);
1470 g_return_val_if_fail(folder->account != NULL, NULL);
1471 g_return_val_if_fail(name != NULL, NULL);
1473 item = FOLDER_ITEM(folder->node->data);
1474 new_item = imap_create_folder(folder, item, name);
1477 g_warning("Can't create '%s'\n", name);
1478 if (!folder->inbox) return NULL;
1480 new_item = imap_create_folder(folder, folder->inbox, name);
1482 g_warning("Can't create '%s' under INBOX\n", name);
1484 new_item->stype = stype;
1486 new_item->stype = stype;
1491 static gchar *imap_folder_get_path(Folder *folder)
1495 g_return_val_if_fail(folder != NULL, NULL);
1496 g_return_val_if_fail(folder->account != NULL, NULL);
1498 folder_path = g_strconcat(get_imap_cache_dir(),
1500 folder->account->recv_server,
1502 folder->account->userid,
1508 static gchar *imap_item_get_path(Folder *folder, FolderItem *item)
1510 gchar *folder_path, *path;
1512 g_return_val_if_fail(folder != NULL, NULL);
1513 g_return_val_if_fail(item != NULL, NULL);
1514 folder_path = imap_folder_get_path(folder);
1516 g_return_val_if_fail(folder_path != NULL, NULL);
1517 if (folder_path[0] == G_DIR_SEPARATOR) {
1519 path = g_strconcat(folder_path, G_DIR_SEPARATOR_S,
1522 path = g_strdup(folder_path);
1525 path = g_strconcat(get_home_dir(), G_DIR_SEPARATOR_S,
1526 folder_path, G_DIR_SEPARATOR_S,
1529 path = g_strconcat(get_home_dir(), G_DIR_SEPARATOR_S,
1532 g_free(folder_path);
1537 static FolderItem *imap_create_folder(Folder *folder, FolderItem *parent,
1540 gchar *dirpath, *imap_path;
1541 IMAPSession *session;
1542 FolderItem *new_item;
1548 g_return_val_if_fail(folder != NULL, NULL);
1549 g_return_val_if_fail(folder->account != NULL, NULL);
1550 g_return_val_if_fail(parent != NULL, NULL);
1551 g_return_val_if_fail(name != NULL, NULL);
1553 MUTEX_TRYLOCK_OR_RETURN_VAL(NULL);
1555 session = imap_session_get(folder);
1561 if (!folder_item_parent(parent) && strcmp(name, "INBOX") == 0)
1562 dirpath = g_strdup(name);
1563 else if (parent->path)
1564 dirpath = g_strconcat(parent->path, "/", name, NULL);
1565 else if ((p = strchr(name, '/')) != NULL && *(p + 1) != '\0')
1566 dirpath = g_strdup(name);
1567 else if (folder->account->imap_dir && *folder->account->imap_dir) {
1570 Xstrdup_a(imap_dir, folder->account->imap_dir, {MUTEX_UNLOCK();return NULL;});
1571 strtailchomp(imap_dir, '/');
1572 dirpath = g_strconcat(imap_dir, "/", name, NULL);
1574 dirpath = g_strdup(name);
1576 /* keep trailing directory separator to create a folder that contains
1578 imap_path = imap_utf8_to_modified_utf7(dirpath);
1579 strtailchomp(dirpath, '/');
1580 Xstrdup_a(new_name, name, {
1585 strtailchomp(new_name, '/');
1586 separator = imap_get_path_separator(IMAP_FOLDER(folder), imap_path);
1587 imap_path_separator_subst(imap_path, separator);
1588 subst_char(new_name, '/', separator);
1590 if (strcmp(name, "INBOX") != 0) {
1592 gboolean exist = FALSE;
1596 argbuf = g_ptr_array_new();
1597 r = imap_threaded_list(folder, "", imap_path, &lep_list);
1598 if (r != MAILIMAP_NO_ERROR) {
1599 log_warning(_("can't create mailbox: LIST failed\n"));
1602 ptr_array_free_strings(argbuf);
1603 g_ptr_array_free(argbuf, TRUE);
1608 if (clist_count(lep_list) > 0)
1612 ok = imap_cmd_create(session, imap_path);
1613 if (ok != IMAP_SUCCESS) {
1614 log_warning(_("can't create mailbox\n"));
1623 new_item = folder_item_new(folder, new_name, dirpath);
1624 folder_item_append(parent, new_item);
1628 dirpath = folder_item_get_path(new_item);
1629 if (!is_dir_exist(dirpath))
1630 make_dir_hier(dirpath);
1637 static gint imap_rename_folder(Folder *folder, FolderItem *item,
1642 gchar *real_oldpath;
1643 gchar *real_newpath;
1645 gchar *old_cache_dir;
1646 gchar *new_cache_dir;
1647 IMAPSession *session;
1650 gint exists, recent, unseen;
1651 guint32 uid_validity;
1653 g_return_val_if_fail(folder != NULL, -1);
1654 g_return_val_if_fail(item != NULL, -1);
1655 g_return_val_if_fail(item->path != NULL, -1);
1656 g_return_val_if_fail(name != NULL, -1);
1658 MUTEX_TRYLOCK_OR_RETURN_VAL(-1);
1660 if (strchr(name, imap_get_path_separator(IMAP_FOLDER(folder), item->path)) != NULL) {
1661 g_warning(_("New folder name must not contain the namespace "
1667 session = imap_session_get(folder);
1672 real_oldpath = imap_get_real_path(IMAP_FOLDER(folder), item->path);
1674 g_free(session->mbox);
1675 session->mbox = NULL;
1676 ok = imap_cmd_examine(session, "INBOX",
1677 &exists, &recent, &unseen, &uid_validity, FALSE);
1678 if (ok != IMAP_SUCCESS) {
1679 g_free(real_oldpath);
1684 separator = imap_get_path_separator(IMAP_FOLDER(folder), item->path);
1685 if (strchr(item->path, G_DIR_SEPARATOR)) {
1686 dirpath = g_path_get_dirname(item->path);
1687 newpath = g_strconcat(dirpath, G_DIR_SEPARATOR_S, name, NULL);
1690 newpath = g_strdup(name);
1692 real_newpath = imap_utf8_to_modified_utf7(newpath);
1693 imap_path_separator_subst(real_newpath, separator);
1695 ok = imap_cmd_rename(session, real_oldpath, real_newpath);
1696 if (ok != IMAP_SUCCESS) {
1697 log_warning(_("can't rename mailbox: %s to %s\n"),
1698 real_oldpath, real_newpath);
1699 g_free(real_oldpath);
1701 g_free(real_newpath);
1707 item->name = g_strdup(name);
1709 old_cache_dir = folder_item_get_path(item);
1711 paths[0] = g_strdup(item->path);
1713 g_node_traverse(item->node, G_PRE_ORDER, G_TRAVERSE_ALL, -1,
1714 imap_rename_folder_func, paths);
1716 if (is_dir_exist(old_cache_dir)) {
1717 new_cache_dir = folder_item_get_path(item);
1718 if (rename(old_cache_dir, new_cache_dir) < 0) {
1719 FILE_OP_ERROR(old_cache_dir, "rename");
1721 g_free(new_cache_dir);
1724 g_free(old_cache_dir);
1727 g_free(real_oldpath);
1728 g_free(real_newpath);
1734 static gint imap_remove_folder_real(Folder *folder, FolderItem *item)
1737 IMAPSession *session;
1740 gint exists, recent, unseen;
1741 guint32 uid_validity;
1743 g_return_val_if_fail(folder != NULL, -1);
1744 g_return_val_if_fail(item != NULL, -1);
1745 g_return_val_if_fail(item->path != NULL, -1);
1747 MUTEX_TRYLOCK_OR_RETURN_VAL(-1);
1749 session = imap_session_get(folder);
1754 path = imap_get_real_path(IMAP_FOLDER(folder), item->path);
1756 ok = imap_cmd_examine(session, "INBOX",
1757 &exists, &recent, &unseen, &uid_validity, FALSE);
1758 if (ok != IMAP_SUCCESS) {
1764 ok = imap_cmd_delete(session, path);
1765 if (ok != IMAP_SUCCESS) {
1766 log_warning(_("can't delete mailbox\n"));
1773 cache_dir = folder_item_get_path(item);
1774 if (is_dir_exist(cache_dir) && remove_dir_recursive(cache_dir) < 0)
1775 g_warning("can't remove directory '%s'\n", cache_dir);
1777 folder_item_remove(item);
1783 static gint imap_remove_folder(Folder *folder, FolderItem *item)
1787 g_return_val_if_fail(item != NULL, -1);
1788 g_return_val_if_fail(item->folder != NULL, -1);
1789 g_return_val_if_fail(item->node != NULL, -1);
1791 node = item->node->children;
1792 while (node != NULL) {
1794 if (imap_remove_folder(folder, FOLDER_ITEM(node->data)) < 0)
1798 debug_print("IMAP removing %s\n", item->path);
1800 if (imap_remove_all_msg(folder, item) < 0)
1802 return imap_remove_folder_real(folder, item);
1805 typedef struct _uncached_data {
1806 IMAPSession *session;
1808 MsgNumberList *numlist;
1814 static void *imap_get_uncached_messages_thread(void *data)
1816 uncached_data *stuff = (uncached_data *)data;
1817 IMAPSession *session = stuff->session;
1818 FolderItem *item = stuff->item;
1819 MsgNumberList *numlist = stuff->numlist;
1821 GSList *newlist = NULL;
1822 GSList *llast = NULL;
1823 GSList *seq_list, *cur;
1825 debug_print("uncached_messages\n");
1827 if (session == NULL || item == NULL || item->folder == NULL
1828 || FOLDER_CLASS(item->folder) != &imap_class) {
1833 seq_list = imap_get_lep_set_from_numlist(numlist);
1834 debug_print("get msgs info\n");
1835 for (cur = seq_list; cur != NULL; cur = g_slist_next(cur)) {
1836 struct mailimap_set * imapset;
1842 imapset = cur->data;
1844 r = imap_threaded_fetch_env(session->folder,
1845 imapset, &env_list);
1846 if (r != MAILIMAP_NO_ERROR)
1850 for(i = 0 ; i < carray_count(env_list) ; i ++) {
1851 struct imap_fetch_env_info * info;
1854 info = carray_get(env_list, i);
1855 msginfo = imap_envelope_from_lep(info, item);
1856 msginfo->folder = item;
1858 llast = newlist = g_slist_append(newlist, msginfo);
1860 llast = g_slist_append(llast, msginfo);
1861 llast = llast->next;
1866 imap_fetch_env_free(env_list);
1869 session_set_access_time(SESSION(session));
1875 #define MAX_MSG_NUM 50
1877 static GSList *imap_get_uncached_messages(IMAPSession *session,
1879 MsgNumberList *numlist)
1881 GSList *result = NULL;
1883 uncached_data *data = g_new0(uncached_data, 1);
1888 data->total = g_slist_length(numlist);
1889 debug_print("messages list : %i\n", data->total);
1890 while (cur != NULL) {
1891 GSList * partial_result;
1899 while (count < MAX_MSG_NUM) {
1904 if (newlist == NULL)
1905 llast = newlist = g_slist_append(newlist, p);
1907 llast = g_slist_append(llast, p);
1908 llast = llast->next;
1918 data->session = session;
1920 data->numlist = newlist;
1923 if (prefs_common.work_offline && !imap_gtk_should_override()) {
1929 (GSList *)imap_get_uncached_messages_thread(data);
1933 g_snprintf(buf, sizeof(buf), "%d / %d",
1934 data->cur, data->total);
1935 gtk_progress_bar_set_text
1936 (GTK_PROGRESS_BAR(mainwindow_get_mainwindow()->progressbar), buf);
1937 gtk_progress_bar_set_fraction
1938 (GTK_PROGRESS_BAR(mainwindow_get_mainwindow()->progressbar),
1939 (gfloat)data->cur / (gfloat)data->total);
1940 debug_print("update progress %g\n",
1941 (gfloat)data->cur / (gfloat)data->total);
1944 g_slist_free(newlist);
1946 result = g_slist_concat(result, partial_result);
1950 gtk_progress_bar_set_fraction
1951 (GTK_PROGRESS_BAR(mainwindow_get_mainwindow()->progressbar), 0);
1952 gtk_progress_bar_set_text
1953 (GTK_PROGRESS_BAR(mainwindow_get_mainwindow()->progressbar), "");
1954 statusbar_pop_all();
1959 static void imap_delete_all_cached_messages(FolderItem *item)
1963 g_return_if_fail(item != NULL);
1964 g_return_if_fail(item->folder != NULL);
1965 g_return_if_fail(FOLDER_CLASS(item->folder) == &imap_class);
1967 debug_print("Deleting all cached messages...\n");
1969 dir = folder_item_get_path(item);
1970 if (is_dir_exist(dir))
1971 remove_all_numbered_files(dir);
1974 debug_print("done.\n");
1977 static IMAPNameSpace *imap_find_namespace_from_list(GList *ns_list,
1980 IMAPNameSpace *namespace = NULL;
1981 gchar *tmp_path, *name;
1983 if (!path) path = "";
1985 for (; ns_list != NULL; ns_list = ns_list->next) {
1986 IMAPNameSpace *tmp_ns = ns_list->data;
1988 Xstrcat_a(tmp_path, path, "/", return namespace);
1989 Xstrdup_a(name, tmp_ns->name, return namespace);
1990 if (tmp_ns->separator && tmp_ns->separator != '/') {
1991 subst_char(tmp_path, tmp_ns->separator, '/');
1992 subst_char(name, tmp_ns->separator, '/');
1994 if (strncmp(tmp_path, name, strlen(name)) == 0)
2001 static IMAPNameSpace *imap_find_namespace(IMAPFolder *folder,
2004 IMAPNameSpace *namespace;
2006 g_return_val_if_fail(folder != NULL, NULL);
2008 namespace = imap_find_namespace_from_list(folder->ns_personal, path);
2009 if (namespace) return namespace;
2010 namespace = imap_find_namespace_from_list(folder->ns_others, path);
2011 if (namespace) return namespace;
2012 namespace = imap_find_namespace_from_list(folder->ns_shared, path);
2013 if (namespace) return namespace;
2018 static gchar imap_get_path_separator(IMAPFolder *folder, const gchar *path)
2020 IMAPNameSpace *namespace;
2021 gchar separator = '/';
2023 namespace = imap_find_namespace(folder, path);
2024 if (namespace && namespace->separator)
2025 separator = namespace->separator;
2030 static gchar *imap_get_real_path(IMAPFolder *folder, const gchar *path)
2035 g_return_val_if_fail(folder != NULL, NULL);
2036 g_return_val_if_fail(path != NULL, NULL);
2038 real_path = imap_utf8_to_modified_utf7(path);
2039 separator = imap_get_path_separator(folder, path);
2040 imap_path_separator_subst(real_path, separator);
2045 static gint imap_set_message_flags(IMAPSession *session,
2046 MsgNumberList *numlist,
2054 seq_list = imap_get_lep_set_from_numlist(numlist);
2056 for(cur = seq_list ; cur != NULL ; cur = g_slist_next(cur)) {
2057 struct mailimap_set * imapset;
2059 imapset = cur->data;
2061 ok = imap_cmd_store(session, imapset,
2065 imap_lep_set_free(seq_list);
2067 return IMAP_SUCCESS;
2070 typedef struct _select_data {
2071 IMAPSession *session;
2076 guint32 *uid_validity;
2080 static gint imap_select(IMAPSession *session, IMAPFolder *folder,
2082 gint *exists, gint *recent, gint *unseen,
2083 guint32 *uid_validity, gboolean block)
2087 gint exists_, recent_, unseen_;
2088 guint32 uid_validity_;
2090 if (!exists || !recent || !unseen || !uid_validity) {
2091 if (session->mbox && strcmp(session->mbox, path) == 0)
2092 return IMAP_SUCCESS;
2096 uid_validity = &uid_validity_;
2099 g_free(session->mbox);
2100 session->mbox = NULL;
2102 real_path = imap_get_real_path(folder, path);
2104 ok = imap_cmd_select(session, real_path,
2105 exists, recent, unseen, uid_validity, block);
2106 if (ok != IMAP_SUCCESS)
2107 log_warning(_("can't select folder: %s\n"), real_path);
2109 session->mbox = g_strdup(path);
2110 session->folder_content_changed = FALSE;
2117 static gint imap_status(IMAPSession *session, IMAPFolder *folder,
2119 gint *messages, gint *recent,
2120 guint32 *uid_next, guint32 *uid_validity,
2121 gint *unseen, gboolean block)
2125 struct mailimap_mailbox_data_status * data_status;
2128 r = imap_threaded_status(FOLDER(folder), path, &data_status);
2129 if (r != MAILIMAP_NO_ERROR)
2132 if (data_status->st_info_list == NULL) {
2133 mailimap_mailbox_data_status_free(data_status);
2138 for(iter = clist_begin(data_status->st_info_list) ; iter != NULL ;
2139 iter = clist_next(iter)) {
2140 struct mailimap_status_info * info;
2142 info = clist_content(iter);
2143 switch (info->st_att) {
2144 case MAILIMAP_STATUS_ATT_MESSAGES:
2145 * messages = info->st_value;
2146 got_values |= 1 << 0;
2149 case MAILIMAP_STATUS_ATT_RECENT:
2150 * recent = info->st_value;
2151 got_values |= 1 << 1;
2154 case MAILIMAP_STATUS_ATT_UIDNEXT:
2155 * uid_next = info->st_value;
2156 got_values |= 1 << 2;
2159 case MAILIMAP_STATUS_ATT_UIDVALIDITY:
2160 * uid_validity = info->st_value;
2161 got_values |= 1 << 3;
2164 case MAILIMAP_STATUS_ATT_UNSEEN:
2165 * unseen = info->st_value;
2166 got_values |= 1 << 4;
2170 mailimap_mailbox_data_status_free(data_status);
2172 if (got_values != ((1 << 4) + (1 << 3) +
2173 (1 << 2) + (1 << 1) + (1 << 0)))
2176 return IMAP_SUCCESS;
2179 static void imap_free_capabilities(IMAPSession *session)
2181 g_strfreev(session->capability);
2182 session->capability = NULL;
2185 /* low-level IMAP4rev1 commands */
2188 static gint imap_cmd_authenticate(IMAPSession *session, const gchar *user,
2189 const gchar *pass, IMAPAuthType type)
2196 gchar hexdigest[33];
2200 auth_type = "CRAM-MD5";
2202 imap_gen_send(session, "AUTHENTICATE %s", auth_type);
2203 ok = imap_gen_recv(session, &buf);
2204 if (ok != IMAP_SUCCESS || buf[0] != '+' || buf[1] != ' ') {
2209 challenge = g_malloc(strlen(buf + 2) + 1);
2210 challenge_len = base64_decode(challenge, buf + 2, -1);
2211 challenge[challenge_len] = '\0';
2213 log_print("IMAP< [Decoded: %s]\n", challenge);
2215 md5_hex_hmac(hexdigest, challenge, challenge_len, pass, strlen(pass));
2218 response = g_strdup_printf("%s %s", user, hexdigest);
2219 log_print("IMAP> [Encoded: %s]\n", response);
2220 response64 = g_malloc((strlen(response) + 3) * 2 + 1);
2221 base64_encode(response64, response, strlen(response));
2224 log_print("IMAP> %s\n", response64);
2225 sock_puts(SESSION(session)->sock, response64);
2226 ok = imap_cmd_ok(session, NULL);
2227 if (ok != IMAP_SUCCESS)
2228 log_warning(_("IMAP4 authentication failed.\n"));
2234 static gint imap_cmd_login(IMAPSession *session,
2235 const gchar *user, const gchar *pass)
2240 r = imap_threaded_login(session->folder, user, pass);
2241 if (r != MAILIMAP_NO_ERROR)
2249 static gint imap_cmd_logout(IMAPSession *session)
2251 imap_threaded_disconnect(session->folder);
2253 return IMAP_SUCCESS;
2256 static gint imap_cmd_noop(IMAPSession *session)
2260 r = imap_threaded_noop(session->folder);
2261 if (r != MAILIMAP_NO_ERROR)
2264 return IMAP_SUCCESS;
2268 static gint imap_cmd_starttls(IMAPSession *session)
2272 r = imap_threaded_starttls(session->folder);
2273 if (r != MAILIMAP_NO_ERROR)
2276 return IMAP_SUCCESS;
2280 static gint imap_cmd_select(IMAPSession *session, const gchar *folder,
2281 gint *exists, gint *recent, gint *unseen,
2282 guint32 *uid_validity, gboolean block)
2286 r = imap_threaded_select(session->folder, folder,
2287 exists, recent, unseen, uid_validity);
2288 if (r != MAILIMAP_NO_ERROR) {
2292 return IMAP_SUCCESS;
2295 static gint imap_cmd_examine(IMAPSession *session, const gchar *folder,
2296 gint *exists, gint *recent, gint *unseen,
2297 guint32 *uid_validity, gboolean block)
2301 r = imap_threaded_examine(session->folder, folder,
2302 exists, recent, unseen, uid_validity);
2303 if (r != MAILIMAP_NO_ERROR) {
2307 return IMAP_SUCCESS;
2310 static gint imap_cmd_create(IMAPSession *session, const gchar *folder)
2314 r = imap_threaded_create(session->folder, folder);
2315 if (r != MAILIMAP_NO_ERROR)
2318 return IMAP_SUCCESS;
2321 static gint imap_cmd_rename(IMAPSession *session, const gchar *old_folder,
2322 const gchar *new_folder)
2326 r = imap_threaded_rename(session->folder, old_folder,
2328 if (r != MAILIMAP_NO_ERROR)
2331 return IMAP_SUCCESS;
2334 static gint imap_cmd_delete(IMAPSession *session, const gchar *folder)
2338 r = imap_threaded_delete(session->folder, folder);
2339 if (r != MAILIMAP_NO_ERROR)
2342 return IMAP_SUCCESS;
2345 typedef struct _fetch_data {
2346 IMAPSession *session;
2348 const gchar *filename;
2354 static void *imap_cmd_fetch_thread(void *data)
2356 fetch_data *stuff = (fetch_data *)data;
2357 IMAPSession *session = stuff->session;
2358 guint32 uid = stuff->uid;
2359 const gchar *filename = stuff->filename;
2363 r = imap_threaded_fetch_content(session->folder,
2367 r = imap_threaded_fetch_content(session->folder,
2370 if (r != MAILIMAP_NO_ERROR)
2371 return GINT_TO_POINTER(IMAP_ERROR);
2373 return GINT_TO_POINTER(IMAP_SUCCESS);
2376 static gint imap_cmd_fetch(IMAPSession *session, guint32 uid,
2377 const gchar *filename, gboolean headers,
2380 fetch_data *data = g_new0(fetch_data, 1);
2383 data->session = session;
2385 data->filename = filename;
2386 data->headers = headers;
2389 if (prefs_common.work_offline && !imap_gtk_should_override()) {
2394 result = GPOINTER_TO_INT(imap_cmd_fetch_thread(data));
2400 static gint imap_cmd_append(IMAPSession *session, const gchar *destfolder,
2401 const gchar *file, IMAPFlags flags,
2404 struct mailimap_flag_list * flag_list;
2407 g_return_val_if_fail(file != NULL, IMAP_ERROR);
2409 flag_list = imap_flag_to_lep(flags);
2410 r = imap_threaded_append(session->folder, destfolder,
2413 if (new_uid != NULL)
2416 if (r != MAILIMAP_NO_ERROR)
2419 return IMAP_SUCCESS;
2422 static gint imap_cmd_copy(IMAPSession *session, struct mailimap_set * set,
2423 const gchar *destfolder, GRelation *uid_mapping)
2427 g_return_val_if_fail(session != NULL, IMAP_ERROR);
2428 g_return_val_if_fail(set != NULL, IMAP_ERROR);
2429 g_return_val_if_fail(destfolder != NULL, IMAP_ERROR);
2431 r = imap_threaded_copy(session->folder, set, destfolder);
2432 if (r != MAILIMAP_NO_ERROR)
2435 return IMAP_SUCCESS;
2438 static gint imap_cmd_store(IMAPSession *session, struct mailimap_set * set,
2439 IMAPFlags flags, int do_add)
2442 struct mailimap_flag_list * flag_list;
2443 struct mailimap_store_att_flags * store_att_flags;
2445 flag_list = imap_flag_to_lep(flags);
2449 mailimap_store_att_flags_new_add_flags_silent(flag_list);
2452 mailimap_store_att_flags_new_remove_flags_silent(flag_list);
2454 r = imap_threaded_store(session->folder, set, store_att_flags);
2455 if (r != MAILIMAP_NO_ERROR)
2458 return IMAP_SUCCESS;
2461 static gint imap_cmd_expunge(IMAPSession *session)
2465 if (prefs_common.work_offline && !imap_gtk_should_override()) {
2469 r = imap_threaded_expunge(session->folder);
2470 if (r != MAILIMAP_NO_ERROR)
2473 return IMAP_SUCCESS;
2476 static void imap_path_separator_subst(gchar *str, gchar separator)
2479 gboolean in_escape = FALSE;
2481 if (!separator || separator == '/') return;
2483 for (p = str; *p != '\0'; p++) {
2484 if (*p == '/' && !in_escape)
2486 else if (*p == '&' && *(p + 1) != '-' && !in_escape)
2488 else if (*p == '-' && in_escape)
2493 static gchar *imap_modified_utf7_to_utf8(const gchar *mutf7_str)
2495 static iconv_t cd = (iconv_t)-1;
2496 static gboolean iconv_ok = TRUE;
2499 size_t norm_utf7_len;
2501 gchar *to_str, *to_p;
2503 gboolean in_escape = FALSE;
2505 if (!iconv_ok) return g_strdup(mutf7_str);
2507 if (cd == (iconv_t)-1) {
2508 cd = iconv_open(CS_INTERNAL, CS_UTF_7);
2509 if (cd == (iconv_t)-1) {
2510 g_warning("iconv cannot convert UTF-7 to %s\n",
2513 return g_strdup(mutf7_str);
2517 /* modified UTF-7 to normal UTF-7 conversion */
2518 norm_utf7 = g_string_new(NULL);
2520 for (p = mutf7_str; *p != '\0'; p++) {
2521 /* replace: '&' -> '+',
2523 escaped ',' -> '/' */
2524 if (!in_escape && *p == '&') {
2525 if (*(p + 1) != '-') {
2526 g_string_append_c(norm_utf7, '+');
2529 g_string_append_c(norm_utf7, '&');
2532 } else if (in_escape && *p == ',') {
2533 g_string_append_c(norm_utf7, '/');
2534 } else if (in_escape && *p == '-') {
2535 g_string_append_c(norm_utf7, '-');
2538 g_string_append_c(norm_utf7, *p);
2542 norm_utf7_p = norm_utf7->str;
2543 norm_utf7_len = norm_utf7->len;
2544 to_len = strlen(mutf7_str) * 5;
2545 to_p = to_str = g_malloc(to_len + 1);
2547 if (iconv(cd, (ICONV_CONST gchar **)&norm_utf7_p, &norm_utf7_len,
2548 &to_p, &to_len) == -1) {
2549 g_warning(_("iconv cannot convert UTF-7 to %s\n"),
2550 conv_get_locale_charset_str());
2551 g_string_free(norm_utf7, TRUE);
2553 return g_strdup(mutf7_str);
2556 /* second iconv() call for flushing */
2557 iconv(cd, NULL, NULL, &to_p, &to_len);
2558 g_string_free(norm_utf7, TRUE);
2564 static gchar *imap_utf8_to_modified_utf7(const gchar *from)
2566 static iconv_t cd = (iconv_t)-1;
2567 static gboolean iconv_ok = TRUE;
2568 gchar *norm_utf7, *norm_utf7_p;
2569 size_t from_len, norm_utf7_len;
2571 gchar *from_tmp, *to, *p;
2572 gboolean in_escape = FALSE;
2574 if (!iconv_ok) return g_strdup(from);
2576 if (cd == (iconv_t)-1) {
2577 cd = iconv_open(CS_UTF_7, CS_INTERNAL);
2578 if (cd == (iconv_t)-1) {
2579 g_warning(_("iconv cannot convert %s to UTF-7\n"),
2582 return g_strdup(from);
2586 /* UTF-8 to normal UTF-7 conversion */
2587 Xstrdup_a(from_tmp, from, return g_strdup(from));
2588 from_len = strlen(from);
2589 norm_utf7_len = from_len * 5;
2590 Xalloca(norm_utf7, norm_utf7_len + 1, return g_strdup(from));
2591 norm_utf7_p = norm_utf7;
2593 #define IS_PRINT(ch) (isprint(ch) && IS_ASCII(ch))
2595 while (from_len > 0) {
2596 if (*from_tmp == '+') {
2597 *norm_utf7_p++ = '+';
2598 *norm_utf7_p++ = '-';
2602 } else if (IS_PRINT(*(guchar *)from_tmp)) {
2603 /* printable ascii char */
2604 *norm_utf7_p = *from_tmp;
2610 size_t conv_len = 0;
2612 /* unprintable char: convert to UTF-7 */
2614 while (!IS_PRINT(*(guchar *)p) && conv_len < from_len) {
2615 conv_len += g_utf8_skip[*(guchar *)p];
2616 p += g_utf8_skip[*(guchar *)p];
2619 from_len -= conv_len;
2620 if (iconv(cd, (ICONV_CONST gchar **)&from_tmp,
2622 &norm_utf7_p, &norm_utf7_len) == -1) {
2623 g_warning(_("iconv cannot convert UTF-8 to UTF-7\n"));
2624 return g_strdup(from);
2627 /* second iconv() call for flushing */
2628 iconv(cd, NULL, NULL, &norm_utf7_p, &norm_utf7_len);
2634 *norm_utf7_p = '\0';
2635 to_str = g_string_new(NULL);
2636 for (p = norm_utf7; p < norm_utf7_p; p++) {
2637 /* replace: '&' -> "&-",
2640 BASE64 '/' -> ',' */
2641 if (!in_escape && *p == '&') {
2642 g_string_append(to_str, "&-");
2643 } else if (!in_escape && *p == '+') {
2644 if (*(p + 1) == '-') {
2645 g_string_append_c(to_str, '+');
2648 g_string_append_c(to_str, '&');
2651 } else if (in_escape && *p == '/') {
2652 g_string_append_c(to_str, ',');
2653 } else if (in_escape && *p == '-') {
2654 g_string_append_c(to_str, '-');
2657 g_string_append_c(to_str, *p);
2663 g_string_append_c(to_str, '-');
2667 g_string_free(to_str, FALSE);
2672 static gboolean imap_rename_folder_func(GNode *node, gpointer data)
2674 FolderItem *item = node->data;
2675 gchar **paths = data;
2676 const gchar *oldpath = paths[0];
2677 const gchar *newpath = paths[1];
2679 gchar *new_itempath;
2682 oldpathlen = strlen(oldpath);
2683 if (strncmp(oldpath, item->path, oldpathlen) != 0) {
2684 g_warning("path doesn't match: %s, %s\n", oldpath, item->path);
2688 base = item->path + oldpathlen;
2689 while (*base == G_DIR_SEPARATOR) base++;
2691 new_itempath = g_strdup(newpath);
2693 new_itempath = g_strconcat(newpath, G_DIR_SEPARATOR_S, base,
2696 item->path = new_itempath;
2701 typedef struct _get_list_uid_data {
2703 IMAPFolderItem *item;
2704 GSList **msgnum_list;
2706 } get_list_uid_data;
2708 static void *get_list_of_uids_thread(void *data)
2710 get_list_uid_data *stuff = (get_list_uid_data *)data;
2711 Folder *folder = stuff->folder;
2712 IMAPFolderItem *item = stuff->item;
2713 GSList **msgnum_list = stuff->msgnum_list;
2714 gint ok, nummsgs = 0, lastuid_old;
2715 IMAPSession *session;
2716 GSList *uidlist, *elem;
2717 struct mailimap_set * set;
2718 clist * lep_uidlist;
2721 session = imap_session_get(folder);
2722 if (session == NULL) {
2724 return GINT_TO_POINTER(-1);
2727 ok = imap_select(session, IMAP_FOLDER(folder), item->item.path,
2728 NULL, NULL, NULL, NULL, TRUE);
2729 if (ok != IMAP_SUCCESS) {
2731 return GINT_TO_POINTER(-1);
2736 set = mailimap_set_new_interval(item->lastuid + 1, 0);
2737 r = imap_threaded_search(folder, IMAP_SEARCH_TYPE_SIMPLE, set,
2739 if (r == MAILIMAP_NO_ERROR) {
2740 GSList * fetchuid_list;
2743 imap_uid_list_from_lep(lep_uidlist);
2744 uidlist = g_slist_concat(fetchuid_list, uidlist);
2747 GSList * fetchuid_list;
2748 carray * lep_uidtab;
2750 r = imap_threaded_fetch_uid(folder, item->lastuid + 1,
2752 if (r == MAILIMAP_NO_ERROR) {
2754 imap_uid_list_from_lep_tab(lep_uidtab);
2755 uidlist = g_slist_concat(fetchuid_list, uidlist);
2759 lastuid_old = item->lastuid;
2760 *msgnum_list = g_slist_copy(item->uid_list);
2761 nummsgs = g_slist_length(*msgnum_list);
2762 debug_print("Got %d uids from cache\n", g_slist_length(item->uid_list));
2764 for (elem = uidlist; elem != NULL; elem = g_slist_next(elem)) {
2767 msgnum = GPOINTER_TO_INT(elem->data);
2768 if (msgnum > lastuid_old) {
2769 *msgnum_list = g_slist_prepend(*msgnum_list, GINT_TO_POINTER(msgnum));
2770 item->uid_list = g_slist_prepend(item->uid_list, GINT_TO_POINTER(msgnum));
2773 if(msgnum > item->lastuid)
2774 item->lastuid = msgnum;
2777 g_slist_free(uidlist);
2780 return GINT_TO_POINTER(nummsgs);
2783 static gint get_list_of_uids(Folder *folder, IMAPFolderItem *item, GSList **msgnum_list)
2786 get_list_uid_data *data = g_new0(get_list_uid_data, 1);
2788 data->folder = folder;
2790 data->msgnum_list = msgnum_list;
2792 if (prefs_common.work_offline && !imap_gtk_should_override()) {
2797 result = GPOINTER_TO_INT(get_list_of_uids_thread(data));
2803 gint imap_get_num_list(Folder *folder, FolderItem *_item, GSList **msgnum_list, gboolean *old_uids_valid)
2805 IMAPFolderItem *item = (IMAPFolderItem *)_item;
2806 IMAPSession *session;
2807 gint ok, nummsgs = 0, exists, recent, uid_val, uid_next, unseen;
2808 GSList *uidlist = NULL;
2810 gboolean selected_folder;
2812 debug_print("get_num_list\n");
2814 g_return_val_if_fail(folder != NULL, -1);
2815 g_return_val_if_fail(item != NULL, -1);
2816 g_return_val_if_fail(item->item.path != NULL, -1);
2817 g_return_val_if_fail(FOLDER_CLASS(folder) == &imap_class, -1);
2818 g_return_val_if_fail(folder->account != NULL, -1);
2820 MUTEX_TRYLOCK_OR_RETURN_VAL(-1);
2822 session = imap_session_get(folder);
2823 g_return_val_if_fail(session != NULL, -1);
2825 selected_folder = (session->mbox != NULL) &&
2826 (!strcmp(session->mbox, item->item.path));
2827 if (selected_folder) {
2828 ok = imap_cmd_noop(session);
2829 if (ok != IMAP_SUCCESS) {
2833 exists = session->exists;
2835 *old_uids_valid = TRUE;
2837 ok = imap_status(session, IMAP_FOLDER(folder), item->item.path,
2838 &exists, &recent, &uid_next, &uid_val, &unseen, FALSE);
2839 if (ok != IMAP_SUCCESS) {
2843 if(item->item.mtime == uid_val)
2844 *old_uids_valid = TRUE;
2846 *old_uids_valid = FALSE;
2848 debug_print("Freeing imap uid cache\n");
2850 g_slist_free(item->uid_list);
2851 item->uid_list = NULL;
2853 item->item.mtime = uid_val;
2855 imap_delete_all_cached_messages((FolderItem *)item);
2859 if (!selected_folder)
2860 item->uid_next = uid_next;
2862 /* If old uid_next matches new uid_next we can be sure no message
2863 was added to the folder */
2864 if (( selected_folder && !session->folder_content_changed) ||
2865 (!selected_folder && uid_next == item->uid_next)) {
2866 nummsgs = g_slist_length(item->uid_list);
2868 /* If number of messages is still the same we
2869 know our caches message numbers are still valid,
2870 otherwise if the number of messages has decrease
2871 we discard our cache to start a new scan to find
2872 out which numbers have been removed */
2873 if (exists == nummsgs) {
2874 *msgnum_list = g_slist_copy(item->uid_list);
2877 } else if (exists < nummsgs) {
2878 debug_print("Freeing imap uid cache");
2880 g_slist_free(item->uid_list);
2881 item->uid_list = NULL;
2886 *msgnum_list = NULL;
2891 nummsgs = get_list_of_uids(folder, item, &uidlist);
2898 if (nummsgs != exists) {
2899 /* Cache contains more messages then folder, we have cached
2900 an old UID of a message that was removed and new messages
2901 have been added too, otherwise the uid_next check would
2903 debug_print("Freeing imap uid cache");
2905 g_slist_free(item->uid_list);
2906 item->uid_list = NULL;
2908 g_slist_free(*msgnum_list);
2910 nummsgs = get_list_of_uids(folder, item, &uidlist);
2913 *msgnum_list = uidlist;
2915 dir = folder_item_get_path((FolderItem *)item);
2916 debug_print("removing old messages from %s\n", dir);
2917 remove_numbered_files_not_in_list(dir, *msgnum_list);
2921 debug_print("get_num_list - ok - %i\n", nummsgs);
2926 static MsgInfo *imap_parse_msg(const gchar *file, FolderItem *item)
2931 flags.perm_flags = MSG_NEW|MSG_UNREAD;
2932 flags.tmp_flags = 0;
2934 g_return_val_if_fail(item != NULL, NULL);
2935 g_return_val_if_fail(file != NULL, NULL);
2937 if (item->stype == F_QUEUE) {
2938 MSG_SET_TMP_FLAGS(flags, MSG_QUEUED);
2939 } else if (item->stype == F_DRAFT) {
2940 MSG_SET_TMP_FLAGS(flags, MSG_DRAFT);
2943 msginfo = procheader_parse_file(file, flags, FALSE, FALSE);
2944 if (!msginfo) return NULL;
2946 msginfo->plaintext_file = g_strdup(file);
2947 msginfo->folder = item;
2952 GSList *imap_get_msginfos(Folder *folder, FolderItem *item,
2953 GSList *msgnum_list)
2955 IMAPSession *session;
2956 MsgInfoList *ret = NULL;
2959 debug_print("get_msginfos\n");
2961 g_return_val_if_fail(folder != NULL, NULL);
2962 g_return_val_if_fail(item != NULL, NULL);
2963 g_return_val_if_fail(msgnum_list != NULL, NULL);
2965 session = imap_session_get(folder);
2966 g_return_val_if_fail(session != NULL, NULL);
2968 debug_print("IMAP getting msginfos\n");
2969 ok = imap_select(session, IMAP_FOLDER(folder), item->path,
2970 NULL, NULL, NULL, NULL, FALSE);
2971 if (ok != IMAP_SUCCESS)
2974 if (!(item->stype == F_QUEUE || item->stype == F_DRAFT)) {
2975 ret = g_slist_concat(ret,
2976 imap_get_uncached_messages(session, item,
2979 MsgNumberList *sorted_list, *elem;
2980 gint startnum, lastnum;
2982 sorted_list = g_slist_sort(g_slist_copy(msgnum_list), g_int_compare);
2984 startnum = lastnum = GPOINTER_TO_INT(sorted_list->data);
2986 for (elem = sorted_list;; elem = g_slist_next(elem)) {
2990 num = GPOINTER_TO_INT(elem->data);
2992 if (num > lastnum + 1 || elem == NULL) {
2994 for (i = startnum; i <= lastnum; ++i) {
2997 file = imap_fetch_msg(folder, item, i);
2999 MsgInfo *msginfo = imap_parse_msg(file, item);
3000 if (msginfo != NULL) {
3001 msginfo->msgnum = i;
3002 ret = g_slist_append(ret, msginfo);
3016 g_slist_free(sorted_list);
3022 MsgInfo *imap_get_msginfo(Folder *folder, FolderItem *item, gint uid)
3024 MsgInfo *msginfo = NULL;
3025 MsgInfoList *msginfolist;
3026 MsgNumberList numlist;
3028 numlist.next = NULL;
3029 numlist.data = GINT_TO_POINTER(uid);
3031 msginfolist = imap_get_msginfos(folder, item, &numlist);
3032 if (msginfolist != NULL) {
3033 msginfo = msginfolist->data;
3034 g_slist_free(msginfolist);
3040 gboolean imap_scan_required(Folder *folder, FolderItem *_item)
3042 IMAPSession *session;
3043 IMAPFolderItem *item = (IMAPFolderItem *)_item;
3044 gint ok, exists = 0, recent = 0, unseen = 0;
3045 guint32 uid_next, uid_val = 0;
3046 gboolean selected_folder;
3048 g_return_val_if_fail(folder != NULL, FALSE);
3049 g_return_val_if_fail(item != NULL, FALSE);
3050 g_return_val_if_fail(item->item.folder != NULL, FALSE);
3051 g_return_val_if_fail(FOLDER_CLASS(item->item.folder) == &imap_class, FALSE);
3053 if (item->item.path == NULL)
3056 session = imap_session_get(folder);
3057 g_return_val_if_fail(session != NULL, FALSE);
3059 selected_folder = (session->mbox != NULL) &&
3060 (!strcmp(session->mbox, item->item.path));
3061 if (selected_folder) {
3062 ok = imap_cmd_noop(session);
3063 if (ok != IMAP_SUCCESS)
3066 if (session->folder_content_changed)
3069 ok = imap_status(session, IMAP_FOLDER(folder), item->item.path,
3070 &exists, &recent, &uid_next, &uid_val, &unseen, FALSE);
3071 if (ok != IMAP_SUCCESS)
3074 if ((uid_next != item->uid_next) || (exists != item->item.total_msgs))
3081 void imap_change_flags(Folder *folder, FolderItem *item, MsgInfo *msginfo, MsgPermFlags newflags)
3083 IMAPSession *session;
3084 IMAPFlags flags_set = 0, flags_unset = 0;
3085 gint ok = IMAP_SUCCESS;
3086 MsgNumberList numlist;
3087 hashtable_data *ht_data = NULL;
3089 g_return_if_fail(folder != NULL);
3090 g_return_if_fail(folder->klass == &imap_class);
3091 g_return_if_fail(item != NULL);
3092 g_return_if_fail(item->folder == folder);
3093 g_return_if_fail(msginfo != NULL);
3094 g_return_if_fail(msginfo->folder == item);
3096 MUTEX_TRYLOCK_OR_RETURN();
3098 session = imap_session_get(folder);
3103 if ((ok = imap_select(session, IMAP_FOLDER(folder), msginfo->folder->path,
3104 NULL, NULL, NULL, NULL, FALSE)) != IMAP_SUCCESS) {
3109 if (!MSG_IS_MARKED(msginfo->flags) && (newflags & MSG_MARKED))
3110 flags_set |= IMAP_FLAG_FLAGGED;
3111 if ( MSG_IS_MARKED(msginfo->flags) && !(newflags & MSG_MARKED))
3112 flags_unset |= IMAP_FLAG_FLAGGED;
3114 if (!MSG_IS_UNREAD(msginfo->flags) && (newflags & MSG_UNREAD))
3115 flags_unset |= IMAP_FLAG_SEEN;
3116 if ( MSG_IS_UNREAD(msginfo->flags) && !(newflags & MSG_UNREAD))
3117 flags_set |= IMAP_FLAG_SEEN;
3119 if (!MSG_IS_REPLIED(msginfo->flags) && (newflags & MSG_REPLIED))
3120 flags_set |= IMAP_FLAG_ANSWERED;
3121 if ( MSG_IS_REPLIED(msginfo->flags) && !(newflags & MSG_REPLIED))
3122 flags_set |= IMAP_FLAG_ANSWERED;
3124 numlist.next = NULL;
3125 numlist.data = GINT_TO_POINTER(msginfo->msgnum);
3127 if (IMAP_FOLDER_ITEM(item)->batching) {
3128 /* instead of performing an UID STORE command for each message change,
3129 * as a lot of them can change "together", we just fill in hashtables
3130 * and defer the treatment so that we're able to send only one
3133 debug_print("IMAP batch mode on, deferring flags change\n");
3135 ht_data = g_hash_table_lookup(flags_set_table, GINT_TO_POINTER(flags_set));
3136 if (ht_data == NULL) {
3137 ht_data = g_new0(hashtable_data, 1);
3138 ht_data->session = session;
3139 g_hash_table_insert(flags_set_table, GINT_TO_POINTER(flags_set), ht_data);
3141 if (!g_slist_find(ht_data->msglist, GINT_TO_POINTER(msginfo->msgnum)))
3142 ht_data->msglist = g_slist_prepend(ht_data->msglist, GINT_TO_POINTER(msginfo->msgnum));
3145 ht_data = g_hash_table_lookup(flags_unset_table, GINT_TO_POINTER(flags_unset));
3146 if (ht_data == NULL) {
3147 ht_data = g_new0(hashtable_data, 1);
3148 ht_data->session = session;
3149 g_hash_table_insert(flags_unset_table, GINT_TO_POINTER(flags_unset), ht_data);
3151 if (!g_slist_find(ht_data->msglist, GINT_TO_POINTER(msginfo->msgnum)))
3152 ht_data->msglist = g_slist_prepend(ht_data->msglist, GINT_TO_POINTER(msginfo->msgnum));
3155 debug_print("IMAP changing flags\n");
3157 ok = imap_set_message_flags(session, &numlist, flags_set, TRUE);
3158 if (ok != IMAP_SUCCESS) {
3165 ok = imap_set_message_flags(session, &numlist, flags_unset, FALSE);
3166 if (ok != IMAP_SUCCESS) {
3172 msginfo->flags.perm_flags = newflags;
3178 static gint imap_remove_msg(Folder *folder, FolderItem *item, gint uid)
3181 IMAPSession *session;
3183 MsgNumberList numlist;
3185 g_return_val_if_fail(folder != NULL, -1);
3186 g_return_val_if_fail(FOLDER_CLASS(folder) == &imap_class, -1);
3187 g_return_val_if_fail(item != NULL, -1);
3189 session = imap_session_get(folder);
3190 if (!session) return -1;
3192 ok = imap_select(session, IMAP_FOLDER(folder), item->path,
3193 NULL, NULL, NULL, NULL, FALSE);
3194 if (ok != IMAP_SUCCESS)
3197 numlist.next = NULL;
3198 numlist.data = GINT_TO_POINTER(uid);
3200 ok = imap_set_message_flags
3201 (IMAP_SESSION(REMOTE_FOLDER(folder)->session),
3202 &numlist, IMAP_FLAG_DELETED, TRUE);
3203 if (ok != IMAP_SUCCESS) {
3204 log_warning(_("can't set deleted flags: %d\n"), uid);
3208 if (!session->uidplus) {
3209 ok = imap_cmd_expunge(session);
3213 uidstr = g_strdup_printf("%u", uid);
3214 ok = imap_cmd_expunge(session);
3217 if (ok != IMAP_SUCCESS) {
3218 log_warning(_("can't expunge\n"));
3222 IMAP_FOLDER_ITEM(item)->uid_list = g_slist_remove(
3223 IMAP_FOLDER_ITEM(item)->uid_list, numlist.data);
3224 dir = folder_item_get_path(item);
3225 if (is_dir_exist(dir))
3226 remove_numbered_files(dir, uid, uid);
3229 return IMAP_SUCCESS;
3232 static gint compare_msginfo(gconstpointer a, gconstpointer b)
3234 return ((MsgInfo *)a)->msgnum - ((MsgInfo *)b)->msgnum;
3237 static guint gslist_find_next_num(MsgNumberList **list, guint num)
3241 g_return_val_if_fail(list != NULL, -1);
3243 for (elem = *list; elem != NULL; elem = g_slist_next(elem))
3244 if (GPOINTER_TO_INT(elem->data) >= num)
3247 return elem != NULL ? GPOINTER_TO_INT(elem->data) : (gint)-1;
3251 * NEW and DELETED flags are not syncronized
3252 * - The NEW/RECENT flags in IMAP folders can not really be directly
3253 * modified by Sylpheed
3254 * - The DELETE/DELETED flag in IMAP and Sylpheed don't have the same
3255 * meaning, in IMAP it always removes the messages from the FolderItem
3256 * in Sylpheed it can mean to move the message to trash
3259 typedef struct _get_flags_data {
3262 MsgInfoList *msginfo_list;
3263 GRelation *msgflags;
3267 static /*gint*/ void *imap_get_flags_thread(void *data)
3269 get_flags_data *stuff = (get_flags_data *)data;
3270 Folder *folder = stuff->folder;
3271 FolderItem *item = stuff->item;
3272 MsgInfoList *msginfo_list = stuff->msginfo_list;
3273 GRelation *msgflags = stuff->msgflags;
3274 IMAPSession *session;
3275 GSList *sorted_list;
3276 GSList *unseen = NULL, *answered = NULL, *flagged = NULL;
3277 GSList *p_unseen, *p_answered, *p_flagged;
3279 GSList *seq_list, *cur;
3280 gboolean reverse_seen = FALSE;
3283 gint exists_cnt, recent_cnt, unseen_cnt, uid_next;
3284 guint32 uidvalidity;
3285 gboolean selected_folder;
3287 if (folder == NULL || item == NULL) {
3289 return GINT_TO_POINTER(-1);
3291 if (msginfo_list == NULL) {
3293 return GINT_TO_POINTER(0);
3296 session = imap_session_get(folder);
3297 if (session == NULL) {
3299 return GINT_TO_POINTER(-1);
3302 selected_folder = (session->mbox != NULL) &&
3303 (!strcmp(session->mbox, item->path));
3305 if (!selected_folder) {
3306 ok = imap_status(session, IMAP_FOLDER(folder), item->path,
3307 &exists_cnt, &recent_cnt, &uid_next, &uidvalidity, &unseen_cnt, TRUE);
3308 ok = imap_select(session, IMAP_FOLDER(folder), item->path,
3309 NULL, NULL, NULL, NULL, TRUE);
3310 if (ok != IMAP_SUCCESS) {
3312 return GINT_TO_POINTER(-1);
3315 if (unseen_cnt > exists_cnt / 2)
3316 reverse_seen = TRUE;
3319 if (item->unread_msgs > item->total_msgs / 2)
3320 reverse_seen = TRUE;
3323 cmd_buf = g_string_new(NULL);
3325 sorted_list = g_slist_sort(g_slist_copy(msginfo_list), compare_msginfo);
3327 seq_list = imap_get_lep_set_from_msglist(msginfo_list);
3329 for (cur = seq_list; cur != NULL; cur = g_slist_next(cur)) {
3330 struct mailimap_set * imapset;
3331 clist * lep_uidlist;
3334 imapset = cur->data;
3336 r = imap_threaded_search(folder, IMAP_SEARCH_TYPE_SEEN,
3337 imapset, &lep_uidlist);
3340 r = imap_threaded_search(folder,
3341 IMAP_SEARCH_TYPE_UNSEEN,
3342 imapset, &lep_uidlist);
3344 if (r == MAILIMAP_NO_ERROR) {
3347 uidlist = imap_uid_list_from_lep(lep_uidlist);
3348 mailimap_search_result_free(lep_uidlist);
3350 unseen = g_slist_concat(unseen, uidlist);
3353 r = imap_threaded_search(folder, IMAP_SEARCH_TYPE_ANSWERED,
3354 imapset, &lep_uidlist);
3355 if (r == MAILIMAP_NO_ERROR) {
3358 uidlist = imap_uid_list_from_lep(lep_uidlist);
3359 mailimap_search_result_free(lep_uidlist);
3361 answered = g_slist_concat(answered, uidlist);
3364 r = imap_threaded_search(folder, IMAP_SEARCH_TYPE_FLAGGED,
3365 imapset, &lep_uidlist);
3366 if (r == MAILIMAP_NO_ERROR) {
3369 uidlist = imap_uid_list_from_lep(lep_uidlist);
3370 mailimap_search_result_free(lep_uidlist);
3372 flagged = g_slist_concat(flagged, uidlist);
3377 p_answered = answered;
3378 p_flagged = flagged;
3380 for (elem = sorted_list; elem != NULL; elem = g_slist_next(elem)) {
3385 msginfo = (MsgInfo *) elem->data;
3386 flags = msginfo->flags.perm_flags;
3387 wasnew = (flags & MSG_NEW);
3388 flags &= ~((reverse_seen ? 0 : MSG_UNREAD | MSG_NEW) | MSG_REPLIED | MSG_MARKED);
3390 flags |= MSG_UNREAD | (wasnew ? MSG_NEW : 0);
3391 if (gslist_find_next_num(&p_unseen, msginfo->msgnum) == msginfo->msgnum) {
3392 if (!reverse_seen) {
3393 flags |= MSG_UNREAD | (wasnew ? MSG_NEW : 0);
3395 flags &= ~(MSG_UNREAD | MSG_NEW);
3398 if (gslist_find_next_num(&p_answered, msginfo->msgnum) == msginfo->msgnum)
3399 flags |= MSG_REPLIED;
3400 if (gslist_find_next_num(&p_flagged, msginfo->msgnum) == msginfo->msgnum)
3401 flags |= MSG_MARKED;
3402 g_relation_insert(msgflags, msginfo, GINT_TO_POINTER(flags));
3405 imap_lep_set_free(seq_list);
3406 g_slist_free(flagged);
3407 g_slist_free(answered);
3408 g_slist_free(unseen);
3409 g_slist_free(sorted_list);
3410 g_string_free(cmd_buf, TRUE);
3413 return GINT_TO_POINTER(0);
3416 static gint imap_get_flags(Folder *folder, FolderItem *item,
3417 MsgInfoList *msginfo_list, GRelation *msgflags)
3420 get_flags_data *data = g_new0(get_flags_data, 1);
3422 data->folder = folder;
3424 data->msginfo_list = msginfo_list;
3425 data->msgflags = msgflags;
3427 if (prefs_common.work_offline && !imap_gtk_should_override()) {
3432 result = GPOINTER_TO_INT(imap_get_flags_thread(data));
3439 static gboolean process_flags(gpointer key, gpointer value, gpointer user_data)
3441 gboolean flags_set = GPOINTER_TO_INT(user_data);
3442 gint flags_value = GPOINTER_TO_INT(key);
3443 hashtable_data *data = (hashtable_data *)value;
3445 data->msglist = g_slist_reverse(data->msglist);
3447 debug_print("IMAP %ssetting flags to %d for %d messages\n",
3450 g_slist_length(data->msglist));
3451 imap_set_message_flags(data->session, data->msglist, flags_value, flags_set);
3453 g_slist_free(data->msglist);
3458 static void process_hashtable(void)
3460 MUTEX_TRYLOCK_OR_RETURN();
3461 if (flags_set_table) {
3462 g_hash_table_foreach_remove(flags_set_table, process_flags, GINT_TO_POINTER(TRUE));
3463 g_free(flags_set_table);
3464 flags_set_table = NULL;
3466 if (flags_unset_table) {
3467 g_hash_table_foreach_remove(flags_unset_table, process_flags, GINT_TO_POINTER(FALSE));
3468 g_free(flags_unset_table);
3469 flags_unset_table = NULL;
3474 static IMAPFolderItem *batching_item = NULL;
3476 static void imap_set_batch (Folder *folder, FolderItem *_item, gboolean batch)