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 static gint imap_auth(IMAPSession *session, const gchar *user, const gchar *pass,
535 /* TODO: implement Automatic mode */
538 case IMAP_AUTH_CRAM_MD5:
539 ok = imap_cmd_login(session, user, pass, "CRAM-MD5");
541 case IMAP_AUTH_LOGIN:
543 ok = imap_cmd_login(session, user, pass, "LOGIN");
546 if (ok == IMAP_SUCCESS)
547 session->authenticated = TRUE;
552 static IMAPSession *imap_session_get(Folder *folder)
554 RemoteFolder *rfolder = REMOTE_FOLDER(folder);
555 IMAPSession *session = NULL;
557 g_return_val_if_fail(folder != NULL, NULL);
558 g_return_val_if_fail(FOLDER_CLASS(folder) == &imap_class, NULL);
559 g_return_val_if_fail(folder->account != NULL, NULL);
561 if (prefs_common.work_offline && !imap_gtk_should_override()) {
565 /* Make sure we have a session */
566 if (rfolder->session != NULL) {
567 session = IMAP_SESSION(rfolder->session);
569 imap_reset_uid_lists(folder);
570 session = imap_session_new(folder, folder->account);
575 /* Make sure session is authenticated */
576 if (!IMAP_SESSION(session)->authenticated)
577 imap_session_authenticate(IMAP_SESSION(session), folder->account);
579 if (!IMAP_SESSION(session)->authenticated) {
580 session_destroy(SESSION(session));
581 rfolder->session = NULL;
586 /* Make sure we have parsed the IMAP namespace */
587 imap_parse_namespace(IMAP_SESSION(session),
588 IMAP_FOLDER(folder));
591 /* I think the point of this code is to avoid sending a
592 * keepalive if we've used the session recently and therefore
593 * think it's still alive. Unfortunately, most of the code
594 * does not yet check for errors on the socket, and so if the
595 * connection drops we don't notice until the timeout expires.
596 * A better solution than sending a NOOP every time would be
597 * for every command to be prepared to retry until it is
598 * successfully sent. -- mbp */
599 if (time(NULL) - SESSION(session)->last_access_time > SESSION_TIMEOUT_INTERVAL) {
600 /* verify that the session is still alive */
601 if (imap_cmd_noop(session) != IMAP_SUCCESS) {
602 /* Check if this is the first try to establish a
603 connection, if yes we don't try to reconnect */
604 if (rfolder->session == NULL) {
605 log_warning(_("Connecting to %s failed"),
606 folder->account->recv_server);
607 session_destroy(SESSION(session));
610 log_warning(_("IMAP4 connection to %s has been"
611 " disconnected. Reconnecting...\n"),
612 folder->account->recv_server);
613 statusbar_print_all(_("IMAP4 connection to %s has been"
614 " disconnected. Reconnecting...\n"),
615 folder->account->recv_server);
616 session_destroy(SESSION(session));
617 /* Clear folders session to make imap_session_get create
618 a new session, because of rfolder->session == NULL
619 it will not try to reconnect again and so avoid an
621 rfolder->session = NULL;
622 session = imap_session_get(folder);
628 rfolder->session = SESSION(session);
630 return IMAP_SESSION(session);
633 static IMAPSession *imap_session_new(Folder * folder,
634 const PrefsAccount *account)
636 IMAPSession *session;
642 /* FIXME: IMAP over SSL only... */
645 port = account->set_imapport ? account->imapport
646 : account->ssl_imap == SSL_TUNNEL ? IMAPS_PORT : IMAP4_PORT;
647 ssl_type = account->ssl_imap;
649 port = account->set_imapport ? account->imapport
654 statusbar_print_all(_("Connecting to IMAP4 server: %s..."), folder->account->recv_server);
655 if (account->set_tunnelcmd) {
656 r = imap_threaded_connect_cmd(folder,
658 account->recv_server,
663 if (ssl_type == SSL_TUNNEL) {
664 r = imap_threaded_connect_ssl(folder,
665 account->recv_server,
671 r = imap_threaded_connect(folder,
672 account->recv_server,
678 if (r == MAILIMAP_NO_ERROR_AUTHENTICATED) {
679 authenticated = TRUE;
681 else if (r == MAILIMAP_NO_ERROR_NON_AUTHENTICATED) {
682 authenticated = FALSE;
685 if(!prefs_common.no_recv_err_panel) {
686 alertpanel_error(_("Can't connect to IMAP4 server: %s:%d"),
687 account->recv_server, port);
693 session = g_new0(IMAPSession, 1);
694 session_init(SESSION(session));
695 SESSION(session)->type = SESSION_IMAP;
696 SESSION(session)->server = g_strdup(account->recv_server);
697 SESSION(session)->sock = NULL;
699 SESSION(session)->destroy = imap_session_destroy;
701 session->capability = NULL;
703 session->authenticated = authenticated;
704 session->mbox = NULL;
705 session->cmd_count = 0;
706 session->folder = folder;
707 IMAP_FOLDER(session->folder)->last_seen_separator = 0;
710 if (account->ssl_imap == SSL_STARTTLS) {
713 ok = imap_cmd_starttls(session);
714 if (ok != IMAP_SUCCESS) {
715 log_warning(_("Can't start TLS session.\n"));
716 session_destroy(SESSION(session));
720 imap_free_capabilities(session);
721 session->authenticated = FALSE;
722 session->uidplus = FALSE;
723 session->cmd_count = 1;
726 log_message("IMAP connection is %s-authenticated\n",
727 (session->authenticated) ? "pre" : "un");
732 static void imap_session_authenticate(IMAPSession *session,
733 const PrefsAccount *account)
737 g_return_if_fail(account->userid != NULL);
739 pass = account->passwd;
742 tmp_pass = input_dialog_query_password(account->recv_server, account->userid);
744 tmp_pass = g_strdup(""); /* allow empty password */
745 Xstrdup_a(pass, tmp_pass, {g_free(tmp_pass); return;});
748 statusbar_print_all(_("Connecting to IMAP4 server %s...\n"),
749 account->recv_server);
750 if (imap_auth(session, account->userid, pass, account->imap_auth_type) != IMAP_SUCCESS) {
751 imap_threaded_disconnect(session->folder);
752 imap_cmd_logout(session);
758 session->authenticated = TRUE;
761 static void imap_session_destroy(Session *session)
763 imap_threaded_disconnect(IMAP_SESSION(session)->folder);
765 imap_free_capabilities(IMAP_SESSION(session));
766 g_free(IMAP_SESSION(session)->mbox);
767 sock_close(session->sock);
768 session->sock = NULL;
771 static gchar *imap_fetch_msg(Folder *folder, FolderItem *item, gint uid)
773 return imap_fetch_msg_full(folder, item, uid, TRUE, TRUE);
776 static guint get_size_with_lfs(MsgInfo *info)
785 fp = procmsg_open_message(info);
789 while (fgets(buf, sizeof (buf), fp) != NULL) {
791 if (!strstr(buf, "\r") && strstr(buf, "\n"))
799 static gchar *imap_fetch_msg_full(Folder *folder, FolderItem *item, gint uid,
800 gboolean headers, gboolean body)
802 gchar *path, *filename;
803 IMAPSession *session;
806 g_return_val_if_fail(folder != NULL, NULL);
807 g_return_val_if_fail(item != NULL, NULL);
812 path = folder_item_get_path(item);
813 if (!is_dir_exist(path))
815 filename = g_strconcat(path, G_DIR_SEPARATOR_S, itos(uid), NULL);
818 if (is_file_exist(filename)) {
819 /* see whether the local file represents the whole message
820 * or not. As the IMAP server reports size with \r chars,
821 * we have to update the local file (UNIX \n only) size */
822 MsgInfo *msginfo = imap_parse_msg(filename, item);
823 MsgInfo *cached = msgcache_get_msg(item->cache,uid);
824 guint have_size = get_size_with_lfs(msginfo);
827 debug_print("message %d has been already %scached (%d/%d).\n", uid,
828 have_size == cached->size ? "fully ":"",
829 have_size, cached->size);
831 if (cached && (cached->size == have_size || !body)) {
832 procmsg_msginfo_free(cached);
833 procmsg_msginfo_free(msginfo);
836 procmsg_msginfo_free(cached);
837 procmsg_msginfo_free(msginfo);
841 session = imap_session_get(folder);
847 debug_print("IMAP fetching messages\n");
848 ok = imap_select(session, IMAP_FOLDER(folder), item->path,
849 NULL, NULL, NULL, NULL, FALSE);
850 if (ok != IMAP_SUCCESS) {
851 g_warning("can't select mailbox %s\n", item->path);
856 debug_print("getting message %d...\n", uid);
857 ok = imap_cmd_fetch(session, (guint32)uid, filename, headers, body);
859 if (ok != IMAP_SUCCESS) {
860 g_warning("can't fetch message %d\n", uid);
868 static gint imap_add_msg(Folder *folder, FolderItem *dest,
869 const gchar *file, MsgFlags *flags)
873 MsgFileInfo fileinfo;
875 g_return_val_if_fail(file != NULL, -1);
877 fileinfo.msginfo = NULL;
878 fileinfo.file = (gchar *)file;
879 fileinfo.flags = flags;
880 file_list.data = &fileinfo;
881 file_list.next = NULL;
883 ret = imap_add_msgs(folder, dest, &file_list, NULL);
887 static gint imap_add_msgs(Folder *folder, FolderItem *dest, GSList *file_list,
891 IMAPSession *session;
892 guint32 last_uid = 0;
894 MsgFileInfo *fileinfo;
898 g_return_val_if_fail(folder != NULL, -1);
899 g_return_val_if_fail(dest != NULL, -1);
900 g_return_val_if_fail(file_list != NULL, -1);
902 session = imap_session_get(folder);
906 destdir = imap_get_real_path(IMAP_FOLDER(folder), dest->path);
908 for (cur = file_list; cur != NULL; cur = cur->next) {
909 IMAPFlags iflags = 0;
912 fileinfo = (MsgFileInfo *)cur->data;
914 if (fileinfo->flags) {
915 if (MSG_IS_MARKED(*fileinfo->flags))
916 iflags |= IMAP_FLAG_FLAGGED;
917 if (MSG_IS_REPLIED(*fileinfo->flags))
918 iflags |= IMAP_FLAG_ANSWERED;
919 if (!MSG_IS_UNREAD(*fileinfo->flags))
920 iflags |= IMAP_FLAG_SEEN;
923 if (folder_has_parent_of_type(dest, F_QUEUE) ||
924 folder_has_parent_of_type(dest, F_OUTBOX) ||
925 folder_has_parent_of_type(dest, F_DRAFT) ||
926 folder_has_parent_of_type(dest, F_TRASH))
927 iflags |= IMAP_FLAG_SEEN;
929 ok = imap_cmd_append(session, destdir, fileinfo->file, iflags,
932 if (ok != IMAP_SUCCESS) {
933 g_warning("can't append message %s\n", fileinfo->file);
938 if (relation != NULL)
939 g_relation_insert(relation, fileinfo->msginfo != NULL ?
940 (gpointer) fileinfo->msginfo : (gpointer) fileinfo,
941 GINT_TO_POINTER(dest->last_num + 1));
942 if (last_uid < new_uid)
951 static gint imap_do_copy_msgs(Folder *folder, FolderItem *dest,
952 MsgInfoList *msglist, GRelation *relation)
956 GSList *seq_list, *cur;
958 IMAPSession *session;
959 gint ok = IMAP_SUCCESS;
960 GRelation *uid_mapping;
963 g_return_val_if_fail(folder != NULL, -1);
964 g_return_val_if_fail(dest != NULL, -1);
965 g_return_val_if_fail(msglist != NULL, -1);
967 session = imap_session_get(folder);
972 msginfo = (MsgInfo *)msglist->data;
974 src = msginfo->folder;
976 g_warning("the src folder is identical to the dest.\n");
980 ok = imap_select(session, IMAP_FOLDER(folder), msginfo->folder->path,
981 NULL, NULL, NULL, NULL, FALSE);
982 if (ok != IMAP_SUCCESS) {
986 destdir = imap_get_real_path(IMAP_FOLDER(folder), dest->path);
987 seq_list = imap_get_lep_set_from_msglist(msglist);
988 uid_mapping = g_relation_new(2);
989 g_relation_index(uid_mapping, 0, g_direct_hash, g_direct_equal);
991 for (cur = seq_list; cur != NULL; cur = g_slist_next(cur)) {
992 struct mailimap_set * seq_set;
996 debug_print("Copying messages from %s to %s ...\n",
999 ok = imap_cmd_copy(session, seq_set, destdir, uid_mapping);
1000 if (ok != IMAP_SUCCESS) {
1001 g_relation_destroy(uid_mapping);
1002 imap_lep_set_free(seq_list);
1007 for (cur = msglist; cur != NULL; cur = g_slist_next(cur)) {
1008 MsgInfo *msginfo = (MsgInfo *)cur->data;
1011 tuples = g_relation_select(uid_mapping,
1012 GINT_TO_POINTER(msginfo->msgnum),
1014 if (tuples->len > 0) {
1015 gint num = GPOINTER_TO_INT(g_tuples_index(tuples, 0, 1));
1016 g_relation_insert(relation, msginfo,
1017 GPOINTER_TO_INT(num));
1021 g_relation_insert(relation, msginfo,
1022 GPOINTER_TO_INT(0));
1023 g_tuples_destroy(tuples);
1026 g_relation_destroy(uid_mapping);
1027 imap_lep_set_free(seq_list);
1031 IMAP_FOLDER_ITEM(dest)->lastuid = 0;
1032 IMAP_FOLDER_ITEM(dest)->uid_next = 0;
1033 g_slist_free(IMAP_FOLDER_ITEM(dest)->uid_list);
1034 IMAP_FOLDER_ITEM(dest)->uid_list = NULL;
1036 if (ok == IMAP_SUCCESS)
1042 static gint imap_copy_msg(Folder *folder, FolderItem *dest, MsgInfo *msginfo)
1046 g_return_val_if_fail(msginfo != NULL, -1);
1048 msglist.data = msginfo;
1049 msglist.next = NULL;
1051 return imap_copy_msgs(folder, dest, &msglist, NULL);
1054 static gint imap_copy_msgs(Folder *folder, FolderItem *dest,
1055 MsgInfoList *msglist, GRelation *relation)
1061 g_return_val_if_fail(folder != NULL, -1);
1062 g_return_val_if_fail(dest != NULL, -1);
1063 g_return_val_if_fail(msglist != NULL, -1);
1065 msginfo = (MsgInfo *)msglist->data;
1066 g_return_val_if_fail(msginfo->folder != NULL, -1);
1068 if (folder == msginfo->folder->folder) {
1069 ret = imap_do_copy_msgs(folder, dest, msglist, relation);
1073 file_list = procmsg_get_message_file_list(msglist);
1074 g_return_val_if_fail(file_list != NULL, -1);
1076 ret = imap_add_msgs(folder, dest, file_list, relation);
1078 procmsg_message_file_list_free(file_list);
1084 static gint imap_do_remove_msgs(Folder *folder, FolderItem *dest,
1085 MsgInfoList *msglist, GRelation *relation)
1088 GSList *seq_list = NULL, *cur;
1090 IMAPSession *session;
1091 gint ok = IMAP_SUCCESS;
1092 GRelation *uid_mapping;
1094 g_return_val_if_fail(folder != NULL, -1);
1095 g_return_val_if_fail(dest != NULL, -1);
1096 g_return_val_if_fail(msglist != NULL, -1);
1098 session = imap_session_get(folder);
1102 msginfo = (MsgInfo *)msglist->data;
1104 ok = imap_select(session, IMAP_FOLDER(folder), msginfo->folder->path,
1105 NULL, NULL, NULL, NULL, FALSE);
1106 if (ok != IMAP_SUCCESS) {
1110 destdir = imap_get_real_path(IMAP_FOLDER(folder), dest->path);
1111 for (cur = msglist; cur; cur = cur->next) {
1112 msginfo = (MsgInfo *)cur->data;
1113 seq_list = g_slist_append(seq_list, GINT_TO_POINTER(msginfo->msgnum));
1116 uid_mapping = g_relation_new(2);
1117 g_relation_index(uid_mapping, 0, g_direct_hash, g_direct_equal);
1119 ok = imap_set_message_flags
1120 (IMAP_SESSION(REMOTE_FOLDER(folder)->session),
1121 seq_list, IMAP_FLAG_DELETED, TRUE);
1122 if (ok != IMAP_SUCCESS) {
1123 log_warning(_("can't set deleted flags\n"));
1126 ok = imap_cmd_expunge(session);
1127 if (ok != IMAP_SUCCESS) {
1128 log_warning(_("can't expunge\n"));
1132 g_relation_destroy(uid_mapping);
1133 g_slist_free(seq_list);
1137 if (ok == IMAP_SUCCESS)
1143 static gint imap_remove_msgs(Folder *folder, FolderItem *dest,
1144 MsgInfoList *msglist, GRelation *relation)
1148 g_return_val_if_fail(folder != NULL, -1);
1149 g_return_val_if_fail(dest != NULL, -1);
1150 if (msglist == NULL)
1153 msginfo = (MsgInfo *)msglist->data;
1154 g_return_val_if_fail(msginfo->folder != NULL, -1);
1156 return imap_do_remove_msgs(folder, dest, msglist, relation);
1159 static gint imap_remove_all_msg(Folder *folder, FolderItem *item)
1161 GSList *list = folder_item_get_msg_list(item);
1162 gint res = imap_remove_msgs(folder, item, list, NULL);
1163 procmsg_msg_list_free(list);
1167 static gboolean imap_is_msg_changed(Folder *folder, FolderItem *item,
1170 /* TODO: properly implement this method */
1174 static gint imap_close(Folder *folder, FolderItem *item)
1179 static gint imap_scan_tree(Folder *folder)
1181 FolderItem *item = NULL;
1182 IMAPSession *session;
1183 gchar *root_folder = NULL;
1185 g_return_val_if_fail(folder != NULL, -1);
1186 g_return_val_if_fail(folder->account != NULL, -1);
1188 session = imap_session_get(folder);
1190 if (!folder->node) {
1191 folder_tree_destroy(folder);
1192 item = folder_item_new(folder, folder->name, NULL);
1193 item->folder = folder;
1194 folder->node = item->node = g_node_new(item);
1199 if (folder->account->imap_dir && *folder->account->imap_dir) {
1204 Xstrdup_a(root_folder, folder->account->imap_dir, {return -1;});
1205 extract_quote(root_folder, '"');
1206 subst_char(root_folder,
1207 imap_get_path_separator(IMAP_FOLDER(folder),
1210 strtailchomp(root_folder, '/');
1211 real_path = imap_get_real_path
1212 (IMAP_FOLDER(folder), root_folder);
1213 debug_print("IMAP root directory: %s\n", real_path);
1215 /* check if root directory exist */
1217 r = imap_threaded_list(session->folder, "", real_path,
1219 if ((r != MAILIMAP_NO_ERROR) || (clist_count(lep_list) == 0)) {
1220 if (!folder->node) {
1221 item = folder_item_new(folder, folder->name, NULL);
1222 item->folder = folder;
1223 folder->node = item->node = g_node_new(item);
1228 mailimap_list_result_free(lep_list);
1234 item = FOLDER_ITEM(folder->node->data);
1235 if (!item || ((item->path || root_folder) &&
1236 strcmp2(item->path, root_folder) != 0)) {
1237 folder_tree_destroy(folder);
1238 item = folder_item_new(folder, folder->name, root_folder);
1239 item->folder = folder;
1240 folder->node = item->node = g_node_new(item);
1243 imap_scan_tree_recursive(session, FOLDER_ITEM(folder->node->data));
1244 imap_create_missing_folders(folder);
1249 static gint imap_scan_tree_recursive(IMAPSession *session, FolderItem *item)
1252 IMAPFolder *imapfolder;
1253 FolderItem *new_item;
1254 GSList *item_list, *cur;
1257 gchar *wildcard_path;
1263 g_return_val_if_fail(item != NULL, -1);
1264 g_return_val_if_fail(item->folder != NULL, -1);
1265 g_return_val_if_fail(item->no_sub == FALSE, -1);
1267 folder = item->folder;
1268 imapfolder = IMAP_FOLDER(folder);
1270 separator = imap_get_path_separator(imapfolder, item->path);
1272 if (folder->ui_func)
1273 folder->ui_func(folder, item, folder->ui_func_data);
1276 wildcard[0] = separator;
1279 real_path = imap_get_real_path(imapfolder, item->path);
1283 real_path = g_strdup("");
1286 Xstrcat_a(wildcard_path, real_path, wildcard,
1287 {g_free(real_path); return IMAP_ERROR;});
1289 r = imap_threaded_list(folder, "", wildcard_path, &lep_list);
1290 if (r != MAILIMAP_NO_ERROR) {
1294 item_list = imap_list_from_lep(imapfolder,
1295 lep_list, real_path);
1296 mailimap_list_result_free(lep_list);
1301 node = item->node->children;
1302 while (node != NULL) {
1303 FolderItem *old_item = FOLDER_ITEM(node->data);
1304 GNode *next = node->next;
1307 for (cur = item_list; cur != NULL; cur = cur->next) {
1308 FolderItem *cur_item = FOLDER_ITEM(cur->data);
1309 if (!strcmp2(old_item->path, cur_item->path)) {
1310 new_item = cur_item;
1315 debug_print("folder '%s' not found. removing...\n",
1317 folder_item_remove(old_item);
1319 old_item->no_sub = new_item->no_sub;
1320 old_item->no_select = new_item->no_select;
1321 if (old_item->no_sub == TRUE && node->children) {
1322 debug_print("folder '%s' doesn't have "
1323 "subfolders. removing...\n",
1325 folder_item_remove_children(old_item);
1332 for (cur = item_list; cur != NULL; cur = cur->next) {
1333 FolderItem *cur_item = FOLDER_ITEM(cur->data);
1335 for (node = item->node->children; node != NULL;
1336 node = node->next) {
1337 if (!strcmp2(FOLDER_ITEM(node->data)->path,
1339 new_item = FOLDER_ITEM(node->data);
1340 folder_item_destroy(cur_item);
1346 new_item = cur_item;
1347 debug_print("new folder '%s' found.\n", new_item->path);
1348 folder_item_append(item, new_item);
1351 if (!strcmp(new_item->path, "INBOX")) {
1352 new_item->stype = F_INBOX;
1353 folder->inbox = new_item;
1354 } else if (!folder_item_parent(item) || item->stype == F_INBOX) {
1357 base = g_path_get_basename(new_item->path);
1359 if (!folder->outbox && !g_ascii_strcasecmp(base, "Sent")) {
1360 new_item->stype = F_OUTBOX;
1361 folder->outbox = new_item;
1362 } else if (!folder->draft && !g_ascii_strcasecmp(base, "Drafts")) {
1363 new_item->stype = F_DRAFT;
1364 folder->draft = new_item;
1365 } else if (!folder->queue && !g_ascii_strcasecmp(base, "Queue")) {
1366 new_item->stype = F_QUEUE;
1367 folder->queue = new_item;
1368 } else if (!folder->trash && !g_ascii_strcasecmp(base, "Trash")) {
1369 new_item->stype = F_TRASH;
1370 folder->trash = new_item;
1375 if (new_item->no_sub == FALSE)
1376 imap_scan_tree_recursive(session, new_item);
1379 g_slist_free(item_list);
1381 return IMAP_SUCCESS;
1384 static gint imap_create_tree(Folder *folder)
1386 g_return_val_if_fail(folder != NULL, -1);
1387 g_return_val_if_fail(folder->node != NULL, -1);
1388 g_return_val_if_fail(folder->node->data != NULL, -1);
1389 g_return_val_if_fail(folder->account != NULL, -1);
1391 imap_scan_tree(folder);
1392 imap_create_missing_folders(folder);
1397 static void imap_create_missing_folders(Folder *folder)
1399 g_return_if_fail(folder != NULL);
1402 folder->inbox = imap_create_special_folder
1403 (folder, F_INBOX, "INBOX");
1405 folder->trash = imap_create_special_folder
1406 (folder, F_TRASH, "Trash");
1408 folder->queue = imap_create_special_folder
1409 (folder, F_QUEUE, "Queue");
1410 if (!folder->outbox)
1411 folder->outbox = imap_create_special_folder
1412 (folder, F_OUTBOX, "Sent");
1414 folder->draft = imap_create_special_folder
1415 (folder, F_DRAFT, "Drafts");
1418 static FolderItem *imap_create_special_folder(Folder *folder,
1419 SpecialFolderItemType stype,
1423 FolderItem *new_item;
1425 g_return_val_if_fail(folder != NULL, NULL);
1426 g_return_val_if_fail(folder->node != NULL, NULL);
1427 g_return_val_if_fail(folder->node->data != NULL, NULL);
1428 g_return_val_if_fail(folder->account != NULL, NULL);
1429 g_return_val_if_fail(name != NULL, NULL);
1431 item = FOLDER_ITEM(folder->node->data);
1432 new_item = imap_create_folder(folder, item, name);
1435 g_warning("Can't create '%s'\n", name);
1436 if (!folder->inbox) return NULL;
1438 new_item = imap_create_folder(folder, folder->inbox, name);
1440 g_warning("Can't create '%s' under INBOX\n", name);
1442 new_item->stype = stype;
1444 new_item->stype = stype;
1449 static gchar *imap_folder_get_path(Folder *folder)
1453 g_return_val_if_fail(folder != NULL, NULL);
1454 g_return_val_if_fail(folder->account != NULL, NULL);
1456 folder_path = g_strconcat(get_imap_cache_dir(),
1458 folder->account->recv_server,
1460 folder->account->userid,
1466 static gchar *imap_item_get_path(Folder *folder, FolderItem *item)
1468 gchar *folder_path, *path;
1470 g_return_val_if_fail(folder != NULL, NULL);
1471 g_return_val_if_fail(item != NULL, NULL);
1472 folder_path = imap_folder_get_path(folder);
1474 g_return_val_if_fail(folder_path != NULL, NULL);
1475 if (folder_path[0] == G_DIR_SEPARATOR) {
1477 path = g_strconcat(folder_path, G_DIR_SEPARATOR_S,
1480 path = g_strdup(folder_path);
1483 path = g_strconcat(get_home_dir(), G_DIR_SEPARATOR_S,
1484 folder_path, G_DIR_SEPARATOR_S,
1487 path = g_strconcat(get_home_dir(), G_DIR_SEPARATOR_S,
1490 g_free(folder_path);
1495 static FolderItem *imap_create_folder(Folder *folder, FolderItem *parent,
1498 gchar *dirpath, *imap_path;
1499 IMAPSession *session;
1500 FolderItem *new_item;
1506 g_return_val_if_fail(folder != NULL, NULL);
1507 g_return_val_if_fail(folder->account != NULL, NULL);
1508 g_return_val_if_fail(parent != NULL, NULL);
1509 g_return_val_if_fail(name != NULL, NULL);
1511 session = imap_session_get(folder);
1516 if (!folder_item_parent(parent) && strcmp(name, "INBOX") == 0)
1517 dirpath = g_strdup(name);
1518 else if (parent->path)
1519 dirpath = g_strconcat(parent->path, "/", name, NULL);
1520 else if ((p = strchr(name, '/')) != NULL && *(p + 1) != '\0')
1521 dirpath = g_strdup(name);
1522 else if (folder->account->imap_dir && *folder->account->imap_dir) {
1525 Xstrdup_a(imap_dir, folder->account->imap_dir, {return NULL;});
1526 strtailchomp(imap_dir, '/');
1527 dirpath = g_strconcat(imap_dir, "/", name, NULL);
1529 dirpath = g_strdup(name);
1531 /* keep trailing directory separator to create a folder that contains
1533 imap_path = imap_utf8_to_modified_utf7(dirpath);
1534 strtailchomp(dirpath, '/');
1535 Xstrdup_a(new_name, name, {
1539 strtailchomp(new_name, '/');
1540 separator = imap_get_path_separator(IMAP_FOLDER(folder), imap_path);
1541 imap_path_separator_subst(imap_path, separator);
1542 subst_char(new_name, '/', separator);
1544 if (strcmp(name, "INBOX") != 0) {
1546 gboolean exist = FALSE;
1550 argbuf = g_ptr_array_new();
1551 r = imap_threaded_list(folder, "", imap_path, &lep_list);
1552 if (r != MAILIMAP_NO_ERROR) {
1553 log_warning(_("can't create mailbox: LIST failed\n"));
1556 ptr_array_free_strings(argbuf);
1557 g_ptr_array_free(argbuf, TRUE);
1561 if (clist_count(lep_list) > 0)
1565 ok = imap_cmd_create(session, imap_path);
1566 if (ok != IMAP_SUCCESS) {
1567 log_warning(_("can't create mailbox\n"));
1575 new_item = folder_item_new(folder, new_name, dirpath);
1576 folder_item_append(parent, new_item);
1580 dirpath = folder_item_get_path(new_item);
1581 if (!is_dir_exist(dirpath))
1582 make_dir_hier(dirpath);
1588 static gint imap_rename_folder(Folder *folder, FolderItem *item,
1593 gchar *real_oldpath;
1594 gchar *real_newpath;
1596 gchar *old_cache_dir;
1597 gchar *new_cache_dir;
1598 IMAPSession *session;
1601 gint exists, recent, unseen;
1602 guint32 uid_validity;
1604 g_return_val_if_fail(folder != NULL, -1);
1605 g_return_val_if_fail(item != NULL, -1);
1606 g_return_val_if_fail(item->path != NULL, -1);
1607 g_return_val_if_fail(name != NULL, -1);
1609 if (strchr(name, imap_get_path_separator(IMAP_FOLDER(folder), item->path)) != NULL) {
1610 g_warning(_("New folder name must not contain the namespace "
1615 session = imap_session_get(folder);
1619 real_oldpath = imap_get_real_path(IMAP_FOLDER(folder), item->path);
1621 g_free(session->mbox);
1622 session->mbox = NULL;
1623 ok = imap_cmd_examine(session, "INBOX",
1624 &exists, &recent, &unseen, &uid_validity, FALSE);
1625 if (ok != IMAP_SUCCESS) {
1626 g_free(real_oldpath);
1630 separator = imap_get_path_separator(IMAP_FOLDER(folder), item->path);
1631 if (strchr(item->path, G_DIR_SEPARATOR)) {
1632 dirpath = g_path_get_dirname(item->path);
1633 newpath = g_strconcat(dirpath, G_DIR_SEPARATOR_S, name, NULL);
1636 newpath = g_strdup(name);
1638 real_newpath = imap_utf8_to_modified_utf7(newpath);
1639 imap_path_separator_subst(real_newpath, separator);
1641 ok = imap_cmd_rename(session, real_oldpath, real_newpath);
1642 if (ok != IMAP_SUCCESS) {
1643 log_warning(_("can't rename mailbox: %s to %s\n"),
1644 real_oldpath, real_newpath);
1645 g_free(real_oldpath);
1647 g_free(real_newpath);
1652 item->name = g_strdup(name);
1654 old_cache_dir = folder_item_get_path(item);
1656 paths[0] = g_strdup(item->path);
1658 g_node_traverse(item->node, G_PRE_ORDER, G_TRAVERSE_ALL, -1,
1659 imap_rename_folder_func, paths);
1661 if (is_dir_exist(old_cache_dir)) {
1662 new_cache_dir = folder_item_get_path(item);
1663 if (rename(old_cache_dir, new_cache_dir) < 0) {
1664 FILE_OP_ERROR(old_cache_dir, "rename");
1666 g_free(new_cache_dir);
1669 g_free(old_cache_dir);
1672 g_free(real_oldpath);
1673 g_free(real_newpath);
1678 static gint imap_remove_folder_real(Folder *folder, FolderItem *item)
1681 IMAPSession *session;
1684 gint exists, recent, unseen;
1685 guint32 uid_validity;
1687 g_return_val_if_fail(folder != NULL, -1);
1688 g_return_val_if_fail(item != NULL, -1);
1689 g_return_val_if_fail(item->path != NULL, -1);
1691 session = imap_session_get(folder);
1695 path = imap_get_real_path(IMAP_FOLDER(folder), item->path);
1697 ok = imap_cmd_examine(session, "INBOX",
1698 &exists, &recent, &unseen, &uid_validity, FALSE);
1699 if (ok != IMAP_SUCCESS) {
1704 ok = imap_cmd_delete(session, path);
1705 if (ok != IMAP_SUCCESS) {
1706 log_warning(_("can't delete mailbox\n"));
1712 cache_dir = folder_item_get_path(item);
1713 if (is_dir_exist(cache_dir) && remove_dir_recursive(cache_dir) < 0)
1714 g_warning("can't remove directory '%s'\n", cache_dir);
1716 folder_item_remove(item);
1721 static gint imap_remove_folder(Folder *folder, FolderItem *item)
1725 g_return_val_if_fail(item != NULL, -1);
1726 g_return_val_if_fail(item->folder != NULL, -1);
1727 g_return_val_if_fail(item->node != NULL, -1);
1729 node = item->node->children;
1730 while (node != NULL) {
1732 if (imap_remove_folder(folder, FOLDER_ITEM(node->data)) < 0)
1736 debug_print("IMAP removing %s\n", item->path);
1738 if (imap_remove_all_msg(folder, item) < 0)
1740 return imap_remove_folder_real(folder, item);
1743 typedef struct _uncached_data {
1744 IMAPSession *session;
1746 MsgNumberList *numlist;
1752 static void *imap_get_uncached_messages_thread(void *data)
1754 uncached_data *stuff = (uncached_data *)data;
1755 IMAPSession *session = stuff->session;
1756 FolderItem *item = stuff->item;
1757 MsgNumberList *numlist = stuff->numlist;
1759 GSList *newlist = NULL;
1760 GSList *llast = NULL;
1761 GSList *seq_list, *cur;
1763 debug_print("uncached_messages\n");
1765 if (session == NULL || item == NULL || item->folder == NULL
1766 || FOLDER_CLASS(item->folder) != &imap_class) {
1771 seq_list = imap_get_lep_set_from_numlist(numlist);
1772 debug_print("get msgs info\n");
1773 for (cur = seq_list; cur != NULL; cur = g_slist_next(cur)) {
1774 struct mailimap_set * imapset;
1780 imapset = cur->data;
1782 r = imap_threaded_fetch_env(session->folder,
1783 imapset, &env_list);
1784 if (r != MAILIMAP_NO_ERROR)
1788 for(i = 0 ; i < carray_count(env_list) ; i ++) {
1789 struct imap_fetch_env_info * info;
1792 info = carray_get(env_list, i);
1793 msginfo = imap_envelope_from_lep(info, item);
1794 msginfo->folder = item;
1796 llast = newlist = g_slist_append(newlist, msginfo);
1798 llast = g_slist_append(llast, msginfo);
1799 llast = llast->next;
1804 imap_fetch_env_free(env_list);
1807 session_set_access_time(SESSION(session));
1812 #define MAX_MSG_NUM 50
1814 static GSList *imap_get_uncached_messages(IMAPSession *session,
1816 MsgNumberList *numlist)
1818 GSList *result = NULL;
1820 uncached_data *data = g_new0(uncached_data, 1);
1825 data->total = g_slist_length(numlist);
1826 debug_print("messages list : %i\n", data->total);
1828 while (cur != NULL) {
1829 GSList * partial_result;
1837 while (count < MAX_MSG_NUM) {
1842 if (newlist == NULL)
1843 llast = newlist = g_slist_append(newlist, p);
1845 llast = g_slist_append(llast, p);
1846 llast = llast->next;
1856 data->session = session;
1858 data->numlist = newlist;
1861 if (prefs_common.work_offline && !imap_gtk_should_override()) {
1867 (GSList *)imap_get_uncached_messages_thread(data);
1869 statusbar_progress_all(data->cur,data->total, 1);
1871 g_slist_free(newlist);
1873 result = g_slist_concat(result, partial_result);
1877 statusbar_progress_all(0,0,0);
1878 statusbar_pop_all();
1883 static void imap_delete_all_cached_messages(FolderItem *item)
1887 g_return_if_fail(item != NULL);
1888 g_return_if_fail(item->folder != NULL);
1889 g_return_if_fail(FOLDER_CLASS(item->folder) == &imap_class);
1891 debug_print("Deleting all cached messages...\n");
1893 dir = folder_item_get_path(item);
1894 if (is_dir_exist(dir))
1895 remove_all_numbered_files(dir);
1898 debug_print("done.\n");
1901 static IMAPNameSpace *imap_find_namespace_from_list(GList *ns_list,
1904 IMAPNameSpace *namespace = NULL;
1905 gchar *tmp_path, *name;
1907 if (!path) path = "";
1909 for (; ns_list != NULL; ns_list = ns_list->next) {
1910 IMAPNameSpace *tmp_ns = ns_list->data;
1912 Xstrcat_a(tmp_path, path, "/", return namespace);
1913 Xstrdup_a(name, tmp_ns->name, return namespace);
1914 if (tmp_ns->separator && tmp_ns->separator != '/') {
1915 subst_char(tmp_path, tmp_ns->separator, '/');
1916 subst_char(name, tmp_ns->separator, '/');
1918 if (strncmp(tmp_path, name, strlen(name)) == 0)
1925 static IMAPNameSpace *imap_find_namespace(IMAPFolder *folder,
1928 IMAPNameSpace *namespace;
1930 g_return_val_if_fail(folder != NULL, NULL);
1932 namespace = imap_find_namespace_from_list(folder->ns_personal, path);
1933 if (namespace) return namespace;
1934 namespace = imap_find_namespace_from_list(folder->ns_others, path);
1935 if (namespace) return namespace;
1936 namespace = imap_find_namespace_from_list(folder->ns_shared, path);
1937 if (namespace) return namespace;
1943 static gchar imap_get_path_separator(IMAPFolder *folder, const gchar *path)
1945 IMAPNameSpace *namespace;
1946 gchar separator = '/';
1948 if (folder->last_seen_separator == 0) {
1950 int r = imap_threaded_list((Folder *)folder, "", "", &lep_list);
1951 if (r != MAILIMAP_NO_ERROR) {
1952 log_warning(_("LIST failed\n"));
1956 if (clist_count(lep_list) > 0) {
1957 clistiter * iter = clist_begin(lep_list);
1958 struct mailimap_mailbox_list * mb;
1959 mb = clist_content(iter);
1961 folder->last_seen_separator = mb->mb_delimiter;
1962 debug_print("got separator: %c\n", folder->last_seen_separator);
1964 mailimap_list_result_free(lep_list);
1967 if (folder->last_seen_separator != 0) {
1968 debug_print("using separator: %c\n", folder->last_seen_separator);
1969 return folder->last_seen_separator;
1972 namespace = imap_find_namespace(folder, path);
1973 if (namespace && namespace->separator)
1974 separator = namespace->separator;
1979 static gchar *imap_get_real_path(IMAPFolder *folder, const gchar *path)
1984 g_return_val_if_fail(folder != NULL, NULL);
1985 g_return_val_if_fail(path != NULL, NULL);
1987 real_path = imap_utf8_to_modified_utf7(path);
1988 separator = imap_get_path_separator(folder, path);
1989 imap_path_separator_subst(real_path, separator);
1994 static gint imap_set_message_flags(IMAPSession *session,
1995 MsgNumberList *numlist,
2003 seq_list = imap_get_lep_set_from_numlist(numlist);
2005 for(cur = seq_list ; cur != NULL ; cur = g_slist_next(cur)) {
2006 struct mailimap_set * imapset;
2008 imapset = cur->data;
2010 ok = imap_cmd_store(session, imapset,
2014 imap_lep_set_free(seq_list);
2016 return IMAP_SUCCESS;
2019 typedef struct _select_data {
2020 IMAPSession *session;
2025 guint32 *uid_validity;
2029 static gint imap_select(IMAPSession *session, IMAPFolder *folder,
2031 gint *exists, gint *recent, gint *unseen,
2032 guint32 *uid_validity, gboolean block)
2036 gint exists_, recent_, unseen_;
2037 guint32 uid_validity_;
2039 if (!exists || !recent || !unseen || !uid_validity) {
2040 if (session->mbox && strcmp(session->mbox, path) == 0)
2041 return IMAP_SUCCESS;
2045 uid_validity = &uid_validity_;
2048 g_free(session->mbox);
2049 session->mbox = NULL;
2051 real_path = imap_get_real_path(folder, path);
2053 ok = imap_cmd_select(session, real_path,
2054 exists, recent, unseen, uid_validity, block);
2055 if (ok != IMAP_SUCCESS)
2056 log_warning(_("can't select folder: %s\n"), real_path);
2058 session->mbox = g_strdup(path);
2059 session->folder_content_changed = FALSE;
2066 static gint imap_status(IMAPSession *session, IMAPFolder *folder,
2068 gint *messages, gint *recent,
2069 guint32 *uid_next, guint32 *uid_validity,
2070 gint *unseen, gboolean block)
2074 struct mailimap_mailbox_data_status * data_status;
2078 real_path = imap_get_real_path(folder, path);
2080 r = imap_threaded_status(FOLDER(folder), real_path, &data_status);
2082 if (r != MAILIMAP_NO_ERROR) {
2083 debug_print("status err %d\n", r);
2087 if (data_status->st_info_list == NULL) {
2088 mailimap_mailbox_data_status_free(data_status);
2089 debug_print("status->st_info_list == NULL\n");
2094 for(iter = clist_begin(data_status->st_info_list) ; iter != NULL ;
2095 iter = clist_next(iter)) {
2096 struct mailimap_status_info * info;
2098 info = clist_content(iter);
2099 switch (info->st_att) {
2100 case MAILIMAP_STATUS_ATT_MESSAGES:
2101 * messages = info->st_value;
2102 got_values |= 1 << 0;
2105 case MAILIMAP_STATUS_ATT_RECENT:
2106 * recent = info->st_value;
2107 got_values |= 1 << 1;
2110 case MAILIMAP_STATUS_ATT_UIDNEXT:
2111 * uid_next = info->st_value;
2112 got_values |= 1 << 2;
2115 case MAILIMAP_STATUS_ATT_UIDVALIDITY:
2116 * uid_validity = info->st_value;
2117 got_values |= 1 << 3;
2120 case MAILIMAP_STATUS_ATT_UNSEEN:
2121 * unseen = info->st_value;
2122 got_values |= 1 << 4;
2126 mailimap_mailbox_data_status_free(data_status);
2128 if (got_values != ((1 << 4) + (1 << 3) +
2129 (1 << 2) + (1 << 1) + (1 << 0))) {
2130 debug_print("status: incomplete values received (%d)\n", got_values);
2133 return IMAP_SUCCESS;
2136 static void imap_free_capabilities(IMAPSession *session)
2138 g_strfreev(session->capability);
2139 session->capability = NULL;
2142 /* low-level IMAP4rev1 commands */
2145 static gint imap_cmd_authenticate(IMAPSession *session, const gchar *user,
2146 const gchar *pass, IMAPAuthType type)
2153 gchar hexdigest[33];
2157 auth_type = "CRAM-MD5";
2159 imap_gen_send(session, "AUTHENTICATE %s", auth_type);
2160 ok = imap_gen_recv(session, &buf);
2161 if (ok != IMAP_SUCCESS || buf[0] != '+' || buf[1] != ' ') {
2166 challenge = g_malloc(strlen(buf + 2) + 1);
2167 challenge_len = base64_decode(challenge, buf + 2, -1);
2168 challenge[challenge_len] = '\0';
2171 md5_hex_hmac(hexdigest, challenge, challenge_len, pass, strlen(pass));
2174 response = g_strdup_printf("%s %s", user, hexdigest);
2175 response64 = g_malloc((strlen(response) + 3) * 2 + 1);
2176 base64_encode(response64, response, strlen(response));
2179 sock_puts(SESSION(session)->sock, response64);
2180 ok = imap_cmd_ok(session, NULL);
2181 if (ok != IMAP_SUCCESS)
2182 log_warning(_("IMAP4 authentication failed.\n"));
2188 static gint imap_cmd_login(IMAPSession *session,
2189 const gchar *user, const gchar *pass,
2194 static time_t last_login_err = 0;
2196 log_print("IMAP4> Logging in to %s\n", SESSION(session)->server);
2197 r = imap_threaded_login(session->folder, user, pass, type);
2198 if (r != MAILIMAP_NO_ERROR) {
2199 gchar *ext_info = NULL;
2201 if (strcmp(type, "CRAM-MD5") == 0) {
2202 ext_info = _("\n\nCRAM-MD5 logins only work if libetpan has been "
2203 "compiled with SASL support and the "
2204 "CRAM-MD5 SASL plugin is installed.");
2209 if (time(NULL) - last_login_err > 10) {
2210 alertpanel_error(_("Connection to %s failed: login refused.%s"),
2211 SESSION(session)->server, ext_info);
2213 last_login_err = time(NULL);
2214 log_error("IMAP4< Error logging in to %s\n",
2215 SESSION(session)->server);
2223 static gint imap_cmd_logout(IMAPSession *session)
2225 imap_threaded_disconnect(session->folder);
2227 return IMAP_SUCCESS;
2230 static gint imap_cmd_noop(IMAPSession *session)
2233 unsigned int exists;
2235 r = imap_threaded_noop(session->folder, &exists);
2236 if (r != MAILIMAP_NO_ERROR) {
2237 debug_print("noop err %d\n", r);
2240 session->exists = exists;
2241 session_set_access_time(SESSION(session));
2243 return IMAP_SUCCESS;
2247 static gint imap_cmd_starttls(IMAPSession *session)
2251 r = imap_threaded_starttls(session->folder);
2252 if (r != MAILIMAP_NO_ERROR) {
2253 debug_print("starttls err %d\n", r);
2256 return IMAP_SUCCESS;
2260 static gint imap_cmd_select(IMAPSession *session, const gchar *folder,
2261 gint *exists, gint *recent, gint *unseen,
2262 guint32 *uid_validity, gboolean block)
2266 r = imap_threaded_select(session->folder, folder,
2267 exists, recent, unseen, uid_validity);
2268 if (r != MAILIMAP_NO_ERROR) {
2269 debug_print("select err %d\n", r);
2272 return IMAP_SUCCESS;
2275 static gint imap_cmd_examine(IMAPSession *session, const gchar *folder,
2276 gint *exists, gint *recent, gint *unseen,
2277 guint32 *uid_validity, gboolean block)
2281 r = imap_threaded_examine(session->folder, folder,
2282 exists, recent, unseen, uid_validity);
2283 if (r != MAILIMAP_NO_ERROR) {
2284 debug_print("examine err %d\n", r);
2288 return IMAP_SUCCESS;
2291 static gint imap_cmd_create(IMAPSession *session, const gchar *folder)
2295 r = imap_threaded_create(session->folder, folder);
2296 if (r != MAILIMAP_NO_ERROR) {
2301 return IMAP_SUCCESS;
2304 static gint imap_cmd_rename(IMAPSession *session, const gchar *old_folder,
2305 const gchar *new_folder)
2309 r = imap_threaded_rename(session->folder, old_folder,
2311 if (r != MAILIMAP_NO_ERROR) {
2316 return IMAP_SUCCESS;
2319 static gint imap_cmd_delete(IMAPSession *session, const gchar *folder)
2324 r = imap_threaded_delete(session->folder, folder);
2325 if (r != MAILIMAP_NO_ERROR) {
2330 return IMAP_SUCCESS;
2333 typedef struct _fetch_data {
2334 IMAPSession *session;
2336 const gchar *filename;
2342 static void *imap_cmd_fetch_thread(void *data)
2344 fetch_data *stuff = (fetch_data *)data;
2345 IMAPSession *session = stuff->session;
2346 guint32 uid = stuff->uid;
2347 const gchar *filename = stuff->filename;
2351 r = imap_threaded_fetch_content(session->folder,
2355 r = imap_threaded_fetch_content(session->folder,
2358 if (r != MAILIMAP_NO_ERROR) {
2359 debug_print("fetch err %d\n", r);
2360 return GINT_TO_POINTER(IMAP_ERROR);
2362 return GINT_TO_POINTER(IMAP_SUCCESS);
2365 static gint imap_cmd_fetch(IMAPSession *session, guint32 uid,
2366 const gchar *filename, gboolean headers,
2369 fetch_data *data = g_new0(fetch_data, 1);
2372 data->session = session;
2374 data->filename = filename;
2375 data->headers = headers;
2378 if (prefs_common.work_offline && !imap_gtk_should_override()) {
2383 result = GPOINTER_TO_INT(imap_cmd_fetch_thread(data));
2389 static gint imap_cmd_append(IMAPSession *session, const gchar *destfolder,
2390 const gchar *file, IMAPFlags flags,
2393 struct mailimap_flag_list * flag_list;
2396 g_return_val_if_fail(file != NULL, IMAP_ERROR);
2398 flag_list = imap_flag_to_lep(flags);
2399 r = imap_threaded_append(session->folder, destfolder,
2402 if (new_uid != NULL)
2405 if (r != MAILIMAP_NO_ERROR) {
2406 debug_print("append err %d\n", r);
2409 return IMAP_SUCCESS;
2412 static gint imap_cmd_copy(IMAPSession *session, struct mailimap_set * set,
2413 const gchar *destfolder, GRelation *uid_mapping)
2417 g_return_val_if_fail(session != NULL, IMAP_ERROR);
2418 g_return_val_if_fail(set != NULL, IMAP_ERROR);
2419 g_return_val_if_fail(destfolder != NULL, IMAP_ERROR);
2421 r = imap_threaded_copy(session->folder, set, destfolder);
2422 if (r != MAILIMAP_NO_ERROR) {
2427 return IMAP_SUCCESS;
2430 static gint imap_cmd_store(IMAPSession *session, struct mailimap_set * set,
2431 IMAPFlags flags, int do_add)
2434 struct mailimap_flag_list * flag_list;
2435 struct mailimap_store_att_flags * store_att_flags;
2437 flag_list = imap_flag_to_lep(flags);
2441 mailimap_store_att_flags_new_add_flags_silent(flag_list);
2444 mailimap_store_att_flags_new_remove_flags_silent(flag_list);
2446 r = imap_threaded_store(session->folder, set, store_att_flags);
2447 if (r != MAILIMAP_NO_ERROR) {
2452 return IMAP_SUCCESS;
2455 static gint imap_cmd_expunge(IMAPSession *session)
2459 if (prefs_common.work_offline && !imap_gtk_should_override()) {
2463 r = imap_threaded_expunge(session->folder);
2464 if (r != MAILIMAP_NO_ERROR) {
2469 return IMAP_SUCCESS;
2472 static void imap_path_separator_subst(gchar *str, gchar separator)
2475 gboolean in_escape = FALSE;
2477 if (!separator || separator == '/') return;
2479 for (p = str; *p != '\0'; p++) {
2480 if (*p == '/' && !in_escape)
2482 else if (*p == '&' && *(p + 1) != '-' && !in_escape)
2484 else if (*p == '-' && in_escape)
2489 static gchar *imap_modified_utf7_to_utf8(const gchar *mutf7_str)
2491 static iconv_t cd = (iconv_t)-1;
2492 static gboolean iconv_ok = TRUE;
2495 size_t norm_utf7_len;
2497 gchar *to_str, *to_p;
2499 gboolean in_escape = FALSE;
2501 if (!iconv_ok) return g_strdup(mutf7_str);
2503 if (cd == (iconv_t)-1) {
2504 cd = iconv_open(CS_INTERNAL, CS_UTF_7);
2505 if (cd == (iconv_t)-1) {
2506 g_warning("iconv cannot convert UTF-7 to %s\n",
2509 return g_strdup(mutf7_str);
2513 /* modified UTF-7 to normal UTF-7 conversion */
2514 norm_utf7 = g_string_new(NULL);
2516 for (p = mutf7_str; *p != '\0'; p++) {
2517 /* replace: '&' -> '+',
2519 escaped ',' -> '/' */
2520 if (!in_escape && *p == '&') {
2521 if (*(p + 1) != '-') {
2522 g_string_append_c(norm_utf7, '+');
2525 g_string_append_c(norm_utf7, '&');
2528 } else if (in_escape && *p == ',') {
2529 g_string_append_c(norm_utf7, '/');
2530 } else if (in_escape && *p == '-') {
2531 g_string_append_c(norm_utf7, '-');
2534 g_string_append_c(norm_utf7, *p);
2538 norm_utf7_p = norm_utf7->str;
2539 norm_utf7_len = norm_utf7->len;
2540 to_len = strlen(mutf7_str) * 5;
2541 to_p = to_str = g_malloc(to_len + 1);
2543 if (iconv(cd, (ICONV_CONST gchar **)&norm_utf7_p, &norm_utf7_len,
2544 &to_p, &to_len) == -1) {
2545 g_warning(_("iconv cannot convert UTF-7 to %s\n"),
2546 conv_get_locale_charset_str());
2547 g_string_free(norm_utf7, TRUE);
2549 return g_strdup(mutf7_str);
2552 /* second iconv() call for flushing */
2553 iconv(cd, NULL, NULL, &to_p, &to_len);
2554 g_string_free(norm_utf7, TRUE);
2560 static gchar *imap_utf8_to_modified_utf7(const gchar *from)
2562 static iconv_t cd = (iconv_t)-1;
2563 static gboolean iconv_ok = TRUE;
2564 gchar *norm_utf7, *norm_utf7_p;
2565 size_t from_len, norm_utf7_len;
2567 gchar *from_tmp, *to, *p;
2568 gboolean in_escape = FALSE;
2570 if (!iconv_ok) return g_strdup(from);
2572 if (cd == (iconv_t)-1) {
2573 cd = iconv_open(CS_UTF_7, CS_INTERNAL);
2574 if (cd == (iconv_t)-1) {
2575 g_warning(_("iconv cannot convert %s to UTF-7\n"),
2578 return g_strdup(from);
2582 /* UTF-8 to normal UTF-7 conversion */
2583 Xstrdup_a(from_tmp, from, return g_strdup(from));
2584 from_len = strlen(from);
2585 norm_utf7_len = from_len * 5;
2586 Xalloca(norm_utf7, norm_utf7_len + 1, return g_strdup(from));
2587 norm_utf7_p = norm_utf7;
2589 #define IS_PRINT(ch) (isprint(ch) && IS_ASCII(ch))
2591 while (from_len > 0) {
2592 if (*from_tmp == '+') {
2593 *norm_utf7_p++ = '+';
2594 *norm_utf7_p++ = '-';
2598 } else if (IS_PRINT(*(guchar *)from_tmp)) {
2599 /* printable ascii char */
2600 *norm_utf7_p = *from_tmp;
2606 size_t conv_len = 0;
2608 /* unprintable char: convert to UTF-7 */
2610 while (!IS_PRINT(*(guchar *)p) && conv_len < from_len) {
2611 conv_len += g_utf8_skip[*(guchar *)p];
2612 p += g_utf8_skip[*(guchar *)p];
2615 from_len -= conv_len;
2616 if (iconv(cd, (ICONV_CONST gchar **)&from_tmp,
2618 &norm_utf7_p, &norm_utf7_len) == -1) {
2619 g_warning(_("iconv cannot convert UTF-8 to UTF-7\n"));
2620 return g_strdup(from);
2623 /* second iconv() call for flushing */
2624 iconv(cd, NULL, NULL, &norm_utf7_p, &norm_utf7_len);
2630 *norm_utf7_p = '\0';
2631 to_str = g_string_new(NULL);
2632 for (p = norm_utf7; p < norm_utf7_p; p++) {
2633 /* replace: '&' -> "&-",
2636 BASE64 '/' -> ',' */
2637 if (!in_escape && *p == '&') {
2638 g_string_append(to_str, "&-");
2639 } else if (!in_escape && *p == '+') {
2640 if (*(p + 1) == '-') {
2641 g_string_append_c(to_str, '+');
2644 g_string_append_c(to_str, '&');
2647 } else if (in_escape && *p == '/') {
2648 g_string_append_c(to_str, ',');
2649 } else if (in_escape && *p == '-') {
2650 g_string_append_c(to_str, '-');
2653 g_string_append_c(to_str, *p);
2659 g_string_append_c(to_str, '-');
2663 g_string_free(to_str, FALSE);
2668 static gboolean imap_rename_folder_func(GNode *node, gpointer data)
2670 FolderItem *item = node->data;
2671 gchar **paths = data;
2672 const gchar *oldpath = paths[0];
2673 const gchar *newpath = paths[1];
2675 gchar *new_itempath;
2678 oldpathlen = strlen(oldpath);
2679 if (strncmp(oldpath, item->path, oldpathlen) != 0) {
2680 g_warning("path doesn't match: %s, %s\n", oldpath, item->path);
2684 base = item->path + oldpathlen;
2685 while (*base == G_DIR_SEPARATOR) base++;
2687 new_itempath = g_strdup(newpath);
2689 new_itempath = g_strconcat(newpath, G_DIR_SEPARATOR_S, base,
2692 item->path = new_itempath;
2697 typedef struct _get_list_uid_data {
2699 IMAPFolderItem *item;
2700 GSList **msgnum_list;
2702 } get_list_uid_data;
2704 static void *get_list_of_uids_thread(void *data)
2706 get_list_uid_data *stuff = (get_list_uid_data *)data;
2707 Folder *folder = stuff->folder;
2708 IMAPFolderItem *item = stuff->item;
2709 GSList **msgnum_list = stuff->msgnum_list;
2710 gint ok, nummsgs = 0, lastuid_old;
2711 IMAPSession *session;
2712 GSList *uidlist, *elem;
2713 struct mailimap_set * set;
2714 clist * lep_uidlist;
2717 session = imap_session_get(folder);
2718 if (session == NULL) {
2720 return GINT_TO_POINTER(-1);
2723 ok = imap_select(session, IMAP_FOLDER(folder), item->item.path,
2724 NULL, NULL, NULL, NULL, TRUE);
2725 if (ok != IMAP_SUCCESS) {
2727 return GINT_TO_POINTER(-1);
2732 set = mailimap_set_new_interval(item->lastuid + 1, 0);
2733 r = imap_threaded_search(folder, IMAP_SEARCH_TYPE_SIMPLE, set,
2735 if (r == MAILIMAP_NO_ERROR) {
2736 GSList * fetchuid_list;
2739 imap_uid_list_from_lep(lep_uidlist);
2740 uidlist = g_slist_concat(fetchuid_list, uidlist);
2743 GSList * fetchuid_list;
2744 carray * lep_uidtab;
2746 r = imap_threaded_fetch_uid(folder, item->lastuid + 1,
2748 if (r == MAILIMAP_NO_ERROR) {
2750 imap_uid_list_from_lep_tab(lep_uidtab);
2751 uidlist = g_slist_concat(fetchuid_list, uidlist);
2755 lastuid_old = item->lastuid;
2756 *msgnum_list = g_slist_copy(item->uid_list);
2757 nummsgs = g_slist_length(*msgnum_list);
2758 debug_print("Got %d uids from cache\n", g_slist_length(item->uid_list));
2760 for (elem = uidlist; elem != NULL; elem = g_slist_next(elem)) {
2763 msgnum = GPOINTER_TO_INT(elem->data);
2764 if (msgnum > lastuid_old) {
2765 *msgnum_list = g_slist_prepend(*msgnum_list, GINT_TO_POINTER(msgnum));
2766 item->uid_list = g_slist_prepend(item->uid_list, GINT_TO_POINTER(msgnum));
2769 if(msgnum > item->lastuid)
2770 item->lastuid = msgnum;
2773 g_slist_free(uidlist);
2776 return GINT_TO_POINTER(nummsgs);
2779 static gint get_list_of_uids(Folder *folder, IMAPFolderItem *item, GSList **msgnum_list)
2782 get_list_uid_data *data = g_new0(get_list_uid_data, 1);
2784 data->folder = folder;
2786 data->msgnum_list = msgnum_list;
2788 if (prefs_common.work_offline && !imap_gtk_should_override()) {
2793 result = GPOINTER_TO_INT(get_list_of_uids_thread(data));
2799 gint imap_get_num_list(Folder *folder, FolderItem *_item, GSList **msgnum_list, gboolean *old_uids_valid)
2801 IMAPFolderItem *item = (IMAPFolderItem *)_item;
2802 IMAPSession *session;
2803 gint ok, nummsgs = 0, exists, recent, uid_val, uid_next, unseen;
2804 GSList *uidlist = NULL;
2806 gboolean selected_folder;
2808 debug_print("get_num_list\n");
2810 g_return_val_if_fail(folder != NULL, -1);
2811 g_return_val_if_fail(item != NULL, -1);
2812 g_return_val_if_fail(item->item.path != NULL, -1);
2813 g_return_val_if_fail(FOLDER_CLASS(folder) == &imap_class, -1);
2814 g_return_val_if_fail(folder->account != NULL, -1);
2816 session = imap_session_get(folder);
2817 g_return_val_if_fail(session != NULL, -1);
2819 selected_folder = (session->mbox != NULL) &&
2820 (!strcmp(session->mbox, item->item.path));
2821 if (selected_folder) {
2822 ok = imap_cmd_noop(session);
2823 if (ok != IMAP_SUCCESS) {
2827 exists = session->exists;
2829 *old_uids_valid = TRUE;
2831 ok = imap_status(session, IMAP_FOLDER(folder), item->item.path,
2832 &exists, &recent, &uid_next, &uid_val, &unseen, FALSE);
2833 if (ok != IMAP_SUCCESS) {
2837 if(item->item.mtime == uid_val)
2838 *old_uids_valid = TRUE;
2840 *old_uids_valid = FALSE;
2842 debug_print("Freeing imap uid cache\n");
2844 g_slist_free(item->uid_list);
2845 item->uid_list = NULL;
2847 item->item.mtime = uid_val;
2849 imap_delete_all_cached_messages((FolderItem *)item);
2853 if (!selected_folder)
2854 item->uid_next = uid_next;
2856 /* If old uid_next matches new uid_next we can be sure no message
2857 was added to the folder */
2858 if (( selected_folder && !session->folder_content_changed) ||
2859 (!selected_folder && uid_next == item->uid_next)) {
2860 nummsgs = g_slist_length(item->uid_list);
2862 /* If number of messages is still the same we
2863 know our caches message numbers are still valid,
2864 otherwise if the number of messages has decrease
2865 we discard our cache to start a new scan to find
2866 out which numbers have been removed */
2867 if (exists == nummsgs) {
2868 *msgnum_list = g_slist_copy(item->uid_list);
2870 } else if (exists < nummsgs) {
2871 debug_print("Freeing imap uid cache");
2873 g_slist_free(item->uid_list);
2874 item->uid_list = NULL;
2879 *msgnum_list = NULL;
2883 nummsgs = get_list_of_uids(folder, item, &uidlist);
2890 if (nummsgs != exists) {
2891 /* Cache contains more messages then folder, we have cached
2892 an old UID of a message that was removed and new messages
2893 have been added too, otherwise the uid_next check would
2895 debug_print("Freeing imap uid cache");
2897 g_slist_free(item->uid_list);
2898 item->uid_list = NULL;
2900 g_slist_free(*msgnum_list);
2902 nummsgs = get_list_of_uids(folder, item, &uidlist);
2905 *msgnum_list = uidlist;
2907 dir = folder_item_get_path((FolderItem *)item);
2908 debug_print("removing old messages from %s\n", dir);
2909 remove_numbered_files_not_in_list(dir, *msgnum_list);
2912 debug_print("get_num_list - ok - %i\n", nummsgs);
2917 static MsgInfo *imap_parse_msg(const gchar *file, FolderItem *item)
2922 flags.perm_flags = MSG_NEW|MSG_UNREAD;
2923 flags.tmp_flags = 0;
2925 g_return_val_if_fail(item != NULL, NULL);
2926 g_return_val_if_fail(file != NULL, NULL);
2928 if (folder_has_parent_of_type(item, F_QUEUE)) {
2929 MSG_SET_TMP_FLAGS(flags, MSG_QUEUED);
2930 } else if (folder_has_parent_of_type(item, F_DRAFT)) {
2931 MSG_SET_TMP_FLAGS(flags, MSG_DRAFT);
2934 msginfo = procheader_parse_file(file, flags, FALSE, FALSE);
2935 if (!msginfo) return NULL;
2937 msginfo->plaintext_file = g_strdup(file);
2938 msginfo->folder = item;
2943 GSList *imap_get_msginfos(Folder *folder, FolderItem *item,
2944 GSList *msgnum_list)
2946 IMAPSession *session;
2947 MsgInfoList *ret = NULL;
2950 debug_print("get_msginfos\n");
2952 g_return_val_if_fail(folder != NULL, NULL);
2953 g_return_val_if_fail(item != NULL, NULL);
2954 g_return_val_if_fail(msgnum_list != NULL, NULL);
2956 session = imap_session_get(folder);
2957 g_return_val_if_fail(session != NULL, NULL);
2959 debug_print("IMAP getting msginfos\n");
2960 ok = imap_select(session, IMAP_FOLDER(folder), item->path,
2961 NULL, NULL, NULL, NULL, FALSE);
2962 if (ok != IMAP_SUCCESS)
2965 if (!(folder_has_parent_of_type(item, F_DRAFT) ||
2966 folder_has_parent_of_type(item, F_QUEUE))) {
2967 ret = g_slist_concat(ret,
2968 imap_get_uncached_messages(session, item,
2971 MsgNumberList *sorted_list, *elem;
2972 gint startnum, lastnum;
2974 sorted_list = g_slist_sort(g_slist_copy(msgnum_list), g_int_compare);
2976 startnum = lastnum = GPOINTER_TO_INT(sorted_list->data);
2978 for (elem = sorted_list;; elem = g_slist_next(elem)) {
2982 num = GPOINTER_TO_INT(elem->data);
2984 if (num > lastnum + 1 || elem == NULL) {
2986 for (i = startnum; i <= lastnum; ++i) {
2989 file = imap_fetch_msg(folder, item, i);
2991 MsgInfo *msginfo = imap_parse_msg(file, item);
2992 if (msginfo != NULL) {
2993 msginfo->msgnum = i;
2994 ret = g_slist_append(ret, msginfo);
3008 g_slist_free(sorted_list);
3014 MsgInfo *imap_get_msginfo(Folder *folder, FolderItem *item, gint uid)
3016 MsgInfo *msginfo = NULL;
3017 MsgInfoList *msginfolist;
3018 MsgNumberList numlist;
3020 numlist.next = NULL;
3021 numlist.data = GINT_TO_POINTER(uid);
3023 msginfolist = imap_get_msginfos(folder, item, &numlist);
3024 if (msginfolist != NULL) {
3025 msginfo = msginfolist->data;
3026 g_slist_free(msginfolist);
3032 gboolean imap_scan_required(Folder *folder, FolderItem *_item)
3034 IMAPSession *session;
3035 IMAPFolderItem *item = (IMAPFolderItem *)_item;
3036 gint ok, exists = 0, recent = 0, unseen = 0;
3037 guint32 uid_next, uid_val = 0;
3038 gboolean selected_folder;
3040 g_return_val_if_fail(folder != NULL, FALSE);
3041 g_return_val_if_fail(item != NULL, FALSE);
3042 g_return_val_if_fail(item->item.folder != NULL, FALSE);
3043 g_return_val_if_fail(FOLDER_CLASS(item->item.folder) == &imap_class, FALSE);
3045 if (item->item.path == NULL)
3048 session = imap_session_get(folder);
3049 g_return_val_if_fail(session != NULL, FALSE);
3051 selected_folder = (session->mbox != NULL) &&
3052 (!strcmp(session->mbox, item->item.path));
3053 if (selected_folder) {
3054 ok = imap_cmd_noop(session);
3055 if (ok != IMAP_SUCCESS)
3058 if (session->folder_content_changed
3059 || session->exists != item->item.total_msgs)
3062 ok = imap_status(session, IMAP_FOLDER(folder), item->item.path,
3063 &exists, &recent, &uid_next, &uid_val, &unseen, FALSE);
3064 if (ok != IMAP_SUCCESS)
3067 if ((uid_next != item->uid_next) || (exists != item->item.total_msgs))
3074 void imap_change_flags(Folder *folder, FolderItem *item, MsgInfo *msginfo, MsgPermFlags newflags)
3076 IMAPSession *session;
3077 IMAPFlags flags_set = 0, flags_unset = 0;
3078 gint ok = IMAP_SUCCESS;
3079 MsgNumberList numlist;
3080 hashtable_data *ht_data = NULL;
3082 g_return_if_fail(folder != NULL);
3083 g_return_if_fail(folder->klass == &imap_class);
3084 g_return_if_fail(item != NULL);
3085 g_return_if_fail(item->folder == folder);
3086 g_return_if_fail(msginfo != NULL);
3087 g_return_if_fail(msginfo->folder == item);
3089 session = imap_session_get(folder);
3093 if ((ok = imap_select(session, IMAP_FOLDER(folder), msginfo->folder->path,
3094 NULL, NULL, NULL, NULL, FALSE)) != IMAP_SUCCESS) {
3098 if (!MSG_IS_MARKED(msginfo->flags) && (newflags & MSG_MARKED))
3099 flags_set |= IMAP_FLAG_FLAGGED;
3100 if ( MSG_IS_MARKED(msginfo->flags) && !(newflags & MSG_MARKED))
3101 flags_unset |= IMAP_FLAG_FLAGGED;
3103 if (!MSG_IS_UNREAD(msginfo->flags) && (newflags & MSG_UNREAD))
3104 flags_unset |= IMAP_FLAG_SEEN;
3105 if ( MSG_IS_UNREAD(msginfo->flags) && !(newflags & MSG_UNREAD))
3106 flags_set |= IMAP_FLAG_SEEN;
3108 if (!MSG_IS_REPLIED(msginfo->flags) && (newflags & MSG_REPLIED))
3109 flags_set |= IMAP_FLAG_ANSWERED;
3110 if ( MSG_IS_REPLIED(msginfo->flags) && !(newflags & MSG_REPLIED))
3111 flags_set |= IMAP_FLAG_ANSWERED;
3113 numlist.next = NULL;
3114 numlist.data = GINT_TO_POINTER(msginfo->msgnum);
3116 if (IMAP_FOLDER_ITEM(item)->batching) {
3117 /* instead of performing an UID STORE command for each message change,
3118 * as a lot of them can change "together", we just fill in hashtables
3119 * and defer the treatment so that we're able to send only one
3122 debug_print("IMAP batch mode on, deferring flags change\n");
3124 ht_data = g_hash_table_lookup(flags_set_table, GINT_TO_POINTER(flags_set));
3125 if (ht_data == NULL) {
3126 ht_data = g_new0(hashtable_data, 1);
3127 ht_data->session = session;
3128 g_hash_table_insert(flags_set_table, GINT_TO_POINTER(flags_set), ht_data);
3130 if (!g_slist_find(ht_data->msglist, GINT_TO_POINTER(msginfo->msgnum)))
3131 ht_data->msglist = g_slist_prepend(ht_data->msglist, GINT_TO_POINTER(msginfo->msgnum));
3134 ht_data = g_hash_table_lookup(flags_unset_table, GINT_TO_POINTER(flags_unset));
3135 if (ht_data == NULL) {
3136 ht_data = g_new0(hashtable_data, 1);
3137 ht_data->session = session;
3138 g_hash_table_insert(flags_unset_table, GINT_TO_POINTER(flags_unset), ht_data);
3140 if (!g_slist_find(ht_data->msglist, GINT_TO_POINTER(msginfo->msgnum)))
3141 ht_data->msglist = g_slist_prepend(ht_data->msglist, GINT_TO_POINTER(msginfo->msgnum));
3144 debug_print("IMAP changing flags\n");
3146 ok = imap_set_message_flags(session, &numlist, flags_set, TRUE);
3147 if (ok != IMAP_SUCCESS) {
3153 ok = imap_set_message_flags(session, &numlist, flags_unset, FALSE);
3154 if (ok != IMAP_SUCCESS) {
3159 msginfo->flags.perm_flags = newflags;
3164 static gint imap_remove_msg(Folder *folder, FolderItem *item, gint uid)
3167 IMAPSession *session;
3169 MsgNumberList numlist;
3171 g_return_val_if_fail(folder != NULL, -1);
3172 g_return_val_if_fail(FOLDER_CLASS(folder) == &imap_class, -1);
3173 g_return_val_if_fail(item != NULL, -1);
3175 session = imap_session_get(folder);
3176 if (!session) return -1;
3178 ok = imap_select(session, IMAP_FOLDER(folder), item->path,
3179 NULL, NULL, NULL, NULL, FALSE);
3180 if (ok != IMAP_SUCCESS)
3183 numlist.next = NULL;
3184 numlist.data = GINT_TO_POINTER(uid);
3186 ok = imap_set_message_flags
3187 (IMAP_SESSION(REMOTE_FOLDER(folder)->session),
3188 &numlist, IMAP_FLAG_DELETED, TRUE);
3189 if (ok != IMAP_SUCCESS) {
3190 log_warning(_("can't set deleted flags: %d\n"), uid);
3194 if (!session->uidplus) {
3195 ok = imap_cmd_expunge(session);
3199 uidstr = g_strdup_printf("%u", uid);
3200 ok = imap_cmd_expunge(session);
3203 if (ok != IMAP_SUCCESS) {
3204 log_warning(_("can't expunge\n"));
3208 IMAP_FOLDER_ITEM(item)->uid_list = g_slist_remove(
3209 IMAP_FOLDER_ITEM(item)->uid_list, numlist.data);
3210 dir = folder_item_get_path(item);
3211 if (is_dir_exist(dir))
3212 remove_numbered_files(dir, uid, uid);
3215 return IMAP_SUCCESS;
3218 static gint compare_msginfo(gconstpointer a, gconstpointer b)
3220 return ((MsgInfo *)a)->msgnum - ((MsgInfo *)b)->msgnum;
3223 static guint gslist_find_next_num(MsgNumberList **list, guint num)
3227 g_return_val_if_fail(list != NULL, -1);
3229 for (elem = *list; elem != NULL; elem = g_slist_next(elem))
3230 if (GPOINTER_TO_INT(elem->data) >= num)
3233 return elem != NULL ? GPOINTER_TO_INT(elem->data) : (gint)-1;
3237 * NEW and DELETED flags are not syncronized
3238 * - The NEW/RECENT flags in IMAP folders can not really be directly
3239 * modified by Sylpheed
3240 * - The DELETE/DELETED flag in IMAP and Sylpheed don't have the same
3241 * meaning, in IMAP it always removes the messages from the FolderItem
3242 * in Sylpheed it can mean to move the message to trash
3245 typedef struct _get_flags_data {
3248 MsgInfoList *msginfo_list;
3249 GRelation *msgflags;
3253 static /*gint*/ void *imap_get_flags_thread(void *data)
3255 get_flags_data *stuff = (get_flags_data *)data;
3256 Folder *folder = stuff->folder;
3257 FolderItem *item = stuff->item;
3258 MsgInfoList *msginfo_list = stuff->msginfo_list;
3259 GRelation *msgflags = stuff->msgflags;
3260 IMAPSession *session;
3261 GSList *sorted_list;
3262 GSList *unseen = NULL, *answered = NULL, *flagged = NULL;
3263 GSList *p_unseen, *p_answered, *p_flagged;
3265 GSList *seq_list, *cur;
3266 gboolean reverse_seen = FALSE;
3269 gint exists_cnt, recent_cnt, unseen_cnt, uid_next;
3270 guint32 uidvalidity;
3271 gboolean selected_folder;
3273 if (folder == NULL || item == NULL) {
3275 return GINT_TO_POINTER(-1);
3277 if (msginfo_list == NULL) {
3279 return GINT_TO_POINTER(0);
3282 session = imap_session_get(folder);
3283 if (session == NULL) {
3285 return GINT_TO_POINTER(-1);
3288 selected_folder = (session->mbox != NULL) &&
3289 (!strcmp(session->mbox, item->path));
3291 if (!selected_folder) {
3292 ok = imap_status(session, IMAP_FOLDER(folder), item->path,
3293 &exists_cnt, &recent_cnt, &uid_next, &uidvalidity, &unseen_cnt, TRUE);
3294 ok = imap_select(session, IMAP_FOLDER(folder), item->path,
3295 NULL, NULL, NULL, NULL, TRUE);
3296 if (ok != IMAP_SUCCESS) {
3298 return GINT_TO_POINTER(-1);
3301 if (unseen_cnt > exists_cnt / 2)
3302 reverse_seen = TRUE;
3305 if (item->unread_msgs > item->total_msgs / 2)
3306 reverse_seen = TRUE;
3309 cmd_buf = g_string_new(NULL);
3311 sorted_list = g_slist_sort(g_slist_copy(msginfo_list), compare_msginfo);
3313 seq_list = imap_get_lep_set_from_msglist(msginfo_list);
3315 for (cur = seq_list; cur != NULL; cur = g_slist_next(cur)) {
3316 struct mailimap_set * imapset;
3317 clist * lep_uidlist;
3320 imapset = cur->data;
3322 r = imap_threaded_search(folder, IMAP_SEARCH_TYPE_SEEN,
3323 imapset, &lep_uidlist);
3326 r = imap_threaded_search(folder,
3327 IMAP_SEARCH_TYPE_UNSEEN,
3328 imapset, &lep_uidlist);
3330 if (r == MAILIMAP_NO_ERROR) {
3333 uidlist = imap_uid_list_from_lep(lep_uidlist);
3334 mailimap_search_result_free(lep_uidlist);
3336 unseen = g_slist_concat(unseen, uidlist);
3339 r = imap_threaded_search(folder, IMAP_SEARCH_TYPE_ANSWERED,
3340 imapset, &lep_uidlist);
3341 if (r == MAILIMAP_NO_ERROR) {
3344 uidlist = imap_uid_list_from_lep(lep_uidlist);
3345 mailimap_search_result_free(lep_uidlist);
3347 answered = g_slist_concat(answered, uidlist);
3350 r = imap_threaded_search(folder, IMAP_SEARCH_TYPE_FLAGGED,
3351 imapset, &lep_uidlist);
3352 if (r == MAILIMAP_NO_ERROR) {
3355 uidlist = imap_uid_list_from_lep(lep_uidlist);
3356 mailimap_search_result_free(lep_uidlist);
3358 flagged = g_slist_concat(flagged, uidlist);
3363 p_answered = answered;
3364 p_flagged = flagged;
3366 for (elem = sorted_list; elem != NULL; elem = g_slist_next(elem)) {
3371 msginfo = (MsgInfo *) elem->data;
3372 flags = msginfo->flags.perm_flags;
3373 wasnew = (flags & MSG_NEW);
3374 flags &= ~((reverse_seen ? 0 : MSG_UNREAD | MSG_NEW) | MSG_REPLIED | MSG_MARKED);
3376 flags |= MSG_UNREAD | (wasnew ? MSG_NEW : 0);
3377 if (gslist_find_next_num(&p_unseen, msginfo->msgnum) == msginfo->msgnum) {
3378 if (!reverse_seen) {
3379 flags |= MSG_UNREAD | (wasnew ? MSG_NEW : 0);
3381 flags &= ~(MSG_UNREAD | MSG_NEW);
3384 if (gslist_find_next_num(&p_answered, msginfo->msgnum) == msginfo->msgnum)
3385 flags |= MSG_REPLIED;
3386 if (gslist_find_next_num(&p_flagged, msginfo->msgnum) == msginfo->msgnum)
3387 flags |= MSG_MARKED;
3388 g_relation_insert(msgflags, msginfo, GINT_TO_POINTER(flags));
3391 imap_lep_set_free(seq_list);
3392 g_slist_free(flagged);
3393 g_slist_free(answered);
3394 g_slist_free(unseen);
3395 g_slist_free(sorted_list);
3396 g_string_free(cmd_buf, TRUE);
3399 return GINT_TO_POINTER(0);
3402 static gint imap_get_flags(Folder *folder, FolderItem *item,
3403 MsgInfoList *msginfo_list, GRelation *msgflags)
3406 get_flags_data *data = g_new0(get_flags_data, 1);
3408 data->folder = folder;
3410 data->msginfo_list = msginfo_list;
3411 data->msgflags = msgflags;
3413 if (prefs_common.work_offline && !imap_gtk_should_override()) {
3418 result = GPOINTER_TO_INT(imap_get_flags_thread(data));
3425 static gboolean process_flags(gpointer key, gpointer value, gpointer user_data)
3427 gboolean flags_set = GPOINTER_TO_INT(user_data);
3428 gint flags_value = GPOINTER_TO_INT(key);
3429 hashtable_data *data = (hashtable_data *)value;
3431 data->msglist = g_slist_reverse(data->msglist);
3433 debug_print("IMAP %ssetting flags to %d for %d messages\n",
3436 g_slist_length(data->msglist));
3437 imap_set_message_flags(data->session, data->msglist, flags_value, flags_set);
3439 g_slist_free(data->msglist);
3444 static void process_hashtable(void)
3446 if (flags_set_table) {
3447 g_hash_table_foreach_remove(flags_set_table, process_flags, GINT_TO_POINTER(TRUE));
3448 g_free(flags_set_table);
3449 flags_set_table = NULL;
3451 if (flags_unset_table) {
3452 g_hash_table_foreach_remove(flags_unset_table, process_flags, GINT_TO_POINTER(FALSE));
3453 g_free(flags_unset_table);
3454 flags_unset_table = NULL;
3458 static IMAPFolderItem *batching_item = NULL;
3460 static void imap_set_batch (Folder *folder, FolderItem *_item, gboolean batch)
3462 IMAPFolderItem *item = (IMAPFolderItem *)_item;
3464 g_return_if_fail(item != NULL);
3466 if (batch && batching_item != NULL) {
3467 g_warning("already batching on %s\n", batching_item->item.path);
3471 if (item->batching == batch)
3474 item->batching = batch;
3476 batching_item = batch?item:NULL;
3479 debug_print("IMAP switching to batch mode\n");
3480 if (flags_set_table) {
3481 g_warning("flags_set_table non-null but we just entered batch mode!\n");
3482 flags_set_table = NULL;
3484 if (flags_unset_table) {
3485 g_warning("flags_unset_table non-null but we just entered batch mode!\n");
3486 flags_unset_table = NULL;
3488 flags_set_table = g_hash_table_new(NULL, g_direct_equal);
3489 flags_unset_table = g_hash_table_new(NULL, g_direct_equal);
3491 debug_print("IMAP switching away from batch mode\n");
3493 process_hashtable();
3499 /* data types conversion libetpan <-> sylpheed */
3503 #define ETPAN_IMAP_MB_MARKED 1
3504 #define ETPAN_IMAP_MB_UNMARKED 2
3505 #define ETPAN_IMAP_MB_NOSELECT 4
3506 #define ETPAN_IMAP_MB_NOINFERIORS 8
3508 static int imap_flags_to_flags(struct mailimap_mbx_list_flags * imap_flags)
3514 if (imap_flags->mbf_type == MAILIMAP_MBX_LIST_FLAGS_SFLAG) {
3515 switch (imap_flags->mbf_sflag) {
3516 case MAILIMAP_MBX_LIST_SFLAG_MARKED:
3517 flags |= ETPAN_IMAP_MB_MARKED;
3519 case MAILIMAP_MBX_LIST_SFLAG_NOSELECT:
3520 flags |= ETPAN_IMAP_MB_NOSELECT;
3522 case MAILIMAP_MBX_LIST_SFLAG_UNMARKED:
3523 flags |= ETPAN_IMAP_MB_UNMARKED;
3528 for(cur = clist_begin(imap_flags->mbf_oflags) ; cur != NULL ;
3529 cur = clist_next(cur)) {
3530 struct mailimap_mbx_list_oflag * oflag;
3532 oflag = clist_content(cur);
3534 switch (oflag->of_type) {
3535 case MAILIMAP_MBX_LIST_OFLAG_NOINFERIORS:
3536 flags |= ETPAN_IMAP_MB_NOINFERIORS;
3544 static GSList * imap_list_from_lep(IMAPFolder * folder,
3545 clist * list, const gchar * real_path)
3552 for(iter = clist_begin(list) ; iter != NULL ;
3553 iter = clist_next(iter)) {
3554 struct mailimap_mailbox_list * mb;
3562 FolderItem *new_item;
3564 mb = clist_content(iter);
3567 if (mb->mb_flag != NULL)
3568 flags = imap_flags_to_flags(mb->mb_flag);
3570 delimiter = mb->mb_delimiter;
3573 dup_name = strdup(name);
3574 if (delimiter != '\0')
3575 subst_char(dup_name, delimiter, '/');
3577 base = g_path_get_basename(dup_name);
3578 if (base[0] == '.') {
3584 if (strcmp(dup_name, real_path) == 0) {
3590 if (dup_name[strlen(dup_name)-1] == '/') {
3596 loc_name = imap_modified_utf7_to_utf8(base);
3597 loc_path = imap_modified_utf7_to_utf8(dup_name);
3599 new_item = folder_item_new(FOLDER(folder), loc_name, loc_path);
3600 if ((flags & ETPAN_IMAP_MB_NOINFERIORS) != 0)
3601 new_item->no_sub = TRUE;
3602 if (strcmp(dup_name, "INBOX") != 0 &&
3603 ((flags & ETPAN_IMAP_MB_NOSELECT) != 0))
3604 new_item->no_select = TRUE;
3606 item_list = g_slist_append(item_list, new_item);
3608 debug_print("folder '%s' found.\n", loc_path);
3619 static GSList * imap_get_lep_set_from_numlist(MsgNumberList *numlist)
3621 GSList *sorted_list, *cur;
3622 guint first, last, next;
3623 GSList *ret_list = NULL;
3625 struct mailimap_set * current_set;
3626 unsigned int item_count;
3628 if (numlist == NULL)
3632 current_set = mailimap_set_new_empty();
3634 sorted_list = g_slist_copy(numlist);
3635 sorted_list = g_slist_sort(sorted_list, g_int_compare);
3637 first = GPOINTER_TO_INT(sorted_list->data);
3640 for (cur = sorted_list; cur != NULL; cur = g_slist_next(cur)) {
3641 if (GPOINTER_TO_INT(cur->data) == 0)
3646 last = GPOINTER_TO_INT(cur->data);
3648 next = GPOINTER_TO_INT(cur->next->data);
3652 if (last + 1 != next || next == 0) {
3654 struct mailimap_set_item * item;
3655 item = mailimap_set_item_new(first, last);
3656 mailimap_set_add(current_set, item);
3661 if (count >= IMAP_SET_MAX_COUNT) {
3662 ret_list = g_slist_append(ret_list,
3664 current_set = mailimap_set_new_empty();
3671 if (clist_count(current_set->set_list) > 0) {
3672 ret_list = g_slist_append(ret_list,
3676 g_slist_free(sorted_list);
3681 static GSList * imap_get_lep_set_from_msglist(MsgInfoList *msglist)
3683 MsgNumberList *numlist = NULL;
3687 for (cur = msglist; cur != NULL; cur = g_slist_next(cur)) {
3688 MsgInfo *msginfo = (MsgInfo *) cur->data;
3690 numlist = g_slist_append(numlist, GINT_TO_POINTER(msginfo->msgnum));
3692 seq_list = imap_get_lep_set_from_numlist(numlist);
3693 g_slist_free(numlist);
3698 static GSList * imap_uid_list_from_lep(clist * list)
3705 for(iter = clist_begin(list) ; iter != NULL ;
3706 iter = clist_next(iter)) {
3709 puid = clist_content(iter);
3710 result = g_slist_append(result, GINT_TO_POINTER(* puid));
3716 static GSList * imap_uid_list_from_lep_tab(carray * list)
3723 for(i = 0 ; i < carray_count(list) ; i ++) {
3726 puid = carray_get(list, i);
3727 result = g_slist_append(result, GINT_TO_POINTER(* puid));
3733 static MsgInfo *imap_envelope_from_lep(struct imap_fetch_env_info * info,
3736 MsgInfo *msginfo = NULL;
3739 MsgFlags flags = {0, 0};
3741 MSG_SET_TMP_FLAGS(flags, MSG_IMAP);
3742 if (folder_has_parent_of_type(item, F_QUEUE)) {
3743 MSG_SET_TMP_FLAGS(flags, MSG_QUEUED);
3744 } else if (folder_has_parent_of_type(item, F_DRAFT)) {
3745 MSG_SET_TMP_FLAGS(flags, MSG_DRAFT);
3747 flags.perm_flags = info->flags;
3751 msginfo = procheader_parse_str(info->headers, flags, FALSE, FALSE);
3754 msginfo->msgnum = uid;
3755 msginfo->size = size;
3761 static void imap_lep_set_free(GSList *seq_list)
3765 for(cur = seq_list ; cur != NULL ; cur = g_slist_next(cur)) {
3766 struct mailimap_set * imapset;
3768 imapset = cur->data;
3769 mailimap_set_free(imapset);
3771 g_slist_free(seq_list);
3774 static struct mailimap_flag_list * imap_flag_to_lep(IMAPFlags flags)
3776 struct mailimap_flag_list * flag_list;
3778 flag_list = mailimap_flag_list_new_empty();
3780 if (IMAP_IS_SEEN(flags))
3781 mailimap_flag_list_add(flag_list,
3782 mailimap_flag_new_seen());
3783 if (IMAP_IS_ANSWERED(flags))
3784 mailimap_flag_list_add(flag_list,
3785 mailimap_flag_new_answered());
3786 if (IMAP_IS_FLAGGED(flags))
3787 mailimap_flag_list_add(flag_list,
3788 mailimap_flag_new_flagged());
3789 if (IMAP_IS_DELETED(flags))
3790 mailimap_flag_list_add(flag_list,
3791 mailimap_flag_new_deleted());
3792 if (IMAP_IS_DRAFT(flags))
3793 mailimap_flag_list_add(flag_list,
3794 mailimap_flag_new_draft());
3799 guint imap_folder_get_refcnt(Folder *folder)
3801 return ((IMAPFolder *)folder)->refcnt;
3804 void imap_folder_ref(Folder *folder)
3806 ((IMAPFolder *)folder)->refcnt++;
3809 void imap_folder_unref(Folder *folder)
3811 if (((IMAPFolder *)folder)->refcnt > 0)
3812 ((IMAPFolder *)folder)->refcnt--;
3815 #else /* HAVE_LIBETPAN */
3817 static FolderClass imap_class;
3819 static Folder *imap_folder_new (const gchar *name,
3824 static gint imap_create_tree (Folder *folder)
3828 static FolderItem *imap_create_folder (Folder *folder,
3834 static gint imap_rename_folder (Folder *folder,
3841 FolderClass *imap_get_class(void)
3843 if (imap_class.idstr == NULL) {
3844 imap_class.type = F_IMAP;
3845 imap_class.idstr = "imap";
3846 imap_class.uistr = "IMAP4";
3848 imap_class.new_folder = imap_folder_new;
3849 imap_class.create_tree = imap_create_tree;
3850 imap_class.create_folder = imap_create_folder;
3851 imap_class.rename_folder = imap_rename_folder;
3852 /* nothing implemented */