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)
2259 unsigned int exists;
2261 r = imap_threaded_noop(session->folder, &exists);
2262 if (r != MAILIMAP_NO_ERROR)
2265 session->exists = exists;
2267 return IMAP_SUCCESS;
2271 static gint imap_cmd_starttls(IMAPSession *session)
2275 r = imap_threaded_starttls(session->folder);
2276 if (r != MAILIMAP_NO_ERROR)
2279 return IMAP_SUCCESS;
2283 static gint imap_cmd_select(IMAPSession *session, const gchar *folder,
2284 gint *exists, gint *recent, gint *unseen,
2285 guint32 *uid_validity, gboolean block)
2289 r = imap_threaded_select(session->folder, folder,
2290 exists, recent, unseen, uid_validity);
2291 if (r != MAILIMAP_NO_ERROR) {
2295 return IMAP_SUCCESS;
2298 static gint imap_cmd_examine(IMAPSession *session, const gchar *folder,
2299 gint *exists, gint *recent, gint *unseen,
2300 guint32 *uid_validity, gboolean block)
2304 r = imap_threaded_examine(session->folder, folder,
2305 exists, recent, unseen, uid_validity);
2306 if (r != MAILIMAP_NO_ERROR) {
2310 return IMAP_SUCCESS;
2313 static gint imap_cmd_create(IMAPSession *session, const gchar *folder)
2317 r = imap_threaded_create(session->folder, folder);
2318 if (r != MAILIMAP_NO_ERROR)
2321 return IMAP_SUCCESS;
2324 static gint imap_cmd_rename(IMAPSession *session, const gchar *old_folder,
2325 const gchar *new_folder)
2329 r = imap_threaded_rename(session->folder, old_folder,
2331 if (r != MAILIMAP_NO_ERROR)
2334 return IMAP_SUCCESS;
2337 static gint imap_cmd_delete(IMAPSession *session, const gchar *folder)
2341 r = imap_threaded_delete(session->folder, folder);
2342 if (r != MAILIMAP_NO_ERROR)
2345 return IMAP_SUCCESS;
2348 typedef struct _fetch_data {
2349 IMAPSession *session;
2351 const gchar *filename;
2357 static void *imap_cmd_fetch_thread(void *data)
2359 fetch_data *stuff = (fetch_data *)data;
2360 IMAPSession *session = stuff->session;
2361 guint32 uid = stuff->uid;
2362 const gchar *filename = stuff->filename;
2366 r = imap_threaded_fetch_content(session->folder,
2370 r = imap_threaded_fetch_content(session->folder,
2373 if (r != MAILIMAP_NO_ERROR)
2374 return GINT_TO_POINTER(IMAP_ERROR);
2376 return GINT_TO_POINTER(IMAP_SUCCESS);
2379 static gint imap_cmd_fetch(IMAPSession *session, guint32 uid,
2380 const gchar *filename, gboolean headers,
2383 fetch_data *data = g_new0(fetch_data, 1);
2386 data->session = session;
2388 data->filename = filename;
2389 data->headers = headers;
2392 if (prefs_common.work_offline && !imap_gtk_should_override()) {
2397 result = GPOINTER_TO_INT(imap_cmd_fetch_thread(data));
2403 static gint imap_cmd_append(IMAPSession *session, const gchar *destfolder,
2404 const gchar *file, IMAPFlags flags,
2407 struct mailimap_flag_list * flag_list;
2410 g_return_val_if_fail(file != NULL, IMAP_ERROR);
2412 flag_list = imap_flag_to_lep(flags);
2413 r = imap_threaded_append(session->folder, destfolder,
2416 if (new_uid != NULL)
2419 if (r != MAILIMAP_NO_ERROR)
2422 return IMAP_SUCCESS;
2425 static gint imap_cmd_copy(IMAPSession *session, struct mailimap_set * set,
2426 const gchar *destfolder, GRelation *uid_mapping)
2430 g_return_val_if_fail(session != NULL, IMAP_ERROR);
2431 g_return_val_if_fail(set != NULL, IMAP_ERROR);
2432 g_return_val_if_fail(destfolder != NULL, IMAP_ERROR);
2434 r = imap_threaded_copy(session->folder, set, destfolder);
2435 if (r != MAILIMAP_NO_ERROR)
2438 return IMAP_SUCCESS;
2441 static gint imap_cmd_store(IMAPSession *session, struct mailimap_set * set,
2442 IMAPFlags flags, int do_add)
2445 struct mailimap_flag_list * flag_list;
2446 struct mailimap_store_att_flags * store_att_flags;
2448 flag_list = imap_flag_to_lep(flags);
2452 mailimap_store_att_flags_new_add_flags_silent(flag_list);
2455 mailimap_store_att_flags_new_remove_flags_silent(flag_list);
2457 r = imap_threaded_store(session->folder, set, store_att_flags);
2458 if (r != MAILIMAP_NO_ERROR)
2461 return IMAP_SUCCESS;
2464 static gint imap_cmd_expunge(IMAPSession *session)
2468 if (prefs_common.work_offline && !imap_gtk_should_override()) {
2472 r = imap_threaded_expunge(session->folder);
2473 if (r != MAILIMAP_NO_ERROR)
2476 return IMAP_SUCCESS;
2479 static void imap_path_separator_subst(gchar *str, gchar separator)
2482 gboolean in_escape = FALSE;
2484 if (!separator || separator == '/') return;
2486 for (p = str; *p != '\0'; p++) {
2487 if (*p == '/' && !in_escape)
2489 else if (*p == '&' && *(p + 1) != '-' && !in_escape)
2491 else if (*p == '-' && in_escape)
2496 static gchar *imap_modified_utf7_to_utf8(const gchar *mutf7_str)
2498 static iconv_t cd = (iconv_t)-1;
2499 static gboolean iconv_ok = TRUE;
2502 size_t norm_utf7_len;
2504 gchar *to_str, *to_p;
2506 gboolean in_escape = FALSE;
2508 if (!iconv_ok) return g_strdup(mutf7_str);
2510 if (cd == (iconv_t)-1) {
2511 cd = iconv_open(CS_INTERNAL, CS_UTF_7);
2512 if (cd == (iconv_t)-1) {
2513 g_warning("iconv cannot convert UTF-7 to %s\n",
2516 return g_strdup(mutf7_str);
2520 /* modified UTF-7 to normal UTF-7 conversion */
2521 norm_utf7 = g_string_new(NULL);
2523 for (p = mutf7_str; *p != '\0'; p++) {
2524 /* replace: '&' -> '+',
2526 escaped ',' -> '/' */
2527 if (!in_escape && *p == '&') {
2528 if (*(p + 1) != '-') {
2529 g_string_append_c(norm_utf7, '+');
2532 g_string_append_c(norm_utf7, '&');
2535 } else if (in_escape && *p == ',') {
2536 g_string_append_c(norm_utf7, '/');
2537 } else if (in_escape && *p == '-') {
2538 g_string_append_c(norm_utf7, '-');
2541 g_string_append_c(norm_utf7, *p);
2545 norm_utf7_p = norm_utf7->str;
2546 norm_utf7_len = norm_utf7->len;
2547 to_len = strlen(mutf7_str) * 5;
2548 to_p = to_str = g_malloc(to_len + 1);
2550 if (iconv(cd, (ICONV_CONST gchar **)&norm_utf7_p, &norm_utf7_len,
2551 &to_p, &to_len) == -1) {
2552 g_warning(_("iconv cannot convert UTF-7 to %s\n"),
2553 conv_get_locale_charset_str());
2554 g_string_free(norm_utf7, TRUE);
2556 return g_strdup(mutf7_str);
2559 /* second iconv() call for flushing */
2560 iconv(cd, NULL, NULL, &to_p, &to_len);
2561 g_string_free(norm_utf7, TRUE);
2567 static gchar *imap_utf8_to_modified_utf7(const gchar *from)
2569 static iconv_t cd = (iconv_t)-1;
2570 static gboolean iconv_ok = TRUE;
2571 gchar *norm_utf7, *norm_utf7_p;
2572 size_t from_len, norm_utf7_len;
2574 gchar *from_tmp, *to, *p;
2575 gboolean in_escape = FALSE;
2577 if (!iconv_ok) return g_strdup(from);
2579 if (cd == (iconv_t)-1) {
2580 cd = iconv_open(CS_UTF_7, CS_INTERNAL);
2581 if (cd == (iconv_t)-1) {
2582 g_warning(_("iconv cannot convert %s to UTF-7\n"),
2585 return g_strdup(from);
2589 /* UTF-8 to normal UTF-7 conversion */
2590 Xstrdup_a(from_tmp, from, return g_strdup(from));
2591 from_len = strlen(from);
2592 norm_utf7_len = from_len * 5;
2593 Xalloca(norm_utf7, norm_utf7_len + 1, return g_strdup(from));
2594 norm_utf7_p = norm_utf7;
2596 #define IS_PRINT(ch) (isprint(ch) && IS_ASCII(ch))
2598 while (from_len > 0) {
2599 if (*from_tmp == '+') {
2600 *norm_utf7_p++ = '+';
2601 *norm_utf7_p++ = '-';
2605 } else if (IS_PRINT(*(guchar *)from_tmp)) {
2606 /* printable ascii char */
2607 *norm_utf7_p = *from_tmp;
2613 size_t conv_len = 0;
2615 /* unprintable char: convert to UTF-7 */
2617 while (!IS_PRINT(*(guchar *)p) && conv_len < from_len) {
2618 conv_len += g_utf8_skip[*(guchar *)p];
2619 p += g_utf8_skip[*(guchar *)p];
2622 from_len -= conv_len;
2623 if (iconv(cd, (ICONV_CONST gchar **)&from_tmp,
2625 &norm_utf7_p, &norm_utf7_len) == -1) {
2626 g_warning(_("iconv cannot convert UTF-8 to UTF-7\n"));
2627 return g_strdup(from);
2630 /* second iconv() call for flushing */
2631 iconv(cd, NULL, NULL, &norm_utf7_p, &norm_utf7_len);
2637 *norm_utf7_p = '\0';
2638 to_str = g_string_new(NULL);
2639 for (p = norm_utf7; p < norm_utf7_p; p++) {
2640 /* replace: '&' -> "&-",
2643 BASE64 '/' -> ',' */
2644 if (!in_escape && *p == '&') {
2645 g_string_append(to_str, "&-");
2646 } else if (!in_escape && *p == '+') {
2647 if (*(p + 1) == '-') {
2648 g_string_append_c(to_str, '+');
2651 g_string_append_c(to_str, '&');
2654 } else if (in_escape && *p == '/') {
2655 g_string_append_c(to_str, ',');
2656 } else if (in_escape && *p == '-') {
2657 g_string_append_c(to_str, '-');
2660 g_string_append_c(to_str, *p);
2666 g_string_append_c(to_str, '-');
2670 g_string_free(to_str, FALSE);
2675 static gboolean imap_rename_folder_func(GNode *node, gpointer data)
2677 FolderItem *item = node->data;
2678 gchar **paths = data;
2679 const gchar *oldpath = paths[0];
2680 const gchar *newpath = paths[1];
2682 gchar *new_itempath;
2685 oldpathlen = strlen(oldpath);
2686 if (strncmp(oldpath, item->path, oldpathlen) != 0) {
2687 g_warning("path doesn't match: %s, %s\n", oldpath, item->path);
2691 base = item->path + oldpathlen;
2692 while (*base == G_DIR_SEPARATOR) base++;
2694 new_itempath = g_strdup(newpath);
2696 new_itempath = g_strconcat(newpath, G_DIR_SEPARATOR_S, base,
2699 item->path = new_itempath;
2704 typedef struct _get_list_uid_data {
2706 IMAPFolderItem *item;
2707 GSList **msgnum_list;
2709 } get_list_uid_data;
2711 static void *get_list_of_uids_thread(void *data)
2713 get_list_uid_data *stuff = (get_list_uid_data *)data;
2714 Folder *folder = stuff->folder;
2715 IMAPFolderItem *item = stuff->item;
2716 GSList **msgnum_list = stuff->msgnum_list;
2717 gint ok, nummsgs = 0, lastuid_old;
2718 IMAPSession *session;
2719 GSList *uidlist, *elem;
2720 struct mailimap_set * set;
2721 clist * lep_uidlist;
2724 session = imap_session_get(folder);
2725 if (session == NULL) {
2727 return GINT_TO_POINTER(-1);
2730 ok = imap_select(session, IMAP_FOLDER(folder), item->item.path,
2731 NULL, NULL, NULL, NULL, TRUE);
2732 if (ok != IMAP_SUCCESS) {
2734 return GINT_TO_POINTER(-1);
2739 set = mailimap_set_new_interval(item->lastuid + 1, 0);
2740 r = imap_threaded_search(folder, IMAP_SEARCH_TYPE_SIMPLE, set,
2742 if (r == MAILIMAP_NO_ERROR) {
2743 GSList * fetchuid_list;
2746 imap_uid_list_from_lep(lep_uidlist);
2747 uidlist = g_slist_concat(fetchuid_list, uidlist);
2750 GSList * fetchuid_list;
2751 carray * lep_uidtab;
2753 r = imap_threaded_fetch_uid(folder, item->lastuid + 1,
2755 if (r == MAILIMAP_NO_ERROR) {
2757 imap_uid_list_from_lep_tab(lep_uidtab);
2758 uidlist = g_slist_concat(fetchuid_list, uidlist);
2762 lastuid_old = item->lastuid;
2763 *msgnum_list = g_slist_copy(item->uid_list);
2764 nummsgs = g_slist_length(*msgnum_list);
2765 debug_print("Got %d uids from cache\n", g_slist_length(item->uid_list));
2767 for (elem = uidlist; elem != NULL; elem = g_slist_next(elem)) {
2770 msgnum = GPOINTER_TO_INT(elem->data);
2771 if (msgnum > lastuid_old) {
2772 *msgnum_list = g_slist_prepend(*msgnum_list, GINT_TO_POINTER(msgnum));
2773 item->uid_list = g_slist_prepend(item->uid_list, GINT_TO_POINTER(msgnum));
2776 if(msgnum > item->lastuid)
2777 item->lastuid = msgnum;
2780 g_slist_free(uidlist);
2783 return GINT_TO_POINTER(nummsgs);
2786 static gint get_list_of_uids(Folder *folder, IMAPFolderItem *item, GSList **msgnum_list)
2789 get_list_uid_data *data = g_new0(get_list_uid_data, 1);
2791 data->folder = folder;
2793 data->msgnum_list = msgnum_list;
2795 if (prefs_common.work_offline && !imap_gtk_should_override()) {
2800 result = GPOINTER_TO_INT(get_list_of_uids_thread(data));
2806 gint imap_get_num_list(Folder *folder, FolderItem *_item, GSList **msgnum_list, gboolean *old_uids_valid)
2808 IMAPFolderItem *item = (IMAPFolderItem *)_item;
2809 IMAPSession *session;
2810 gint ok, nummsgs = 0, exists, recent, uid_val, uid_next, unseen;
2811 GSList *uidlist = NULL;
2813 gboolean selected_folder;
2815 debug_print("get_num_list\n");
2817 g_return_val_if_fail(folder != NULL, -1);
2818 g_return_val_if_fail(item != NULL, -1);
2819 g_return_val_if_fail(item->item.path != NULL, -1);
2820 g_return_val_if_fail(FOLDER_CLASS(folder) == &imap_class, -1);
2821 g_return_val_if_fail(folder->account != NULL, -1);
2823 MUTEX_TRYLOCK_OR_RETURN_VAL(-1);
2825 session = imap_session_get(folder);
2826 g_return_val_if_fail(session != NULL, -1);
2828 selected_folder = (session->mbox != NULL) &&
2829 (!strcmp(session->mbox, item->item.path));
2830 if (selected_folder) {
2831 ok = imap_cmd_noop(session);
2832 if (ok != IMAP_SUCCESS) {
2836 exists = session->exists;
2838 *old_uids_valid = TRUE;
2840 ok = imap_status(session, IMAP_FOLDER(folder), item->item.path,
2841 &exists, &recent, &uid_next, &uid_val, &unseen, FALSE);
2842 if (ok != IMAP_SUCCESS) {
2846 if(item->item.mtime == uid_val)
2847 *old_uids_valid = TRUE;
2849 *old_uids_valid = FALSE;
2851 debug_print("Freeing imap uid cache\n");
2853 g_slist_free(item->uid_list);
2854 item->uid_list = NULL;
2856 item->item.mtime = uid_val;
2858 imap_delete_all_cached_messages((FolderItem *)item);
2862 if (!selected_folder)
2863 item->uid_next = uid_next;
2865 /* If old uid_next matches new uid_next we can be sure no message
2866 was added to the folder */
2867 if (( selected_folder && !session->folder_content_changed) ||
2868 (!selected_folder && uid_next == item->uid_next)) {
2869 nummsgs = g_slist_length(item->uid_list);
2871 /* If number of messages is still the same we
2872 know our caches message numbers are still valid,
2873 otherwise if the number of messages has decrease
2874 we discard our cache to start a new scan to find
2875 out which numbers have been removed */
2876 if (exists == nummsgs) {
2877 *msgnum_list = g_slist_copy(item->uid_list);
2880 } else if (exists < nummsgs) {
2881 debug_print("Freeing imap uid cache");
2883 g_slist_free(item->uid_list);
2884 item->uid_list = NULL;
2889 *msgnum_list = NULL;
2894 nummsgs = get_list_of_uids(folder, item, &uidlist);
2901 if (nummsgs != exists) {
2902 /* Cache contains more messages then folder, we have cached
2903 an old UID of a message that was removed and new messages
2904 have been added too, otherwise the uid_next check would
2906 debug_print("Freeing imap uid cache");
2908 g_slist_free(item->uid_list);
2909 item->uid_list = NULL;
2911 g_slist_free(*msgnum_list);
2913 nummsgs = get_list_of_uids(folder, item, &uidlist);
2916 *msgnum_list = uidlist;
2918 dir = folder_item_get_path((FolderItem *)item);
2919 debug_print("removing old messages from %s\n", dir);
2920 remove_numbered_files_not_in_list(dir, *msgnum_list);
2924 debug_print("get_num_list - ok - %i\n", nummsgs);
2929 static MsgInfo *imap_parse_msg(const gchar *file, FolderItem *item)
2934 flags.perm_flags = MSG_NEW|MSG_UNREAD;
2935 flags.tmp_flags = 0;
2937 g_return_val_if_fail(item != NULL, NULL);
2938 g_return_val_if_fail(file != NULL, NULL);
2940 if (item->stype == F_QUEUE) {
2941 MSG_SET_TMP_FLAGS(flags, MSG_QUEUED);
2942 } else if (item->stype == F_DRAFT) {
2943 MSG_SET_TMP_FLAGS(flags, MSG_DRAFT);
2946 msginfo = procheader_parse_file(file, flags, FALSE, FALSE);
2947 if (!msginfo) return NULL;
2949 msginfo->plaintext_file = g_strdup(file);
2950 msginfo->folder = item;
2955 GSList *imap_get_msginfos(Folder *folder, FolderItem *item,
2956 GSList *msgnum_list)
2958 IMAPSession *session;
2959 MsgInfoList *ret = NULL;
2962 debug_print("get_msginfos\n");
2964 g_return_val_if_fail(folder != NULL, NULL);
2965 g_return_val_if_fail(item != NULL, NULL);
2966 g_return_val_if_fail(msgnum_list != NULL, NULL);
2968 session = imap_session_get(folder);
2969 g_return_val_if_fail(session != NULL, NULL);
2971 debug_print("IMAP getting msginfos\n");
2972 ok = imap_select(session, IMAP_FOLDER(folder), item->path,
2973 NULL, NULL, NULL, NULL, FALSE);
2974 if (ok != IMAP_SUCCESS)
2977 if (!(item->stype == F_QUEUE || item->stype == F_DRAFT)) {
2978 ret = g_slist_concat(ret,
2979 imap_get_uncached_messages(session, item,
2982 MsgNumberList *sorted_list, *elem;
2983 gint startnum, lastnum;
2985 sorted_list = g_slist_sort(g_slist_copy(msgnum_list), g_int_compare);
2987 startnum = lastnum = GPOINTER_TO_INT(sorted_list->data);
2989 for (elem = sorted_list;; elem = g_slist_next(elem)) {
2993 num = GPOINTER_TO_INT(elem->data);
2995 if (num > lastnum + 1 || elem == NULL) {
2997 for (i = startnum; i <= lastnum; ++i) {
3000 file = imap_fetch_msg(folder, item, i);
3002 MsgInfo *msginfo = imap_parse_msg(file, item);
3003 if (msginfo != NULL) {
3004 msginfo->msgnum = i;
3005 ret = g_slist_append(ret, msginfo);
3019 g_slist_free(sorted_list);
3025 MsgInfo *imap_get_msginfo(Folder *folder, FolderItem *item, gint uid)
3027 MsgInfo *msginfo = NULL;
3028 MsgInfoList *msginfolist;
3029 MsgNumberList numlist;
3031 numlist.next = NULL;
3032 numlist.data = GINT_TO_POINTER(uid);
3034 msginfolist = imap_get_msginfos(folder, item, &numlist);
3035 if (msginfolist != NULL) {
3036 msginfo = msginfolist->data;
3037 g_slist_free(msginfolist);
3043 gboolean imap_scan_required(Folder *folder, FolderItem *_item)
3045 IMAPSession *session;
3046 IMAPFolderItem *item = (IMAPFolderItem *)_item;
3047 gint ok, exists = 0, recent = 0, unseen = 0;
3048 guint32 uid_next, uid_val = 0;
3049 gboolean selected_folder;
3051 g_return_val_if_fail(folder != NULL, FALSE);
3052 g_return_val_if_fail(item != NULL, FALSE);
3053 g_return_val_if_fail(item->item.folder != NULL, FALSE);
3054 g_return_val_if_fail(FOLDER_CLASS(item->item.folder) == &imap_class, FALSE);
3056 if (item->item.path == NULL)
3059 session = imap_session_get(folder);
3060 g_return_val_if_fail(session != NULL, FALSE);
3062 selected_folder = (session->mbox != NULL) &&
3063 (!strcmp(session->mbox, item->item.path));
3064 if (selected_folder) {
3065 ok = imap_cmd_noop(session);
3066 if (ok != IMAP_SUCCESS)
3069 if (session->folder_content_changed)
3072 ok = imap_status(session, IMAP_FOLDER(folder), item->item.path,
3073 &exists, &recent, &uid_next, &uid_val, &unseen, FALSE);
3074 if (ok != IMAP_SUCCESS)
3077 if ((uid_next != item->uid_next) || (exists != item->item.total_msgs))
3084 void imap_change_flags(Folder *folder, FolderItem *item, MsgInfo *msginfo, MsgPermFlags newflags)
3086 IMAPSession *session;
3087 IMAPFlags flags_set = 0, flags_unset = 0;
3088 gint ok = IMAP_SUCCESS;
3089 MsgNumberList numlist;
3090 hashtable_data *ht_data = NULL;
3092 g_return_if_fail(folder != NULL);
3093 g_return_if_fail(folder->klass == &imap_class);
3094 g_return_if_fail(item != NULL);
3095 g_return_if_fail(item->folder == folder);
3096 g_return_if_fail(msginfo != NULL);
3097 g_return_if_fail(msginfo->folder == item);
3099 MUTEX_TRYLOCK_OR_RETURN();
3101 session = imap_session_get(folder);
3106 if ((ok = imap_select(session, IMAP_FOLDER(folder), msginfo->folder->path,
3107 NULL, NULL, NULL, NULL, FALSE)) != IMAP_SUCCESS) {
3112 if (!MSG_IS_MARKED(msginfo->flags) && (newflags & MSG_MARKED))
3113 flags_set |= IMAP_FLAG_FLAGGED;
3114 if ( MSG_IS_MARKED(msginfo->flags) && !(newflags & MSG_MARKED))
3115 flags_unset |= IMAP_FLAG_FLAGGED;
3117 if (!MSG_IS_UNREAD(msginfo->flags) && (newflags & MSG_UNREAD))
3118 flags_unset |= IMAP_FLAG_SEEN;
3119 if ( MSG_IS_UNREAD(msginfo->flags) && !(newflags & MSG_UNREAD))
3120 flags_set |= IMAP_FLAG_SEEN;
3122 if (!MSG_IS_REPLIED(msginfo->flags) && (newflags & MSG_REPLIED))
3123 flags_set |= IMAP_FLAG_ANSWERED;
3124 if ( MSG_IS_REPLIED(msginfo->flags) && !(newflags & MSG_REPLIED))
3125 flags_set |= IMAP_FLAG_ANSWERED;
3127 numlist.next = NULL;
3128 numlist.data = GINT_TO_POINTER(msginfo->msgnum);
3130 if (IMAP_FOLDER_ITEM(item)->batching) {
3131 /* instead of performing an UID STORE command for each message change,
3132 * as a lot of them can change "together", we just fill in hashtables
3133 * and defer the treatment so that we're able to send only one
3136 debug_print("IMAP batch mode on, deferring flags change\n");
3138 ht_data = g_hash_table_lookup(flags_set_table, GINT_TO_POINTER(flags_set));
3139 if (ht_data == NULL) {
3140 ht_data = g_new0(hashtable_data, 1);
3141 ht_data->session = session;
3142 g_hash_table_insert(flags_set_table, GINT_TO_POINTER(flags_set), ht_data);
3144 if (!g_slist_find(ht_data->msglist, GINT_TO_POINTER(msginfo->msgnum)))
3145 ht_data->msglist = g_slist_prepend(ht_data->msglist, GINT_TO_POINTER(msginfo->msgnum));
3148 ht_data = g_hash_table_lookup(flags_unset_table, GINT_TO_POINTER(flags_unset));
3149 if (ht_data == NULL) {
3150 ht_data = g_new0(hashtable_data, 1);
3151 ht_data->session = session;
3152 g_hash_table_insert(flags_unset_table, GINT_TO_POINTER(flags_unset), ht_data);
3154 if (!g_slist_find(ht_data->msglist, GINT_TO_POINTER(msginfo->msgnum)))
3155 ht_data->msglist = g_slist_prepend(ht_data->msglist, GINT_TO_POINTER(msginfo->msgnum));
3158 debug_print("IMAP changing flags\n");
3160 ok = imap_set_message_flags(session, &numlist, flags_set, TRUE);
3161 if (ok != IMAP_SUCCESS) {
3168 ok = imap_set_message_flags(session, &numlist, flags_unset, FALSE);
3169 if (ok != IMAP_SUCCESS) {
3175 msginfo->flags.perm_flags = newflags;
3181 static gint imap_remove_msg(Folder *folder, FolderItem *item, gint uid)
3184 IMAPSession *session;
3186 MsgNumberList numlist;
3188 g_return_val_if_fail(folder != NULL, -1);
3189 g_return_val_if_fail(FOLDER_CLASS(folder) == &imap_class, -1);
3190 g_return_val_if_fail(item != NULL, -1);
3192 session = imap_session_get(folder);
3193 if (!session) return -1;
3195 ok = imap_select(session, IMAP_FOLDER(folder), item->path,
3196 NULL, NULL, NULL, NULL, FALSE);
3197 if (ok != IMAP_SUCCESS)
3200 numlist.next = NULL;
3201 numlist.data = GINT_TO_POINTER(uid);
3203 ok = imap_set_message_flags
3204 (IMAP_SESSION(REMOTE_FOLDER(folder)->session),
3205 &numlist, IMAP_FLAG_DELETED, TRUE);
3206 if (ok != IMAP_SUCCESS) {
3207 log_warning(_("can't set deleted flags: %d\n"), uid);
3211 if (!session->uidplus) {
3212 ok = imap_cmd_expunge(session);
3216 uidstr = g_strdup_printf("%u", uid);
3217 ok = imap_cmd_expunge(session);
3220 if (ok != IMAP_SUCCESS) {
3221 log_warning(_("can't expunge\n"));
3225 IMAP_FOLDER_ITEM(item)->uid_list = g_slist_remove(
3226 IMAP_FOLDER_ITEM(item)->uid_list, numlist.data);
3227 dir = folder_item_get_path(item);
3228 if (is_dir_exist(dir))
3229 remove_numbered_files(dir, uid, uid);
3232 return IMAP_SUCCESS;
3235 static gint compare_msginfo(gconstpointer a, gconstpointer b)
3237 return ((MsgInfo *)a)->msgnum - ((MsgInfo *)b)->msgnum;
3240 static guint gslist_find_next_num(MsgNumberList **list, guint num)
3244 g_return_val_if_fail(list != NULL, -1);
3246 for (elem = *list; elem != NULL; elem = g_slist_next(elem))
3247 if (GPOINTER_TO_INT(elem->data) >= num)
3250 return elem != NULL ? GPOINTER_TO_INT(elem->data) : (gint)-1;
3254 * NEW and DELETED flags are not syncronized
3255 * - The NEW/RECENT flags in IMAP folders can not really be directly
3256 * modified by Sylpheed
3257 * - The DELETE/DELETED flag in IMAP and Sylpheed don't have the same
3258 * meaning, in IMAP it always removes the messages from the FolderItem
3259 * in Sylpheed it can mean to move the message to trash
3262 typedef struct _get_flags_data {
3265 MsgInfoList *msginfo_list;
3266 GRelation *msgflags;
3270 static /*gint*/ void *imap_get_flags_thread(void *data)
3272 get_flags_data *stuff = (get_flags_data *)data;
3273 Folder *folder = stuff->folder;
3274 FolderItem *item = stuff->item;
3275 MsgInfoList *msginfo_list = stuff->msginfo_list;
3276 GRelation *msgflags = stuff->msgflags;
3277 IMAPSession *session;
3278 GSList *sorted_list;
3279 GSList *unseen = NULL, *answered = NULL, *flagged = NULL;
3280 GSList *p_unseen, *p_answered, *p_flagged;
3282 GSList *seq_list, *cur;
3283 gboolean reverse_seen = FALSE;
3286 gint exists_cnt, recent_cnt, unseen_cnt, uid_next;
3287 guint32 uidvalidity;
3288 gboolean selected_folder;
3290 if (folder == NULL || item == NULL) {
3292 return GINT_TO_POINTER(-1);
3294 if (msginfo_list == NULL) {
3296 return GINT_TO_POINTER(0);
3299 session = imap_session_get(folder);
3300 if (session == NULL) {
3302 return GINT_TO_POINTER(-1);
3305 selected_folder = (session->mbox != NULL) &&
3306 (!strcmp(session->mbox, item->path));
3308 if (!selected_folder) {
3309 ok = imap_status(session, IMAP_FOLDER(folder), item->path,
3310 &exists_cnt, &recent_cnt, &uid_next, &uidvalidity, &unseen_cnt, TRUE);
3311 ok = imap_select(session, IMAP_FOLDER(folder), item->path,
3312 NULL, NULL, NULL, NULL, TRUE);
3313 if (ok != IMAP_SUCCESS) {
3315 return GINT_TO_POINTER(-1);
3318 if (unseen_cnt > exists_cnt / 2)
3319 reverse_seen = TRUE;
3322 if (item->unread_msgs > item->total_msgs / 2)
3323 reverse_seen = TRUE;
3326 cmd_buf = g_string_new(NULL);
3328 sorted_list = g_slist_sort(g_slist_copy(msginfo_list), compare_msginfo);
3330 seq_list = imap_get_lep_set_from_msglist(msginfo_list);
3332 for (cur = seq_list; cur != NULL; cur = g_slist_next(cur)) {
3333 struct mailimap_set * imapset;
3334 clist * lep_uidlist;
3337 imapset = cur->data;
3339 r = imap_threaded_search(folder, IMAP_SEARCH_TYPE_SEEN,
3340 imapset, &lep_uidlist);
3343 r = imap_threaded_search(folder,
3344 IMAP_SEARCH_TYPE_UNSEEN,
3345 imapset, &lep_uidlist);
3347 if (r == MAILIMAP_NO_ERROR) {
3350 uidlist = imap_uid_list_from_lep(lep_uidlist);
3351 mailimap_search_result_free(lep_uidlist);
3353 unseen = g_slist_concat(unseen, uidlist);
3356 r = imap_threaded_search(folder, IMAP_SEARCH_TYPE_ANSWERED,
3357 imapset, &lep_uidlist);
3358 if (r == MAILIMAP_NO_ERROR) {
3361 uidlist = imap_uid_list_from_lep(lep_uidlist);
3362 mailimap_search_result_free(lep_uidlist);
3364 answered = g_slist_concat(answered, uidlist);
3367 r = imap_threaded_search(folder, IMAP_SEARCH_TYPE_FLAGGED,
3368 imapset, &lep_uidlist);
3369 if (r == MAILIMAP_NO_ERROR) {
3372 uidlist = imap_uid_list_from_lep(lep_uidlist);
3373 mailimap_search_result_free(lep_uidlist);
3375 flagged = g_slist_concat(flagged, uidlist);
3380 p_answered = answered;
3381 p_flagged = flagged;
3383 for (elem = sorted_list; elem != NULL; elem = g_slist_next(elem)) {
3388 msginfo = (MsgInfo *) elem->data;
3389 flags = msginfo->flags.perm_flags;
3390 wasnew = (flags & MSG_NEW);
3391 flags &= ~((reverse_seen ? 0 : MSG_UNREAD | MSG_NEW) | MSG_REPLIED | MSG_MARKED);
3393 flags |= MSG_UNREAD | (wasnew ? MSG_NEW : 0);
3394 if (gslist_find_next_num(&p_unseen, msginfo->msgnum) == msginfo->msgnum) {
3395 if (!reverse_seen) {
3396 flags |= MSG_UNREAD | (wasnew ? MSG_NEW : 0);
3398 flags &= ~(MSG_UNREAD | MSG_NEW);
3401 if (gslist_find_next_num(&p_answered, msginfo->msgnum) == msginfo->msgnum)
3402 flags |= MSG_REPLIED;
3403 if (gslist_find_next_num(&p_flagged, msginfo->msgnum) == msginfo->msgnum)
3404 flags |= MSG_MARKED;
3405 g_relation_insert(msgflags, msginfo, GINT_TO_POINTER(flags));
3408 imap_lep_set_free(seq_list);
3409 g_slist_free(flagged);
3410 g_slist_free(answered);
3411 g_slist_free(unseen);
3412 g_slist_free(sorted_list);
3413 g_string_free(cmd_buf, TRUE);
3416 return GINT_TO_POINTER(0);
3419 static gint imap_get_flags(Folder *folder, FolderItem *item,
3420 MsgInfoList *msginfo_list, GRelation *msgflags)
3423 get_flags_data *data = g_new0(get_flags_data, 1);
3425 data->folder = folder;
3427 data->msginfo_list = msginfo_list;
3428 data->msgflags = msgflags;
3430 if (prefs_common.work_offline && !imap_gtk_should_override()) {
3435 result = GPOINTER_TO_INT(imap_get_flags_thread(data));
3442 static gboolean process_flags(gpointer key, gpointer value, gpointer user_data)
3444 gboolean flags_set = GPOINTER_TO_INT(user_data);
3445 gint flags_value = GPOINTER_TO_INT(key);
3446 hashtable_data *data = (hashtable_data *)value;
3448 data->msglist = g_slist_reverse(data->msglist);
3450 debug_print("IMAP %ssetting flags to %d for %d messages\n",
3453 g_slist_length(data->msglist));
3454 imap_set_message_flags(data->session, data->msglist, flags_value, flags_set);
3456 g_slist_free(data->msglist);
3461 static void process_hashtable(void)
3463 MUTEX_TRYLOCK_OR_RETURN();
3464 if (flags_set_table) {
3465 g_hash_table_foreach_remove(flags_set_table, process_flags, GINT_TO_POINTER(TRUE));
3466 g_free(flags_set_table);
3467 flags_set_table = NULL;
3469 if (flags_unset_table) {
3470 g_hash_table_foreach_remove(flags_unset_table, process_flags, GINT_TO_POINTER(FALSE));
3471 g_free(flags_unset_table);
3472 flags_unset_table = NULL;
3477 static IMAPFolderItem *batching_item = NULL;
3479 static void imap_set_batch (Folder *folder, FolderItem *_item, gboolean batch)
3481 IMAPFolderItem *item = (IMAPFolderItem *)_item;
3483 g_return_if_fail(item != NULL);
3485 if (batch && batching_item != NULL) {
3486 g_warning("already batching on %s\n", batching_item->item.path);
3490 if (item->batching == batch)
3493 item->batching = batch;
3495 batching_item = batch?item:NULL;
3498 debug_print("IMAP switching to batch mode\n");
3499 if (flags_set_table) {
3500 g_warning("flags_set_table non-null but we just entered batch mode!\n");
3501 flags_set_table = NULL;
3503 if (flags_unset_table) {
3504 g_warning("flags_unset_table non-null but we just entered batch mode!\n");
3505 flags_unset_table = NULL;
3507 flags_set_table = g_hash_table_new(NULL, g_direct_equal);
3508 flags_unset_table = g_hash_table_new(NULL, g_direct_equal);
3510 debug_print("IMAP switching away from batch mode\n");
3512 process_hashtable();
3518 /* data types conversion libetpan <-> sylpheed */
3522 #define ETPAN_IMAP_MB_MARKED 1
3523 #define ETPAN_IMAP_MB_UNMARKED 2
3524 #define ETPAN_IMAP_MB_NOSELECT 4
3525 #define ETPAN_IMAP_MB_NOINFERIORS 8
3527 static int imap_flags_to_flags(struct mailimap_mbx_list_flags * imap_flags)
3533 if (imap_flags->mbf_type == MAILIMAP_MBX_LIST_FLAGS_SFLAG) {
3534 switch (imap_flags->mbf_sflag) {
3535 case MAILIMAP_MBX_LIST_SFLAG_MARKED:
3536 flags |= ETPAN_IMAP_MB_MARKED;
3538 case MAILIMAP_MBX_LIST_SFLAG_NOSELECT:
3539 flags |= ETPAN_IMAP_MB_NOSELECT;
3541 case MAILIMAP_MBX_LIST_SFLAG_UNMARKED:
3542 flags |= ETPAN_IMAP_MB_UNMARKED;
3547 for(cur = clist_begin(imap_flags->mbf_oflags) ; cur != NULL ;
3548 cur = clist_next(cur)) {
3549 struct mailimap_mbx_list_oflag * oflag;
3551 oflag = clist_content(cur);
3553 switch (oflag->of_type) {
3554 case MAILIMAP_MBX_LIST_OFLAG_NOINFERIORS:
3555 flags |= ETPAN_IMAP_MB_NOINFERIORS;
3563 static GSList * imap_list_from_lep(IMAPFolder * folder,
3564 clist * list, const gchar * real_path)
3571 for(iter = clist_begin(list) ; iter != NULL ;
3572 iter = clist_next(iter)) {
3573 struct mailimap_mailbox_list * mb;
3581 FolderItem *new_item;
3583 mb = clist_content(iter);
3586 if (mb->mb_flag != NULL)
3587 flags = imap_flags_to_flags(mb->mb_flag);
3589 delimiter = mb->mb_delimiter;
3592 dup_name = strdup(name);
3593 if (delimiter != '\0')
3594 subst_char(dup_name, delimiter, '/');
3596 base = g_path_get_basename(dup_name);
3597 if (base[0] == '.') {
3603 if (strcmp(dup_name, real_path) == 0) {
3609 loc_name = imap_modified_utf7_to_utf8(base);
3610 loc_path = imap_modified_utf7_to_utf8(dup_name);
3612 new_item = folder_item_new(FOLDER(folder), loc_name, loc_path);
3613 if ((flags & ETPAN_IMAP_MB_NOINFERIORS) != 0)
3614 new_item->no_sub = TRUE;
3615 if (strcmp(dup_name, "INBOX") != 0 &&
3616 ((flags & ETPAN_IMAP_MB_NOSELECT) != 0))
3617 new_item->no_select = TRUE;
3619 item_list = g_slist_append(item_list, new_item);
3621 debug_print("folder '%s' found.\n", loc_path);
3632 static GSList * imap_get_lep_set_from_numlist(MsgNumberList *numlist)
3634 GSList *sorted_list, *cur;
3635 guint first, last, next;
3636 GSList *ret_list = NULL;
3638 struct mailimap_set * current_set;
3639 unsigned int item_count;
3641 if (numlist == NULL)
3645 current_set = mailimap_set_new_empty();
3647 sorted_list = g_slist_copy(numlist);
3648 sorted_list = g_slist_sort(sorted_list, g_int_compare);
3650 first = GPOINTER_TO_INT(sorted_list->data);
3653 for (cur = sorted_list; cur != NULL; cur = g_slist_next(cur)) {
3654 if (GPOINTER_TO_INT(cur->data) == 0)
3659 last = GPOINTER_TO_INT(cur->data);
3661 next = GPOINTER_TO_INT(cur->next->data);
3665 if (last + 1 != next || next == 0) {
3667 struct mailimap_set_item * item;
3668 item = mailimap_set_item_new(first, last);
3669 mailimap_set_add(current_set, item);
3674 if (count >= IMAP_SET_MAX_COUNT) {
3675 ret_list = g_slist_append(ret_list,
3677 current_set = mailimap_set_new_empty();
3684 if (clist_count(current_set->set_list) > 0) {
3685 ret_list = g_slist_append(ret_list,
3689 g_slist_free(sorted_list);
3694 static GSList * imap_get_lep_set_from_msglist(MsgInfoList *msglist)
3696 MsgNumberList *numlist = NULL;
3700 for (cur = msglist; cur != NULL; cur = g_slist_next(cur)) {
3701 MsgInfo *msginfo = (MsgInfo *) cur->data;
3703 numlist = g_slist_append(numlist, GINT_TO_POINTER(msginfo->msgnum));
3705 seq_list = imap_get_lep_set_from_numlist(numlist);
3706 g_slist_free(numlist);
3711 static GSList * imap_uid_list_from_lep(clist * list)
3718 for(iter = clist_begin(list) ; iter != NULL ;
3719 iter = clist_next(iter)) {
3722 puid = clist_content(iter);
3723 result = g_slist_append(result, GINT_TO_POINTER(* puid));
3729 static GSList * imap_uid_list_from_lep_tab(carray * list)
3736 for(i = 0 ; i < carray_count(list) ; i ++) {
3739 puid = carray_get(list, i);
3740 result = g_slist_append(result, GINT_TO_POINTER(* puid));
3746 static MsgInfo *imap_envelope_from_lep(struct imap_fetch_env_info * info,
3749 MsgInfo *msginfo = NULL;
3752 MsgFlags flags = {0, 0};
3754 MSG_SET_TMP_FLAGS(flags, MSG_IMAP);
3755 if (item->stype == F_QUEUE) {
3756 MSG_SET_TMP_FLAGS(flags, MSG_QUEUED);
3757 } else if (item->stype == F_DRAFT) {
3758 MSG_SET_TMP_FLAGS(flags, MSG_DRAFT);
3760 flags.perm_flags = info->flags;
3764 msginfo = procheader_parse_str(info->headers, flags, FALSE, FALSE);
3767 msginfo->msgnum = uid;
3768 msginfo->size = size;
3774 static void imap_lep_set_free(GSList *seq_list)
3778 for(cur = seq_list ; cur != NULL ; cur = g_slist_next(cur)) {
3779 struct mailimap_set * imapset;
3781 imapset = cur->data;
3782 mailimap_set_free(imapset);
3784 g_slist_free(seq_list);
3787 static struct mailimap_flag_list * imap_flag_to_lep(IMAPFlags flags)
3789 struct mailimap_flag_list * flag_list;
3791 flag_list = mailimap_flag_list_new_empty();
3793 if (IMAP_IS_SEEN(flags))
3794 mailimap_flag_list_add(flag_list,
3795 mailimap_flag_new_seen());
3796 if (IMAP_IS_ANSWERED(flags))
3797 mailimap_flag_list_add(flag_list,
3798 mailimap_flag_new_answered());
3799 if (IMAP_IS_FLAGGED(flags))
3800 mailimap_flag_list_add(flag_list,
3801 mailimap_flag_new_flagged());
3802 if (IMAP_IS_DELETED(flags))
3803 mailimap_flag_list_add(flag_list,
3804 mailimap_flag_new_deleted());
3805 if (IMAP_IS_DRAFT(flags))
3806 mailimap_flag_list_add(flag_list,
3807 mailimap_flag_new_draft());