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_reconnect_if_possible(Folder *folder, IMAPSession *session)
613 RemoteFolder *rfolder = REMOTE_FOLDER(folder);
614 /* Check if this is the first try to establish a
615 connection, if yes we don't try to reconnect */
616 debug_print("reconnecting\n");
617 if (rfolder->session == NULL) {
618 log_warning(_("Connecting to %s failed"),
619 folder->account->recv_server);
620 session_destroy(SESSION(session));
623 log_warning(_("IMAP4 connection to %s has been"
624 " disconnected. Reconnecting...\n"),
625 folder->account->recv_server);
626 statusbar_print_all(_("IMAP4 connection to %s has been"
627 " disconnected. Reconnecting...\n"),
628 folder->account->recv_server);
629 SESSION(session)->state = SESSION_DISCONNECTED;
630 session_destroy(SESSION(session));
631 /* Clear folders session to make imap_session_get create
632 a new session, because of rfolder->session == NULL
633 it will not try to reconnect again and so avoid an
635 rfolder->session = NULL;
636 session = imap_session_get(folder);
637 rfolder->session = SESSION(session);
643 static IMAPSession *imap_session_get(Folder *folder)
645 RemoteFolder *rfolder = REMOTE_FOLDER(folder);
646 IMAPSession *session = NULL;
648 g_return_val_if_fail(folder != NULL, NULL);
649 g_return_val_if_fail(FOLDER_CLASS(folder) == &imap_class, NULL);
650 g_return_val_if_fail(folder->account != NULL, NULL);
652 if (prefs_common.work_offline && !imap_gtk_should_override()) {
656 /* Make sure we have a session */
657 if (rfolder->session != NULL) {
658 session = IMAP_SESSION(rfolder->session);
660 imap_reset_uid_lists(folder);
661 session = imap_session_new(folder, folder->account);
666 /* Make sure session is authenticated */
667 if (!IMAP_SESSION(session)->authenticated)
668 imap_session_authenticate(IMAP_SESSION(session), folder->account);
670 if (!IMAP_SESSION(session)->authenticated) {
671 session_destroy(SESSION(session));
672 rfolder->session = NULL;
677 /* Make sure we have parsed the IMAP namespace */
678 imap_parse_namespace(IMAP_SESSION(session),
679 IMAP_FOLDER(folder));
682 /* I think the point of this code is to avoid sending a
683 * keepalive if we've used the session recently and therefore
684 * think it's still alive. Unfortunately, most of the code
685 * does not yet check for errors on the socket, and so if the
686 * connection drops we don't notice until the timeout expires.
687 * A better solution than sending a NOOP every time would be
688 * for every command to be prepared to retry until it is
689 * successfully sent. -- mbp */
690 if (time(NULL) - SESSION(session)->last_access_time > SESSION_TIMEOUT_INTERVAL) {
691 /* verify that the session is still alive */
692 if (imap_cmd_noop(session) != IMAP_SUCCESS) {
693 debug_print("disconnected!\n");
694 session = imap_reconnect_if_possible(folder, session);
698 rfolder->session = SESSION(session);
700 return IMAP_SESSION(session);
703 static IMAPSession *imap_session_new(Folder * folder,
704 const PrefsAccount *account)
706 IMAPSession *session;
712 /* FIXME: IMAP over SSL only... */
715 port = account->set_imapport ? account->imapport
716 : account->ssl_imap == SSL_TUNNEL ? IMAPS_PORT : IMAP4_PORT;
717 ssl_type = account->ssl_imap;
719 port = account->set_imapport ? account->imapport
724 statusbar_print_all(_("Connecting to IMAP4 server: %s..."), folder->account->recv_server);
725 if (account->set_tunnelcmd) {
726 r = imap_threaded_connect_cmd(folder,
728 account->recv_server,
733 if (ssl_type == SSL_TUNNEL) {
734 r = imap_threaded_connect_ssl(folder,
735 account->recv_server,
741 r = imap_threaded_connect(folder,
742 account->recv_server,
748 if (r == MAILIMAP_NO_ERROR_AUTHENTICATED) {
749 authenticated = TRUE;
751 else if (r == MAILIMAP_NO_ERROR_NON_AUTHENTICATED) {
752 authenticated = FALSE;
755 if(!prefs_common.no_recv_err_panel) {
756 alertpanel_error(_("Can't connect to IMAP4 server: %s:%d"),
757 account->recv_server, port);
759 log_error(_("Can't connect to IMAP4 server: %s:%d"),
760 account->recv_server, port);
766 session = g_new0(IMAPSession, 1);
767 session_init(SESSION(session));
768 SESSION(session)->type = SESSION_IMAP;
769 SESSION(session)->server = g_strdup(account->recv_server);
770 SESSION(session)->sock = NULL;
772 SESSION(session)->destroy = imap_session_destroy;
774 session->capability = NULL;
776 session->authenticated = authenticated;
777 session->mbox = NULL;
778 session->cmd_count = 0;
779 session->folder = folder;
780 IMAP_FOLDER(session->folder)->last_seen_separator = 0;
783 if (account->ssl_imap == SSL_STARTTLS) {
786 ok = imap_cmd_starttls(session);
787 if (ok != IMAP_SUCCESS) {
788 log_warning(_("Can't start TLS session.\n"));
789 session_destroy(SESSION(session));
793 imap_free_capabilities(session);
794 session->authenticated = FALSE;
795 session->uidplus = FALSE;
796 session->cmd_count = 1;
799 log_message("IMAP connection is %s-authenticated\n",
800 (session->authenticated) ? "pre" : "un");
805 static void imap_session_authenticate(IMAPSession *session,
806 const PrefsAccount *account)
810 g_return_if_fail(account->userid != NULL);
812 pass = account->passwd;
815 tmp_pass = input_dialog_query_password(account->recv_server, account->userid);
817 tmp_pass = g_strdup(""); /* allow empty password */
818 Xstrdup_a(pass, tmp_pass, {g_free(tmp_pass); return;});
821 statusbar_print_all(_("Connecting to IMAP4 server %s...\n"),
822 account->recv_server);
823 if (imap_auth(session, account->userid, pass, account->imap_auth_type) != IMAP_SUCCESS) {
824 imap_threaded_disconnect(session->folder);
825 imap_cmd_logout(session);
831 session->authenticated = TRUE;
834 static void imap_session_destroy(Session *session)
836 if (session->state != SESSION_DISCONNECTED)
837 imap_threaded_disconnect(IMAP_SESSION(session)->folder);
839 imap_free_capabilities(IMAP_SESSION(session));
840 g_free(IMAP_SESSION(session)->mbox);
841 sock_close(session->sock);
842 session->sock = NULL;
845 static gchar *imap_fetch_msg(Folder *folder, FolderItem *item, gint uid)
847 return imap_fetch_msg_full(folder, item, uid, TRUE, TRUE);
850 static guint get_size_with_crs(MsgInfo *info)
859 fp = procmsg_open_message(info);
863 while (fgets(buf, sizeof (buf), fp) != NULL) {
865 if (!strstr(buf, "\r") && strstr(buf, "\n"))
873 static gchar *imap_fetch_msg_full(Folder *folder, FolderItem *item, gint uid,
874 gboolean headers, gboolean body)
876 gchar *path, *filename;
877 IMAPSession *session;
880 g_return_val_if_fail(folder != NULL, NULL);
881 g_return_val_if_fail(item != NULL, NULL);
886 path = folder_item_get_path(item);
887 if (!is_dir_exist(path))
889 filename = g_strconcat(path, G_DIR_SEPARATOR_S, itos(uid), NULL);
892 if (is_file_exist(filename)) {
893 /* see whether the local file represents the whole message
894 * or not. As the IMAP server reports size with \r chars,
895 * we have to update the local file (UNIX \n only) size */
896 MsgInfo *msginfo = imap_parse_msg(filename, item);
897 MsgInfo *cached = msgcache_get_msg(item->cache,uid);
898 guint have_size = get_size_with_crs(msginfo);
901 debug_print("message %d has been already %scached (%d/%d).\n", uid,
902 have_size == cached->size ? "fully ":"",
903 have_size, (int)cached->size);
905 if (cached && (cached->size == have_size || !body)) {
906 procmsg_msginfo_free(cached);
907 procmsg_msginfo_free(msginfo);
908 file_strip_crs(filename);
911 procmsg_msginfo_free(cached);
912 procmsg_msginfo_free(msginfo);
916 session = imap_session_get(folder);
922 debug_print("IMAP fetching messages\n");
923 ok = imap_select(session, IMAP_FOLDER(folder), item->path,
924 NULL, NULL, NULL, NULL, FALSE);
925 if (ok != IMAP_SUCCESS) {
926 g_warning("can't select mailbox %s\n", item->path);
931 debug_print("getting message %d...\n", uid);
932 ok = imap_cmd_fetch(session, (guint32)uid, filename, headers, body);
934 if (ok != IMAP_SUCCESS) {
935 g_warning("can't fetch message %d\n", uid);
940 file_strip_crs(filename);
944 static gint imap_add_msg(Folder *folder, FolderItem *dest,
945 const gchar *file, MsgFlags *flags)
949 MsgFileInfo fileinfo;
951 g_return_val_if_fail(file != NULL, -1);
953 fileinfo.msginfo = NULL;
954 fileinfo.file = (gchar *)file;
955 fileinfo.flags = flags;
956 file_list.data = &fileinfo;
957 file_list.next = NULL;
959 ret = imap_add_msgs(folder, dest, &file_list, NULL);
963 static gint imap_add_msgs(Folder *folder, FolderItem *dest, GSList *file_list,
967 IMAPSession *session;
968 guint32 last_uid = 0;
970 MsgFileInfo *fileinfo;
974 g_return_val_if_fail(folder != NULL, -1);
975 g_return_val_if_fail(dest != NULL, -1);
976 g_return_val_if_fail(file_list != NULL, -1);
978 session = imap_session_get(folder);
982 destdir = imap_get_real_path(IMAP_FOLDER(folder), dest->path);
984 for (cur = file_list; cur != NULL; cur = cur->next) {
985 IMAPFlags iflags = 0;
988 fileinfo = (MsgFileInfo *)cur->data;
990 if (fileinfo->flags) {
991 if (MSG_IS_MARKED(*fileinfo->flags))
992 iflags |= IMAP_FLAG_FLAGGED;
993 if (MSG_IS_REPLIED(*fileinfo->flags))
994 iflags |= IMAP_FLAG_ANSWERED;
995 if (!MSG_IS_UNREAD(*fileinfo->flags))
996 iflags |= IMAP_FLAG_SEEN;
999 if (folder_has_parent_of_type(dest, F_QUEUE) ||
1000 folder_has_parent_of_type(dest, F_OUTBOX) ||
1001 folder_has_parent_of_type(dest, F_DRAFT) ||
1002 folder_has_parent_of_type(dest, F_TRASH))
1003 iflags |= IMAP_FLAG_SEEN;
1005 ok = imap_cmd_append(session, destdir, fileinfo->file, iflags,
1008 if (ok != IMAP_SUCCESS) {
1009 g_warning("can't append message %s\n", fileinfo->file);
1014 if (relation != NULL)
1015 g_relation_insert(relation, fileinfo->msginfo != NULL ?
1016 (gpointer) fileinfo->msginfo : (gpointer) fileinfo,
1017 GINT_TO_POINTER(dest->last_num + 1));
1018 if (last_uid < new_uid)
1027 static gint imap_do_copy_msgs(Folder *folder, FolderItem *dest,
1028 MsgInfoList *msglist, GRelation *relation)
1032 GSList *seq_list, *cur;
1034 IMAPSession *session;
1035 gint ok = IMAP_SUCCESS;
1036 GRelation *uid_mapping;
1039 g_return_val_if_fail(folder != NULL, -1);
1040 g_return_val_if_fail(dest != NULL, -1);
1041 g_return_val_if_fail(msglist != NULL, -1);
1043 session = imap_session_get(folder);
1048 msginfo = (MsgInfo *)msglist->data;
1050 src = msginfo->folder;
1052 g_warning("the src folder is identical to the dest.\n");
1056 ok = imap_select(session, IMAP_FOLDER(folder), msginfo->folder->path,
1057 NULL, NULL, NULL, NULL, FALSE);
1058 if (ok != IMAP_SUCCESS) {
1062 destdir = imap_get_real_path(IMAP_FOLDER(folder), dest->path);
1063 seq_list = imap_get_lep_set_from_msglist(msglist);
1064 uid_mapping = g_relation_new(2);
1065 g_relation_index(uid_mapping, 0, g_direct_hash, g_direct_equal);
1067 for (cur = seq_list; cur != NULL; cur = g_slist_next(cur)) {
1068 struct mailimap_set * seq_set;
1070 seq_set = cur->data;
1072 debug_print("Copying messages from %s to %s ...\n",
1073 src->path, destdir);
1075 ok = imap_cmd_copy(session, seq_set, destdir, uid_mapping);
1076 if (ok != IMAP_SUCCESS) {
1077 g_relation_destroy(uid_mapping);
1078 imap_lep_set_free(seq_list);
1083 for (cur = msglist; cur != NULL; cur = g_slist_next(cur)) {
1084 MsgInfo *msginfo = (MsgInfo *)cur->data;
1087 tuples = g_relation_select(uid_mapping,
1088 GINT_TO_POINTER(msginfo->msgnum),
1090 if (tuples->len > 0) {
1091 gint num = GPOINTER_TO_INT(g_tuples_index(tuples, 0, 1));
1092 g_relation_insert(relation, msginfo,
1093 GPOINTER_TO_INT(num));
1097 g_relation_insert(relation, msginfo,
1098 GPOINTER_TO_INT(0));
1099 g_tuples_destroy(tuples);
1102 g_relation_destroy(uid_mapping);
1103 imap_lep_set_free(seq_list);
1107 IMAP_FOLDER_ITEM(dest)->lastuid = 0;
1108 IMAP_FOLDER_ITEM(dest)->uid_next = 0;
1109 g_slist_free(IMAP_FOLDER_ITEM(dest)->uid_list);
1110 IMAP_FOLDER_ITEM(dest)->uid_list = NULL;
1112 if (ok == IMAP_SUCCESS)
1118 static gint imap_copy_msg(Folder *folder, FolderItem *dest, MsgInfo *msginfo)
1122 g_return_val_if_fail(msginfo != NULL, -1);
1124 msglist.data = msginfo;
1125 msglist.next = NULL;
1127 return imap_copy_msgs(folder, dest, &msglist, NULL);
1130 static gint imap_copy_msgs(Folder *folder, FolderItem *dest,
1131 MsgInfoList *msglist, GRelation *relation)
1137 g_return_val_if_fail(folder != NULL, -1);
1138 g_return_val_if_fail(dest != NULL, -1);
1139 g_return_val_if_fail(msglist != NULL, -1);
1141 msginfo = (MsgInfo *)msglist->data;
1142 g_return_val_if_fail(msginfo->folder != NULL, -1);
1144 if (folder == msginfo->folder->folder) {
1145 ret = imap_do_copy_msgs(folder, dest, msglist, relation);
1149 file_list = procmsg_get_message_file_list(msglist);
1150 g_return_val_if_fail(file_list != NULL, -1);
1152 ret = imap_add_msgs(folder, dest, file_list, relation);
1154 procmsg_message_file_list_free(file_list);
1160 static gint imap_do_remove_msgs(Folder *folder, FolderItem *dest,
1161 MsgInfoList *msglist, GRelation *relation)
1164 GSList *seq_list = NULL, *cur;
1166 IMAPSession *session;
1167 gint ok = IMAP_SUCCESS;
1168 GRelation *uid_mapping;
1170 g_return_val_if_fail(folder != NULL, -1);
1171 g_return_val_if_fail(dest != NULL, -1);
1172 g_return_val_if_fail(msglist != NULL, -1);
1174 session = imap_session_get(folder);
1178 msginfo = (MsgInfo *)msglist->data;
1180 ok = imap_select(session, IMAP_FOLDER(folder), msginfo->folder->path,
1181 NULL, NULL, NULL, NULL, FALSE);
1182 if (ok != IMAP_SUCCESS) {
1186 destdir = imap_get_real_path(IMAP_FOLDER(folder), dest->path);
1187 for (cur = msglist; cur; cur = cur->next) {
1188 msginfo = (MsgInfo *)cur->data;
1189 seq_list = g_slist_append(seq_list, GINT_TO_POINTER(msginfo->msgnum));
1192 uid_mapping = g_relation_new(2);
1193 g_relation_index(uid_mapping, 0, g_direct_hash, g_direct_equal);
1195 ok = imap_set_message_flags
1196 (IMAP_SESSION(REMOTE_FOLDER(folder)->session),
1197 seq_list, IMAP_FLAG_DELETED, TRUE);
1198 if (ok != IMAP_SUCCESS) {
1199 log_warning(_("can't set deleted flags\n"));
1202 ok = imap_cmd_expunge(session);
1203 if (ok != IMAP_SUCCESS) {
1204 log_warning(_("can't expunge\n"));
1208 g_relation_destroy(uid_mapping);
1209 g_slist_free(seq_list);
1213 if (ok == IMAP_SUCCESS)
1219 static gint imap_remove_msgs(Folder *folder, FolderItem *dest,
1220 MsgInfoList *msglist, GRelation *relation)
1224 g_return_val_if_fail(folder != NULL, -1);
1225 g_return_val_if_fail(dest != NULL, -1);
1226 if (msglist == NULL)
1229 msginfo = (MsgInfo *)msglist->data;
1230 g_return_val_if_fail(msginfo->folder != NULL, -1);
1232 return imap_do_remove_msgs(folder, dest, msglist, relation);
1235 static gint imap_remove_all_msg(Folder *folder, FolderItem *item)
1237 GSList *list = folder_item_get_msg_list(item);
1238 gint res = imap_remove_msgs(folder, item, list, NULL);
1239 procmsg_msg_list_free(list);
1243 static gboolean imap_is_msg_changed(Folder *folder, FolderItem *item,
1246 /* TODO: properly implement this method */
1250 static gint imap_close(Folder *folder, FolderItem *item)
1255 static gint imap_scan_tree(Folder *folder)
1257 FolderItem *item = NULL;
1258 IMAPSession *session;
1259 gchar *root_folder = NULL;
1261 g_return_val_if_fail(folder != NULL, -1);
1262 g_return_val_if_fail(folder->account != NULL, -1);
1264 session = imap_session_get(folder);
1266 if (!folder->node) {
1267 folder_tree_destroy(folder);
1268 item = folder_item_new(folder, folder->name, NULL);
1269 item->folder = folder;
1270 folder->node = item->node = g_node_new(item);
1275 if (folder->account->imap_dir && *folder->account->imap_dir) {
1280 Xstrdup_a(root_folder, folder->account->imap_dir, {return -1;});
1281 extract_quote(root_folder, '"');
1282 subst_char(root_folder,
1283 imap_get_path_separator(IMAP_FOLDER(folder),
1286 strtailchomp(root_folder, '/');
1287 real_path = imap_get_real_path
1288 (IMAP_FOLDER(folder), root_folder);
1289 debug_print("IMAP root directory: %s\n", real_path);
1291 /* check if root directory exist */
1293 r = imap_threaded_list(session->folder, "", real_path,
1295 if ((r != MAILIMAP_NO_ERROR) || (clist_count(lep_list) == 0)) {
1296 if (!folder->node) {
1297 item = folder_item_new(folder, folder->name, NULL);
1298 item->folder = folder;
1299 folder->node = item->node = g_node_new(item);
1304 mailimap_list_result_free(lep_list);
1310 item = FOLDER_ITEM(folder->node->data);
1311 if (!item || ((item->path || root_folder) &&
1312 strcmp2(item->path, root_folder) != 0)) {
1313 folder_tree_destroy(folder);
1314 item = folder_item_new(folder, folder->name, root_folder);
1315 item->folder = folder;
1316 folder->node = item->node = g_node_new(item);
1319 imap_scan_tree_recursive(session, FOLDER_ITEM(folder->node->data));
1320 imap_create_missing_folders(folder);
1325 static gint imap_scan_tree_recursive(IMAPSession *session, FolderItem *item)
1328 IMAPFolder *imapfolder;
1329 FolderItem *new_item;
1330 GSList *item_list, *cur;
1333 gchar *wildcard_path;
1339 g_return_val_if_fail(item != NULL, -1);
1340 g_return_val_if_fail(item->folder != NULL, -1);
1341 g_return_val_if_fail(item->no_sub == FALSE, -1);
1343 folder = item->folder;
1344 imapfolder = IMAP_FOLDER(folder);
1346 separator = imap_get_path_separator(imapfolder, item->path);
1348 if (folder->ui_func)
1349 folder->ui_func(folder, item, folder->ui_func_data);
1352 wildcard[0] = separator;
1355 real_path = imap_get_real_path(imapfolder, item->path);
1359 real_path = g_strdup("");
1362 Xstrcat_a(wildcard_path, real_path, wildcard,
1363 {g_free(real_path); return IMAP_ERROR;});
1365 r = imap_threaded_list(folder, "", wildcard_path, &lep_list);
1366 if (r != MAILIMAP_NO_ERROR) {
1370 item_list = imap_list_from_lep(imapfolder,
1371 lep_list, real_path);
1372 mailimap_list_result_free(lep_list);
1377 node = item->node->children;
1378 while (node != NULL) {
1379 FolderItem *old_item = FOLDER_ITEM(node->data);
1380 GNode *next = node->next;
1383 for (cur = item_list; cur != NULL; cur = cur->next) {
1384 FolderItem *cur_item = FOLDER_ITEM(cur->data);
1385 if (!strcmp2(old_item->path, cur_item->path)) {
1386 new_item = cur_item;
1391 debug_print("folder '%s' not found. removing...\n",
1393 folder_item_remove(old_item);
1395 old_item->no_sub = new_item->no_sub;
1396 old_item->no_select = new_item->no_select;
1397 if (old_item->no_sub == TRUE && node->children) {
1398 debug_print("folder '%s' doesn't have "
1399 "subfolders. removing...\n",
1401 folder_item_remove_children(old_item);
1408 for (cur = item_list; cur != NULL; cur = cur->next) {
1409 FolderItem *cur_item = FOLDER_ITEM(cur->data);
1411 for (node = item->node->children; node != NULL;
1412 node = node->next) {
1413 if (!strcmp2(FOLDER_ITEM(node->data)->path,
1415 new_item = FOLDER_ITEM(node->data);
1416 folder_item_destroy(cur_item);
1422 new_item = cur_item;
1423 debug_print("new folder '%s' found.\n", new_item->path);
1424 folder_item_append(item, new_item);
1427 if (!strcmp(new_item->path, "INBOX")) {
1428 new_item->stype = F_INBOX;
1429 folder->inbox = new_item;
1430 } else if (!folder_item_parent(item) || item->stype == F_INBOX) {
1433 base = g_path_get_basename(new_item->path);
1435 if (!folder->outbox && !g_ascii_strcasecmp(base, "Sent")) {
1436 new_item->stype = F_OUTBOX;
1437 folder->outbox = new_item;
1438 } else if (!folder->draft && !g_ascii_strcasecmp(base, "Drafts")) {
1439 new_item->stype = F_DRAFT;
1440 folder->draft = new_item;
1441 } else if (!folder->queue && !g_ascii_strcasecmp(base, "Queue")) {
1442 new_item->stype = F_QUEUE;
1443 folder->queue = new_item;
1444 } else if (!folder->trash && !g_ascii_strcasecmp(base, "Trash")) {
1445 new_item->stype = F_TRASH;
1446 folder->trash = new_item;
1451 if (new_item->no_sub == FALSE)
1452 imap_scan_tree_recursive(session, new_item);
1455 g_slist_free(item_list);
1457 return IMAP_SUCCESS;
1460 static gint imap_create_tree(Folder *folder)
1462 g_return_val_if_fail(folder != NULL, -1);
1463 g_return_val_if_fail(folder->node != NULL, -1);
1464 g_return_val_if_fail(folder->node->data != NULL, -1);
1465 g_return_val_if_fail(folder->account != NULL, -1);
1467 imap_scan_tree(folder);
1468 imap_create_missing_folders(folder);
1473 static void imap_create_missing_folders(Folder *folder)
1475 g_return_if_fail(folder != NULL);
1478 folder->inbox = imap_create_special_folder
1479 (folder, F_INBOX, "INBOX");
1481 folder->trash = imap_create_special_folder
1482 (folder, F_TRASH, "Trash");
1484 folder->queue = imap_create_special_folder
1485 (folder, F_QUEUE, "Queue");
1486 if (!folder->outbox)
1487 folder->outbox = imap_create_special_folder
1488 (folder, F_OUTBOX, "Sent");
1490 folder->draft = imap_create_special_folder
1491 (folder, F_DRAFT, "Drafts");
1494 static FolderItem *imap_create_special_folder(Folder *folder,
1495 SpecialFolderItemType stype,
1499 FolderItem *new_item;
1501 g_return_val_if_fail(folder != NULL, NULL);
1502 g_return_val_if_fail(folder->node != NULL, NULL);
1503 g_return_val_if_fail(folder->node->data != NULL, NULL);
1504 g_return_val_if_fail(folder->account != NULL, NULL);
1505 g_return_val_if_fail(name != NULL, NULL);
1507 item = FOLDER_ITEM(folder->node->data);
1508 new_item = imap_create_folder(folder, item, name);
1511 g_warning("Can't create '%s'\n", name);
1512 if (!folder->inbox) return NULL;
1514 new_item = imap_create_folder(folder, folder->inbox, name);
1516 g_warning("Can't create '%s' under INBOX\n", name);
1518 new_item->stype = stype;
1520 new_item->stype = stype;
1525 static gchar *imap_folder_get_path(Folder *folder)
1529 g_return_val_if_fail(folder != NULL, NULL);
1530 g_return_val_if_fail(folder->account != NULL, NULL);
1532 folder_path = g_strconcat(get_imap_cache_dir(),
1534 folder->account->recv_server,
1536 folder->account->userid,
1542 static gchar *imap_item_get_path(Folder *folder, FolderItem *item)
1544 gchar *folder_path, *path;
1546 g_return_val_if_fail(folder != NULL, NULL);
1547 g_return_val_if_fail(item != NULL, NULL);
1548 folder_path = imap_folder_get_path(folder);
1550 g_return_val_if_fail(folder_path != NULL, NULL);
1551 if (folder_path[0] == G_DIR_SEPARATOR) {
1553 path = g_strconcat(folder_path, G_DIR_SEPARATOR_S,
1556 path = g_strdup(folder_path);
1559 path = g_strconcat(get_home_dir(), G_DIR_SEPARATOR_S,
1560 folder_path, G_DIR_SEPARATOR_S,
1563 path = g_strconcat(get_home_dir(), G_DIR_SEPARATOR_S,
1566 g_free(folder_path);
1571 static FolderItem *imap_create_folder(Folder *folder, FolderItem *parent,
1574 gchar *dirpath, *imap_path;
1575 IMAPSession *session;
1576 FolderItem *new_item;
1582 g_return_val_if_fail(folder != NULL, NULL);
1583 g_return_val_if_fail(folder->account != NULL, NULL);
1584 g_return_val_if_fail(parent != NULL, NULL);
1585 g_return_val_if_fail(name != NULL, NULL);
1587 session = imap_session_get(folder);
1592 if (!folder_item_parent(parent) && strcmp(name, "INBOX") == 0)
1593 dirpath = g_strdup(name);
1594 else if (parent->path)
1595 dirpath = g_strconcat(parent->path, "/", name, NULL);
1596 else if ((p = strchr(name, '/')) != NULL && *(p + 1) != '\0')
1597 dirpath = g_strdup(name);
1598 else if (folder->account->imap_dir && *folder->account->imap_dir) {
1601 Xstrdup_a(imap_dir, folder->account->imap_dir, {return NULL;});
1602 strtailchomp(imap_dir, '/');
1603 dirpath = g_strconcat(imap_dir, "/", name, NULL);
1605 dirpath = g_strdup(name);
1607 /* keep trailing directory separator to create a folder that contains
1609 imap_path = imap_utf8_to_modified_utf7(dirpath);
1610 strtailchomp(dirpath, '/');
1611 Xstrdup_a(new_name, name, {
1615 strtailchomp(new_name, '/');
1616 separator = imap_get_path_separator(IMAP_FOLDER(folder), imap_path);
1617 imap_path_separator_subst(imap_path, separator);
1618 subst_char(new_name, '/', separator);
1620 if (strcmp(name, "INBOX") != 0) {
1622 gboolean exist = FALSE;
1626 argbuf = g_ptr_array_new();
1627 r = imap_threaded_list(folder, "", imap_path, &lep_list);
1628 if (r != MAILIMAP_NO_ERROR) {
1629 log_warning(_("can't create mailbox: LIST failed\n"));
1632 ptr_array_free_strings(argbuf);
1633 g_ptr_array_free(argbuf, TRUE);
1637 if (clist_count(lep_list) > 0)
1641 ok = imap_cmd_create(session, imap_path);
1642 if (ok != IMAP_SUCCESS) {
1643 log_warning(_("can't create mailbox\n"));
1651 new_item = folder_item_new(folder, new_name, dirpath);
1652 folder_item_append(parent, new_item);
1656 dirpath = folder_item_get_path(new_item);
1657 if (!is_dir_exist(dirpath))
1658 make_dir_hier(dirpath);
1664 static gint imap_rename_folder(Folder *folder, FolderItem *item,
1669 gchar *real_oldpath;
1670 gchar *real_newpath;
1672 gchar *old_cache_dir;
1673 gchar *new_cache_dir;
1674 IMAPSession *session;
1677 gint exists, recent, unseen;
1678 guint32 uid_validity;
1680 g_return_val_if_fail(folder != NULL, -1);
1681 g_return_val_if_fail(item != NULL, -1);
1682 g_return_val_if_fail(item->path != NULL, -1);
1683 g_return_val_if_fail(name != NULL, -1);
1685 if (strchr(name, imap_get_path_separator(IMAP_FOLDER(folder), item->path)) != NULL) {
1686 g_warning(_("New folder name must not contain the namespace "
1691 session = imap_session_get(folder);
1695 real_oldpath = imap_get_real_path(IMAP_FOLDER(folder), item->path);
1697 g_free(session->mbox);
1698 session->mbox = NULL;
1699 ok = imap_cmd_examine(session, "INBOX",
1700 &exists, &recent, &unseen, &uid_validity, FALSE);
1701 if (ok != IMAP_SUCCESS) {
1702 g_free(real_oldpath);
1706 separator = imap_get_path_separator(IMAP_FOLDER(folder), item->path);
1707 if (strchr(item->path, G_DIR_SEPARATOR)) {
1708 dirpath = g_path_get_dirname(item->path);
1709 newpath = g_strconcat(dirpath, G_DIR_SEPARATOR_S, name, NULL);
1712 newpath = g_strdup(name);
1714 real_newpath = imap_utf8_to_modified_utf7(newpath);
1715 imap_path_separator_subst(real_newpath, separator);
1717 ok = imap_cmd_rename(session, real_oldpath, real_newpath);
1718 if (ok != IMAP_SUCCESS) {
1719 log_warning(_("can't rename mailbox: %s to %s\n"),
1720 real_oldpath, real_newpath);
1721 g_free(real_oldpath);
1723 g_free(real_newpath);
1728 item->name = g_strdup(name);
1730 old_cache_dir = folder_item_get_path(item);
1732 paths[0] = g_strdup(item->path);
1734 g_node_traverse(item->node, G_PRE_ORDER, G_TRAVERSE_ALL, -1,
1735 imap_rename_folder_func, paths);
1737 if (is_dir_exist(old_cache_dir)) {
1738 new_cache_dir = folder_item_get_path(item);
1739 if (rename(old_cache_dir, new_cache_dir) < 0) {
1740 FILE_OP_ERROR(old_cache_dir, "rename");
1742 g_free(new_cache_dir);
1745 g_free(old_cache_dir);
1748 g_free(real_oldpath);
1749 g_free(real_newpath);
1754 static gint imap_remove_folder_real(Folder *folder, FolderItem *item)
1757 IMAPSession *session;
1760 gint exists, recent, unseen;
1761 guint32 uid_validity;
1763 g_return_val_if_fail(folder != NULL, -1);
1764 g_return_val_if_fail(item != NULL, -1);
1765 g_return_val_if_fail(item->path != NULL, -1);
1767 session = imap_session_get(folder);
1771 path = imap_get_real_path(IMAP_FOLDER(folder), item->path);
1773 ok = imap_cmd_examine(session, "INBOX",
1774 &exists, &recent, &unseen, &uid_validity, FALSE);
1775 if (ok != IMAP_SUCCESS) {
1780 ok = imap_cmd_delete(session, path);
1781 if (ok != IMAP_SUCCESS) {
1782 log_warning(_("can't delete mailbox\n"));
1788 cache_dir = folder_item_get_path(item);
1789 if (is_dir_exist(cache_dir) && remove_dir_recursive(cache_dir) < 0)
1790 g_warning("can't remove directory '%s'\n", cache_dir);
1792 folder_item_remove(item);
1797 static gint imap_remove_folder(Folder *folder, FolderItem *item)
1801 g_return_val_if_fail(item != NULL, -1);
1802 g_return_val_if_fail(item->folder != NULL, -1);
1803 g_return_val_if_fail(item->node != NULL, -1);
1805 node = item->node->children;
1806 while (node != NULL) {
1808 if (imap_remove_folder(folder, FOLDER_ITEM(node->data)) < 0)
1812 debug_print("IMAP removing %s\n", item->path);
1814 if (imap_remove_all_msg(folder, item) < 0)
1816 return imap_remove_folder_real(folder, item);
1819 typedef struct _uncached_data {
1820 IMAPSession *session;
1822 MsgNumberList *numlist;
1828 static void *imap_get_uncached_messages_thread(void *data)
1830 uncached_data *stuff = (uncached_data *)data;
1831 IMAPSession *session = stuff->session;
1832 FolderItem *item = stuff->item;
1833 MsgNumberList *numlist = stuff->numlist;
1835 GSList *newlist = NULL;
1836 GSList *llast = NULL;
1837 GSList *seq_list, *cur;
1839 debug_print("uncached_messages\n");
1841 if (session == NULL || item == NULL || item->folder == NULL
1842 || FOLDER_CLASS(item->folder) != &imap_class) {
1847 seq_list = imap_get_lep_set_from_numlist(numlist);
1848 debug_print("get msgs info\n");
1849 for (cur = seq_list; cur != NULL; cur = g_slist_next(cur)) {
1850 struct mailimap_set * imapset;
1856 imapset = cur->data;
1858 r = imap_threaded_fetch_env(session->folder,
1859 imapset, &env_list);
1860 if (r != MAILIMAP_NO_ERROR)
1864 for(i = 0 ; i < carray_count(env_list) ; i ++) {
1865 struct imap_fetch_env_info * info;
1868 info = carray_get(env_list, i);
1869 msginfo = imap_envelope_from_lep(info, item);
1870 msginfo->folder = item;
1872 llast = newlist = g_slist_append(newlist, msginfo);
1874 llast = g_slist_append(llast, msginfo);
1875 llast = llast->next;
1880 imap_fetch_env_free(env_list);
1883 session_set_access_time(SESSION(session));
1888 #define MAX_MSG_NUM 50
1890 static GSList *imap_get_uncached_messages(IMAPSession *session,
1892 MsgNumberList *numlist)
1894 GSList *result = NULL;
1896 uncached_data *data = g_new0(uncached_data, 1);
1901 data->total = g_slist_length(numlist);
1902 debug_print("messages list : %i\n", data->total);
1904 while (cur != NULL) {
1905 GSList * partial_result;
1913 while (count < MAX_MSG_NUM) {
1918 if (newlist == NULL)
1919 llast = newlist = g_slist_append(newlist, p);
1921 llast = g_slist_append(llast, p);
1922 llast = llast->next;
1932 data->session = session;
1934 data->numlist = newlist;
1937 if (prefs_common.work_offline && !imap_gtk_should_override()) {
1943 (GSList *)imap_get_uncached_messages_thread(data);
1945 statusbar_progress_all(data->cur,data->total, 1);
1947 g_slist_free(newlist);
1949 result = g_slist_concat(result, partial_result);
1953 statusbar_progress_all(0,0,0);
1954 statusbar_pop_all();
1959 static void imap_delete_all_cached_messages(FolderItem *item)
1963 g_return_if_fail(item != NULL);
1964 g_return_if_fail(item->folder != NULL);
1965 g_return_if_fail(FOLDER_CLASS(item->folder) == &imap_class);
1967 debug_print("Deleting all cached messages...\n");
1969 dir = folder_item_get_path(item);
1970 if (is_dir_exist(dir))
1971 remove_all_numbered_files(dir);
1974 debug_print("done.\n");
1977 static IMAPNameSpace *imap_find_namespace_from_list(GList *ns_list,
1980 IMAPNameSpace *namespace = NULL;
1981 gchar *tmp_path, *name;
1983 if (!path) path = "";
1985 for (; ns_list != NULL; ns_list = ns_list->next) {
1986 IMAPNameSpace *tmp_ns = ns_list->data;
1988 Xstrcat_a(tmp_path, path, "/", return namespace);
1989 Xstrdup_a(name, tmp_ns->name, return namespace);
1990 if (tmp_ns->separator && tmp_ns->separator != '/') {
1991 subst_char(tmp_path, tmp_ns->separator, '/');
1992 subst_char(name, tmp_ns->separator, '/');
1994 if (strncmp(tmp_path, name, strlen(name)) == 0)
2001 static IMAPNameSpace *imap_find_namespace(IMAPFolder *folder,
2004 IMAPNameSpace *namespace;
2006 g_return_val_if_fail(folder != NULL, NULL);
2008 namespace = imap_find_namespace_from_list(folder->ns_personal, path);
2009 if (namespace) return namespace;
2010 namespace = imap_find_namespace_from_list(folder->ns_others, path);
2011 if (namespace) return namespace;
2012 namespace = imap_find_namespace_from_list(folder->ns_shared, path);
2013 if (namespace) return namespace;
2019 static gchar imap_get_path_separator(IMAPFolder *folder, const gchar *path)
2021 IMAPNameSpace *namespace;
2022 gchar separator = '/';
2024 if (folder->last_seen_separator == 0) {
2026 int r = imap_threaded_list((Folder *)folder, "", "", &lep_list);
2027 if (r != MAILIMAP_NO_ERROR) {
2028 log_warning(_("LIST failed\n"));
2032 if (clist_count(lep_list) > 0) {
2033 clistiter * iter = clist_begin(lep_list);
2034 struct mailimap_mailbox_list * mb;
2035 mb = clist_content(iter);
2037 folder->last_seen_separator = mb->mb_delimiter;
2038 debug_print("got separator: %c\n", folder->last_seen_separator);
2040 mailimap_list_result_free(lep_list);
2043 if (folder->last_seen_separator != 0) {
2044 debug_print("using separator: %c\n", folder->last_seen_separator);
2045 return folder->last_seen_separator;
2048 namespace = imap_find_namespace(folder, path);
2049 if (namespace && namespace->separator)
2050 separator = namespace->separator;
2055 static gchar *imap_get_real_path(IMAPFolder *folder, const gchar *path)
2060 g_return_val_if_fail(folder != NULL, NULL);
2061 g_return_val_if_fail(path != NULL, NULL);
2063 real_path = imap_utf8_to_modified_utf7(path);
2064 separator = imap_get_path_separator(folder, path);
2065 imap_path_separator_subst(real_path, separator);
2070 static gint imap_set_message_flags(IMAPSession *session,
2071 MsgNumberList *numlist,
2079 seq_list = imap_get_lep_set_from_numlist(numlist);
2081 for(cur = seq_list ; cur != NULL ; cur = g_slist_next(cur)) {
2082 struct mailimap_set * imapset;
2084 imapset = cur->data;
2086 ok = imap_cmd_store(session, imapset,
2090 imap_lep_set_free(seq_list);
2092 return IMAP_SUCCESS;
2095 typedef struct _select_data {
2096 IMAPSession *session;
2101 guint32 *uid_validity;
2105 static gint imap_select(IMAPSession *session, IMAPFolder *folder,
2107 gint *exists, gint *recent, gint *unseen,
2108 guint32 *uid_validity, gboolean block)
2112 gint exists_, recent_, unseen_;
2113 guint32 uid_validity_;
2115 if (!exists || !recent || !unseen || !uid_validity) {
2116 if (session->mbox && strcmp(session->mbox, path) == 0)
2117 return IMAP_SUCCESS;
2121 uid_validity = &uid_validity_;
2124 g_free(session->mbox);
2125 session->mbox = NULL;
2127 real_path = imap_get_real_path(folder, path);
2129 ok = imap_cmd_select(session, real_path,
2130 exists, recent, unseen, uid_validity, block);
2131 if (ok != IMAP_SUCCESS)
2132 log_warning(_("can't select folder: %s\n"), real_path);
2134 session->mbox = g_strdup(path);
2135 session->folder_content_changed = FALSE;
2142 static gint imap_status(IMAPSession *session, IMAPFolder *folder,
2144 gint *messages, gint *recent,
2145 guint32 *uid_next, guint32 *uid_validity,
2146 gint *unseen, gboolean block)
2150 struct mailimap_mailbox_data_status * data_status;
2154 real_path = imap_get_real_path(folder, path);
2156 r = imap_threaded_status(FOLDER(folder), real_path, &data_status);
2158 if (r != MAILIMAP_NO_ERROR) {
2159 debug_print("status err %d\n", r);
2163 if (data_status->st_info_list == NULL) {
2164 mailimap_mailbox_data_status_free(data_status);
2165 debug_print("status->st_info_list == NULL\n");
2170 for(iter = clist_begin(data_status->st_info_list) ; iter != NULL ;
2171 iter = clist_next(iter)) {
2172 struct mailimap_status_info * info;
2174 info = clist_content(iter);
2175 switch (info->st_att) {
2176 case MAILIMAP_STATUS_ATT_MESSAGES:
2177 * messages = info->st_value;
2178 got_values |= 1 << 0;
2181 case MAILIMAP_STATUS_ATT_RECENT:
2182 * recent = info->st_value;
2183 got_values |= 1 << 1;
2186 case MAILIMAP_STATUS_ATT_UIDNEXT:
2187 * uid_next = info->st_value;
2188 got_values |= 1 << 2;
2191 case MAILIMAP_STATUS_ATT_UIDVALIDITY:
2192 * uid_validity = info->st_value;
2193 got_values |= 1 << 3;
2196 case MAILIMAP_STATUS_ATT_UNSEEN:
2197 * unseen = info->st_value;
2198 got_values |= 1 << 4;
2202 mailimap_mailbox_data_status_free(data_status);
2204 if (got_values != ((1 << 4) + (1 << 3) +
2205 (1 << 2) + (1 << 1) + (1 << 0))) {
2206 debug_print("status: incomplete values received (%d)\n", got_values);
2209 return IMAP_SUCCESS;
2212 static void imap_free_capabilities(IMAPSession *session)
2214 slist_free_strings(session->capability);
2215 g_slist_free(session->capability);
2216 session->capability = NULL;
2219 /* low-level IMAP4rev1 commands */
2222 static gint imap_cmd_authenticate(IMAPSession *session, const gchar *user,
2223 const gchar *pass, IMAPAuthType type)
2230 gchar hexdigest[33];
2234 auth_type = "CRAM-MD5";
2236 imap_gen_send(session, "AUTHENTICATE %s", auth_type);
2237 ok = imap_gen_recv(session, &buf);
2238 if (ok != IMAP_SUCCESS || buf[0] != '+' || buf[1] != ' ') {
2243 challenge = g_malloc(strlen(buf + 2) + 1);
2244 challenge_len = base64_decode(challenge, buf + 2, -1);
2245 challenge[challenge_len] = '\0';
2248 md5_hex_hmac(hexdigest, challenge, challenge_len, pass, strlen(pass));
2251 response = g_strdup_printf("%s %s", user, hexdigest);
2252 response64 = g_malloc((strlen(response) + 3) * 2 + 1);
2253 base64_encode(response64, response, strlen(response));
2256 sock_puts(SESSION(session)->sock, response64);
2257 ok = imap_cmd_ok(session, NULL);
2258 if (ok != IMAP_SUCCESS)
2259 log_warning(_("IMAP4 authentication failed.\n"));
2265 static gint imap_cmd_login(IMAPSession *session,
2266 const gchar *user, const gchar *pass,
2272 log_print("IMAP4> Logging %s to %s using %s\n",
2274 SESSION(session)->server,
2276 r = imap_threaded_login(session->folder, user, pass, type);
2277 if (r != MAILIMAP_NO_ERROR) {
2278 log_error("IMAP4< Error logging in to %s\n",
2279 SESSION(session)->server);
2287 static gint imap_cmd_logout(IMAPSession *session)
2289 imap_threaded_disconnect(session->folder);
2291 return IMAP_SUCCESS;
2294 static gint imap_cmd_noop(IMAPSession *session)
2297 unsigned int exists;
2299 r = imap_threaded_noop(session->folder, &exists);
2300 if (r != MAILIMAP_NO_ERROR) {
2301 debug_print("noop err %d\n", r);
2304 session->exists = exists;
2305 session_set_access_time(SESSION(session));
2307 return IMAP_SUCCESS;
2311 static gint imap_cmd_starttls(IMAPSession *session)
2315 r = imap_threaded_starttls(session->folder);
2316 if (r != MAILIMAP_NO_ERROR) {
2317 debug_print("starttls err %d\n", r);
2320 return IMAP_SUCCESS;
2324 static gint imap_cmd_select(IMAPSession *session, const gchar *folder,
2325 gint *exists, gint *recent, gint *unseen,
2326 guint32 *uid_validity, gboolean block)
2330 r = imap_threaded_select(session->folder, folder,
2331 exists, recent, unseen, uid_validity);
2332 if (r != MAILIMAP_NO_ERROR) {
2333 debug_print("select err %d\n", r);
2336 return IMAP_SUCCESS;
2339 static gint imap_cmd_examine(IMAPSession *session, const gchar *folder,
2340 gint *exists, gint *recent, gint *unseen,
2341 guint32 *uid_validity, gboolean block)
2345 r = imap_threaded_examine(session->folder, folder,
2346 exists, recent, unseen, uid_validity);
2347 if (r != MAILIMAP_NO_ERROR) {
2348 debug_print("examine err %d\n", r);
2352 return IMAP_SUCCESS;
2355 static gint imap_cmd_create(IMAPSession *session, const gchar *folder)
2359 r = imap_threaded_create(session->folder, folder);
2360 if (r != MAILIMAP_NO_ERROR) {
2365 return IMAP_SUCCESS;
2368 static gint imap_cmd_rename(IMAPSession *session, const gchar *old_folder,
2369 const gchar *new_folder)
2373 r = imap_threaded_rename(session->folder, old_folder,
2375 if (r != MAILIMAP_NO_ERROR) {
2380 return IMAP_SUCCESS;
2383 static gint imap_cmd_delete(IMAPSession *session, const gchar *folder)
2388 r = imap_threaded_delete(session->folder, folder);
2389 if (r != MAILIMAP_NO_ERROR) {
2394 return IMAP_SUCCESS;
2397 typedef struct _fetch_data {
2398 IMAPSession *session;
2400 const gchar *filename;
2406 static void *imap_cmd_fetch_thread(void *data)
2408 fetch_data *stuff = (fetch_data *)data;
2409 IMAPSession *session = stuff->session;
2410 guint32 uid = stuff->uid;
2411 const gchar *filename = stuff->filename;
2415 r = imap_threaded_fetch_content(session->folder,
2419 r = imap_threaded_fetch_content(session->folder,
2422 if (r != MAILIMAP_NO_ERROR) {
2423 debug_print("fetch err %d\n", r);
2424 return GINT_TO_POINTER(IMAP_ERROR);
2426 return GINT_TO_POINTER(IMAP_SUCCESS);
2429 static gint imap_cmd_fetch(IMAPSession *session, guint32 uid,
2430 const gchar *filename, gboolean headers,
2433 fetch_data *data = g_new0(fetch_data, 1);
2436 data->session = session;
2438 data->filename = filename;
2439 data->headers = headers;
2442 if (prefs_common.work_offline && !imap_gtk_should_override()) {
2446 statusbar_print_all(_("Fetching message..."));
2447 result = GPOINTER_TO_INT(imap_cmd_fetch_thread(data));
2448 statusbar_pop_all();
2454 static gint imap_cmd_append(IMAPSession *session, const gchar *destfolder,
2455 const gchar *file, IMAPFlags flags,
2458 struct mailimap_flag_list * flag_list;
2461 g_return_val_if_fail(file != NULL, IMAP_ERROR);
2463 flag_list = imap_flag_to_lep(flags);
2464 statusbar_print_all(_("Adding messages..."));
2465 r = imap_threaded_append(session->folder, destfolder,
2467 statusbar_pop_all();
2468 if (new_uid != NULL)
2471 if (r != MAILIMAP_NO_ERROR) {
2472 debug_print("append err %d\n", r);
2475 return IMAP_SUCCESS;
2478 static gint imap_cmd_copy(IMAPSession *session, struct mailimap_set * set,
2479 const gchar *destfolder, GRelation *uid_mapping)
2483 g_return_val_if_fail(session != NULL, IMAP_ERROR);
2484 g_return_val_if_fail(set != NULL, IMAP_ERROR);
2485 g_return_val_if_fail(destfolder != NULL, IMAP_ERROR);
2487 statusbar_print_all(_("Copying messages..."));
2488 r = imap_threaded_copy(session->folder, set, destfolder);
2489 statusbar_pop_all();
2490 if (r != MAILIMAP_NO_ERROR) {
2495 return IMAP_SUCCESS;
2498 static gint imap_cmd_store(IMAPSession *session, struct mailimap_set * set,
2499 IMAPFlags flags, int do_add)
2502 struct mailimap_flag_list * flag_list;
2503 struct mailimap_store_att_flags * store_att_flags;
2505 flag_list = imap_flag_to_lep(flags);
2509 mailimap_store_att_flags_new_add_flags_silent(flag_list);
2512 mailimap_store_att_flags_new_remove_flags_silent(flag_list);
2514 r = imap_threaded_store(session->folder, set, store_att_flags);
2515 if (r != MAILIMAP_NO_ERROR) {
2520 return IMAP_SUCCESS;
2523 static gint imap_cmd_expunge(IMAPSession *session)
2527 if (prefs_common.work_offline && !imap_gtk_should_override()) {
2531 r = imap_threaded_expunge(session->folder);
2532 if (r != MAILIMAP_NO_ERROR) {
2537 return IMAP_SUCCESS;
2540 static void imap_path_separator_subst(gchar *str, gchar separator)
2543 gboolean in_escape = FALSE;
2545 if (!separator || separator == '/') return;
2547 for (p = str; *p != '\0'; p++) {
2548 if (*p == '/' && !in_escape)
2550 else if (*p == '&' && *(p + 1) != '-' && !in_escape)
2552 else if (*p == '-' && in_escape)
2557 static gchar *imap_modified_utf7_to_utf8(const gchar *mutf7_str)
2559 static iconv_t cd = (iconv_t)-1;
2560 static gboolean iconv_ok = TRUE;
2563 size_t norm_utf7_len;
2565 gchar *to_str, *to_p;
2567 gboolean in_escape = FALSE;
2569 if (!iconv_ok) return g_strdup(mutf7_str);
2571 if (cd == (iconv_t)-1) {
2572 cd = iconv_open(CS_INTERNAL, CS_UTF_7);
2573 if (cd == (iconv_t)-1) {
2574 g_warning("iconv cannot convert UTF-7 to %s\n",
2577 return g_strdup(mutf7_str);
2581 /* modified UTF-7 to normal UTF-7 conversion */
2582 norm_utf7 = g_string_new(NULL);
2584 for (p = mutf7_str; *p != '\0'; p++) {
2585 /* replace: '&' -> '+',
2587 escaped ',' -> '/' */
2588 if (!in_escape && *p == '&') {
2589 if (*(p + 1) != '-') {
2590 g_string_append_c(norm_utf7, '+');
2593 g_string_append_c(norm_utf7, '&');
2596 } else if (in_escape && *p == ',') {
2597 g_string_append_c(norm_utf7, '/');
2598 } else if (in_escape && *p == '-') {
2599 g_string_append_c(norm_utf7, '-');
2602 g_string_append_c(norm_utf7, *p);
2606 norm_utf7_p = norm_utf7->str;
2607 norm_utf7_len = norm_utf7->len;
2608 to_len = strlen(mutf7_str) * 5;
2609 to_p = to_str = g_malloc(to_len + 1);
2611 if (iconv(cd, (ICONV_CONST gchar **)&norm_utf7_p, &norm_utf7_len,
2612 &to_p, &to_len) == -1) {
2613 g_warning(_("iconv cannot convert UTF-7 to %s\n"),
2614 conv_get_locale_charset_str());
2615 g_string_free(norm_utf7, TRUE);
2617 return g_strdup(mutf7_str);
2620 /* second iconv() call for flushing */
2621 iconv(cd, NULL, NULL, &to_p, &to_len);
2622 g_string_free(norm_utf7, TRUE);
2628 static gchar *imap_utf8_to_modified_utf7(const gchar *from)
2630 static iconv_t cd = (iconv_t)-1;
2631 static gboolean iconv_ok = TRUE;
2632 gchar *norm_utf7, *norm_utf7_p;
2633 size_t from_len, norm_utf7_len;
2635 gchar *from_tmp, *to, *p;
2636 gboolean in_escape = FALSE;
2638 if (!iconv_ok) return g_strdup(from);
2640 if (cd == (iconv_t)-1) {
2641 cd = iconv_open(CS_UTF_7, CS_INTERNAL);
2642 if (cd == (iconv_t)-1) {
2643 g_warning(_("iconv cannot convert %s to UTF-7\n"),
2646 return g_strdup(from);
2650 /* UTF-8 to normal UTF-7 conversion */
2651 Xstrdup_a(from_tmp, from, return g_strdup(from));
2652 from_len = strlen(from);
2653 norm_utf7_len = from_len * 5;
2654 Xalloca(norm_utf7, norm_utf7_len + 1, return g_strdup(from));
2655 norm_utf7_p = norm_utf7;
2657 #define IS_PRINT(ch) (isprint(ch) && IS_ASCII(ch))
2659 while (from_len > 0) {
2660 if (*from_tmp == '+') {
2661 *norm_utf7_p++ = '+';
2662 *norm_utf7_p++ = '-';
2666 } else if (IS_PRINT(*(guchar *)from_tmp)) {
2667 /* printable ascii char */
2668 *norm_utf7_p = *from_tmp;
2674 size_t conv_len = 0;
2676 /* unprintable char: convert to UTF-7 */
2678 while (!IS_PRINT(*(guchar *)p) && conv_len < from_len) {
2679 conv_len += g_utf8_skip[*(guchar *)p];
2680 p += g_utf8_skip[*(guchar *)p];
2683 from_len -= conv_len;
2684 if (iconv(cd, (ICONV_CONST gchar **)&from_tmp,
2686 &norm_utf7_p, &norm_utf7_len) == -1) {
2687 g_warning(_("iconv cannot convert UTF-8 to UTF-7\n"));
2688 return g_strdup(from);
2691 /* second iconv() call for flushing */
2692 iconv(cd, NULL, NULL, &norm_utf7_p, &norm_utf7_len);
2698 *norm_utf7_p = '\0';
2699 to_str = g_string_new(NULL);
2700 for (p = norm_utf7; p < norm_utf7_p; p++) {
2701 /* replace: '&' -> "&-",
2704 BASE64 '/' -> ',' */
2705 if (!in_escape && *p == '&') {
2706 g_string_append(to_str, "&-");
2707 } else if (!in_escape && *p == '+') {
2708 if (*(p + 1) == '-') {
2709 g_string_append_c(to_str, '+');
2712 g_string_append_c(to_str, '&');
2715 } else if (in_escape && *p == '/') {
2716 g_string_append_c(to_str, ',');
2717 } else if (in_escape && *p == '-') {
2718 g_string_append_c(to_str, '-');
2721 g_string_append_c(to_str, *p);
2727 g_string_append_c(to_str, '-');
2731 g_string_free(to_str, FALSE);
2736 static gboolean imap_rename_folder_func(GNode *node, gpointer data)
2738 FolderItem *item = node->data;
2739 gchar **paths = data;
2740 const gchar *oldpath = paths[0];
2741 const gchar *newpath = paths[1];
2743 gchar *new_itempath;
2746 oldpathlen = strlen(oldpath);
2747 if (strncmp(oldpath, item->path, oldpathlen) != 0) {
2748 g_warning("path doesn't match: %s, %s\n", oldpath, item->path);
2752 base = item->path + oldpathlen;
2753 while (*base == G_DIR_SEPARATOR) base++;
2755 new_itempath = g_strdup(newpath);
2757 new_itempath = g_strconcat(newpath, G_DIR_SEPARATOR_S, base,
2760 item->path = new_itempath;
2765 typedef struct _get_list_uid_data {
2767 IMAPFolderItem *item;
2768 GSList **msgnum_list;
2770 } get_list_uid_data;
2772 static void *get_list_of_uids_thread(void *data)
2774 get_list_uid_data *stuff = (get_list_uid_data *)data;
2775 Folder *folder = stuff->folder;
2776 IMAPFolderItem *item = stuff->item;
2777 GSList **msgnum_list = stuff->msgnum_list;
2778 gint ok, nummsgs = 0, lastuid_old;
2779 IMAPSession *session;
2780 GSList *uidlist, *elem;
2781 struct mailimap_set * set;
2782 clist * lep_uidlist;
2785 session = imap_session_get(folder);
2786 if (session == NULL) {
2788 return GINT_TO_POINTER(-1);
2791 ok = imap_select(session, IMAP_FOLDER(folder), item->item.path,
2792 NULL, NULL, NULL, NULL, TRUE);
2793 if (ok != IMAP_SUCCESS) {
2795 return GINT_TO_POINTER(-1);
2800 set = mailimap_set_new_interval(item->lastuid + 1, 0);
2801 r = imap_threaded_search(folder, IMAP_SEARCH_TYPE_SIMPLE, set,
2803 if (r == MAILIMAP_NO_ERROR) {
2804 GSList * fetchuid_list;
2807 imap_uid_list_from_lep(lep_uidlist);
2808 uidlist = g_slist_concat(fetchuid_list, uidlist);
2811 GSList * fetchuid_list;
2812 carray * lep_uidtab;
2814 r = imap_threaded_fetch_uid(folder, item->lastuid + 1,
2816 if (r == MAILIMAP_NO_ERROR) {
2818 imap_uid_list_from_lep_tab(lep_uidtab);
2819 uidlist = g_slist_concat(fetchuid_list, uidlist);
2823 lastuid_old = item->lastuid;
2824 *msgnum_list = g_slist_copy(item->uid_list);
2825 nummsgs = g_slist_length(*msgnum_list);
2826 debug_print("Got %d uids from cache\n", g_slist_length(item->uid_list));
2828 for (elem = uidlist; elem != NULL; elem = g_slist_next(elem)) {
2831 msgnum = GPOINTER_TO_INT(elem->data);
2832 if (msgnum > lastuid_old) {
2833 *msgnum_list = g_slist_prepend(*msgnum_list, GINT_TO_POINTER(msgnum));
2834 item->uid_list = g_slist_prepend(item->uid_list, GINT_TO_POINTER(msgnum));
2837 if(msgnum > item->lastuid)
2838 item->lastuid = msgnum;
2841 g_slist_free(uidlist);
2844 return GINT_TO_POINTER(nummsgs);
2847 static gint get_list_of_uids(Folder *folder, IMAPFolderItem *item, GSList **msgnum_list)
2850 get_list_uid_data *data = g_new0(get_list_uid_data, 1);
2852 data->folder = folder;
2854 data->msgnum_list = msgnum_list;
2856 if (prefs_common.work_offline && !imap_gtk_should_override()) {
2861 result = GPOINTER_TO_INT(get_list_of_uids_thread(data));
2867 gint imap_get_num_list(Folder *folder, FolderItem *_item, GSList **msgnum_list, gboolean *old_uids_valid)
2869 IMAPFolderItem *item = (IMAPFolderItem *)_item;
2870 IMAPSession *session;
2871 gint ok, nummsgs = 0, exists, recent, uid_val, uid_next, unseen;
2872 GSList *uidlist = NULL;
2874 gboolean selected_folder;
2876 debug_print("get_num_list\n");
2877 statusbar_print_all("Scanning %s...\n", FOLDER_ITEM(item)->path
2878 ? FOLDER_ITEM(item)->path:"");
2880 g_return_val_if_fail(folder != NULL, -1);
2881 g_return_val_if_fail(item != NULL, -1);
2882 g_return_val_if_fail(item->item.path != NULL, -1);
2883 g_return_val_if_fail(FOLDER_CLASS(folder) == &imap_class, -1);
2884 g_return_val_if_fail(folder->account != NULL, -1);
2886 session = imap_session_get(folder);
2887 g_return_val_if_fail(session != NULL, -1);
2889 selected_folder = (session->mbox != NULL) &&
2890 (!strcmp(session->mbox, item->item.path));
2891 if (selected_folder) {
2892 ok = imap_cmd_noop(session);
2893 if (ok != IMAP_SUCCESS) {
2894 debug_print("disconnected!\n");
2895 session = imap_reconnect_if_possible(folder, session);
2896 if (session == NULL) {
2897 statusbar_pop_all();
2901 exists = session->exists;
2903 *old_uids_valid = TRUE;
2905 ok = imap_status(session, IMAP_FOLDER(folder), item->item.path,
2906 &exists, &recent, &uid_next, &uid_val, &unseen, FALSE);
2907 if (ok != IMAP_SUCCESS) {
2908 statusbar_pop_all();
2911 if(item->item.mtime == uid_val)
2912 *old_uids_valid = TRUE;
2914 *old_uids_valid = FALSE;
2916 debug_print("Freeing imap uid cache\n");
2918 g_slist_free(item->uid_list);
2919 item->uid_list = NULL;
2921 item->item.mtime = uid_val;
2923 imap_delete_all_cached_messages((FolderItem *)item);
2927 if (!selected_folder)
2928 item->uid_next = uid_next;
2930 /* If old uid_next matches new uid_next we can be sure no message
2931 was added to the folder */
2932 if (( selected_folder && !session->folder_content_changed) ||
2933 (!selected_folder && uid_next == item->uid_next)) {
2934 nummsgs = g_slist_length(item->uid_list);
2936 /* If number of messages is still the same we
2937 know our caches message numbers are still valid,
2938 otherwise if the number of messages has decrease
2939 we discard our cache to start a new scan to find
2940 out which numbers have been removed */
2941 if (exists == nummsgs) {
2942 *msgnum_list = g_slist_copy(item->uid_list);
2943 statusbar_pop_all();
2945 } else if (exists < nummsgs) {
2946 debug_print("Freeing imap uid cache");
2948 g_slist_free(item->uid_list);
2949 item->uid_list = NULL;
2954 *msgnum_list = NULL;
2955 statusbar_pop_all();
2959 nummsgs = get_list_of_uids(folder, item, &uidlist);
2962 statusbar_pop_all();
2966 if (nummsgs != exists) {
2967 /* Cache contains more messages then folder, we have cached
2968 an old UID of a message that was removed and new messages
2969 have been added too, otherwise the uid_next check would
2971 debug_print("Freeing imap uid cache");
2973 g_slist_free(item->uid_list);
2974 item->uid_list = NULL;
2976 g_slist_free(*msgnum_list);
2978 nummsgs = get_list_of_uids(folder, item, &uidlist);
2981 *msgnum_list = uidlist;
2983 dir = folder_item_get_path((FolderItem *)item);
2984 debug_print("removing old messages from %s\n", dir);
2985 remove_numbered_files_not_in_list(dir, *msgnum_list);
2988 debug_print("get_num_list - ok - %i\n", nummsgs);
2989 statusbar_pop_all();
2993 static MsgInfo *imap_parse_msg(const gchar *file, FolderItem *item)
2998 flags.perm_flags = MSG_NEW|MSG_UNREAD;
2999 flags.tmp_flags = 0;
3001 g_return_val_if_fail(item != NULL, NULL);
3002 g_return_val_if_fail(file != NULL, NULL);
3004 if (folder_has_parent_of_type(item, F_QUEUE)) {
3005 MSG_SET_TMP_FLAGS(flags, MSG_QUEUED);
3006 } else if (folder_has_parent_of_type(item, F_DRAFT)) {
3007 MSG_SET_TMP_FLAGS(flags, MSG_DRAFT);
3010 msginfo = procheader_parse_file(file, flags, FALSE, FALSE);
3011 if (!msginfo) return NULL;
3013 msginfo->plaintext_file = g_strdup(file);
3014 msginfo->folder = item;
3019 GSList *imap_get_msginfos(Folder *folder, FolderItem *item,
3020 GSList *msgnum_list)
3022 IMAPSession *session;
3023 MsgInfoList *ret = NULL;
3026 debug_print("get_msginfos\n");
3028 g_return_val_if_fail(folder != NULL, NULL);
3029 g_return_val_if_fail(item != NULL, NULL);
3030 g_return_val_if_fail(msgnum_list != NULL, NULL);
3032 session = imap_session_get(folder);
3033 g_return_val_if_fail(session != NULL, NULL);
3035 debug_print("IMAP getting msginfos\n");
3036 ok = imap_select(session, IMAP_FOLDER(folder), item->path,
3037 NULL, NULL, NULL, NULL, FALSE);
3038 if (ok != IMAP_SUCCESS)
3041 if (!(folder_has_parent_of_type(item, F_DRAFT) ||
3042 folder_has_parent_of_type(item, F_QUEUE))) {
3043 ret = g_slist_concat(ret,
3044 imap_get_uncached_messages(session, item,
3047 MsgNumberList *sorted_list, *elem;
3048 gint startnum, lastnum;
3050 sorted_list = g_slist_sort(g_slist_copy(msgnum_list), g_int_compare);
3052 startnum = lastnum = GPOINTER_TO_INT(sorted_list->data);
3054 for (elem = sorted_list;; elem = g_slist_next(elem)) {
3058 num = GPOINTER_TO_INT(elem->data);
3060 if (num > lastnum + 1 || elem == NULL) {
3062 for (i = startnum; i <= lastnum; ++i) {
3065 file = imap_fetch_msg(folder, item, i);
3067 MsgInfo *msginfo = imap_parse_msg(file, item);
3068 if (msginfo != NULL) {
3069 msginfo->msgnum = i;
3070 ret = g_slist_append(ret, msginfo);
3084 g_slist_free(sorted_list);
3090 MsgInfo *imap_get_msginfo(Folder *folder, FolderItem *item, gint uid)
3092 MsgInfo *msginfo = NULL;
3093 MsgInfoList *msginfolist;
3094 MsgNumberList numlist;
3096 numlist.next = NULL;
3097 numlist.data = GINT_TO_POINTER(uid);
3099 msginfolist = imap_get_msginfos(folder, item, &numlist);
3100 if (msginfolist != NULL) {
3101 msginfo = msginfolist->data;
3102 g_slist_free(msginfolist);
3108 gboolean imap_scan_required(Folder *folder, FolderItem *_item)
3110 IMAPSession *session;
3111 IMAPFolderItem *item = (IMAPFolderItem *)_item;
3112 gint ok, exists = 0, recent = 0, unseen = 0;
3113 guint32 uid_next, uid_val = 0;
3114 gboolean selected_folder;
3116 g_return_val_if_fail(folder != NULL, FALSE);
3117 g_return_val_if_fail(item != NULL, FALSE);
3118 g_return_val_if_fail(item->item.folder != NULL, FALSE);
3119 g_return_val_if_fail(FOLDER_CLASS(item->item.folder) == &imap_class, FALSE);
3121 if (item->item.path == NULL)
3124 session = imap_session_get(folder);
3125 g_return_val_if_fail(session != NULL, FALSE);
3127 selected_folder = (session->mbox != NULL) &&
3128 (!strcmp(session->mbox, item->item.path));
3129 if (selected_folder) {
3130 ok = imap_cmd_noop(session);
3131 if (ok != IMAP_SUCCESS) {
3132 debug_print("disconnected!\n");
3133 session = imap_reconnect_if_possible(folder, session);
3134 if (session == NULL)
3138 if (session->folder_content_changed
3139 || session->exists != item->item.total_msgs)
3142 ok = imap_status(session, IMAP_FOLDER(folder), item->item.path,
3143 &exists, &recent, &uid_next, &uid_val, &unseen, FALSE);
3144 if (ok != IMAP_SUCCESS)
3147 if ((uid_next != item->uid_next) || (exists != item->item.total_msgs))
3154 void imap_change_flags(Folder *folder, FolderItem *item, MsgInfo *msginfo, MsgPermFlags newflags)
3156 IMAPSession *session;
3157 IMAPFlags flags_set = 0, flags_unset = 0;
3158 gint ok = IMAP_SUCCESS;
3159 MsgNumberList numlist;
3160 hashtable_data *ht_data = NULL;
3162 g_return_if_fail(folder != NULL);
3163 g_return_if_fail(folder->klass == &imap_class);
3164 g_return_if_fail(item != NULL);
3165 g_return_if_fail(item->folder == folder);
3166 g_return_if_fail(msginfo != NULL);
3167 g_return_if_fail(msginfo->folder == item);
3169 session = imap_session_get(folder);
3173 if ((ok = imap_select(session, IMAP_FOLDER(folder), msginfo->folder->path,
3174 NULL, NULL, NULL, NULL, FALSE)) != IMAP_SUCCESS) {
3178 if (!MSG_IS_MARKED(msginfo->flags) && (newflags & MSG_MARKED))
3179 flags_set |= IMAP_FLAG_FLAGGED;
3180 if ( MSG_IS_MARKED(msginfo->flags) && !(newflags & MSG_MARKED))
3181 flags_unset |= IMAP_FLAG_FLAGGED;
3183 if (!MSG_IS_UNREAD(msginfo->flags) && (newflags & MSG_UNREAD))
3184 flags_unset |= IMAP_FLAG_SEEN;
3185 if ( MSG_IS_UNREAD(msginfo->flags) && !(newflags & MSG_UNREAD))
3186 flags_set |= IMAP_FLAG_SEEN;
3188 if (!MSG_IS_REPLIED(msginfo->flags) && (newflags & MSG_REPLIED))
3189 flags_set |= IMAP_FLAG_ANSWERED;
3190 if ( MSG_IS_REPLIED(msginfo->flags) && !(newflags & MSG_REPLIED))
3191 flags_set |= IMAP_FLAG_ANSWERED;
3193 numlist.next = NULL;
3194 numlist.data = GINT_TO_POINTER(msginfo->msgnum);
3196 if (IMAP_FOLDER_ITEM(item)->batching) {
3197 /* instead of performing an UID STORE command for each message change,
3198 * as a lot of them can change "together", we just fill in hashtables
3199 * and defer the treatment so that we're able to send only one
3202 debug_print("IMAP batch mode on, deferring flags change\n");
3204 ht_data = g_hash_table_lookup(flags_set_table, GINT_TO_POINTER(flags_set));
3205 if (ht_data == NULL) {
3206 ht_data = g_new0(hashtable_data, 1);
3207 ht_data->session = session;
3208 g_hash_table_insert(flags_set_table, GINT_TO_POINTER(flags_set), ht_data);
3210 if (!g_slist_find(ht_data->msglist, GINT_TO_POINTER(msginfo->msgnum)))
3211 ht_data->msglist = g_slist_prepend(ht_data->msglist, GINT_TO_POINTER(msginfo->msgnum));
3214 ht_data = g_hash_table_lookup(flags_unset_table, GINT_TO_POINTER(flags_unset));
3215 if (ht_data == NULL) {
3216 ht_data = g_new0(hashtable_data, 1);
3217 ht_data->session = session;
3218 g_hash_table_insert(flags_unset_table, GINT_TO_POINTER(flags_unset), ht_data);
3220 if (!g_slist_find(ht_data->msglist, GINT_TO_POINTER(msginfo->msgnum)))
3221 ht_data->msglist = g_slist_prepend(ht_data->msglist, GINT_TO_POINTER(msginfo->msgnum));
3224 debug_print("IMAP changing flags\n");
3226 ok = imap_set_message_flags(session, &numlist, flags_set, TRUE);
3227 if (ok != IMAP_SUCCESS) {
3233 ok = imap_set_message_flags(session, &numlist, flags_unset, FALSE);
3234 if (ok != IMAP_SUCCESS) {
3239 msginfo->flags.perm_flags = newflags;
3244 static gint imap_remove_msg(Folder *folder, FolderItem *item, gint uid)
3247 IMAPSession *session;
3249 MsgNumberList numlist;
3251 g_return_val_if_fail(folder != NULL, -1);
3252 g_return_val_if_fail(FOLDER_CLASS(folder) == &imap_class, -1);
3253 g_return_val_if_fail(item != NULL, -1);
3255 session = imap_session_get(folder);
3256 if (!session) return -1;
3258 ok = imap_select(session, IMAP_FOLDER(folder), item->path,
3259 NULL, NULL, NULL, NULL, FALSE);
3260 if (ok != IMAP_SUCCESS)
3263 numlist.next = NULL;
3264 numlist.data = GINT_TO_POINTER(uid);
3266 ok = imap_set_message_flags
3267 (IMAP_SESSION(REMOTE_FOLDER(folder)->session),
3268 &numlist, IMAP_FLAG_DELETED, TRUE);
3269 if (ok != IMAP_SUCCESS) {
3270 log_warning(_("can't set deleted flags: %d\n"), uid);
3274 if (!session->uidplus) {
3275 ok = imap_cmd_expunge(session);
3279 uidstr = g_strdup_printf("%u", uid);
3280 ok = imap_cmd_expunge(session);
3283 if (ok != IMAP_SUCCESS) {
3284 log_warning(_("can't expunge\n"));
3288 IMAP_FOLDER_ITEM(item)->uid_list = g_slist_remove(
3289 IMAP_FOLDER_ITEM(item)->uid_list, numlist.data);
3290 dir = folder_item_get_path(item);
3291 if (is_dir_exist(dir))
3292 remove_numbered_files(dir, uid, uid);
3295 return IMAP_SUCCESS;
3298 static gint compare_msginfo(gconstpointer a, gconstpointer b)
3300 return ((MsgInfo *)a)->msgnum - ((MsgInfo *)b)->msgnum;
3303 static guint gslist_find_next_num(MsgNumberList **list, guint num)
3307 g_return_val_if_fail(list != NULL, -1);
3309 for (elem = *list; elem != NULL; elem = g_slist_next(elem))
3310 if (GPOINTER_TO_INT(elem->data) >= num)
3313 return elem != NULL ? GPOINTER_TO_INT(elem->data) : (gint)-1;
3317 * NEW and DELETED flags are not syncronized
3318 * - The NEW/RECENT flags in IMAP folders can not really be directly
3319 * modified by Sylpheed
3320 * - The DELETE/DELETED flag in IMAP and Sylpheed don't have the same
3321 * meaning, in IMAP it always removes the messages from the FolderItem
3322 * in Sylpheed it can mean to move the message to trash
3325 typedef struct _get_flags_data {
3328 MsgInfoList *msginfo_list;
3329 GRelation *msgflags;
3333 static /*gint*/ void *imap_get_flags_thread(void *data)
3335 get_flags_data *stuff = (get_flags_data *)data;
3336 Folder *folder = stuff->folder;
3337 FolderItem *item = stuff->item;
3338 MsgInfoList *msginfo_list = stuff->msginfo_list;
3339 GRelation *msgflags = stuff->msgflags;
3340 IMAPSession *session;
3341 GSList *sorted_list;
3342 GSList *unseen = NULL, *answered = NULL, *flagged = NULL;
3343 GSList *p_unseen, *p_answered, *p_flagged;
3345 GSList *seq_list, *cur;
3346 gboolean reverse_seen = FALSE;
3349 gint exists_cnt, recent_cnt, unseen_cnt, uid_next;
3350 guint32 uidvalidity;
3351 gboolean selected_folder;
3353 if (folder == NULL || item == NULL) {
3355 return GINT_TO_POINTER(-1);
3357 if (msginfo_list == NULL) {
3359 return GINT_TO_POINTER(0);
3362 session = imap_session_get(folder);
3363 if (session == NULL) {
3365 return GINT_TO_POINTER(-1);
3368 selected_folder = (session->mbox != NULL) &&
3369 (!strcmp(session->mbox, item->path));
3371 if (!selected_folder) {
3372 ok = imap_status(session, IMAP_FOLDER(folder), item->path,
3373 &exists_cnt, &recent_cnt, &uid_next, &uidvalidity, &unseen_cnt, TRUE);
3374 ok = imap_select(session, IMAP_FOLDER(folder), item->path,
3375 NULL, NULL, NULL, NULL, TRUE);
3376 if (ok != IMAP_SUCCESS) {
3378 return GINT_TO_POINTER(-1);
3381 if (unseen_cnt > exists_cnt / 2)
3382 reverse_seen = TRUE;
3385 if (item->unread_msgs > item->total_msgs / 2)
3386 reverse_seen = TRUE;
3389 cmd_buf = g_string_new(NULL);
3391 sorted_list = g_slist_sort(g_slist_copy(msginfo_list), compare_msginfo);
3393 seq_list = imap_get_lep_set_from_msglist(msginfo_list);
3395 for (cur = seq_list; cur != NULL; cur = g_slist_next(cur)) {
3396 struct mailimap_set * imapset;
3397 clist * lep_uidlist;
3400 imapset = cur->data;
3402 r = imap_threaded_search(folder, IMAP_SEARCH_TYPE_SEEN,
3403 imapset, &lep_uidlist);
3406 r = imap_threaded_search(folder,
3407 IMAP_SEARCH_TYPE_UNSEEN,
3408 imapset, &lep_uidlist);
3410 if (r == MAILIMAP_NO_ERROR) {
3413 uidlist = imap_uid_list_from_lep(lep_uidlist);
3414 mailimap_search_result_free(lep_uidlist);
3416 unseen = g_slist_concat(unseen, uidlist);
3419 r = imap_threaded_search(folder, IMAP_SEARCH_TYPE_ANSWERED,
3420 imapset, &lep_uidlist);
3421 if (r == MAILIMAP_NO_ERROR) {
3424 uidlist = imap_uid_list_from_lep(lep_uidlist);
3425 mailimap_search_result_free(lep_uidlist);
3427 answered = g_slist_concat(answered, uidlist);
3430 r = imap_threaded_search(folder, IMAP_SEARCH_TYPE_FLAGGED,
3431 imapset, &lep_uidlist);
3432 if (r == MAILIMAP_NO_ERROR) {
3435 uidlist = imap_uid_list_from_lep(lep_uidlist);
3436 mailimap_search_result_free(lep_uidlist);
3438 flagged = g_slist_concat(flagged, uidlist);
3443 p_answered = answered;
3444 p_flagged = flagged;
3446 for (elem = sorted_list; elem != NULL; elem = g_slist_next(elem)) {
3451 msginfo = (MsgInfo *) elem->data;
3452 flags = msginfo->flags.perm_flags;
3453 wasnew = (flags & MSG_NEW);
3454 flags &= ~((reverse_seen ? 0 : MSG_UNREAD | MSG_NEW) | MSG_REPLIED | MSG_MARKED);
3456 flags |= MSG_UNREAD | (wasnew ? MSG_NEW : 0);
3457 if (gslist_find_next_num(&p_unseen, msginfo->msgnum) == msginfo->msgnum) {
3458 if (!reverse_seen) {
3459 flags |= MSG_UNREAD | (wasnew ? MSG_NEW : 0);
3461 flags &= ~(MSG_UNREAD | MSG_NEW);
3464 if (gslist_find_next_num(&p_answered, msginfo->msgnum) == msginfo->msgnum)
3465 flags |= MSG_REPLIED;
3466 if (gslist_find_next_num(&p_flagged, msginfo->msgnum) == msginfo->msgnum)
3467 flags |= MSG_MARKED;
3468 g_relation_insert(msgflags, msginfo, GINT_TO_POINTER(flags));
3471 imap_lep_set_free(seq_list);
3472 g_slist_free(flagged);
3473 g_slist_free(answered);
3474 g_slist_free(unseen);
3475 g_slist_free(sorted_list);
3476 g_string_free(cmd_buf, TRUE);
3479 return GINT_TO_POINTER(0);
3482 static gint imap_get_flags(Folder *folder, FolderItem *item,
3483 MsgInfoList *msginfo_list, GRelation *msgflags)
3486 get_flags_data *data = g_new0(get_flags_data, 1);
3488 data->folder = folder;
3490 data->msginfo_list = msginfo_list;
3491 data->msgflags = msgflags;
3493 if (prefs_common.work_offline && !imap_gtk_should_override()) {
3498 result = GPOINTER_TO_INT(imap_get_flags_thread(data));
3505 static gboolean process_flags(gpointer key, gpointer value, gpointer user_data)
3507 gboolean flags_set = GPOINTER_TO_INT(user_data);
3508 gint flags_value = GPOINTER_TO_INT(key);
3509 hashtable_data *data = (hashtable_data *)value;
3511 data->msglist = g_slist_reverse(data->msglist);
3513 debug_print("IMAP %ssetting flags to %d for %d messages\n",
3516 g_slist_length(data->msglist));
3517 imap_set_message_flags(data->session, data->msglist, flags_value, flags_set);
3519 g_slist_free(data->msglist);
3524 static void process_hashtable(void)
3526 if (flags_set_table) {
3527 g_hash_table_foreach_remove(flags_set_table, process_flags, GINT_TO_POINTER(TRUE));
3528 g_free(flags_set_table);
3529 flags_set_table = NULL;
3531 if (flags_unset_table) {
3532 g_hash_table_foreach_remove(flags_unset_table, process_flags, GINT_TO_POINTER(FALSE));
3533 g_free(flags_unset_table);
3534 flags_unset_table = NULL;
3538 static IMAPFolderItem *batching_item = NULL;
3540 static void imap_set_batch (Folder *folder, FolderItem *_item, gboolean batch)
3542 IMAPFolderItem *item = (IMAPFolderItem *)_item;
3544 g_return_if_fail(item != NULL);
3546 if (batch && batching_item != NULL) {
3547 g_warning("already batching on %s\n", batching_item->item.path);
3551 if (item->batching == batch)
3554 item->batching = batch;
3556 batching_item = batch?item:NULL;
3559 debug_print("IMAP switching to batch mode\n");
3560 if (flags_set_table) {
3561 g_warning("flags_set_table non-null but we just entered batch mode!\n");
3562 flags_set_table = NULL;
3564 if (flags_unset_table) {
3565 g_warning("flags_unset_table non-null but we just entered batch mode!\n");
3566 flags_unset_table = NULL;
3568 flags_set_table = g_hash_table_new(NULL, g_direct_equal);
3569 flags_unset_table = g_hash_table_new(NULL, g_direct_equal);
3571 debug_print("IMAP switching away from batch mode\n");
3573 process_hashtable();
3579 /* data types conversion libetpan <-> sylpheed */
3583 #define ETPAN_IMAP_MB_MARKED 1
3584 #define ETPAN_IMAP_MB_UNMARKED 2
3585 #define ETPAN_IMAP_MB_NOSELECT 4
3586 #define ETPAN_IMAP_MB_NOINFERIORS 8
3588 static int imap_flags_to_flags(struct mailimap_mbx_list_flags * imap_flags)
3594 if (imap_flags->mbf_type == MAILIMAP_MBX_LIST_FLAGS_SFLAG) {
3595 switch (imap_flags->mbf_sflag) {
3596 case MAILIMAP_MBX_LIST_SFLAG_MARKED:
3597 flags |= ETPAN_IMAP_MB_MARKED;
3599 case MAILIMAP_MBX_LIST_SFLAG_NOSELECT:
3600 flags |= ETPAN_IMAP_MB_NOSELECT;
3602 case MAILIMAP_MBX_LIST_SFLAG_UNMARKED:
3603 flags |= ETPAN_IMAP_MB_UNMARKED;
3608 for(cur = clist_begin(imap_flags->mbf_oflags) ; cur != NULL ;
3609 cur = clist_next(cur)) {
3610 struct mailimap_mbx_list_oflag * oflag;
3612 oflag = clist_content(cur);
3614 switch (oflag->of_type) {
3615 case MAILIMAP_MBX_LIST_OFLAG_NOINFERIORS:
3616 flags |= ETPAN_IMAP_MB_NOINFERIORS;
3624 static GSList * imap_list_from_lep(IMAPFolder * folder,
3625 clist * list, const gchar * real_path)
3632 for(iter = clist_begin(list) ; iter != NULL ;
3633 iter = clist_next(iter)) {
3634 struct mailimap_mailbox_list * mb;
3642 FolderItem *new_item;
3644 mb = clist_content(iter);
3647 if (mb->mb_flag != NULL)
3648 flags = imap_flags_to_flags(mb->mb_flag);
3650 delimiter = mb->mb_delimiter;
3653 dup_name = strdup(name);
3654 if (delimiter != '\0')
3655 subst_char(dup_name, delimiter, '/');
3657 base = g_path_get_basename(dup_name);
3658 if (base[0] == '.') {
3664 if (strcmp(dup_name, real_path) == 0) {
3670 if (dup_name[strlen(dup_name)-1] == '/') {
3676 loc_name = imap_modified_utf7_to_utf8(base);
3677 loc_path = imap_modified_utf7_to_utf8(dup_name);
3679 new_item = folder_item_new(FOLDER(folder), loc_name, loc_path);
3680 if ((flags & ETPAN_IMAP_MB_NOINFERIORS) != 0)
3681 new_item->no_sub = TRUE;
3682 if (strcmp(dup_name, "INBOX") != 0 &&
3683 ((flags & ETPAN_IMAP_MB_NOSELECT) != 0))
3684 new_item->no_select = TRUE;
3686 item_list = g_slist_append(item_list, new_item);
3688 debug_print("folder '%s' found.\n", loc_path);
3699 static GSList * imap_get_lep_set_from_numlist(MsgNumberList *numlist)
3701 GSList *sorted_list, *cur;
3702 guint first, last, next;
3703 GSList *ret_list = NULL;
3705 struct mailimap_set * current_set;
3706 unsigned int item_count;
3708 if (numlist == NULL)
3712 current_set = mailimap_set_new_empty();
3714 sorted_list = g_slist_copy(numlist);
3715 sorted_list = g_slist_sort(sorted_list, g_int_compare);
3717 first = GPOINTER_TO_INT(sorted_list->data);
3720 for (cur = sorted_list; cur != NULL; cur = g_slist_next(cur)) {
3721 if (GPOINTER_TO_INT(cur->data) == 0)
3726 last = GPOINTER_TO_INT(cur->data);
3728 next = GPOINTER_TO_INT(cur->next->data);
3732 if (last + 1 != next || next == 0) {
3734 struct mailimap_set_item * item;
3735 item = mailimap_set_item_new(first, last);
3736 mailimap_set_add(current_set, item);
3741 if (count >= IMAP_SET_MAX_COUNT) {
3742 ret_list = g_slist_append(ret_list,
3744 current_set = mailimap_set_new_empty();
3751 if (clist_count(current_set->set_list) > 0) {
3752 ret_list = g_slist_append(ret_list,
3756 g_slist_free(sorted_list);
3761 static GSList * imap_get_lep_set_from_msglist(MsgInfoList *msglist)
3763 MsgNumberList *numlist = NULL;
3767 for (cur = msglist; cur != NULL; cur = g_slist_next(cur)) {
3768 MsgInfo *msginfo = (MsgInfo *) cur->data;
3770 numlist = g_slist_append(numlist, GINT_TO_POINTER(msginfo->msgnum));
3772 seq_list = imap_get_lep_set_from_numlist(numlist);
3773 g_slist_free(numlist);
3778 static GSList * imap_uid_list_from_lep(clist * list)
3785 for(iter = clist_begin(list) ; iter != NULL ;
3786 iter = clist_next(iter)) {
3789 puid = clist_content(iter);
3790 result = g_slist_append(result, GINT_TO_POINTER(* puid));
3796 static GSList * imap_uid_list_from_lep_tab(carray * list)
3803 for(i = 0 ; i < carray_count(list) ; i ++) {
3806 puid = carray_get(list, i);
3807 result = g_slist_append(result, GINT_TO_POINTER(* puid));
3813 static MsgInfo *imap_envelope_from_lep(struct imap_fetch_env_info * info,
3816 MsgInfo *msginfo = NULL;
3819 MsgFlags flags = {0, 0};
3821 MSG_SET_TMP_FLAGS(flags, MSG_IMAP);
3822 if (folder_has_parent_of_type(item, F_QUEUE)) {
3823 MSG_SET_TMP_FLAGS(flags, MSG_QUEUED);
3824 } else if (folder_has_parent_of_type(item, F_DRAFT)) {
3825 MSG_SET_TMP_FLAGS(flags, MSG_DRAFT);
3827 flags.perm_flags = info->flags;
3831 msginfo = procheader_parse_str(info->headers, flags, FALSE, FALSE);
3834 msginfo->msgnum = uid;
3835 msginfo->size = size;
3841 static void imap_lep_set_free(GSList *seq_list)
3845 for(cur = seq_list ; cur != NULL ; cur = g_slist_next(cur)) {
3846 struct mailimap_set * imapset;
3848 imapset = cur->data;
3849 mailimap_set_free(imapset);
3851 g_slist_free(seq_list);
3854 static struct mailimap_flag_list * imap_flag_to_lep(IMAPFlags flags)
3856 struct mailimap_flag_list * flag_list;
3858 flag_list = mailimap_flag_list_new_empty();
3860 if (IMAP_IS_SEEN(flags))
3861 mailimap_flag_list_add(flag_list,
3862 mailimap_flag_new_seen());
3863 if (IMAP_IS_ANSWERED(flags))
3864 mailimap_flag_list_add(flag_list,
3865 mailimap_flag_new_answered());
3866 if (IMAP_IS_FLAGGED(flags))
3867 mailimap_flag_list_add(flag_list,
3868 mailimap_flag_new_flagged());
3869 if (IMAP_IS_DELETED(flags))
3870 mailimap_flag_list_add(flag_list,
3871 mailimap_flag_new_deleted());
3872 if (IMAP_IS_DRAFT(flags))
3873 mailimap_flag_list_add(flag_list,
3874 mailimap_flag_new_draft());
3879 guint imap_folder_get_refcnt(Folder *folder)
3881 return ((IMAPFolder *)folder)->refcnt;
3884 void imap_folder_ref(Folder *folder)
3886 ((IMAPFolder *)folder)->refcnt++;
3889 void imap_folder_unref(Folder *folder)
3891 if (((IMAPFolder *)folder)->refcnt > 0)
3892 ((IMAPFolder *)folder)->refcnt--;
3895 #else /* HAVE_LIBETPAN */
3897 static FolderClass imap_class;
3899 static Folder *imap_folder_new (const gchar *name,
3904 static gint imap_create_tree (Folder *folder)
3908 static FolderItem *imap_create_folder (Folder *folder,
3914 static gint imap_rename_folder (Folder *folder,
3921 FolderClass *imap_get_class(void)
3923 if (imap_class.idstr == NULL) {
3924 imap_class.type = F_IMAP;
3925 imap_class.idstr = "imap";
3926 imap_class.uistr = "IMAP4";
3928 imap_class.new_folder = imap_folder_new;
3929 imap_class.create_tree = imap_create_tree;
3930 imap_class.create_folder = imap_create_folder;
3931 imap_class.rename_folder = imap_rename_folder;
3932 /* nothing implemented */