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,
286 static void imap_synchronise (FolderItem *item);
288 static void imap_free_capabilities (IMAPSession *session);
290 /* low-level IMAP4rev1 commands */
291 static gint imap_cmd_login (IMAPSession *session,
295 static gint imap_cmd_logout (IMAPSession *session);
296 static gint imap_cmd_noop (IMAPSession *session);
298 static gint imap_cmd_starttls (IMAPSession *session);
300 static gint imap_cmd_select (IMAPSession *session,
305 guint32 *uid_validity,
307 static gint imap_cmd_examine (IMAPSession *session,
312 guint32 *uid_validity,
314 static gint imap_cmd_create (IMAPSession *sock,
315 const gchar *folder);
316 static gint imap_cmd_rename (IMAPSession *sock,
317 const gchar *oldfolder,
318 const gchar *newfolder);
319 static gint imap_cmd_delete (IMAPSession *session,
320 const gchar *folder);
321 static gint imap_cmd_fetch (IMAPSession *sock,
323 const gchar *filename,
326 static gint imap_cmd_append (IMAPSession *session,
327 const gchar *destfolder,
331 static gint imap_cmd_copy (IMAPSession *session,
332 struct mailimap_set * set,
333 const gchar *destfolder,
334 GRelation *uid_mapping);
335 static gint imap_cmd_store (IMAPSession *session,
336 struct mailimap_set * set,
339 static gint imap_cmd_expunge (IMAPSession *session);
341 static void imap_path_separator_subst (gchar *str,
344 static gchar *imap_utf8_to_modified_utf7 (const gchar *from);
345 static gchar *imap_modified_utf7_to_utf8 (const gchar *mutf7_str);
347 static gboolean imap_rename_folder_func (GNode *node,
349 static gint imap_get_num_list (Folder *folder,
352 gboolean *old_uids_valid);
353 static GSList *imap_get_msginfos (Folder *folder,
355 GSList *msgnum_list);
356 static MsgInfo *imap_get_msginfo (Folder *folder,
359 static gboolean imap_scan_required (Folder *folder,
361 static void imap_change_flags (Folder *folder,
364 MsgPermFlags newflags);
365 static gint imap_get_flags (Folder *folder,
367 MsgInfoList *msglist,
368 GRelation *msgflags);
369 static gchar *imap_folder_get_path (Folder *folder);
370 static gchar *imap_item_get_path (Folder *folder,
372 static MsgInfo *imap_parse_msg(const gchar *file, FolderItem *item);
375 /* data types conversion libetpan <-> sylpheed */
376 static GSList * imap_list_from_lep(IMAPFolder * folder,
377 clist * list, const gchar * real_path);
378 static GSList * imap_get_lep_set_from_numlist(MsgNumberList *numlist);
379 static GSList * imap_get_lep_set_from_msglist(MsgInfoList *msglist);
380 static GSList * imap_uid_list_from_lep(clist * list);
381 static GSList * imap_uid_list_from_lep_tab(carray * list);
382 static MsgInfo *imap_envelope_from_lep(struct imap_fetch_env_info * info,
384 static void imap_lep_set_free(GSList *seq_list);
385 static struct mailimap_flag_list * imap_flag_to_lep(IMAPFlags flags);
388 static GHashTable *flags_set_table = NULL;
389 static GHashTable *flags_unset_table = NULL;
390 typedef struct _hashtable_data {
391 IMAPSession *session;
395 static FolderClass imap_class;
397 typedef struct _thread_data {
407 FolderClass *imap_get_class(void)
409 if (imap_class.idstr == NULL) {
410 imap_class.type = F_IMAP;
411 imap_class.idstr = "imap";
412 imap_class.uistr = "IMAP4";
414 /* Folder functions */
415 imap_class.new_folder = imap_folder_new;
416 imap_class.destroy_folder = imap_folder_destroy;
417 imap_class.scan_tree = imap_scan_tree;
418 imap_class.create_tree = imap_create_tree;
420 /* FolderItem functions */
421 imap_class.item_new = imap_folder_item_new;
422 imap_class.item_destroy = imap_folder_item_destroy;
423 imap_class.item_get_path = imap_item_get_path;
424 imap_class.create_folder = imap_create_folder;
425 imap_class.rename_folder = imap_rename_folder;
426 imap_class.remove_folder = imap_remove_folder;
427 imap_class.close = imap_close;
428 imap_class.get_num_list = imap_get_num_list;
429 imap_class.scan_required = imap_scan_required;
431 /* Message functions */
432 imap_class.get_msginfo = imap_get_msginfo;
433 imap_class.get_msginfos = imap_get_msginfos;
434 imap_class.fetch_msg = imap_fetch_msg;
435 imap_class.fetch_msg_full = imap_fetch_msg_full;
436 imap_class.add_msg = imap_add_msg;
437 imap_class.add_msgs = imap_add_msgs;
438 imap_class.copy_msg = imap_copy_msg;
439 imap_class.copy_msgs = imap_copy_msgs;
440 imap_class.remove_msg = imap_remove_msg;
441 imap_class.remove_msgs = imap_remove_msgs;
442 imap_class.remove_all_msg = imap_remove_all_msg;
443 imap_class.is_msg_changed = imap_is_msg_changed;
444 imap_class.change_flags = imap_change_flags;
445 imap_class.get_flags = imap_get_flags;
446 imap_class.set_batch = imap_set_batch;
447 imap_class.synchronise = imap_synchronise;
449 pthread_mutex_init(&imap_mutex, NULL);
456 static Folder *imap_folder_new(const gchar *name, const gchar *path)
460 folder = (Folder *)g_new0(IMAPFolder, 1);
461 folder->klass = &imap_class;
462 imap_folder_init(folder, name, path);
467 static void imap_folder_destroy(Folder *folder)
471 while (imap_folder_get_refcnt(folder) > 0)
472 gtk_main_iteration();
474 dir = imap_folder_get_path(folder);
475 if (is_dir_exist(dir))
476 remove_dir_recursive(dir);
479 folder_remote_folder_destroy(REMOTE_FOLDER(folder));
483 static void imap_folder_init(Folder *folder, const gchar *name,
486 folder_remote_folder_init((Folder *)folder, name, path);
489 static FolderItem *imap_folder_item_new(Folder *folder)
491 IMAPFolderItem *item;
493 item = g_new0(IMAPFolderItem, 1);
496 item->uid_list = NULL;
498 return (FolderItem *)item;
501 static void imap_folder_item_destroy(Folder *folder, FolderItem *_item)
503 IMAPFolderItem *item = (IMAPFolderItem *)_item;
505 g_return_if_fail(item != NULL);
506 g_slist_free(item->uid_list);
511 static gboolean imap_reset_uid_lists_func(GNode *node, gpointer data)
513 IMAPFolderItem *item = (IMAPFolderItem *)node->data;
517 g_slist_free(item->uid_list);
518 item->uid_list = NULL;
523 static void imap_reset_uid_lists(Folder *folder)
525 if(folder->node == NULL)
528 /* Destroy all uid lists and rest last uid */
529 g_node_traverse(folder->node, G_IN_ORDER, G_TRAVERSE_ALL, -1, imap_reset_uid_lists_func, NULL);
532 void imap_get_capabilities(IMAPSession *session)
534 struct mailimap_capability_data *capabilities = NULL;
537 if (session->capability != NULL)
540 capabilities = imap_threaded_capability(session->folder);
542 if (capabilities == NULL)
545 for(cur = clist_begin(capabilities->cap_list) ; cur != NULL ;
546 cur = clist_next(cur)) {
547 struct mailimap_capability * cap =
549 if (!cap || cap->cap_data.cap_name == NULL)
551 session->capability = g_slist_append
552 (session->capability,
553 g_strdup(cap->cap_data.cap_name));
554 debug_print("got capa %s\n", cap->cap_data.cap_name);
556 mailimap_capability_data_free(capabilities);
559 gboolean imap_has_capability(IMAPSession *session, const gchar *cap)
562 for (cur = session->capability; cur; cur = cur->next) {
563 if (!g_ascii_strcasecmp(cur->data, cap))
569 static gint imap_auth(IMAPSession *session, const gchar *user, const gchar *pass,
572 gint ok = IMAP_ERROR;
573 static time_t last_login_err = 0;
575 imap_get_capabilities(session);
578 case IMAP_AUTH_CRAM_MD5:
579 ok = imap_cmd_login(session, user, pass, "CRAM-MD5");
581 case IMAP_AUTH_LOGIN:
582 ok = imap_cmd_login(session, user, pass, "LOGIN");
585 debug_print("capabilities:\n"
588 imap_has_capability(session, "CRAM-MD5"),
589 imap_has_capability(session, "LOGIN"));
590 if (imap_has_capability(session, "CRAM-MD5"))
591 ok = imap_cmd_login(session, user, pass, "CRAM-MD5");
592 if (ok == IMAP_ERROR) /* we always try LOGIN before giving up */
593 ok = imap_cmd_login(session, user, pass, "LOGIN");
595 if (ok == IMAP_SUCCESS)
596 session->authenticated = TRUE;
598 gchar *ext_info = NULL;
600 if (type == IMAP_AUTH_CRAM_MD5) {
601 ext_info = _("\n\nCRAM-MD5 logins only work if libetpan has been "
602 "compiled with SASL support and the "
603 "CRAM-MD5 SASL plugin is installed.");
608 if (time(NULL) - last_login_err > 10) {
609 alertpanel_error(_("Connection to %s failed: login refused.%s"),
610 SESSION(session)->server, ext_info);
612 last_login_err = time(NULL);
617 static IMAPSession *imap_reconnect_if_possible(Folder *folder, IMAPSession *session)
619 RemoteFolder *rfolder = REMOTE_FOLDER(folder);
620 /* Check if this is the first try to establish a
621 connection, if yes we don't try to reconnect */
622 debug_print("reconnecting\n");
623 if (rfolder->session == NULL) {
624 log_warning(_("Connecting to %s failed"),
625 folder->account->recv_server);
626 session_destroy(SESSION(session));
629 log_warning(_("IMAP4 connection to %s has been"
630 " disconnected. Reconnecting...\n"),
631 folder->account->recv_server);
632 statusbar_print_all(_("IMAP4 connection to %s has been"
633 " disconnected. Reconnecting...\n"),
634 folder->account->recv_server);
635 SESSION(session)->state = SESSION_DISCONNECTED;
636 session_destroy(SESSION(session));
637 /* Clear folders session to make imap_session_get create
638 a new session, because of rfolder->session == NULL
639 it will not try to reconnect again and so avoid an
641 rfolder->session = NULL;
642 session = imap_session_get(folder);
643 rfolder->session = SESSION(session);
649 static IMAPSession *imap_session_get(Folder *folder)
651 RemoteFolder *rfolder = REMOTE_FOLDER(folder);
652 IMAPSession *session = NULL;
654 g_return_val_if_fail(folder != NULL, NULL);
655 g_return_val_if_fail(FOLDER_CLASS(folder) == &imap_class, NULL);
656 g_return_val_if_fail(folder->account != NULL, NULL);
658 if (prefs_common.work_offline && !imap_gtk_should_override()) {
662 /* Make sure we have a session */
663 if (rfolder->session != NULL) {
664 session = IMAP_SESSION(rfolder->session);
666 imap_reset_uid_lists(folder);
667 session = imap_session_new(folder, folder->account);
672 /* Make sure session is authenticated */
673 if (!IMAP_SESSION(session)->authenticated)
674 imap_session_authenticate(IMAP_SESSION(session), folder->account);
676 if (!IMAP_SESSION(session)->authenticated) {
677 session_destroy(SESSION(session));
678 rfolder->session = NULL;
683 /* Make sure we have parsed the IMAP namespace */
684 imap_parse_namespace(IMAP_SESSION(session),
685 IMAP_FOLDER(folder));
688 /* I think the point of this code is to avoid sending a
689 * keepalive if we've used the session recently and therefore
690 * think it's still alive. Unfortunately, most of the code
691 * does not yet check for errors on the socket, and so if the
692 * connection drops we don't notice until the timeout expires.
693 * A better solution than sending a NOOP every time would be
694 * for every command to be prepared to retry until it is
695 * successfully sent. -- mbp */
696 if (time(NULL) - SESSION(session)->last_access_time > SESSION_TIMEOUT_INTERVAL) {
697 /* verify that the session is still alive */
698 if (imap_cmd_noop(session) != IMAP_SUCCESS) {
699 debug_print("disconnected!\n");
700 session = imap_reconnect_if_possible(folder, session);
704 rfolder->session = SESSION(session);
706 return IMAP_SESSION(session);
709 static IMAPSession *imap_session_new(Folder * folder,
710 const PrefsAccount *account)
712 IMAPSession *session;
718 /* FIXME: IMAP over SSL only... */
721 port = account->set_imapport ? account->imapport
722 : account->ssl_imap == SSL_TUNNEL ? IMAPS_PORT : IMAP4_PORT;
723 ssl_type = account->ssl_imap;
725 port = account->set_imapport ? account->imapport
730 statusbar_print_all(_("Connecting to IMAP4 server: %s..."), folder->account->recv_server);
731 if (account->set_tunnelcmd) {
732 r = imap_threaded_connect_cmd(folder,
734 account->recv_server,
739 if (ssl_type == SSL_TUNNEL) {
740 r = imap_threaded_connect_ssl(folder,
741 account->recv_server,
747 r = imap_threaded_connect(folder,
748 account->recv_server,
754 if (r == MAILIMAP_NO_ERROR_AUTHENTICATED) {
755 authenticated = TRUE;
757 else if (r == MAILIMAP_NO_ERROR_NON_AUTHENTICATED) {
758 authenticated = FALSE;
761 if(!prefs_common.no_recv_err_panel) {
762 alertpanel_error(_("Can't connect to IMAP4 server: %s:%d"),
763 account->recv_server, port);
765 log_error(_("Can't connect to IMAP4 server: %s:%d"),
766 account->recv_server, port);
772 session = g_new0(IMAPSession, 1);
773 session_init(SESSION(session));
774 SESSION(session)->type = SESSION_IMAP;
775 SESSION(session)->server = g_strdup(account->recv_server);
776 SESSION(session)->sock = NULL;
778 SESSION(session)->destroy = imap_session_destroy;
780 session->capability = NULL;
782 session->authenticated = authenticated;
783 session->mbox = NULL;
784 session->cmd_count = 0;
785 session->folder = folder;
786 IMAP_FOLDER(session->folder)->last_seen_separator = 0;
789 if (account->ssl_imap == SSL_STARTTLS) {
792 ok = imap_cmd_starttls(session);
793 if (ok != IMAP_SUCCESS) {
794 log_warning(_("Can't start TLS session.\n"));
795 session_destroy(SESSION(session));
799 imap_free_capabilities(session);
800 session->authenticated = FALSE;
801 session->uidplus = FALSE;
802 session->cmd_count = 1;
805 log_message("IMAP connection is %s-authenticated\n",
806 (session->authenticated) ? "pre" : "un");
811 static void imap_session_authenticate(IMAPSession *session,
812 const PrefsAccount *account)
816 g_return_if_fail(account->userid != NULL);
818 pass = account->passwd;
821 tmp_pass = input_dialog_query_password(account->recv_server, account->userid);
823 tmp_pass = g_strdup(""); /* allow empty password */
824 Xstrdup_a(pass, tmp_pass, {g_free(tmp_pass); return;});
827 statusbar_print_all(_("Connecting to IMAP4 server %s...\n"),
828 account->recv_server);
829 if (imap_auth(session, account->userid, pass, account->imap_auth_type) != IMAP_SUCCESS) {
830 imap_threaded_disconnect(session->folder);
831 imap_cmd_logout(session);
837 session->authenticated = TRUE;
840 static void imap_session_destroy(Session *session)
842 if (session->state != SESSION_DISCONNECTED)
843 imap_threaded_disconnect(IMAP_SESSION(session)->folder);
845 imap_free_capabilities(IMAP_SESSION(session));
846 g_free(IMAP_SESSION(session)->mbox);
847 sock_close(session->sock);
848 session->sock = NULL;
851 static gchar *imap_fetch_msg(Folder *folder, FolderItem *item, gint uid)
853 return imap_fetch_msg_full(folder, item, uid, TRUE, TRUE);
856 static guint get_size_with_crs(MsgInfo *info)
865 fp = procmsg_open_message(info);
869 while (fgets(buf, sizeof (buf), fp) != NULL) {
871 if (!strstr(buf, "\r") && strstr(buf, "\n"))
879 static gchar *imap_fetch_msg_full(Folder *folder, FolderItem *item, gint uid,
880 gboolean headers, gboolean body)
882 gchar *path, *filename;
883 IMAPSession *session;
886 g_return_val_if_fail(folder != NULL, NULL);
887 g_return_val_if_fail(item != NULL, NULL);
892 path = folder_item_get_path(item);
893 if (!is_dir_exist(path))
895 filename = g_strconcat(path, G_DIR_SEPARATOR_S, itos(uid), NULL);
898 if (is_file_exist(filename)) {
899 /* see whether the local file represents the whole message
900 * or not. As the IMAP server reports size with \r chars,
901 * we have to update the local file (UNIX \n only) size */
902 MsgInfo *msginfo = imap_parse_msg(filename, item);
903 MsgInfo *cached = msgcache_get_msg(item->cache,uid);
904 guint have_size = get_size_with_crs(msginfo);
907 debug_print("message %d has been already %scached (%d/%d).\n", uid,
908 have_size == cached->size ? "fully ":"",
909 have_size, (int)cached->size);
911 if (cached && (cached->size == have_size || !body)) {
912 procmsg_msginfo_free(cached);
913 procmsg_msginfo_free(msginfo);
914 file_strip_crs(filename);
917 procmsg_msginfo_free(cached);
918 procmsg_msginfo_free(msginfo);
922 session = imap_session_get(folder);
928 debug_print("IMAP fetching messages\n");
929 ok = imap_select(session, IMAP_FOLDER(folder), item->path,
930 NULL, NULL, NULL, NULL, FALSE);
931 if (ok != IMAP_SUCCESS) {
932 g_warning("can't select mailbox %s\n", item->path);
937 debug_print("getting message %d...\n", uid);
938 ok = imap_cmd_fetch(session, (guint32)uid, filename, headers, body);
940 if (ok != IMAP_SUCCESS) {
941 g_warning("can't fetch message %d\n", uid);
946 file_strip_crs(filename);
950 static gint imap_add_msg(Folder *folder, FolderItem *dest,
951 const gchar *file, MsgFlags *flags)
955 MsgFileInfo fileinfo;
957 g_return_val_if_fail(file != NULL, -1);
959 fileinfo.msginfo = NULL;
960 fileinfo.file = (gchar *)file;
961 fileinfo.flags = flags;
962 file_list.data = &fileinfo;
963 file_list.next = NULL;
965 ret = imap_add_msgs(folder, dest, &file_list, NULL);
969 static gint imap_add_msgs(Folder *folder, FolderItem *dest, GSList *file_list,
973 IMAPSession *session;
974 guint32 last_uid = 0;
976 MsgFileInfo *fileinfo;
980 g_return_val_if_fail(folder != NULL, -1);
981 g_return_val_if_fail(dest != NULL, -1);
982 g_return_val_if_fail(file_list != NULL, -1);
984 session = imap_session_get(folder);
988 destdir = imap_get_real_path(IMAP_FOLDER(folder), dest->path);
990 for (cur = file_list; cur != NULL; cur = cur->next) {
991 IMAPFlags iflags = 0;
994 fileinfo = (MsgFileInfo *)cur->data;
996 if (fileinfo->flags) {
997 if (MSG_IS_MARKED(*fileinfo->flags))
998 iflags |= IMAP_FLAG_FLAGGED;
999 if (MSG_IS_REPLIED(*fileinfo->flags))
1000 iflags |= IMAP_FLAG_ANSWERED;
1001 if (!MSG_IS_UNREAD(*fileinfo->flags))
1002 iflags |= IMAP_FLAG_SEEN;
1005 if (folder_has_parent_of_type(dest, F_QUEUE) ||
1006 folder_has_parent_of_type(dest, F_OUTBOX) ||
1007 folder_has_parent_of_type(dest, F_DRAFT) ||
1008 folder_has_parent_of_type(dest, F_TRASH))
1009 iflags |= IMAP_FLAG_SEEN;
1011 ok = imap_cmd_append(session, destdir, fileinfo->file, iflags,
1014 if (ok != IMAP_SUCCESS) {
1015 g_warning("can't append message %s\n", fileinfo->file);
1020 if (relation != NULL)
1021 g_relation_insert(relation, fileinfo->msginfo != NULL ?
1022 (gpointer) fileinfo->msginfo : (gpointer) fileinfo,
1023 GINT_TO_POINTER(dest->last_num + 1));
1024 if (last_uid < new_uid)
1033 static gint imap_do_copy_msgs(Folder *folder, FolderItem *dest,
1034 MsgInfoList *msglist, GRelation *relation)
1038 GSList *seq_list, *cur;
1040 IMAPSession *session;
1041 gint ok = IMAP_SUCCESS;
1042 GRelation *uid_mapping;
1045 g_return_val_if_fail(folder != NULL, -1);
1046 g_return_val_if_fail(dest != NULL, -1);
1047 g_return_val_if_fail(msglist != NULL, -1);
1049 session = imap_session_get(folder);
1054 msginfo = (MsgInfo *)msglist->data;
1056 src = msginfo->folder;
1058 g_warning("the src folder is identical to the dest.\n");
1062 ok = imap_select(session, IMAP_FOLDER(folder), msginfo->folder->path,
1063 NULL, NULL, NULL, NULL, FALSE);
1064 if (ok != IMAP_SUCCESS) {
1068 destdir = imap_get_real_path(IMAP_FOLDER(folder), dest->path);
1069 seq_list = imap_get_lep_set_from_msglist(msglist);
1070 uid_mapping = g_relation_new(2);
1071 g_relation_index(uid_mapping, 0, g_direct_hash, g_direct_equal);
1073 for (cur = seq_list; cur != NULL; cur = g_slist_next(cur)) {
1074 struct mailimap_set * seq_set;
1076 seq_set = cur->data;
1078 debug_print("Copying messages from %s to %s ...\n",
1079 src->path, destdir);
1081 ok = imap_cmd_copy(session, seq_set, destdir, uid_mapping);
1082 if (ok != IMAP_SUCCESS) {
1083 g_relation_destroy(uid_mapping);
1084 imap_lep_set_free(seq_list);
1089 for (cur = msglist; cur != NULL; cur = g_slist_next(cur)) {
1090 MsgInfo *msginfo = (MsgInfo *)cur->data;
1093 tuples = g_relation_select(uid_mapping,
1094 GINT_TO_POINTER(msginfo->msgnum),
1096 if (tuples->len > 0) {
1097 gint num = GPOINTER_TO_INT(g_tuples_index(tuples, 0, 1));
1098 g_relation_insert(relation, msginfo,
1099 GPOINTER_TO_INT(num));
1103 g_relation_insert(relation, msginfo,
1104 GPOINTER_TO_INT(0));
1105 g_tuples_destroy(tuples);
1108 g_relation_destroy(uid_mapping);
1109 imap_lep_set_free(seq_list);
1113 IMAP_FOLDER_ITEM(dest)->lastuid = 0;
1114 IMAP_FOLDER_ITEM(dest)->uid_next = 0;
1115 g_slist_free(IMAP_FOLDER_ITEM(dest)->uid_list);
1116 IMAP_FOLDER_ITEM(dest)->uid_list = NULL;
1118 if (ok == IMAP_SUCCESS)
1124 static gint imap_copy_msg(Folder *folder, FolderItem *dest, MsgInfo *msginfo)
1128 g_return_val_if_fail(msginfo != NULL, -1);
1130 msglist.data = msginfo;
1131 msglist.next = NULL;
1133 return imap_copy_msgs(folder, dest, &msglist, NULL);
1136 static gint imap_copy_msgs(Folder *folder, FolderItem *dest,
1137 MsgInfoList *msglist, GRelation *relation)
1143 g_return_val_if_fail(folder != NULL, -1);
1144 g_return_val_if_fail(dest != NULL, -1);
1145 g_return_val_if_fail(msglist != NULL, -1);
1147 msginfo = (MsgInfo *)msglist->data;
1148 g_return_val_if_fail(msginfo->folder != NULL, -1);
1150 if (folder == msginfo->folder->folder) {
1151 ret = imap_do_copy_msgs(folder, dest, msglist, relation);
1155 file_list = procmsg_get_message_file_list(msglist);
1156 g_return_val_if_fail(file_list != NULL, -1);
1158 ret = imap_add_msgs(folder, dest, file_list, relation);
1160 procmsg_message_file_list_free(file_list);
1166 static gint imap_do_remove_msgs(Folder *folder, FolderItem *dest,
1167 MsgInfoList *msglist, GRelation *relation)
1170 GSList *seq_list = NULL, *cur;
1172 IMAPSession *session;
1173 gint ok = IMAP_SUCCESS;
1174 GRelation *uid_mapping;
1176 g_return_val_if_fail(folder != NULL, -1);
1177 g_return_val_if_fail(dest != NULL, -1);
1178 g_return_val_if_fail(msglist != NULL, -1);
1180 session = imap_session_get(folder);
1184 msginfo = (MsgInfo *)msglist->data;
1186 ok = imap_select(session, IMAP_FOLDER(folder), msginfo->folder->path,
1187 NULL, NULL, NULL, NULL, FALSE);
1188 if (ok != IMAP_SUCCESS) {
1192 destdir = imap_get_real_path(IMAP_FOLDER(folder), dest->path);
1193 for (cur = msglist; cur; cur = cur->next) {
1194 msginfo = (MsgInfo *)cur->data;
1195 seq_list = g_slist_append(seq_list, GINT_TO_POINTER(msginfo->msgnum));
1198 uid_mapping = g_relation_new(2);
1199 g_relation_index(uid_mapping, 0, g_direct_hash, g_direct_equal);
1201 ok = imap_set_message_flags
1202 (IMAP_SESSION(REMOTE_FOLDER(folder)->session),
1203 seq_list, IMAP_FLAG_DELETED, TRUE);
1204 if (ok != IMAP_SUCCESS) {
1205 log_warning(_("can't set deleted flags\n"));
1208 ok = imap_cmd_expunge(session);
1209 if (ok != IMAP_SUCCESS) {
1210 log_warning(_("can't expunge\n"));
1214 g_relation_destroy(uid_mapping);
1215 g_slist_free(seq_list);
1219 if (ok == IMAP_SUCCESS)
1225 static gint imap_remove_msgs(Folder *folder, FolderItem *dest,
1226 MsgInfoList *msglist, GRelation *relation)
1230 g_return_val_if_fail(folder != NULL, -1);
1231 g_return_val_if_fail(dest != NULL, -1);
1232 if (msglist == NULL)
1235 msginfo = (MsgInfo *)msglist->data;
1236 g_return_val_if_fail(msginfo->folder != NULL, -1);
1238 return imap_do_remove_msgs(folder, dest, msglist, relation);
1241 static gint imap_remove_all_msg(Folder *folder, FolderItem *item)
1243 GSList *list = folder_item_get_msg_list(item);
1244 gint res = imap_remove_msgs(folder, item, list, NULL);
1245 procmsg_msg_list_free(list);
1249 static gboolean imap_is_msg_changed(Folder *folder, FolderItem *item,
1252 /* TODO: properly implement this method */
1256 static gint imap_close(Folder *folder, FolderItem *item)
1261 static gint imap_scan_tree(Folder *folder)
1263 FolderItem *item = NULL;
1264 IMAPSession *session;
1265 gchar *root_folder = NULL;
1267 g_return_val_if_fail(folder != NULL, -1);
1268 g_return_val_if_fail(folder->account != NULL, -1);
1270 session = imap_session_get(folder);
1272 if (!folder->node) {
1273 folder_tree_destroy(folder);
1274 item = folder_item_new(folder, folder->name, NULL);
1275 item->folder = folder;
1276 folder->node = item->node = g_node_new(item);
1281 if (folder->account->imap_dir && *folder->account->imap_dir) {
1286 Xstrdup_a(root_folder, folder->account->imap_dir, {return -1;});
1287 extract_quote(root_folder, '"');
1288 subst_char(root_folder,
1289 imap_get_path_separator(IMAP_FOLDER(folder),
1292 strtailchomp(root_folder, '/');
1293 real_path = imap_get_real_path
1294 (IMAP_FOLDER(folder), root_folder);
1295 debug_print("IMAP root directory: %s\n", real_path);
1297 /* check if root directory exist */
1299 r = imap_threaded_list(session->folder, "", real_path,
1301 if ((r != MAILIMAP_NO_ERROR) || (clist_count(lep_list) == 0)) {
1302 if (!folder->node) {
1303 item = folder_item_new(folder, folder->name, NULL);
1304 item->folder = folder;
1305 folder->node = item->node = g_node_new(item);
1310 mailimap_list_result_free(lep_list);
1316 item = FOLDER_ITEM(folder->node->data);
1317 if (!item || ((item->path || root_folder) &&
1318 strcmp2(item->path, root_folder) != 0)) {
1319 folder_tree_destroy(folder);
1320 item = folder_item_new(folder, folder->name, root_folder);
1321 item->folder = folder;
1322 folder->node = item->node = g_node_new(item);
1325 imap_scan_tree_recursive(session, FOLDER_ITEM(folder->node->data));
1326 imap_create_missing_folders(folder);
1331 static gint imap_scan_tree_recursive(IMAPSession *session, FolderItem *item)
1334 IMAPFolder *imapfolder;
1335 FolderItem *new_item;
1336 GSList *item_list, *cur;
1339 gchar *wildcard_path;
1345 g_return_val_if_fail(item != NULL, -1);
1346 g_return_val_if_fail(item->folder != NULL, -1);
1347 g_return_val_if_fail(item->no_sub == FALSE, -1);
1349 folder = item->folder;
1350 imapfolder = IMAP_FOLDER(folder);
1352 separator = imap_get_path_separator(imapfolder, item->path);
1354 if (folder->ui_func)
1355 folder->ui_func(folder, item, folder->ui_func_data);
1358 wildcard[0] = separator;
1361 real_path = imap_get_real_path(imapfolder, item->path);
1365 real_path = g_strdup("");
1368 Xstrcat_a(wildcard_path, real_path, wildcard,
1369 {g_free(real_path); return IMAP_ERROR;});
1371 r = imap_threaded_list(folder, "", wildcard_path, &lep_list);
1372 if (r != MAILIMAP_NO_ERROR) {
1376 item_list = imap_list_from_lep(imapfolder,
1377 lep_list, real_path);
1378 mailimap_list_result_free(lep_list);
1383 node = item->node->children;
1384 while (node != NULL) {
1385 FolderItem *old_item = FOLDER_ITEM(node->data);
1386 GNode *next = node->next;
1389 for (cur = item_list; cur != NULL; cur = cur->next) {
1390 FolderItem *cur_item = FOLDER_ITEM(cur->data);
1391 if (!strcmp2(old_item->path, cur_item->path)) {
1392 new_item = cur_item;
1397 debug_print("folder '%s' not found. removing...\n",
1399 folder_item_remove(old_item);
1401 old_item->no_sub = new_item->no_sub;
1402 old_item->no_select = new_item->no_select;
1403 if (old_item->no_sub == TRUE && node->children) {
1404 debug_print("folder '%s' doesn't have "
1405 "subfolders. removing...\n",
1407 folder_item_remove_children(old_item);
1414 for (cur = item_list; cur != NULL; cur = cur->next) {
1415 FolderItem *cur_item = FOLDER_ITEM(cur->data);
1417 for (node = item->node->children; node != NULL;
1418 node = node->next) {
1419 if (!strcmp2(FOLDER_ITEM(node->data)->path,
1421 new_item = FOLDER_ITEM(node->data);
1422 folder_item_destroy(cur_item);
1428 new_item = cur_item;
1429 debug_print("new folder '%s' found.\n", new_item->path);
1430 folder_item_append(item, new_item);
1433 if (!strcmp(new_item->path, "INBOX")) {
1434 new_item->stype = F_INBOX;
1435 folder->inbox = new_item;
1436 } else if (!folder_item_parent(item) || item->stype == F_INBOX) {
1439 base = g_path_get_basename(new_item->path);
1441 if (!folder->outbox && !g_ascii_strcasecmp(base, "Sent")) {
1442 new_item->stype = F_OUTBOX;
1443 folder->outbox = new_item;
1444 } else if (!folder->draft && !g_ascii_strcasecmp(base, "Drafts")) {
1445 new_item->stype = F_DRAFT;
1446 folder->draft = new_item;
1447 } else if (!folder->queue && !g_ascii_strcasecmp(base, "Queue")) {
1448 new_item->stype = F_QUEUE;
1449 folder->queue = new_item;
1450 } else if (!folder->trash && !g_ascii_strcasecmp(base, "Trash")) {
1451 new_item->stype = F_TRASH;
1452 folder->trash = new_item;
1457 if (new_item->no_sub == FALSE)
1458 imap_scan_tree_recursive(session, new_item);
1461 g_slist_free(item_list);
1463 return IMAP_SUCCESS;
1466 static gint imap_create_tree(Folder *folder)
1468 g_return_val_if_fail(folder != NULL, -1);
1469 g_return_val_if_fail(folder->node != NULL, -1);
1470 g_return_val_if_fail(folder->node->data != NULL, -1);
1471 g_return_val_if_fail(folder->account != NULL, -1);
1473 imap_scan_tree(folder);
1474 imap_create_missing_folders(folder);
1479 static void imap_create_missing_folders(Folder *folder)
1481 g_return_if_fail(folder != NULL);
1484 folder->inbox = imap_create_special_folder
1485 (folder, F_INBOX, "INBOX");
1487 folder->trash = imap_create_special_folder
1488 (folder, F_TRASH, "Trash");
1490 folder->queue = imap_create_special_folder
1491 (folder, F_QUEUE, "Queue");
1492 if (!folder->outbox)
1493 folder->outbox = imap_create_special_folder
1494 (folder, F_OUTBOX, "Sent");
1496 folder->draft = imap_create_special_folder
1497 (folder, F_DRAFT, "Drafts");
1500 static FolderItem *imap_create_special_folder(Folder *folder,
1501 SpecialFolderItemType stype,
1505 FolderItem *new_item;
1507 g_return_val_if_fail(folder != NULL, NULL);
1508 g_return_val_if_fail(folder->node != NULL, NULL);
1509 g_return_val_if_fail(folder->node->data != NULL, NULL);
1510 g_return_val_if_fail(folder->account != NULL, NULL);
1511 g_return_val_if_fail(name != NULL, NULL);
1513 item = FOLDER_ITEM(folder->node->data);
1514 new_item = imap_create_folder(folder, item, name);
1517 g_warning("Can't create '%s'\n", name);
1518 if (!folder->inbox) return NULL;
1520 new_item = imap_create_folder(folder, folder->inbox, name);
1522 g_warning("Can't create '%s' under INBOX\n", name);
1524 new_item->stype = stype;
1526 new_item->stype = stype;
1531 static gchar *imap_folder_get_path(Folder *folder)
1535 g_return_val_if_fail(folder != NULL, NULL);
1536 g_return_val_if_fail(folder->account != NULL, NULL);
1538 folder_path = g_strconcat(get_imap_cache_dir(),
1540 folder->account->recv_server,
1542 folder->account->userid,
1548 static gchar *imap_item_get_path(Folder *folder, FolderItem *item)
1550 gchar *folder_path, *path;
1552 g_return_val_if_fail(folder != NULL, NULL);
1553 g_return_val_if_fail(item != NULL, NULL);
1554 folder_path = imap_folder_get_path(folder);
1556 g_return_val_if_fail(folder_path != NULL, NULL);
1557 if (folder_path[0] == G_DIR_SEPARATOR) {
1559 path = g_strconcat(folder_path, G_DIR_SEPARATOR_S,
1562 path = g_strdup(folder_path);
1565 path = g_strconcat(get_home_dir(), G_DIR_SEPARATOR_S,
1566 folder_path, G_DIR_SEPARATOR_S,
1569 path = g_strconcat(get_home_dir(), G_DIR_SEPARATOR_S,
1572 g_free(folder_path);
1577 static FolderItem *imap_create_folder(Folder *folder, FolderItem *parent,
1580 gchar *dirpath, *imap_path;
1581 IMAPSession *session;
1582 FolderItem *new_item;
1588 g_return_val_if_fail(folder != NULL, NULL);
1589 g_return_val_if_fail(folder->account != NULL, NULL);
1590 g_return_val_if_fail(parent != NULL, NULL);
1591 g_return_val_if_fail(name != NULL, NULL);
1593 session = imap_session_get(folder);
1598 if (!folder_item_parent(parent) && strcmp(name, "INBOX") == 0)
1599 dirpath = g_strdup(name);
1600 else if (parent->path)
1601 dirpath = g_strconcat(parent->path, "/", name, NULL);
1602 else if ((p = strchr(name, '/')) != NULL && *(p + 1) != '\0')
1603 dirpath = g_strdup(name);
1604 else if (folder->account->imap_dir && *folder->account->imap_dir) {
1607 Xstrdup_a(imap_dir, folder->account->imap_dir, {return NULL;});
1608 strtailchomp(imap_dir, '/');
1609 dirpath = g_strconcat(imap_dir, "/", name, NULL);
1611 dirpath = g_strdup(name);
1613 /* keep trailing directory separator to create a folder that contains
1615 imap_path = imap_utf8_to_modified_utf7(dirpath);
1616 strtailchomp(dirpath, '/');
1617 Xstrdup_a(new_name, name, {
1621 strtailchomp(new_name, '/');
1622 separator = imap_get_path_separator(IMAP_FOLDER(folder), imap_path);
1623 imap_path_separator_subst(imap_path, separator);
1624 subst_char(new_name, '/', separator);
1626 if (strcmp(name, "INBOX") != 0) {
1628 gboolean exist = FALSE;
1632 argbuf = g_ptr_array_new();
1633 r = imap_threaded_list(folder, "", imap_path, &lep_list);
1634 if (r != MAILIMAP_NO_ERROR) {
1635 log_warning(_("can't create mailbox: LIST failed\n"));
1638 ptr_array_free_strings(argbuf);
1639 g_ptr_array_free(argbuf, TRUE);
1643 if (clist_count(lep_list) > 0)
1647 ok = imap_cmd_create(session, imap_path);
1648 if (ok != IMAP_SUCCESS) {
1649 log_warning(_("can't create mailbox\n"));
1657 new_item = folder_item_new(folder, new_name, dirpath);
1658 folder_item_append(parent, new_item);
1662 dirpath = folder_item_get_path(new_item);
1663 if (!is_dir_exist(dirpath))
1664 make_dir_hier(dirpath);
1670 static gint imap_rename_folder(Folder *folder, FolderItem *item,
1675 gchar *real_oldpath;
1676 gchar *real_newpath;
1678 gchar *old_cache_dir;
1679 gchar *new_cache_dir;
1680 IMAPSession *session;
1683 gint exists, recent, unseen;
1684 guint32 uid_validity;
1686 g_return_val_if_fail(folder != NULL, -1);
1687 g_return_val_if_fail(item != NULL, -1);
1688 g_return_val_if_fail(item->path != NULL, -1);
1689 g_return_val_if_fail(name != NULL, -1);
1691 if (strchr(name, imap_get_path_separator(IMAP_FOLDER(folder), item->path)) != NULL) {
1692 g_warning(_("New folder name must not contain the namespace "
1697 session = imap_session_get(folder);
1701 real_oldpath = imap_get_real_path(IMAP_FOLDER(folder), item->path);
1703 g_free(session->mbox);
1704 session->mbox = NULL;
1705 ok = imap_cmd_examine(session, "INBOX",
1706 &exists, &recent, &unseen, &uid_validity, FALSE);
1707 if (ok != IMAP_SUCCESS) {
1708 g_free(real_oldpath);
1712 separator = imap_get_path_separator(IMAP_FOLDER(folder), item->path);
1713 if (strchr(item->path, G_DIR_SEPARATOR)) {
1714 dirpath = g_path_get_dirname(item->path);
1715 newpath = g_strconcat(dirpath, G_DIR_SEPARATOR_S, name, NULL);
1718 newpath = g_strdup(name);
1720 real_newpath = imap_utf8_to_modified_utf7(newpath);
1721 imap_path_separator_subst(real_newpath, separator);
1723 ok = imap_cmd_rename(session, real_oldpath, real_newpath);
1724 if (ok != IMAP_SUCCESS) {
1725 log_warning(_("can't rename mailbox: %s to %s\n"),
1726 real_oldpath, real_newpath);
1727 g_free(real_oldpath);
1729 g_free(real_newpath);
1734 item->name = g_strdup(name);
1736 old_cache_dir = folder_item_get_path(item);
1738 paths[0] = g_strdup(item->path);
1740 g_node_traverse(item->node, G_PRE_ORDER, G_TRAVERSE_ALL, -1,
1741 imap_rename_folder_func, paths);
1743 if (is_dir_exist(old_cache_dir)) {
1744 new_cache_dir = folder_item_get_path(item);
1745 if (rename(old_cache_dir, new_cache_dir) < 0) {
1746 FILE_OP_ERROR(old_cache_dir, "rename");
1748 g_free(new_cache_dir);
1751 g_free(old_cache_dir);
1754 g_free(real_oldpath);
1755 g_free(real_newpath);
1760 static gint imap_remove_folder_real(Folder *folder, FolderItem *item)
1763 IMAPSession *session;
1766 gint exists, recent, unseen;
1767 guint32 uid_validity;
1769 g_return_val_if_fail(folder != NULL, -1);
1770 g_return_val_if_fail(item != NULL, -1);
1771 g_return_val_if_fail(item->path != NULL, -1);
1773 session = imap_session_get(folder);
1777 path = imap_get_real_path(IMAP_FOLDER(folder), item->path);
1779 ok = imap_cmd_examine(session, "INBOX",
1780 &exists, &recent, &unseen, &uid_validity, FALSE);
1781 if (ok != IMAP_SUCCESS) {
1786 ok = imap_cmd_delete(session, path);
1787 if (ok != IMAP_SUCCESS) {
1788 log_warning(_("can't delete mailbox\n"));
1794 cache_dir = folder_item_get_path(item);
1795 if (is_dir_exist(cache_dir) && remove_dir_recursive(cache_dir) < 0)
1796 g_warning("can't remove directory '%s'\n", cache_dir);
1798 folder_item_remove(item);
1803 static gint imap_remove_folder(Folder *folder, FolderItem *item)
1807 g_return_val_if_fail(item != NULL, -1);
1808 g_return_val_if_fail(item->folder != NULL, -1);
1809 g_return_val_if_fail(item->node != NULL, -1);
1811 node = item->node->children;
1812 while (node != NULL) {
1814 if (imap_remove_folder(folder, FOLDER_ITEM(node->data)) < 0)
1818 debug_print("IMAP removing %s\n", item->path);
1820 if (imap_remove_all_msg(folder, item) < 0)
1822 return imap_remove_folder_real(folder, item);
1825 typedef struct _uncached_data {
1826 IMAPSession *session;
1828 MsgNumberList *numlist;
1834 static void *imap_get_uncached_messages_thread(void *data)
1836 uncached_data *stuff = (uncached_data *)data;
1837 IMAPSession *session = stuff->session;
1838 FolderItem *item = stuff->item;
1839 MsgNumberList *numlist = stuff->numlist;
1841 GSList *newlist = NULL;
1842 GSList *llast = NULL;
1843 GSList *seq_list, *cur;
1845 debug_print("uncached_messages\n");
1847 if (session == NULL || item == NULL || item->folder == NULL
1848 || FOLDER_CLASS(item->folder) != &imap_class) {
1853 seq_list = imap_get_lep_set_from_numlist(numlist);
1854 debug_print("get msgs info\n");
1855 for (cur = seq_list; cur != NULL; cur = g_slist_next(cur)) {
1856 struct mailimap_set * imapset;
1862 imapset = cur->data;
1864 r = imap_threaded_fetch_env(session->folder,
1865 imapset, &env_list);
1866 if (r != MAILIMAP_NO_ERROR)
1870 for(i = 0 ; i < carray_count(env_list) ; i ++) {
1871 struct imap_fetch_env_info * info;
1874 info = carray_get(env_list, i);
1875 msginfo = imap_envelope_from_lep(info, item);
1876 msginfo->folder = item;
1878 llast = newlist = g_slist_append(newlist, msginfo);
1880 llast = g_slist_append(llast, msginfo);
1881 llast = llast->next;
1886 imap_fetch_env_free(env_list);
1889 session_set_access_time(SESSION(session));
1894 #define MAX_MSG_NUM 50
1896 static GSList *imap_get_uncached_messages(IMAPSession *session,
1898 MsgNumberList *numlist)
1900 GSList *result = NULL;
1902 uncached_data *data = g_new0(uncached_data, 1);
1907 data->total = g_slist_length(numlist);
1908 debug_print("messages list : %i\n", data->total);
1910 while (cur != NULL) {
1911 GSList * partial_result;
1919 while (count < MAX_MSG_NUM) {
1924 if (newlist == NULL)
1925 llast = newlist = g_slist_append(newlist, p);
1927 llast = g_slist_append(llast, p);
1928 llast = llast->next;
1938 data->session = session;
1940 data->numlist = newlist;
1943 if (prefs_common.work_offline && !imap_gtk_should_override()) {
1949 (GSList *)imap_get_uncached_messages_thread(data);
1951 statusbar_progress_all(data->cur,data->total, 1);
1953 g_slist_free(newlist);
1955 result = g_slist_concat(result, partial_result);
1959 statusbar_progress_all(0,0,0);
1960 statusbar_pop_all();
1965 static void imap_delete_all_cached_messages(FolderItem *item)
1969 g_return_if_fail(item != NULL);
1970 g_return_if_fail(item->folder != NULL);
1971 g_return_if_fail(FOLDER_CLASS(item->folder) == &imap_class);
1973 debug_print("Deleting all cached messages...\n");
1975 dir = folder_item_get_path(item);
1976 if (is_dir_exist(dir))
1977 remove_all_numbered_files(dir);
1980 debug_print("done.\n");
1983 static IMAPNameSpace *imap_find_namespace_from_list(GList *ns_list,
1986 IMAPNameSpace *namespace = NULL;
1987 gchar *tmp_path, *name;
1989 if (!path) path = "";
1991 for (; ns_list != NULL; ns_list = ns_list->next) {
1992 IMAPNameSpace *tmp_ns = ns_list->data;
1994 Xstrcat_a(tmp_path, path, "/", return namespace);
1995 Xstrdup_a(name, tmp_ns->name, return namespace);
1996 if (tmp_ns->separator && tmp_ns->separator != '/') {
1997 subst_char(tmp_path, tmp_ns->separator, '/');
1998 subst_char(name, tmp_ns->separator, '/');
2000 if (strncmp(tmp_path, name, strlen(name)) == 0)
2007 static IMAPNameSpace *imap_find_namespace(IMAPFolder *folder,
2010 IMAPNameSpace *namespace;
2012 g_return_val_if_fail(folder != NULL, NULL);
2014 namespace = imap_find_namespace_from_list(folder->ns_personal, path);
2015 if (namespace) return namespace;
2016 namespace = imap_find_namespace_from_list(folder->ns_others, path);
2017 if (namespace) return namespace;
2018 namespace = imap_find_namespace_from_list(folder->ns_shared, path);
2019 if (namespace) return namespace;
2025 static gchar imap_get_path_separator(IMAPFolder *folder, const gchar *path)
2027 IMAPNameSpace *namespace;
2028 gchar separator = '/';
2030 if (folder->last_seen_separator == 0) {
2032 int r = imap_threaded_list((Folder *)folder, "", "", &lep_list);
2033 if (r != MAILIMAP_NO_ERROR) {
2034 log_warning(_("LIST failed\n"));
2038 if (clist_count(lep_list) > 0) {
2039 clistiter * iter = clist_begin(lep_list);
2040 struct mailimap_mailbox_list * mb;
2041 mb = clist_content(iter);
2043 folder->last_seen_separator = mb->mb_delimiter;
2044 debug_print("got separator: %c\n", folder->last_seen_separator);
2046 mailimap_list_result_free(lep_list);
2049 if (folder->last_seen_separator != 0) {
2050 debug_print("using separator: %c\n", folder->last_seen_separator);
2051 return folder->last_seen_separator;
2054 namespace = imap_find_namespace(folder, path);
2055 if (namespace && namespace->separator)
2056 separator = namespace->separator;
2061 static gchar *imap_get_real_path(IMAPFolder *folder, const gchar *path)
2066 g_return_val_if_fail(folder != NULL, NULL);
2067 g_return_val_if_fail(path != NULL, NULL);
2069 real_path = imap_utf8_to_modified_utf7(path);
2070 separator = imap_get_path_separator(folder, path);
2071 imap_path_separator_subst(real_path, separator);
2076 static gint imap_set_message_flags(IMAPSession *session,
2077 MsgNumberList *numlist,
2085 seq_list = imap_get_lep_set_from_numlist(numlist);
2087 for(cur = seq_list ; cur != NULL ; cur = g_slist_next(cur)) {
2088 struct mailimap_set * imapset;
2090 imapset = cur->data;
2092 ok = imap_cmd_store(session, imapset,
2096 imap_lep_set_free(seq_list);
2098 return IMAP_SUCCESS;
2101 typedef struct _select_data {
2102 IMAPSession *session;
2107 guint32 *uid_validity;
2111 static gint imap_select(IMAPSession *session, IMAPFolder *folder,
2113 gint *exists, gint *recent, gint *unseen,
2114 guint32 *uid_validity, gboolean block)
2118 gint exists_, recent_, unseen_;
2119 guint32 uid_validity_;
2121 if (!exists || !recent || !unseen || !uid_validity) {
2122 if (session->mbox && strcmp(session->mbox, path) == 0)
2123 return IMAP_SUCCESS;
2127 uid_validity = &uid_validity_;
2130 g_free(session->mbox);
2131 session->mbox = NULL;
2133 real_path = imap_get_real_path(folder, path);
2135 ok = imap_cmd_select(session, real_path,
2136 exists, recent, unseen, uid_validity, block);
2137 if (ok != IMAP_SUCCESS)
2138 log_warning(_("can't select folder: %s\n"), real_path);
2140 session->mbox = g_strdup(path);
2141 session->folder_content_changed = FALSE;
2148 static gint imap_status(IMAPSession *session, IMAPFolder *folder,
2150 gint *messages, gint *recent,
2151 guint32 *uid_next, guint32 *uid_validity,
2152 gint *unseen, gboolean block)
2156 struct mailimap_mailbox_data_status * data_status;
2160 real_path = imap_get_real_path(folder, path);
2162 r = imap_threaded_status(FOLDER(folder), real_path, &data_status);
2164 if (r != MAILIMAP_NO_ERROR) {
2165 debug_print("status err %d\n", r);
2169 if (data_status->st_info_list == NULL) {
2170 mailimap_mailbox_data_status_free(data_status);
2171 debug_print("status->st_info_list == NULL\n");
2176 for(iter = clist_begin(data_status->st_info_list) ; iter != NULL ;
2177 iter = clist_next(iter)) {
2178 struct mailimap_status_info * info;
2180 info = clist_content(iter);
2181 switch (info->st_att) {
2182 case MAILIMAP_STATUS_ATT_MESSAGES:
2183 * messages = info->st_value;
2184 got_values |= 1 << 0;
2187 case MAILIMAP_STATUS_ATT_RECENT:
2188 * recent = info->st_value;
2189 got_values |= 1 << 1;
2192 case MAILIMAP_STATUS_ATT_UIDNEXT:
2193 * uid_next = info->st_value;
2194 got_values |= 1 << 2;
2197 case MAILIMAP_STATUS_ATT_UIDVALIDITY:
2198 * uid_validity = info->st_value;
2199 got_values |= 1 << 3;
2202 case MAILIMAP_STATUS_ATT_UNSEEN:
2203 * unseen = info->st_value;
2204 got_values |= 1 << 4;
2208 mailimap_mailbox_data_status_free(data_status);
2210 if (got_values != ((1 << 4) + (1 << 3) +
2211 (1 << 2) + (1 << 1) + (1 << 0))) {
2212 debug_print("status: incomplete values received (%d)\n", got_values);
2215 return IMAP_SUCCESS;
2218 static void imap_free_capabilities(IMAPSession *session)
2220 slist_free_strings(session->capability);
2221 g_slist_free(session->capability);
2222 session->capability = NULL;
2225 /* low-level IMAP4rev1 commands */
2228 static gint imap_cmd_authenticate(IMAPSession *session, const gchar *user,
2229 const gchar *pass, IMAPAuthType type)
2236 gchar hexdigest[33];
2240 auth_type = "CRAM-MD5";
2242 imap_gen_send(session, "AUTHENTICATE %s", auth_type);
2243 ok = imap_gen_recv(session, &buf);
2244 if (ok != IMAP_SUCCESS || buf[0] != '+' || buf[1] != ' ') {
2249 challenge = g_malloc(strlen(buf + 2) + 1);
2250 challenge_len = base64_decode(challenge, buf + 2, -1);
2251 challenge[challenge_len] = '\0';
2254 md5_hex_hmac(hexdigest, challenge, challenge_len, pass, strlen(pass));
2257 response = g_strdup_printf("%s %s", user, hexdigest);
2258 response64 = g_malloc((strlen(response) + 3) * 2 + 1);
2259 base64_encode(response64, response, strlen(response));
2262 sock_puts(SESSION(session)->sock, response64);
2263 ok = imap_cmd_ok(session, NULL);
2264 if (ok != IMAP_SUCCESS)
2265 log_warning(_("IMAP4 authentication failed.\n"));
2271 static gint imap_cmd_login(IMAPSession *session,
2272 const gchar *user, const gchar *pass,
2278 log_print("IMAP4> Logging %s to %s using %s\n",
2280 SESSION(session)->server,
2282 r = imap_threaded_login(session->folder, user, pass, type);
2283 if (r != MAILIMAP_NO_ERROR) {
2284 log_error("IMAP4< Error logging in to %s\n",
2285 SESSION(session)->server);
2293 static gint imap_cmd_logout(IMAPSession *session)
2295 imap_threaded_disconnect(session->folder);
2297 return IMAP_SUCCESS;
2300 static gint imap_cmd_noop(IMAPSession *session)
2303 unsigned int exists;
2305 r = imap_threaded_noop(session->folder, &exists);
2306 if (r != MAILIMAP_NO_ERROR) {
2307 debug_print("noop err %d\n", r);
2310 session->exists = exists;
2311 session_set_access_time(SESSION(session));
2313 return IMAP_SUCCESS;
2317 static gint imap_cmd_starttls(IMAPSession *session)
2321 r = imap_threaded_starttls(session->folder);
2322 if (r != MAILIMAP_NO_ERROR) {
2323 debug_print("starttls err %d\n", r);
2326 return IMAP_SUCCESS;
2330 static gint imap_cmd_select(IMAPSession *session, const gchar *folder,
2331 gint *exists, gint *recent, gint *unseen,
2332 guint32 *uid_validity, gboolean block)
2336 r = imap_threaded_select(session->folder, folder,
2337 exists, recent, unseen, uid_validity);
2338 if (r != MAILIMAP_NO_ERROR) {
2339 debug_print("select err %d\n", r);
2342 return IMAP_SUCCESS;
2345 static gint imap_cmd_examine(IMAPSession *session, const gchar *folder,
2346 gint *exists, gint *recent, gint *unseen,
2347 guint32 *uid_validity, gboolean block)
2351 r = imap_threaded_examine(session->folder, folder,
2352 exists, recent, unseen, uid_validity);
2353 if (r != MAILIMAP_NO_ERROR) {
2354 debug_print("examine err %d\n", r);
2358 return IMAP_SUCCESS;
2361 static gint imap_cmd_create(IMAPSession *session, const gchar *folder)
2365 r = imap_threaded_create(session->folder, folder);
2366 if (r != MAILIMAP_NO_ERROR) {
2371 return IMAP_SUCCESS;
2374 static gint imap_cmd_rename(IMAPSession *session, const gchar *old_folder,
2375 const gchar *new_folder)
2379 r = imap_threaded_rename(session->folder, old_folder,
2381 if (r != MAILIMAP_NO_ERROR) {
2386 return IMAP_SUCCESS;
2389 static gint imap_cmd_delete(IMAPSession *session, const gchar *folder)
2394 r = imap_threaded_delete(session->folder, folder);
2395 if (r != MAILIMAP_NO_ERROR) {
2400 return IMAP_SUCCESS;
2403 typedef struct _fetch_data {
2404 IMAPSession *session;
2406 const gchar *filename;
2412 static void *imap_cmd_fetch_thread(void *data)
2414 fetch_data *stuff = (fetch_data *)data;
2415 IMAPSession *session = stuff->session;
2416 guint32 uid = stuff->uid;
2417 const gchar *filename = stuff->filename;
2421 r = imap_threaded_fetch_content(session->folder,
2425 r = imap_threaded_fetch_content(session->folder,
2428 if (r != MAILIMAP_NO_ERROR) {
2429 debug_print("fetch err %d\n", r);
2430 return GINT_TO_POINTER(IMAP_ERROR);
2432 return GINT_TO_POINTER(IMAP_SUCCESS);
2435 static gint imap_cmd_fetch(IMAPSession *session, guint32 uid,
2436 const gchar *filename, gboolean headers,
2439 fetch_data *data = g_new0(fetch_data, 1);
2442 data->session = session;
2444 data->filename = filename;
2445 data->headers = headers;
2448 if (prefs_common.work_offline && !imap_gtk_should_override()) {
2452 statusbar_print_all(_("Fetching message..."));
2453 result = GPOINTER_TO_INT(imap_cmd_fetch_thread(data));
2454 statusbar_pop_all();
2460 static gint imap_cmd_append(IMAPSession *session, const gchar *destfolder,
2461 const gchar *file, IMAPFlags flags,
2464 struct mailimap_flag_list * flag_list;
2467 g_return_val_if_fail(file != NULL, IMAP_ERROR);
2469 flag_list = imap_flag_to_lep(flags);
2470 statusbar_print_all(_("Adding messages..."));
2471 r = imap_threaded_append(session->folder, destfolder,
2473 statusbar_pop_all();
2474 if (new_uid != NULL)
2477 if (r != MAILIMAP_NO_ERROR) {
2478 debug_print("append err %d\n", r);
2481 return IMAP_SUCCESS;
2484 static gint imap_cmd_copy(IMAPSession *session, struct mailimap_set * set,
2485 const gchar *destfolder, GRelation *uid_mapping)
2489 g_return_val_if_fail(session != NULL, IMAP_ERROR);
2490 g_return_val_if_fail(set != NULL, IMAP_ERROR);
2491 g_return_val_if_fail(destfolder != NULL, IMAP_ERROR);
2493 statusbar_print_all(_("Copying messages..."));
2494 r = imap_threaded_copy(session->folder, set, destfolder);
2495 statusbar_pop_all();
2496 if (r != MAILIMAP_NO_ERROR) {
2501 return IMAP_SUCCESS;
2504 static gint imap_cmd_store(IMAPSession *session, struct mailimap_set * set,
2505 IMAPFlags flags, int do_add)
2508 struct mailimap_flag_list * flag_list;
2509 struct mailimap_store_att_flags * store_att_flags;
2511 flag_list = imap_flag_to_lep(flags);
2515 mailimap_store_att_flags_new_add_flags_silent(flag_list);
2518 mailimap_store_att_flags_new_remove_flags_silent(flag_list);
2520 r = imap_threaded_store(session->folder, set, store_att_flags);
2521 if (r != MAILIMAP_NO_ERROR) {
2526 return IMAP_SUCCESS;
2529 static gint imap_cmd_expunge(IMAPSession *session)
2533 if (prefs_common.work_offline && !imap_gtk_should_override()) {
2537 r = imap_threaded_expunge(session->folder);
2538 if (r != MAILIMAP_NO_ERROR) {
2543 return IMAP_SUCCESS;
2546 static void imap_path_separator_subst(gchar *str, gchar separator)
2549 gboolean in_escape = FALSE;
2551 if (!separator || separator == '/') return;
2553 for (p = str; *p != '\0'; p++) {
2554 if (*p == '/' && !in_escape)
2556 else if (*p == '&' && *(p + 1) != '-' && !in_escape)
2558 else if (*p == '-' && in_escape)
2563 static gchar *imap_modified_utf7_to_utf8(const gchar *mutf7_str)
2565 static iconv_t cd = (iconv_t)-1;
2566 static gboolean iconv_ok = TRUE;
2569 size_t norm_utf7_len;
2571 gchar *to_str, *to_p;
2573 gboolean in_escape = FALSE;
2575 if (!iconv_ok) return g_strdup(mutf7_str);
2577 if (cd == (iconv_t)-1) {
2578 cd = iconv_open(CS_INTERNAL, CS_UTF_7);
2579 if (cd == (iconv_t)-1) {
2580 g_warning("iconv cannot convert UTF-7 to %s\n",
2583 return g_strdup(mutf7_str);
2587 /* modified UTF-7 to normal UTF-7 conversion */
2588 norm_utf7 = g_string_new(NULL);
2590 for (p = mutf7_str; *p != '\0'; p++) {
2591 /* replace: '&' -> '+',
2593 escaped ',' -> '/' */
2594 if (!in_escape && *p == '&') {
2595 if (*(p + 1) != '-') {
2596 g_string_append_c(norm_utf7, '+');
2599 g_string_append_c(norm_utf7, '&');
2602 } else if (in_escape && *p == ',') {
2603 g_string_append_c(norm_utf7, '/');
2604 } else if (in_escape && *p == '-') {
2605 g_string_append_c(norm_utf7, '-');
2608 g_string_append_c(norm_utf7, *p);
2612 norm_utf7_p = norm_utf7->str;
2613 norm_utf7_len = norm_utf7->len;
2614 to_len = strlen(mutf7_str) * 5;
2615 to_p = to_str = g_malloc(to_len + 1);
2617 if (iconv(cd, (ICONV_CONST gchar **)&norm_utf7_p, &norm_utf7_len,
2618 &to_p, &to_len) == -1) {
2619 g_warning(_("iconv cannot convert UTF-7 to %s\n"),
2620 conv_get_locale_charset_str());
2621 g_string_free(norm_utf7, TRUE);
2623 return g_strdup(mutf7_str);
2626 /* second iconv() call for flushing */
2627 iconv(cd, NULL, NULL, &to_p, &to_len);
2628 g_string_free(norm_utf7, TRUE);
2634 static gchar *imap_utf8_to_modified_utf7(const gchar *from)
2636 static iconv_t cd = (iconv_t)-1;
2637 static gboolean iconv_ok = TRUE;
2638 gchar *norm_utf7, *norm_utf7_p;
2639 size_t from_len, norm_utf7_len;
2641 gchar *from_tmp, *to, *p;
2642 gboolean in_escape = FALSE;
2644 if (!iconv_ok) return g_strdup(from);
2646 if (cd == (iconv_t)-1) {
2647 cd = iconv_open(CS_UTF_7, CS_INTERNAL);
2648 if (cd == (iconv_t)-1) {
2649 g_warning(_("iconv cannot convert %s to UTF-7\n"),
2652 return g_strdup(from);
2656 /* UTF-8 to normal UTF-7 conversion */
2657 Xstrdup_a(from_tmp, from, return g_strdup(from));
2658 from_len = strlen(from);
2659 norm_utf7_len = from_len * 5;
2660 Xalloca(norm_utf7, norm_utf7_len + 1, return g_strdup(from));
2661 norm_utf7_p = norm_utf7;
2663 #define IS_PRINT(ch) (isprint(ch) && IS_ASCII(ch))
2665 while (from_len > 0) {
2666 if (*from_tmp == '+') {
2667 *norm_utf7_p++ = '+';
2668 *norm_utf7_p++ = '-';
2672 } else if (IS_PRINT(*(guchar *)from_tmp)) {
2673 /* printable ascii char */
2674 *norm_utf7_p = *from_tmp;
2680 size_t conv_len = 0;
2682 /* unprintable char: convert to UTF-7 */
2684 while (!IS_PRINT(*(guchar *)p) && conv_len < from_len) {
2685 conv_len += g_utf8_skip[*(guchar *)p];
2686 p += g_utf8_skip[*(guchar *)p];
2689 from_len -= conv_len;
2690 if (iconv(cd, (ICONV_CONST gchar **)&from_tmp,
2692 &norm_utf7_p, &norm_utf7_len) == -1) {
2693 g_warning(_("iconv cannot convert UTF-8 to UTF-7\n"));
2694 return g_strdup(from);
2697 /* second iconv() call for flushing */
2698 iconv(cd, NULL, NULL, &norm_utf7_p, &norm_utf7_len);
2704 *norm_utf7_p = '\0';
2705 to_str = g_string_new(NULL);
2706 for (p = norm_utf7; p < norm_utf7_p; p++) {
2707 /* replace: '&' -> "&-",
2710 BASE64 '/' -> ',' */
2711 if (!in_escape && *p == '&') {
2712 g_string_append(to_str, "&-");
2713 } else if (!in_escape && *p == '+') {
2714 if (*(p + 1) == '-') {
2715 g_string_append_c(to_str, '+');
2718 g_string_append_c(to_str, '&');
2721 } else if (in_escape && *p == '/') {
2722 g_string_append_c(to_str, ',');
2723 } else if (in_escape && *p == '-') {
2724 g_string_append_c(to_str, '-');
2727 g_string_append_c(to_str, *p);
2733 g_string_append_c(to_str, '-');
2737 g_string_free(to_str, FALSE);
2742 static gboolean imap_rename_folder_func(GNode *node, gpointer data)
2744 FolderItem *item = node->data;
2745 gchar **paths = data;
2746 const gchar *oldpath = paths[0];
2747 const gchar *newpath = paths[1];
2749 gchar *new_itempath;
2752 oldpathlen = strlen(oldpath);
2753 if (strncmp(oldpath, item->path, oldpathlen) != 0) {
2754 g_warning("path doesn't match: %s, %s\n", oldpath, item->path);
2758 base = item->path + oldpathlen;
2759 while (*base == G_DIR_SEPARATOR) base++;
2761 new_itempath = g_strdup(newpath);
2763 new_itempath = g_strconcat(newpath, G_DIR_SEPARATOR_S, base,
2766 item->path = new_itempath;
2771 typedef struct _get_list_uid_data {
2773 IMAPFolderItem *item;
2774 GSList **msgnum_list;
2776 } get_list_uid_data;
2778 static void *get_list_of_uids_thread(void *data)
2780 get_list_uid_data *stuff = (get_list_uid_data *)data;
2781 Folder *folder = stuff->folder;
2782 IMAPFolderItem *item = stuff->item;
2783 GSList **msgnum_list = stuff->msgnum_list;
2784 gint ok, nummsgs = 0, lastuid_old;
2785 IMAPSession *session;
2786 GSList *uidlist, *elem;
2787 struct mailimap_set * set;
2788 clist * lep_uidlist;
2791 session = imap_session_get(folder);
2792 if (session == NULL) {
2794 return GINT_TO_POINTER(-1);
2797 ok = imap_select(session, IMAP_FOLDER(folder), item->item.path,
2798 NULL, NULL, NULL, NULL, TRUE);
2799 if (ok != IMAP_SUCCESS) {
2801 return GINT_TO_POINTER(-1);
2806 set = mailimap_set_new_interval(item->lastuid + 1, 0);
2807 r = imap_threaded_search(folder, IMAP_SEARCH_TYPE_SIMPLE, set,
2809 if (r == MAILIMAP_NO_ERROR) {
2810 GSList * fetchuid_list;
2813 imap_uid_list_from_lep(lep_uidlist);
2814 uidlist = g_slist_concat(fetchuid_list, uidlist);
2817 GSList * fetchuid_list;
2818 carray * lep_uidtab;
2820 r = imap_threaded_fetch_uid(folder, item->lastuid + 1,
2822 if (r == MAILIMAP_NO_ERROR) {
2824 imap_uid_list_from_lep_tab(lep_uidtab);
2825 uidlist = g_slist_concat(fetchuid_list, uidlist);
2829 lastuid_old = item->lastuid;
2830 *msgnum_list = g_slist_copy(item->uid_list);
2831 nummsgs = g_slist_length(*msgnum_list);
2832 debug_print("Got %d uids from cache\n", g_slist_length(item->uid_list));
2834 for (elem = uidlist; elem != NULL; elem = g_slist_next(elem)) {
2837 msgnum = GPOINTER_TO_INT(elem->data);
2838 if (msgnum > lastuid_old) {
2839 *msgnum_list = g_slist_prepend(*msgnum_list, GINT_TO_POINTER(msgnum));
2840 item->uid_list = g_slist_prepend(item->uid_list, GINT_TO_POINTER(msgnum));
2843 if(msgnum > item->lastuid)
2844 item->lastuid = msgnum;
2847 g_slist_free(uidlist);
2850 return GINT_TO_POINTER(nummsgs);
2853 static gint get_list_of_uids(Folder *folder, IMAPFolderItem *item, GSList **msgnum_list)
2856 get_list_uid_data *data = g_new0(get_list_uid_data, 1);
2858 data->folder = folder;
2860 data->msgnum_list = msgnum_list;
2862 if (prefs_common.work_offline && !imap_gtk_should_override()) {
2867 result = GPOINTER_TO_INT(get_list_of_uids_thread(data));
2873 gint imap_get_num_list(Folder *folder, FolderItem *_item, GSList **msgnum_list, gboolean *old_uids_valid)
2875 IMAPFolderItem *item = (IMAPFolderItem *)_item;
2876 IMAPSession *session;
2877 gint ok, nummsgs = 0, exists, recent, uid_val, uid_next, unseen;
2878 GSList *uidlist = NULL;
2880 gboolean selected_folder;
2882 debug_print("get_num_list\n");
2883 statusbar_print_all("Scanning %s...\n", FOLDER_ITEM(item)->path
2884 ? FOLDER_ITEM(item)->path:"");
2886 g_return_val_if_fail(folder != NULL, -1);
2887 g_return_val_if_fail(item != NULL, -1);
2888 g_return_val_if_fail(item->item.path != NULL, -1);
2889 g_return_val_if_fail(FOLDER_CLASS(folder) == &imap_class, -1);
2890 g_return_val_if_fail(folder->account != NULL, -1);
2892 session = imap_session_get(folder);
2893 g_return_val_if_fail(session != NULL, -1);
2895 selected_folder = (session->mbox != NULL) &&
2896 (!strcmp(session->mbox, item->item.path));
2897 if (selected_folder) {
2898 ok = imap_cmd_noop(session);
2899 if (ok != IMAP_SUCCESS) {
2900 debug_print("disconnected!\n");
2901 session = imap_reconnect_if_possible(folder, session);
2902 if (session == NULL) {
2903 statusbar_pop_all();
2907 exists = session->exists;
2909 *old_uids_valid = TRUE;
2911 ok = imap_status(session, IMAP_FOLDER(folder), item->item.path,
2912 &exists, &recent, &uid_next, &uid_val, &unseen, FALSE);
2913 if (ok != IMAP_SUCCESS) {
2914 statusbar_pop_all();
2917 if(item->item.mtime == uid_val)
2918 *old_uids_valid = TRUE;
2920 *old_uids_valid = FALSE;
2922 debug_print("Freeing imap uid cache\n");
2924 g_slist_free(item->uid_list);
2925 item->uid_list = NULL;
2927 item->item.mtime = uid_val;
2929 imap_delete_all_cached_messages((FolderItem *)item);
2933 if (!selected_folder)
2934 item->uid_next = uid_next;
2936 /* If old uid_next matches new uid_next we can be sure no message
2937 was added to the folder */
2938 if (( selected_folder && !session->folder_content_changed) ||
2939 (!selected_folder && uid_next == item->uid_next)) {
2940 nummsgs = g_slist_length(item->uid_list);
2942 /* If number of messages is still the same we
2943 know our caches message numbers are still valid,
2944 otherwise if the number of messages has decrease
2945 we discard our cache to start a new scan to find
2946 out which numbers have been removed */
2947 if (exists == nummsgs) {
2948 *msgnum_list = g_slist_copy(item->uid_list);
2949 statusbar_pop_all();
2951 } else if (exists < nummsgs) {
2952 debug_print("Freeing imap uid cache");
2954 g_slist_free(item->uid_list);
2955 item->uid_list = NULL;
2960 *msgnum_list = NULL;
2961 statusbar_pop_all();
2965 nummsgs = get_list_of_uids(folder, item, &uidlist);
2968 statusbar_pop_all();
2972 if (nummsgs != exists) {
2973 /* Cache contains more messages then folder, we have cached
2974 an old UID of a message that was removed and new messages
2975 have been added too, otherwise the uid_next check would
2977 debug_print("Freeing imap uid cache");
2979 g_slist_free(item->uid_list);
2980 item->uid_list = NULL;
2982 g_slist_free(*msgnum_list);
2984 nummsgs = get_list_of_uids(folder, item, &uidlist);
2987 *msgnum_list = uidlist;
2989 dir = folder_item_get_path((FolderItem *)item);
2990 debug_print("removing old messages from %s\n", dir);
2991 remove_numbered_files_not_in_list(dir, *msgnum_list);
2994 debug_print("get_num_list - ok - %i\n", nummsgs);
2995 statusbar_pop_all();
2999 static MsgInfo *imap_parse_msg(const gchar *file, FolderItem *item)
3004 flags.perm_flags = MSG_NEW|MSG_UNREAD;
3005 flags.tmp_flags = 0;
3007 g_return_val_if_fail(item != NULL, NULL);
3008 g_return_val_if_fail(file != NULL, NULL);
3010 if (folder_has_parent_of_type(item, F_QUEUE)) {
3011 MSG_SET_TMP_FLAGS(flags, MSG_QUEUED);
3012 } else if (folder_has_parent_of_type(item, F_DRAFT)) {
3013 MSG_SET_TMP_FLAGS(flags, MSG_DRAFT);
3016 msginfo = procheader_parse_file(file, flags, FALSE, FALSE);
3017 if (!msginfo) return NULL;
3019 msginfo->plaintext_file = g_strdup(file);
3020 msginfo->folder = item;
3025 GSList *imap_get_msginfos(Folder *folder, FolderItem *item,
3026 GSList *msgnum_list)
3028 IMAPSession *session;
3029 MsgInfoList *ret = NULL;
3032 debug_print("get_msginfos\n");
3034 g_return_val_if_fail(folder != NULL, NULL);
3035 g_return_val_if_fail(item != NULL, NULL);
3036 g_return_val_if_fail(msgnum_list != NULL, NULL);
3038 session = imap_session_get(folder);
3039 g_return_val_if_fail(session != NULL, NULL);
3041 debug_print("IMAP getting msginfos\n");
3042 ok = imap_select(session, IMAP_FOLDER(folder), item->path,
3043 NULL, NULL, NULL, NULL, FALSE);
3044 if (ok != IMAP_SUCCESS)
3047 if (!(folder_has_parent_of_type(item, F_DRAFT) ||
3048 folder_has_parent_of_type(item, F_QUEUE))) {
3049 ret = g_slist_concat(ret,
3050 imap_get_uncached_messages(session, item,
3053 MsgNumberList *sorted_list, *elem;
3054 gint startnum, lastnum;
3056 sorted_list = g_slist_sort(g_slist_copy(msgnum_list), g_int_compare);
3058 startnum = lastnum = GPOINTER_TO_INT(sorted_list->data);
3060 for (elem = sorted_list;; elem = g_slist_next(elem)) {
3064 num = GPOINTER_TO_INT(elem->data);
3066 if (num > lastnum + 1 || elem == NULL) {
3068 for (i = startnum; i <= lastnum; ++i) {
3071 file = imap_fetch_msg(folder, item, i);
3073 MsgInfo *msginfo = imap_parse_msg(file, item);
3074 if (msginfo != NULL) {
3075 msginfo->msgnum = i;
3076 ret = g_slist_append(ret, msginfo);
3090 g_slist_free(sorted_list);
3096 MsgInfo *imap_get_msginfo(Folder *folder, FolderItem *item, gint uid)
3098 MsgInfo *msginfo = NULL;
3099 MsgInfoList *msginfolist;
3100 MsgNumberList numlist;
3102 numlist.next = NULL;
3103 numlist.data = GINT_TO_POINTER(uid);
3105 msginfolist = imap_get_msginfos(folder, item, &numlist);
3106 if (msginfolist != NULL) {
3107 msginfo = msginfolist->data;
3108 g_slist_free(msginfolist);
3114 gboolean imap_scan_required(Folder *folder, FolderItem *_item)
3116 IMAPSession *session;
3117 IMAPFolderItem *item = (IMAPFolderItem *)_item;
3118 gint ok, exists = 0, recent = 0, unseen = 0;
3119 guint32 uid_next, uid_val = 0;
3120 gboolean selected_folder;
3122 g_return_val_if_fail(folder != NULL, FALSE);
3123 g_return_val_if_fail(item != NULL, FALSE);
3124 g_return_val_if_fail(item->item.folder != NULL, FALSE);
3125 g_return_val_if_fail(FOLDER_CLASS(item->item.folder) == &imap_class, FALSE);
3127 if (item->item.path == NULL)
3130 session = imap_session_get(folder);
3131 g_return_val_if_fail(session != NULL, FALSE);
3133 selected_folder = (session->mbox != NULL) &&
3134 (!strcmp(session->mbox, item->item.path));
3135 if (selected_folder) {
3136 ok = imap_cmd_noop(session);
3137 if (ok != IMAP_SUCCESS) {
3138 debug_print("disconnected!\n");
3139 session = imap_reconnect_if_possible(folder, session);
3140 if (session == NULL)
3144 if (session->folder_content_changed
3145 || session->exists != item->item.total_msgs)
3148 ok = imap_status(session, IMAP_FOLDER(folder), item->item.path,
3149 &exists, &recent, &uid_next, &uid_val, &unseen, FALSE);
3150 if (ok != IMAP_SUCCESS)
3153 if ((uid_next != item->uid_next) || (exists != item->item.total_msgs))
3160 void imap_change_flags(Folder *folder, FolderItem *item, MsgInfo *msginfo, MsgPermFlags newflags)
3162 IMAPSession *session;
3163 IMAPFlags flags_set = 0, flags_unset = 0;
3164 gint ok = IMAP_SUCCESS;
3165 MsgNumberList numlist;
3166 hashtable_data *ht_data = NULL;
3168 g_return_if_fail(folder != NULL);
3169 g_return_if_fail(folder->klass == &imap_class);
3170 g_return_if_fail(item != NULL);
3171 g_return_if_fail(item->folder == folder);
3172 g_return_if_fail(msginfo != NULL);
3173 g_return_if_fail(msginfo->folder == item);
3175 session = imap_session_get(folder);
3179 if ((ok = imap_select(session, IMAP_FOLDER(folder), msginfo->folder->path,
3180 NULL, NULL, NULL, NULL, FALSE)) != IMAP_SUCCESS) {
3184 if (!MSG_IS_MARKED(msginfo->flags) && (newflags & MSG_MARKED))
3185 flags_set |= IMAP_FLAG_FLAGGED;
3186 if ( MSG_IS_MARKED(msginfo->flags) && !(newflags & MSG_MARKED))
3187 flags_unset |= IMAP_FLAG_FLAGGED;
3189 if (!MSG_IS_UNREAD(msginfo->flags) && (newflags & MSG_UNREAD))
3190 flags_unset |= IMAP_FLAG_SEEN;
3191 if ( MSG_IS_UNREAD(msginfo->flags) && !(newflags & MSG_UNREAD))
3192 flags_set |= IMAP_FLAG_SEEN;
3194 if (!MSG_IS_REPLIED(msginfo->flags) && (newflags & MSG_REPLIED))
3195 flags_set |= IMAP_FLAG_ANSWERED;
3196 if ( MSG_IS_REPLIED(msginfo->flags) && !(newflags & MSG_REPLIED))
3197 flags_set |= IMAP_FLAG_ANSWERED;
3199 numlist.next = NULL;
3200 numlist.data = GINT_TO_POINTER(msginfo->msgnum);
3202 if (IMAP_FOLDER_ITEM(item)->batching) {
3203 /* instead of performing an UID STORE command for each message change,
3204 * as a lot of them can change "together", we just fill in hashtables
3205 * and defer the treatment so that we're able to send only one
3208 debug_print("IMAP batch mode on, deferring flags change\n");
3210 ht_data = g_hash_table_lookup(flags_set_table, GINT_TO_POINTER(flags_set));
3211 if (ht_data == NULL) {
3212 ht_data = g_new0(hashtable_data, 1);
3213 ht_data->session = session;
3214 g_hash_table_insert(flags_set_table, GINT_TO_POINTER(flags_set), ht_data);
3216 if (!g_slist_find(ht_data->msglist, GINT_TO_POINTER(msginfo->msgnum)))
3217 ht_data->msglist = g_slist_prepend(ht_data->msglist, GINT_TO_POINTER(msginfo->msgnum));
3220 ht_data = g_hash_table_lookup(flags_unset_table, GINT_TO_POINTER(flags_unset));
3221 if (ht_data == NULL) {
3222 ht_data = g_new0(hashtable_data, 1);
3223 ht_data->session = session;
3224 g_hash_table_insert(flags_unset_table, GINT_TO_POINTER(flags_unset), ht_data);
3226 if (!g_slist_find(ht_data->msglist, GINT_TO_POINTER(msginfo->msgnum)))
3227 ht_data->msglist = g_slist_prepend(ht_data->msglist, GINT_TO_POINTER(msginfo->msgnum));
3230 debug_print("IMAP changing flags\n");
3232 ok = imap_set_message_flags(session, &numlist, flags_set, TRUE);
3233 if (ok != IMAP_SUCCESS) {
3239 ok = imap_set_message_flags(session, &numlist, flags_unset, FALSE);
3240 if (ok != IMAP_SUCCESS) {
3245 msginfo->flags.perm_flags = newflags;
3250 static gint imap_remove_msg(Folder *folder, FolderItem *item, gint uid)
3253 IMAPSession *session;
3255 MsgNumberList numlist;
3257 g_return_val_if_fail(folder != NULL, -1);
3258 g_return_val_if_fail(FOLDER_CLASS(folder) == &imap_class, -1);
3259 g_return_val_if_fail(item != NULL, -1);
3261 session = imap_session_get(folder);
3262 if (!session) return -1;
3264 ok = imap_select(session, IMAP_FOLDER(folder), item->path,
3265 NULL, NULL, NULL, NULL, FALSE);
3266 if (ok != IMAP_SUCCESS)
3269 numlist.next = NULL;
3270 numlist.data = GINT_TO_POINTER(uid);
3272 ok = imap_set_message_flags
3273 (IMAP_SESSION(REMOTE_FOLDER(folder)->session),
3274 &numlist, IMAP_FLAG_DELETED, TRUE);
3275 if (ok != IMAP_SUCCESS) {
3276 log_warning(_("can't set deleted flags: %d\n"), uid);
3280 if (!session->uidplus) {
3281 ok = imap_cmd_expunge(session);
3285 uidstr = g_strdup_printf("%u", uid);
3286 ok = imap_cmd_expunge(session);
3289 if (ok != IMAP_SUCCESS) {
3290 log_warning(_("can't expunge\n"));
3294 IMAP_FOLDER_ITEM(item)->uid_list = g_slist_remove(
3295 IMAP_FOLDER_ITEM(item)->uid_list, numlist.data);
3296 dir = folder_item_get_path(item);
3297 if (is_dir_exist(dir))
3298 remove_numbered_files(dir, uid, uid);
3301 return IMAP_SUCCESS;
3304 static gint compare_msginfo(gconstpointer a, gconstpointer b)
3306 return ((MsgInfo *)a)->msgnum - ((MsgInfo *)b)->msgnum;
3309 static guint gslist_find_next_num(MsgNumberList **list, guint num)
3313 g_return_val_if_fail(list != NULL, -1);
3315 for (elem = *list; elem != NULL; elem = g_slist_next(elem))
3316 if (GPOINTER_TO_INT(elem->data) >= num)
3319 return elem != NULL ? GPOINTER_TO_INT(elem->data) : (gint)-1;
3323 * NEW and DELETED flags are not syncronized
3324 * - The NEW/RECENT flags in IMAP folders can not really be directly
3325 * modified by Sylpheed
3326 * - The DELETE/DELETED flag in IMAP and Sylpheed don't have the same
3327 * meaning, in IMAP it always removes the messages from the FolderItem
3328 * in Sylpheed it can mean to move the message to trash
3331 typedef struct _get_flags_data {
3334 MsgInfoList *msginfo_list;
3335 GRelation *msgflags;
3339 static /*gint*/ void *imap_get_flags_thread(void *data)
3341 get_flags_data *stuff = (get_flags_data *)data;
3342 Folder *folder = stuff->folder;
3343 FolderItem *item = stuff->item;
3344 MsgInfoList *msginfo_list = stuff->msginfo_list;
3345 GRelation *msgflags = stuff->msgflags;
3346 IMAPSession *session;
3347 GSList *sorted_list;
3348 GSList *unseen = NULL, *answered = NULL, *flagged = NULL;
3349 GSList *p_unseen, *p_answered, *p_flagged;
3351 GSList *seq_list, *cur;
3352 gboolean reverse_seen = FALSE;
3355 gint exists_cnt, recent_cnt, unseen_cnt, uid_next;
3356 guint32 uidvalidity;
3357 gboolean selected_folder;
3359 if (folder == NULL || item == NULL) {
3361 return GINT_TO_POINTER(-1);
3363 if (msginfo_list == NULL) {
3365 return GINT_TO_POINTER(0);
3368 session = imap_session_get(folder);
3369 if (session == NULL) {
3371 return GINT_TO_POINTER(-1);
3374 selected_folder = (session->mbox != NULL) &&
3375 (!strcmp(session->mbox, item->path));
3377 if (!selected_folder) {
3378 ok = imap_status(session, IMAP_FOLDER(folder), item->path,
3379 &exists_cnt, &recent_cnt, &uid_next, &uidvalidity, &unseen_cnt, TRUE);
3380 ok = imap_select(session, IMAP_FOLDER(folder), item->path,
3381 NULL, NULL, NULL, NULL, TRUE);
3382 if (ok != IMAP_SUCCESS) {
3384 return GINT_TO_POINTER(-1);
3387 if (unseen_cnt > exists_cnt / 2)
3388 reverse_seen = TRUE;
3391 if (item->unread_msgs > item->total_msgs / 2)
3392 reverse_seen = TRUE;
3395 cmd_buf = g_string_new(NULL);
3397 sorted_list = g_slist_sort(g_slist_copy(msginfo_list), compare_msginfo);
3399 seq_list = imap_get_lep_set_from_msglist(msginfo_list);
3401 for (cur = seq_list; cur != NULL; cur = g_slist_next(cur)) {
3402 struct mailimap_set * imapset;
3403 clist * lep_uidlist;
3406 imapset = cur->data;
3408 r = imap_threaded_search(folder, IMAP_SEARCH_TYPE_SEEN,
3409 imapset, &lep_uidlist);
3412 r = imap_threaded_search(folder,
3413 IMAP_SEARCH_TYPE_UNSEEN,
3414 imapset, &lep_uidlist);
3416 if (r == MAILIMAP_NO_ERROR) {
3419 uidlist = imap_uid_list_from_lep(lep_uidlist);
3420 mailimap_search_result_free(lep_uidlist);
3422 unseen = g_slist_concat(unseen, uidlist);
3425 r = imap_threaded_search(folder, IMAP_SEARCH_TYPE_ANSWERED,
3426 imapset, &lep_uidlist);
3427 if (r == MAILIMAP_NO_ERROR) {
3430 uidlist = imap_uid_list_from_lep(lep_uidlist);
3431 mailimap_search_result_free(lep_uidlist);
3433 answered = g_slist_concat(answered, uidlist);
3436 r = imap_threaded_search(folder, IMAP_SEARCH_TYPE_FLAGGED,
3437 imapset, &lep_uidlist);
3438 if (r == MAILIMAP_NO_ERROR) {
3441 uidlist = imap_uid_list_from_lep(lep_uidlist);
3442 mailimap_search_result_free(lep_uidlist);
3444 flagged = g_slist_concat(flagged, uidlist);
3449 p_answered = answered;
3450 p_flagged = flagged;
3452 for (elem = sorted_list; elem != NULL; elem = g_slist_next(elem)) {
3457 msginfo = (MsgInfo *) elem->data;
3458 flags = msginfo->flags.perm_flags;
3459 wasnew = (flags & MSG_NEW);
3460 flags &= ~((reverse_seen ? 0 : MSG_UNREAD | MSG_NEW) | MSG_REPLIED | MSG_MARKED);
3462 flags |= MSG_UNREAD | (wasnew ? MSG_NEW : 0);
3463 if (gslist_find_next_num(&p_unseen, msginfo->msgnum) == msginfo->msgnum) {
3464 if (!reverse_seen) {
3465 flags |= MSG_UNREAD | (wasnew ? MSG_NEW : 0);
3467 flags &= ~(MSG_UNREAD | MSG_NEW);
3470 if (gslist_find_next_num(&p_answered, msginfo->msgnum) == msginfo->msgnum)
3471 flags |= MSG_REPLIED;
3472 if (gslist_find_next_num(&p_flagged, msginfo->msgnum) == msginfo->msgnum)
3473 flags |= MSG_MARKED;
3474 g_relation_insert(msgflags, msginfo, GINT_TO_POINTER(flags));
3477 imap_lep_set_free(seq_list);
3478 g_slist_free(flagged);
3479 g_slist_free(answered);
3480 g_slist_free(unseen);
3481 g_slist_free(sorted_list);
3482 g_string_free(cmd_buf, TRUE);
3485 return GINT_TO_POINTER(0);
3488 static gint imap_get_flags(Folder *folder, FolderItem *item,
3489 MsgInfoList *msginfo_list, GRelation *msgflags)
3492 get_flags_data *data = g_new0(get_flags_data, 1);
3494 data->folder = folder;
3496 data->msginfo_list = msginfo_list;
3497 data->msgflags = msgflags;
3499 if (prefs_common.work_offline && !imap_gtk_should_override()) {
3504 result = GPOINTER_TO_INT(imap_get_flags_thread(data));
3511 static gboolean process_flags(gpointer key, gpointer value, gpointer user_data)
3513 gboolean flags_set = GPOINTER_TO_INT(user_data);
3514 gint flags_value = GPOINTER_TO_INT(key);
3515 hashtable_data *data = (hashtable_data *)value;
3517 data->msglist = g_slist_reverse(data->msglist);
3519 debug_print("IMAP %ssetting flags to %d for %d messages\n",
3522 g_slist_length(data->msglist));
3523 imap_set_message_flags(data->session, data->msglist, flags_value, flags_set);
3525 g_slist_free(data->msglist);
3530 static void process_hashtable(void)
3532 if (flags_set_table) {
3533 g_hash_table_foreach_remove(flags_set_table, process_flags, GINT_TO_POINTER(TRUE));
3534 g_free(flags_set_table);
3535 flags_set_table = NULL;
3537 if (flags_unset_table) {
3538 g_hash_table_foreach_remove(flags_unset_table, process_flags, GINT_TO_POINTER(FALSE));
3539 g_free(flags_unset_table);
3540 flags_unset_table = NULL;
3544 static IMAPFolderItem *batching_item = NULL;
3546 static void imap_set_batch (Folder *folder, FolderItem *_item, gboolean batch)
3548 IMAPFolderItem *item = (IMAPFolderItem *)_item;
3550 g_return_if_fail(item != NULL);
3552 if (batch && batching_item != NULL) {
3553 g_warning("already batching on %s\n", batching_item->item.path);
3557 if (item->batching == batch)
3560 item->batching = batch;
3562 batching_item = batch?item:NULL;
3565 debug_print("IMAP switching to batch mode\n");
3566 if (flags_set_table) {
3567 g_warning("flags_set_table non-null but we just entered batch mode!\n");
3568 flags_set_table = NULL;
3570 if (flags_unset_table) {
3571 g_warning("flags_unset_table non-null but we just entered batch mode!\n");
3572 flags_unset_table = NULL;
3574 flags_set_table = g_hash_table_new(NULL, g_direct_equal);
3575 flags_unset_table = g_hash_table_new(NULL, g_direct_equal);
3577 debug_print("IMAP switching away from batch mode\n");
3579 process_hashtable();
3585 /* data types conversion libetpan <-> sylpheed */
3589 #define ETPAN_IMAP_MB_MARKED 1
3590 #define ETPAN_IMAP_MB_UNMARKED 2
3591 #define ETPAN_IMAP_MB_NOSELECT 4
3592 #define ETPAN_IMAP_MB_NOINFERIORS 8
3594 static int imap_flags_to_flags(struct mailimap_mbx_list_flags * imap_flags)
3600 if (imap_flags->mbf_type == MAILIMAP_MBX_LIST_FLAGS_SFLAG) {
3601 switch (imap_flags->mbf_sflag) {
3602 case MAILIMAP_MBX_LIST_SFLAG_MARKED:
3603 flags |= ETPAN_IMAP_MB_MARKED;
3605 case MAILIMAP_MBX_LIST_SFLAG_NOSELECT:
3606 flags |= ETPAN_IMAP_MB_NOSELECT;
3608 case MAILIMAP_MBX_LIST_SFLAG_UNMARKED:
3609 flags |= ETPAN_IMAP_MB_UNMARKED;
3614 for(cur = clist_begin(imap_flags->mbf_oflags) ; cur != NULL ;
3615 cur = clist_next(cur)) {
3616 struct mailimap_mbx_list_oflag * oflag;
3618 oflag = clist_content(cur);
3620 switch (oflag->of_type) {
3621 case MAILIMAP_MBX_LIST_OFLAG_NOINFERIORS:
3622 flags |= ETPAN_IMAP_MB_NOINFERIORS;
3630 static GSList * imap_list_from_lep(IMAPFolder * folder,
3631 clist * list, const gchar * real_path)
3638 for(iter = clist_begin(list) ; iter != NULL ;
3639 iter = clist_next(iter)) {
3640 struct mailimap_mailbox_list * mb;
3648 FolderItem *new_item;
3650 mb = clist_content(iter);
3653 if (mb->mb_flag != NULL)
3654 flags = imap_flags_to_flags(mb->mb_flag);
3656 delimiter = mb->mb_delimiter;
3659 dup_name = strdup(name);
3660 if (delimiter != '\0')
3661 subst_char(dup_name, delimiter, '/');
3663 base = g_path_get_basename(dup_name);
3664 if (base[0] == '.') {
3670 if (strcmp(dup_name, real_path) == 0) {
3676 if (dup_name[strlen(dup_name)-1] == '/') {
3682 loc_name = imap_modified_utf7_to_utf8(base);
3683 loc_path = imap_modified_utf7_to_utf8(dup_name);
3685 new_item = folder_item_new(FOLDER(folder), loc_name, loc_path);
3686 if ((flags & ETPAN_IMAP_MB_NOINFERIORS) != 0)
3687 new_item->no_sub = TRUE;
3688 if (strcmp(dup_name, "INBOX") != 0 &&
3689 ((flags & ETPAN_IMAP_MB_NOSELECT) != 0))
3690 new_item->no_select = TRUE;
3692 item_list = g_slist_append(item_list, new_item);
3694 debug_print("folder '%s' found.\n", loc_path);
3705 static GSList * imap_get_lep_set_from_numlist(MsgNumberList *numlist)
3707 GSList *sorted_list, *cur;
3708 guint first, last, next;
3709 GSList *ret_list = NULL;
3711 struct mailimap_set * current_set;
3712 unsigned int item_count;
3714 if (numlist == NULL)
3718 current_set = mailimap_set_new_empty();
3720 sorted_list = g_slist_copy(numlist);
3721 sorted_list = g_slist_sort(sorted_list, g_int_compare);
3723 first = GPOINTER_TO_INT(sorted_list->data);
3726 for (cur = sorted_list; cur != NULL; cur = g_slist_next(cur)) {
3727 if (GPOINTER_TO_INT(cur->data) == 0)
3732 last = GPOINTER_TO_INT(cur->data);
3734 next = GPOINTER_TO_INT(cur->next->data);
3738 if (last + 1 != next || next == 0) {
3740 struct mailimap_set_item * item;
3741 item = mailimap_set_item_new(first, last);
3742 mailimap_set_add(current_set, item);
3747 if (count >= IMAP_SET_MAX_COUNT) {
3748 ret_list = g_slist_append(ret_list,
3750 current_set = mailimap_set_new_empty();
3757 if (clist_count(current_set->set_list) > 0) {
3758 ret_list = g_slist_append(ret_list,
3762 g_slist_free(sorted_list);
3767 static GSList * imap_get_lep_set_from_msglist(MsgInfoList *msglist)
3769 MsgNumberList *numlist = NULL;
3773 for (cur = msglist; cur != NULL; cur = g_slist_next(cur)) {
3774 MsgInfo *msginfo = (MsgInfo *) cur->data;
3776 numlist = g_slist_append(numlist, GINT_TO_POINTER(msginfo->msgnum));
3778 seq_list = imap_get_lep_set_from_numlist(numlist);
3779 g_slist_free(numlist);
3784 static GSList * imap_uid_list_from_lep(clist * list)
3791 for(iter = clist_begin(list) ; iter != NULL ;
3792 iter = clist_next(iter)) {
3795 puid = clist_content(iter);
3796 result = g_slist_append(result, GINT_TO_POINTER(* puid));
3802 static GSList * imap_uid_list_from_lep_tab(carray * list)
3809 for(i = 0 ; i < carray_count(list) ; i ++) {
3812 puid = carray_get(list, i);
3813 result = g_slist_append(result, GINT_TO_POINTER(* puid));
3819 static MsgInfo *imap_envelope_from_lep(struct imap_fetch_env_info * info,
3822 MsgInfo *msginfo = NULL;
3825 MsgFlags flags = {0, 0};
3827 MSG_SET_TMP_FLAGS(flags, MSG_IMAP);
3828 if (folder_has_parent_of_type(item, F_QUEUE)) {
3829 MSG_SET_TMP_FLAGS(flags, MSG_QUEUED);
3830 } else if (folder_has_parent_of_type(item, F_DRAFT)) {
3831 MSG_SET_TMP_FLAGS(flags, MSG_DRAFT);
3833 flags.perm_flags = info->flags;
3837 msginfo = procheader_parse_str(info->headers, flags, FALSE, FALSE);
3840 msginfo->msgnum = uid;
3841 msginfo->size = size;
3847 static void imap_lep_set_free(GSList *seq_list)
3851 for(cur = seq_list ; cur != NULL ; cur = g_slist_next(cur)) {
3852 struct mailimap_set * imapset;
3854 imapset = cur->data;
3855 mailimap_set_free(imapset);
3857 g_slist_free(seq_list);
3860 static struct mailimap_flag_list * imap_flag_to_lep(IMAPFlags flags)
3862 struct mailimap_flag_list * flag_list;
3864 flag_list = mailimap_flag_list_new_empty();
3866 if (IMAP_IS_SEEN(flags))
3867 mailimap_flag_list_add(flag_list,
3868 mailimap_flag_new_seen());
3869 if (IMAP_IS_ANSWERED(flags))
3870 mailimap_flag_list_add(flag_list,
3871 mailimap_flag_new_answered());
3872 if (IMAP_IS_FLAGGED(flags))
3873 mailimap_flag_list_add(flag_list,
3874 mailimap_flag_new_flagged());
3875 if (IMAP_IS_DELETED(flags))
3876 mailimap_flag_list_add(flag_list,
3877 mailimap_flag_new_deleted());
3878 if (IMAP_IS_DRAFT(flags))
3879 mailimap_flag_list_add(flag_list,
3880 mailimap_flag_new_draft());
3885 guint imap_folder_get_refcnt(Folder *folder)
3887 return ((IMAPFolder *)folder)->refcnt;
3890 void imap_folder_ref(Folder *folder)
3892 ((IMAPFolder *)folder)->refcnt++;
3895 void imap_folder_unref(Folder *folder)
3897 if (((IMAPFolder *)folder)->refcnt > 0)
3898 ((IMAPFolder *)folder)->refcnt--;
3901 #else /* HAVE_LIBETPAN */
3903 static FolderClass imap_class;
3905 static Folder *imap_folder_new (const gchar *name,
3910 static gint imap_create_tree (Folder *folder)
3914 static FolderItem *imap_create_folder (Folder *folder,
3920 static gint imap_rename_folder (Folder *folder,
3927 FolderClass *imap_get_class(void)
3929 if (imap_class.idstr == NULL) {
3930 imap_class.type = F_IMAP;
3931 imap_class.idstr = "imap";
3932 imap_class.uistr = "IMAP4";
3934 imap_class.new_folder = imap_folder_new;
3935 imap_class.create_tree = imap_create_tree;
3936 imap_class.create_folder = imap_create_folder;
3937 imap_class.rename_folder = imap_rename_folder;
3938 /* nothing implemented */
3945 void imap_synchronise(FolderItem *item)
3947 imap_gtk_synchronise(item);