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>
54 #include "procheader.h"
55 #include "prefs_account.h"
60 #include "prefs_common.h"
61 #include "inputdialog.h"
63 #include "remotefolder.h"
64 #include "alertpanel.h"
66 #include "statusbar.h"
68 #include "imap-thread.h"
70 typedef struct _IMAPFolder IMAPFolder;
71 typedef struct _IMAPSession IMAPSession;
72 typedef struct _IMAPNameSpace IMAPNameSpace;
73 typedef struct _IMAPFolderItem IMAPFolderItem;
75 #include "prefs_account.h"
77 #define IMAP_FOLDER(obj) ((IMAPFolder *)obj)
78 #define IMAP_FOLDER_ITEM(obj) ((IMAPFolderItem *)obj)
79 #define IMAP_SESSION(obj) ((IMAPSession *)obj)
85 /* list of IMAPNameSpace */
89 gchar last_seen_separator;
97 gboolean authenticated;
106 gboolean folder_content_changed;
111 struct _IMAPNameSpace
117 #define IMAP_SUCCESS 0
118 #define IMAP_SOCKET 2
119 #define IMAP_AUTHFAIL 3
120 #define IMAP_PROTOCOL 4
121 #define IMAP_SYNTAX 5
125 #define IMAPBUFSIZE 8192
129 IMAP_FLAG_SEEN = 1 << 0,
130 IMAP_FLAG_ANSWERED = 1 << 1,
131 IMAP_FLAG_FLAGGED = 1 << 2,
132 IMAP_FLAG_DELETED = 1 << 3,
133 IMAP_FLAG_DRAFT = 1 << 4
136 #define IMAP_IS_SEEN(flags) ((flags & IMAP_FLAG_SEEN) != 0)
137 #define IMAP_IS_ANSWERED(flags) ((flags & IMAP_FLAG_ANSWERED) != 0)
138 #define IMAP_IS_FLAGGED(flags) ((flags & IMAP_FLAG_FLAGGED) != 0)
139 #define IMAP_IS_DELETED(flags) ((flags & IMAP_FLAG_DELETED) != 0)
140 #define IMAP_IS_DRAFT(flags) ((flags & IMAP_FLAG_DRAFT) != 0)
143 #define IMAP4_PORT 143
145 #define IMAPS_PORT 993
148 #define IMAP_CMD_LIMIT 1000
150 struct _IMAPFolderItem
160 static void imap_folder_init (Folder *folder,
164 static Folder *imap_folder_new (const gchar *name,
166 static void imap_folder_destroy (Folder *folder);
168 static IMAPSession *imap_session_new (Folder *folder,
169 const PrefsAccount *account);
170 static void imap_session_authenticate(IMAPSession *session,
171 const PrefsAccount *account);
172 static void imap_session_destroy (Session *session);
174 static gchar *imap_fetch_msg (Folder *folder,
177 static gchar *imap_fetch_msg_full (Folder *folder,
182 static gint imap_add_msg (Folder *folder,
186 static gint imap_add_msgs (Folder *folder,
189 GRelation *relation);
191 static gint imap_copy_msg (Folder *folder,
194 static gint imap_copy_msgs (Folder *folder,
196 MsgInfoList *msglist,
197 GRelation *relation);
199 static gint imap_remove_msg (Folder *folder,
202 static gint imap_remove_msgs (Folder *folder,
204 MsgInfoList *msglist,
205 GRelation *relation);
206 static gint imap_remove_all_msg (Folder *folder,
209 static gboolean imap_is_msg_changed (Folder *folder,
213 static gint imap_close (Folder *folder,
216 static gint imap_scan_tree (Folder *folder);
218 static gint imap_create_tree (Folder *folder);
220 static FolderItem *imap_create_folder (Folder *folder,
223 static gint imap_rename_folder (Folder *folder,
226 static gint imap_remove_folder (Folder *folder,
229 static FolderItem *imap_folder_item_new (Folder *folder);
230 static void imap_folder_item_destroy (Folder *folder,
233 static IMAPSession *imap_session_get (Folder *folder);
235 static gint imap_auth (IMAPSession *session,
240 static gint imap_scan_tree_recursive (IMAPSession *session,
243 static void imap_create_missing_folders (Folder *folder);
244 static FolderItem *imap_create_special_folder
246 SpecialFolderItemType stype,
249 static gint imap_do_copy_msgs (Folder *folder,
251 MsgInfoList *msglist,
252 GRelation *relation);
254 static void imap_delete_all_cached_messages (FolderItem *item);
255 static void imap_set_batch (Folder *folder,
258 static gint imap_set_message_flags (IMAPSession *session,
259 MsgNumberList *numlist,
262 static gint imap_select (IMAPSession *session,
268 guint32 *uid_validity,
270 static gint imap_status (IMAPSession *session,
276 guint32 *uid_validity,
280 static IMAPNameSpace *imap_find_namespace (IMAPFolder *folder,
282 static gchar imap_get_path_separator (IMAPFolder *folder,
284 static gchar *imap_get_real_path (IMAPFolder *folder,
287 static void imap_free_capabilities (IMAPSession *session);
289 /* low-level IMAP4rev1 commands */
290 static gint imap_cmd_login (IMAPSession *session,
294 static gint imap_cmd_logout (IMAPSession *session);
295 static gint imap_cmd_noop (IMAPSession *session);
297 static gint imap_cmd_starttls (IMAPSession *session);
299 static gint imap_cmd_select (IMAPSession *session,
304 guint32 *uid_validity,
306 static gint imap_cmd_examine (IMAPSession *session,
311 guint32 *uid_validity,
313 static gint imap_cmd_create (IMAPSession *sock,
314 const gchar *folder);
315 static gint imap_cmd_rename (IMAPSession *sock,
316 const gchar *oldfolder,
317 const gchar *newfolder);
318 static gint imap_cmd_delete (IMAPSession *session,
319 const gchar *folder);
320 static gint imap_cmd_fetch (IMAPSession *sock,
322 const gchar *filename,
325 static gint imap_cmd_append (IMAPSession *session,
326 const gchar *destfolder,
330 static gint imap_cmd_copy (IMAPSession *session,
331 struct mailimap_set * set,
332 const gchar *destfolder,
333 GRelation *uid_mapping);
334 static gint imap_cmd_store (IMAPSession *session,
335 struct mailimap_set * set,
338 static gint imap_cmd_expunge (IMAPSession *session);
340 static void imap_path_separator_subst (gchar *str,
343 static gchar *imap_utf8_to_modified_utf7 (const gchar *from);
344 static gchar *imap_modified_utf7_to_utf8 (const gchar *mutf7_str);
346 static gboolean imap_rename_folder_func (GNode *node,
348 static gint imap_get_num_list (Folder *folder,
351 gboolean *old_uids_valid);
352 static GSList *imap_get_msginfos (Folder *folder,
354 GSList *msgnum_list);
355 static MsgInfo *imap_get_msginfo (Folder *folder,
358 static gboolean imap_scan_required (Folder *folder,
360 static void imap_change_flags (Folder *folder,
363 MsgPermFlags newflags);
364 static gint imap_get_flags (Folder *folder,
366 MsgInfoList *msglist,
367 GRelation *msgflags);
368 static gchar *imap_folder_get_path (Folder *folder);
369 static gchar *imap_item_get_path (Folder *folder,
371 static MsgInfo *imap_parse_msg(const gchar *file, FolderItem *item);
374 /* data types conversion libetpan <-> sylpheed */
375 static GSList * imap_list_from_lep(IMAPFolder * folder,
376 clist * list, const gchar * real_path);
377 static GSList * imap_get_lep_set_from_numlist(MsgNumberList *numlist);
378 static GSList * imap_get_lep_set_from_msglist(MsgInfoList *msglist);
379 static GSList * imap_uid_list_from_lep(clist * list);
380 static GSList * imap_uid_list_from_lep_tab(carray * list);
381 static MsgInfo *imap_envelope_from_lep(struct imap_fetch_env_info * info,
383 static void imap_lep_set_free(GSList *seq_list);
384 static struct mailimap_flag_list * imap_flag_to_lep(IMAPFlags flags);
387 static GHashTable *flags_set_table = NULL;
388 static GHashTable *flags_unset_table = NULL;
389 typedef struct _hashtable_data {
390 IMAPSession *session;
394 static FolderClass imap_class;
396 typedef struct _thread_data {
406 FolderClass *imap_get_class(void)
408 if (imap_class.idstr == NULL) {
409 imap_class.type = F_IMAP;
410 imap_class.idstr = "imap";
411 imap_class.uistr = "IMAP4";
413 /* Folder functions */
414 imap_class.new_folder = imap_folder_new;
415 imap_class.destroy_folder = imap_folder_destroy;
416 imap_class.scan_tree = imap_scan_tree;
417 imap_class.create_tree = imap_create_tree;
419 /* FolderItem functions */
420 imap_class.item_new = imap_folder_item_new;
421 imap_class.item_destroy = imap_folder_item_destroy;
422 imap_class.item_get_path = imap_item_get_path;
423 imap_class.create_folder = imap_create_folder;
424 imap_class.rename_folder = imap_rename_folder;
425 imap_class.remove_folder = imap_remove_folder;
426 imap_class.close = imap_close;
427 imap_class.get_num_list = imap_get_num_list;
428 imap_class.scan_required = imap_scan_required;
430 /* Message functions */
431 imap_class.get_msginfo = imap_get_msginfo;
432 imap_class.get_msginfos = imap_get_msginfos;
433 imap_class.fetch_msg = imap_fetch_msg;
434 imap_class.fetch_msg_full = imap_fetch_msg_full;
435 imap_class.add_msg = imap_add_msg;
436 imap_class.add_msgs = imap_add_msgs;
437 imap_class.copy_msg = imap_copy_msg;
438 imap_class.copy_msgs = imap_copy_msgs;
439 imap_class.remove_msg = imap_remove_msg;
440 imap_class.remove_msgs = imap_remove_msgs;
441 imap_class.remove_all_msg = imap_remove_all_msg;
442 imap_class.is_msg_changed = imap_is_msg_changed;
443 imap_class.change_flags = imap_change_flags;
444 imap_class.get_flags = imap_get_flags;
445 imap_class.set_batch = imap_set_batch;
447 pthread_mutex_init(&imap_mutex, NULL);
454 static Folder *imap_folder_new(const gchar *name, const gchar *path)
458 folder = (Folder *)g_new0(IMAPFolder, 1);
459 folder->klass = &imap_class;
460 imap_folder_init(folder, name, path);
465 static void imap_folder_destroy(Folder *folder)
469 while (imap_folder_get_refcnt(folder) > 0)
470 gtk_main_iteration();
472 dir = imap_folder_get_path(folder);
473 if (is_dir_exist(dir))
474 remove_dir_recursive(dir);
477 folder_remote_folder_destroy(REMOTE_FOLDER(folder));
481 static void imap_folder_init(Folder *folder, const gchar *name,
484 folder_remote_folder_init((Folder *)folder, name, path);
487 static FolderItem *imap_folder_item_new(Folder *folder)
489 IMAPFolderItem *item;
491 item = g_new0(IMAPFolderItem, 1);
494 item->uid_list = NULL;
496 return (FolderItem *)item;
499 static void imap_folder_item_destroy(Folder *folder, FolderItem *_item)
501 IMAPFolderItem *item = (IMAPFolderItem *)_item;
503 g_return_if_fail(item != NULL);
504 g_slist_free(item->uid_list);
509 static gboolean imap_reset_uid_lists_func(GNode *node, gpointer data)
511 IMAPFolderItem *item = (IMAPFolderItem *)node->data;
515 g_slist_free(item->uid_list);
516 item->uid_list = NULL;
521 static void imap_reset_uid_lists(Folder *folder)
523 if(folder->node == NULL)
526 /* Destroy all uid lists and rest last uid */
527 g_node_traverse(folder->node, G_IN_ORDER, G_TRAVERSE_ALL, -1, imap_reset_uid_lists_func, NULL);
530 void imap_get_capabilities(IMAPSession *session)
532 struct mailimap_capability_data *capabilities = NULL;
535 if (session->capability != NULL)
538 capabilities = imap_threaded_capability(session->folder);
539 for(cur = clist_begin(capabilities->cap_list) ; cur != NULL ;
540 cur = clist_next(cur)) {
541 struct mailimap_capability * cap =
543 if (cap->cap_data.cap_name == NULL)
545 session->capability = g_slist_append
546 (session->capability,
547 g_strdup(cap->cap_data.cap_name));
548 debug_print("got capa %s\n", cap->cap_data.cap_name);
550 mailimap_capability_data_free(capabilities);
553 gboolean imap_has_capability(IMAPSession *session, const gchar *cap)
556 for (cur = session->capability; cur; cur = cur->next) {
557 if (!g_ascii_strcasecmp(cur->data, cap))
563 static gint imap_auth(IMAPSession *session, const gchar *user, const gchar *pass,
566 gint ok = IMAP_ERROR;
567 static time_t last_login_err = 0;
569 imap_get_capabilities(session);
572 case IMAP_AUTH_CRAM_MD5:
573 ok = imap_cmd_login(session, user, pass, "CRAM-MD5");
575 case IMAP_AUTH_LOGIN:
576 ok = imap_cmd_login(session, user, pass, "LOGIN");
579 debug_print("capabilities:\n"
582 imap_has_capability(session, "CRAM-MD5"),
583 imap_has_capability(session, "LOGIN"));
584 if (imap_has_capability(session, "CRAM-MD5"))
585 ok = imap_cmd_login(session, user, pass, "CRAM-MD5");
586 if (ok == IMAP_ERROR) /* we always try LOGIN before giving up */
587 ok = imap_cmd_login(session, user, pass, "LOGIN");
589 if (ok == IMAP_SUCCESS)
590 session->authenticated = TRUE;
592 gchar *ext_info = NULL;
594 if (type == IMAP_AUTH_CRAM_MD5) {
595 ext_info = _("\n\nCRAM-MD5 logins only work if libetpan has been "
596 "compiled with SASL support and the "
597 "CRAM-MD5 SASL plugin is installed.");
602 if (time(NULL) - last_login_err > 10) {
603 alertpanel_error(_("Connection to %s failed: login refused.%s"),
604 SESSION(session)->server, ext_info);
606 last_login_err = time(NULL);
611 static IMAPSession *imap_session_get(Folder *folder)
613 RemoteFolder *rfolder = REMOTE_FOLDER(folder);
614 IMAPSession *session = NULL;
616 g_return_val_if_fail(folder != NULL, NULL);
617 g_return_val_if_fail(FOLDER_CLASS(folder) == &imap_class, NULL);
618 g_return_val_if_fail(folder->account != NULL, NULL);
620 if (prefs_common.work_offline && !imap_gtk_should_override()) {
624 /* Make sure we have a session */
625 if (rfolder->session != NULL) {
626 session = IMAP_SESSION(rfolder->session);
628 imap_reset_uid_lists(folder);
629 session = imap_session_new(folder, folder->account);
634 /* Make sure session is authenticated */
635 if (!IMAP_SESSION(session)->authenticated)
636 imap_session_authenticate(IMAP_SESSION(session), folder->account);
638 if (!IMAP_SESSION(session)->authenticated) {
639 session_destroy(SESSION(session));
640 rfolder->session = NULL;
645 /* Make sure we have parsed the IMAP namespace */
646 imap_parse_namespace(IMAP_SESSION(session),
647 IMAP_FOLDER(folder));
650 /* I think the point of this code is to avoid sending a
651 * keepalive if we've used the session recently and therefore
652 * think it's still alive. Unfortunately, most of the code
653 * does not yet check for errors on the socket, and so if the
654 * connection drops we don't notice until the timeout expires.
655 * A better solution than sending a NOOP every time would be
656 * for every command to be prepared to retry until it is
657 * successfully sent. -- mbp */
658 if (time(NULL) - SESSION(session)->last_access_time > SESSION_TIMEOUT_INTERVAL) {
659 /* verify that the session is still alive */
660 if (imap_cmd_noop(session) != IMAP_SUCCESS) {
661 /* Check if this is the first try to establish a
662 connection, if yes we don't try to reconnect */
663 if (rfolder->session == NULL) {
664 log_warning(_("Connecting to %s failed"),
665 folder->account->recv_server);
666 session_destroy(SESSION(session));
669 log_warning(_("IMAP4 connection to %s has been"
670 " disconnected. Reconnecting...\n"),
671 folder->account->recv_server);
672 statusbar_print_all(_("IMAP4 connection to %s has been"
673 " disconnected. Reconnecting...\n"),
674 folder->account->recv_server);
675 session_destroy(SESSION(session));
676 /* Clear folders session to make imap_session_get create
677 a new session, because of rfolder->session == NULL
678 it will not try to reconnect again and so avoid an
680 rfolder->session = NULL;
681 session = imap_session_get(folder);
687 rfolder->session = SESSION(session);
689 return IMAP_SESSION(session);
692 static IMAPSession *imap_session_new(Folder * folder,
693 const PrefsAccount *account)
695 IMAPSession *session;
701 /* FIXME: IMAP over SSL only... */
704 port = account->set_imapport ? account->imapport
705 : account->ssl_imap == SSL_TUNNEL ? IMAPS_PORT : IMAP4_PORT;
706 ssl_type = account->ssl_imap;
708 port = account->set_imapport ? account->imapport
713 statusbar_print_all(_("Connecting to IMAP4 server: %s..."), folder->account->recv_server);
714 if (account->set_tunnelcmd) {
715 r = imap_threaded_connect_cmd(folder,
717 account->recv_server,
722 if (ssl_type == SSL_TUNNEL) {
723 r = imap_threaded_connect_ssl(folder,
724 account->recv_server,
730 r = imap_threaded_connect(folder,
731 account->recv_server,
737 if (r == MAILIMAP_NO_ERROR_AUTHENTICATED) {
738 authenticated = TRUE;
740 else if (r == MAILIMAP_NO_ERROR_NON_AUTHENTICATED) {
741 authenticated = FALSE;
744 if(!prefs_common.no_recv_err_panel) {
745 alertpanel_error(_("Can't connect to IMAP4 server: %s:%d"),
746 account->recv_server, port);
752 session = g_new0(IMAPSession, 1);
753 session_init(SESSION(session));
754 SESSION(session)->type = SESSION_IMAP;
755 SESSION(session)->server = g_strdup(account->recv_server);
756 SESSION(session)->sock = NULL;
758 SESSION(session)->destroy = imap_session_destroy;
760 session->capability = NULL;
762 session->authenticated = authenticated;
763 session->mbox = NULL;
764 session->cmd_count = 0;
765 session->folder = folder;
766 IMAP_FOLDER(session->folder)->last_seen_separator = 0;
769 if (account->ssl_imap == SSL_STARTTLS) {
772 ok = imap_cmd_starttls(session);
773 if (ok != IMAP_SUCCESS) {
774 log_warning(_("Can't start TLS session.\n"));
775 session_destroy(SESSION(session));
779 imap_free_capabilities(session);
780 session->authenticated = FALSE;
781 session->uidplus = FALSE;
782 session->cmd_count = 1;
785 log_message("IMAP connection is %s-authenticated\n",
786 (session->authenticated) ? "pre" : "un");
791 static void imap_session_authenticate(IMAPSession *session,
792 const PrefsAccount *account)
796 g_return_if_fail(account->userid != NULL);
798 pass = account->passwd;
801 tmp_pass = input_dialog_query_password(account->recv_server, account->userid);
803 tmp_pass = g_strdup(""); /* allow empty password */
804 Xstrdup_a(pass, tmp_pass, {g_free(tmp_pass); return;});
807 statusbar_print_all(_("Connecting to IMAP4 server %s...\n"),
808 account->recv_server);
809 if (imap_auth(session, account->userid, pass, account->imap_auth_type) != IMAP_SUCCESS) {
810 imap_threaded_disconnect(session->folder);
811 imap_cmd_logout(session);
817 session->authenticated = TRUE;
820 static void imap_session_destroy(Session *session)
822 imap_threaded_disconnect(IMAP_SESSION(session)->folder);
824 imap_free_capabilities(IMAP_SESSION(session));
825 g_free(IMAP_SESSION(session)->mbox);
826 sock_close(session->sock);
827 session->sock = NULL;
830 static gchar *imap_fetch_msg(Folder *folder, FolderItem *item, gint uid)
832 return imap_fetch_msg_full(folder, item, uid, TRUE, TRUE);
835 static guint get_size_with_crs(MsgInfo *info)
844 fp = procmsg_open_message(info);
848 while (fgets(buf, sizeof (buf), fp) != NULL) {
850 if (!strstr(buf, "\r") && strstr(buf, "\n"))
858 static gchar *imap_fetch_msg_full(Folder *folder, FolderItem *item, gint uid,
859 gboolean headers, gboolean body)
861 gchar *path, *filename;
862 IMAPSession *session;
865 g_return_val_if_fail(folder != NULL, NULL);
866 g_return_val_if_fail(item != NULL, NULL);
871 path = folder_item_get_path(item);
872 if (!is_dir_exist(path))
874 filename = g_strconcat(path, G_DIR_SEPARATOR_S, itos(uid), NULL);
877 if (is_file_exist(filename)) {
878 /* see whether the local file represents the whole message
879 * or not. As the IMAP server reports size with \r chars,
880 * we have to update the local file (UNIX \n only) size */
881 MsgInfo *msginfo = imap_parse_msg(filename, item);
882 MsgInfo *cached = msgcache_get_msg(item->cache,uid);
883 guint have_size = get_size_with_crs(msginfo);
886 debug_print("message %d has been already %scached (%d/%d).\n", uid,
887 have_size == cached->size ? "fully ":"",
888 have_size, (int)cached->size);
890 if (cached && (cached->size == have_size || !body)) {
891 procmsg_msginfo_free(cached);
892 procmsg_msginfo_free(msginfo);
893 file_strip_crs(filename);
896 procmsg_msginfo_free(cached);
897 procmsg_msginfo_free(msginfo);
901 session = imap_session_get(folder);
907 debug_print("IMAP fetching messages\n");
908 ok = imap_select(session, IMAP_FOLDER(folder), item->path,
909 NULL, NULL, NULL, NULL, FALSE);
910 if (ok != IMAP_SUCCESS) {
911 g_warning("can't select mailbox %s\n", item->path);
916 debug_print("getting message %d...\n", uid);
917 ok = imap_cmd_fetch(session, (guint32)uid, filename, headers, body);
919 if (ok != IMAP_SUCCESS) {
920 g_warning("can't fetch message %d\n", uid);
925 file_strip_crs(filename);
929 static gint imap_add_msg(Folder *folder, FolderItem *dest,
930 const gchar *file, MsgFlags *flags)
934 MsgFileInfo fileinfo;
936 g_return_val_if_fail(file != NULL, -1);
938 fileinfo.msginfo = NULL;
939 fileinfo.file = (gchar *)file;
940 fileinfo.flags = flags;
941 file_list.data = &fileinfo;
942 file_list.next = NULL;
944 ret = imap_add_msgs(folder, dest, &file_list, NULL);
948 static gint imap_add_msgs(Folder *folder, FolderItem *dest, GSList *file_list,
952 IMAPSession *session;
953 guint32 last_uid = 0;
955 MsgFileInfo *fileinfo;
959 g_return_val_if_fail(folder != NULL, -1);
960 g_return_val_if_fail(dest != NULL, -1);
961 g_return_val_if_fail(file_list != NULL, -1);
963 session = imap_session_get(folder);
967 destdir = imap_get_real_path(IMAP_FOLDER(folder), dest->path);
969 for (cur = file_list; cur != NULL; cur = cur->next) {
970 IMAPFlags iflags = 0;
973 fileinfo = (MsgFileInfo *)cur->data;
975 if (fileinfo->flags) {
976 if (MSG_IS_MARKED(*fileinfo->flags))
977 iflags |= IMAP_FLAG_FLAGGED;
978 if (MSG_IS_REPLIED(*fileinfo->flags))
979 iflags |= IMAP_FLAG_ANSWERED;
980 if (!MSG_IS_UNREAD(*fileinfo->flags))
981 iflags |= IMAP_FLAG_SEEN;
984 if (folder_has_parent_of_type(dest, F_QUEUE) ||
985 folder_has_parent_of_type(dest, F_OUTBOX) ||
986 folder_has_parent_of_type(dest, F_DRAFT) ||
987 folder_has_parent_of_type(dest, F_TRASH))
988 iflags |= IMAP_FLAG_SEEN;
990 ok = imap_cmd_append(session, destdir, fileinfo->file, iflags,
993 if (ok != IMAP_SUCCESS) {
994 g_warning("can't append message %s\n", fileinfo->file);
999 if (relation != NULL)
1000 g_relation_insert(relation, fileinfo->msginfo != NULL ?
1001 (gpointer) fileinfo->msginfo : (gpointer) fileinfo,
1002 GINT_TO_POINTER(dest->last_num + 1));
1003 if (last_uid < new_uid)
1012 static gint imap_do_copy_msgs(Folder *folder, FolderItem *dest,
1013 MsgInfoList *msglist, GRelation *relation)
1017 GSList *seq_list, *cur;
1019 IMAPSession *session;
1020 gint ok = IMAP_SUCCESS;
1021 GRelation *uid_mapping;
1024 g_return_val_if_fail(folder != NULL, -1);
1025 g_return_val_if_fail(dest != NULL, -1);
1026 g_return_val_if_fail(msglist != NULL, -1);
1028 session = imap_session_get(folder);
1033 msginfo = (MsgInfo *)msglist->data;
1035 src = msginfo->folder;
1037 g_warning("the src folder is identical to the dest.\n");
1041 ok = imap_select(session, IMAP_FOLDER(folder), msginfo->folder->path,
1042 NULL, NULL, NULL, NULL, FALSE);
1043 if (ok != IMAP_SUCCESS) {
1047 destdir = imap_get_real_path(IMAP_FOLDER(folder), dest->path);
1048 seq_list = imap_get_lep_set_from_msglist(msglist);
1049 uid_mapping = g_relation_new(2);
1050 g_relation_index(uid_mapping, 0, g_direct_hash, g_direct_equal);
1052 for (cur = seq_list; cur != NULL; cur = g_slist_next(cur)) {
1053 struct mailimap_set * seq_set;
1055 seq_set = cur->data;
1057 debug_print("Copying messages from %s to %s ...\n",
1058 src->path, destdir);
1060 ok = imap_cmd_copy(session, seq_set, destdir, uid_mapping);
1061 if (ok != IMAP_SUCCESS) {
1062 g_relation_destroy(uid_mapping);
1063 imap_lep_set_free(seq_list);
1068 for (cur = msglist; cur != NULL; cur = g_slist_next(cur)) {
1069 MsgInfo *msginfo = (MsgInfo *)cur->data;
1072 tuples = g_relation_select(uid_mapping,
1073 GINT_TO_POINTER(msginfo->msgnum),
1075 if (tuples->len > 0) {
1076 gint num = GPOINTER_TO_INT(g_tuples_index(tuples, 0, 1));
1077 g_relation_insert(relation, msginfo,
1078 GPOINTER_TO_INT(num));
1082 g_relation_insert(relation, msginfo,
1083 GPOINTER_TO_INT(0));
1084 g_tuples_destroy(tuples);
1087 g_relation_destroy(uid_mapping);
1088 imap_lep_set_free(seq_list);
1092 IMAP_FOLDER_ITEM(dest)->lastuid = 0;
1093 IMAP_FOLDER_ITEM(dest)->uid_next = 0;
1094 g_slist_free(IMAP_FOLDER_ITEM(dest)->uid_list);
1095 IMAP_FOLDER_ITEM(dest)->uid_list = NULL;
1097 if (ok == IMAP_SUCCESS)
1103 static gint imap_copy_msg(Folder *folder, FolderItem *dest, MsgInfo *msginfo)
1107 g_return_val_if_fail(msginfo != NULL, -1);
1109 msglist.data = msginfo;
1110 msglist.next = NULL;
1112 return imap_copy_msgs(folder, dest, &msglist, NULL);
1115 static gint imap_copy_msgs(Folder *folder, FolderItem *dest,
1116 MsgInfoList *msglist, GRelation *relation)
1122 g_return_val_if_fail(folder != NULL, -1);
1123 g_return_val_if_fail(dest != NULL, -1);
1124 g_return_val_if_fail(msglist != NULL, -1);
1126 msginfo = (MsgInfo *)msglist->data;
1127 g_return_val_if_fail(msginfo->folder != NULL, -1);
1129 if (folder == msginfo->folder->folder) {
1130 ret = imap_do_copy_msgs(folder, dest, msglist, relation);
1134 file_list = procmsg_get_message_file_list(msglist);
1135 g_return_val_if_fail(file_list != NULL, -1);
1137 ret = imap_add_msgs(folder, dest, file_list, relation);
1139 procmsg_message_file_list_free(file_list);
1145 static gint imap_do_remove_msgs(Folder *folder, FolderItem *dest,
1146 MsgInfoList *msglist, GRelation *relation)
1149 GSList *seq_list = NULL, *cur;
1151 IMAPSession *session;
1152 gint ok = IMAP_SUCCESS;
1153 GRelation *uid_mapping;
1155 g_return_val_if_fail(folder != NULL, -1);
1156 g_return_val_if_fail(dest != NULL, -1);
1157 g_return_val_if_fail(msglist != NULL, -1);
1159 session = imap_session_get(folder);
1163 msginfo = (MsgInfo *)msglist->data;
1165 ok = imap_select(session, IMAP_FOLDER(folder), msginfo->folder->path,
1166 NULL, NULL, NULL, NULL, FALSE);
1167 if (ok != IMAP_SUCCESS) {
1171 destdir = imap_get_real_path(IMAP_FOLDER(folder), dest->path);
1172 for (cur = msglist; cur; cur = cur->next) {
1173 msginfo = (MsgInfo *)cur->data;
1174 seq_list = g_slist_append(seq_list, GINT_TO_POINTER(msginfo->msgnum));
1177 uid_mapping = g_relation_new(2);
1178 g_relation_index(uid_mapping, 0, g_direct_hash, g_direct_equal);
1180 ok = imap_set_message_flags
1181 (IMAP_SESSION(REMOTE_FOLDER(folder)->session),
1182 seq_list, IMAP_FLAG_DELETED, TRUE);
1183 if (ok != IMAP_SUCCESS) {
1184 log_warning(_("can't set deleted flags\n"));
1187 ok = imap_cmd_expunge(session);
1188 if (ok != IMAP_SUCCESS) {
1189 log_warning(_("can't expunge\n"));
1193 g_relation_destroy(uid_mapping);
1194 g_slist_free(seq_list);
1198 if (ok == IMAP_SUCCESS)
1204 static gint imap_remove_msgs(Folder *folder, FolderItem *dest,
1205 MsgInfoList *msglist, GRelation *relation)
1209 g_return_val_if_fail(folder != NULL, -1);
1210 g_return_val_if_fail(dest != NULL, -1);
1211 if (msglist == NULL)
1214 msginfo = (MsgInfo *)msglist->data;
1215 g_return_val_if_fail(msginfo->folder != NULL, -1);
1217 return imap_do_remove_msgs(folder, dest, msglist, relation);
1220 static gint imap_remove_all_msg(Folder *folder, FolderItem *item)
1222 GSList *list = folder_item_get_msg_list(item);
1223 gint res = imap_remove_msgs(folder, item, list, NULL);
1224 procmsg_msg_list_free(list);
1228 static gboolean imap_is_msg_changed(Folder *folder, FolderItem *item,
1231 /* TODO: properly implement this method */
1235 static gint imap_close(Folder *folder, FolderItem *item)
1240 static gint imap_scan_tree(Folder *folder)
1242 FolderItem *item = NULL;
1243 IMAPSession *session;
1244 gchar *root_folder = NULL;
1246 g_return_val_if_fail(folder != NULL, -1);
1247 g_return_val_if_fail(folder->account != NULL, -1);
1249 session = imap_session_get(folder);
1251 if (!folder->node) {
1252 folder_tree_destroy(folder);
1253 item = folder_item_new(folder, folder->name, NULL);
1254 item->folder = folder;
1255 folder->node = item->node = g_node_new(item);
1260 if (folder->account->imap_dir && *folder->account->imap_dir) {
1265 Xstrdup_a(root_folder, folder->account->imap_dir, {return -1;});
1266 extract_quote(root_folder, '"');
1267 subst_char(root_folder,
1268 imap_get_path_separator(IMAP_FOLDER(folder),
1271 strtailchomp(root_folder, '/');
1272 real_path = imap_get_real_path
1273 (IMAP_FOLDER(folder), root_folder);
1274 debug_print("IMAP root directory: %s\n", real_path);
1276 /* check if root directory exist */
1278 r = imap_threaded_list(session->folder, "", real_path,
1280 if ((r != MAILIMAP_NO_ERROR) || (clist_count(lep_list) == 0)) {
1281 if (!folder->node) {
1282 item = folder_item_new(folder, folder->name, NULL);
1283 item->folder = folder;
1284 folder->node = item->node = g_node_new(item);
1289 mailimap_list_result_free(lep_list);
1295 item = FOLDER_ITEM(folder->node->data);
1296 if (!item || ((item->path || root_folder) &&
1297 strcmp2(item->path, root_folder) != 0)) {
1298 folder_tree_destroy(folder);
1299 item = folder_item_new(folder, folder->name, root_folder);
1300 item->folder = folder;
1301 folder->node = item->node = g_node_new(item);
1304 imap_scan_tree_recursive(session, FOLDER_ITEM(folder->node->data));
1305 imap_create_missing_folders(folder);
1310 static gint imap_scan_tree_recursive(IMAPSession *session, FolderItem *item)
1313 IMAPFolder *imapfolder;
1314 FolderItem *new_item;
1315 GSList *item_list, *cur;
1318 gchar *wildcard_path;
1324 g_return_val_if_fail(item != NULL, -1);
1325 g_return_val_if_fail(item->folder != NULL, -1);
1326 g_return_val_if_fail(item->no_sub == FALSE, -1);
1328 folder = item->folder;
1329 imapfolder = IMAP_FOLDER(folder);
1331 separator = imap_get_path_separator(imapfolder, item->path);
1333 if (folder->ui_func)
1334 folder->ui_func(folder, item, folder->ui_func_data);
1337 wildcard[0] = separator;
1340 real_path = imap_get_real_path(imapfolder, item->path);
1344 real_path = g_strdup("");
1347 Xstrcat_a(wildcard_path, real_path, wildcard,
1348 {g_free(real_path); return IMAP_ERROR;});
1350 r = imap_threaded_list(folder, "", wildcard_path, &lep_list);
1351 if (r != MAILIMAP_NO_ERROR) {
1355 item_list = imap_list_from_lep(imapfolder,
1356 lep_list, real_path);
1357 mailimap_list_result_free(lep_list);
1362 node = item->node->children;
1363 while (node != NULL) {
1364 FolderItem *old_item = FOLDER_ITEM(node->data);
1365 GNode *next = node->next;
1368 for (cur = item_list; cur != NULL; cur = cur->next) {
1369 FolderItem *cur_item = FOLDER_ITEM(cur->data);
1370 if (!strcmp2(old_item->path, cur_item->path)) {
1371 new_item = cur_item;
1376 debug_print("folder '%s' not found. removing...\n",
1378 folder_item_remove(old_item);
1380 old_item->no_sub = new_item->no_sub;
1381 old_item->no_select = new_item->no_select;
1382 if (old_item->no_sub == TRUE && node->children) {
1383 debug_print("folder '%s' doesn't have "
1384 "subfolders. removing...\n",
1386 folder_item_remove_children(old_item);
1393 for (cur = item_list; cur != NULL; cur = cur->next) {
1394 FolderItem *cur_item = FOLDER_ITEM(cur->data);
1396 for (node = item->node->children; node != NULL;
1397 node = node->next) {
1398 if (!strcmp2(FOLDER_ITEM(node->data)->path,
1400 new_item = FOLDER_ITEM(node->data);
1401 folder_item_destroy(cur_item);
1407 new_item = cur_item;
1408 debug_print("new folder '%s' found.\n", new_item->path);
1409 folder_item_append(item, new_item);
1412 if (!strcmp(new_item->path, "INBOX")) {
1413 new_item->stype = F_INBOX;
1414 folder->inbox = new_item;
1415 } else if (!folder_item_parent(item) || item->stype == F_INBOX) {
1418 base = g_path_get_basename(new_item->path);
1420 if (!folder->outbox && !g_ascii_strcasecmp(base, "Sent")) {
1421 new_item->stype = F_OUTBOX;
1422 folder->outbox = new_item;
1423 } else if (!folder->draft && !g_ascii_strcasecmp(base, "Drafts")) {
1424 new_item->stype = F_DRAFT;
1425 folder->draft = new_item;
1426 } else if (!folder->queue && !g_ascii_strcasecmp(base, "Queue")) {
1427 new_item->stype = F_QUEUE;
1428 folder->queue = new_item;
1429 } else if (!folder->trash && !g_ascii_strcasecmp(base, "Trash")) {
1430 new_item->stype = F_TRASH;
1431 folder->trash = new_item;
1436 if (new_item->no_sub == FALSE)
1437 imap_scan_tree_recursive(session, new_item);
1440 g_slist_free(item_list);
1442 return IMAP_SUCCESS;
1445 static gint imap_create_tree(Folder *folder)
1447 g_return_val_if_fail(folder != NULL, -1);
1448 g_return_val_if_fail(folder->node != NULL, -1);
1449 g_return_val_if_fail(folder->node->data != NULL, -1);
1450 g_return_val_if_fail(folder->account != NULL, -1);
1452 imap_scan_tree(folder);
1453 imap_create_missing_folders(folder);
1458 static void imap_create_missing_folders(Folder *folder)
1460 g_return_if_fail(folder != NULL);
1463 folder->inbox = imap_create_special_folder
1464 (folder, F_INBOX, "INBOX");
1466 folder->trash = imap_create_special_folder
1467 (folder, F_TRASH, "Trash");
1469 folder->queue = imap_create_special_folder
1470 (folder, F_QUEUE, "Queue");
1471 if (!folder->outbox)
1472 folder->outbox = imap_create_special_folder
1473 (folder, F_OUTBOX, "Sent");
1475 folder->draft = imap_create_special_folder
1476 (folder, F_DRAFT, "Drafts");
1479 static FolderItem *imap_create_special_folder(Folder *folder,
1480 SpecialFolderItemType stype,
1484 FolderItem *new_item;
1486 g_return_val_if_fail(folder != NULL, NULL);
1487 g_return_val_if_fail(folder->node != NULL, NULL);
1488 g_return_val_if_fail(folder->node->data != NULL, NULL);
1489 g_return_val_if_fail(folder->account != NULL, NULL);
1490 g_return_val_if_fail(name != NULL, NULL);
1492 item = FOLDER_ITEM(folder->node->data);
1493 new_item = imap_create_folder(folder, item, name);
1496 g_warning("Can't create '%s'\n", name);
1497 if (!folder->inbox) return NULL;
1499 new_item = imap_create_folder(folder, folder->inbox, name);
1501 g_warning("Can't create '%s' under INBOX\n", name);
1503 new_item->stype = stype;
1505 new_item->stype = stype;
1510 static gchar *imap_folder_get_path(Folder *folder)
1514 g_return_val_if_fail(folder != NULL, NULL);
1515 g_return_val_if_fail(folder->account != NULL, NULL);
1517 folder_path = g_strconcat(get_imap_cache_dir(),
1519 folder->account->recv_server,
1521 folder->account->userid,
1527 static gchar *imap_item_get_path(Folder *folder, FolderItem *item)
1529 gchar *folder_path, *path;
1531 g_return_val_if_fail(folder != NULL, NULL);
1532 g_return_val_if_fail(item != NULL, NULL);
1533 folder_path = imap_folder_get_path(folder);
1535 g_return_val_if_fail(folder_path != NULL, NULL);
1536 if (folder_path[0] == G_DIR_SEPARATOR) {
1538 path = g_strconcat(folder_path, G_DIR_SEPARATOR_S,
1541 path = g_strdup(folder_path);
1544 path = g_strconcat(get_home_dir(), G_DIR_SEPARATOR_S,
1545 folder_path, G_DIR_SEPARATOR_S,
1548 path = g_strconcat(get_home_dir(), G_DIR_SEPARATOR_S,
1551 g_free(folder_path);
1556 static FolderItem *imap_create_folder(Folder *folder, FolderItem *parent,
1559 gchar *dirpath, *imap_path;
1560 IMAPSession *session;
1561 FolderItem *new_item;
1567 g_return_val_if_fail(folder != NULL, NULL);
1568 g_return_val_if_fail(folder->account != NULL, NULL);
1569 g_return_val_if_fail(parent != NULL, NULL);
1570 g_return_val_if_fail(name != NULL, NULL);
1572 session = imap_session_get(folder);
1577 if (!folder_item_parent(parent) && strcmp(name, "INBOX") == 0)
1578 dirpath = g_strdup(name);
1579 else if (parent->path)
1580 dirpath = g_strconcat(parent->path, "/", name, NULL);
1581 else if ((p = strchr(name, '/')) != NULL && *(p + 1) != '\0')
1582 dirpath = g_strdup(name);
1583 else if (folder->account->imap_dir && *folder->account->imap_dir) {
1586 Xstrdup_a(imap_dir, folder->account->imap_dir, {return NULL;});
1587 strtailchomp(imap_dir, '/');
1588 dirpath = g_strconcat(imap_dir, "/", name, NULL);
1590 dirpath = g_strdup(name);
1592 /* keep trailing directory separator to create a folder that contains
1594 imap_path = imap_utf8_to_modified_utf7(dirpath);
1595 strtailchomp(dirpath, '/');
1596 Xstrdup_a(new_name, name, {
1600 strtailchomp(new_name, '/');
1601 separator = imap_get_path_separator(IMAP_FOLDER(folder), imap_path);
1602 imap_path_separator_subst(imap_path, separator);
1603 subst_char(new_name, '/', separator);
1605 if (strcmp(name, "INBOX") != 0) {
1607 gboolean exist = FALSE;
1611 argbuf = g_ptr_array_new();
1612 r = imap_threaded_list(folder, "", imap_path, &lep_list);
1613 if (r != MAILIMAP_NO_ERROR) {
1614 log_warning(_("can't create mailbox: LIST failed\n"));
1617 ptr_array_free_strings(argbuf);
1618 g_ptr_array_free(argbuf, TRUE);
1622 if (clist_count(lep_list) > 0)
1626 ok = imap_cmd_create(session, imap_path);
1627 if (ok != IMAP_SUCCESS) {
1628 log_warning(_("can't create mailbox\n"));
1636 new_item = folder_item_new(folder, new_name, dirpath);
1637 folder_item_append(parent, new_item);
1641 dirpath = folder_item_get_path(new_item);
1642 if (!is_dir_exist(dirpath))
1643 make_dir_hier(dirpath);
1649 static gint imap_rename_folder(Folder *folder, FolderItem *item,
1654 gchar *real_oldpath;
1655 gchar *real_newpath;
1657 gchar *old_cache_dir;
1658 gchar *new_cache_dir;
1659 IMAPSession *session;
1662 gint exists, recent, unseen;
1663 guint32 uid_validity;
1665 g_return_val_if_fail(folder != NULL, -1);
1666 g_return_val_if_fail(item != NULL, -1);
1667 g_return_val_if_fail(item->path != NULL, -1);
1668 g_return_val_if_fail(name != NULL, -1);
1670 if (strchr(name, imap_get_path_separator(IMAP_FOLDER(folder), item->path)) != NULL) {
1671 g_warning(_("New folder name must not contain the namespace "
1676 session = imap_session_get(folder);
1680 real_oldpath = imap_get_real_path(IMAP_FOLDER(folder), item->path);
1682 g_free(session->mbox);
1683 session->mbox = NULL;
1684 ok = imap_cmd_examine(session, "INBOX",
1685 &exists, &recent, &unseen, &uid_validity, FALSE);
1686 if (ok != IMAP_SUCCESS) {
1687 g_free(real_oldpath);
1691 separator = imap_get_path_separator(IMAP_FOLDER(folder), item->path);
1692 if (strchr(item->path, G_DIR_SEPARATOR)) {
1693 dirpath = g_path_get_dirname(item->path);
1694 newpath = g_strconcat(dirpath, G_DIR_SEPARATOR_S, name, NULL);
1697 newpath = g_strdup(name);
1699 real_newpath = imap_utf8_to_modified_utf7(newpath);
1700 imap_path_separator_subst(real_newpath, separator);
1702 ok = imap_cmd_rename(session, real_oldpath, real_newpath);
1703 if (ok != IMAP_SUCCESS) {
1704 log_warning(_("can't rename mailbox: %s to %s\n"),
1705 real_oldpath, real_newpath);
1706 g_free(real_oldpath);
1708 g_free(real_newpath);
1713 item->name = g_strdup(name);
1715 old_cache_dir = folder_item_get_path(item);
1717 paths[0] = g_strdup(item->path);
1719 g_node_traverse(item->node, G_PRE_ORDER, G_TRAVERSE_ALL, -1,
1720 imap_rename_folder_func, paths);
1722 if (is_dir_exist(old_cache_dir)) {
1723 new_cache_dir = folder_item_get_path(item);
1724 if (rename(old_cache_dir, new_cache_dir) < 0) {
1725 FILE_OP_ERROR(old_cache_dir, "rename");
1727 g_free(new_cache_dir);
1730 g_free(old_cache_dir);
1733 g_free(real_oldpath);
1734 g_free(real_newpath);
1739 static gint imap_remove_folder_real(Folder *folder, FolderItem *item)
1742 IMAPSession *session;
1745 gint exists, recent, unseen;
1746 guint32 uid_validity;
1748 g_return_val_if_fail(folder != NULL, -1);
1749 g_return_val_if_fail(item != NULL, -1);
1750 g_return_val_if_fail(item->path != NULL, -1);
1752 session = imap_session_get(folder);
1756 path = imap_get_real_path(IMAP_FOLDER(folder), item->path);
1758 ok = imap_cmd_examine(session, "INBOX",
1759 &exists, &recent, &unseen, &uid_validity, FALSE);
1760 if (ok != IMAP_SUCCESS) {
1765 ok = imap_cmd_delete(session, path);
1766 if (ok != IMAP_SUCCESS) {
1767 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);
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));
1873 #define MAX_MSG_NUM 50
1875 static GSList *imap_get_uncached_messages(IMAPSession *session,
1877 MsgNumberList *numlist)
1879 GSList *result = NULL;
1881 uncached_data *data = g_new0(uncached_data, 1);
1886 data->total = g_slist_length(numlist);
1887 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);
1930 statusbar_progress_all(data->cur,data->total, 1);
1932 g_slist_free(newlist);
1934 result = g_slist_concat(result, partial_result);
1938 statusbar_progress_all(0,0,0);
1939 statusbar_pop_all();
1944 static void imap_delete_all_cached_messages(FolderItem *item)
1948 g_return_if_fail(item != NULL);
1949 g_return_if_fail(item->folder != NULL);
1950 g_return_if_fail(FOLDER_CLASS(item->folder) == &imap_class);
1952 debug_print("Deleting all cached messages...\n");
1954 dir = folder_item_get_path(item);
1955 if (is_dir_exist(dir))
1956 remove_all_numbered_files(dir);
1959 debug_print("done.\n");
1962 static IMAPNameSpace *imap_find_namespace_from_list(GList *ns_list,
1965 IMAPNameSpace *namespace = NULL;
1966 gchar *tmp_path, *name;
1968 if (!path) path = "";
1970 for (; ns_list != NULL; ns_list = ns_list->next) {
1971 IMAPNameSpace *tmp_ns = ns_list->data;
1973 Xstrcat_a(tmp_path, path, "/", return namespace);
1974 Xstrdup_a(name, tmp_ns->name, return namespace);
1975 if (tmp_ns->separator && tmp_ns->separator != '/') {
1976 subst_char(tmp_path, tmp_ns->separator, '/');
1977 subst_char(name, tmp_ns->separator, '/');
1979 if (strncmp(tmp_path, name, strlen(name)) == 0)
1986 static IMAPNameSpace *imap_find_namespace(IMAPFolder *folder,
1989 IMAPNameSpace *namespace;
1991 g_return_val_if_fail(folder != NULL, NULL);
1993 namespace = imap_find_namespace_from_list(folder->ns_personal, path);
1994 if (namespace) return namespace;
1995 namespace = imap_find_namespace_from_list(folder->ns_others, path);
1996 if (namespace) return namespace;
1997 namespace = imap_find_namespace_from_list(folder->ns_shared, path);
1998 if (namespace) return namespace;
2004 static gchar imap_get_path_separator(IMAPFolder *folder, const gchar *path)
2006 IMAPNameSpace *namespace;
2007 gchar separator = '/';
2009 if (folder->last_seen_separator == 0) {
2011 int r = imap_threaded_list((Folder *)folder, "", "", &lep_list);
2012 if (r != MAILIMAP_NO_ERROR) {
2013 log_warning(_("LIST failed\n"));
2017 if (clist_count(lep_list) > 0) {
2018 clistiter * iter = clist_begin(lep_list);
2019 struct mailimap_mailbox_list * mb;
2020 mb = clist_content(iter);
2022 folder->last_seen_separator = mb->mb_delimiter;
2023 debug_print("got separator: %c\n", folder->last_seen_separator);
2025 mailimap_list_result_free(lep_list);
2028 if (folder->last_seen_separator != 0) {
2029 debug_print("using separator: %c\n", folder->last_seen_separator);
2030 return folder->last_seen_separator;
2033 namespace = imap_find_namespace(folder, path);
2034 if (namespace && namespace->separator)
2035 separator = namespace->separator;
2040 static gchar *imap_get_real_path(IMAPFolder *folder, const gchar *path)
2045 g_return_val_if_fail(folder != NULL, NULL);
2046 g_return_val_if_fail(path != NULL, NULL);
2048 real_path = imap_utf8_to_modified_utf7(path);
2049 separator = imap_get_path_separator(folder, path);
2050 imap_path_separator_subst(real_path, separator);
2055 static gint imap_set_message_flags(IMAPSession *session,
2056 MsgNumberList *numlist,
2064 seq_list = imap_get_lep_set_from_numlist(numlist);
2066 for(cur = seq_list ; cur != NULL ; cur = g_slist_next(cur)) {
2067 struct mailimap_set * imapset;
2069 imapset = cur->data;
2071 ok = imap_cmd_store(session, imapset,
2075 imap_lep_set_free(seq_list);
2077 return IMAP_SUCCESS;
2080 typedef struct _select_data {
2081 IMAPSession *session;
2086 guint32 *uid_validity;
2090 static gint imap_select(IMAPSession *session, IMAPFolder *folder,
2092 gint *exists, gint *recent, gint *unseen,
2093 guint32 *uid_validity, gboolean block)
2097 gint exists_, recent_, unseen_;
2098 guint32 uid_validity_;
2100 if (!exists || !recent || !unseen || !uid_validity) {
2101 if (session->mbox && strcmp(session->mbox, path) == 0)
2102 return IMAP_SUCCESS;
2106 uid_validity = &uid_validity_;
2109 g_free(session->mbox);
2110 session->mbox = NULL;
2112 real_path = imap_get_real_path(folder, path);
2114 ok = imap_cmd_select(session, real_path,
2115 exists, recent, unseen, uid_validity, block);
2116 if (ok != IMAP_SUCCESS)
2117 log_warning(_("can't select folder: %s\n"), real_path);
2119 session->mbox = g_strdup(path);
2120 session->folder_content_changed = FALSE;
2127 static gint imap_status(IMAPSession *session, IMAPFolder *folder,
2129 gint *messages, gint *recent,
2130 guint32 *uid_next, guint32 *uid_validity,
2131 gint *unseen, gboolean block)
2135 struct mailimap_mailbox_data_status * data_status;
2139 real_path = imap_get_real_path(folder, path);
2141 r = imap_threaded_status(FOLDER(folder), real_path, &data_status);
2143 if (r != MAILIMAP_NO_ERROR) {
2144 debug_print("status err %d\n", r);
2148 if (data_status->st_info_list == NULL) {
2149 mailimap_mailbox_data_status_free(data_status);
2150 debug_print("status->st_info_list == NULL\n");
2155 for(iter = clist_begin(data_status->st_info_list) ; iter != NULL ;
2156 iter = clist_next(iter)) {
2157 struct mailimap_status_info * info;
2159 info = clist_content(iter);
2160 switch (info->st_att) {
2161 case MAILIMAP_STATUS_ATT_MESSAGES:
2162 * messages = info->st_value;
2163 got_values |= 1 << 0;
2166 case MAILIMAP_STATUS_ATT_RECENT:
2167 * recent = info->st_value;
2168 got_values |= 1 << 1;
2171 case MAILIMAP_STATUS_ATT_UIDNEXT:
2172 * uid_next = info->st_value;
2173 got_values |= 1 << 2;
2176 case MAILIMAP_STATUS_ATT_UIDVALIDITY:
2177 * uid_validity = info->st_value;
2178 got_values |= 1 << 3;
2181 case MAILIMAP_STATUS_ATT_UNSEEN:
2182 * unseen = info->st_value;
2183 got_values |= 1 << 4;
2187 mailimap_mailbox_data_status_free(data_status);
2189 if (got_values != ((1 << 4) + (1 << 3) +
2190 (1 << 2) + (1 << 1) + (1 << 0))) {
2191 debug_print("status: incomplete values received (%d)\n", got_values);
2194 return IMAP_SUCCESS;
2197 static void imap_free_capabilities(IMAPSession *session)
2199 slist_free_strings(session->capability);
2200 g_slist_free(session->capability);
2201 session->capability = NULL;
2204 /* low-level IMAP4rev1 commands */
2207 static gint imap_cmd_authenticate(IMAPSession *session, const gchar *user,
2208 const gchar *pass, IMAPAuthType type)
2215 gchar hexdigest[33];
2219 auth_type = "CRAM-MD5";
2221 imap_gen_send(session, "AUTHENTICATE %s", auth_type);
2222 ok = imap_gen_recv(session, &buf);
2223 if (ok != IMAP_SUCCESS || buf[0] != '+' || buf[1] != ' ') {
2228 challenge = g_malloc(strlen(buf + 2) + 1);
2229 challenge_len = base64_decode(challenge, buf + 2, -1);
2230 challenge[challenge_len] = '\0';
2233 md5_hex_hmac(hexdigest, challenge, challenge_len, pass, strlen(pass));
2236 response = g_strdup_printf("%s %s", user, hexdigest);
2237 response64 = g_malloc((strlen(response) + 3) * 2 + 1);
2238 base64_encode(response64, response, strlen(response));
2241 sock_puts(SESSION(session)->sock, response64);
2242 ok = imap_cmd_ok(session, NULL);
2243 if (ok != IMAP_SUCCESS)
2244 log_warning(_("IMAP4 authentication failed.\n"));
2250 static gint imap_cmd_login(IMAPSession *session,
2251 const gchar *user, const gchar *pass,
2257 log_print("IMAP4> Logging %s to %s using %s\n",
2259 SESSION(session)->server,
2261 r = imap_threaded_login(session->folder, user, pass, type);
2262 if (r != MAILIMAP_NO_ERROR) {
2263 log_error("IMAP4< Error logging in to %s\n",
2264 SESSION(session)->server);
2272 static gint imap_cmd_logout(IMAPSession *session)
2274 imap_threaded_disconnect(session->folder);
2276 return IMAP_SUCCESS;
2279 static gint imap_cmd_noop(IMAPSession *session)
2282 unsigned int exists;
2284 r = imap_threaded_noop(session->folder, &exists);
2285 if (r != MAILIMAP_NO_ERROR) {
2286 debug_print("noop err %d\n", r);
2289 session->exists = exists;
2290 session_set_access_time(SESSION(session));
2292 return IMAP_SUCCESS;
2296 static gint imap_cmd_starttls(IMAPSession *session)
2300 r = imap_threaded_starttls(session->folder);
2301 if (r != MAILIMAP_NO_ERROR) {
2302 debug_print("starttls err %d\n", r);
2305 return IMAP_SUCCESS;
2309 static gint imap_cmd_select(IMAPSession *session, const gchar *folder,
2310 gint *exists, gint *recent, gint *unseen,
2311 guint32 *uid_validity, gboolean block)
2315 r = imap_threaded_select(session->folder, folder,
2316 exists, recent, unseen, uid_validity);
2317 if (r != MAILIMAP_NO_ERROR) {
2318 debug_print("select err %d\n", r);
2321 return IMAP_SUCCESS;
2324 static gint imap_cmd_examine(IMAPSession *session, const gchar *folder,
2325 gint *exists, gint *recent, gint *unseen,
2326 guint32 *uid_validity, gboolean block)
2330 r = imap_threaded_examine(session->folder, folder,
2331 exists, recent, unseen, uid_validity);
2332 if (r != MAILIMAP_NO_ERROR) {
2333 debug_print("examine err %d\n", r);
2337 return IMAP_SUCCESS;
2340 static gint imap_cmd_create(IMAPSession *session, const gchar *folder)
2344 r = imap_threaded_create(session->folder, folder);
2345 if (r != MAILIMAP_NO_ERROR) {
2350 return IMAP_SUCCESS;
2353 static gint imap_cmd_rename(IMAPSession *session, const gchar *old_folder,
2354 const gchar *new_folder)
2358 r = imap_threaded_rename(session->folder, old_folder,
2360 if (r != MAILIMAP_NO_ERROR) {
2365 return IMAP_SUCCESS;
2368 static gint imap_cmd_delete(IMAPSession *session, const gchar *folder)
2373 r = imap_threaded_delete(session->folder, folder);
2374 if (r != MAILIMAP_NO_ERROR) {
2379 return IMAP_SUCCESS;
2382 typedef struct _fetch_data {
2383 IMAPSession *session;
2385 const gchar *filename;
2391 static void *imap_cmd_fetch_thread(void *data)
2393 fetch_data *stuff = (fetch_data *)data;
2394 IMAPSession *session = stuff->session;
2395 guint32 uid = stuff->uid;
2396 const gchar *filename = stuff->filename;
2400 r = imap_threaded_fetch_content(session->folder,
2404 r = imap_threaded_fetch_content(session->folder,
2407 if (r != MAILIMAP_NO_ERROR) {
2408 debug_print("fetch err %d\n", r);
2409 return GINT_TO_POINTER(IMAP_ERROR);
2411 return GINT_TO_POINTER(IMAP_SUCCESS);
2414 static gint imap_cmd_fetch(IMAPSession *session, guint32 uid,
2415 const gchar *filename, gboolean headers,
2418 fetch_data *data = g_new0(fetch_data, 1);
2421 data->session = session;
2423 data->filename = filename;
2424 data->headers = headers;
2427 if (prefs_common.work_offline && !imap_gtk_should_override()) {
2432 result = GPOINTER_TO_INT(imap_cmd_fetch_thread(data));
2438 static gint imap_cmd_append(IMAPSession *session, const gchar *destfolder,
2439 const gchar *file, IMAPFlags flags,
2442 struct mailimap_flag_list * flag_list;
2445 g_return_val_if_fail(file != NULL, IMAP_ERROR);
2447 flag_list = imap_flag_to_lep(flags);
2448 r = imap_threaded_append(session->folder, destfolder,
2451 if (new_uid != NULL)
2454 if (r != MAILIMAP_NO_ERROR) {
2455 debug_print("append err %d\n", r);
2458 return IMAP_SUCCESS;
2461 static gint imap_cmd_copy(IMAPSession *session, struct mailimap_set * set,
2462 const gchar *destfolder, GRelation *uid_mapping)
2466 g_return_val_if_fail(session != NULL, IMAP_ERROR);
2467 g_return_val_if_fail(set != NULL, IMAP_ERROR);
2468 g_return_val_if_fail(destfolder != NULL, IMAP_ERROR);
2470 r = imap_threaded_copy(session->folder, set, destfolder);
2471 if (r != MAILIMAP_NO_ERROR) {
2476 return IMAP_SUCCESS;
2479 static gint imap_cmd_store(IMAPSession *session, struct mailimap_set * set,
2480 IMAPFlags flags, int do_add)
2483 struct mailimap_flag_list * flag_list;
2484 struct mailimap_store_att_flags * store_att_flags;
2486 flag_list = imap_flag_to_lep(flags);
2490 mailimap_store_att_flags_new_add_flags_silent(flag_list);
2493 mailimap_store_att_flags_new_remove_flags_silent(flag_list);
2495 r = imap_threaded_store(session->folder, set, store_att_flags);
2496 if (r != MAILIMAP_NO_ERROR) {
2501 return IMAP_SUCCESS;
2504 static gint imap_cmd_expunge(IMAPSession *session)
2508 if (prefs_common.work_offline && !imap_gtk_should_override()) {
2512 r = imap_threaded_expunge(session->folder);
2513 if (r != MAILIMAP_NO_ERROR) {
2518 return IMAP_SUCCESS;
2521 static void imap_path_separator_subst(gchar *str, gchar separator)
2524 gboolean in_escape = FALSE;
2526 if (!separator || separator == '/') return;
2528 for (p = str; *p != '\0'; p++) {
2529 if (*p == '/' && !in_escape)
2531 else if (*p == '&' && *(p + 1) != '-' && !in_escape)
2533 else if (*p == '-' && in_escape)
2538 static gchar *imap_modified_utf7_to_utf8(const gchar *mutf7_str)
2540 static iconv_t cd = (iconv_t)-1;
2541 static gboolean iconv_ok = TRUE;
2544 size_t norm_utf7_len;
2546 gchar *to_str, *to_p;
2548 gboolean in_escape = FALSE;
2550 if (!iconv_ok) return g_strdup(mutf7_str);
2552 if (cd == (iconv_t)-1) {
2553 cd = iconv_open(CS_INTERNAL, CS_UTF_7);
2554 if (cd == (iconv_t)-1) {
2555 g_warning("iconv cannot convert UTF-7 to %s\n",
2558 return g_strdup(mutf7_str);
2562 /* modified UTF-7 to normal UTF-7 conversion */
2563 norm_utf7 = g_string_new(NULL);
2565 for (p = mutf7_str; *p != '\0'; p++) {
2566 /* replace: '&' -> '+',
2568 escaped ',' -> '/' */
2569 if (!in_escape && *p == '&') {
2570 if (*(p + 1) != '-') {
2571 g_string_append_c(norm_utf7, '+');
2574 g_string_append_c(norm_utf7, '&');
2577 } else if (in_escape && *p == ',') {
2578 g_string_append_c(norm_utf7, '/');
2579 } else if (in_escape && *p == '-') {
2580 g_string_append_c(norm_utf7, '-');
2583 g_string_append_c(norm_utf7, *p);
2587 norm_utf7_p = norm_utf7->str;
2588 norm_utf7_len = norm_utf7->len;
2589 to_len = strlen(mutf7_str) * 5;
2590 to_p = to_str = g_malloc(to_len + 1);
2592 if (iconv(cd, (ICONV_CONST gchar **)&norm_utf7_p, &norm_utf7_len,
2593 &to_p, &to_len) == -1) {
2594 g_warning(_("iconv cannot convert UTF-7 to %s\n"),
2595 conv_get_locale_charset_str());
2596 g_string_free(norm_utf7, TRUE);
2598 return g_strdup(mutf7_str);
2601 /* second iconv() call for flushing */
2602 iconv(cd, NULL, NULL, &to_p, &to_len);
2603 g_string_free(norm_utf7, TRUE);
2609 static gchar *imap_utf8_to_modified_utf7(const gchar *from)
2611 static iconv_t cd = (iconv_t)-1;
2612 static gboolean iconv_ok = TRUE;
2613 gchar *norm_utf7, *norm_utf7_p;
2614 size_t from_len, norm_utf7_len;
2616 gchar *from_tmp, *to, *p;
2617 gboolean in_escape = FALSE;
2619 if (!iconv_ok) return g_strdup(from);
2621 if (cd == (iconv_t)-1) {
2622 cd = iconv_open(CS_UTF_7, CS_INTERNAL);
2623 if (cd == (iconv_t)-1) {
2624 g_warning(_("iconv cannot convert %s to UTF-7\n"),
2627 return g_strdup(from);
2631 /* UTF-8 to normal UTF-7 conversion */
2632 Xstrdup_a(from_tmp, from, return g_strdup(from));
2633 from_len = strlen(from);
2634 norm_utf7_len = from_len * 5;
2635 Xalloca(norm_utf7, norm_utf7_len + 1, return g_strdup(from));
2636 norm_utf7_p = norm_utf7;
2638 #define IS_PRINT(ch) (isprint(ch) && IS_ASCII(ch))
2640 while (from_len > 0) {
2641 if (*from_tmp == '+') {
2642 *norm_utf7_p++ = '+';
2643 *norm_utf7_p++ = '-';
2647 } else if (IS_PRINT(*(guchar *)from_tmp)) {
2648 /* printable ascii char */
2649 *norm_utf7_p = *from_tmp;
2655 size_t conv_len = 0;
2657 /* unprintable char: convert to UTF-7 */
2659 while (!IS_PRINT(*(guchar *)p) && conv_len < from_len) {
2660 conv_len += g_utf8_skip[*(guchar *)p];
2661 p += g_utf8_skip[*(guchar *)p];
2664 from_len -= conv_len;
2665 if (iconv(cd, (ICONV_CONST gchar **)&from_tmp,
2667 &norm_utf7_p, &norm_utf7_len) == -1) {
2668 g_warning(_("iconv cannot convert UTF-8 to UTF-7\n"));
2669 return g_strdup(from);
2672 /* second iconv() call for flushing */
2673 iconv(cd, NULL, NULL, &norm_utf7_p, &norm_utf7_len);
2679 *norm_utf7_p = '\0';
2680 to_str = g_string_new(NULL);
2681 for (p = norm_utf7; p < norm_utf7_p; p++) {
2682 /* replace: '&' -> "&-",
2685 BASE64 '/' -> ',' */
2686 if (!in_escape && *p == '&') {
2687 g_string_append(to_str, "&-");
2688 } else if (!in_escape && *p == '+') {
2689 if (*(p + 1) == '-') {
2690 g_string_append_c(to_str, '+');
2693 g_string_append_c(to_str, '&');
2696 } else if (in_escape && *p == '/') {
2697 g_string_append_c(to_str, ',');
2698 } else if (in_escape && *p == '-') {
2699 g_string_append_c(to_str, '-');
2702 g_string_append_c(to_str, *p);
2708 g_string_append_c(to_str, '-');
2712 g_string_free(to_str, FALSE);
2717 static gboolean imap_rename_folder_func(GNode *node, gpointer data)
2719 FolderItem *item = node->data;
2720 gchar **paths = data;
2721 const gchar *oldpath = paths[0];
2722 const gchar *newpath = paths[1];
2724 gchar *new_itempath;
2727 oldpathlen = strlen(oldpath);
2728 if (strncmp(oldpath, item->path, oldpathlen) != 0) {
2729 g_warning("path doesn't match: %s, %s\n", oldpath, item->path);
2733 base = item->path + oldpathlen;
2734 while (*base == G_DIR_SEPARATOR) base++;
2736 new_itempath = g_strdup(newpath);
2738 new_itempath = g_strconcat(newpath, G_DIR_SEPARATOR_S, base,
2741 item->path = new_itempath;
2746 typedef struct _get_list_uid_data {
2748 IMAPFolderItem *item;
2749 GSList **msgnum_list;
2751 } get_list_uid_data;
2753 static void *get_list_of_uids_thread(void *data)
2755 get_list_uid_data *stuff = (get_list_uid_data *)data;
2756 Folder *folder = stuff->folder;
2757 IMAPFolderItem *item = stuff->item;
2758 GSList **msgnum_list = stuff->msgnum_list;
2759 gint ok, nummsgs = 0, lastuid_old;
2760 IMAPSession *session;
2761 GSList *uidlist, *elem;
2762 struct mailimap_set * set;
2763 clist * lep_uidlist;
2766 session = imap_session_get(folder);
2767 if (session == NULL) {
2769 return GINT_TO_POINTER(-1);
2772 ok = imap_select(session, IMAP_FOLDER(folder), item->item.path,
2773 NULL, NULL, NULL, NULL, TRUE);
2774 if (ok != IMAP_SUCCESS) {
2776 return GINT_TO_POINTER(-1);
2781 set = mailimap_set_new_interval(item->lastuid + 1, 0);
2782 r = imap_threaded_search(folder, IMAP_SEARCH_TYPE_SIMPLE, set,
2784 if (r == MAILIMAP_NO_ERROR) {
2785 GSList * fetchuid_list;
2788 imap_uid_list_from_lep(lep_uidlist);
2789 uidlist = g_slist_concat(fetchuid_list, uidlist);
2792 GSList * fetchuid_list;
2793 carray * lep_uidtab;
2795 r = imap_threaded_fetch_uid(folder, item->lastuid + 1,
2797 if (r == MAILIMAP_NO_ERROR) {
2799 imap_uid_list_from_lep_tab(lep_uidtab);
2800 uidlist = g_slist_concat(fetchuid_list, uidlist);
2804 lastuid_old = item->lastuid;
2805 *msgnum_list = g_slist_copy(item->uid_list);
2806 nummsgs = g_slist_length(*msgnum_list);
2807 debug_print("Got %d uids from cache\n", g_slist_length(item->uid_list));
2809 for (elem = uidlist; elem != NULL; elem = g_slist_next(elem)) {
2812 msgnum = GPOINTER_TO_INT(elem->data);
2813 if (msgnum > lastuid_old) {
2814 *msgnum_list = g_slist_prepend(*msgnum_list, GINT_TO_POINTER(msgnum));
2815 item->uid_list = g_slist_prepend(item->uid_list, GINT_TO_POINTER(msgnum));
2818 if(msgnum > item->lastuid)
2819 item->lastuid = msgnum;
2822 g_slist_free(uidlist);
2825 return GINT_TO_POINTER(nummsgs);
2828 static gint get_list_of_uids(Folder *folder, IMAPFolderItem *item, GSList **msgnum_list)
2831 get_list_uid_data *data = g_new0(get_list_uid_data, 1);
2833 data->folder = folder;
2835 data->msgnum_list = msgnum_list;
2837 if (prefs_common.work_offline && !imap_gtk_should_override()) {
2842 result = GPOINTER_TO_INT(get_list_of_uids_thread(data));
2848 gint imap_get_num_list(Folder *folder, FolderItem *_item, GSList **msgnum_list, gboolean *old_uids_valid)
2850 IMAPFolderItem *item = (IMAPFolderItem *)_item;
2851 IMAPSession *session;
2852 gint ok, nummsgs = 0, exists, recent, uid_val, uid_next, unseen;
2853 GSList *uidlist = NULL;
2855 gboolean selected_folder;
2857 debug_print("get_num_list\n");
2859 g_return_val_if_fail(folder != NULL, -1);
2860 g_return_val_if_fail(item != NULL, -1);
2861 g_return_val_if_fail(item->item.path != NULL, -1);
2862 g_return_val_if_fail(FOLDER_CLASS(folder) == &imap_class, -1);
2863 g_return_val_if_fail(folder->account != NULL, -1);
2865 session = imap_session_get(folder);
2866 g_return_val_if_fail(session != NULL, -1);
2868 selected_folder = (session->mbox != NULL) &&
2869 (!strcmp(session->mbox, item->item.path));
2870 if (selected_folder) {
2871 ok = imap_cmd_noop(session);
2872 if (ok != IMAP_SUCCESS) {
2876 exists = session->exists;
2878 *old_uids_valid = TRUE;
2880 ok = imap_status(session, IMAP_FOLDER(folder), item->item.path,
2881 &exists, &recent, &uid_next, &uid_val, &unseen, FALSE);
2882 if (ok != IMAP_SUCCESS) {
2886 if(item->item.mtime == uid_val)
2887 *old_uids_valid = TRUE;
2889 *old_uids_valid = FALSE;
2891 debug_print("Freeing imap uid cache\n");
2893 g_slist_free(item->uid_list);
2894 item->uid_list = NULL;
2896 item->item.mtime = uid_val;
2898 imap_delete_all_cached_messages((FolderItem *)item);
2902 if (!selected_folder)
2903 item->uid_next = uid_next;
2905 /* If old uid_next matches new uid_next we can be sure no message
2906 was added to the folder */
2907 if (( selected_folder && !session->folder_content_changed) ||
2908 (!selected_folder && uid_next == item->uid_next)) {
2909 nummsgs = g_slist_length(item->uid_list);
2911 /* If number of messages is still the same we
2912 know our caches message numbers are still valid,
2913 otherwise if the number of messages has decrease
2914 we discard our cache to start a new scan to find
2915 out which numbers have been removed */
2916 if (exists == nummsgs) {
2917 *msgnum_list = g_slist_copy(item->uid_list);
2919 } else if (exists < nummsgs) {
2920 debug_print("Freeing imap uid cache");
2922 g_slist_free(item->uid_list);
2923 item->uid_list = NULL;
2928 *msgnum_list = NULL;
2932 nummsgs = get_list_of_uids(folder, item, &uidlist);
2939 if (nummsgs != exists) {
2940 /* Cache contains more messages then folder, we have cached
2941 an old UID of a message that was removed and new messages
2942 have been added too, otherwise the uid_next check would
2944 debug_print("Freeing imap uid cache");
2946 g_slist_free(item->uid_list);
2947 item->uid_list = NULL;
2949 g_slist_free(*msgnum_list);
2951 nummsgs = get_list_of_uids(folder, item, &uidlist);
2954 *msgnum_list = uidlist;
2956 dir = folder_item_get_path((FolderItem *)item);
2957 debug_print("removing old messages from %s\n", dir);
2958 remove_numbered_files_not_in_list(dir, *msgnum_list);
2961 debug_print("get_num_list - ok - %i\n", nummsgs);
2966 static MsgInfo *imap_parse_msg(const gchar *file, FolderItem *item)
2971 flags.perm_flags = MSG_NEW|MSG_UNREAD;
2972 flags.tmp_flags = 0;
2974 g_return_val_if_fail(item != NULL, NULL);
2975 g_return_val_if_fail(file != NULL, NULL);
2977 if (folder_has_parent_of_type(item, F_QUEUE)) {
2978 MSG_SET_TMP_FLAGS(flags, MSG_QUEUED);
2979 } else if (folder_has_parent_of_type(item, F_DRAFT)) {
2980 MSG_SET_TMP_FLAGS(flags, MSG_DRAFT);
2983 msginfo = procheader_parse_file(file, flags, FALSE, FALSE);
2984 if (!msginfo) return NULL;
2986 msginfo->plaintext_file = g_strdup(file);
2987 msginfo->folder = item;
2992 GSList *imap_get_msginfos(Folder *folder, FolderItem *item,
2993 GSList *msgnum_list)
2995 IMAPSession *session;
2996 MsgInfoList *ret = NULL;
2999 debug_print("get_msginfos\n");
3001 g_return_val_if_fail(folder != NULL, NULL);
3002 g_return_val_if_fail(item != NULL, NULL);
3003 g_return_val_if_fail(msgnum_list != NULL, NULL);
3005 session = imap_session_get(folder);
3006 g_return_val_if_fail(session != NULL, NULL);
3008 debug_print("IMAP getting msginfos\n");
3009 ok = imap_select(session, IMAP_FOLDER(folder), item->path,
3010 NULL, NULL, NULL, NULL, FALSE);
3011 if (ok != IMAP_SUCCESS)
3014 if (!(folder_has_parent_of_type(item, F_DRAFT) ||
3015 folder_has_parent_of_type(item, F_QUEUE))) {
3016 ret = g_slist_concat(ret,
3017 imap_get_uncached_messages(session, item,
3020 MsgNumberList *sorted_list, *elem;
3021 gint startnum, lastnum;
3023 sorted_list = g_slist_sort(g_slist_copy(msgnum_list), g_int_compare);
3025 startnum = lastnum = GPOINTER_TO_INT(sorted_list->data);
3027 for (elem = sorted_list;; elem = g_slist_next(elem)) {
3031 num = GPOINTER_TO_INT(elem->data);
3033 if (num > lastnum + 1 || elem == NULL) {
3035 for (i = startnum; i <= lastnum; ++i) {
3038 file = imap_fetch_msg(folder, item, i);
3040 MsgInfo *msginfo = imap_parse_msg(file, item);
3041 if (msginfo != NULL) {
3042 msginfo->msgnum = i;
3043 ret = g_slist_append(ret, msginfo);
3057 g_slist_free(sorted_list);
3063 MsgInfo *imap_get_msginfo(Folder *folder, FolderItem *item, gint uid)
3065 MsgInfo *msginfo = NULL;
3066 MsgInfoList *msginfolist;
3067 MsgNumberList numlist;
3069 numlist.next = NULL;
3070 numlist.data = GINT_TO_POINTER(uid);
3072 msginfolist = imap_get_msginfos(folder, item, &numlist);
3073 if (msginfolist != NULL) {
3074 msginfo = msginfolist->data;
3075 g_slist_free(msginfolist);
3081 gboolean imap_scan_required(Folder *folder, FolderItem *_item)
3083 IMAPSession *session;
3084 IMAPFolderItem *item = (IMAPFolderItem *)_item;
3085 gint ok, exists = 0, recent = 0, unseen = 0;
3086 guint32 uid_next, uid_val = 0;
3087 gboolean selected_folder;
3089 g_return_val_if_fail(folder != NULL, FALSE);
3090 g_return_val_if_fail(item != NULL, FALSE);
3091 g_return_val_if_fail(item->item.folder != NULL, FALSE);
3092 g_return_val_if_fail(FOLDER_CLASS(item->item.folder) == &imap_class, FALSE);
3094 if (item->item.path == NULL)
3097 session = imap_session_get(folder);
3098 g_return_val_if_fail(session != NULL, FALSE);
3100 selected_folder = (session->mbox != NULL) &&
3101 (!strcmp(session->mbox, item->item.path));
3102 if (selected_folder) {
3103 ok = imap_cmd_noop(session);
3104 if (ok != IMAP_SUCCESS)
3107 if (session->folder_content_changed
3108 || session->exists != item->item.total_msgs)
3111 ok = imap_status(session, IMAP_FOLDER(folder), item->item.path,
3112 &exists, &recent, &uid_next, &uid_val, &unseen, FALSE);
3113 if (ok != IMAP_SUCCESS)
3116 if ((uid_next != item->uid_next) || (exists != item->item.total_msgs))
3123 void imap_change_flags(Folder *folder, FolderItem *item, MsgInfo *msginfo, MsgPermFlags newflags)
3125 IMAPSession *session;
3126 IMAPFlags flags_set = 0, flags_unset = 0;
3127 gint ok = IMAP_SUCCESS;
3128 MsgNumberList numlist;
3129 hashtable_data *ht_data = NULL;
3131 g_return_if_fail(folder != NULL);
3132 g_return_if_fail(folder->klass == &imap_class);
3133 g_return_if_fail(item != NULL);
3134 g_return_if_fail(item->folder == folder);
3135 g_return_if_fail(msginfo != NULL);
3136 g_return_if_fail(msginfo->folder == item);
3138 session = imap_session_get(folder);
3142 if ((ok = imap_select(session, IMAP_FOLDER(folder), msginfo->folder->path,
3143 NULL, NULL, NULL, NULL, FALSE)) != IMAP_SUCCESS) {
3147 if (!MSG_IS_MARKED(msginfo->flags) && (newflags & MSG_MARKED))
3148 flags_set |= IMAP_FLAG_FLAGGED;
3149 if ( MSG_IS_MARKED(msginfo->flags) && !(newflags & MSG_MARKED))
3150 flags_unset |= IMAP_FLAG_FLAGGED;
3152 if (!MSG_IS_UNREAD(msginfo->flags) && (newflags & MSG_UNREAD))
3153 flags_unset |= IMAP_FLAG_SEEN;
3154 if ( MSG_IS_UNREAD(msginfo->flags) && !(newflags & MSG_UNREAD))
3155 flags_set |= IMAP_FLAG_SEEN;
3157 if (!MSG_IS_REPLIED(msginfo->flags) && (newflags & MSG_REPLIED))
3158 flags_set |= IMAP_FLAG_ANSWERED;
3159 if ( MSG_IS_REPLIED(msginfo->flags) && !(newflags & MSG_REPLIED))
3160 flags_set |= IMAP_FLAG_ANSWERED;
3162 numlist.next = NULL;
3163 numlist.data = GINT_TO_POINTER(msginfo->msgnum);
3165 if (IMAP_FOLDER_ITEM(item)->batching) {
3166 /* instead of performing an UID STORE command for each message change,
3167 * as a lot of them can change "together", we just fill in hashtables
3168 * and defer the treatment so that we're able to send only one
3171 debug_print("IMAP batch mode on, deferring flags change\n");
3173 ht_data = g_hash_table_lookup(flags_set_table, GINT_TO_POINTER(flags_set));
3174 if (ht_data == NULL) {
3175 ht_data = g_new0(hashtable_data, 1);
3176 ht_data->session = session;
3177 g_hash_table_insert(flags_set_table, GINT_TO_POINTER(flags_set), ht_data);
3179 if (!g_slist_find(ht_data->msglist, GINT_TO_POINTER(msginfo->msgnum)))
3180 ht_data->msglist = g_slist_prepend(ht_data->msglist, GINT_TO_POINTER(msginfo->msgnum));
3183 ht_data = g_hash_table_lookup(flags_unset_table, GINT_TO_POINTER(flags_unset));
3184 if (ht_data == NULL) {
3185 ht_data = g_new0(hashtable_data, 1);
3186 ht_data->session = session;
3187 g_hash_table_insert(flags_unset_table, GINT_TO_POINTER(flags_unset), ht_data);
3189 if (!g_slist_find(ht_data->msglist, GINT_TO_POINTER(msginfo->msgnum)))
3190 ht_data->msglist = g_slist_prepend(ht_data->msglist, GINT_TO_POINTER(msginfo->msgnum));
3193 debug_print("IMAP changing flags\n");
3195 ok = imap_set_message_flags(session, &numlist, flags_set, TRUE);
3196 if (ok != IMAP_SUCCESS) {
3202 ok = imap_set_message_flags(session, &numlist, flags_unset, FALSE);
3203 if (ok != IMAP_SUCCESS) {
3208 msginfo->flags.perm_flags = newflags;
3213 static gint imap_remove_msg(Folder *folder, FolderItem *item, gint uid)
3216 IMAPSession *session;
3218 MsgNumberList numlist;
3220 g_return_val_if_fail(folder != NULL, -1);
3221 g_return_val_if_fail(FOLDER_CLASS(folder) == &imap_class, -1);
3222 g_return_val_if_fail(item != NULL, -1);
3224 session = imap_session_get(folder);
3225 if (!session) return -1;
3227 ok = imap_select(session, IMAP_FOLDER(folder), item->path,
3228 NULL, NULL, NULL, NULL, FALSE);
3229 if (ok != IMAP_SUCCESS)
3232 numlist.next = NULL;
3233 numlist.data = GINT_TO_POINTER(uid);
3235 ok = imap_set_message_flags
3236 (IMAP_SESSION(REMOTE_FOLDER(folder)->session),
3237 &numlist, IMAP_FLAG_DELETED, TRUE);
3238 if (ok != IMAP_SUCCESS) {
3239 log_warning(_("can't set deleted flags: %d\n"), uid);
3243 if (!session->uidplus) {
3244 ok = imap_cmd_expunge(session);
3248 uidstr = g_strdup_printf("%u", uid);
3249 ok = imap_cmd_expunge(session);
3252 if (ok != IMAP_SUCCESS) {
3253 log_warning(_("can't expunge\n"));
3257 IMAP_FOLDER_ITEM(item)->uid_list = g_slist_remove(
3258 IMAP_FOLDER_ITEM(item)->uid_list, numlist.data);
3259 dir = folder_item_get_path(item);
3260 if (is_dir_exist(dir))
3261 remove_numbered_files(dir, uid, uid);
3264 return IMAP_SUCCESS;
3267 static gint compare_msginfo(gconstpointer a, gconstpointer b)
3269 return ((MsgInfo *)a)->msgnum - ((MsgInfo *)b)->msgnum;
3272 static guint gslist_find_next_num(MsgNumberList **list, guint num)
3276 g_return_val_if_fail(list != NULL, -1);
3278 for (elem = *list; elem != NULL; elem = g_slist_next(elem))
3279 if (GPOINTER_TO_INT(elem->data) >= num)
3282 return elem != NULL ? GPOINTER_TO_INT(elem->data) : (gint)-1;
3286 * NEW and DELETED flags are not syncronized
3287 * - The NEW/RECENT flags in IMAP folders can not really be directly
3288 * modified by Sylpheed
3289 * - The DELETE/DELETED flag in IMAP and Sylpheed don't have the same
3290 * meaning, in IMAP it always removes the messages from the FolderItem
3291 * in Sylpheed it can mean to move the message to trash
3294 typedef struct _get_flags_data {
3297 MsgInfoList *msginfo_list;
3298 GRelation *msgflags;
3302 static /*gint*/ void *imap_get_flags_thread(void *data)
3304 get_flags_data *stuff = (get_flags_data *)data;
3305 Folder *folder = stuff->folder;
3306 FolderItem *item = stuff->item;
3307 MsgInfoList *msginfo_list = stuff->msginfo_list;
3308 GRelation *msgflags = stuff->msgflags;
3309 IMAPSession *session;
3310 GSList *sorted_list;
3311 GSList *unseen = NULL, *answered = NULL, *flagged = NULL;
3312 GSList *p_unseen, *p_answered, *p_flagged;
3314 GSList *seq_list, *cur;
3315 gboolean reverse_seen = FALSE;
3318 gint exists_cnt, recent_cnt, unseen_cnt, uid_next;
3319 guint32 uidvalidity;
3320 gboolean selected_folder;
3322 if (folder == NULL || item == NULL) {
3324 return GINT_TO_POINTER(-1);
3326 if (msginfo_list == NULL) {
3328 return GINT_TO_POINTER(0);
3331 session = imap_session_get(folder);
3332 if (session == NULL) {
3334 return GINT_TO_POINTER(-1);
3337 selected_folder = (session->mbox != NULL) &&
3338 (!strcmp(session->mbox, item->path));
3340 if (!selected_folder) {
3341 ok = imap_status(session, IMAP_FOLDER(folder), item->path,
3342 &exists_cnt, &recent_cnt, &uid_next, &uidvalidity, &unseen_cnt, TRUE);
3343 ok = imap_select(session, IMAP_FOLDER(folder), item->path,
3344 NULL, NULL, NULL, NULL, TRUE);
3345 if (ok != IMAP_SUCCESS) {
3347 return GINT_TO_POINTER(-1);
3350 if (unseen_cnt > exists_cnt / 2)
3351 reverse_seen = TRUE;
3354 if (item->unread_msgs > item->total_msgs / 2)
3355 reverse_seen = TRUE;
3358 cmd_buf = g_string_new(NULL);
3360 sorted_list = g_slist_sort(g_slist_copy(msginfo_list), compare_msginfo);
3362 seq_list = imap_get_lep_set_from_msglist(msginfo_list);
3364 for (cur = seq_list; cur != NULL; cur = g_slist_next(cur)) {
3365 struct mailimap_set * imapset;
3366 clist * lep_uidlist;
3369 imapset = cur->data;
3371 r = imap_threaded_search(folder, IMAP_SEARCH_TYPE_SEEN,
3372 imapset, &lep_uidlist);
3375 r = imap_threaded_search(folder,
3376 IMAP_SEARCH_TYPE_UNSEEN,
3377 imapset, &lep_uidlist);
3379 if (r == MAILIMAP_NO_ERROR) {
3382 uidlist = imap_uid_list_from_lep(lep_uidlist);
3383 mailimap_search_result_free(lep_uidlist);
3385 unseen = g_slist_concat(unseen, uidlist);
3388 r = imap_threaded_search(folder, IMAP_SEARCH_TYPE_ANSWERED,
3389 imapset, &lep_uidlist);
3390 if (r == MAILIMAP_NO_ERROR) {
3393 uidlist = imap_uid_list_from_lep(lep_uidlist);
3394 mailimap_search_result_free(lep_uidlist);
3396 answered = g_slist_concat(answered, uidlist);
3399 r = imap_threaded_search(folder, IMAP_SEARCH_TYPE_FLAGGED,
3400 imapset, &lep_uidlist);
3401 if (r == MAILIMAP_NO_ERROR) {
3404 uidlist = imap_uid_list_from_lep(lep_uidlist);
3405 mailimap_search_result_free(lep_uidlist);
3407 flagged = g_slist_concat(flagged, uidlist);
3412 p_answered = answered;
3413 p_flagged = flagged;
3415 for (elem = sorted_list; elem != NULL; elem = g_slist_next(elem)) {
3420 msginfo = (MsgInfo *) elem->data;
3421 flags = msginfo->flags.perm_flags;
3422 wasnew = (flags & MSG_NEW);
3423 flags &= ~((reverse_seen ? 0 : MSG_UNREAD | MSG_NEW) | MSG_REPLIED | MSG_MARKED);
3425 flags |= MSG_UNREAD | (wasnew ? MSG_NEW : 0);
3426 if (gslist_find_next_num(&p_unseen, msginfo->msgnum) == msginfo->msgnum) {
3427 if (!reverse_seen) {
3428 flags |= MSG_UNREAD | (wasnew ? MSG_NEW : 0);
3430 flags &= ~(MSG_UNREAD | MSG_NEW);
3433 if (gslist_find_next_num(&p_answered, msginfo->msgnum) == msginfo->msgnum)
3434 flags |= MSG_REPLIED;
3435 if (gslist_find_next_num(&p_flagged, msginfo->msgnum) == msginfo->msgnum)
3436 flags |= MSG_MARKED;
3437 g_relation_insert(msgflags, msginfo, GINT_TO_POINTER(flags));
3440 imap_lep_set_free(seq_list);
3441 g_slist_free(flagged);
3442 g_slist_free(answered);
3443 g_slist_free(unseen);
3444 g_slist_free(sorted_list);
3445 g_string_free(cmd_buf, TRUE);
3448 return GINT_TO_POINTER(0);
3451 static gint imap_get_flags(Folder *folder, FolderItem *item,
3452 MsgInfoList *msginfo_list, GRelation *msgflags)
3455 get_flags_data *data = g_new0(get_flags_data, 1);
3457 data->folder = folder;
3459 data->msginfo_list = msginfo_list;
3460 data->msgflags = msgflags;
3462 if (prefs_common.work_offline && !imap_gtk_should_override()) {
3467 result = GPOINTER_TO_INT(imap_get_flags_thread(data));
3474 static gboolean process_flags(gpointer key, gpointer value, gpointer user_data)
3476 gboolean flags_set = GPOINTER_TO_INT(user_data);
3477 gint flags_value = GPOINTER_TO_INT(key);
3478 hashtable_data *data = (hashtable_data *)value;
3480 data->msglist = g_slist_reverse(data->msglist);
3482 debug_print("IMAP %ssetting flags to %d for %d messages\n",
3485 g_slist_length(data->msglist));
3486 imap_set_message_flags(data->session, data->msglist, flags_value, flags_set);
3488 g_slist_free(data->msglist);
3493 static void process_hashtable(void)
3495 if (flags_set_table) {
3496 g_hash_table_foreach_remove(flags_set_table, process_flags, GINT_TO_POINTER(TRUE));
3497 g_free(flags_set_table);
3498 flags_set_table = NULL;
3500 if (flags_unset_table) {
3501 g_hash_table_foreach_remove(flags_unset_table, process_flags, GINT_TO_POINTER(FALSE));
3502 g_free(flags_unset_table);
3503 flags_unset_table = NULL;
3507 static IMAPFolderItem *batching_item = NULL;
3509 static void imap_set_batch (Folder *folder, FolderItem *_item, gboolean batch)
3511 IMAPFolderItem *item = (IMAPFolderItem *)_item;
3513 g_return_if_fail(item != NULL);
3515 if (batch && batching_item != NULL) {
3516 g_warning("already batching on %s\n", batching_item->item.path);
3520 if (item->batching == batch)
3523 item->batching = batch;
3525 batching_item = batch?item:NULL;
3528 debug_print("IMAP switching to batch mode\n");
3529 if (flags_set_table) {
3530 g_warning("flags_set_table non-null but we just entered batch mode!\n");
3531 flags_set_table = NULL;
3533 if (flags_unset_table) {
3534 g_warning("flags_unset_table non-null but we just entered batch mode!\n");
3535 flags_unset_table = NULL;
3537 flags_set_table = g_hash_table_new(NULL, g_direct_equal);
3538 flags_unset_table = g_hash_table_new(NULL, g_direct_equal);
3540 debug_print("IMAP switching away from batch mode\n");
3542 process_hashtable();
3548 /* data types conversion libetpan <-> sylpheed */
3552 #define ETPAN_IMAP_MB_MARKED 1
3553 #define ETPAN_IMAP_MB_UNMARKED 2
3554 #define ETPAN_IMAP_MB_NOSELECT 4
3555 #define ETPAN_IMAP_MB_NOINFERIORS 8
3557 static int imap_flags_to_flags(struct mailimap_mbx_list_flags * imap_flags)
3563 if (imap_flags->mbf_type == MAILIMAP_MBX_LIST_FLAGS_SFLAG) {
3564 switch (imap_flags->mbf_sflag) {
3565 case MAILIMAP_MBX_LIST_SFLAG_MARKED:
3566 flags |= ETPAN_IMAP_MB_MARKED;
3568 case MAILIMAP_MBX_LIST_SFLAG_NOSELECT:
3569 flags |= ETPAN_IMAP_MB_NOSELECT;
3571 case MAILIMAP_MBX_LIST_SFLAG_UNMARKED:
3572 flags |= ETPAN_IMAP_MB_UNMARKED;
3577 for(cur = clist_begin(imap_flags->mbf_oflags) ; cur != NULL ;
3578 cur = clist_next(cur)) {
3579 struct mailimap_mbx_list_oflag * oflag;
3581 oflag = clist_content(cur);
3583 switch (oflag->of_type) {
3584 case MAILIMAP_MBX_LIST_OFLAG_NOINFERIORS:
3585 flags |= ETPAN_IMAP_MB_NOINFERIORS;
3593 static GSList * imap_list_from_lep(IMAPFolder * folder,
3594 clist * list, const gchar * real_path)
3601 for(iter = clist_begin(list) ; iter != NULL ;
3602 iter = clist_next(iter)) {
3603 struct mailimap_mailbox_list * mb;
3611 FolderItem *new_item;
3613 mb = clist_content(iter);
3616 if (mb->mb_flag != NULL)
3617 flags = imap_flags_to_flags(mb->mb_flag);
3619 delimiter = mb->mb_delimiter;
3622 dup_name = strdup(name);
3623 if (delimiter != '\0')
3624 subst_char(dup_name, delimiter, '/');
3626 base = g_path_get_basename(dup_name);
3627 if (base[0] == '.') {
3633 if (strcmp(dup_name, real_path) == 0) {
3639 if (dup_name[strlen(dup_name)-1] == '/') {
3645 loc_name = imap_modified_utf7_to_utf8(base);
3646 loc_path = imap_modified_utf7_to_utf8(dup_name);
3648 new_item = folder_item_new(FOLDER(folder), loc_name, loc_path);
3649 if ((flags & ETPAN_IMAP_MB_NOINFERIORS) != 0)
3650 new_item->no_sub = TRUE;
3651 if (strcmp(dup_name, "INBOX") != 0 &&
3652 ((flags & ETPAN_IMAP_MB_NOSELECT) != 0))
3653 new_item->no_select = TRUE;
3655 item_list = g_slist_append(item_list, new_item);
3657 debug_print("folder '%s' found.\n", loc_path);
3668 static GSList * imap_get_lep_set_from_numlist(MsgNumberList *numlist)
3670 GSList *sorted_list, *cur;
3671 guint first, last, next;
3672 GSList *ret_list = NULL;
3674 struct mailimap_set * current_set;
3675 unsigned int item_count;
3677 if (numlist == NULL)
3681 current_set = mailimap_set_new_empty();
3683 sorted_list = g_slist_copy(numlist);
3684 sorted_list = g_slist_sort(sorted_list, g_int_compare);
3686 first = GPOINTER_TO_INT(sorted_list->data);
3689 for (cur = sorted_list; cur != NULL; cur = g_slist_next(cur)) {
3690 if (GPOINTER_TO_INT(cur->data) == 0)
3695 last = GPOINTER_TO_INT(cur->data);
3697 next = GPOINTER_TO_INT(cur->next->data);
3701 if (last + 1 != next || next == 0) {
3703 struct mailimap_set_item * item;
3704 item = mailimap_set_item_new(first, last);
3705 mailimap_set_add(current_set, item);
3710 if (count >= IMAP_SET_MAX_COUNT) {
3711 ret_list = g_slist_append(ret_list,
3713 current_set = mailimap_set_new_empty();
3720 if (clist_count(current_set->set_list) > 0) {
3721 ret_list = g_slist_append(ret_list,
3725 g_slist_free(sorted_list);
3730 static GSList * imap_get_lep_set_from_msglist(MsgInfoList *msglist)
3732 MsgNumberList *numlist = NULL;
3736 for (cur = msglist; cur != NULL; cur = g_slist_next(cur)) {
3737 MsgInfo *msginfo = (MsgInfo *) cur->data;
3739 numlist = g_slist_append(numlist, GINT_TO_POINTER(msginfo->msgnum));
3741 seq_list = imap_get_lep_set_from_numlist(numlist);
3742 g_slist_free(numlist);
3747 static GSList * imap_uid_list_from_lep(clist * list)
3754 for(iter = clist_begin(list) ; iter != NULL ;
3755 iter = clist_next(iter)) {
3758 puid = clist_content(iter);
3759 result = g_slist_append(result, GINT_TO_POINTER(* puid));
3765 static GSList * imap_uid_list_from_lep_tab(carray * list)
3772 for(i = 0 ; i < carray_count(list) ; i ++) {
3775 puid = carray_get(list, i);
3776 result = g_slist_append(result, GINT_TO_POINTER(* puid));
3782 static MsgInfo *imap_envelope_from_lep(struct imap_fetch_env_info * info,
3785 MsgInfo *msginfo = NULL;
3788 MsgFlags flags = {0, 0};
3790 MSG_SET_TMP_FLAGS(flags, MSG_IMAP);
3791 if (folder_has_parent_of_type(item, F_QUEUE)) {
3792 MSG_SET_TMP_FLAGS(flags, MSG_QUEUED);
3793 } else if (folder_has_parent_of_type(item, F_DRAFT)) {
3794 MSG_SET_TMP_FLAGS(flags, MSG_DRAFT);
3796 flags.perm_flags = info->flags;
3800 msginfo = procheader_parse_str(info->headers, flags, FALSE, FALSE);
3803 msginfo->msgnum = uid;
3804 msginfo->size = size;
3810 static void imap_lep_set_free(GSList *seq_list)
3814 for(cur = seq_list ; cur != NULL ; cur = g_slist_next(cur)) {
3815 struct mailimap_set * imapset;
3817 imapset = cur->data;
3818 mailimap_set_free(imapset);
3820 g_slist_free(seq_list);
3823 static struct mailimap_flag_list * imap_flag_to_lep(IMAPFlags flags)
3825 struct mailimap_flag_list * flag_list;
3827 flag_list = mailimap_flag_list_new_empty();
3829 if (IMAP_IS_SEEN(flags))
3830 mailimap_flag_list_add(flag_list,
3831 mailimap_flag_new_seen());
3832 if (IMAP_IS_ANSWERED(flags))
3833 mailimap_flag_list_add(flag_list,
3834 mailimap_flag_new_answered());
3835 if (IMAP_IS_FLAGGED(flags))
3836 mailimap_flag_list_add(flag_list,
3837 mailimap_flag_new_flagged());
3838 if (IMAP_IS_DELETED(flags))
3839 mailimap_flag_list_add(flag_list,
3840 mailimap_flag_new_deleted());
3841 if (IMAP_IS_DRAFT(flags))
3842 mailimap_flag_list_add(flag_list,
3843 mailimap_flag_new_draft());
3848 guint imap_folder_get_refcnt(Folder *folder)
3850 return ((IMAPFolder *)folder)->refcnt;
3853 void imap_folder_ref(Folder *folder)
3855 ((IMAPFolder *)folder)->refcnt++;
3858 void imap_folder_unref(Folder *folder)
3860 if (((IMAPFolder *)folder)->refcnt > 0)
3861 ((IMAPFolder *)folder)->refcnt--;
3864 #else /* HAVE_LIBETPAN */
3866 static FolderClass imap_class;
3868 static Folder *imap_folder_new (const gchar *name,
3873 static gint imap_create_tree (Folder *folder)
3877 static FolderItem *imap_create_folder (Folder *folder,
3883 static gint imap_rename_folder (Folder *folder,
3890 FolderClass *imap_get_class(void)
3892 if (imap_class.idstr == NULL) {
3893 imap_class.type = F_IMAP;
3894 imap_class.idstr = "imap";
3895 imap_class.uistr = "IMAP4";
3897 imap_class.new_folder = imap_folder_new;
3898 imap_class.create_tree = imap_create_tree;
3899 imap_class.create_folder = imap_create_folder;
3900 imap_class.rename_folder = imap_rename_folder;
3901 /* nothing implemented */