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 messages from %s to %s ...\n",
1033 src->path, destdir);
1035 ok = imap_cmd_copy(session, seq_set, destdir, uid_mapping);
1036 if (ok != IMAP_SUCCESS) {
1037 g_relation_destroy(uid_mapping);
1038 imap_lep_set_free(seq_list);
1043 for (cur = msglist; cur != NULL; cur = g_slist_next(cur)) {
1044 MsgInfo *msginfo = (MsgInfo *)cur->data;
1047 tuples = g_relation_select(uid_mapping,
1048 GINT_TO_POINTER(msginfo->msgnum),
1050 if (tuples->len > 0) {
1051 gint num = GPOINTER_TO_INT(g_tuples_index(tuples, 0, 1));
1052 g_relation_insert(relation, msginfo,
1053 GPOINTER_TO_INT(num));
1057 g_relation_insert(relation, msginfo,
1058 GPOINTER_TO_INT(0));
1059 g_tuples_destroy(tuples);
1062 g_relation_destroy(uid_mapping);
1063 imap_lep_set_free(seq_list);
1067 IMAP_FOLDER_ITEM(dest)->lastuid = 0;
1068 IMAP_FOLDER_ITEM(dest)->uid_next = 0;
1069 g_slist_free(IMAP_FOLDER_ITEM(dest)->uid_list);
1070 IMAP_FOLDER_ITEM(dest)->uid_list = NULL;
1074 if (ok == IMAP_SUCCESS)
1080 static gint imap_copy_msg(Folder *folder, FolderItem *dest, MsgInfo *msginfo)
1084 g_return_val_if_fail(msginfo != NULL, -1);
1086 msglist.data = msginfo;
1087 msglist.next = NULL;
1089 return imap_copy_msgs(folder, dest, &msglist, NULL);
1092 static gint imap_copy_msgs(Folder *folder, FolderItem *dest,
1093 MsgInfoList *msglist, GRelation *relation)
1099 g_return_val_if_fail(folder != NULL, -1);
1100 g_return_val_if_fail(dest != NULL, -1);
1101 g_return_val_if_fail(msglist != NULL, -1);
1103 msginfo = (MsgInfo *)msglist->data;
1104 g_return_val_if_fail(msginfo->folder != NULL, -1);
1106 if (folder == msginfo->folder->folder) {
1107 ret = imap_do_copy_msgs(folder, dest, msglist, relation);
1111 file_list = procmsg_get_message_file_list(msglist);
1112 g_return_val_if_fail(file_list != NULL, -1);
1114 ret = imap_add_msgs(folder, dest, file_list, relation);
1116 procmsg_message_file_list_free(file_list);
1122 static gint imap_do_remove_msgs(Folder *folder, FolderItem *dest,
1123 MsgInfoList *msglist, GRelation *relation)
1126 GSList *seq_list = NULL, *cur;
1128 IMAPSession *session;
1129 gint ok = IMAP_SUCCESS;
1130 GRelation *uid_mapping;
1132 g_return_val_if_fail(folder != NULL, -1);
1133 g_return_val_if_fail(dest != NULL, -1);
1134 g_return_val_if_fail(msglist != NULL, -1);
1136 MUTEX_TRYLOCK_OR_RETURN_VAL(-1);
1138 session = imap_session_get(folder);
1143 msginfo = (MsgInfo *)msglist->data;
1145 ok = imap_select(session, IMAP_FOLDER(folder), msginfo->folder->path,
1146 NULL, NULL, NULL, NULL, FALSE);
1147 if (ok != IMAP_SUCCESS) {
1152 destdir = imap_get_real_path(IMAP_FOLDER(folder), dest->path);
1153 for (cur = msglist; cur; cur = cur->next) {
1154 msginfo = (MsgInfo *)cur->data;
1155 seq_list = g_slist_append(seq_list, GINT_TO_POINTER(msginfo->msgnum));
1158 uid_mapping = g_relation_new(2);
1159 g_relation_index(uid_mapping, 0, g_direct_hash, g_direct_equal);
1161 ok = imap_set_message_flags
1162 (IMAP_SESSION(REMOTE_FOLDER(folder)->session),
1163 seq_list, IMAP_FLAG_DELETED, TRUE);
1164 if (ok != IMAP_SUCCESS) {
1165 log_warning(_("can't set deleted flags\n"));
1169 ok = imap_cmd_expunge(session);
1170 if (ok != IMAP_SUCCESS) {
1171 log_warning(_("can't expunge\n"));
1176 g_relation_destroy(uid_mapping);
1177 g_slist_free(seq_list);
1182 if (ok == IMAP_SUCCESS)
1188 static gint imap_remove_msgs(Folder *folder, FolderItem *dest,
1189 MsgInfoList *msglist, GRelation *relation)
1193 g_return_val_if_fail(folder != NULL, -1);
1194 g_return_val_if_fail(dest != NULL, -1);
1195 if (msglist == NULL)
1198 msginfo = (MsgInfo *)msglist->data;
1199 g_return_val_if_fail(msginfo->folder != NULL, -1);
1201 return imap_do_remove_msgs(folder, dest, msglist, relation);
1204 static gint imap_remove_all_msg(Folder *folder, FolderItem *item)
1206 GSList *list = folder_item_get_msg_list(item);
1207 gint res = imap_remove_msgs(folder, item, list, NULL);
1212 static gboolean imap_is_msg_changed(Folder *folder, FolderItem *item,
1215 /* TODO: properly implement this method */
1219 static gint imap_close(Folder *folder, FolderItem *item)
1224 static gint imap_scan_tree(Folder *folder)
1226 FolderItem *item = NULL;
1227 IMAPSession *session;
1228 gchar *root_folder = NULL;
1230 g_return_val_if_fail(folder != NULL, -1);
1231 g_return_val_if_fail(folder->account != NULL, -1);
1233 MUTEX_TRYLOCK_OR_RETURN_VAL(-1);
1235 session = imap_session_get(folder);
1237 if (!folder->node) {
1238 folder_tree_destroy(folder);
1239 item = folder_item_new(folder, folder->name, NULL);
1240 item->folder = folder;
1241 folder->node = item->node = g_node_new(item);
1247 if (folder->account->imap_dir && *folder->account->imap_dir) {
1252 Xstrdup_a(root_folder, folder->account->imap_dir, {MUTEX_UNLOCK();return -1;});
1253 extract_quote(root_folder, '"');
1254 subst_char(root_folder,
1255 imap_get_path_separator(IMAP_FOLDER(folder),
1258 strtailchomp(root_folder, '/');
1259 real_path = imap_get_real_path
1260 (IMAP_FOLDER(folder), root_folder);
1261 debug_print("IMAP root directory: %s\n", real_path);
1263 /* check if root directory exist */
1265 r = imap_threaded_list(session->folder, "", real_path,
1267 if ((r != MAILIMAP_NO_ERROR) || (clist_count(lep_list) == 0)) {
1268 if (!folder->node) {
1269 item = folder_item_new(folder, folder->name, NULL);
1270 item->folder = folder;
1271 folder->node = item->node = g_node_new(item);
1277 mailimap_list_result_free(lep_list);
1283 item = FOLDER_ITEM(folder->node->data);
1284 if (!item || ((item->path || root_folder) &&
1285 strcmp2(item->path, root_folder) != 0)) {
1286 folder_tree_destroy(folder);
1287 item = folder_item_new(folder, folder->name, root_folder);
1288 item->folder = folder;
1289 folder->node = item->node = g_node_new(item);
1292 imap_scan_tree_recursive(session, FOLDER_ITEM(folder->node->data));
1293 imap_create_missing_folders(folder);
1299 static gint imap_scan_tree_recursive(IMAPSession *session, FolderItem *item)
1302 IMAPFolder *imapfolder;
1303 FolderItem *new_item;
1304 GSList *item_list, *cur;
1307 gchar *wildcard_path;
1313 g_return_val_if_fail(item != NULL, -1);
1314 g_return_val_if_fail(item->folder != NULL, -1);
1315 g_return_val_if_fail(item->no_sub == FALSE, -1);
1317 folder = item->folder;
1318 imapfolder = IMAP_FOLDER(folder);
1320 separator = imap_get_path_separator(imapfolder, item->path);
1322 if (folder->ui_func)
1323 folder->ui_func(folder, item, folder->ui_func_data);
1326 wildcard[0] = separator;
1329 real_path = imap_get_real_path(imapfolder, item->path);
1333 real_path = g_strdup("");
1336 Xstrcat_a(wildcard_path, real_path, wildcard,
1337 {g_free(real_path); return IMAP_ERROR;});
1339 r = imap_threaded_list(folder, "", wildcard_path, &lep_list);
1340 if (r != MAILIMAP_NO_ERROR) {
1344 item_list = imap_list_from_lep(imapfolder,
1345 lep_list, real_path);
1346 mailimap_list_result_free(lep_list);
1351 node = item->node->children;
1352 while (node != NULL) {
1353 FolderItem *old_item = FOLDER_ITEM(node->data);
1354 GNode *next = node->next;
1357 for (cur = item_list; cur != NULL; cur = cur->next) {
1358 FolderItem *cur_item = FOLDER_ITEM(cur->data);
1359 if (!strcmp2(old_item->path, cur_item->path)) {
1360 new_item = cur_item;
1365 debug_print("folder '%s' not found. removing...\n",
1367 folder_item_remove(old_item);
1369 old_item->no_sub = new_item->no_sub;
1370 old_item->no_select = new_item->no_select;
1371 if (old_item->no_sub == TRUE && node->children) {
1372 debug_print("folder '%s' doesn't have "
1373 "subfolders. removing...\n",
1375 folder_item_remove_children(old_item);
1382 for (cur = item_list; cur != NULL; cur = cur->next) {
1383 FolderItem *cur_item = FOLDER_ITEM(cur->data);
1385 for (node = item->node->children; node != NULL;
1386 node = node->next) {
1387 if (!strcmp2(FOLDER_ITEM(node->data)->path,
1389 new_item = FOLDER_ITEM(node->data);
1390 folder_item_destroy(cur_item);
1396 new_item = cur_item;
1397 debug_print("new folder '%s' found.\n", new_item->path);
1398 folder_item_append(item, new_item);
1401 if (!strcmp(new_item->path, "INBOX")) {
1402 new_item->stype = F_INBOX;
1403 folder->inbox = new_item;
1404 } else if (!folder_item_parent(item) || item->stype == F_INBOX) {
1407 base = g_path_get_basename(new_item->path);
1409 if (!folder->outbox && !g_ascii_strcasecmp(base, "Sent")) {
1410 new_item->stype = F_OUTBOX;
1411 folder->outbox = new_item;
1412 } else if (!folder->draft && !g_ascii_strcasecmp(base, "Drafts")) {
1413 new_item->stype = F_DRAFT;
1414 folder->draft = new_item;
1415 } else if (!folder->queue && !g_ascii_strcasecmp(base, "Queue")) {
1416 new_item->stype = F_QUEUE;
1417 folder->queue = new_item;
1418 } else if (!folder->trash && !g_ascii_strcasecmp(base, "Trash")) {
1419 new_item->stype = F_TRASH;
1420 folder->trash = new_item;
1425 if (new_item->no_sub == FALSE)
1426 imap_scan_tree_recursive(session, new_item);
1429 g_slist_free(item_list);
1431 return IMAP_SUCCESS;
1434 static gint imap_create_tree(Folder *folder)
1436 g_return_val_if_fail(folder != NULL, -1);
1437 g_return_val_if_fail(folder->node != NULL, -1);
1438 g_return_val_if_fail(folder->node->data != NULL, -1);
1439 g_return_val_if_fail(folder->account != NULL, -1);
1441 imap_scan_tree(folder);
1442 imap_create_missing_folders(folder);
1447 static void imap_create_missing_folders(Folder *folder)
1449 g_return_if_fail(folder != NULL);
1452 folder->inbox = imap_create_special_folder
1453 (folder, F_INBOX, "INBOX");
1455 folder->trash = imap_create_special_folder
1456 (folder, F_TRASH, "Trash");
1459 static FolderItem *imap_create_special_folder(Folder *folder,
1460 SpecialFolderItemType stype,
1464 FolderItem *new_item;
1466 g_return_val_if_fail(folder != NULL, NULL);
1467 g_return_val_if_fail(folder->node != NULL, NULL);
1468 g_return_val_if_fail(folder->node->data != NULL, NULL);
1469 g_return_val_if_fail(folder->account != NULL, NULL);
1470 g_return_val_if_fail(name != NULL, NULL);
1472 item = FOLDER_ITEM(folder->node->data);
1473 new_item = imap_create_folder(folder, item, name);
1476 g_warning("Can't create '%s'\n", name);
1477 if (!folder->inbox) return NULL;
1479 new_item = imap_create_folder(folder, folder->inbox, name);
1481 g_warning("Can't create '%s' under INBOX\n", name);
1483 new_item->stype = stype;
1485 new_item->stype = stype;
1490 static gchar *imap_folder_get_path(Folder *folder)
1494 g_return_val_if_fail(folder != NULL, NULL);
1495 g_return_val_if_fail(folder->account != NULL, NULL);
1497 folder_path = g_strconcat(get_imap_cache_dir(),
1499 folder->account->recv_server,
1501 folder->account->userid,
1507 static gchar *imap_item_get_path(Folder *folder, FolderItem *item)
1509 gchar *folder_path, *path;
1511 g_return_val_if_fail(folder != NULL, NULL);
1512 g_return_val_if_fail(item != NULL, NULL);
1513 folder_path = imap_folder_get_path(folder);
1515 g_return_val_if_fail(folder_path != NULL, NULL);
1516 if (folder_path[0] == G_DIR_SEPARATOR) {
1518 path = g_strconcat(folder_path, G_DIR_SEPARATOR_S,
1521 path = g_strdup(folder_path);
1524 path = g_strconcat(get_home_dir(), G_DIR_SEPARATOR_S,
1525 folder_path, G_DIR_SEPARATOR_S,
1528 path = g_strconcat(get_home_dir(), G_DIR_SEPARATOR_S,
1531 g_free(folder_path);
1536 static FolderItem *imap_create_folder(Folder *folder, FolderItem *parent,
1539 gchar *dirpath, *imap_path;
1540 IMAPSession *session;
1541 FolderItem *new_item;
1547 g_return_val_if_fail(folder != NULL, NULL);
1548 g_return_val_if_fail(folder->account != NULL, NULL);
1549 g_return_val_if_fail(parent != NULL, NULL);
1550 g_return_val_if_fail(name != NULL, NULL);
1552 MUTEX_TRYLOCK_OR_RETURN_VAL(NULL);
1554 session = imap_session_get(folder);
1560 if (!folder_item_parent(parent) && strcmp(name, "INBOX") == 0)
1561 dirpath = g_strdup(name);
1562 else if (parent->path)
1563 dirpath = g_strconcat(parent->path, "/", name, NULL);
1564 else if ((p = strchr(name, '/')) != NULL && *(p + 1) != '\0')
1565 dirpath = g_strdup(name);
1566 else if (folder->account->imap_dir && *folder->account->imap_dir) {
1569 Xstrdup_a(imap_dir, folder->account->imap_dir, {MUTEX_UNLOCK();return NULL;});
1570 strtailchomp(imap_dir, '/');
1571 dirpath = g_strconcat(imap_dir, "/", name, NULL);
1573 dirpath = g_strdup(name);
1575 /* keep trailing directory separator to create a folder that contains
1577 imap_path = imap_utf8_to_modified_utf7(dirpath);
1578 strtailchomp(dirpath, '/');
1579 Xstrdup_a(new_name, name, {
1584 strtailchomp(new_name, '/');
1585 separator = imap_get_path_separator(IMAP_FOLDER(folder), imap_path);
1586 imap_path_separator_subst(imap_path, separator);
1587 subst_char(new_name, '/', separator);
1589 if (strcmp(name, "INBOX") != 0) {
1591 gboolean exist = FALSE;
1595 argbuf = g_ptr_array_new();
1596 r = imap_threaded_list(folder, "", imap_path, &lep_list);
1597 if (r != MAILIMAP_NO_ERROR) {
1598 log_warning(_("can't create mailbox: LIST failed\n"));
1601 ptr_array_free_strings(argbuf);
1602 g_ptr_array_free(argbuf, TRUE);
1607 if (clist_count(lep_list) > 0)
1611 ok = imap_cmd_create(session, imap_path);
1612 if (ok != IMAP_SUCCESS) {
1613 log_warning(_("can't create mailbox\n"));
1622 new_item = folder_item_new(folder, new_name, dirpath);
1623 folder_item_append(parent, new_item);
1627 dirpath = folder_item_get_path(new_item);
1628 if (!is_dir_exist(dirpath))
1629 make_dir_hier(dirpath);
1636 static gint imap_rename_folder(Folder *folder, FolderItem *item,
1641 gchar *real_oldpath;
1642 gchar *real_newpath;
1644 gchar *old_cache_dir;
1645 gchar *new_cache_dir;
1646 IMAPSession *session;
1649 gint exists, recent, unseen;
1650 guint32 uid_validity;
1652 g_return_val_if_fail(folder != NULL, -1);
1653 g_return_val_if_fail(item != NULL, -1);
1654 g_return_val_if_fail(item->path != NULL, -1);
1655 g_return_val_if_fail(name != NULL, -1);
1657 MUTEX_TRYLOCK_OR_RETURN_VAL(-1);
1659 if (strchr(name, imap_get_path_separator(IMAP_FOLDER(folder), item->path)) != NULL) {
1660 g_warning(_("New folder name must not contain the namespace "
1666 session = imap_session_get(folder);
1671 real_oldpath = imap_get_real_path(IMAP_FOLDER(folder), item->path);
1673 g_free(session->mbox);
1674 session->mbox = NULL;
1675 ok = imap_cmd_examine(session, "INBOX",
1676 &exists, &recent, &unseen, &uid_validity, FALSE);
1677 if (ok != IMAP_SUCCESS) {
1678 g_free(real_oldpath);
1683 separator = imap_get_path_separator(IMAP_FOLDER(folder), item->path);
1684 if (strchr(item->path, G_DIR_SEPARATOR)) {
1685 dirpath = g_path_get_dirname(item->path);
1686 newpath = g_strconcat(dirpath, G_DIR_SEPARATOR_S, name, NULL);
1689 newpath = g_strdup(name);
1691 real_newpath = imap_utf8_to_modified_utf7(newpath);
1692 imap_path_separator_subst(real_newpath, separator);
1694 ok = imap_cmd_rename(session, real_oldpath, real_newpath);
1695 if (ok != IMAP_SUCCESS) {
1696 log_warning(_("can't rename mailbox: %s to %s\n"),
1697 real_oldpath, real_newpath);
1698 g_free(real_oldpath);
1700 g_free(real_newpath);
1706 item->name = g_strdup(name);
1708 old_cache_dir = folder_item_get_path(item);
1710 paths[0] = g_strdup(item->path);
1712 g_node_traverse(item->node, G_PRE_ORDER, G_TRAVERSE_ALL, -1,
1713 imap_rename_folder_func, paths);
1715 if (is_dir_exist(old_cache_dir)) {
1716 new_cache_dir = folder_item_get_path(item);
1717 if (rename(old_cache_dir, new_cache_dir) < 0) {
1718 FILE_OP_ERROR(old_cache_dir, "rename");
1720 g_free(new_cache_dir);
1723 g_free(old_cache_dir);
1726 g_free(real_oldpath);
1727 g_free(real_newpath);
1733 static gint imap_remove_folder_real(Folder *folder, FolderItem *item)
1736 IMAPSession *session;
1739 gint exists, recent, unseen;
1740 guint32 uid_validity;
1742 g_return_val_if_fail(folder != NULL, -1);
1743 g_return_val_if_fail(item != NULL, -1);
1744 g_return_val_if_fail(item->path != NULL, -1);
1746 MUTEX_TRYLOCK_OR_RETURN_VAL(-1);
1748 session = imap_session_get(folder);
1753 path = imap_get_real_path(IMAP_FOLDER(folder), item->path);
1755 ok = imap_cmd_examine(session, "INBOX",
1756 &exists, &recent, &unseen, &uid_validity, FALSE);
1757 if (ok != IMAP_SUCCESS) {
1763 ok = imap_cmd_delete(session, path);
1764 if (ok != IMAP_SUCCESS) {
1765 log_warning(_("can't delete mailbox\n"));
1772 cache_dir = folder_item_get_path(item);
1773 if (is_dir_exist(cache_dir) && remove_dir_recursive(cache_dir) < 0)
1774 g_warning("can't remove directory '%s'\n", cache_dir);
1776 folder_item_remove(item);
1782 static gint imap_remove_folder(Folder *folder, FolderItem *item)
1786 g_return_val_if_fail(item != NULL, -1);
1787 g_return_val_if_fail(item->folder != NULL, -1);
1788 g_return_val_if_fail(item->node != NULL, -1);
1790 node = item->node->children;
1791 while (node != NULL) {
1793 if (imap_remove_folder(folder, FOLDER_ITEM(node->data)) < 0)
1797 debug_print("IMAP removing %s\n", item->path);
1799 if (imap_remove_all_msg(folder, item) < 0)
1801 return imap_remove_folder_real(folder, item);
1804 typedef struct _uncached_data {
1805 IMAPSession *session;
1807 MsgNumberList *numlist;
1813 static void *imap_get_uncached_messages_thread(void *data)
1815 uncached_data *stuff = (uncached_data *)data;
1816 IMAPSession *session = stuff->session;
1817 FolderItem *item = stuff->item;
1818 MsgNumberList *numlist = stuff->numlist;
1820 GSList *newlist = NULL;
1821 GSList *llast = NULL;
1822 GSList *seq_list, *cur;
1824 debug_print("uncached_messages\n");
1826 if (session == NULL || item == NULL || item->folder == NULL
1827 || FOLDER_CLASS(item->folder) != &imap_class) {
1832 seq_list = imap_get_lep_set_from_numlist(numlist);
1833 debug_print("get msgs info\n");
1834 for (cur = seq_list; cur != NULL; cur = g_slist_next(cur)) {
1835 struct mailimap_set * imapset;
1841 imapset = cur->data;
1843 r = imap_threaded_fetch_env(session->folder,
1844 imapset, &env_list);
1845 if (r != MAILIMAP_NO_ERROR)
1849 for(i = 0 ; i < carray_count(env_list) ; i ++) {
1850 struct imap_fetch_env_info * info;
1853 info = carray_get(env_list, i);
1854 msginfo = imap_envelope_from_lep(info, item);
1855 msginfo->folder = item;
1857 llast = newlist = g_slist_append(newlist, msginfo);
1859 llast = g_slist_append(llast, msginfo);
1860 llast = llast->next;
1865 imap_fetch_env_free(env_list);
1868 session_set_access_time(SESSION(session));
1874 #define MAX_MSG_NUM 50
1876 static GSList *imap_get_uncached_messages(IMAPSession *session,
1878 MsgNumberList *numlist)
1880 GSList *result = NULL;
1882 uncached_data *data = g_new0(uncached_data, 1);
1887 data->total = g_slist_length(numlist);
1888 debug_print("messages list : %i\n", data->total);
1889 while (cur != NULL) {
1890 GSList * partial_result;
1898 while (count < MAX_MSG_NUM) {
1903 if (newlist == NULL)
1904 llast = newlist = g_slist_append(newlist, p);
1906 llast = g_slist_append(llast, p);
1907 llast = llast->next;
1917 data->session = session;
1919 data->numlist = newlist;
1922 if (prefs_common.work_offline && !imap_gtk_should_override()) {
1928 (GSList *)imap_get_uncached_messages_thread(data);
1932 g_snprintf(buf, sizeof(buf), "%d / %d",
1933 data->cur, data->total);
1934 gtk_progress_bar_set_text
1935 (GTK_PROGRESS_BAR(mainwindow_get_mainwindow()->progressbar), buf);
1936 gtk_progress_bar_set_fraction
1937 (GTK_PROGRESS_BAR(mainwindow_get_mainwindow()->progressbar),
1938 (gfloat)data->cur / (gfloat)data->total);
1939 debug_print("update progress %g\n",
1940 (gfloat)data->cur / (gfloat)data->total);
1943 g_slist_free(newlist);
1945 result = g_slist_concat(result, partial_result);
1949 gtk_progress_bar_set_fraction
1950 (GTK_PROGRESS_BAR(mainwindow_get_mainwindow()->progressbar), 0);
1951 gtk_progress_bar_set_text
1952 (GTK_PROGRESS_BAR(mainwindow_get_mainwindow()->progressbar), "");
1953 statusbar_pop_all();
1958 static void imap_delete_all_cached_messages(FolderItem *item)
1962 g_return_if_fail(item != NULL);
1963 g_return_if_fail(item->folder != NULL);
1964 g_return_if_fail(FOLDER_CLASS(item->folder) == &imap_class);
1966 debug_print("Deleting all cached messages...\n");
1968 dir = folder_item_get_path(item);
1969 if (is_dir_exist(dir))
1970 remove_all_numbered_files(dir);
1973 debug_print("done.\n");
1976 static IMAPNameSpace *imap_find_namespace_from_list(GList *ns_list,
1979 IMAPNameSpace *namespace = NULL;
1980 gchar *tmp_path, *name;
1982 if (!path) path = "";
1984 for (; ns_list != NULL; ns_list = ns_list->next) {
1985 IMAPNameSpace *tmp_ns = ns_list->data;
1987 Xstrcat_a(tmp_path, path, "/", return namespace);
1988 Xstrdup_a(name, tmp_ns->name, return namespace);
1989 if (tmp_ns->separator && tmp_ns->separator != '/') {
1990 subst_char(tmp_path, tmp_ns->separator, '/');
1991 subst_char(name, tmp_ns->separator, '/');
1993 if (strncmp(tmp_path, name, strlen(name)) == 0)
2000 static IMAPNameSpace *imap_find_namespace(IMAPFolder *folder,
2003 IMAPNameSpace *namespace;
2005 g_return_val_if_fail(folder != NULL, NULL);
2007 namespace = imap_find_namespace_from_list(folder->ns_personal, path);
2008 if (namespace) return namespace;
2009 namespace = imap_find_namespace_from_list(folder->ns_others, path);
2010 if (namespace) return namespace;
2011 namespace = imap_find_namespace_from_list(folder->ns_shared, path);
2012 if (namespace) return namespace;
2017 static gchar imap_get_path_separator(IMAPFolder *folder, const gchar *path)
2019 IMAPNameSpace *namespace;
2020 gchar separator = '/';
2022 namespace = imap_find_namespace(folder, path);
2023 if (namespace && namespace->separator)
2024 separator = namespace->separator;
2029 static gchar *imap_get_real_path(IMAPFolder *folder, const gchar *path)
2034 g_return_val_if_fail(folder != NULL, NULL);
2035 g_return_val_if_fail(path != NULL, NULL);
2037 real_path = imap_utf8_to_modified_utf7(path);
2038 separator = imap_get_path_separator(folder, path);
2039 imap_path_separator_subst(real_path, separator);
2044 static gint imap_set_message_flags(IMAPSession *session,
2045 MsgNumberList *numlist,
2053 seq_list = imap_get_lep_set_from_numlist(numlist);
2055 for(cur = seq_list ; cur != NULL ; cur = g_slist_next(cur)) {
2056 struct mailimap_set * imapset;
2058 imapset = cur->data;
2060 ok = imap_cmd_store(session, imapset,
2064 imap_lep_set_free(seq_list);
2066 return IMAP_SUCCESS;
2069 typedef struct _select_data {
2070 IMAPSession *session;
2075 guint32 *uid_validity;
2079 static gint imap_select(IMAPSession *session, IMAPFolder *folder,
2081 gint *exists, gint *recent, gint *unseen,
2082 guint32 *uid_validity, gboolean block)
2086 gint exists_, recent_, unseen_;
2087 guint32 uid_validity_;
2089 if (!exists || !recent || !unseen || !uid_validity) {
2090 if (session->mbox && strcmp(session->mbox, path) == 0)
2091 return IMAP_SUCCESS;
2095 uid_validity = &uid_validity_;
2098 g_free(session->mbox);
2099 session->mbox = NULL;
2101 real_path = imap_get_real_path(folder, path);
2103 ok = imap_cmd_select(session, real_path,
2104 exists, recent, unseen, uid_validity, block);
2105 if (ok != IMAP_SUCCESS)
2106 log_warning(_("can't select folder: %s\n"), real_path);
2108 session->mbox = g_strdup(path);
2109 session->folder_content_changed = FALSE;
2116 static gint imap_status(IMAPSession *session, IMAPFolder *folder,
2118 gint *messages, gint *recent,
2119 guint32 *uid_next, guint32 *uid_validity,
2120 gint *unseen, gboolean block)
2124 struct mailimap_mailbox_data_status * data_status;
2127 r = imap_threaded_status(FOLDER(folder), path, &data_status);
2128 if (r != MAILIMAP_NO_ERROR)
2131 if (data_status->st_info_list == NULL) {
2132 mailimap_mailbox_data_status_free(data_status);
2137 for(iter = clist_begin(data_status->st_info_list) ; iter != NULL ;
2138 iter = clist_next(iter)) {
2139 struct mailimap_status_info * info;
2141 info = clist_content(iter);
2142 switch (info->st_att) {
2143 case MAILIMAP_STATUS_ATT_MESSAGES:
2144 * messages = info->st_value;
2145 got_values |= 1 << 0;
2148 case MAILIMAP_STATUS_ATT_RECENT:
2149 * recent = info->st_value;
2150 got_values |= 1 << 1;
2153 case MAILIMAP_STATUS_ATT_UIDNEXT:
2154 * uid_next = info->st_value;
2155 got_values |= 1 << 2;
2158 case MAILIMAP_STATUS_ATT_UIDVALIDITY:
2159 * uid_validity = info->st_value;
2160 got_values |= 1 << 3;
2163 case MAILIMAP_STATUS_ATT_UNSEEN:
2164 * unseen = info->st_value;
2165 got_values |= 1 << 4;
2169 mailimap_mailbox_data_status_free(data_status);
2171 if (got_values != ((1 << 4) + (1 << 3) +
2172 (1 << 2) + (1 << 1) + (1 << 0)))
2175 return IMAP_SUCCESS;
2178 static void imap_free_capabilities(IMAPSession *session)
2180 g_strfreev(session->capability);
2181 session->capability = NULL;
2184 /* low-level IMAP4rev1 commands */
2187 static gint imap_cmd_authenticate(IMAPSession *session, const gchar *user,
2188 const gchar *pass, IMAPAuthType type)
2195 gchar hexdigest[33];
2199 auth_type = "CRAM-MD5";
2201 imap_gen_send(session, "AUTHENTICATE %s", auth_type);
2202 ok = imap_gen_recv(session, &buf);
2203 if (ok != IMAP_SUCCESS || buf[0] != '+' || buf[1] != ' ') {
2208 challenge = g_malloc(strlen(buf + 2) + 1);
2209 challenge_len = base64_decode(challenge, buf + 2, -1);
2210 challenge[challenge_len] = '\0';
2212 log_print("IMAP< [Decoded: %s]\n", challenge);
2214 md5_hex_hmac(hexdigest, challenge, challenge_len, pass, strlen(pass));
2217 response = g_strdup_printf("%s %s", user, hexdigest);
2218 log_print("IMAP> [Encoded: %s]\n", response);
2219 response64 = g_malloc((strlen(response) + 3) * 2 + 1);
2220 base64_encode(response64, response, strlen(response));
2223 log_print("IMAP> %s\n", response64);
2224 sock_puts(SESSION(session)->sock, response64);
2225 ok = imap_cmd_ok(session, NULL);
2226 if (ok != IMAP_SUCCESS)
2227 log_warning(_("IMAP4 authentication failed.\n"));
2233 static gint imap_cmd_login(IMAPSession *session,
2234 const gchar *user, const gchar *pass)
2239 r = imap_threaded_login(session->folder, user, pass);
2240 if (r != MAILIMAP_NO_ERROR)
2248 static gint imap_cmd_logout(IMAPSession *session)
2250 imap_threaded_disconnect(session->folder);
2252 return IMAP_SUCCESS;
2255 static gint imap_cmd_noop(IMAPSession *session)
2258 unsigned int exists;
2260 r = imap_threaded_noop(session->folder, &exists);
2261 if (r != MAILIMAP_NO_ERROR)
2264 session->exists = exists;
2266 return IMAP_SUCCESS;
2270 static gint imap_cmd_starttls(IMAPSession *session)
2274 r = imap_threaded_starttls(session->folder);
2275 if (r != MAILIMAP_NO_ERROR)
2278 return IMAP_SUCCESS;
2282 static gint imap_cmd_select(IMAPSession *session, const gchar *folder,
2283 gint *exists, gint *recent, gint *unseen,
2284 guint32 *uid_validity, gboolean block)
2288 r = imap_threaded_select(session->folder, folder,
2289 exists, recent, unseen, uid_validity);
2290 if (r != MAILIMAP_NO_ERROR) {
2294 return IMAP_SUCCESS;
2297 static gint imap_cmd_examine(IMAPSession *session, const gchar *folder,
2298 gint *exists, gint *recent, gint *unseen,
2299 guint32 *uid_validity, gboolean block)
2303 r = imap_threaded_examine(session->folder, folder,
2304 exists, recent, unseen, uid_validity);
2305 if (r != MAILIMAP_NO_ERROR) {
2309 return IMAP_SUCCESS;
2312 static gint imap_cmd_create(IMAPSession *session, const gchar *folder)
2316 r = imap_threaded_create(session->folder, folder);
2317 if (r != MAILIMAP_NO_ERROR)
2320 return IMAP_SUCCESS;
2323 static gint imap_cmd_rename(IMAPSession *session, const gchar *old_folder,
2324 const gchar *new_folder)
2328 r = imap_threaded_rename(session->folder, old_folder,
2330 if (r != MAILIMAP_NO_ERROR)
2333 return IMAP_SUCCESS;
2336 static gint imap_cmd_delete(IMAPSession *session, const gchar *folder)
2340 r = imap_threaded_delete(session->folder, folder);
2341 if (r != MAILIMAP_NO_ERROR)
2344 return IMAP_SUCCESS;
2347 typedef struct _fetch_data {
2348 IMAPSession *session;
2350 const gchar *filename;
2356 static void *imap_cmd_fetch_thread(void *data)
2358 fetch_data *stuff = (fetch_data *)data;
2359 IMAPSession *session = stuff->session;
2360 guint32 uid = stuff->uid;
2361 const gchar *filename = stuff->filename;
2365 r = imap_threaded_fetch_content(session->folder,
2369 r = imap_threaded_fetch_content(session->folder,
2372 if (r != MAILIMAP_NO_ERROR)
2373 return GINT_TO_POINTER(IMAP_ERROR);
2375 return GINT_TO_POINTER(IMAP_SUCCESS);
2378 static gint imap_cmd_fetch(IMAPSession *session, guint32 uid,
2379 const gchar *filename, gboolean headers,
2382 fetch_data *data = g_new0(fetch_data, 1);
2385 data->session = session;
2387 data->filename = filename;
2388 data->headers = headers;
2391 if (prefs_common.work_offline && !imap_gtk_should_override()) {
2396 result = GPOINTER_TO_INT(imap_cmd_fetch_thread(data));
2402 static gint imap_cmd_append(IMAPSession *session, const gchar *destfolder,
2403 const gchar *file, IMAPFlags flags,
2406 struct mailimap_flag_list * flag_list;
2409 g_return_val_if_fail(file != NULL, IMAP_ERROR);
2411 flag_list = imap_flag_to_lep(flags);
2412 r = imap_threaded_append(session->folder, destfolder,
2415 if (new_uid != NULL)
2418 if (r != MAILIMAP_NO_ERROR)
2421 return IMAP_SUCCESS;
2424 static gint imap_cmd_copy(IMAPSession *session, struct mailimap_set * set,
2425 const gchar *destfolder, GRelation *uid_mapping)
2429 g_return_val_if_fail(session != NULL, IMAP_ERROR);
2430 g_return_val_if_fail(set != NULL, IMAP_ERROR);
2431 g_return_val_if_fail(destfolder != NULL, IMAP_ERROR);
2433 r = imap_threaded_copy(session->folder, set, destfolder);
2434 if (r != MAILIMAP_NO_ERROR)
2437 return IMAP_SUCCESS;
2440 static gint imap_cmd_store(IMAPSession *session, struct mailimap_set * set,
2441 IMAPFlags flags, int do_add)
2444 struct mailimap_flag_list * flag_list;
2445 struct mailimap_store_att_flags * store_att_flags;
2447 flag_list = imap_flag_to_lep(flags);
2451 mailimap_store_att_flags_new_add_flags_silent(flag_list);
2454 mailimap_store_att_flags_new_remove_flags_silent(flag_list);
2456 r = imap_threaded_store(session->folder, set, store_att_flags);
2457 if (r != MAILIMAP_NO_ERROR)
2460 return IMAP_SUCCESS;
2463 static gint imap_cmd_expunge(IMAPSession *session)
2467 if (prefs_common.work_offline && !imap_gtk_should_override()) {
2471 r = imap_threaded_expunge(session->folder);
2472 if (r != MAILIMAP_NO_ERROR)
2475 return IMAP_SUCCESS;
2478 static void imap_path_separator_subst(gchar *str, gchar separator)
2481 gboolean in_escape = FALSE;
2483 if (!separator || separator == '/') return;
2485 for (p = str; *p != '\0'; p++) {
2486 if (*p == '/' && !in_escape)
2488 else if (*p == '&' && *(p + 1) != '-' && !in_escape)
2490 else if (*p == '-' && in_escape)
2495 static gchar *imap_modified_utf7_to_utf8(const gchar *mutf7_str)
2497 static iconv_t cd = (iconv_t)-1;
2498 static gboolean iconv_ok = TRUE;
2501 size_t norm_utf7_len;
2503 gchar *to_str, *to_p;
2505 gboolean in_escape = FALSE;
2507 if (!iconv_ok) return g_strdup(mutf7_str);
2509 if (cd == (iconv_t)-1) {
2510 cd = iconv_open(CS_INTERNAL, CS_UTF_7);
2511 if (cd == (iconv_t)-1) {
2512 g_warning("iconv cannot convert UTF-7 to %s\n",
2515 return g_strdup(mutf7_str);
2519 /* modified UTF-7 to normal UTF-7 conversion */
2520 norm_utf7 = g_string_new(NULL);
2522 for (p = mutf7_str; *p != '\0'; p++) {
2523 /* replace: '&' -> '+',
2525 escaped ',' -> '/' */
2526 if (!in_escape && *p == '&') {
2527 if (*(p + 1) != '-') {
2528 g_string_append_c(norm_utf7, '+');
2531 g_string_append_c(norm_utf7, '&');
2534 } else if (in_escape && *p == ',') {
2535 g_string_append_c(norm_utf7, '/');
2536 } else if (in_escape && *p == '-') {
2537 g_string_append_c(norm_utf7, '-');
2540 g_string_append_c(norm_utf7, *p);
2544 norm_utf7_p = norm_utf7->str;
2545 norm_utf7_len = norm_utf7->len;
2546 to_len = strlen(mutf7_str) * 5;
2547 to_p = to_str = g_malloc(to_len + 1);
2549 if (iconv(cd, (ICONV_CONST gchar **)&norm_utf7_p, &norm_utf7_len,
2550 &to_p, &to_len) == -1) {
2551 g_warning(_("iconv cannot convert UTF-7 to %s\n"),
2552 conv_get_locale_charset_str());
2553 g_string_free(norm_utf7, TRUE);
2555 return g_strdup(mutf7_str);
2558 /* second iconv() call for flushing */
2559 iconv(cd, NULL, NULL, &to_p, &to_len);
2560 g_string_free(norm_utf7, TRUE);
2566 static gchar *imap_utf8_to_modified_utf7(const gchar *from)
2568 static iconv_t cd = (iconv_t)-1;
2569 static gboolean iconv_ok = TRUE;
2570 gchar *norm_utf7, *norm_utf7_p;
2571 size_t from_len, norm_utf7_len;
2573 gchar *from_tmp, *to, *p;
2574 gboolean in_escape = FALSE;
2576 if (!iconv_ok) return g_strdup(from);
2578 if (cd == (iconv_t)-1) {
2579 cd = iconv_open(CS_UTF_7, CS_INTERNAL);
2580 if (cd == (iconv_t)-1) {
2581 g_warning(_("iconv cannot convert %s to UTF-7\n"),
2584 return g_strdup(from);
2588 /* UTF-8 to normal UTF-7 conversion */
2589 Xstrdup_a(from_tmp, from, return g_strdup(from));
2590 from_len = strlen(from);
2591 norm_utf7_len = from_len * 5;
2592 Xalloca(norm_utf7, norm_utf7_len + 1, return g_strdup(from));
2593 norm_utf7_p = norm_utf7;
2595 #define IS_PRINT(ch) (isprint(ch) && IS_ASCII(ch))
2597 while (from_len > 0) {
2598 if (*from_tmp == '+') {
2599 *norm_utf7_p++ = '+';
2600 *norm_utf7_p++ = '-';
2604 } else if (IS_PRINT(*(guchar *)from_tmp)) {
2605 /* printable ascii char */
2606 *norm_utf7_p = *from_tmp;
2612 size_t conv_len = 0;
2614 /* unprintable char: convert to UTF-7 */
2616 while (!IS_PRINT(*(guchar *)p) && conv_len < from_len) {
2617 conv_len += g_utf8_skip[*(guchar *)p];
2618 p += g_utf8_skip[*(guchar *)p];
2621 from_len -= conv_len;
2622 if (iconv(cd, (ICONV_CONST gchar **)&from_tmp,
2624 &norm_utf7_p, &norm_utf7_len) == -1) {
2625 g_warning(_("iconv cannot convert UTF-8 to UTF-7\n"));
2626 return g_strdup(from);
2629 /* second iconv() call for flushing */
2630 iconv(cd, NULL, NULL, &norm_utf7_p, &norm_utf7_len);
2636 *norm_utf7_p = '\0';
2637 to_str = g_string_new(NULL);
2638 for (p = norm_utf7; p < norm_utf7_p; p++) {
2639 /* replace: '&' -> "&-",
2642 BASE64 '/' -> ',' */
2643 if (!in_escape && *p == '&') {
2644 g_string_append(to_str, "&-");
2645 } else if (!in_escape && *p == '+') {
2646 if (*(p + 1) == '-') {
2647 g_string_append_c(to_str, '+');
2650 g_string_append_c(to_str, '&');
2653 } else if (in_escape && *p == '/') {
2654 g_string_append_c(to_str, ',');
2655 } else if (in_escape && *p == '-') {
2656 g_string_append_c(to_str, '-');
2659 g_string_append_c(to_str, *p);
2665 g_string_append_c(to_str, '-');
2669 g_string_free(to_str, FALSE);
2674 static gboolean imap_rename_folder_func(GNode *node, gpointer data)
2676 FolderItem *item = node->data;
2677 gchar **paths = data;
2678 const gchar *oldpath = paths[0];
2679 const gchar *newpath = paths[1];
2681 gchar *new_itempath;
2684 oldpathlen = strlen(oldpath);
2685 if (strncmp(oldpath, item->path, oldpathlen) != 0) {
2686 g_warning("path doesn't match: %s, %s\n", oldpath, item->path);
2690 base = item->path + oldpathlen;
2691 while (*base == G_DIR_SEPARATOR) base++;
2693 new_itempath = g_strdup(newpath);
2695 new_itempath = g_strconcat(newpath, G_DIR_SEPARATOR_S, base,
2698 item->path = new_itempath;
2703 typedef struct _get_list_uid_data {
2705 IMAPFolderItem *item;
2706 GSList **msgnum_list;
2708 } get_list_uid_data;
2710 static void *get_list_of_uids_thread(void *data)
2712 get_list_uid_data *stuff = (get_list_uid_data *)data;
2713 Folder *folder = stuff->folder;
2714 IMAPFolderItem *item = stuff->item;
2715 GSList **msgnum_list = stuff->msgnum_list;
2716 gint ok, nummsgs = 0, lastuid_old;
2717 IMAPSession *session;
2718 GSList *uidlist, *elem;
2719 struct mailimap_set * set;
2720 clist * lep_uidlist;
2723 session = imap_session_get(folder);
2724 if (session == NULL) {
2726 return GINT_TO_POINTER(-1);
2729 ok = imap_select(session, IMAP_FOLDER(folder), item->item.path,
2730 NULL, NULL, NULL, NULL, TRUE);
2731 if (ok != IMAP_SUCCESS) {
2733 return GINT_TO_POINTER(-1);
2738 set = mailimap_set_new_interval(item->lastuid + 1, 0);
2739 r = imap_threaded_search(folder, IMAP_SEARCH_TYPE_SIMPLE, set,
2741 if (r == MAILIMAP_NO_ERROR) {
2742 GSList * fetchuid_list;
2745 imap_uid_list_from_lep(lep_uidlist);
2746 uidlist = g_slist_concat(fetchuid_list, uidlist);
2749 GSList * fetchuid_list;
2750 carray * lep_uidtab;
2752 r = imap_threaded_fetch_uid(folder, item->lastuid + 1,
2754 if (r == MAILIMAP_NO_ERROR) {
2756 imap_uid_list_from_lep_tab(lep_uidtab);
2757 uidlist = g_slist_concat(fetchuid_list, uidlist);
2761 lastuid_old = item->lastuid;
2762 *msgnum_list = g_slist_copy(item->uid_list);
2763 nummsgs = g_slist_length(*msgnum_list);
2764 debug_print("Got %d uids from cache\n", g_slist_length(item->uid_list));
2766 for (elem = uidlist; elem != NULL; elem = g_slist_next(elem)) {
2769 msgnum = GPOINTER_TO_INT(elem->data);
2770 if (msgnum > lastuid_old) {
2771 *msgnum_list = g_slist_prepend(*msgnum_list, GINT_TO_POINTER(msgnum));
2772 item->uid_list = g_slist_prepend(item->uid_list, GINT_TO_POINTER(msgnum));
2775 if(msgnum > item->lastuid)
2776 item->lastuid = msgnum;
2779 g_slist_free(uidlist);
2782 return GINT_TO_POINTER(nummsgs);
2785 static gint get_list_of_uids(Folder *folder, IMAPFolderItem *item, GSList **msgnum_list)
2788 get_list_uid_data *data = g_new0(get_list_uid_data, 1);
2790 data->folder = folder;
2792 data->msgnum_list = msgnum_list;
2794 if (prefs_common.work_offline && !imap_gtk_should_override()) {
2799 result = GPOINTER_TO_INT(get_list_of_uids_thread(data));
2805 gint imap_get_num_list(Folder *folder, FolderItem *_item, GSList **msgnum_list, gboolean *old_uids_valid)
2807 IMAPFolderItem *item = (IMAPFolderItem *)_item;
2808 IMAPSession *session;
2809 gint ok, nummsgs = 0, exists, recent, uid_val, uid_next, unseen;
2810 GSList *uidlist = NULL;
2812 gboolean selected_folder;
2814 debug_print("get_num_list\n");
2816 g_return_val_if_fail(folder != NULL, -1);
2817 g_return_val_if_fail(item != NULL, -1);
2818 g_return_val_if_fail(item->item.path != NULL, -1);
2819 g_return_val_if_fail(FOLDER_CLASS(folder) == &imap_class, -1);
2820 g_return_val_if_fail(folder->account != NULL, -1);
2822 MUTEX_TRYLOCK_OR_RETURN_VAL(-1);
2824 session = imap_session_get(folder);
2825 g_return_val_if_fail(session != NULL, -1);
2827 selected_folder = (session->mbox != NULL) &&
2828 (!strcmp(session->mbox, item->item.path));
2829 if (selected_folder) {
2830 ok = imap_cmd_noop(session);
2831 if (ok != IMAP_SUCCESS) {
2835 exists = session->exists;
2837 *old_uids_valid = TRUE;
2839 ok = imap_status(session, IMAP_FOLDER(folder), item->item.path,
2840 &exists, &recent, &uid_next, &uid_val, &unseen, FALSE);
2841 if (ok != IMAP_SUCCESS) {
2845 if(item->item.mtime == uid_val)
2846 *old_uids_valid = TRUE;
2848 *old_uids_valid = FALSE;
2850 debug_print("Freeing imap uid cache\n");
2852 g_slist_free(item->uid_list);
2853 item->uid_list = NULL;
2855 item->item.mtime = uid_val;
2857 imap_delete_all_cached_messages((FolderItem *)item);
2861 if (!selected_folder)
2862 item->uid_next = uid_next;
2864 /* If old uid_next matches new uid_next we can be sure no message
2865 was added to the folder */
2866 if (( selected_folder && !session->folder_content_changed) ||
2867 (!selected_folder && uid_next == item->uid_next)) {
2868 nummsgs = g_slist_length(item->uid_list);
2870 /* If number of messages is still the same we
2871 know our caches message numbers are still valid,
2872 otherwise if the number of messages has decrease
2873 we discard our cache to start a new scan to find
2874 out which numbers have been removed */
2875 if (exists == nummsgs) {
2876 *msgnum_list = g_slist_copy(item->uid_list);
2879 } else if (exists < nummsgs) {
2880 debug_print("Freeing imap uid cache");
2882 g_slist_free(item->uid_list);
2883 item->uid_list = NULL;
2888 *msgnum_list = NULL;
2893 nummsgs = get_list_of_uids(folder, item, &uidlist);
2900 if (nummsgs != exists) {
2901 /* Cache contains more messages then folder, we have cached
2902 an old UID of a message that was removed and new messages
2903 have been added too, otherwise the uid_next check would
2905 debug_print("Freeing imap uid cache");
2907 g_slist_free(item->uid_list);
2908 item->uid_list = NULL;
2910 g_slist_free(*msgnum_list);
2912 nummsgs = get_list_of_uids(folder, item, &uidlist);
2915 *msgnum_list = uidlist;
2917 dir = folder_item_get_path((FolderItem *)item);
2918 debug_print("removing old messages from %s\n", dir);
2919 remove_numbered_files_not_in_list(dir, *msgnum_list);
2923 debug_print("get_num_list - ok - %i\n", nummsgs);
2928 static MsgInfo *imap_parse_msg(const gchar *file, FolderItem *item)
2933 flags.perm_flags = MSG_NEW|MSG_UNREAD;
2934 flags.tmp_flags = 0;
2936 g_return_val_if_fail(item != NULL, NULL);
2937 g_return_val_if_fail(file != NULL, NULL);
2939 if (item->stype == F_QUEUE) {
2940 MSG_SET_TMP_FLAGS(flags, MSG_QUEUED);
2941 } else if (item->stype == F_DRAFT) {
2942 MSG_SET_TMP_FLAGS(flags, MSG_DRAFT);
2945 msginfo = procheader_parse_file(file, flags, FALSE, FALSE);
2946 if (!msginfo) return NULL;
2948 msginfo->plaintext_file = g_strdup(file);
2949 msginfo->folder = item;
2954 GSList *imap_get_msginfos(Folder *folder, FolderItem *item,
2955 GSList *msgnum_list)
2957 IMAPSession *session;
2958 MsgInfoList *ret = NULL;
2961 debug_print("get_msginfos\n");
2963 g_return_val_if_fail(folder != NULL, NULL);
2964 g_return_val_if_fail(item != NULL, NULL);
2965 g_return_val_if_fail(msgnum_list != NULL, NULL);
2967 session = imap_session_get(folder);
2968 g_return_val_if_fail(session != NULL, NULL);
2970 debug_print("IMAP getting msginfos\n");
2971 ok = imap_select(session, IMAP_FOLDER(folder), item->path,
2972 NULL, NULL, NULL, NULL, FALSE);
2973 if (ok != IMAP_SUCCESS)
2976 if (!(item->stype == F_QUEUE || item->stype == F_DRAFT)) {
2977 ret = g_slist_concat(ret,
2978 imap_get_uncached_messages(session, item,
2981 MsgNumberList *sorted_list, *elem;
2982 gint startnum, lastnum;
2984 sorted_list = g_slist_sort(g_slist_copy(msgnum_list), g_int_compare);
2986 startnum = lastnum = GPOINTER_TO_INT(sorted_list->data);
2988 for (elem = sorted_list;; elem = g_slist_next(elem)) {
2992 num = GPOINTER_TO_INT(elem->data);
2994 if (num > lastnum + 1 || elem == NULL) {
2996 for (i = startnum; i <= lastnum; ++i) {
2999 file = imap_fetch_msg(folder, item, i);
3001 MsgInfo *msginfo = imap_parse_msg(file, item);
3002 if (msginfo != NULL) {
3003 msginfo->msgnum = i;
3004 ret = g_slist_append(ret, msginfo);
3018 g_slist_free(sorted_list);
3024 MsgInfo *imap_get_msginfo(Folder *folder, FolderItem *item, gint uid)
3026 MsgInfo *msginfo = NULL;
3027 MsgInfoList *msginfolist;
3028 MsgNumberList numlist;
3030 numlist.next = NULL;
3031 numlist.data = GINT_TO_POINTER(uid);
3033 msginfolist = imap_get_msginfos(folder, item, &numlist);
3034 if (msginfolist != NULL) {
3035 msginfo = msginfolist->data;
3036 g_slist_free(msginfolist);
3042 gboolean imap_scan_required(Folder *folder, FolderItem *_item)
3044 IMAPSession *session;
3045 IMAPFolderItem *item = (IMAPFolderItem *)_item;
3046 gint ok, exists = 0, recent = 0, unseen = 0;
3047 guint32 uid_next, uid_val = 0;
3048 gboolean selected_folder;
3050 g_return_val_if_fail(folder != NULL, FALSE);
3051 g_return_val_if_fail(item != NULL, FALSE);
3052 g_return_val_if_fail(item->item.folder != NULL, FALSE);
3053 g_return_val_if_fail(FOLDER_CLASS(item->item.folder) == &imap_class, FALSE);
3055 if (item->item.path == NULL)
3058 session = imap_session_get(folder);
3059 g_return_val_if_fail(session != NULL, FALSE);
3061 selected_folder = (session->mbox != NULL) &&
3062 (!strcmp(session->mbox, item->item.path));
3063 if (selected_folder) {
3064 ok = imap_cmd_noop(session);
3065 if (ok != IMAP_SUCCESS)
3068 if (session->folder_content_changed
3069 || session->exists != item->item.total_msgs)
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());