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 */
95 gboolean authenticated;
104 gboolean folder_content_changed;
109 struct _IMAPNameSpace
115 #define IMAP_SUCCESS 0
116 #define IMAP_SOCKET 2
117 #define IMAP_AUTHFAIL 3
118 #define IMAP_PROTOCOL 4
119 #define IMAP_SYNTAX 5
123 #define IMAPBUFSIZE 8192
127 IMAP_FLAG_SEEN = 1 << 0,
128 IMAP_FLAG_ANSWERED = 1 << 1,
129 IMAP_FLAG_FLAGGED = 1 << 2,
130 IMAP_FLAG_DELETED = 1 << 3,
131 IMAP_FLAG_DRAFT = 1 << 4
134 #define IMAP_IS_SEEN(flags) ((flags & IMAP_FLAG_SEEN) != 0)
135 #define IMAP_IS_ANSWERED(flags) ((flags & IMAP_FLAG_ANSWERED) != 0)
136 #define IMAP_IS_FLAGGED(flags) ((flags & IMAP_FLAG_FLAGGED) != 0)
137 #define IMAP_IS_DELETED(flags) ((flags & IMAP_FLAG_DELETED) != 0)
138 #define IMAP_IS_DRAFT(flags) ((flags & IMAP_FLAG_DRAFT) != 0)
141 #define IMAP4_PORT 143
143 #define IMAPS_PORT 993
146 #define IMAP_CMD_LIMIT 1000
148 struct _IMAPFolderItem
158 static void imap_folder_init (Folder *folder,
162 static Folder *imap_folder_new (const gchar *name,
164 static void imap_folder_destroy (Folder *folder);
166 static IMAPSession *imap_session_new (Folder *folder,
167 const PrefsAccount *account);
168 static void imap_session_authenticate(IMAPSession *session,
169 const PrefsAccount *account);
170 static void imap_session_destroy (Session *session);
172 static gchar *imap_fetch_msg (Folder *folder,
175 static gchar *imap_fetch_msg_full (Folder *folder,
180 static gint imap_add_msg (Folder *folder,
184 static gint imap_add_msgs (Folder *folder,
187 GRelation *relation);
189 static gint imap_copy_msg (Folder *folder,
192 static gint imap_copy_msgs (Folder *folder,
194 MsgInfoList *msglist,
195 GRelation *relation);
197 static gint imap_remove_msg (Folder *folder,
200 static gint imap_remove_msgs (Folder *folder,
202 MsgInfoList *msglist,
203 GRelation *relation);
204 static gint imap_remove_all_msg (Folder *folder,
207 static gboolean imap_is_msg_changed (Folder *folder,
211 static gint imap_close (Folder *folder,
214 static gint imap_scan_tree (Folder *folder);
216 static gint imap_create_tree (Folder *folder);
218 static FolderItem *imap_create_folder (Folder *folder,
221 static gint imap_rename_folder (Folder *folder,
224 static gint imap_remove_folder (Folder *folder,
227 static FolderItem *imap_folder_item_new (Folder *folder);
228 static void imap_folder_item_destroy (Folder *folder,
231 static IMAPSession *imap_session_get (Folder *folder);
233 static gint imap_auth (IMAPSession *session,
238 static gint imap_scan_tree_recursive (IMAPSession *session,
241 static void imap_create_missing_folders (Folder *folder);
242 static FolderItem *imap_create_special_folder
244 SpecialFolderItemType stype,
247 static gint imap_do_copy_msgs (Folder *folder,
249 MsgInfoList *msglist,
250 GRelation *relation);
252 static void imap_delete_all_cached_messages (FolderItem *item);
253 static void imap_set_batch (Folder *folder,
256 static gint imap_set_message_flags (IMAPSession *session,
257 MsgNumberList *numlist,
260 static gint imap_select (IMAPSession *session,
266 guint32 *uid_validity,
268 static gint imap_status (IMAPSession *session,
274 guint32 *uid_validity,
278 static IMAPNameSpace *imap_find_namespace (IMAPFolder *folder,
280 static gchar imap_get_path_separator (IMAPFolder *folder,
282 static gchar *imap_get_real_path (IMAPFolder *folder,
285 static void imap_free_capabilities (IMAPSession *session);
287 /* low-level IMAP4rev1 commands */
288 static gint imap_cmd_login (IMAPSession *session,
291 static gint imap_cmd_logout (IMAPSession *session);
292 static gint imap_cmd_noop (IMAPSession *session);
294 static gint imap_cmd_starttls (IMAPSession *session);
296 static gint imap_cmd_select (IMAPSession *session,
301 guint32 *uid_validity,
303 static gint imap_cmd_examine (IMAPSession *session,
308 guint32 *uid_validity,
310 static gint imap_cmd_create (IMAPSession *sock,
311 const gchar *folder);
312 static gint imap_cmd_rename (IMAPSession *sock,
313 const gchar *oldfolder,
314 const gchar *newfolder);
315 static gint imap_cmd_delete (IMAPSession *session,
316 const gchar *folder);
317 static gint imap_cmd_fetch (IMAPSession *sock,
319 const gchar *filename,
322 static gint imap_cmd_append (IMAPSession *session,
323 const gchar *destfolder,
327 static gint imap_cmd_copy (IMAPSession *session,
328 struct mailimap_set * set,
329 const gchar *destfolder,
330 GRelation *uid_mapping);
331 static gint imap_cmd_store (IMAPSession *session,
332 struct mailimap_set * set,
335 static gint imap_cmd_expunge (IMAPSession *session);
337 static void imap_path_separator_subst (gchar *str,
340 static gchar *imap_utf8_to_modified_utf7 (const gchar *from);
341 static gchar *imap_modified_utf7_to_utf8 (const gchar *mutf7_str);
343 static gboolean imap_rename_folder_func (GNode *node,
345 static gint imap_get_num_list (Folder *folder,
348 gboolean *old_uids_valid);
349 static GSList *imap_get_msginfos (Folder *folder,
351 GSList *msgnum_list);
352 static MsgInfo *imap_get_msginfo (Folder *folder,
355 static gboolean imap_scan_required (Folder *folder,
357 static void imap_change_flags (Folder *folder,
360 MsgPermFlags newflags);
361 static gint imap_get_flags (Folder *folder,
363 MsgInfoList *msglist,
364 GRelation *msgflags);
365 static gchar *imap_folder_get_path (Folder *folder);
366 static gchar *imap_item_get_path (Folder *folder,
368 static MsgInfo *imap_parse_msg(const gchar *file, FolderItem *item);
371 /* data types conversion libetpan <-> sylpheed */
372 static GSList * imap_list_from_lep(IMAPFolder * folder,
373 clist * list, const gchar * real_path);
374 static GSList * imap_get_lep_set_from_numlist(MsgNumberList *numlist);
375 static GSList * imap_get_lep_set_from_msglist(MsgInfoList *msglist);
376 static GSList * imap_uid_list_from_lep(clist * list);
377 static GSList * imap_uid_list_from_lep_tab(carray * list);
378 static MsgInfo *imap_envelope_from_lep(struct imap_fetch_env_info * info,
380 static void imap_lep_set_free(GSList *seq_list);
381 static struct mailimap_flag_list * imap_flag_to_lep(IMAPFlags flags);
384 static GHashTable *flags_set_table = NULL;
385 static GHashTable *flags_unset_table = NULL;
386 typedef struct _hashtable_data {
387 IMAPSession *session;
391 static FolderClass imap_class;
393 typedef struct _thread_data {
403 FolderClass *imap_get_class(void)
405 if (imap_class.idstr == NULL) {
406 imap_class.type = F_IMAP;
407 imap_class.idstr = "imap";
408 imap_class.uistr = "IMAP4";
410 /* Folder functions */
411 imap_class.new_folder = imap_folder_new;
412 imap_class.destroy_folder = imap_folder_destroy;
413 imap_class.scan_tree = imap_scan_tree;
414 imap_class.create_tree = imap_create_tree;
416 /* FolderItem functions */
417 imap_class.item_new = imap_folder_item_new;
418 imap_class.item_destroy = imap_folder_item_destroy;
419 imap_class.item_get_path = imap_item_get_path;
420 imap_class.create_folder = imap_create_folder;
421 imap_class.rename_folder = imap_rename_folder;
422 imap_class.remove_folder = imap_remove_folder;
423 imap_class.close = imap_close;
424 imap_class.get_num_list = imap_get_num_list;
425 imap_class.scan_required = imap_scan_required;
427 /* Message functions */
428 imap_class.get_msginfo = imap_get_msginfo;
429 imap_class.get_msginfos = imap_get_msginfos;
430 imap_class.fetch_msg = imap_fetch_msg;
431 imap_class.fetch_msg_full = imap_fetch_msg_full;
432 imap_class.add_msg = imap_add_msg;
433 imap_class.add_msgs = imap_add_msgs;
434 imap_class.copy_msg = imap_copy_msg;
435 imap_class.copy_msgs = imap_copy_msgs;
436 imap_class.remove_msg = imap_remove_msg;
437 imap_class.remove_msgs = imap_remove_msgs;
438 imap_class.remove_all_msg = imap_remove_all_msg;
439 imap_class.is_msg_changed = imap_is_msg_changed;
440 imap_class.change_flags = imap_change_flags;
441 imap_class.get_flags = imap_get_flags;
442 imap_class.set_batch = imap_set_batch;
444 pthread_mutex_init(&imap_mutex, NULL);
451 static Folder *imap_folder_new(const gchar *name, const gchar *path)
455 folder = (Folder *)g_new0(IMAPFolder, 1);
456 folder->klass = &imap_class;
457 imap_folder_init(folder, name, path);
462 static void imap_folder_destroy(Folder *folder)
467 dir = imap_folder_get_path(folder);
468 if (is_dir_exist(dir))
469 remove_dir_recursive(dir);
472 folder_remote_folder_destroy(REMOTE_FOLDER(folder));
475 static void imap_folder_init(Folder *folder, const gchar *name,
478 folder_remote_folder_init((Folder *)folder, name, path);
481 static FolderItem *imap_folder_item_new(Folder *folder)
483 IMAPFolderItem *item;
485 item = g_new0(IMAPFolderItem, 1);
488 item->uid_list = NULL;
490 return (FolderItem *)item;
493 static void imap_folder_item_destroy(Folder *folder, FolderItem *_item)
495 IMAPFolderItem *item = (IMAPFolderItem *)_item;
497 g_return_if_fail(item != NULL);
498 g_slist_free(item->uid_list);
503 static gboolean imap_reset_uid_lists_func(GNode *node, gpointer data)
505 IMAPFolderItem *item = (IMAPFolderItem *)node->data;
509 g_slist_free(item->uid_list);
510 item->uid_list = NULL;
515 static void imap_reset_uid_lists(Folder *folder)
517 if(folder->node == NULL)
520 /* Destroy all uid lists and rest last uid */
521 g_node_traverse(folder->node, G_IN_ORDER, G_TRAVERSE_ALL, -1, imap_reset_uid_lists_func, NULL);
524 static gint imap_auth(IMAPSession *session, const gchar *user, const gchar *pass,
529 ok = imap_cmd_login(session, user, pass);
531 if (ok == IMAP_SUCCESS)
532 session->authenticated = TRUE;
537 static IMAPSession *imap_session_get(Folder *folder)
539 RemoteFolder *rfolder = REMOTE_FOLDER(folder);
540 IMAPSession *session = NULL;
542 g_return_val_if_fail(folder != NULL, NULL);
543 g_return_val_if_fail(FOLDER_CLASS(folder) == &imap_class, NULL);
544 g_return_val_if_fail(folder->account != NULL, NULL);
546 if (prefs_common.work_offline && !imap_gtk_should_override()) {
550 /* Make sure we have a session */
551 if (rfolder->session != NULL) {
552 session = IMAP_SESSION(rfolder->session);
554 imap_reset_uid_lists(folder);
555 session = imap_session_new(folder, folder->account);
560 /* Make sure session is authenticated */
561 if (!IMAP_SESSION(session)->authenticated)
562 imap_session_authenticate(IMAP_SESSION(session), folder->account);
564 if (!IMAP_SESSION(session)->authenticated) {
565 session_destroy(SESSION(session));
566 rfolder->session = NULL;
571 /* Make sure we have parsed the IMAP namespace */
572 imap_parse_namespace(IMAP_SESSION(session),
573 IMAP_FOLDER(folder));
576 /* I think the point of this code is to avoid sending a
577 * keepalive if we've used the session recently and therefore
578 * think it's still alive. Unfortunately, most of the code
579 * does not yet check for errors on the socket, and so if the
580 * connection drops we don't notice until the timeout expires.
581 * A better solution than sending a NOOP every time would be
582 * for every command to be prepared to retry until it is
583 * successfully sent. -- mbp */
584 if (time(NULL) - SESSION(session)->last_access_time > SESSION_TIMEOUT_INTERVAL) {
585 /* verify that the session is still alive */
586 if (imap_cmd_noop(session) != IMAP_SUCCESS) {
587 /* Check if this is the first try to establish a
588 connection, if yes we don't try to reconnect */
589 if (rfolder->session == NULL) {
590 log_warning(_("Connecting to %s failed"),
591 folder->account->recv_server);
592 session_destroy(SESSION(session));
595 log_warning(_("IMAP4 connection to %s has been"
596 " disconnected. Reconnecting...\n"),
597 folder->account->recv_server);
598 statusbar_print_all(_("IMAP4 connection to %s has been"
599 " disconnected. Reconnecting...\n"),
600 folder->account->recv_server);
601 session_destroy(SESSION(session));
602 /* Clear folders session to make imap_session_get create
603 a new session, because of rfolder->session == NULL
604 it will not try to reconnect again and so avoid an
606 rfolder->session = NULL;
607 session = imap_session_get(folder);
613 rfolder->session = SESSION(session);
615 return IMAP_SESSION(session);
618 static IMAPSession *imap_session_new(Folder * folder,
619 const PrefsAccount *account)
621 IMAPSession *session;
627 /* FIXME: IMAP over SSL only... */
630 port = account->set_imapport ? account->imapport
631 : account->ssl_imap == SSL_TUNNEL ? IMAPS_PORT : IMAP4_PORT;
632 ssl_type = account->ssl_imap;
634 port = account->set_imapport ? account->imapport
639 statusbar_print_all(_("Connecting to IMAP4 server: %s..."), folder->account->recv_server);
640 if (account->set_tunnelcmd) {
641 r = imap_threaded_connect_cmd(folder,
643 account->recv_server,
648 if (ssl_type == SSL_TUNNEL) {
649 r = imap_threaded_connect_ssl(folder,
650 account->recv_server,
656 r = imap_threaded_connect(folder,
657 account->recv_server,
663 if (r == MAILIMAP_NO_ERROR_AUTHENTICATED) {
664 authenticated = TRUE;
666 else if (r == MAILIMAP_NO_ERROR_NON_AUTHENTICATED) {
667 authenticated = FALSE;
670 if(!prefs_common.no_recv_err_panel) {
671 alertpanel_error(_("Can't connect to IMAP4 server: %s:%d"),
672 account->recv_server, port);
678 session = g_new0(IMAPSession, 1);
679 session_init(SESSION(session));
680 SESSION(session)->type = SESSION_IMAP;
681 SESSION(session)->server = g_strdup(account->recv_server);
682 SESSION(session)->sock = NULL;
684 SESSION(session)->destroy = imap_session_destroy;
686 session->capability = NULL;
688 session->authenticated = authenticated;
689 session->mbox = NULL;
690 session->cmd_count = 0;
691 session->folder = folder;
694 if (account->ssl_imap == SSL_STARTTLS) {
697 ok = imap_cmd_starttls(session);
698 if (ok != IMAP_SUCCESS) {
699 log_warning(_("Can't start TLS session.\n"));
700 session_destroy(SESSION(session));
704 imap_free_capabilities(session);
705 session->authenticated = FALSE;
706 session->uidplus = FALSE;
707 session->cmd_count = 1;
710 log_message("IMAP connection is %s-authenticated\n",
711 (session->authenticated) ? "pre" : "un");
716 static void imap_session_authenticate(IMAPSession *session,
717 const PrefsAccount *account)
721 g_return_if_fail(account->userid != NULL);
723 pass = account->passwd;
726 tmp_pass = input_dialog_query_password(account->recv_server, account->userid);
729 Xstrdup_a(pass, tmp_pass, {g_free(tmp_pass); return;});
732 statusbar_print_all(_("Connecting to IMAP4 server %s...\n"),
733 account->recv_server);
734 if (imap_auth(session, account->userid, pass, account->imap_auth_type) != IMAP_SUCCESS) {
735 imap_threaded_disconnect(session->folder);
736 imap_cmd_logout(session);
742 session->authenticated = TRUE;
745 static void imap_session_destroy(Session *session)
747 imap_threaded_disconnect(IMAP_SESSION(session)->folder);
749 imap_free_capabilities(IMAP_SESSION(session));
750 g_free(IMAP_SESSION(session)->mbox);
751 sock_close(session->sock);
752 session->sock = NULL;
755 static gchar *imap_fetch_msg(Folder *folder, FolderItem *item, gint uid)
757 return imap_fetch_msg_full(folder, item, uid, TRUE, TRUE);
760 static guint get_size_with_lfs(MsgInfo *info)
769 fp = procmsg_open_message(info);
773 while (fgets(buf, sizeof (buf), fp) != NULL) {
775 if (!strstr(buf, "\r") && strstr(buf, "\n"))
783 static gchar *imap_fetch_msg_full(Folder *folder, FolderItem *item, gint uid,
784 gboolean headers, gboolean body)
786 gchar *path, *filename;
787 IMAPSession *session;
790 g_return_val_if_fail(folder != NULL, NULL);
791 g_return_val_if_fail(item != NULL, NULL);
796 path = folder_item_get_path(item);
797 if (!is_dir_exist(path))
799 filename = g_strconcat(path, G_DIR_SEPARATOR_S, itos(uid), NULL);
802 if (is_file_exist(filename)) {
803 /* see whether the local file represents the whole message
804 * or not. As the IMAP server reports size with \r chars,
805 * we have to update the local file (UNIX \n only) size */
806 MsgInfo *msginfo = imap_parse_msg(filename, item);
807 MsgInfo *cached = msgcache_get_msg(item->cache,uid);
808 guint have_size = get_size_with_lfs(msginfo);
809 debug_print("message %d has been already %scached (%d/%d).\n", uid,
810 have_size == cached->size ? "fully ":"",
811 have_size, cached? (int)cached->size : -1);
813 if (cached && (cached->size == have_size || !body)) {
814 procmsg_msginfo_free(cached);
815 procmsg_msginfo_free(msginfo);
818 procmsg_msginfo_free(cached);
819 procmsg_msginfo_free(msginfo);
823 session = imap_session_get(folder);
829 debug_print("IMAP fetching messages\n");
830 ok = imap_select(session, IMAP_FOLDER(folder), item->path,
831 NULL, NULL, NULL, NULL, FALSE);
832 if (ok != IMAP_SUCCESS) {
833 g_warning("can't select mailbox %s\n", item->path);
838 debug_print("getting message %d...\n", uid);
839 ok = imap_cmd_fetch(session, (guint32)uid, filename, headers, body);
841 if (ok != IMAP_SUCCESS) {
842 g_warning("can't fetch message %d\n", uid);
850 static gint imap_add_msg(Folder *folder, FolderItem *dest,
851 const gchar *file, MsgFlags *flags)
855 MsgFileInfo fileinfo;
857 g_return_val_if_fail(file != NULL, -1);
859 fileinfo.msginfo = NULL;
860 fileinfo.file = (gchar *)file;
861 fileinfo.flags = flags;
862 file_list.data = &fileinfo;
863 file_list.next = NULL;
865 ret = imap_add_msgs(folder, dest, &file_list, NULL);
869 static gint imap_add_msgs(Folder *folder, FolderItem *dest, GSList *file_list,
873 IMAPSession *session;
874 guint32 last_uid = 0;
876 MsgFileInfo *fileinfo;
880 g_return_val_if_fail(folder != NULL, -1);
881 g_return_val_if_fail(dest != NULL, -1);
882 g_return_val_if_fail(file_list != NULL, -1);
884 session = imap_session_get(folder);
888 destdir = imap_get_real_path(IMAP_FOLDER(folder), dest->path);
890 for (cur = file_list; cur != NULL; cur = cur->next) {
891 IMAPFlags iflags = 0;
894 fileinfo = (MsgFileInfo *)cur->data;
896 if (fileinfo->flags) {
897 if (MSG_IS_MARKED(*fileinfo->flags))
898 iflags |= IMAP_FLAG_FLAGGED;
899 if (MSG_IS_REPLIED(*fileinfo->flags))
900 iflags |= IMAP_FLAG_ANSWERED;
901 if (!MSG_IS_UNREAD(*fileinfo->flags))
902 iflags |= IMAP_FLAG_SEEN;
905 if (dest->stype == F_OUTBOX ||
906 dest->stype == F_QUEUE ||
907 dest->stype == F_DRAFT ||
908 dest->stype == F_TRASH)
909 iflags |= IMAP_FLAG_SEEN;
911 ok = imap_cmd_append(session, destdir, fileinfo->file, iflags,
914 if (ok != IMAP_SUCCESS) {
915 g_warning("can't append message %s\n", fileinfo->file);
920 if (relation != NULL)
921 g_relation_insert(relation, fileinfo->msginfo != NULL ?
922 (gpointer) fileinfo->msginfo : (gpointer) fileinfo,
923 GINT_TO_POINTER(dest->last_num + 1));
924 if (last_uid < new_uid)
933 static gint imap_do_copy_msgs(Folder *folder, FolderItem *dest,
934 MsgInfoList *msglist, GRelation *relation)
938 GSList *seq_list, *cur;
940 IMAPSession *session;
941 gint ok = IMAP_SUCCESS;
942 GRelation *uid_mapping;
945 g_return_val_if_fail(folder != NULL, -1);
946 g_return_val_if_fail(dest != NULL, -1);
947 g_return_val_if_fail(msglist != NULL, -1);
949 session = imap_session_get(folder);
954 msginfo = (MsgInfo *)msglist->data;
956 src = msginfo->folder;
958 g_warning("the src folder is identical to the dest.\n");
962 ok = imap_select(session, IMAP_FOLDER(folder), msginfo->folder->path,
963 NULL, NULL, NULL, NULL, FALSE);
964 if (ok != IMAP_SUCCESS) {
968 destdir = imap_get_real_path(IMAP_FOLDER(folder), dest->path);
969 seq_list = imap_get_lep_set_from_msglist(msglist);
970 uid_mapping = g_relation_new(2);
971 g_relation_index(uid_mapping, 0, g_direct_hash, g_direct_equal);
973 for (cur = seq_list; cur != NULL; cur = g_slist_next(cur)) {
974 struct mailimap_set * seq_set;
978 debug_print("Copying messages from %s to %s ...\n",
981 ok = imap_cmd_copy(session, seq_set, destdir, uid_mapping);
982 if (ok != IMAP_SUCCESS) {
983 g_relation_destroy(uid_mapping);
984 imap_lep_set_free(seq_list);
989 for (cur = msglist; cur != NULL; cur = g_slist_next(cur)) {
990 MsgInfo *msginfo = (MsgInfo *)cur->data;
993 tuples = g_relation_select(uid_mapping,
994 GINT_TO_POINTER(msginfo->msgnum),
996 if (tuples->len > 0) {
997 gint num = GPOINTER_TO_INT(g_tuples_index(tuples, 0, 1));
998 g_relation_insert(relation, msginfo,
999 GPOINTER_TO_INT(num));
1003 g_relation_insert(relation, msginfo,
1004 GPOINTER_TO_INT(0));
1005 g_tuples_destroy(tuples);
1008 g_relation_destroy(uid_mapping);
1009 imap_lep_set_free(seq_list);
1013 IMAP_FOLDER_ITEM(dest)->lastuid = 0;
1014 IMAP_FOLDER_ITEM(dest)->uid_next = 0;
1015 g_slist_free(IMAP_FOLDER_ITEM(dest)->uid_list);
1016 IMAP_FOLDER_ITEM(dest)->uid_list = NULL;
1018 if (ok == IMAP_SUCCESS)
1024 static gint imap_copy_msg(Folder *folder, FolderItem *dest, MsgInfo *msginfo)
1028 g_return_val_if_fail(msginfo != NULL, -1);
1030 msglist.data = msginfo;
1031 msglist.next = NULL;
1033 return imap_copy_msgs(folder, dest, &msglist, NULL);
1036 static gint imap_copy_msgs(Folder *folder, FolderItem *dest,
1037 MsgInfoList *msglist, GRelation *relation)
1043 g_return_val_if_fail(folder != NULL, -1);
1044 g_return_val_if_fail(dest != NULL, -1);
1045 g_return_val_if_fail(msglist != NULL, -1);
1047 msginfo = (MsgInfo *)msglist->data;
1048 g_return_val_if_fail(msginfo->folder != NULL, -1);
1050 if (folder == msginfo->folder->folder) {
1051 ret = imap_do_copy_msgs(folder, dest, msglist, relation);
1055 file_list = procmsg_get_message_file_list(msglist);
1056 g_return_val_if_fail(file_list != NULL, -1);
1058 ret = imap_add_msgs(folder, dest, file_list, relation);
1060 procmsg_message_file_list_free(file_list);
1066 static gint imap_do_remove_msgs(Folder *folder, FolderItem *dest,
1067 MsgInfoList *msglist, GRelation *relation)
1070 GSList *seq_list = NULL, *cur;
1072 IMAPSession *session;
1073 gint ok = IMAP_SUCCESS;
1074 GRelation *uid_mapping;
1076 g_return_val_if_fail(folder != NULL, -1);
1077 g_return_val_if_fail(dest != NULL, -1);
1078 g_return_val_if_fail(msglist != NULL, -1);
1080 session = imap_session_get(folder);
1084 msginfo = (MsgInfo *)msglist->data;
1086 ok = imap_select(session, IMAP_FOLDER(folder), msginfo->folder->path,
1087 NULL, NULL, NULL, NULL, FALSE);
1088 if (ok != IMAP_SUCCESS) {
1092 destdir = imap_get_real_path(IMAP_FOLDER(folder), dest->path);
1093 for (cur = msglist; cur; cur = cur->next) {
1094 msginfo = (MsgInfo *)cur->data;
1095 seq_list = g_slist_append(seq_list, GINT_TO_POINTER(msginfo->msgnum));
1098 uid_mapping = g_relation_new(2);
1099 g_relation_index(uid_mapping, 0, g_direct_hash, g_direct_equal);
1101 ok = imap_set_message_flags
1102 (IMAP_SESSION(REMOTE_FOLDER(folder)->session),
1103 seq_list, IMAP_FLAG_DELETED, TRUE);
1104 if (ok != IMAP_SUCCESS) {
1105 log_warning(_("can't set deleted flags\n"));
1108 ok = imap_cmd_expunge(session);
1109 if (ok != IMAP_SUCCESS) {
1110 log_warning(_("can't expunge\n"));
1114 g_relation_destroy(uid_mapping);
1115 g_slist_free(seq_list);
1119 if (ok == IMAP_SUCCESS)
1125 static gint imap_remove_msgs(Folder *folder, FolderItem *dest,
1126 MsgInfoList *msglist, GRelation *relation)
1130 g_return_val_if_fail(folder != NULL, -1);
1131 g_return_val_if_fail(dest != NULL, -1);
1132 if (msglist == NULL)
1135 msginfo = (MsgInfo *)msglist->data;
1136 g_return_val_if_fail(msginfo->folder != NULL, -1);
1138 return imap_do_remove_msgs(folder, dest, msglist, relation);
1141 static gint imap_remove_all_msg(Folder *folder, FolderItem *item)
1143 GSList *list = folder_item_get_msg_list(item);
1144 gint res = imap_remove_msgs(folder, item, list, NULL);
1149 static gboolean imap_is_msg_changed(Folder *folder, FolderItem *item,
1152 /* TODO: properly implement this method */
1156 static gint imap_close(Folder *folder, FolderItem *item)
1161 static gint imap_scan_tree(Folder *folder)
1163 FolderItem *item = NULL;
1164 IMAPSession *session;
1165 gchar *root_folder = NULL;
1167 g_return_val_if_fail(folder != NULL, -1);
1168 g_return_val_if_fail(folder->account != NULL, -1);
1170 session = imap_session_get(folder);
1172 if (!folder->node) {
1173 folder_tree_destroy(folder);
1174 item = folder_item_new(folder, folder->name, NULL);
1175 item->folder = folder;
1176 folder->node = item->node = g_node_new(item);
1181 if (folder->account->imap_dir && *folder->account->imap_dir) {
1186 Xstrdup_a(root_folder, folder->account->imap_dir, {return -1;});
1187 extract_quote(root_folder, '"');
1188 subst_char(root_folder,
1189 imap_get_path_separator(IMAP_FOLDER(folder),
1192 strtailchomp(root_folder, '/');
1193 real_path = imap_get_real_path
1194 (IMAP_FOLDER(folder), root_folder);
1195 debug_print("IMAP root directory: %s\n", real_path);
1197 /* check if root directory exist */
1199 r = imap_threaded_list(session->folder, "", real_path,
1201 if ((r != MAILIMAP_NO_ERROR) || (clist_count(lep_list) == 0)) {
1202 if (!folder->node) {
1203 item = folder_item_new(folder, folder->name, NULL);
1204 item->folder = folder;
1205 folder->node = item->node = g_node_new(item);
1210 mailimap_list_result_free(lep_list);
1216 item = FOLDER_ITEM(folder->node->data);
1217 if (!item || ((item->path || root_folder) &&
1218 strcmp2(item->path, root_folder) != 0)) {
1219 folder_tree_destroy(folder);
1220 item = folder_item_new(folder, folder->name, root_folder);
1221 item->folder = folder;
1222 folder->node = item->node = g_node_new(item);
1225 imap_scan_tree_recursive(session, FOLDER_ITEM(folder->node->data));
1226 imap_create_missing_folders(folder);
1231 static gint imap_scan_tree_recursive(IMAPSession *session, FolderItem *item)
1234 IMAPFolder *imapfolder;
1235 FolderItem *new_item;
1236 GSList *item_list, *cur;
1239 gchar *wildcard_path;
1245 g_return_val_if_fail(item != NULL, -1);
1246 g_return_val_if_fail(item->folder != NULL, -1);
1247 g_return_val_if_fail(item->no_sub == FALSE, -1);
1249 folder = item->folder;
1250 imapfolder = IMAP_FOLDER(folder);
1252 separator = imap_get_path_separator(imapfolder, item->path);
1254 if (folder->ui_func)
1255 folder->ui_func(folder, item, folder->ui_func_data);
1258 wildcard[0] = separator;
1261 real_path = imap_get_real_path(imapfolder, item->path);
1265 real_path = g_strdup("");
1268 Xstrcat_a(wildcard_path, real_path, wildcard,
1269 {g_free(real_path); return IMAP_ERROR;});
1271 r = imap_threaded_list(folder, "", wildcard_path, &lep_list);
1272 if (r != MAILIMAP_NO_ERROR) {
1276 item_list = imap_list_from_lep(imapfolder,
1277 lep_list, real_path);
1278 mailimap_list_result_free(lep_list);
1283 node = item->node->children;
1284 while (node != NULL) {
1285 FolderItem *old_item = FOLDER_ITEM(node->data);
1286 GNode *next = node->next;
1289 for (cur = item_list; cur != NULL; cur = cur->next) {
1290 FolderItem *cur_item = FOLDER_ITEM(cur->data);
1291 if (!strcmp2(old_item->path, cur_item->path)) {
1292 new_item = cur_item;
1297 debug_print("folder '%s' not found. removing...\n",
1299 folder_item_remove(old_item);
1301 old_item->no_sub = new_item->no_sub;
1302 old_item->no_select = new_item->no_select;
1303 if (old_item->no_sub == TRUE && node->children) {
1304 debug_print("folder '%s' doesn't have "
1305 "subfolders. removing...\n",
1307 folder_item_remove_children(old_item);
1314 for (cur = item_list; cur != NULL; cur = cur->next) {
1315 FolderItem *cur_item = FOLDER_ITEM(cur->data);
1317 for (node = item->node->children; node != NULL;
1318 node = node->next) {
1319 if (!strcmp2(FOLDER_ITEM(node->data)->path,
1321 new_item = FOLDER_ITEM(node->data);
1322 folder_item_destroy(cur_item);
1328 new_item = cur_item;
1329 debug_print("new folder '%s' found.\n", new_item->path);
1330 folder_item_append(item, new_item);
1333 if (!strcmp(new_item->path, "INBOX")) {
1334 new_item->stype = F_INBOX;
1335 folder->inbox = new_item;
1336 } else if (!folder_item_parent(item) || item->stype == F_INBOX) {
1339 base = g_path_get_basename(new_item->path);
1341 if (!folder->outbox && !g_ascii_strcasecmp(base, "Sent")) {
1342 new_item->stype = F_OUTBOX;
1343 folder->outbox = new_item;
1344 } else if (!folder->draft && !g_ascii_strcasecmp(base, "Drafts")) {
1345 new_item->stype = F_DRAFT;
1346 folder->draft = new_item;
1347 } else if (!folder->queue && !g_ascii_strcasecmp(base, "Queue")) {
1348 new_item->stype = F_QUEUE;
1349 folder->queue = new_item;
1350 } else if (!folder->trash && !g_ascii_strcasecmp(base, "Trash")) {
1351 new_item->stype = F_TRASH;
1352 folder->trash = new_item;
1357 if (new_item->no_sub == FALSE)
1358 imap_scan_tree_recursive(session, new_item);
1361 g_slist_free(item_list);
1363 return IMAP_SUCCESS;
1366 static gint imap_create_tree(Folder *folder)
1368 g_return_val_if_fail(folder != NULL, -1);
1369 g_return_val_if_fail(folder->node != NULL, -1);
1370 g_return_val_if_fail(folder->node->data != NULL, -1);
1371 g_return_val_if_fail(folder->account != NULL, -1);
1373 imap_scan_tree(folder);
1374 imap_create_missing_folders(folder);
1379 static void imap_create_missing_folders(Folder *folder)
1381 g_return_if_fail(folder != NULL);
1384 folder->inbox = imap_create_special_folder
1385 (folder, F_INBOX, "INBOX");
1387 folder->trash = imap_create_special_folder
1388 (folder, F_TRASH, "Trash");
1391 static FolderItem *imap_create_special_folder(Folder *folder,
1392 SpecialFolderItemType stype,
1396 FolderItem *new_item;
1398 g_return_val_if_fail(folder != NULL, NULL);
1399 g_return_val_if_fail(folder->node != NULL, NULL);
1400 g_return_val_if_fail(folder->node->data != NULL, NULL);
1401 g_return_val_if_fail(folder->account != NULL, NULL);
1402 g_return_val_if_fail(name != NULL, NULL);
1404 item = FOLDER_ITEM(folder->node->data);
1405 new_item = imap_create_folder(folder, item, name);
1408 g_warning("Can't create '%s'\n", name);
1409 if (!folder->inbox) return NULL;
1411 new_item = imap_create_folder(folder, folder->inbox, name);
1413 g_warning("Can't create '%s' under INBOX\n", name);
1415 new_item->stype = stype;
1417 new_item->stype = stype;
1422 static gchar *imap_folder_get_path(Folder *folder)
1426 g_return_val_if_fail(folder != NULL, NULL);
1427 g_return_val_if_fail(folder->account != NULL, NULL);
1429 folder_path = g_strconcat(get_imap_cache_dir(),
1431 folder->account->recv_server,
1433 folder->account->userid,
1439 static gchar *imap_item_get_path(Folder *folder, FolderItem *item)
1441 gchar *folder_path, *path;
1443 g_return_val_if_fail(folder != NULL, NULL);
1444 g_return_val_if_fail(item != NULL, NULL);
1445 folder_path = imap_folder_get_path(folder);
1447 g_return_val_if_fail(folder_path != NULL, NULL);
1448 if (folder_path[0] == G_DIR_SEPARATOR) {
1450 path = g_strconcat(folder_path, G_DIR_SEPARATOR_S,
1453 path = g_strdup(folder_path);
1456 path = g_strconcat(get_home_dir(), G_DIR_SEPARATOR_S,
1457 folder_path, G_DIR_SEPARATOR_S,
1460 path = g_strconcat(get_home_dir(), G_DIR_SEPARATOR_S,
1463 g_free(folder_path);
1468 static FolderItem *imap_create_folder(Folder *folder, FolderItem *parent,
1471 gchar *dirpath, *imap_path;
1472 IMAPSession *session;
1473 FolderItem *new_item;
1479 g_return_val_if_fail(folder != NULL, NULL);
1480 g_return_val_if_fail(folder->account != NULL, NULL);
1481 g_return_val_if_fail(parent != NULL, NULL);
1482 g_return_val_if_fail(name != NULL, NULL);
1484 session = imap_session_get(folder);
1489 if (!folder_item_parent(parent) && strcmp(name, "INBOX") == 0)
1490 dirpath = g_strdup(name);
1491 else if (parent->path)
1492 dirpath = g_strconcat(parent->path, "/", name, NULL);
1493 else if ((p = strchr(name, '/')) != NULL && *(p + 1) != '\0')
1494 dirpath = g_strdup(name);
1495 else if (folder->account->imap_dir && *folder->account->imap_dir) {
1498 Xstrdup_a(imap_dir, folder->account->imap_dir, {return NULL;});
1499 strtailchomp(imap_dir, '/');
1500 dirpath = g_strconcat(imap_dir, "/", name, NULL);
1502 dirpath = g_strdup(name);
1504 /* keep trailing directory separator to create a folder that contains
1506 imap_path = imap_utf8_to_modified_utf7(dirpath);
1507 strtailchomp(dirpath, '/');
1508 Xstrdup_a(new_name, name, {
1512 strtailchomp(new_name, '/');
1513 separator = imap_get_path_separator(IMAP_FOLDER(folder), imap_path);
1514 imap_path_separator_subst(imap_path, separator);
1515 subst_char(new_name, '/', separator);
1517 if (strcmp(name, "INBOX") != 0) {
1519 gboolean exist = FALSE;
1523 argbuf = g_ptr_array_new();
1524 r = imap_threaded_list(folder, "", imap_path, &lep_list);
1525 if (r != MAILIMAP_NO_ERROR) {
1526 log_warning(_("can't create mailbox: LIST failed\n"));
1529 ptr_array_free_strings(argbuf);
1530 g_ptr_array_free(argbuf, TRUE);
1534 if (clist_count(lep_list) > 0)
1538 ok = imap_cmd_create(session, imap_path);
1539 if (ok != IMAP_SUCCESS) {
1540 log_warning(_("can't create mailbox\n"));
1548 new_item = folder_item_new(folder, new_name, dirpath);
1549 folder_item_append(parent, new_item);
1553 dirpath = folder_item_get_path(new_item);
1554 if (!is_dir_exist(dirpath))
1555 make_dir_hier(dirpath);
1561 static gint imap_rename_folder(Folder *folder, FolderItem *item,
1566 gchar *real_oldpath;
1567 gchar *real_newpath;
1569 gchar *old_cache_dir;
1570 gchar *new_cache_dir;
1571 IMAPSession *session;
1574 gint exists, recent, unseen;
1575 guint32 uid_validity;
1577 g_return_val_if_fail(folder != NULL, -1);
1578 g_return_val_if_fail(item != NULL, -1);
1579 g_return_val_if_fail(item->path != NULL, -1);
1580 g_return_val_if_fail(name != NULL, -1);
1582 if (strchr(name, imap_get_path_separator(IMAP_FOLDER(folder), item->path)) != NULL) {
1583 g_warning(_("New folder name must not contain the namespace "
1588 session = imap_session_get(folder);
1592 real_oldpath = imap_get_real_path(IMAP_FOLDER(folder), item->path);
1594 g_free(session->mbox);
1595 session->mbox = NULL;
1596 ok = imap_cmd_examine(session, "INBOX",
1597 &exists, &recent, &unseen, &uid_validity, FALSE);
1598 if (ok != IMAP_SUCCESS) {
1599 g_free(real_oldpath);
1603 separator = imap_get_path_separator(IMAP_FOLDER(folder), item->path);
1604 if (strchr(item->path, G_DIR_SEPARATOR)) {
1605 dirpath = g_path_get_dirname(item->path);
1606 newpath = g_strconcat(dirpath, G_DIR_SEPARATOR_S, name, NULL);
1609 newpath = g_strdup(name);
1611 real_newpath = imap_utf8_to_modified_utf7(newpath);
1612 imap_path_separator_subst(real_newpath, separator);
1614 ok = imap_cmd_rename(session, real_oldpath, real_newpath);
1615 if (ok != IMAP_SUCCESS) {
1616 log_warning(_("can't rename mailbox: %s to %s\n"),
1617 real_oldpath, real_newpath);
1618 g_free(real_oldpath);
1620 g_free(real_newpath);
1625 item->name = g_strdup(name);
1627 old_cache_dir = folder_item_get_path(item);
1629 paths[0] = g_strdup(item->path);
1631 g_node_traverse(item->node, G_PRE_ORDER, G_TRAVERSE_ALL, -1,
1632 imap_rename_folder_func, paths);
1634 if (is_dir_exist(old_cache_dir)) {
1635 new_cache_dir = folder_item_get_path(item);
1636 if (rename(old_cache_dir, new_cache_dir) < 0) {
1637 FILE_OP_ERROR(old_cache_dir, "rename");
1639 g_free(new_cache_dir);
1642 g_free(old_cache_dir);
1645 g_free(real_oldpath);
1646 g_free(real_newpath);
1651 static gint imap_remove_folder_real(Folder *folder, FolderItem *item)
1654 IMAPSession *session;
1657 gint exists, recent, unseen;
1658 guint32 uid_validity;
1660 g_return_val_if_fail(folder != NULL, -1);
1661 g_return_val_if_fail(item != NULL, -1);
1662 g_return_val_if_fail(item->path != NULL, -1);
1664 session = imap_session_get(folder);
1668 path = imap_get_real_path(IMAP_FOLDER(folder), item->path);
1670 ok = imap_cmd_examine(session, "INBOX",
1671 &exists, &recent, &unseen, &uid_validity, FALSE);
1672 if (ok != IMAP_SUCCESS) {
1677 ok = imap_cmd_delete(session, path);
1678 if (ok != IMAP_SUCCESS) {
1679 log_warning(_("can't delete mailbox\n"));
1685 cache_dir = folder_item_get_path(item);
1686 if (is_dir_exist(cache_dir) && remove_dir_recursive(cache_dir) < 0)
1687 g_warning("can't remove directory '%s'\n", cache_dir);
1689 folder_item_remove(item);
1694 static gint imap_remove_folder(Folder *folder, FolderItem *item)
1698 g_return_val_if_fail(item != NULL, -1);
1699 g_return_val_if_fail(item->folder != NULL, -1);
1700 g_return_val_if_fail(item->node != NULL, -1);
1702 node = item->node->children;
1703 while (node != NULL) {
1705 if (imap_remove_folder(folder, FOLDER_ITEM(node->data)) < 0)
1709 debug_print("IMAP removing %s\n", item->path);
1711 if (imap_remove_all_msg(folder, item) < 0)
1713 return imap_remove_folder_real(folder, item);
1716 typedef struct _uncached_data {
1717 IMAPSession *session;
1719 MsgNumberList *numlist;
1725 static void *imap_get_uncached_messages_thread(void *data)
1727 uncached_data *stuff = (uncached_data *)data;
1728 IMAPSession *session = stuff->session;
1729 FolderItem *item = stuff->item;
1730 MsgNumberList *numlist = stuff->numlist;
1732 GSList *newlist = NULL;
1733 GSList *llast = NULL;
1734 GSList *seq_list, *cur;
1736 debug_print("uncached_messages\n");
1738 if (session == NULL || item == NULL || item->folder == NULL
1739 || FOLDER_CLASS(item->folder) != &imap_class) {
1744 seq_list = imap_get_lep_set_from_numlist(numlist);
1745 debug_print("get msgs info\n");
1746 for (cur = seq_list; cur != NULL; cur = g_slist_next(cur)) {
1747 struct mailimap_set * imapset;
1753 imapset = cur->data;
1755 r = imap_threaded_fetch_env(session->folder,
1756 imapset, &env_list);
1757 if (r != MAILIMAP_NO_ERROR)
1761 for(i = 0 ; i < carray_count(env_list) ; i ++) {
1762 struct imap_fetch_env_info * info;
1765 info = carray_get(env_list, i);
1766 msginfo = imap_envelope_from_lep(info, item);
1767 msginfo->folder = item;
1769 llast = newlist = g_slist_append(newlist, msginfo);
1771 llast = g_slist_append(llast, msginfo);
1772 llast = llast->next;
1777 imap_fetch_env_free(env_list);
1780 session_set_access_time(SESSION(session));
1785 #define MAX_MSG_NUM 50
1787 static GSList *imap_get_uncached_messages(IMAPSession *session,
1789 MsgNumberList *numlist)
1791 GSList *result = NULL;
1793 uncached_data *data = g_new0(uncached_data, 1);
1798 data->total = g_slist_length(numlist);
1799 debug_print("messages list : %i\n", data->total);
1800 log_print(_("IMAP> Getting uncached messages for %s\n"), item->path);
1801 while (cur != NULL) {
1802 GSList * partial_result;
1810 while (count < MAX_MSG_NUM) {
1815 if (newlist == NULL)
1816 llast = newlist = g_slist_append(newlist, p);
1818 llast = g_slist_append(llast, p);
1819 llast = llast->next;
1829 data->session = session;
1831 data->numlist = newlist;
1834 if (prefs_common.work_offline && !imap_gtk_should_override()) {
1836 log_error(_("IMAP< Error\n"));
1841 (GSList *)imap_get_uncached_messages_thread(data);
1845 g_snprintf(buf, sizeof(buf), "%d / %d",
1846 data->cur, data->total);
1847 gtk_progress_bar_set_text
1848 (GTK_PROGRESS_BAR(mainwindow_get_mainwindow()->progressbar), buf);
1849 gtk_progress_bar_set_fraction
1850 (GTK_PROGRESS_BAR(mainwindow_get_mainwindow()->progressbar),
1851 (gfloat)data->cur / (gfloat)data->total);
1852 debug_print("update progress %g\n",
1853 (gfloat)data->cur / (gfloat)data->total);
1856 g_slist_free(newlist);
1858 result = g_slist_concat(result, partial_result);
1862 gtk_progress_bar_set_fraction
1863 (GTK_PROGRESS_BAR(mainwindow_get_mainwindow()->progressbar), 0);
1864 gtk_progress_bar_set_text
1865 (GTK_PROGRESS_BAR(mainwindow_get_mainwindow()->progressbar), "");
1866 statusbar_pop_all();
1868 log_print(_("IMAP< Done\n"));
1872 static void imap_delete_all_cached_messages(FolderItem *item)
1876 g_return_if_fail(item != NULL);
1877 g_return_if_fail(item->folder != NULL);
1878 g_return_if_fail(FOLDER_CLASS(item->folder) == &imap_class);
1880 debug_print("Deleting all cached messages...\n");
1882 dir = folder_item_get_path(item);
1883 if (is_dir_exist(dir))
1884 remove_all_numbered_files(dir);
1887 debug_print("done.\n");
1890 static IMAPNameSpace *imap_find_namespace_from_list(GList *ns_list,
1893 IMAPNameSpace *namespace = NULL;
1894 gchar *tmp_path, *name;
1896 if (!path) path = "";
1898 for (; ns_list != NULL; ns_list = ns_list->next) {
1899 IMAPNameSpace *tmp_ns = ns_list->data;
1901 Xstrcat_a(tmp_path, path, "/", return namespace);
1902 Xstrdup_a(name, tmp_ns->name, return namespace);
1903 if (tmp_ns->separator && tmp_ns->separator != '/') {
1904 subst_char(tmp_path, tmp_ns->separator, '/');
1905 subst_char(name, tmp_ns->separator, '/');
1907 if (strncmp(tmp_path, name, strlen(name)) == 0)
1914 static IMAPNameSpace *imap_find_namespace(IMAPFolder *folder,
1917 IMAPNameSpace *namespace;
1919 g_return_val_if_fail(folder != NULL, NULL);
1921 namespace = imap_find_namespace_from_list(folder->ns_personal, path);
1922 if (namespace) return namespace;
1923 namespace = imap_find_namespace_from_list(folder->ns_others, path);
1924 if (namespace) return namespace;
1925 namespace = imap_find_namespace_from_list(folder->ns_shared, path);
1926 if (namespace) return namespace;
1931 static gchar imap_get_path_separator(IMAPFolder *folder, const gchar *path)
1933 IMAPNameSpace *namespace;
1934 gchar separator = '/';
1936 namespace = imap_find_namespace(folder, path);
1937 if (namespace && namespace->separator)
1938 separator = namespace->separator;
1943 static gchar *imap_get_real_path(IMAPFolder *folder, const gchar *path)
1948 g_return_val_if_fail(folder != NULL, NULL);
1949 g_return_val_if_fail(path != NULL, NULL);
1951 real_path = imap_utf8_to_modified_utf7(path);
1952 separator = imap_get_path_separator(folder, path);
1953 imap_path_separator_subst(real_path, separator);
1958 static gint imap_set_message_flags(IMAPSession *session,
1959 MsgNumberList *numlist,
1967 seq_list = imap_get_lep_set_from_numlist(numlist);
1969 for(cur = seq_list ; cur != NULL ; cur = g_slist_next(cur)) {
1970 struct mailimap_set * imapset;
1972 imapset = cur->data;
1974 ok = imap_cmd_store(session, imapset,
1978 imap_lep_set_free(seq_list);
1980 return IMAP_SUCCESS;
1983 typedef struct _select_data {
1984 IMAPSession *session;
1989 guint32 *uid_validity;
1993 static gint imap_select(IMAPSession *session, IMAPFolder *folder,
1995 gint *exists, gint *recent, gint *unseen,
1996 guint32 *uid_validity, gboolean block)
2000 gint exists_, recent_, unseen_;
2001 guint32 uid_validity_;
2003 if (!exists || !recent || !unseen || !uid_validity) {
2004 if (session->mbox && strcmp(session->mbox, path) == 0)
2005 return IMAP_SUCCESS;
2009 uid_validity = &uid_validity_;
2012 g_free(session->mbox);
2013 session->mbox = NULL;
2015 real_path = imap_get_real_path(folder, path);
2017 ok = imap_cmd_select(session, real_path,
2018 exists, recent, unseen, uid_validity, block);
2019 if (ok != IMAP_SUCCESS)
2020 log_warning(_("can't select folder: %s\n"), real_path);
2022 session->mbox = g_strdup(path);
2023 session->folder_content_changed = FALSE;
2030 static gint imap_status(IMAPSession *session, IMAPFolder *folder,
2032 gint *messages, gint *recent,
2033 guint32 *uid_next, guint32 *uid_validity,
2034 gint *unseen, gboolean block)
2038 struct mailimap_mailbox_data_status * data_status;
2041 r = imap_threaded_status(FOLDER(folder), path, &data_status);
2042 if (r != MAILIMAP_NO_ERROR) {
2043 debug_print("status err %d\n", r);
2047 if (data_status->st_info_list == NULL) {
2048 mailimap_mailbox_data_status_free(data_status);
2049 debug_print("status->st_info_list == NULL\n");
2054 for(iter = clist_begin(data_status->st_info_list) ; iter != NULL ;
2055 iter = clist_next(iter)) {
2056 struct mailimap_status_info * info;
2058 info = clist_content(iter);
2059 switch (info->st_att) {
2060 case MAILIMAP_STATUS_ATT_MESSAGES:
2061 * messages = info->st_value;
2062 got_values |= 1 << 0;
2065 case MAILIMAP_STATUS_ATT_RECENT:
2066 * recent = info->st_value;
2067 got_values |= 1 << 1;
2070 case MAILIMAP_STATUS_ATT_UIDNEXT:
2071 * uid_next = info->st_value;
2072 got_values |= 1 << 2;
2075 case MAILIMAP_STATUS_ATT_UIDVALIDITY:
2076 * uid_validity = info->st_value;
2077 got_values |= 1 << 3;
2080 case MAILIMAP_STATUS_ATT_UNSEEN:
2081 * unseen = info->st_value;
2082 got_values |= 1 << 4;
2086 mailimap_mailbox_data_status_free(data_status);
2088 if (got_values != ((1 << 4) + (1 << 3) +
2089 (1 << 2) + (1 << 1) + (1 << 0))) {
2090 debug_print("status: incomplete values received (%d)\n", got_values);
2093 return IMAP_SUCCESS;
2096 static void imap_free_capabilities(IMAPSession *session)
2098 g_strfreev(session->capability);
2099 session->capability = NULL;
2102 /* low-level IMAP4rev1 commands */
2105 static gint imap_cmd_authenticate(IMAPSession *session, const gchar *user,
2106 const gchar *pass, IMAPAuthType type)
2113 gchar hexdigest[33];
2117 auth_type = "CRAM-MD5";
2119 imap_gen_send(session, "AUTHENTICATE %s", auth_type);
2120 ok = imap_gen_recv(session, &buf);
2121 if (ok != IMAP_SUCCESS || buf[0] != '+' || buf[1] != ' ') {
2126 challenge = g_malloc(strlen(buf + 2) + 1);
2127 challenge_len = base64_decode(challenge, buf + 2, -1);
2128 challenge[challenge_len] = '\0';
2130 log_print(_("IMAP< [Decoded: %s]\n"), challenge);
2132 md5_hex_hmac(hexdigest, challenge, challenge_len, pass, strlen(pass));
2135 response = g_strdup_printf("%s %s", user, hexdigest);
2136 log_print(_("IMAP> [Encoded: %s]\n"), response);
2137 response64 = g_malloc((strlen(response) + 3) * 2 + 1);
2138 base64_encode(response64, response, strlen(response));
2141 log_print(_("IMAP> %s\n"), response64);
2142 sock_puts(SESSION(session)->sock, response64);
2143 ok = imap_cmd_ok(session, NULL);
2144 if (ok != IMAP_SUCCESS)
2145 log_warning(_("IMAP4 authentication failed.\n"));
2151 static gint imap_cmd_login(IMAPSession *session,
2152 const gchar *user, const gchar *pass)
2156 static time_t last_login_err = 0;
2158 log_print(_("IMAP> Logging in to %s\n"), SESSION(session)->server);
2159 r = imap_threaded_login(session->folder, user, pass);
2160 if (r != MAILIMAP_NO_ERROR) {
2161 if (time(NULL) - last_login_err > 10) {
2162 alertpanel_error(_("Connection to %s failed: login refused."),
2163 SESSION(session)->server);
2165 last_login_err = time(NULL);
2166 log_error(_("IMAP< Error\n"));
2169 log_print(_("IMAP< Done\n"));
2175 static gint imap_cmd_logout(IMAPSession *session)
2177 log_print(_("IMAP> Logging out of %s\n"), SESSION(session)->server);
2178 imap_threaded_disconnect(session->folder);
2180 return IMAP_SUCCESS;
2183 static gint imap_cmd_noop(IMAPSession *session)
2186 unsigned int exists;
2188 r = imap_threaded_noop(session->folder, &exists);
2189 if (r != MAILIMAP_NO_ERROR) {
2190 debug_print("noop err %d\n", r);
2193 session->exists = exists;
2195 return IMAP_SUCCESS;
2199 static gint imap_cmd_starttls(IMAPSession *session)
2203 log_print(_("IMAP> Starting TLS\n"));
2204 r = imap_threaded_starttls(session->folder);
2205 if (r != MAILIMAP_NO_ERROR) {
2206 debug_print("starttls err %d\n", r);
2207 log_error(_("IMAP< Error\n"));
2210 log_print(_("IMAP< Done\n"));
2211 return IMAP_SUCCESS;
2215 static gint imap_cmd_select(IMAPSession *session, const gchar *folder,
2216 gint *exists, gint *recent, gint *unseen,
2217 guint32 *uid_validity, gboolean block)
2221 log_print(_("IMAP> Selecting %s\n"), folder);
2222 r = imap_threaded_select(session->folder, folder,
2223 exists, recent, unseen, uid_validity);
2224 if (r != MAILIMAP_NO_ERROR) {
2225 debug_print("select err %d\n", r);
2226 log_error(_("IMAP< Error\n"));
2229 log_print(_("IMAP< Done\n"));
2230 return IMAP_SUCCESS;
2233 static gint imap_cmd_examine(IMAPSession *session, const gchar *folder,
2234 gint *exists, gint *recent, gint *unseen,
2235 guint32 *uid_validity, gboolean block)
2239 log_print(_("IMAP> Examining %s\n"), folder);
2240 r = imap_threaded_examine(session->folder, folder,
2241 exists, recent, unseen, uid_validity);
2242 if (r != MAILIMAP_NO_ERROR) {
2243 debug_print("examine err %d\n", r);
2244 log_error(_("IMAP< Error\n"));
2247 log_print(_("IMAP< Done\n"));
2248 return IMAP_SUCCESS;
2251 static gint imap_cmd_create(IMAPSession *session, const gchar *folder)
2254 log_print(_("IMAP> Creating %s\n"), folder);
2255 r = imap_threaded_create(session->folder, folder);
2256 if (r != MAILIMAP_NO_ERROR) {
2257 log_error(_("IMAP< Error\n"));
2260 log_print(_("IMAP< Done\n"));
2261 return IMAP_SUCCESS;
2264 static gint imap_cmd_rename(IMAPSession *session, const gchar *old_folder,
2265 const gchar *new_folder)
2268 log_print(_("IMAP> Renaming %s to %s\n"), old_folder, new_folder);
2269 r = imap_threaded_rename(session->folder, old_folder,
2271 if (r != MAILIMAP_NO_ERROR) {
2272 log_error(_("IMAP< Error\n"));
2275 log_print(_("IMAP< Done\n"));
2276 return IMAP_SUCCESS;
2279 static gint imap_cmd_delete(IMAPSession *session, const gchar *folder)
2283 log_print(_("IMAP> Deleting %s\n"), folder);
2284 r = imap_threaded_delete(session->folder, folder);
2285 if (r != MAILIMAP_NO_ERROR) {
2286 log_error(_("IMAP< Error\n"));
2289 log_print(_("IMAP< Done\n"));
2290 return IMAP_SUCCESS;
2293 typedef struct _fetch_data {
2294 IMAPSession *session;
2296 const gchar *filename;
2302 static void *imap_cmd_fetch_thread(void *data)
2304 fetch_data *stuff = (fetch_data *)data;
2305 IMAPSession *session = stuff->session;
2306 guint32 uid = stuff->uid;
2307 const gchar *filename = stuff->filename;
2311 r = imap_threaded_fetch_content(session->folder,
2315 r = imap_threaded_fetch_content(session->folder,
2318 if (r != MAILIMAP_NO_ERROR) {
2319 debug_print("fetch err %d\n", r);
2320 return GINT_TO_POINTER(IMAP_ERROR);
2322 return GINT_TO_POINTER(IMAP_SUCCESS);
2325 static gint imap_cmd_fetch(IMAPSession *session, guint32 uid,
2326 const gchar *filename, gboolean headers,
2329 fetch_data *data = g_new0(fetch_data, 1);
2332 data->session = session;
2334 data->filename = filename;
2335 data->headers = headers;
2338 if (prefs_common.work_offline && !imap_gtk_should_override()) {
2343 result = GPOINTER_TO_INT(imap_cmd_fetch_thread(data));
2349 static gint imap_cmd_append(IMAPSession *session, const gchar *destfolder,
2350 const gchar *file, IMAPFlags flags,
2353 struct mailimap_flag_list * flag_list;
2356 g_return_val_if_fail(file != NULL, IMAP_ERROR);
2358 flag_list = imap_flag_to_lep(flags);
2359 r = imap_threaded_append(session->folder, destfolder,
2362 if (new_uid != NULL)
2365 if (r != MAILIMAP_NO_ERROR) {
2366 debug_print("append err %d\n", r);
2369 return IMAP_SUCCESS;
2372 static gint imap_cmd_copy(IMAPSession *session, struct mailimap_set * set,
2373 const gchar *destfolder, GRelation *uid_mapping)
2377 g_return_val_if_fail(session != NULL, IMAP_ERROR);
2378 g_return_val_if_fail(set != NULL, IMAP_ERROR);
2379 g_return_val_if_fail(destfolder != NULL, IMAP_ERROR);
2381 log_print(_("IMAP> Copying to %s\n"), destfolder);
2382 r = imap_threaded_copy(session->folder, set, destfolder);
2383 if (r != MAILIMAP_NO_ERROR) {
2384 log_error(_("IMAP< Error\n"));
2387 log_print(_("IMAP< Done\n"));
2388 return IMAP_SUCCESS;
2391 static gint imap_cmd_store(IMAPSession *session, struct mailimap_set * set,
2392 IMAPFlags flags, int do_add)
2395 struct mailimap_flag_list * flag_list;
2396 struct mailimap_store_att_flags * store_att_flags;
2398 flag_list = imap_flag_to_lep(flags);
2402 mailimap_store_att_flags_new_add_flags_silent(flag_list);
2405 mailimap_store_att_flags_new_remove_flags_silent(flag_list);
2407 log_print(_("IMAP> Storing\n"));
2408 r = imap_threaded_store(session->folder, set, store_att_flags);
2409 if (r != MAILIMAP_NO_ERROR) {
2410 log_error(_("IMAP< Error\n"));
2413 log_print(_("IMAP< Done\n"));
2414 return IMAP_SUCCESS;
2417 static gint imap_cmd_expunge(IMAPSession *session)
2421 if (prefs_common.work_offline && !imap_gtk_should_override()) {
2424 log_print(_("IMAP> Expunging\n"));
2425 r = imap_threaded_expunge(session->folder);
2426 if (r != MAILIMAP_NO_ERROR) {
2427 log_error(_("IMAP< Error\n"));
2430 log_print(_("IMAP< Done\n"));
2431 return IMAP_SUCCESS;
2434 static void imap_path_separator_subst(gchar *str, gchar separator)
2437 gboolean in_escape = FALSE;
2439 if (!separator || separator == '/') return;
2441 for (p = str; *p != '\0'; p++) {
2442 if (*p == '/' && !in_escape)
2444 else if (*p == '&' && *(p + 1) != '-' && !in_escape)
2446 else if (*p == '-' && in_escape)
2451 static gchar *imap_modified_utf7_to_utf8(const gchar *mutf7_str)
2453 static iconv_t cd = (iconv_t)-1;
2454 static gboolean iconv_ok = TRUE;
2457 size_t norm_utf7_len;
2459 gchar *to_str, *to_p;
2461 gboolean in_escape = FALSE;
2463 if (!iconv_ok) return g_strdup(mutf7_str);
2465 if (cd == (iconv_t)-1) {
2466 cd = iconv_open(CS_INTERNAL, CS_UTF_7);
2467 if (cd == (iconv_t)-1) {
2468 g_warning("iconv cannot convert UTF-7 to %s\n",
2471 return g_strdup(mutf7_str);
2475 /* modified UTF-7 to normal UTF-7 conversion */
2476 norm_utf7 = g_string_new(NULL);
2478 for (p = mutf7_str; *p != '\0'; p++) {
2479 /* replace: '&' -> '+',
2481 escaped ',' -> '/' */
2482 if (!in_escape && *p == '&') {
2483 if (*(p + 1) != '-') {
2484 g_string_append_c(norm_utf7, '+');
2487 g_string_append_c(norm_utf7, '&');
2490 } else if (in_escape && *p == ',') {
2491 g_string_append_c(norm_utf7, '/');
2492 } else if (in_escape && *p == '-') {
2493 g_string_append_c(norm_utf7, '-');
2496 g_string_append_c(norm_utf7, *p);
2500 norm_utf7_p = norm_utf7->str;
2501 norm_utf7_len = norm_utf7->len;
2502 to_len = strlen(mutf7_str) * 5;
2503 to_p = to_str = g_malloc(to_len + 1);
2505 if (iconv(cd, (ICONV_CONST gchar **)&norm_utf7_p, &norm_utf7_len,
2506 &to_p, &to_len) == -1) {
2507 g_warning(_("iconv cannot convert UTF-7 to %s\n"),
2508 conv_get_locale_charset_str());
2509 g_string_free(norm_utf7, TRUE);
2511 return g_strdup(mutf7_str);
2514 /* second iconv() call for flushing */
2515 iconv(cd, NULL, NULL, &to_p, &to_len);
2516 g_string_free(norm_utf7, TRUE);
2522 static gchar *imap_utf8_to_modified_utf7(const gchar *from)
2524 static iconv_t cd = (iconv_t)-1;
2525 static gboolean iconv_ok = TRUE;
2526 gchar *norm_utf7, *norm_utf7_p;
2527 size_t from_len, norm_utf7_len;
2529 gchar *from_tmp, *to, *p;
2530 gboolean in_escape = FALSE;
2532 if (!iconv_ok) return g_strdup(from);
2534 if (cd == (iconv_t)-1) {
2535 cd = iconv_open(CS_UTF_7, CS_INTERNAL);
2536 if (cd == (iconv_t)-1) {
2537 g_warning(_("iconv cannot convert %s to UTF-7\n"),
2540 return g_strdup(from);
2544 /* UTF-8 to normal UTF-7 conversion */
2545 Xstrdup_a(from_tmp, from, return g_strdup(from));
2546 from_len = strlen(from);
2547 norm_utf7_len = from_len * 5;
2548 Xalloca(norm_utf7, norm_utf7_len + 1, return g_strdup(from));
2549 norm_utf7_p = norm_utf7;
2551 #define IS_PRINT(ch) (isprint(ch) && IS_ASCII(ch))
2553 while (from_len > 0) {
2554 if (*from_tmp == '+') {
2555 *norm_utf7_p++ = '+';
2556 *norm_utf7_p++ = '-';
2560 } else if (IS_PRINT(*(guchar *)from_tmp)) {
2561 /* printable ascii char */
2562 *norm_utf7_p = *from_tmp;
2568 size_t conv_len = 0;
2570 /* unprintable char: convert to UTF-7 */
2572 while (!IS_PRINT(*(guchar *)p) && conv_len < from_len) {
2573 conv_len += g_utf8_skip[*(guchar *)p];
2574 p += g_utf8_skip[*(guchar *)p];
2577 from_len -= conv_len;
2578 if (iconv(cd, (ICONV_CONST gchar **)&from_tmp,
2580 &norm_utf7_p, &norm_utf7_len) == -1) {
2581 g_warning(_("iconv cannot convert UTF-8 to UTF-7\n"));
2582 return g_strdup(from);
2585 /* second iconv() call for flushing */
2586 iconv(cd, NULL, NULL, &norm_utf7_p, &norm_utf7_len);
2592 *norm_utf7_p = '\0';
2593 to_str = g_string_new(NULL);
2594 for (p = norm_utf7; p < norm_utf7_p; p++) {
2595 /* replace: '&' -> "&-",
2598 BASE64 '/' -> ',' */
2599 if (!in_escape && *p == '&') {
2600 g_string_append(to_str, "&-");
2601 } else if (!in_escape && *p == '+') {
2602 if (*(p + 1) == '-') {
2603 g_string_append_c(to_str, '+');
2606 g_string_append_c(to_str, '&');
2609 } else if (in_escape && *p == '/') {
2610 g_string_append_c(to_str, ',');
2611 } else if (in_escape && *p == '-') {
2612 g_string_append_c(to_str, '-');
2615 g_string_append_c(to_str, *p);
2621 g_string_append_c(to_str, '-');
2625 g_string_free(to_str, FALSE);
2630 static gboolean imap_rename_folder_func(GNode *node, gpointer data)
2632 FolderItem *item = node->data;
2633 gchar **paths = data;
2634 const gchar *oldpath = paths[0];
2635 const gchar *newpath = paths[1];
2637 gchar *new_itempath;
2640 oldpathlen = strlen(oldpath);
2641 if (strncmp(oldpath, item->path, oldpathlen) != 0) {
2642 g_warning("path doesn't match: %s, %s\n", oldpath, item->path);
2646 base = item->path + oldpathlen;
2647 while (*base == G_DIR_SEPARATOR) base++;
2649 new_itempath = g_strdup(newpath);
2651 new_itempath = g_strconcat(newpath, G_DIR_SEPARATOR_S, base,
2654 item->path = new_itempath;
2659 typedef struct _get_list_uid_data {
2661 IMAPFolderItem *item;
2662 GSList **msgnum_list;
2664 } get_list_uid_data;
2666 static void *get_list_of_uids_thread(void *data)
2668 get_list_uid_data *stuff = (get_list_uid_data *)data;
2669 Folder *folder = stuff->folder;
2670 IMAPFolderItem *item = stuff->item;
2671 GSList **msgnum_list = stuff->msgnum_list;
2672 gint ok, nummsgs = 0, lastuid_old;
2673 IMAPSession *session;
2674 GSList *uidlist, *elem;
2675 struct mailimap_set * set;
2676 clist * lep_uidlist;
2679 session = imap_session_get(folder);
2680 if (session == NULL) {
2682 return GINT_TO_POINTER(-1);
2685 ok = imap_select(session, IMAP_FOLDER(folder), item->item.path,
2686 NULL, NULL, NULL, NULL, TRUE);
2687 if (ok != IMAP_SUCCESS) {
2689 return GINT_TO_POINTER(-1);
2694 set = mailimap_set_new_interval(item->lastuid + 1, 0);
2695 r = imap_threaded_search(folder, IMAP_SEARCH_TYPE_SIMPLE, set,
2697 if (r == MAILIMAP_NO_ERROR) {
2698 GSList * fetchuid_list;
2701 imap_uid_list_from_lep(lep_uidlist);
2702 uidlist = g_slist_concat(fetchuid_list, uidlist);
2705 GSList * fetchuid_list;
2706 carray * lep_uidtab;
2708 r = imap_threaded_fetch_uid(folder, item->lastuid + 1,
2710 if (r == MAILIMAP_NO_ERROR) {
2712 imap_uid_list_from_lep_tab(lep_uidtab);
2713 uidlist = g_slist_concat(fetchuid_list, uidlist);
2717 lastuid_old = item->lastuid;
2718 *msgnum_list = g_slist_copy(item->uid_list);
2719 nummsgs = g_slist_length(*msgnum_list);
2720 debug_print("Got %d uids from cache\n", g_slist_length(item->uid_list));
2722 for (elem = uidlist; elem != NULL; elem = g_slist_next(elem)) {
2725 msgnum = GPOINTER_TO_INT(elem->data);
2726 if (msgnum > lastuid_old) {
2727 *msgnum_list = g_slist_prepend(*msgnum_list, GINT_TO_POINTER(msgnum));
2728 item->uid_list = g_slist_prepend(item->uid_list, GINT_TO_POINTER(msgnum));
2731 if(msgnum > item->lastuid)
2732 item->lastuid = msgnum;
2735 g_slist_free(uidlist);
2738 return GINT_TO_POINTER(nummsgs);
2741 static gint get_list_of_uids(Folder *folder, IMAPFolderItem *item, GSList **msgnum_list)
2744 get_list_uid_data *data = g_new0(get_list_uid_data, 1);
2746 data->folder = folder;
2748 data->msgnum_list = msgnum_list;
2750 if (prefs_common.work_offline && !imap_gtk_should_override()) {
2755 result = GPOINTER_TO_INT(get_list_of_uids_thread(data));
2761 gint imap_get_num_list(Folder *folder, FolderItem *_item, GSList **msgnum_list, gboolean *old_uids_valid)
2763 IMAPFolderItem *item = (IMAPFolderItem *)_item;
2764 IMAPSession *session;
2765 gint ok, nummsgs = 0, exists, recent, uid_val, uid_next, unseen;
2766 GSList *uidlist = NULL;
2768 gboolean selected_folder;
2770 debug_print("get_num_list\n");
2772 g_return_val_if_fail(folder != NULL, -1);
2773 g_return_val_if_fail(item != NULL, -1);
2774 g_return_val_if_fail(item->item.path != NULL, -1);
2775 g_return_val_if_fail(FOLDER_CLASS(folder) == &imap_class, -1);
2776 g_return_val_if_fail(folder->account != NULL, -1);
2778 session = imap_session_get(folder);
2779 g_return_val_if_fail(session != NULL, -1);
2781 log_print(_("IMAP> Getting numbers list for %s\n"), item->item.path);
2783 selected_folder = (session->mbox != NULL) &&
2784 (!strcmp(session->mbox, item->item.path));
2785 if (selected_folder) {
2786 ok = imap_cmd_noop(session);
2787 if (ok != IMAP_SUCCESS) {
2788 log_error(_("IMAP< Error\n"));
2791 exists = session->exists;
2793 *old_uids_valid = TRUE;
2795 ok = imap_status(session, IMAP_FOLDER(folder), item->item.path,
2796 &exists, &recent, &uid_next, &uid_val, &unseen, FALSE);
2797 if (ok != IMAP_SUCCESS) {
2798 log_error(_("IMAP< Error\n"));
2801 if(item->item.mtime == uid_val)
2802 *old_uids_valid = TRUE;
2804 *old_uids_valid = FALSE;
2806 debug_print("Freeing imap uid cache\n");
2808 g_slist_free(item->uid_list);
2809 item->uid_list = NULL;
2811 item->item.mtime = uid_val;
2813 imap_delete_all_cached_messages((FolderItem *)item);
2817 if (!selected_folder)
2818 item->uid_next = uid_next;
2820 /* If old uid_next matches new uid_next we can be sure no message
2821 was added to the folder */
2822 if (( selected_folder && !session->folder_content_changed) ||
2823 (!selected_folder && uid_next == item->uid_next)) {
2824 nummsgs = g_slist_length(item->uid_list);
2826 /* If number of messages is still the same we
2827 know our caches message numbers are still valid,
2828 otherwise if the number of messages has decrease
2829 we discard our cache to start a new scan to find
2830 out which numbers have been removed */
2831 if (exists == nummsgs) {
2832 *msgnum_list = g_slist_copy(item->uid_list);
2833 log_print(_("IMAP< Done\n"));
2835 } else if (exists < nummsgs) {
2836 debug_print("Freeing imap uid cache");
2838 g_slist_free(item->uid_list);
2839 item->uid_list = NULL;
2844 *msgnum_list = NULL;
2845 log_print(_("IMAP< Done\n"));
2849 nummsgs = get_list_of_uids(folder, item, &uidlist);
2852 log_error(_("IMAP< Error\n"));
2856 if (nummsgs != exists) {
2857 /* Cache contains more messages then folder, we have cached
2858 an old UID of a message that was removed and new messages
2859 have been added too, otherwise the uid_next check would
2861 debug_print("Freeing imap uid cache");
2863 g_slist_free(item->uid_list);
2864 item->uid_list = NULL;
2866 g_slist_free(*msgnum_list);
2868 nummsgs = get_list_of_uids(folder, item, &uidlist);
2871 *msgnum_list = uidlist;
2873 dir = folder_item_get_path((FolderItem *)item);
2874 debug_print("removing old messages from %s\n", dir);
2875 remove_numbered_files_not_in_list(dir, *msgnum_list);
2878 debug_print("get_num_list - ok - %i\n", nummsgs);
2880 log_print(_("IMAP< Done\n"));
2884 static MsgInfo *imap_parse_msg(const gchar *file, FolderItem *item)
2889 flags.perm_flags = MSG_NEW|MSG_UNREAD;
2890 flags.tmp_flags = 0;
2892 g_return_val_if_fail(item != NULL, NULL);
2893 g_return_val_if_fail(file != NULL, NULL);
2895 if (item->stype == F_QUEUE) {
2896 MSG_SET_TMP_FLAGS(flags, MSG_QUEUED);
2897 } else if (item->stype == F_DRAFT) {
2898 MSG_SET_TMP_FLAGS(flags, MSG_DRAFT);
2901 msginfo = procheader_parse_file(file, flags, FALSE, FALSE);
2902 if (!msginfo) return NULL;
2904 msginfo->plaintext_file = g_strdup(file);
2905 msginfo->folder = item;
2910 GSList *imap_get_msginfos(Folder *folder, FolderItem *item,
2911 GSList *msgnum_list)
2913 IMAPSession *session;
2914 MsgInfoList *ret = NULL;
2917 debug_print("get_msginfos\n");
2919 g_return_val_if_fail(folder != NULL, NULL);
2920 g_return_val_if_fail(item != NULL, NULL);
2921 g_return_val_if_fail(msgnum_list != NULL, NULL);
2923 session = imap_session_get(folder);
2924 g_return_val_if_fail(session != NULL, NULL);
2926 debug_print("IMAP getting msginfos\n");
2927 ok = imap_select(session, IMAP_FOLDER(folder), item->path,
2928 NULL, NULL, NULL, NULL, FALSE);
2929 if (ok != IMAP_SUCCESS)
2932 if (!(item->stype == F_QUEUE || item->stype == F_DRAFT)) {
2933 ret = g_slist_concat(ret,
2934 imap_get_uncached_messages(session, item,
2937 MsgNumberList *sorted_list, *elem;
2938 gint startnum, lastnum;
2940 sorted_list = g_slist_sort(g_slist_copy(msgnum_list), g_int_compare);
2942 startnum = lastnum = GPOINTER_TO_INT(sorted_list->data);
2944 for (elem = sorted_list;; elem = g_slist_next(elem)) {
2948 num = GPOINTER_TO_INT(elem->data);
2950 if (num > lastnum + 1 || elem == NULL) {
2952 for (i = startnum; i <= lastnum; ++i) {
2955 file = imap_fetch_msg(folder, item, i);
2957 MsgInfo *msginfo = imap_parse_msg(file, item);
2958 if (msginfo != NULL) {
2959 msginfo->msgnum = i;
2960 ret = g_slist_append(ret, msginfo);
2974 g_slist_free(sorted_list);
2980 MsgInfo *imap_get_msginfo(Folder *folder, FolderItem *item, gint uid)
2982 MsgInfo *msginfo = NULL;
2983 MsgInfoList *msginfolist;
2984 MsgNumberList numlist;
2986 numlist.next = NULL;
2987 numlist.data = GINT_TO_POINTER(uid);
2989 msginfolist = imap_get_msginfos(folder, item, &numlist);
2990 if (msginfolist != NULL) {
2991 msginfo = msginfolist->data;
2992 g_slist_free(msginfolist);
2998 gboolean imap_scan_required(Folder *folder, FolderItem *_item)
3000 IMAPSession *session;
3001 IMAPFolderItem *item = (IMAPFolderItem *)_item;
3002 gint ok, exists = 0, recent = 0, unseen = 0;
3003 guint32 uid_next, uid_val = 0;
3004 gboolean selected_folder;
3006 g_return_val_if_fail(folder != NULL, FALSE);
3007 g_return_val_if_fail(item != NULL, FALSE);
3008 g_return_val_if_fail(item->item.folder != NULL, FALSE);
3009 g_return_val_if_fail(FOLDER_CLASS(item->item.folder) == &imap_class, FALSE);
3011 if (item->item.path == NULL)
3014 session = imap_session_get(folder);
3015 g_return_val_if_fail(session != NULL, FALSE);
3017 selected_folder = (session->mbox != NULL) &&
3018 (!strcmp(session->mbox, item->item.path));
3019 if (selected_folder) {
3020 ok = imap_cmd_noop(session);
3021 if (ok != IMAP_SUCCESS)
3024 if (session->folder_content_changed
3025 || session->exists != item->item.total_msgs)
3028 ok = imap_status(session, IMAP_FOLDER(folder), item->item.path,
3029 &exists, &recent, &uid_next, &uid_val, &unseen, FALSE);
3030 if (ok != IMAP_SUCCESS)
3033 if ((uid_next != item->uid_next) || (exists != item->item.total_msgs))
3040 void imap_change_flags(Folder *folder, FolderItem *item, MsgInfo *msginfo, MsgPermFlags newflags)
3042 IMAPSession *session;
3043 IMAPFlags flags_set = 0, flags_unset = 0;
3044 gint ok = IMAP_SUCCESS;
3045 MsgNumberList numlist;
3046 hashtable_data *ht_data = NULL;
3048 g_return_if_fail(folder != NULL);
3049 g_return_if_fail(folder->klass == &imap_class);
3050 g_return_if_fail(item != NULL);
3051 g_return_if_fail(item->folder == folder);
3052 g_return_if_fail(msginfo != NULL);
3053 g_return_if_fail(msginfo->folder == item);
3055 session = imap_session_get(folder);
3059 if ((ok = imap_select(session, IMAP_FOLDER(folder), msginfo->folder->path,
3060 NULL, NULL, NULL, NULL, FALSE)) != IMAP_SUCCESS) {
3064 if (!MSG_IS_MARKED(msginfo->flags) && (newflags & MSG_MARKED))
3065 flags_set |= IMAP_FLAG_FLAGGED;
3066 if ( MSG_IS_MARKED(msginfo->flags) && !(newflags & MSG_MARKED))
3067 flags_unset |= IMAP_FLAG_FLAGGED;
3069 if (!MSG_IS_UNREAD(msginfo->flags) && (newflags & MSG_UNREAD))
3070 flags_unset |= IMAP_FLAG_SEEN;
3071 if ( MSG_IS_UNREAD(msginfo->flags) && !(newflags & MSG_UNREAD))
3072 flags_set |= IMAP_FLAG_SEEN;
3074 if (!MSG_IS_REPLIED(msginfo->flags) && (newflags & MSG_REPLIED))
3075 flags_set |= IMAP_FLAG_ANSWERED;
3076 if ( MSG_IS_REPLIED(msginfo->flags) && !(newflags & MSG_REPLIED))
3077 flags_set |= IMAP_FLAG_ANSWERED;
3079 numlist.next = NULL;
3080 numlist.data = GINT_TO_POINTER(msginfo->msgnum);
3082 if (IMAP_FOLDER_ITEM(item)->batching) {
3083 /* instead of performing an UID STORE command for each message change,
3084 * as a lot of them can change "together", we just fill in hashtables
3085 * and defer the treatment so that we're able to send only one
3088 debug_print("IMAP batch mode on, deferring flags change\n");
3090 ht_data = g_hash_table_lookup(flags_set_table, GINT_TO_POINTER(flags_set));
3091 if (ht_data == NULL) {
3092 ht_data = g_new0(hashtable_data, 1);
3093 ht_data->session = session;
3094 g_hash_table_insert(flags_set_table, GINT_TO_POINTER(flags_set), ht_data);
3096 if (!g_slist_find(ht_data->msglist, GINT_TO_POINTER(msginfo->msgnum)))
3097 ht_data->msglist = g_slist_prepend(ht_data->msglist, GINT_TO_POINTER(msginfo->msgnum));
3100 ht_data = g_hash_table_lookup(flags_unset_table, GINT_TO_POINTER(flags_unset));
3101 if (ht_data == NULL) {
3102 ht_data = g_new0(hashtable_data, 1);
3103 ht_data->session = session;
3104 g_hash_table_insert(flags_unset_table, GINT_TO_POINTER(flags_unset), ht_data);
3106 if (!g_slist_find(ht_data->msglist, GINT_TO_POINTER(msginfo->msgnum)))
3107 ht_data->msglist = g_slist_prepend(ht_data->msglist, GINT_TO_POINTER(msginfo->msgnum));
3110 debug_print("IMAP changing flags\n");
3112 ok = imap_set_message_flags(session, &numlist, flags_set, TRUE);
3113 if (ok != IMAP_SUCCESS) {
3119 ok = imap_set_message_flags(session, &numlist, flags_unset, FALSE);
3120 if (ok != IMAP_SUCCESS) {
3125 msginfo->flags.perm_flags = newflags;
3130 static gint imap_remove_msg(Folder *folder, FolderItem *item, gint uid)
3133 IMAPSession *session;
3135 MsgNumberList numlist;
3137 g_return_val_if_fail(folder != NULL, -1);
3138 g_return_val_if_fail(FOLDER_CLASS(folder) == &imap_class, -1);
3139 g_return_val_if_fail(item != NULL, -1);
3141 session = imap_session_get(folder);
3142 if (!session) return -1;
3144 ok = imap_select(session, IMAP_FOLDER(folder), item->path,
3145 NULL, NULL, NULL, NULL, FALSE);
3146 if (ok != IMAP_SUCCESS)
3149 numlist.next = NULL;
3150 numlist.data = GINT_TO_POINTER(uid);
3152 ok = imap_set_message_flags
3153 (IMAP_SESSION(REMOTE_FOLDER(folder)->session),
3154 &numlist, IMAP_FLAG_DELETED, TRUE);
3155 if (ok != IMAP_SUCCESS) {
3156 log_warning(_("can't set deleted flags: %d\n"), uid);
3160 if (!session->uidplus) {
3161 ok = imap_cmd_expunge(session);
3165 uidstr = g_strdup_printf("%u", uid);
3166 ok = imap_cmd_expunge(session);
3169 if (ok != IMAP_SUCCESS) {
3170 log_warning(_("can't expunge\n"));
3174 IMAP_FOLDER_ITEM(item)->uid_list = g_slist_remove(
3175 IMAP_FOLDER_ITEM(item)->uid_list, numlist.data);
3176 dir = folder_item_get_path(item);
3177 if (is_dir_exist(dir))
3178 remove_numbered_files(dir, uid, uid);
3181 return IMAP_SUCCESS;
3184 static gint compare_msginfo(gconstpointer a, gconstpointer b)
3186 return ((MsgInfo *)a)->msgnum - ((MsgInfo *)b)->msgnum;
3189 static guint gslist_find_next_num(MsgNumberList **list, guint num)
3193 g_return_val_if_fail(list != NULL, -1);
3195 for (elem = *list; elem != NULL; elem = g_slist_next(elem))
3196 if (GPOINTER_TO_INT(elem->data) >= num)
3199 return elem != NULL ? GPOINTER_TO_INT(elem->data) : (gint)-1;
3203 * NEW and DELETED flags are not syncronized
3204 * - The NEW/RECENT flags in IMAP folders can not really be directly
3205 * modified by Sylpheed
3206 * - The DELETE/DELETED flag in IMAP and Sylpheed don't have the same
3207 * meaning, in IMAP it always removes the messages from the FolderItem
3208 * in Sylpheed it can mean to move the message to trash
3211 typedef struct _get_flags_data {
3214 MsgInfoList *msginfo_list;
3215 GRelation *msgflags;
3219 static /*gint*/ void *imap_get_flags_thread(void *data)
3221 get_flags_data *stuff = (get_flags_data *)data;
3222 Folder *folder = stuff->folder;
3223 FolderItem *item = stuff->item;
3224 MsgInfoList *msginfo_list = stuff->msginfo_list;
3225 GRelation *msgflags = stuff->msgflags;
3226 IMAPSession *session;
3227 GSList *sorted_list;
3228 GSList *unseen = NULL, *answered = NULL, *flagged = NULL;
3229 GSList *p_unseen, *p_answered, *p_flagged;
3231 GSList *seq_list, *cur;
3232 gboolean reverse_seen = FALSE;
3235 gint exists_cnt, recent_cnt, unseen_cnt, uid_next;
3236 guint32 uidvalidity;
3237 gboolean selected_folder;
3239 if (folder == NULL || item == NULL) {
3241 return GINT_TO_POINTER(-1);
3243 if (msginfo_list == NULL) {
3245 return GINT_TO_POINTER(0);
3248 session = imap_session_get(folder);
3249 if (session == NULL) {
3251 return GINT_TO_POINTER(-1);
3254 selected_folder = (session->mbox != NULL) &&
3255 (!strcmp(session->mbox, item->path));
3257 if (!selected_folder) {
3258 ok = imap_status(session, IMAP_FOLDER(folder), item->path,
3259 &exists_cnt, &recent_cnt, &uid_next, &uidvalidity, &unseen_cnt, TRUE);
3260 ok = imap_select(session, IMAP_FOLDER(folder), item->path,
3261 NULL, NULL, NULL, NULL, TRUE);
3262 if (ok != IMAP_SUCCESS) {
3264 return GINT_TO_POINTER(-1);
3267 if (unseen_cnt > exists_cnt / 2)
3268 reverse_seen = TRUE;
3271 if (item->unread_msgs > item->total_msgs / 2)
3272 reverse_seen = TRUE;
3275 cmd_buf = g_string_new(NULL);
3277 sorted_list = g_slist_sort(g_slist_copy(msginfo_list), compare_msginfo);
3279 seq_list = imap_get_lep_set_from_msglist(msginfo_list);
3281 for (cur = seq_list; cur != NULL; cur = g_slist_next(cur)) {
3282 struct mailimap_set * imapset;
3283 clist * lep_uidlist;
3286 imapset = cur->data;
3288 r = imap_threaded_search(folder, IMAP_SEARCH_TYPE_SEEN,
3289 imapset, &lep_uidlist);
3292 r = imap_threaded_search(folder,
3293 IMAP_SEARCH_TYPE_UNSEEN,
3294 imapset, &lep_uidlist);
3296 if (r == MAILIMAP_NO_ERROR) {
3299 uidlist = imap_uid_list_from_lep(lep_uidlist);
3300 mailimap_search_result_free(lep_uidlist);
3302 unseen = g_slist_concat(unseen, uidlist);
3305 r = imap_threaded_search(folder, IMAP_SEARCH_TYPE_ANSWERED,
3306 imapset, &lep_uidlist);
3307 if (r == MAILIMAP_NO_ERROR) {
3310 uidlist = imap_uid_list_from_lep(lep_uidlist);
3311 mailimap_search_result_free(lep_uidlist);
3313 answered = g_slist_concat(answered, uidlist);
3316 r = imap_threaded_search(folder, IMAP_SEARCH_TYPE_FLAGGED,
3317 imapset, &lep_uidlist);
3318 if (r == MAILIMAP_NO_ERROR) {
3321 uidlist = imap_uid_list_from_lep(lep_uidlist);
3322 mailimap_search_result_free(lep_uidlist);
3324 flagged = g_slist_concat(flagged, uidlist);
3329 p_answered = answered;
3330 p_flagged = flagged;
3332 for (elem = sorted_list; elem != NULL; elem = g_slist_next(elem)) {
3337 msginfo = (MsgInfo *) elem->data;
3338 flags = msginfo->flags.perm_flags;
3339 wasnew = (flags & MSG_NEW);
3340 flags &= ~((reverse_seen ? 0 : MSG_UNREAD | MSG_NEW) | MSG_REPLIED | MSG_MARKED);
3342 flags |= MSG_UNREAD | (wasnew ? MSG_NEW : 0);
3343 if (gslist_find_next_num(&p_unseen, msginfo->msgnum) == msginfo->msgnum) {
3344 if (!reverse_seen) {
3345 flags |= MSG_UNREAD | (wasnew ? MSG_NEW : 0);
3347 flags &= ~(MSG_UNREAD | MSG_NEW);
3350 if (gslist_find_next_num(&p_answered, msginfo->msgnum) == msginfo->msgnum)
3351 flags |= MSG_REPLIED;
3352 if (gslist_find_next_num(&p_flagged, msginfo->msgnum) == msginfo->msgnum)
3353 flags |= MSG_MARKED;
3354 g_relation_insert(msgflags, msginfo, GINT_TO_POINTER(flags));
3357 imap_lep_set_free(seq_list);
3358 g_slist_free(flagged);
3359 g_slist_free(answered);
3360 g_slist_free(unseen);
3361 g_slist_free(sorted_list);
3362 g_string_free(cmd_buf, TRUE);
3365 return GINT_TO_POINTER(0);
3368 static gint imap_get_flags(Folder *folder, FolderItem *item,
3369 MsgInfoList *msginfo_list, GRelation *msgflags)
3372 get_flags_data *data = g_new0(get_flags_data, 1);
3374 data->folder = folder;
3376 data->msginfo_list = msginfo_list;
3377 data->msgflags = msgflags;
3379 if (prefs_common.work_offline && !imap_gtk_should_override()) {
3384 result = GPOINTER_TO_INT(imap_get_flags_thread(data));
3391 static gboolean process_flags(gpointer key, gpointer value, gpointer user_data)
3393 gboolean flags_set = GPOINTER_TO_INT(user_data);
3394 gint flags_value = GPOINTER_TO_INT(key);
3395 hashtable_data *data = (hashtable_data *)value;
3397 data->msglist = g_slist_reverse(data->msglist);
3399 debug_print("IMAP %ssetting flags to %d for %d messages\n",
3402 g_slist_length(data->msglist));
3403 imap_set_message_flags(data->session, data->msglist, flags_value, flags_set);
3405 g_slist_free(data->msglist);
3410 static void process_hashtable(void)
3412 if (flags_set_table) {
3413 g_hash_table_foreach_remove(flags_set_table, process_flags, GINT_TO_POINTER(TRUE));
3414 g_free(flags_set_table);
3415 flags_set_table = NULL;
3417 if (flags_unset_table) {
3418 g_hash_table_foreach_remove(flags_unset_table, process_flags, GINT_TO_POINTER(FALSE));
3419 g_free(flags_unset_table);
3420 flags_unset_table = NULL;
3424 static IMAPFolderItem *batching_item = NULL;
3426 static void imap_set_batch (Folder *folder, FolderItem *_item, gboolean batch)
3428 IMAPFolderItem *item = (IMAPFolderItem *)_item;
3430 g_return_if_fail(item != NULL);
3432 if (batch && batching_item != NULL) {
3433 g_warning("already batching on %s\n", batching_item->item.path);
3437 if (item->batching == batch)
3440 item->batching = batch;
3442 batching_item = batch?item:NULL;
3445 debug_print("IMAP switching to batch mode\n");
3446 if (flags_set_table) {
3447 g_warning("flags_set_table non-null but we just entered batch mode!\n");
3448 flags_set_table = NULL;
3450 if (flags_unset_table) {
3451 g_warning("flags_unset_table non-null but we just entered batch mode!\n");
3452 flags_unset_table = NULL;
3454 flags_set_table = g_hash_table_new(NULL, g_direct_equal);
3455 flags_unset_table = g_hash_table_new(NULL, g_direct_equal);
3457 debug_print("IMAP switching away from batch mode\n");
3459 process_hashtable();
3465 /* data types conversion libetpan <-> sylpheed */
3469 #define ETPAN_IMAP_MB_MARKED 1
3470 #define ETPAN_IMAP_MB_UNMARKED 2
3471 #define ETPAN_IMAP_MB_NOSELECT 4
3472 #define ETPAN_IMAP_MB_NOINFERIORS 8
3474 static int imap_flags_to_flags(struct mailimap_mbx_list_flags * imap_flags)
3480 if (imap_flags->mbf_type == MAILIMAP_MBX_LIST_FLAGS_SFLAG) {
3481 switch (imap_flags->mbf_sflag) {
3482 case MAILIMAP_MBX_LIST_SFLAG_MARKED:
3483 flags |= ETPAN_IMAP_MB_MARKED;
3485 case MAILIMAP_MBX_LIST_SFLAG_NOSELECT:
3486 flags |= ETPAN_IMAP_MB_NOSELECT;
3488 case MAILIMAP_MBX_LIST_SFLAG_UNMARKED:
3489 flags |= ETPAN_IMAP_MB_UNMARKED;
3494 for(cur = clist_begin(imap_flags->mbf_oflags) ; cur != NULL ;
3495 cur = clist_next(cur)) {
3496 struct mailimap_mbx_list_oflag * oflag;
3498 oflag = clist_content(cur);
3500 switch (oflag->of_type) {
3501 case MAILIMAP_MBX_LIST_OFLAG_NOINFERIORS:
3502 flags |= ETPAN_IMAP_MB_NOINFERIORS;
3510 static GSList * imap_list_from_lep(IMAPFolder * folder,
3511 clist * list, const gchar * real_path)
3518 for(iter = clist_begin(list) ; iter != NULL ;
3519 iter = clist_next(iter)) {
3520 struct mailimap_mailbox_list * mb;
3528 FolderItem *new_item;
3530 mb = clist_content(iter);
3533 if (mb->mb_flag != NULL)
3534 flags = imap_flags_to_flags(mb->mb_flag);
3536 delimiter = mb->mb_delimiter;
3539 dup_name = strdup(name);
3540 if (delimiter != '\0')
3541 subst_char(dup_name, delimiter, '/');
3543 base = g_path_get_basename(dup_name);
3544 if (base[0] == '.') {
3550 if (strcmp(dup_name, real_path) == 0) {
3556 loc_name = imap_modified_utf7_to_utf8(base);
3557 loc_path = imap_modified_utf7_to_utf8(dup_name);
3559 new_item = folder_item_new(FOLDER(folder), loc_name, loc_path);
3560 if ((flags & ETPAN_IMAP_MB_NOINFERIORS) != 0)
3561 new_item->no_sub = TRUE;
3562 if (strcmp(dup_name, "INBOX") != 0 &&
3563 ((flags & ETPAN_IMAP_MB_NOSELECT) != 0))
3564 new_item->no_select = TRUE;
3566 item_list = g_slist_append(item_list, new_item);
3568 debug_print("folder '%s' found.\n", loc_path);
3579 static GSList * imap_get_lep_set_from_numlist(MsgNumberList *numlist)
3581 GSList *sorted_list, *cur;
3582 guint first, last, next;
3583 GSList *ret_list = NULL;
3585 struct mailimap_set * current_set;
3586 unsigned int item_count;
3588 if (numlist == NULL)
3592 current_set = mailimap_set_new_empty();
3594 sorted_list = g_slist_copy(numlist);
3595 sorted_list = g_slist_sort(sorted_list, g_int_compare);
3597 first = GPOINTER_TO_INT(sorted_list->data);
3600 for (cur = sorted_list; cur != NULL; cur = g_slist_next(cur)) {
3601 if (GPOINTER_TO_INT(cur->data) == 0)
3606 last = GPOINTER_TO_INT(cur->data);
3608 next = GPOINTER_TO_INT(cur->next->data);
3612 if (last + 1 != next || next == 0) {
3614 struct mailimap_set_item * item;
3615 item = mailimap_set_item_new(first, last);
3616 mailimap_set_add(current_set, item);
3621 if (count >= IMAP_SET_MAX_COUNT) {
3622 ret_list = g_slist_append(ret_list,
3624 current_set = mailimap_set_new_empty();
3631 if (clist_count(current_set->set_list) > 0) {
3632 ret_list = g_slist_append(ret_list,
3636 g_slist_free(sorted_list);
3641 static GSList * imap_get_lep_set_from_msglist(MsgInfoList *msglist)
3643 MsgNumberList *numlist = NULL;
3647 for (cur = msglist; cur != NULL; cur = g_slist_next(cur)) {
3648 MsgInfo *msginfo = (MsgInfo *) cur->data;
3650 numlist = g_slist_append(numlist, GINT_TO_POINTER(msginfo->msgnum));
3652 seq_list = imap_get_lep_set_from_numlist(numlist);
3653 g_slist_free(numlist);
3658 static GSList * imap_uid_list_from_lep(clist * list)
3665 for(iter = clist_begin(list) ; iter != NULL ;
3666 iter = clist_next(iter)) {
3669 puid = clist_content(iter);
3670 result = g_slist_append(result, GINT_TO_POINTER(* puid));
3676 static GSList * imap_uid_list_from_lep_tab(carray * list)
3683 for(i = 0 ; i < carray_count(list) ; i ++) {
3686 puid = carray_get(list, i);
3687 result = g_slist_append(result, GINT_TO_POINTER(* puid));
3693 static MsgInfo *imap_envelope_from_lep(struct imap_fetch_env_info * info,
3696 MsgInfo *msginfo = NULL;
3699 MsgFlags flags = {0, 0};
3701 MSG_SET_TMP_FLAGS(flags, MSG_IMAP);
3702 if (item->stype == F_QUEUE) {
3703 MSG_SET_TMP_FLAGS(flags, MSG_QUEUED);
3704 } else if (item->stype == F_DRAFT) {
3705 MSG_SET_TMP_FLAGS(flags, MSG_DRAFT);
3707 flags.perm_flags = info->flags;
3711 msginfo = procheader_parse_str(info->headers, flags, FALSE, FALSE);
3714 msginfo->msgnum = uid;
3715 msginfo->size = size;
3721 static void imap_lep_set_free(GSList *seq_list)
3725 for(cur = seq_list ; cur != NULL ; cur = g_slist_next(cur)) {
3726 struct mailimap_set * imapset;
3728 imapset = cur->data;
3729 mailimap_set_free(imapset);
3731 g_slist_free(seq_list);
3734 static struct mailimap_flag_list * imap_flag_to_lep(IMAPFlags flags)
3736 struct mailimap_flag_list * flag_list;
3738 flag_list = mailimap_flag_list_new_empty();
3740 if (IMAP_IS_SEEN(flags))
3741 mailimap_flag_list_add(flag_list,
3742 mailimap_flag_new_seen());
3743 if (IMAP_IS_ANSWERED(flags))
3744 mailimap_flag_list_add(flag_list,
3745 mailimap_flag_new_answered());
3746 if (IMAP_IS_FLAGGED(flags))
3747 mailimap_flag_list_add(flag_list,
3748 mailimap_flag_new_flagged());
3749 if (IMAP_IS_DELETED(flags))
3750 mailimap_flag_list_add(flag_list,
3751 mailimap_flag_new_deleted());
3752 if (IMAP_IS_DRAFT(flags))
3753 mailimap_flag_list_add(flag_list,
3754 mailimap_flag_new_draft());
3758 #else /* HAVE_LIBETPAN */
3760 static FolderClass imap_class;
3762 static Folder *imap_folder_new (const gchar *name,
3767 static gint imap_create_tree (Folder *folder)
3771 static FolderItem *imap_create_folder (Folder *folder,
3777 static gint imap_rename_folder (Folder *folder,
3784 FolderClass *imap_get_class(void)
3786 if (imap_class.idstr == NULL) {
3787 imap_class.type = F_IMAP;
3788 imap_class.idstr = "imap";
3789 imap_class.uistr = "IMAP4";
3791 imap_class.new_folder = imap_folder_new;
3792 imap_class.create_tree = imap_create_tree;
3793 imap_class.create_folder = imap_create_folder;
3794 imap_class.rename_folder = imap_rename_folder;
3795 /* nothing implemented */