2 * Sylpheed -- a GTK+ based, lightweight, and fast e-mail client
3 * Copyright (C) 1999-2012 Hiroyuki Yamamoto and the Claws Mail team
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 3 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, see <http://www.gnu.org/licenses/>.
22 #include "claws-features.h"
28 #include <glib/gi18n.h>
35 #include "alertpanel.h"
58 #include "procheader.h"
59 #include "prefs_account.h"
64 #include "prefs_common.h"
65 #include "inputdialog.h"
67 #include "remotefolder.h"
69 #include "statusbar.h"
71 #include "imap-thread.h"
76 typedef struct _IMAPFolder IMAPFolder;
77 typedef struct _IMAPSession IMAPSession;
78 typedef struct _IMAPNameSpace IMAPNameSpace;
79 typedef struct _IMAPFolderItem IMAPFolderItem;
81 #include "prefs_account.h"
83 #define IMAP_FOLDER(obj) ((IMAPFolder *)obj)
84 #define IMAP_FOLDER_ITEM(obj) ((IMAPFolderItem *)obj)
85 #define IMAP_SESSION(obj) ((IMAPSession *)obj)
91 /* list of IMAPNameSpace */
95 gchar last_seen_separator;
104 gboolean authenticated;
113 gboolean folder_content_changed;
124 gboolean sens_update_block;
127 struct _IMAPNameSpace
133 #define IMAPBUFSIZE 8192
135 #define IMAP_IS_SEEN(flags) ((flags & IMAP_FLAG_SEEN) != 0)
136 #define IMAP_IS_ANSWERED(flags) ((flags & IMAP_FLAG_ANSWERED) != 0)
137 #define IMAP_IS_FLAGGED(flags) ((flags & IMAP_FLAG_FLAGGED) != 0)
138 #define IMAP_IS_DELETED(flags) ((flags & IMAP_FLAG_DELETED) != 0)
139 #define IMAP_IS_DRAFT(flags) ((flags & IMAP_FLAG_DRAFT) != 0)
140 #define IMAP_IS_FORWARDED(flags) ((flags & IMAP_FLAG_FORWARDED) != 0)
141 #define IMAP_IS_SPAM(flags) ((flags & IMAP_FLAG_SPAM) != 0)
142 #define IMAP_IS_HAM(flags) ((flags & IMAP_FLAG_HAM) != 0)
145 #define IMAP4_PORT 143
147 #define IMAPS_PORT 993
150 #define IMAP_CMD_LIMIT 1000
153 ITEM_CAN_CREATE_FLAGS_UNKNOWN = 0,
154 ITEM_CAN_CREATE_FLAGS,
155 ITEM_CANNOT_CREATE_FLAGS
158 struct _IMAPFolderItem
167 GHashTable *flags_set_table;
168 GHashTable *flags_unset_table;
171 gboolean should_update;
172 gboolean should_trash_cache;
173 gint can_create_flags;
175 GHashTable *tags_set_table;
176 GHashTable *tags_unset_table;
181 static XMLTag *imap_item_get_xml(Folder *folder, FolderItem *item);
182 static void imap_item_set_xml(Folder *folder, FolderItem *item, XMLTag *tag);
184 static void imap_folder_init (Folder *folder,
188 static Folder *imap_folder_new (const gchar *name,
190 static void imap_folder_destroy (Folder *folder);
192 static IMAPSession *imap_session_new (Folder *folder,
193 const PrefsAccount *account);
194 static gint imap_session_authenticate(IMAPSession *session,
195 PrefsAccount *account);
196 static void imap_session_destroy (Session *session);
198 static gchar *imap_fetch_msg (Folder *folder,
201 static gchar *imap_fetch_msg_full (Folder *folder,
206 static void imap_remove_cached_msg (Folder *folder,
209 static gint imap_add_msg (Folder *folder,
213 static gint imap_add_msgs (Folder *folder,
216 GHashTable *relation);
218 static gint imap_copy_msg (Folder *folder,
221 static gint imap_copy_msgs (Folder *folder,
223 MsgInfoList *msglist,
224 GHashTable *relation);
226 static gint search_msgs (Folder *folder,
227 FolderItem *container,
228 MsgNumberList **msgs,
230 MatcherList *predicate,
231 SearchProgressNotify progress_cb,
232 gpointer progress_data);
234 static gint imap_remove_msg (Folder *folder,
237 static gint imap_remove_msgs (Folder *folder,
239 MsgInfoList *msglist,
240 GHashTable *relation);
241 static gint imap_expunge (Folder *folder,
243 static gint imap_remove_all_msg (Folder *folder,
246 static gboolean imap_is_msg_changed (Folder *folder,
250 static gint imap_close (Folder *folder,
253 static gint imap_scan_tree (Folder *folder);
255 static gint imap_create_tree (Folder *folder);
257 static FolderItem *imap_create_folder (Folder *folder,
260 static gint imap_rename_folder (Folder *folder,
263 static gint imap_remove_folder (Folder *folder,
266 static FolderItem *imap_folder_item_new (Folder *folder);
267 static void imap_folder_item_destroy (Folder *folder,
270 static IMAPSession *imap_session_get (Folder *folder);
272 static gint imap_auth (IMAPSession *session,
277 static gint imap_scan_tree_recursive (IMAPSession *session,
281 static void imap_create_missing_folders (Folder *folder);
282 static FolderItem *imap_create_special_folder
284 SpecialFolderItemType stype,
287 static gint imap_do_copy_msgs (Folder *folder,
289 MsgInfoList *msglist,
290 GHashTable *relation);
292 static void imap_delete_all_cached_messages (FolderItem *item);
293 static void imap_set_batch (Folder *folder,
296 static gint imap_set_message_flags (IMAPSession *session,
297 IMAPFolderItem *item,
298 MsgNumberList *numlist,
302 static gint imap_select (IMAPSession *session,
308 guint32 *uid_validity,
309 gint *can_create_flags,
311 static gint imap_status (IMAPSession *session,
314 IMAPFolderItem *item,
317 guint32 *uid_validity,
320 static void imap_commit_tags (FolderItem *item,
325 static gchar imap_get_path_separator (IMAPSession *session,
329 static gchar *imap_get_real_path (IMAPSession *session,
334 static void imap_synchronise (FolderItem *item, gint days);
336 static gboolean imap_is_busy (Folder *folder);
338 static void imap_free_capabilities (IMAPSession *session);
340 /* low-level IMAP4rev1 commands */
341 static gint imap_cmd_login (IMAPSession *session,
345 static gint imap_cmd_noop (IMAPSession *session);
347 static gint imap_cmd_starttls (IMAPSession *session);
349 static gint imap_cmd_select (IMAPSession *session,
354 guint32 *uid_validity,
355 gint *can_create_flags,
358 static gint imap_cmd_close (IMAPSession *session);
359 static gint imap_cmd_examine (IMAPSession *session,
364 guint32 *uid_validity,
366 static gint imap_cmd_create (IMAPSession *sock,
367 const gchar *folder);
368 static gint imap_cmd_rename (IMAPSession *sock,
369 const gchar *oldfolder,
370 const gchar *newfolder);
371 static gint imap_cmd_delete (IMAPSession *session,
372 const gchar *folder);
373 static gint imap_cmd_fetch (IMAPSession *sock,
375 const gchar *filename,
378 static gint imap_cmd_append (IMAPSession *session,
379 IMAPFolderItem *item,
380 const gchar *destfolder,
384 static gint imap_cmd_copy (IMAPSession *session,
385 struct mailimap_set * set,
386 const gchar *destfolder,
387 struct mailimap_set ** source,
388 struct mailimap_set ** dest);
389 static gint imap_cmd_store (IMAPSession *session,
390 IMAPFolderItem *item,
391 struct mailimap_set * set,
395 static gint imap_cmd_expunge (IMAPSession *session, gboolean force);
397 static void imap_path_separator_subst (gchar *str,
400 static gboolean imap_rename_folder_func (GNode *node,
402 static gint imap_get_num_list (Folder *folder,
405 gboolean *old_uids_valid);
406 static GSList *imap_get_msginfos (Folder *folder,
408 GSList *msgnum_list);
409 static MsgInfo *imap_get_msginfo (Folder *folder,
412 static gboolean imap_scan_required (Folder *folder,
414 static void imap_change_flags (Folder *folder,
417 MsgPermFlags newflags);
418 static gint imap_get_flags (Folder *folder,
420 MsgInfoList *msglist,
421 GHashTable *msgflags);
422 static gchar *imap_folder_get_path (Folder *folder);
423 static gchar *imap_item_get_path (Folder *folder,
425 static MsgInfo *imap_parse_msg(const gchar *file, FolderItem *item);
428 /* data types conversion libetpan <-> claws */
429 static GSList * imap_list_from_lep(IMAPFolder * folder,
430 clist * list, const gchar * real_path, gboolean all);
431 static GSList * imap_get_lep_set_from_numlist(IMAPFolder *folder, MsgNumberList *numlist);
432 static GSList * imap_get_lep_set_from_msglist(IMAPFolder *folder, MsgInfoList *msglist);
433 static GSList * imap_uid_list_from_lep(clist * list, gint* length);
434 static GSList * imap_uid_list_from_lep_tab(carray * list);
435 static void imap_flags_hash_from_lep_uid_flags_tab(carray * list,
437 GHashTable *tags_hash);
438 static MsgInfo *imap_envelope_from_lep(struct imap_fetch_env_info * info,
440 static void imap_lep_set_free(GSList *seq_list);
441 static struct mailimap_flag_list * imap_flag_to_lep(IMAPFolderItem *item, IMAPFlags flags, GSList *tags);
443 typedef struct _hashtable_data {
445 IMAPFolderItem *item;
448 static FolderClass imap_class;
450 FolderClass *imap_get_class(void)
452 if (imap_class.idstr == NULL) {
453 imap_class.type = F_IMAP;
454 imap_class.idstr = "imap";
455 imap_class.uistr = "IMAP4";
456 imap_class.supports_server_search = TRUE;
458 /* Folder functions */
459 imap_class.new_folder = imap_folder_new;
460 imap_class.destroy_folder = imap_folder_destroy;
461 imap_class.scan_tree = imap_scan_tree;
462 imap_class.create_tree = imap_create_tree;
464 /* FolderItem functions */
465 imap_class.item_new = imap_folder_item_new;
466 imap_class.item_destroy = imap_folder_item_destroy;
467 imap_class.item_get_path = imap_item_get_path;
468 imap_class.create_folder = imap_create_folder;
469 imap_class.rename_folder = imap_rename_folder;
470 imap_class.remove_folder = imap_remove_folder;
471 imap_class.close = imap_close;
472 imap_class.get_num_list = imap_get_num_list;
473 imap_class.scan_required = imap_scan_required;
474 imap_class.set_xml = folder_set_xml;
475 imap_class.get_xml = folder_get_xml;
476 imap_class.item_set_xml = imap_item_set_xml;
477 imap_class.item_get_xml = imap_item_get_xml;
479 /* Message functions */
480 imap_class.get_msginfo = imap_get_msginfo;
481 imap_class.get_msginfos = imap_get_msginfos;
482 imap_class.fetch_msg = imap_fetch_msg;
483 imap_class.fetch_msg_full = imap_fetch_msg_full;
484 imap_class.add_msg = imap_add_msg;
485 imap_class.add_msgs = imap_add_msgs;
486 imap_class.copy_msg = imap_copy_msg;
487 imap_class.copy_msgs = imap_copy_msgs;
488 imap_class.search_msgs = search_msgs;
489 imap_class.remove_msg = imap_remove_msg;
490 imap_class.remove_msgs = imap_remove_msgs;
491 imap_class.expunge = imap_expunge;
492 imap_class.remove_all_msg = imap_remove_all_msg;
493 imap_class.is_msg_changed = imap_is_msg_changed;
494 imap_class.change_flags = imap_change_flags;
495 imap_class.get_flags = imap_get_flags;
496 imap_class.set_batch = imap_set_batch;
497 imap_class.synchronise = imap_synchronise;
498 imap_class.remove_cached_msg = imap_remove_cached_msg;
499 imap_class.commit_tags = imap_commit_tags;
501 pthread_mutex_init(&imap_mutex, NULL);
508 static void imap_refresh_sensitivity (IMAPSession *session)
512 if (session->sens_update_block)
514 mainwin = mainwindow_get_mainwindow();
516 toolbar_main_set_sensitive(mainwin);
517 main_window_set_menu_sensitive(mainwin);
521 static void lock_session(IMAPSession *session)
524 debug_print("locking session %p (%d)\n", session, session->busy);
526 debug_print(" SESSION WAS LOCKED !! \n");
527 session->busy = TRUE;
528 imap_refresh_sensitivity(session);
530 debug_print("can't lock null session\n");
534 static void unlock_session(IMAPSession *session)
537 debug_print("unlocking session %p\n", session);
538 session->busy = FALSE;
539 imap_refresh_sensitivity(session);
541 debug_print("can't unlock null session\n");
545 static void imap_disc_session_destroy(IMAPSession *session)
547 RemoteFolder *rfolder = NULL;
552 rfolder = REMOTE_FOLDER(IMAP_SESSION(session)->folder);
556 log_warning(LOG_PROTOCOL, _("IMAP4 connection broken\n"));
557 SESSION(session)->state = SESSION_DISCONNECTED;
558 SESSION(session)->sock = NULL;
561 static gboolean is_fatal(int libetpan_errcode)
563 switch(libetpan_errcode) {
564 case MAILIMAP_ERROR_STREAM:
565 case MAILIMAP_ERROR_PROTOCOL:
566 case MAILIMAP_ERROR_PARSE:
567 case MAILIMAP_ERROR_BAD_STATE:
574 static void imap_handle_error(Session *session, const gchar *server, int libetpan_errcode)
576 const gchar *session_server = (session ? session->server : NULL);
578 if (session_server == NULL)
579 session_server = server;
580 if (session_server == NULL)
581 session_server = "(null)";
583 switch(libetpan_errcode) {
584 case MAILIMAP_NO_ERROR:
586 case MAILIMAP_NO_ERROR_AUTHENTICATED:
587 log_warning(LOG_PROTOCOL, _("IMAP error on %s: authenticated\n"), session_server);
589 case MAILIMAP_NO_ERROR_NON_AUTHENTICATED:
590 log_warning(LOG_PROTOCOL, _("IMAP error on %s: not authenticated\n"), session_server);
592 case MAILIMAP_ERROR_BAD_STATE:
593 log_warning(LOG_PROTOCOL, _("IMAP error on %s: bad state\n"), session_server);
595 case MAILIMAP_ERROR_STREAM:
596 log_warning(LOG_PROTOCOL, _("IMAP error on %s: stream error\n"), session_server);
598 case MAILIMAP_ERROR_PARSE:
599 log_warning(LOG_PROTOCOL, _("IMAP error on %s: parse error "
600 "(very probably non-RFC compliance from the server)\n"), session_server);
602 case MAILIMAP_ERROR_CONNECTION_REFUSED:
603 log_warning(LOG_PROTOCOL, _("IMAP error on %s: connection refused\n"), session_server);
605 case MAILIMAP_ERROR_MEMORY:
606 log_warning(LOG_PROTOCOL, _("IMAP error on %s: memory error\n"), session_server);
608 case MAILIMAP_ERROR_FATAL:
609 log_warning(LOG_PROTOCOL, _("IMAP error on %s: fatal error\n"), session_server);
611 case MAILIMAP_ERROR_PROTOCOL:
612 log_warning(LOG_PROTOCOL, _("IMAP error on %s: protocol error"
613 "(very probably non-RFC compliance from the server)\n"), session_server);
615 case MAILIMAP_ERROR_DONT_ACCEPT_CONNECTION:
616 log_warning(LOG_PROTOCOL, _("IMAP error on %s: connection not accepted\n"), session_server);
618 case MAILIMAP_ERROR_APPEND:
619 log_warning(LOG_PROTOCOL, _("IMAP error on %s: APPEND error\n"), session_server);
621 case MAILIMAP_ERROR_NOOP:
622 log_warning(LOG_PROTOCOL, _("IMAP error on %s: NOOP error\n"), session_server);
624 case MAILIMAP_ERROR_LOGOUT:
625 log_warning(LOG_PROTOCOL, _("IMAP error on %s: LOGOUT error\n"), session_server);
627 case MAILIMAP_ERROR_CAPABILITY:
628 log_warning(LOG_PROTOCOL, _("IMAP error on %s: CAPABILITY error\n"), session_server);
630 case MAILIMAP_ERROR_CHECK:
631 log_warning(LOG_PROTOCOL, _("IMAP error on %s: CHECK error\n"), session_server);
633 case MAILIMAP_ERROR_CLOSE:
634 log_warning(LOG_PROTOCOL, _("IMAP error on %s: CLOSE error\n"), session_server);
636 case MAILIMAP_ERROR_EXPUNGE:
637 log_warning(LOG_PROTOCOL, _("IMAP error on %s: EXPUNGE error\n"), session_server);
639 case MAILIMAP_ERROR_COPY:
640 log_warning(LOG_PROTOCOL, _("IMAP error on %s: COPY error\n"), session_server);
642 case MAILIMAP_ERROR_UID_COPY:
643 log_warning(LOG_PROTOCOL, _("IMAP error on %s: UID COPY error\n"), session_server);
645 case MAILIMAP_ERROR_CREATE:
646 log_warning(LOG_PROTOCOL, _("IMAP error on %s: CREATE error\n"), session_server);
648 case MAILIMAP_ERROR_DELETE:
649 log_warning(LOG_PROTOCOL, _("IMAP error on %s: DELETE error\n"), session_server);
651 case MAILIMAP_ERROR_EXAMINE:
652 log_warning(LOG_PROTOCOL, _("IMAP error on %s: EXAMINE error\n"), session_server);
654 case MAILIMAP_ERROR_FETCH:
655 log_warning(LOG_PROTOCOL, _("IMAP error on %s: FETCH error\n"), session_server);
657 case MAILIMAP_ERROR_UID_FETCH:
658 log_warning(LOG_PROTOCOL, _("IMAP error on %s: UID FETCH error\n"), session_server);
660 case MAILIMAP_ERROR_LIST:
661 log_warning(LOG_PROTOCOL, _("IMAP error on %s: LIST error\n"), session_server);
663 case MAILIMAP_ERROR_LOGIN:
664 log_warning(LOG_PROTOCOL, _("IMAP error on %s: LOGIN error\n"), session_server);
666 case MAILIMAP_ERROR_LSUB:
667 log_warning(LOG_PROTOCOL, _("IMAP error on %s: LSUB error\n"), session_server);
669 case MAILIMAP_ERROR_RENAME:
670 log_warning(LOG_PROTOCOL, _("IMAP error on %s: RENAME error\n"), session_server);
672 case MAILIMAP_ERROR_SEARCH:
673 log_warning(LOG_PROTOCOL, _("IMAP error on %s: SEARCH error\n"), session_server);
675 case MAILIMAP_ERROR_UID_SEARCH:
676 log_warning(LOG_PROTOCOL, _("IMAP error on %s: UID SEARCH error\n"), session_server);
678 case MAILIMAP_ERROR_SELECT:
679 log_warning(LOG_PROTOCOL, _("IMAP error on %s: SELECT error\n"), session_server);
681 case MAILIMAP_ERROR_STATUS:
682 log_warning(LOG_PROTOCOL, _("IMAP error on %s: STATUS error\n"), session_server);
684 case MAILIMAP_ERROR_STORE:
685 log_warning(LOG_PROTOCOL, _("IMAP error on %s: STORE error\n"), session_server);
687 case MAILIMAP_ERROR_UID_STORE:
688 log_warning(LOG_PROTOCOL, _("IMAP error on %s: UID STORE error\n"), session_server);
690 case MAILIMAP_ERROR_SUBSCRIBE:
691 log_warning(LOG_PROTOCOL, _("IMAP error on %s: SUBSCRIBE error\n"), session_server);
693 case MAILIMAP_ERROR_UNSUBSCRIBE:
694 log_warning(LOG_PROTOCOL, _("IMAP error on %s: UNSUBSCRIBE error\n"), session_server);
696 case MAILIMAP_ERROR_STARTTLS:
697 log_warning(LOG_PROTOCOL, _("IMAP error on %s: STARTTLS error\n"), session_server);
699 case MAILIMAP_ERROR_INVAL:
700 log_warning(LOG_PROTOCOL, _("IMAP error on %s: INVAL error\n"), session_server);
702 case MAILIMAP_ERROR_EXTENSION:
703 log_warning(LOG_PROTOCOL, _("IMAP error on %s: EXTENSION error\n"), session_server);
705 case MAILIMAP_ERROR_SASL:
706 log_warning(LOG_PROTOCOL, _("IMAP error on %s: SASL error\n"), session_server);
709 case MAILIMAP_ERROR_SSL:
710 log_warning(LOG_PROTOCOL, _("IMAP error on %s: SSL error\n"), session_server);
714 log_warning(LOG_PROTOCOL, _("IMAP error on %s: Unknown error [%d]\n"),
715 session_server, libetpan_errcode);
719 if (session && is_fatal(libetpan_errcode)) {
720 imap_disc_session_destroy(IMAP_SESSION(session));
721 } else if (session && !is_fatal(libetpan_errcode)) {
722 if (IMAP_SESSION(session)->busy)
723 unlock_session(IMAP_SESSION(session));
727 static Folder *imap_folder_new(const gchar *name, const gchar *path)
731 folder = (Folder *)g_new0(IMAPFolder, 1);
732 folder->klass = &imap_class;
733 imap_folder_init(folder, name, path);
738 static void imap_folder_destroy(Folder *folder)
740 while (imap_folder_get_refcnt(folder) > 0)
741 gtk_main_iteration();
743 folder_remote_folder_destroy(REMOTE_FOLDER(folder));
747 static void imap_folder_init(Folder *folder, const gchar *name,
750 folder_remote_folder_init((Folder *)folder, name, path);
751 IMAP_FOLDER(folder)->max_set_size = IMAP_SET_MAX_COUNT;
754 static FolderItem *imap_folder_item_new(Folder *folder)
756 IMAPFolderItem *item;
758 item = g_new0(IMAPFolderItem, 1);
761 item->uid_list = NULL;
763 return (FolderItem *)item;
766 static void imap_folder_item_destroy(Folder *folder, FolderItem *_item)
768 IMAPFolderItem *item = (IMAPFolderItem *)_item;
770 g_return_if_fail(item != NULL);
771 g_slist_free(item->uid_list);
776 static gboolean imap_reset_uid_lists_func(GNode *node, gpointer data)
778 IMAPFolderItem *item = (IMAPFolderItem *)node->data;
781 g_slist_free(item->uid_list);
782 item->uid_list = NULL;
787 static void imap_reset_uid_lists(Folder *folder)
789 if(folder->node == NULL)
792 /* Destroy all uid lists and rest last uid */
793 g_node_traverse(folder->node, G_IN_ORDER, G_TRAVERSE_ALL, -1, imap_reset_uid_lists_func, NULL);
796 static int imap_get_capabilities(IMAPSession *session)
798 struct mailimap_capability_data *capabilities = NULL;
802 if (session->capability != NULL)
803 return MAILIMAP_NO_ERROR;
805 capabilities = imap_threaded_capability(session->folder, &result);
807 if (result != MAILIMAP_NO_ERROR) {
811 if (capabilities == NULL || capabilities->cap_list == NULL) {
812 return MAILIMAP_NO_ERROR;
815 for(cur = clist_begin(capabilities->cap_list) ; cur != NULL ;
816 cur = clist_next(cur)) {
817 struct mailimap_capability * cap =
819 if (!cap || cap->cap_data.cap_name == NULL)
821 session->capability = g_slist_append
822 (session->capability,
823 g_strdup(cap->cap_data.cap_name));
824 debug_print("got capa %s\n", cap->cap_data.cap_name);
826 mailimap_capability_data_free(capabilities);
827 return MAILIMAP_NO_ERROR;
830 static gboolean imap_has_capability(IMAPSession *session, const gchar *cap)
833 for (cur = session->capability; cur; cur = cur->next) {
834 if (!g_ascii_strcasecmp(cur->data, cap))
840 static gint imap_auth(IMAPSession *session, const gchar *user, const gchar *pass,
843 gint ok = MAILIMAP_ERROR_LOGIN;
844 static time_t last_login_err = 0;
845 gchar *ext_info = "";
847 gchar *server = NULL;
848 if ((r = imap_get_capabilities(session)) != MAILIMAP_NO_ERROR) {
849 imap_handle_error(SESSION(session), NULL, r);
852 server = g_strdup(SESSION(session)->server);
855 ok = imap_cmd_login(session, user, pass, "ANONYMOUS");
857 case IMAP_AUTH_CRAM_MD5:
858 ok = imap_cmd_login(session, user, pass, "CRAM-MD5");
860 case IMAP_AUTH_DIGEST_MD5:
861 ok = imap_cmd_login(session, user, pass, "DIGEST-MD5");
863 case IMAP_AUTH_LOGIN:
864 ok = imap_cmd_login(session, user, pass, "LOGIN");
866 case IMAP_AUTH_GSSAPI:
867 ok = imap_cmd_login(session, user, pass, "GSSAPI");
870 debug_print("capabilities:\n"
876 imap_has_capability(session, "ANONYMOUS"),
877 imap_has_capability(session, "CRAM-MD5"),
878 imap_has_capability(session, "DIGEST-MD5"),
879 imap_has_capability(session, "LOGIN"),
880 imap_has_capability(session, "GSSAPI"));
881 if (imap_has_capability(session, "CRAM-MD5"))
882 ok = imap_cmd_login(session, user, pass, "CRAM-MD5");
883 if (ok == MAILIMAP_ERROR_LOGIN && imap_has_capability(session, "DIGEST-MD5"))
884 ok = imap_cmd_login(session, user, pass, "DIGEST-MD5");
885 if (ok == MAILIMAP_ERROR_LOGIN && imap_has_capability(session, "GSSAPI"))
886 ok = imap_cmd_login(session, user, pass, "GSSAPI");
887 if (ok == MAILIMAP_ERROR_LOGIN) /* we always try LOGIN before giving up */
888 ok = imap_cmd_login(session, user, pass, "LOGIN");
891 if (ok == MAILIMAP_NO_ERROR)
892 session->authenticated = TRUE;
894 if (type == IMAP_AUTH_CRAM_MD5) {
895 ext_info = _("\n\nCRAM-MD5 logins only work if libetpan has been "
896 "compiled with SASL support and the "
897 "CRAM-MD5 SASL plugin is installed.");
900 if (type == IMAP_AUTH_DIGEST_MD5) {
901 ext_info = _("\n\nDIGEST-MD5 logins only work if libetpan has been "
902 "compiled with SASL support and the "
903 "DIGEST-MD5 SASL plugin is installed.");
906 if (time(NULL) - last_login_err > 10) {
907 if (!prefs_common.no_recv_err_panel) {
908 alertpanel_error_log(_("Connection to %s failed: "
912 log_error(LOG_PROTOCOL, _("Connection to %s failed: "
913 "login refused.%s\n"),
917 last_login_err = time(NULL);
923 static IMAPSession *imap_reconnect_if_possible(Folder *folder, IMAPSession *session)
925 RemoteFolder *rfolder = REMOTE_FOLDER(folder);
926 /* Check if this is the first try to establish a
927 connection, if yes we don't try to reconnect */
928 debug_print("reconnecting\n");
929 if (rfolder->session == NULL) {
930 log_warning(LOG_PROTOCOL, _("Connecting to %s failed"),
931 folder->account->recv_server);
932 SESSION(session)->sock = NULL;
933 session_destroy(SESSION(session));
936 rfolder->session = NULL;
937 log_warning(LOG_PROTOCOL, _("IMAP4 connection to %s has been"
938 " disconnected. Reconnecting...\n"),
939 folder->account->recv_server);
940 statusbar_print_all(_("IMAP4 connection to %s has been"
941 " disconnected. Reconnecting...\n"),
942 folder->account->recv_server);
943 SESSION(session)->state = SESSION_DISCONNECTED;
944 SESSION(session)->sock = NULL;
945 session_destroy(SESSION(session));
946 /* Clear folders session to make imap_session_get create
947 a new session, because of rfolder->session == NULL
948 it will not try to reconnect again and so avoid an
950 debug_print("getting session...\n");
951 session = imap_session_get(folder);
952 rfolder->session = SESSION(session);
958 static IMAPSession *imap_session_get(Folder *folder)
960 RemoteFolder *rfolder = REMOTE_FOLDER(folder);
961 IMAPSession *session = NULL;
962 gint r = MAILIMAP_NO_ERROR;
964 g_return_val_if_fail(folder != NULL, NULL);
965 g_return_val_if_fail(FOLDER_CLASS(folder) == &imap_class, NULL);
966 g_return_val_if_fail(folder->account != NULL, NULL);
968 if (prefs_common.work_offline &&
969 !inc_offline_should_override(FALSE,
970 _("Claws Mail needs network access in order "
971 "to access the IMAP server."))) {
975 /* Make sure we have a session */
976 if (rfolder->session != NULL && rfolder->session->state != SESSION_DISCONNECTED) {
977 session = IMAP_SESSION(rfolder->session);
978 } else if (rfolder->session != NULL && rfolder->session->state == SESSION_DISCONNECTED) {
979 session_destroy(SESSION(rfolder->session));
980 rfolder->session = NULL;
982 } else if (rfolder->connecting) {
983 debug_print("already connecting\n");
987 imap_reset_uid_lists(folder);
988 if (time(NULL) - rfolder->last_failure <= 2)
990 rfolder->connecting = TRUE;
991 session = imap_session_new(folder, folder->account);
993 if(session == NULL) {
994 rfolder->last_failure = time(NULL);
995 rfolder->connecting = FALSE;
999 /* Make sure session is authenticated */
1000 if (!IMAP_SESSION(session)->authenticated)
1001 r = imap_session_authenticate(IMAP_SESSION(session), folder->account);
1003 if (r != MAILIMAP_NO_ERROR || (!is_fatal(r) && !IMAP_SESSION(session)->authenticated)) {
1004 rfolder->session = NULL;
1006 imap_threaded_disconnect(session->folder);
1007 SESSION(session)->state = SESSION_DISCONNECTED;
1008 SESSION(session)->sock = NULL;
1009 session_destroy(SESSION(session));
1011 rfolder->last_failure = time(NULL);
1012 rfolder->connecting = FALSE;
1016 /* I think the point of this code is to avoid sending a
1017 * keepalive if we've used the session recently and therefore
1018 * think it's still alive. Unfortunately, most of the code
1019 * does not yet check for errors on the socket, and so if the
1020 * connection drops we don't notice until the timeout expires.
1021 * A better solution than sending a NOOP every time would be
1022 * for every command to be prepared to retry until it is
1023 * successfully sent. -- mbp */
1024 if ((time(NULL) - SESSION(session)->last_access_time > SESSION_TIMEOUT_INTERVAL) || session->cancelled) {
1025 /* verify that the session is still alive */
1026 if ((r = imap_cmd_noop(session)) != MAILIMAP_NO_ERROR) {
1027 debug_print("disconnected!\n");
1029 session = imap_reconnect_if_possible(folder, session);
1031 rfolder->session = NULL;
1032 rfolder->connecting = FALSE;
1033 session = imap_session_get(folder);
1037 session->cancelled = FALSE;
1040 rfolder->session = SESSION(session);
1041 rfolder->connecting = FALSE;
1043 return IMAP_SESSION(session);
1046 static IMAPSession *imap_session_new(Folder * folder,
1047 const PrefsAccount *account)
1049 IMAPSession *session;
1052 int authenticated = FALSE;
1056 /* FIXME: IMAP over SSL only... */
1059 port = account->set_imapport ? account->imapport
1060 : account->ssl_imap == SSL_TUNNEL ? IMAPS_PORT : IMAP4_PORT;
1061 ssl_type = account->ssl_imap;
1063 if (account->ssl_imap != SSL_NONE) {
1064 if (alertpanel_full(_("Insecure connection"),
1065 _("This connection is configured to be secured "
1066 "using SSL, but SSL is not available in this "
1067 "build of Claws Mail. \n\n"
1068 "Do you want to continue connecting to this "
1069 "server? The communication would not be "
1071 GTK_STOCK_CANCEL, _("Con_tinue connecting"),
1072 NULL, FALSE, NULL, ALERT_WARNING,
1073 G_ALERTDEFAULT) != G_ALERTALTERNATE)
1076 port = account->set_imapport ? account->imapport
1081 buf = g_strdup_printf(_("Account '%s': Connecting to IMAP4 server: %s:%d..."),
1082 folder->account->account_name, folder->account->recv_server,
1084 statuswindow_print_all("%s", buf);
1085 log_message(LOG_PROTOCOL, "%s\n", buf);
1089 if (account->set_tunnelcmd) {
1090 r = imap_threaded_connect_cmd(folder,
1092 account->recv_server,
1099 if (ssl_type == SSL_TUNNEL) {
1100 r = imap_threaded_connect_ssl(folder,
1101 account->recv_server,
1107 r = imap_threaded_connect(folder,
1108 account->recv_server,
1113 statuswindow_pop_all();
1114 if (r == MAILIMAP_NO_ERROR_AUTHENTICATED) {
1115 authenticated = TRUE;
1117 else if (r == MAILIMAP_NO_ERROR_NON_AUTHENTICATED) {
1118 authenticated = FALSE;
1122 if (r == MAILIMAP_ERROR_SSL)
1123 log_error(LOG_PROTOCOL, _("SSL handshake failed\n"));
1126 imap_handle_error(NULL, account->recv_server, r);
1128 if(!prefs_common.no_recv_err_panel) {
1129 alertpanel_error_log(_("Can't connect to IMAP4 server: %s:%d"),
1130 account->recv_server, port);
1132 log_error(LOG_PROTOCOL, _("Can't connect to IMAP4 server: %s:%d\n"),
1133 account->recv_server, port);
1139 session = g_new0(IMAPSession, 1);
1140 session_init(SESSION(session), account, FALSE);
1141 SESSION(session)->type = SESSION_IMAP;
1142 SESSION(session)->server = g_strdup(account->recv_server);
1143 SESSION(session)->port = port;
1144 SESSION(session)->sock = NULL;
1146 SESSION(session)->destroy = imap_session_destroy;
1148 session->capability = NULL;
1150 session->authenticated = authenticated;
1151 session->mbox = NULL;
1152 session->exists = 0;
1153 session->recent = 0;
1154 session->expunge = 0;
1155 session->cmd_count = 0;
1156 session->folder = folder;
1157 IMAP_FOLDER(session->folder)->last_seen_separator = 0;
1160 if (account->ssl_imap == SSL_STARTTLS) {
1163 ok = imap_cmd_starttls(session);
1164 if (ok != MAILIMAP_NO_ERROR) {
1165 log_warning(LOG_PROTOCOL, _("Can't start TLS session.\n"));
1166 if (!is_fatal(ok)) {
1167 SESSION(session)->sock = NULL;
1168 session_destroy(SESSION(session));
1173 imap_free_capabilities(session);
1174 session->authenticated = FALSE;
1175 session->uidplus = FALSE;
1176 session->cmd_count = 1;
1179 log_message(LOG_PROTOCOL, "IMAP connection is %s-authenticated\n",
1180 (session->authenticated) ? "pre" : "un");
1185 static gint imap_session_authenticate(IMAPSession *session,
1186 PrefsAccount *account)
1188 gchar *pass, *acc_pass;
1189 gboolean failed = FALSE;
1190 gint ok = MAILIMAP_NO_ERROR;
1191 g_return_val_if_fail(account->userid != NULL, MAILIMAP_ERROR_BAD_STATE);
1192 acc_pass = account->passwd;
1195 if (!pass && account->imap_auth_type != IMAP_AUTH_ANON && account->imap_auth_type != IMAP_AUTH_GSSAPI) {
1197 tmp_pass = input_dialog_query_password_keep(account->recv_server,
1199 &(account->session_passwd));
1201 return MAILIMAP_NO_ERROR;
1202 Xstrdup_a(pass, tmp_pass, {g_free(tmp_pass); return MAILIMAP_NO_ERROR;});
1204 } else if (account->imap_auth_type == IMAP_AUTH_ANON || account->imap_auth_type == IMAP_AUTH_GSSAPI) {
1207 if ((ok = imap_auth(session, account->userid, pass, account->imap_auth_type)) != MAILIMAP_NO_ERROR) {
1209 if (!failed && !is_fatal(ok)) {
1212 if (account->session_passwd != NULL) {
1213 g_free(account->session_passwd);
1214 account->session_passwd = NULL;
1218 if (prefs_common.no_recv_err_panel) {
1219 log_error(LOG_PROTOCOL, _("Couldn't login to IMAP server %s.\n"), account->recv_server);
1220 mainwindow_show_error();
1222 alertpanel_error_log(_("Couldn't login to IMAP server %s."), account->recv_server);
1228 statuswindow_pop_all();
1229 session->authenticated = TRUE;
1230 return MAILIMAP_NO_ERROR;
1233 static void imap_session_destroy(Session *session)
1235 if (session->state != SESSION_DISCONNECTED)
1236 imap_threaded_disconnect(IMAP_SESSION(session)->folder);
1238 imap_free_capabilities(IMAP_SESSION(session));
1239 g_free(IMAP_SESSION(session)->mbox);
1242 static gchar *imap_fetch_msg(Folder *folder, FolderItem *item, gint uid)
1244 return imap_fetch_msg_full(folder, item, uid, TRUE, TRUE);
1247 static guint get_file_size_with_crs(const gchar *filename)
1253 if (filename == NULL)
1256 fp = g_fopen(filename, "rb");
1260 while (fgets(buf, sizeof (buf), fp) != NULL) {
1262 if (!strstr(buf, "\r\n") && strstr(buf, "\n"))
1270 static void imap_remove_cached_msg(Folder *folder, FolderItem *item, MsgInfo *msginfo)
1272 gchar *path, *filename;
1274 path = folder_item_get_path(item);
1276 if (!is_dir_exist(path)) {
1281 filename = g_strconcat(path, G_DIR_SEPARATOR_S, itos(msginfo->msgnum), NULL);
1284 if (is_file_exist(filename)) {
1285 claws_unlink(filename);
1290 typedef struct _TagsData {
1293 IMAPFolderItem *item;
1296 static void imap_commit_tags(FolderItem *item, MsgInfo *msginfo, GSList *tags_set, GSList *tags_unset)
1298 IMAPSession *session;
1299 gint ok, can_create_tags;
1300 Folder *folder = NULL;
1301 TagsData *ht_data = NULL;
1304 g_return_if_fail(item != NULL);
1305 g_return_if_fail(msginfo != NULL);
1307 folder = item->folder;
1308 debug_print("getting session...\n");
1309 session = imap_session_get(folder);
1312 debug_print("can't get session\n");
1316 ok = imap_select(session, IMAP_FOLDER(folder), item,
1317 NULL, NULL, NULL, NULL, &can_create_tags, FALSE);
1319 if (ok != MAILIMAP_NO_ERROR) {
1324 if (IMAP_FOLDER_ITEM(item)->can_create_flags != ITEM_CAN_CREATE_FLAGS)
1327 if (IMAP_FOLDER_ITEM(item)->batching) {
1328 /* instead of performing an UID STORE command for each message change,
1329 * as a lot of them can change "together", we just fill in hashtables
1330 * and defer the treatment so that we're able to send only one
1333 debug_print("IMAP batch mode on, deferring tags change\n");
1334 for (cur = tags_set; cur; cur = cur->next) {
1335 gint cur_tag = GPOINTER_TO_INT(cur->data);
1337 ht_data = g_hash_table_lookup(IMAP_FOLDER_ITEM(item)->tags_set_table,
1338 GINT_TO_POINTER(cur_tag));
1339 if (ht_data == NULL) {
1340 ht_data = g_new0(TagsData, 1);
1341 ht_data->str = g_strdup(tags_get_tag(cur_tag));
1342 ht_data->item = IMAP_FOLDER_ITEM(item);
1343 g_hash_table_insert(IMAP_FOLDER_ITEM(item)->tags_set_table,
1344 GINT_TO_POINTER(cur_tag), ht_data);
1346 ht_data->msglist = g_slist_prepend(ht_data->msglist, GINT_TO_POINTER(msginfo->msgnum));
1349 for (cur = tags_unset; cur; cur = cur->next) {
1350 gint cur_tag = GPOINTER_TO_INT(cur->data);
1352 ht_data = g_hash_table_lookup(IMAP_FOLDER_ITEM(item)->tags_unset_table,
1353 GINT_TO_POINTER(cur_tag));
1354 if (ht_data == NULL) {
1355 ht_data = g_new0(TagsData, 1);
1356 ht_data->str = g_strdup(tags_get_tag(cur_tag));
1357 ht_data->item = IMAP_FOLDER_ITEM(item);
1358 g_hash_table_insert(IMAP_FOLDER_ITEM(item)->tags_unset_table,
1359 GINT_TO_POINTER(cur_tag), ht_data);
1361 ht_data->msglist = g_slist_prepend(ht_data->msglist, GINT_TO_POINTER(msginfo->msgnum));
1365 GSList *list_set = NULL;
1366 GSList *list_unset = NULL;
1369 numlist.data = GINT_TO_POINTER(msginfo->msgnum);
1370 numlist.next = NULL;
1372 debug_print("IMAP changing tags NOW\n");
1373 for (cur = tags_set; cur; cur = cur->next) {
1374 gint cur_tag = GPOINTER_TO_INT(cur->data);
1375 const gchar *str = tags_get_tag(cur_tag);
1376 if (IS_NOT_RESERVED_TAG(str))
1377 list_set = g_slist_prepend(list_set, g_strdup(str));
1380 ok = imap_set_message_flags(session,
1381 IMAP_FOLDER_ITEM(item), &numlist, 0, list_set, TRUE);
1382 slist_free_strings(list_set);
1383 g_slist_free(list_set);
1384 if (ok != MAILIMAP_NO_ERROR) {
1389 for (cur = tags_unset; cur; cur = cur->next) {
1390 gint cur_tag = GPOINTER_TO_INT(cur->data);
1391 const gchar *str = tags_get_tag(cur_tag);
1392 if (IS_NOT_RESERVED_TAG(str))
1393 list_unset = g_slist_prepend(list_unset, g_strdup(str));
1396 ok = imap_set_message_flags(session,
1397 IMAP_FOLDER_ITEM(item), &numlist, 0, list_unset, FALSE);
1398 slist_free_strings(list_unset);
1399 g_slist_free(list_unset);
1400 if (ok != MAILIMAP_NO_ERROR) {
1407 static gchar *imap_fetch_msg_full(Folder *folder, FolderItem *item, gint uid,
1408 gboolean headers, gboolean body)
1410 gchar *path, *filename;
1411 IMAPSession *session;
1414 g_return_val_if_fail(folder != NULL, NULL);
1415 g_return_val_if_fail(item != NULL, NULL);
1420 path = folder_item_get_path(item);
1421 if (!is_dir_exist(path))
1422 make_dir_hier(path);
1423 filename = g_strconcat(path, G_DIR_SEPARATOR_S, itos(uid), NULL);
1425 debug_print("trying to fetch cached %s\n", filename);
1426 if (is_file_exist(filename)) {
1427 /* see whether the local file represents the whole message
1428 * or not. As the IMAP server reports size with \r chars,
1429 * we have to update the local file (UNIX \n only) size */
1430 MsgInfo *cached = msgcache_get_msg(item->cache,uid);
1431 guint have_size = -1;
1434 debug_print("message %d has been already %scached.\n", uid,
1435 MSG_IS_FULLY_CACHED(cached->flags) ? "fully ":"");
1437 if (!cached || !MSG_IS_FULLY_CACHED(cached->flags)) {
1438 have_size = get_file_size_with_crs(filename);
1439 if (cached && (cached->size <= have_size || !body)) {
1440 procmsg_msginfo_free(cached);
1441 ok = file_strip_crs(filename);
1442 if (ok == 0 && cached && cached->size <= have_size) {
1443 /* we have it all and stripped */
1444 debug_print("...fully cached in fact; setting flag.\n");
1445 procmsg_msginfo_set_flags(cached, MSG_FULLY_CACHED, 0);
1448 } else if (!cached && time(NULL) - get_file_mtime(filename) < 60) {
1449 debug_print("message not cached and file recent, considering file complete\n");
1450 ok = file_strip_crs(filename);
1454 procmsg_msginfo_free(cached);
1457 if (cached && MSG_IS_FULLY_CACHED(cached->flags)) {
1458 procmsg_msginfo_free(cached);
1462 MsgInfo *cached = msgcache_get_msg(item->cache,uid);
1464 procmsg_msginfo_unset_flags(cached, MSG_FULLY_CACHED, 0);
1465 procmsg_msginfo_free(cached);
1469 debug_print("getting session...\n");
1470 session = imap_session_get(folder);
1476 session_set_access_time(SESSION(session));
1477 lock_session(session); /* unlocked later in the function */
1479 debug_print("IMAP fetching messages\n");
1480 ok = imap_select(session, IMAP_FOLDER(folder), item,
1481 NULL, NULL, NULL, NULL, NULL, FALSE);
1482 if (ok != MAILIMAP_NO_ERROR) {
1483 g_warning("can't select mailbox %s\n", item->path);
1488 session_set_access_time(SESSION(session));
1490 debug_print("getting message %d...\n", uid);
1491 ok = imap_cmd_fetch(session, (guint32)uid, filename, headers, body);
1493 if (ok != MAILIMAP_NO_ERROR) {
1494 g_warning("can't fetch message %d\n", uid);
1499 session_set_access_time(SESSION(session));
1500 unlock_session(session);
1502 ok = file_strip_crs(filename);
1504 if (ok == 0 && headers && body) {
1505 MsgInfo *cached = msgcache_get_msg(item->cache,uid);
1507 procmsg_msginfo_set_flags(cached, MSG_FULLY_CACHED, 0);
1508 procmsg_msginfo_free(cached);
1510 } else if (ok == -1) {
1511 MsgInfo *cached = msgcache_get_msg(item->cache,uid);
1513 procmsg_msginfo_unset_flags(cached, MSG_FULLY_CACHED, 0);
1514 procmsg_msginfo_free(cached);
1520 static gboolean imap_is_msg_fully_cached(Folder *folder, FolderItem *item, gint uid)
1522 gchar *path, *filename;
1524 MsgInfo *cached = msgcache_get_msg(item->cache,uid);
1529 if (MSG_IS_FULLY_CACHED(cached->flags)) {
1530 procmsg_msginfo_free(cached);
1533 path = folder_item_get_path(item);
1534 if (!is_dir_exist(path))
1537 filename = g_strconcat(path, G_DIR_SEPARATOR_S, itos(uid), NULL);
1539 if (is_file_exist(filename)) {
1540 if (cached && cached->total_size == cached->size) {
1543 procmsg_msginfo_set_flags(cached, MSG_FULLY_CACHED, 0);
1546 size = get_file_size_with_crs(filename);
1549 if (cached && size >= cached->size) {
1550 cached->total_size = cached->size;
1551 procmsg_msginfo_set_flags(cached, MSG_FULLY_CACHED, 0);
1552 procmsg_msginfo_free(cached);
1556 procmsg_msginfo_free(cached);
1560 void imap_cache_msg(FolderItem *item, gint msgnum)
1562 Folder *folder = NULL;
1566 folder = item->folder;
1568 if (!imap_is_msg_fully_cached(folder, item, msgnum)) {
1569 gchar *tmp = imap_fetch_msg_full(folder, item, msgnum, TRUE, TRUE);
1570 debug_print("fetched %s\n", tmp);
1575 static gint imap_add_msg(Folder *folder, FolderItem *dest,
1576 const gchar *file, MsgFlags *flags)
1580 MsgFileInfo fileinfo;
1582 g_return_val_if_fail(file != NULL, -1);
1584 fileinfo.msginfo = NULL;
1585 fileinfo.file = (gchar *)file;
1586 fileinfo.flags = flags;
1587 file_list.data = &fileinfo;
1588 file_list.next = NULL;
1590 ret = imap_add_msgs(folder, dest, &file_list, NULL);
1594 static gint imap_add_msgs(Folder *folder, FolderItem *dest, GSList *file_list,
1595 GHashTable *relation)
1598 IMAPSession *session;
1599 guint32 last_uid = 0;
1601 MsgFileInfo *fileinfo;
1602 gint ok = MAILIMAP_NO_ERROR;
1603 gint curnum = 0, total = 0;
1604 gboolean missing_uids = FALSE;
1606 g_return_val_if_fail(folder != NULL, -1);
1607 g_return_val_if_fail(dest != NULL, -1);
1608 g_return_val_if_fail(file_list != NULL, -1);
1610 debug_print("getting session...\n");
1611 session = imap_session_get(folder);
1615 destdir = imap_get_real_path(session, IMAP_FOLDER(folder), dest->path, &ok);
1618 statusbar_print_all(_("Adding messages..."));
1619 total = g_slist_length(file_list);
1620 for (cur = file_list; cur != NULL; cur = cur->next) {
1621 IMAPFlags iflags = 0;
1622 guint32 new_uid = 0;
1623 gchar *real_file = NULL;
1624 fileinfo = (MsgFileInfo *)cur->data;
1626 statusbar_progress_all(curnum, total, total < 10 ? 1:10);
1629 if (fileinfo->flags) {
1630 if (MSG_IS_MARKED(*fileinfo->flags))
1631 iflags |= IMAP_FLAG_FLAGGED;
1632 if (MSG_IS_REPLIED(*fileinfo->flags))
1633 iflags |= IMAP_FLAG_ANSWERED;
1634 if (MSG_IS_FORWARDED(*fileinfo->flags))
1635 iflags |= IMAP_FLAG_FORWARDED;
1636 if (MSG_IS_SPAM(*fileinfo->flags))
1637 iflags |= IMAP_FLAG_SPAM;
1639 iflags |= IMAP_FLAG_HAM;
1640 if (!MSG_IS_UNREAD(*fileinfo->flags))
1641 iflags |= IMAP_FLAG_SEEN;
1645 if (real_file == NULL)
1646 real_file = g_strdup(fileinfo->file);
1648 if (folder_has_parent_of_type(dest, F_QUEUE) ||
1649 folder_has_parent_of_type(dest, F_OUTBOX) ||
1650 folder_has_parent_of_type(dest, F_DRAFT) ||
1651 folder_has_parent_of_type(dest, F_TRASH))
1652 iflags |= IMAP_FLAG_SEEN;
1654 ok = imap_cmd_append(session, IMAP_FOLDER_ITEM(dest), destdir, real_file, iflags,
1657 if (ok != MAILIMAP_NO_ERROR) {
1658 g_warning("can't append message %s\n", real_file);
1661 statusbar_progress_all(0,0,0);
1662 statusbar_pop_all();
1665 debug_print("appended new message as %d\n", new_uid);
1666 /* put the local file in the imapcache, so that we don't
1667 * have to fetch it back later. */
1670 missing_uids = TRUE;
1671 debug_print("Missing UID (0)\n");
1674 gchar *cache_path = folder_item_get_path(dest);
1675 if (!is_dir_exist(cache_path))
1676 make_dir_hier(cache_path);
1677 if (is_dir_exist(cache_path)) {
1678 gchar *cache_file = g_strconcat(
1679 cache_path, G_DIR_SEPARATOR_S,
1680 itos(new_uid), NULL);
1681 copy_file(real_file, cache_file, TRUE);
1682 debug_print("got UID %d, copied to cache: %s\n", new_uid, cache_file);
1689 if (relation != NULL)
1690 g_hash_table_insert(relation, fileinfo->msginfo != NULL ?
1691 (gpointer) fileinfo->msginfo : (gpointer) fileinfo,
1692 GINT_TO_POINTER(new_uid));
1693 if (last_uid < new_uid) {
1700 statusbar_progress_all(0,0,0);
1701 statusbar_pop_all();
1706 imap_scan_required(folder, dest);
1708 session = imap_session_get(folder);
1714 ok = imap_select(session, IMAP_FOLDER(folder), dest,
1715 &a, NULL, NULL, NULL, NULL, FALSE);
1720 static GSList *flatten_mailimap_set(struct mailimap_set * set)
1722 GSList *result = NULL;
1724 guint32 start, end, t;
1727 if (!set || !set->set_list)
1730 for (list = clist_begin(set->set_list); list; list = clist_next(list)) {
1731 struct mailimap_set_item *item = (struct mailimap_set_item *)clist_content(list);
1732 start = item->set_first;
1733 end = item->set_last;
1735 for (t = start; t <= end; t++) {
1736 result = g_slist_prepend(result, GINT_TO_POINTER(t));
1740 result = g_slist_reverse(result);
1741 if (debug_get_mode()) {
1742 debug_print("flat imap set: ");
1743 for (cur = result; cur; cur = cur->next) {
1744 debug_print("%d ", GPOINTER_TO_INT(cur->data));
1751 static gint imap_do_copy_msgs(Folder *folder, FolderItem *dest,
1752 MsgInfoList *msglist, GHashTable *relation)
1756 GSList *seq_list, *cur;
1758 IMAPSession *session;
1759 gint ok = MAILIMAP_NO_ERROR;
1760 GHashTable *uid_hash;
1763 g_return_val_if_fail(folder != NULL, -1);
1764 g_return_val_if_fail(dest != NULL, -1);
1765 g_return_val_if_fail(msglist != NULL, -1);
1767 debug_print("getting session...\n");
1768 session = imap_session_get(folder);
1774 msginfo = (MsgInfo *)msglist->data;
1775 src = msginfo->folder;
1777 g_warning("the src folder is identical to the dest.\n");
1781 if (src->folder != dest->folder) {
1782 GSList *infolist = NULL, *cur;
1784 for (cur = msglist; cur; cur = cur->next) {
1785 msginfo = (MsgInfo *)cur->data;
1786 MsgFileInfo *fileinfo = g_new0(MsgFileInfo, 1);
1787 fileinfo->file = procmsg_get_message_file(msginfo);
1788 fileinfo->flags = &(msginfo->flags);
1789 infolist = g_slist_prepend(infolist, fileinfo);
1791 infolist = g_slist_reverse(infolist);
1792 res = folder_item_add_msgs(dest, infolist, FALSE);
1793 for (cur = infolist; cur; cur = cur->next) {
1794 MsgFileInfo *info = (MsgFileInfo *)cur->data;
1798 g_slist_free(infolist);
1802 lock_session(session); /* unlocked later in the function */
1804 ok = imap_select(session, IMAP_FOLDER(folder), msginfo->folder,
1805 NULL, NULL, NULL, NULL, NULL, FALSE);
1806 if (ok != MAILIMAP_NO_ERROR) {
1810 unlock_session(session);
1812 destdir = imap_get_real_path(session, IMAP_FOLDER(folder), dest->path, &ok);
1817 seq_list = imap_get_lep_set_from_msglist(IMAP_FOLDER(folder), msglist);
1818 uid_hash = g_hash_table_new(g_direct_hash, g_direct_equal);
1820 statusbar_print_all(_("Copying messages..."));
1821 for (cur = seq_list; cur != NULL; cur = g_slist_next(cur)) {
1822 struct mailimap_set * seq_set;
1823 struct mailimap_set * source = NULL;
1824 struct mailimap_set * dest = NULL;
1825 seq_set = cur->data;
1827 debug_print("Copying messages from %s to %s ...\n",
1828 src->path, destdir);
1830 lock_session(session); /* unlocked later in the function */
1831 ok = imap_cmd_copy(session, seq_set, destdir,
1838 if (ok == MAILIMAP_NO_ERROR) {
1839 unlock_session(session);
1840 if (relation && source && dest) {
1841 GSList *s_list = flatten_mailimap_set(source);
1842 GSList *d_list = flatten_mailimap_set(dest);
1843 GSList *s_cur, *d_cur;
1844 if (g_slist_length(s_list) == g_slist_length(d_list)) {
1846 for (s_cur = s_list, d_cur = d_list;
1848 s_cur = s_cur->next, d_cur = d_cur->next) {
1849 g_hash_table_insert(uid_hash, s_cur->data, d_cur->data);
1853 debug_print("hhhmm, source list length != dest list length.\n");
1855 g_slist_free(s_list);
1856 g_slist_free(d_list);
1862 mailimap_set_free(source);
1864 mailimap_set_free(dest);
1866 if (ok != MAILIMAP_NO_ERROR) {
1867 g_hash_table_destroy(uid_hash);
1868 imap_lep_set_free(seq_list);
1869 statusbar_pop_all();
1874 for (cur = msglist; cur != NULL; cur = g_slist_next(cur)) {
1875 MsgInfo *msginfo = (MsgInfo *)cur->data;
1878 hashval = g_hash_table_lookup(uid_hash, GINT_TO_POINTER(msginfo->msgnum));
1880 if (hashval != NULL) {
1881 gint num = GPOINTER_TO_INT(hashval);
1882 g_hash_table_insert(relation, msginfo,
1883 GINT_TO_POINTER(num));
1886 debug_print("copied message %d as %d\n", msginfo->msgnum, num);
1887 /* put the local file in the imapcache, so that we don't
1888 * have to fetch it back later. */
1890 gchar *cache_path = folder_item_get_path(msginfo->folder);
1891 gchar *real_file = g_strconcat(
1892 cache_path, G_DIR_SEPARATOR_S,
1893 itos(msginfo->msgnum), NULL);
1894 gchar *cache_file = NULL;
1896 cache_path = folder_item_get_path(dest);
1897 cache_file = g_strconcat(
1898 cache_path, G_DIR_SEPARATOR_S,
1900 if (!is_dir_exist(cache_path))
1901 make_dir_hier(cache_path);
1902 if (is_file_exist(real_file) && is_dir_exist(cache_path)) {
1903 copy_file(real_file, cache_file, TRUE);
1904 debug_print("copied to cache: %s\n", cache_file);
1911 g_hash_table_insert(relation, msginfo,
1912 GINT_TO_POINTER(0));
1914 statusbar_pop_all();
1916 g_hash_table_destroy(uid_hash);
1917 imap_lep_set_free(seq_list);
1921 IMAP_FOLDER_ITEM(dest)->lastuid = 0;
1922 IMAP_FOLDER_ITEM(dest)->uid_next = 0;
1923 g_slist_free(IMAP_FOLDER_ITEM(dest)->uid_list);
1924 IMAP_FOLDER_ITEM(dest)->uid_list = NULL;
1926 imap_scan_required(folder, dest);
1927 if (ok == MAILIMAP_NO_ERROR)
1933 static gint imap_copy_msg(Folder *folder, FolderItem *dest, MsgInfo *msginfo)
1937 g_return_val_if_fail(msginfo != NULL, -1);
1939 msglist.data = msginfo;
1940 msglist.next = NULL;
1942 return imap_copy_msgs(folder, dest, &msglist, NULL);
1945 static gint imap_copy_msgs(Folder *folder, FolderItem *dest,
1946 MsgInfoList *msglist, GHashTable *relation)
1951 g_return_val_if_fail(folder != NULL, -1);
1952 g_return_val_if_fail(dest != NULL, -1);
1953 g_return_val_if_fail(msglist != NULL, -1);
1955 msginfo = (MsgInfo *)msglist->data;
1956 g_return_val_if_fail(msginfo->folder != NULL, -1);
1958 ret = imap_do_copy_msgs(folder, dest, msglist, relation);
1963 static IMAPSearchKey* search_make_key(MatcherProp* match, gboolean* is_all)
1965 if (match->matchtype == MATCHTYPE_MATCHCASE || match->matchtype == MATCHTYPE_MATCH) {
1966 IMAPSearchKey* result = NULL;
1967 gboolean invert = FALSE;
1968 gint matchertype = match->criteria;
1974 switch (matchertype) {
1975 case MATCHCRITERIA_NOT_NEW: invert = TRUE; matchertype = MATCHCRITERIA_NEW; break;
1976 case MATCHCRITERIA_NOT_MARKED: invert = TRUE; matchertype = MATCHCRITERIA_MARKED; break;
1977 case MATCHCRITERIA_NOT_FORWARDED: invert = TRUE; matchertype = MATCHCRITERIA_FORWARDED; break;
1978 case MATCHCRITERIA_NOT_SPAM: invert = TRUE; matchertype = MATCHCRITERIA_SPAM; break;
1979 case MATCHCRITERIA_NOT_SUBJECT: invert = TRUE; matchertype = MATCHCRITERIA_SUBJECT; break;
1980 case MATCHCRITERIA_NOT_FROM: invert = TRUE; matchertype = MATCHCRITERIA_FROM; break;
1981 case MATCHCRITERIA_NOT_TO: invert = TRUE; matchertype = MATCHCRITERIA_TO; break;
1982 case MATCHCRITERIA_NOT_CC: invert = TRUE; matchertype = MATCHCRITERIA_CC; break;
1983 case MATCHCRITERIA_NOT_REFERENCES: invert = TRUE; matchertype = MATCHCRITERIA_REFERENCES; break;
1984 case MATCHCRITERIA_NOT_HEADER: invert = TRUE; matchertype = MATCHCRITERIA_HEADER; break;
1985 case MATCHCRITERIA_NOT_TAG: invert = TRUE; matchertype = MATCHCRITERIA_TAG; break;
1986 case MATCHCRITERIA_NOT_HEADERS_PART: invert = TRUE; matchertype = MATCHCRITERIA_HEADERS_PART; break;
1987 case MATCHCRITERIA_NOT_MESSAGE: invert = TRUE; matchertype = MATCHCRITERIA_MESSAGE; break;
1988 case MATCHCRITERIA_NOT_BODY_PART: invert = TRUE; matchertype = MATCHCRITERIA_BODY_PART; break;
1989 case MATCHCRITERIA_NOT_TO_AND_NOT_CC: invert = TRUE; matchertype = MATCHCRITERIA_TO_OR_CC; break;
1990 case MATCHCRITERIA_NOT_INREPLYTO: invert = TRUE; matchertype = MATCHCRITERIA_INREPLYTO; break;
1994 * this aborts conversion even for predicates understood by the following code.
1995 * while that might seem wasteful, claws local search for information listed below
1996 * has proven faster than IMAP search plus network roundtrips. once this changes,
1997 * consider removing these exceptions.
1999 switch (matchertype) {
2000 case MATCHCRITERIA_FROM:
2001 case MATCHCRITERIA_TO:
2002 case MATCHCRITERIA_CC:
2003 case MATCHCRITERIA_TO_OR_CC:
2004 case MATCHCRITERIA_SUBJECT:
2005 case MATCHCRITERIA_REFERENCES:
2006 case MATCHCRITERIA_INREPLYTO:
2007 case MATCHCRITERIA_AGE_GREATER:
2008 case MATCHCRITERIA_AGE_LOWER:
2009 case MATCHCRITERIA_FORWARDED:
2010 case MATCHCRITERIA_SPAM:
2011 case MATCHCRITERIA_UNREAD:
2012 case MATCHCRITERIA_NEW:
2013 case MATCHCRITERIA_MARKED:
2014 case MATCHCRITERIA_REPLIED:
2015 case MATCHCRITERIA_DELETED:
2016 case MATCHCRITERIA_SIZE_GREATER:
2017 case MATCHCRITERIA_SIZE_SMALLER:
2018 case MATCHCRITERIA_SIZE_EQUAL:
2022 /* the Message-ID header is also cached */
2023 if (matchertype == MATCHCRITERIA_HEADER && g_strcmp0("Message-ID", match->header) == 0) {
2027 switch (matchertype) {
2028 case MATCHCRITERIA_FORWARDED:
2029 result = imap_search_new(IMAP_SEARCH_CRITERIA_TAG, NULL, RTAG_FORWARDED, 0);
2032 case MATCHCRITERIA_SPAM:
2033 result = imap_search_new(IMAP_SEARCH_CRITERIA_TAG, NULL, RTAG_JUNK, 0);
2036 case MATCHCRITERIA_INREPLYTO:
2037 result = imap_search_new(IMAP_SEARCH_CRITERIA_HEADER, "In-Reply-To", match->expr, 0);
2040 case MATCHCRITERIA_REFERENCES:
2041 result = imap_search_new(IMAP_SEARCH_CRITERIA_HEADER, "References", match->expr, 0);
2044 case MATCHCRITERIA_TO_OR_CC:
2045 result = imap_search_or(
2046 imap_search_new(IMAP_SEARCH_CRITERIA_TO, NULL, match->expr, 0),
2047 imap_search_new(IMAP_SEARCH_CRITERIA_CC, NULL, match->expr, 0)
2051 case MATCHCRITERIA_HEADERS_PART:
2052 result = imap_search_and(
2053 imap_search_not(imap_search_new(IMAP_SEARCH_CRITERIA_BODY, NULL, match->expr, 0)),
2054 imap_search_new(IMAP_SEARCH_CRITERIA_MESSAGE, NULL, match->expr, 0)
2058 case MATCHCRITERIA_SIZE_EQUAL:
2059 result = imap_search_and(
2060 imap_search_not(imap_search_new(IMAP_SEARCH_CRITERIA_SIZE_SMALLER, NULL, NULL, match->value)),
2061 imap_search_not(imap_search_new(IMAP_SEARCH_CRITERIA_SIZE_GREATER, NULL, NULL, match->value))
2065 case MATCHCRITERIA_NOT_UNREAD:
2066 result = imap_search_new(IMAP_SEARCH_CRITERIA_READ, NULL, NULL, 0);
2069 case MATCHCRITERIA_UNREAD:
2070 result = imap_search_new(IMAP_SEARCH_CRITERIA_UNREAD, NULL, NULL, 0);
2073 case MATCHCRITERIA_NEW:
2074 result = imap_search_new(IMAP_SEARCH_CRITERIA_NEW, NULL, NULL, 0);
2077 case MATCHCRITERIA_MARKED:
2078 result = imap_search_new(IMAP_SEARCH_CRITERIA_MARKED, NULL, NULL, 0);
2081 case MATCHCRITERIA_DELETED:
2082 result = imap_search_new(IMAP_SEARCH_CRITERIA_DELETED, NULL, NULL, 0);
2085 case MATCHCRITERIA_REPLIED:
2086 result = imap_search_new(IMAP_SEARCH_CRITERIA_REPLIED, NULL, NULL, 0);
2089 case MATCHCRITERIA_TAG:
2091 gchar *tmp = imap_utf8_to_modified_utf7(match->expr, TRUE);
2092 result = imap_search_new(IMAP_SEARCH_CRITERIA_TAG, NULL, tmp, 0);
2097 case MATCHCRITERIA_SUBJECT:
2098 result = imap_search_new(IMAP_SEARCH_CRITERIA_SUBJECT, NULL, match->expr, 0);
2101 case MATCHCRITERIA_FROM:
2102 result = imap_search_new(IMAP_SEARCH_CRITERIA_FROM, NULL, match->expr, 0);
2105 case MATCHCRITERIA_TO:
2106 result = imap_search_new(IMAP_SEARCH_CRITERIA_TO, NULL, match->expr, 0);
2109 case MATCHCRITERIA_CC:
2110 result = imap_search_new(IMAP_SEARCH_CRITERIA_CC, NULL, match->expr, 0);
2113 case MATCHCRITERIA_AGE_GREATER:
2114 result = imap_search_new(IMAP_SEARCH_CRITERIA_AGE_GREATER, NULL, NULL, match->value);
2117 case MATCHCRITERIA_AGE_LOWER:
2118 result = imap_search_new(IMAP_SEARCH_CRITERIA_AGE_LOWER, NULL, NULL, match->value);
2121 case MATCHCRITERIA_BODY_PART:
2122 result = imap_search_new(IMAP_SEARCH_CRITERIA_BODY, NULL, match->expr, 0);
2125 case MATCHCRITERIA_MESSAGE:
2126 result = imap_search_new(IMAP_SEARCH_CRITERIA_MESSAGE, NULL, match->expr, 0);
2129 case MATCHCRITERIA_HEADER:
2130 result = imap_search_new(IMAP_SEARCH_CRITERIA_HEADER, match->header, match->expr, 0);
2133 case MATCHCRITERIA_SIZE_GREATER:
2134 result = imap_search_new(IMAP_SEARCH_CRITERIA_SIZE_GREATER, NULL, NULL, match->value);
2137 case MATCHCRITERIA_SIZE_SMALLER:
2138 result = imap_search_new(IMAP_SEARCH_CRITERIA_SIZE_SMALLER, NULL, NULL, match->value);
2142 result = imap_search_new(IMAP_SEARCH_CRITERIA_ALL, NULL, NULL, 0);
2150 result = imap_search_not(result);
2151 if (is_all && *is_all) {
2162 static gint search_msgs (Folder *folder,
2163 FolderItem *container,
2164 MsgNumberList **msgs,
2165 gboolean *on_server,
2166 MatcherList *predicate,
2167 SearchProgressNotify progress_cb,
2168 gpointer progress_data)
2170 IMAPSearchKey* key = NULL;
2173 clist* uidlist = NULL;
2174 gboolean server_filtering_useless = FALSE;
2175 IMAPSession *session;
2177 if (on_server == NULL || !*on_server) {
2178 return folder_item_search_msgs_local(folder, container, msgs, on_server,
2179 predicate, progress_cb, progress_data);
2182 for (cur = predicate->matchers; cur != NULL; cur = cur->next) {
2183 IMAPSearchKey* matcherPart = NULL;
2184 MatcherProp* prop = (MatcherProp*) cur->data;
2187 matcherPart = search_make_key(prop, &is_all);
2190 *on_server &= matcherPart != NULL && prop->matchtype == MATCHTYPE_MATCHCASE;
2196 server_filtering_useless = is_all;
2197 } else if (predicate->bool_and) {
2198 key = imap_search_and(key, matcherPart);
2199 server_filtering_useless &= is_all;
2201 key = imap_search_or(key, matcherPart);
2202 server_filtering_useless |= is_all;
2207 if (server_filtering_useless) {
2208 imap_search_free(key);
2212 if (key == NULL && progress_cb != NULL) {
2217 progress_cb(progress_data, TRUE, 0, 0, container->total_msgs);
2218 progress_cb(progress_data, TRUE, container->total_msgs, 0, container->total_msgs);
2220 list = folder_item_get_msg_list(container);
2221 for (cur = list; cur != NULL; cur = cur->next) {
2222 *msgs = g_slist_prepend(*msgs, GUINT_TO_POINTER(((MsgInfo*) cur->data)->msgnum));
2225 *msgs = g_slist_reverse(*msgs);
2229 session = imap_session_get(folder);
2233 result = imap_select(session, IMAP_FOLDER(folder), FOLDER_ITEM(container),
2234 NULL, NULL, NULL, NULL, NULL, TRUE);
2235 if (result != MAILIMAP_NO_ERROR)
2239 progress_cb(progress_data, TRUE, 0, 0, container->total_msgs);
2240 result = imap_threaded_search(folder, IMAP_SEARCH_TYPE_KEYED, key, NULL, &uidlist);
2242 progress_cb(progress_data, TRUE, container->total_msgs, 0, container->total_msgs);
2244 if (result == MAILIMAP_NO_ERROR) {
2247 *msgs = imap_uid_list_from_lep(uidlist, &result);
2248 mailimap_search_result_free(uidlist);
2257 static gint imap_do_remove_msgs(Folder *folder, FolderItem *dest,
2258 MsgInfoList *msglist, GHashTable *relation)
2260 gchar *destdir, *dir;
2261 GSList *numlist = NULL, *cur;
2263 IMAPSession *session;
2264 gint ok = MAILIMAP_NO_ERROR;
2266 g_return_val_if_fail(folder != NULL, -1);
2267 g_return_val_if_fail(dest != NULL, -1);
2268 g_return_val_if_fail(msglist != NULL, -1);
2270 debug_print("getting session...\n");
2271 session = imap_session_get(folder);
2276 lock_session(session); /* unlocked later in the function */
2278 msginfo = (MsgInfo *)msglist->data;
2280 ok = imap_select(session, IMAP_FOLDER(folder), msginfo->folder,
2281 NULL, NULL, NULL, NULL, NULL, FALSE);
2282 if (ok != MAILIMAP_NO_ERROR) {
2286 destdir = imap_get_real_path(session, IMAP_FOLDER(folder), dest->path, &ok);
2291 for (cur = msglist; cur; cur = cur->next) {
2292 msginfo = (MsgInfo *)cur->data;
2293 if (!MSG_IS_DELETED(msginfo->flags))
2294 numlist = g_slist_prepend(numlist, GINT_TO_POINTER(msginfo->msgnum));
2296 numlist = g_slist_reverse(numlist);
2298 if (numlist != NULL) {
2299 ok = imap_set_message_flags
2300 (session, IMAP_FOLDER_ITEM(msginfo->folder), numlist, IMAP_FLAG_DELETED, NULL, TRUE);
2301 if (ok != MAILIMAP_NO_ERROR) {
2302 log_warning(LOG_PROTOCOL, _("can't set deleted flags\n"));
2306 } /* else we just need to expunge */
2307 ok = imap_cmd_expunge(session, folder->account->imap_use_trash);
2308 if (ok != MAILIMAP_NO_ERROR) {
2309 log_warning(LOG_PROTOCOL, _("can't expunge\n"));
2314 session->folder_content_changed = TRUE;
2315 unlock_session(session);
2317 dir = folder_item_get_path(msginfo->folder);
2318 if (is_dir_exist(dir)) {
2319 for (cur = msglist; cur; cur = cur->next) {
2320 msginfo = (MsgInfo *)cur->data;
2321 remove_numbered_files(dir, msginfo->msgnum, msginfo->msgnum);
2326 g_slist_free(numlist);
2328 imap_scan_required(folder, dest);
2331 if (ok == MAILIMAP_NO_ERROR)
2337 static gint imap_remove_msgs(Folder *folder, FolderItem *dest,
2338 MsgInfoList *msglist, GHashTable *relation)
2342 g_return_val_if_fail(folder != NULL, -1);
2343 g_return_val_if_fail(dest != NULL, -1);
2344 if (msglist == NULL)
2347 msginfo = (MsgInfo *)msglist->data;
2348 g_return_val_if_fail(msginfo->folder != NULL, -1);
2350 return imap_do_remove_msgs(folder, dest, msglist, relation);
2353 static gint imap_remove_all_msg(Folder *folder, FolderItem *item)
2355 GSList *list = folder_item_get_msg_list(item);
2356 gint res = imap_remove_msgs(folder, item, list, NULL);
2357 procmsg_msg_list_free(list);
2361 static gboolean imap_is_msg_changed(Folder *folder, FolderItem *item,
2364 /* TODO: properly implement this method */
2368 static gint imap_close(Folder *folder, FolderItem *item)
2373 static gint imap_scan_tree_real(Folder *folder, gboolean subs_only)
2375 FolderItem *item = NULL;
2376 IMAPSession *session;
2377 gchar *root_folder = NULL;
2379 g_return_val_if_fail(folder != NULL, -1);
2380 g_return_val_if_fail(folder->account != NULL, -1);
2382 debug_print("getting session...\n");
2383 session = imap_session_get(folder);
2385 if (!folder->node) {
2386 folder_tree_destroy(folder);
2387 item = folder_item_new(folder, folder->name, NULL);
2388 item->folder = folder;
2389 folder->node = item->node = g_node_new(item);
2394 if (folder->account->imap_dir && *folder->account->imap_dir) {
2396 int r = MAILIMAP_NO_ERROR;
2399 Xstrdup_a(root_folder, folder->account->imap_dir, {return -1;});
2400 extract_quote(root_folder, '"');
2401 subst_char(root_folder,
2402 imap_get_path_separator(session, IMAP_FOLDER(folder),
2407 strtailchomp(root_folder, '/');
2408 real_path = imap_get_real_path
2409 (session, IMAP_FOLDER(folder), root_folder, &r);
2412 debug_print("IMAP root directory: %s\n", real_path);
2414 /* check if root directory exist */
2416 r = imap_threaded_list(session->folder, "", real_path,
2419 if (r != MAILIMAP_NO_ERROR)
2420 imap_handle_error(SESSION(session), NULL, r);
2422 if ((r != MAILIMAP_NO_ERROR) || (clist_count(lep_list) == 0)) {
2423 if (!folder->node) {
2424 item = folder_item_new(folder, folder->name, NULL);
2425 item->folder = folder;
2426 folder->node = item->node = g_node_new(item);
2430 mailimap_list_result_free(lep_list);
2436 item = FOLDER_ITEM(folder->node->data);
2438 if (item && !item->path && root_folder) {
2439 item->path = g_strdup(root_folder);
2442 if (!item || ((item->path || root_folder) &&
2443 strcmp2(item->path, root_folder) != 0)) {
2444 folder_tree_destroy(folder);
2445 item = folder_item_new(folder, folder->name, root_folder);
2446 item->folder = folder;
2447 folder->node = item->node = g_node_new(item);
2450 imap_scan_tree_recursive(session, FOLDER_ITEM(folder->node->data), subs_only);
2451 imap_create_missing_folders(folder);
2456 static gint imap_scan_tree(Folder *folder)
2458 gboolean subs_only = FALSE;
2459 if (folder->account) {
2460 debug_print(" scanning only subs %d\n", folder->account->imap_subsonly);
2461 subs_only = folder->account->imap_subsonly;
2463 return imap_scan_tree_real(folder, subs_only);
2466 static gint imap_scan_tree_recursive(IMAPSession *session, FolderItem *item, gboolean subs_only)
2469 IMAPFolder *imapfolder;
2470 FolderItem *new_item;
2471 GSList *item_list, *cur;
2474 gchar *wildcard_path;
2478 int r = MAILIMAP_NO_ERROR;
2480 g_return_val_if_fail(item != NULL, -1);
2481 g_return_val_if_fail(item->folder != NULL, -1);
2482 g_return_val_if_fail(item->no_sub == FALSE, -1);
2484 folder = item->folder;
2485 imapfolder = IMAP_FOLDER(folder);
2487 separator = imap_get_path_separator(session, imapfolder, item->path, &r);
2491 if (folder->ui_func)
2492 folder->ui_func(folder, item, folder->ui_func_data);
2495 wildcard[0] = separator;
2498 real_path = imap_get_real_path(session, imapfolder, item->path, &r);
2506 real_path = g_strdup("");
2509 Xstrcat_a(wildcard_path, real_path, wildcard,
2510 {g_free(real_path); return MAILIMAP_ERROR_BAD_STATE;});
2514 r = imap_threaded_lsub(folder, "", wildcard_path, &lep_list);
2516 r = imap_threaded_list(folder, "", wildcard_path, &lep_list);
2518 if (r != MAILIMAP_NO_ERROR) {
2519 imap_handle_error(SESSION(session), NULL, r);
2525 item_list = imap_list_from_lep(imapfolder,
2526 lep_list, real_path, FALSE);
2527 mailimap_list_result_free(lep_list);
2532 node = item->node->children;
2533 while (node != NULL) {
2534 FolderItem *old_item = FOLDER_ITEM(node->data);
2535 GNode *next = node->next;
2538 for (cur = item_list; cur != NULL; cur = cur->next) {
2539 FolderItem *cur_item = FOLDER_ITEM(cur->data);
2540 if (!strcmp2(old_item->path, cur_item->path)) {
2541 new_item = cur_item;
2546 if (old_item && old_item->path && !strcmp(old_item->path, "INBOX")) {
2547 debug_print("not removing INBOX\n");
2549 debug_print("folder '%s' not found. removing...\n",
2551 folder_item_remove(old_item);
2554 old_item->no_sub = new_item->no_sub;
2555 old_item->no_select = new_item->no_select;
2556 if (old_item->no_sub == TRUE && node->children) {
2557 debug_print("folder '%s' doesn't have "
2558 "subfolders. removing...\n",
2560 folder_item_remove_children(old_item);
2567 for (cur = item_list; cur != NULL; cur = cur->next) {
2568 FolderItem *cur_item = FOLDER_ITEM(cur->data);
2571 for (node = item->node->children; node != NULL;
2572 node = node->next) {
2573 if (!strcmp2(FOLDER_ITEM(node->data)->path,
2575 new_item = FOLDER_ITEM(node->data);
2576 folder_item_destroy(cur_item);
2582 new_item = cur_item;
2583 debug_print("new folder '%s' found.\n", new_item->path);
2584 folder_item_append(item, new_item);
2587 if (!strcmp(new_item->path, "INBOX")) {
2588 new_item->stype = F_INBOX;
2589 folder->inbox = new_item;
2590 } else if (!folder_item_parent(item) || item->stype == F_INBOX) {
2593 base = g_path_get_basename(new_item->path);
2595 if (!folder->outbox && !g_ascii_strcasecmp(base, "Sent")) {
2596 new_item->stype = F_OUTBOX;
2597 folder->outbox = new_item;
2598 } else if (!folder->draft && !g_ascii_strcasecmp(base, "Drafts")) {
2599 new_item->stype = F_DRAFT;
2600 folder->draft = new_item;
2601 } else if (!folder->queue && !g_ascii_strcasecmp(base, "Queue")) {
2602 new_item->stype = F_QUEUE;
2603 folder->queue = new_item;
2604 } else if (!folder->trash && !g_ascii_strcasecmp(base, "Trash")) {
2605 new_item->stype = F_TRASH;
2606 folder->trash = new_item;
2611 if (new_item->no_sub == FALSE)
2612 imap_scan_tree_recursive(session, new_item, subs_only);
2615 g_slist_free(item_list);
2617 return MAILIMAP_NO_ERROR;
2620 GList *imap_scan_subtree(Folder *folder, FolderItem *item, gboolean unsubs_only, gboolean recursive)
2622 IMAPSession *session = imap_session_get(folder);
2624 gchar *wildcard_path;
2628 GSList *item_list = NULL, *cur;
2629 GList *child_list = NULL, *tmplist = NULL;
2630 GSList *sub_list = NULL;
2631 int r = MAILIMAP_NO_ERROR;
2636 separator = imap_get_path_separator(session, IMAP_FOLDER(folder), item->path, &r);
2641 wildcard[0] = separator;
2644 real_path = imap_get_real_path(session, IMAP_FOLDER(folder), item->path, &r);
2652 real_path = g_strdup("");
2655 Xstrcat_a(wildcard_path, real_path, wildcard,
2656 {g_free(real_path); return NULL;});
2660 statusbar_print_all(_("Looking for unsubscribed folders in %s..."),
2661 item->path?item->path:item->name);
2663 statusbar_print_all(_("Looking for subfolders of %s..."),
2664 item->path?item->path:item->name);
2666 r = imap_threaded_list(folder, "", wildcard_path, &lep_list);
2669 statusbar_pop_all();
2672 item_list = imap_list_from_lep(IMAP_FOLDER(folder),
2673 lep_list, real_path, FALSE);
2674 mailimap_list_result_free(lep_list);
2676 for (cur = item_list; cur != NULL; cur = cur->next) {
2677 FolderItem *cur_item = FOLDER_ITEM(cur->data);
2679 tmplist = imap_scan_subtree(folder, cur_item,
2680 unsubs_only, recursive);
2682 child_list = g_list_concat(child_list, tmplist);
2684 child_list = g_list_prepend(child_list,
2685 imap_get_real_path(session,
2686 IMAP_FOLDER(folder), cur_item->path, &r));
2689 statusbar_pop_all();
2692 folder_item_destroy(cur_item);
2694 child_list = g_list_reverse(child_list);
2695 g_slist_free(item_list);
2698 r = imap_threaded_lsub(folder, "", wildcard_path, &lep_list);
2701 statusbar_pop_all();
2704 sub_list = imap_list_from_lep(IMAP_FOLDER(folder),
2705 lep_list, real_path, FALSE);
2706 mailimap_list_result_free(lep_list);
2708 for (cur = sub_list; cur != NULL; cur = cur->next) {
2709 FolderItem *cur_item = FOLDER_ITEM(cur->data);
2710 GList *oldlitem = NULL;
2711 gchar *tmp = imap_get_real_path(session,
2712 IMAP_FOLDER(folder), cur_item->path, &r);
2715 statusbar_pop_all();
2718 folder_item_destroy(cur_item);
2719 oldlitem = g_list_find_custom(
2720 child_list, tmp, (GCompareFunc)strcmp2);
2722 child_list = g_list_remove_link(child_list, oldlitem);
2723 g_free(oldlitem->data);
2724 g_list_free(oldlitem);
2731 statusbar_pop_all();
2736 static gint imap_create_tree(Folder *folder)
2738 g_return_val_if_fail(folder != NULL, -1);
2739 g_return_val_if_fail(folder->node != NULL, -1);
2740 g_return_val_if_fail(folder->node->data != NULL, -1);
2741 g_return_val_if_fail(folder->account != NULL, -1);
2743 imap_scan_tree(folder);
2744 imap_create_missing_folders(folder);
2749 static void imap_create_missing_folders(Folder *folder)
2751 g_return_if_fail(folder != NULL);
2754 folder->inbox = imap_create_special_folder
2755 (folder, F_INBOX, "INBOX");
2757 folder->trash = imap_create_special_folder
2758 (folder, F_TRASH, "Trash");
2760 folder->queue = imap_create_special_folder
2761 (folder, F_QUEUE, "Queue");
2762 if (!folder->outbox)
2763 folder->outbox = imap_create_special_folder
2764 (folder, F_OUTBOX, "Sent");
2766 folder->draft = imap_create_special_folder
2767 (folder, F_DRAFT, "Drafts");
2770 static FolderItem *imap_create_special_folder(Folder *folder,
2771 SpecialFolderItemType stype,
2775 FolderItem *new_item;
2777 g_return_val_if_fail(folder != NULL, NULL);
2778 g_return_val_if_fail(folder->node != NULL, NULL);
2779 g_return_val_if_fail(folder->node->data != NULL, NULL);
2780 g_return_val_if_fail(folder->account != NULL, NULL);
2781 g_return_val_if_fail(name != NULL, NULL);
2783 item = FOLDER_ITEM(folder->node->data);
2784 new_item = imap_create_folder(folder, item, name);
2787 g_warning("Can't create '%s'\n", name);
2788 if (!folder->inbox) return NULL;
2790 new_item = imap_create_folder(folder, folder->inbox, name);
2792 g_warning("Can't create '%s' under INBOX\n", name);
2794 new_item->stype = stype;
2796 new_item->stype = stype;
2801 static gchar *imap_folder_get_path(Folder *folder)
2805 g_return_val_if_fail(folder != NULL, NULL);
2806 g_return_val_if_fail(folder->account != NULL, NULL);
2808 folder_path = g_strconcat(get_imap_cache_dir(),
2810 folder->account->recv_server,
2812 folder->account->userid,
2819 static gchar *imap_encode_unsafe_chars(const gchar *str)
2821 gchar *ret = NULL, *o_ret;
2825 ret = g_malloc(3*strlen(str)+1);
2827 for (i = str; *i; i++) {
2837 *ret++ = '0'+(*i/10);
2838 *ret++ = '0'+(*i%10);
2848 static gchar *imap_item_get_path(Folder *folder, FolderItem *item)
2850 gchar *folder_path, *path;
2851 gchar *item_path = NULL;
2853 g_return_val_if_fail(folder != NULL, NULL);
2854 g_return_val_if_fail(item != NULL, NULL);
2855 folder_path = imap_folder_get_path(folder);
2857 g_return_val_if_fail(folder_path != NULL, NULL);
2860 item_path = g_strdup(item->path);
2862 item_path = imap_encode_unsafe_chars(item->path);
2865 if (g_path_is_absolute(folder_path)) {
2867 path = g_strconcat(folder_path, G_DIR_SEPARATOR_S,
2870 path = g_strdup(folder_path);
2873 path = g_strconcat(get_home_dir(), G_DIR_SEPARATOR_S,
2874 folder_path, G_DIR_SEPARATOR_S,
2877 path = g_strconcat(get_home_dir(), G_DIR_SEPARATOR_S,
2880 g_free(folder_path);
2883 while (strchr(path, '/'))
2884 *strchr(path, '/') = '\\';
2890 static FolderItem *imap_create_folder(Folder *folder, FolderItem *parent,
2893 gchar *dirpath, *imap_path;
2894 IMAPSession *session;
2895 FolderItem *new_item;
2899 gint ok = MAILIMAP_NO_ERROR;
2900 gboolean no_select = FALSE, no_sub = FALSE;
2901 gboolean exist = FALSE;
2903 g_return_val_if_fail(folder != NULL, NULL);
2904 g_return_val_if_fail(folder->account != NULL, NULL);
2905 g_return_val_if_fail(parent != NULL, NULL);
2906 g_return_val_if_fail(name != NULL, NULL);
2908 debug_print("getting session...\n");
2909 session = imap_session_get(folder);
2914 if (!folder_item_parent(parent) && strcmp(name, "INBOX") == 0) {
2915 dirpath = g_strdup(name);
2916 }else if (parent->path)
2917 dirpath = g_strconcat(parent->path, "/", name, NULL);
2918 else if ((p = strchr(name, '/')) != NULL && *(p + 1) != '\0')
2919 dirpath = g_strdup(name);
2920 else if (folder->account->imap_dir && *folder->account->imap_dir) {
2923 Xstrdup_a(imap_dir, folder->account->imap_dir, {return NULL;});
2924 strtailchomp(imap_dir, '/');
2925 dirpath = g_strconcat(imap_dir, "/", name, NULL);
2927 dirpath = g_strdup(name);
2931 /* keep trailing directory separator to create a folder that contains
2933 imap_path = imap_utf8_to_modified_utf7(dirpath, FALSE);
2935 strtailchomp(dirpath, '/');
2936 Xstrdup_a(new_name, name, {
2941 separator = imap_get_path_separator(session, IMAP_FOLDER(folder), imap_path, &ok);
2946 imap_path_separator_subst(imap_path, separator);
2947 /* remove trailing / for display */
2948 strtailchomp(new_name, '/');
2950 if (strcmp(dirpath, "INBOX") != 0) {
2955 argbuf = g_ptr_array_new();
2956 r = imap_threaded_list(folder, "", imap_path, &lep_list);
2957 if (r != MAILIMAP_NO_ERROR) {
2958 imap_handle_error(SESSION(session), NULL, r);
2959 log_warning(LOG_PROTOCOL, _("can't create mailbox: LIST failed\n"));
2962 ptr_array_free_strings(argbuf);
2963 g_ptr_array_free(argbuf, TRUE);
2967 if (clist_count(lep_list) > 0)
2969 mailimap_list_result_free(lep_list);
2972 ok = imap_cmd_create(session, imap_path);
2973 if (ok != MAILIMAP_NO_ERROR) {
2974 log_warning(LOG_PROTOCOL, _("can't create mailbox\n"));
2979 r = imap_threaded_list(folder, "", imap_path, &lep_list);
2980 if (r == MAILIMAP_NO_ERROR) {
2981 GSList *item_list = imap_list_from_lep(IMAP_FOLDER(folder),
2982 lep_list, dirpath, TRUE);
2984 FolderItem *cur_item = FOLDER_ITEM(item_list->data);
2985 no_select = cur_item->no_select;
2986 no_sub = cur_item->no_sub;
2987 g_slist_free(item_list);
2989 mailimap_list_result_free(lep_list);
2991 imap_handle_error(SESSION(session), NULL, r);
2994 imap_threaded_subscribe(folder, imap_path, TRUE);
2998 /* just get flags */
2999 r = imap_threaded_list(folder, "", "INBOX", &lep_list);
3000 if (r == MAILIMAP_NO_ERROR) {
3001 GSList *item_list = imap_list_from_lep(IMAP_FOLDER(folder),
3002 lep_list, dirpath, TRUE);
3004 FolderItem *cur_item = FOLDER_ITEM(item_list->data);
3005 no_select = cur_item->no_select;
3006 no_sub = cur_item->no_sub;
3007 g_slist_free(item_list);
3009 mailimap_list_result_free(lep_list);
3011 imap_handle_error(SESSION(session), NULL, r);
3015 new_item = folder_item_new(folder, new_name, dirpath);
3016 new_item->no_select = no_select;
3017 new_item->no_sub = no_sub;
3018 folder_item_append(parent, new_item);
3022 dirpath = folder_item_get_path(new_item);
3023 if (!is_dir_exist(dirpath))
3024 make_dir_hier(dirpath);
3028 /* folder existed, scan it */
3029 imap_scan_required(folder, new_item);
3030 folder_item_scan_full(new_item, FALSE);
3036 static gint imap_rename_folder(Folder *folder, FolderItem *item,
3041 gchar *real_oldpath;
3042 gchar *real_newpath;
3044 gchar *old_cache_dir;
3045 gchar *new_cache_dir;
3046 IMAPSession *session;
3048 gint ok = MAILIMAP_NO_ERROR;
3049 gint exists, recent, unseen;
3050 guint32 uid_validity;
3052 g_return_val_if_fail(folder != NULL, -1);
3053 g_return_val_if_fail(item != NULL, -1);
3054 g_return_val_if_fail(item->path != NULL, -1);
3055 g_return_val_if_fail(name != NULL, -1);
3057 debug_print("getting session...\n");
3058 session = imap_session_get(folder);
3063 if (strchr(name, imap_get_path_separator(session, IMAP_FOLDER(folder), item->path, &ok)) != NULL ||
3065 g_warning(_("New folder name must not contain the namespace "
3070 real_oldpath = imap_get_real_path(session, IMAP_FOLDER(folder), item->path, &ok);
3075 g_free(session->mbox);
3076 session->mbox = NULL;
3077 session->exists = 0;
3078 session->recent = 0;
3079 session->expunge = 0;
3080 ok = imap_cmd_examine(session, "INBOX",
3081 &exists, &recent, &unseen, &uid_validity, FALSE);
3082 if (ok != MAILIMAP_NO_ERROR) {
3083 g_free(real_oldpath);
3087 separator = imap_get_path_separator(session, IMAP_FOLDER(folder), item->path, &ok);
3090 if (strchr(item->path, G_DIR_SEPARATOR)) {
3091 dirpath = g_path_get_dirname(item->path);
3092 newpath = g_strconcat(dirpath, G_DIR_SEPARATOR_S, name, NULL);
3095 newpath = g_strdup(name);
3097 real_newpath = imap_utf8_to_modified_utf7(newpath, FALSE);
3098 imap_path_separator_subst(real_newpath, separator);
3100 ok = imap_cmd_rename(session, real_oldpath, real_newpath);
3101 if (ok != MAILIMAP_NO_ERROR) {
3102 log_warning(LOG_PROTOCOL, _("can't rename mailbox: %s to %s\n"),
3103 real_oldpath, real_newpath);
3104 g_free(real_oldpath);
3106 g_free(real_newpath);
3110 item->name = g_strdup(name);
3112 old_cache_dir = folder_item_get_path(item);
3114 paths[0] = g_strdup(item->path);
3116 g_node_traverse(item->node, G_PRE_ORDER, G_TRAVERSE_ALL, -1,
3117 imap_rename_folder_func, paths);
3119 if (is_dir_exist(old_cache_dir)) {
3120 new_cache_dir = folder_item_get_path(item);
3121 if (g_rename(old_cache_dir, new_cache_dir) < 0) {
3122 FILE_OP_ERROR(old_cache_dir, "rename");
3124 g_free(new_cache_dir);
3127 g_free(old_cache_dir);
3130 g_free(real_oldpath);
3131 g_free(real_newpath);
3135 gint imap_subscribe(Folder *folder, FolderItem *item, gchar *rpath, gboolean sub)
3138 gint r = MAILIMAP_NO_ERROR;
3139 IMAPSession *session;
3140 debug_print("getting session...\n");
3142 session = imap_session_get(folder);
3146 if (item && item->path) {
3147 path = imap_get_real_path(session, IMAP_FOLDER(folder), item->path, &r);
3154 if (!strcmp(path, "INBOX") && sub == FALSE) {
3158 debug_print("%ssubscribing %s\n", sub?"":"un", path);
3159 r = imap_threaded_subscribe(folder, path, sub);
3162 r = imap_threaded_subscribe(folder, rpath, sub);
3168 static gint imap_remove_folder_real(Folder *folder, FolderItem *item)
3170 gint ok = MAILIMAP_NO_ERROR;
3171 IMAPSession *session;
3174 gboolean selected_folder;
3176 g_return_val_if_fail(folder != NULL, -1);
3177 g_return_val_if_fail(item != NULL, -1);
3178 g_return_val_if_fail(item->path != NULL, -1);
3180 debug_print("getting session...\n");
3181 session = imap_session_get(folder);
3185 path = imap_get_real_path(session, IMAP_FOLDER(folder), item->path, &ok);
3189 imap_threaded_subscribe(folder, path, FALSE);
3191 selected_folder = (session->mbox != NULL) &&
3192 (!strcmp(session->mbox, item->path));
3193 if (selected_folder) {
3194 ok = imap_cmd_close(session);
3195 if (ok != MAILIMAP_NO_ERROR) {
3196 debug_print("close err %d\n", ok);
3200 ok = imap_cmd_delete(session, path);
3201 if (ok != MAILIMAP_NO_ERROR && !is_fatal(ok)) {
3204 ok = MAILIMAP_NO_ERROR;
3205 tmp = g_strdup_printf("%s%c", path,
3206 imap_get_path_separator(session, IMAP_FOLDER(folder), path, &ok));
3210 ok = imap_cmd_delete(session, path);
3213 if (ok != MAILIMAP_NO_ERROR) {
3214 log_warning(LOG_PROTOCOL, _("can't delete mailbox\n"));
3220 cache_dir = folder_item_get_path(item);
3221 if (is_dir_exist(cache_dir) && remove_dir_recursive(cache_dir) < 0)
3222 g_warning("can't remove directory '%s'\n", cache_dir);
3224 folder_item_remove(item);
3228 static gint imap_remove_folder(Folder *folder, FolderItem *item)
3232 g_return_val_if_fail(item != NULL, -1);
3233 g_return_val_if_fail(item->folder != NULL, -1);
3234 g_return_val_if_fail(item->node != NULL, -1);
3236 node = item->node->children;
3237 while (node != NULL) {
3239 if (imap_remove_folder(folder, FOLDER_ITEM(node->data)) < 0)
3243 debug_print("IMAP removing %s\n", item->path);
3245 if (imap_remove_all_msg(folder, item) < 0)
3247 return imap_remove_folder_real(folder, item);
3250 typedef struct _uncached_data {
3251 IMAPSession *session;
3253 MsgNumberList *numlist;
3260 static void *imap_get_uncached_messages_thread(void *data)
3262 uncached_data *stuff = (uncached_data *)data;
3263 IMAPSession *session = stuff->session;
3264 FolderItem *item = stuff->item;
3265 MsgNumberList *numlist = stuff->numlist;
3266 GSList *newlist = NULL;
3267 GSList *llast = NULL;
3268 GSList *seq_list, *cur;
3269 gboolean got_alien_tags = FALSE;
3271 debug_print("uncached_messages\n");
3273 if (session == NULL || item == NULL || item->folder == NULL
3274 || FOLDER_CLASS(item->folder) != &imap_class) {
3279 seq_list = imap_get_lep_set_from_numlist(IMAP_FOLDER(item->folder), numlist);
3280 debug_print("get msgs info\n");
3281 for (cur = seq_list; cur != NULL; cur = g_slist_next(cur)) {
3282 struct mailimap_set * imapset;
3288 if (session->cancelled)
3291 imapset = cur->data;
3293 r = imap_threaded_fetch_env(session->folder,
3294 imapset, &env_list);
3295 if (r != MAILIMAP_NO_ERROR) {
3296 imap_handle_error(SESSION(session), NULL, r);
3304 session_set_access_time(SESSION(session));
3307 for(i = 0 ; i < carray_count(env_list) ; i += 2) {
3308 struct imap_fetch_env_info * info;
3310 GSList *tags = NULL, *cur = NULL;
3311 info = carray_get(env_list, i);
3312 tags = carray_get(env_list, i+1);
3313 msginfo = imap_envelope_from_lep(info, item);
3314 if (msginfo == NULL) {
3315 slist_free_strings(tags);
3319 g_slist_free(msginfo->tags);
3320 msginfo->tags = NULL;
3322 for (cur = tags; cur; cur = cur->next) {
3323 gchar *real_tag = imap_modified_utf7_to_utf8(cur->data, TRUE);
3325 id = tags_get_id_for_str(real_tag);
3327 id = tags_add_tag(real_tag);
3328 got_alien_tags = TRUE;
3330 if (!g_slist_find(msginfo->tags, GINT_TO_POINTER(id))) {
3331 msginfo->tags = g_slist_prepend(
3333 GINT_TO_POINTER(id));
3338 msginfo->tags = g_slist_reverse(msginfo->tags);
3339 slist_free_strings(tags);
3341 msginfo->folder = item;
3343 llast = newlist = g_slist_append(newlist, msginfo);
3345 llast = g_slist_append(llast, msginfo);
3346 llast = llast->next;
3351 imap_fetch_env_free(env_list);
3354 if (got_alien_tags) {
3356 main_window_reflect_tags_changes(mainwindow_get_mainwindow());
3359 for (cur = seq_list; cur != NULL; cur = g_slist_next(cur)) {
3360 struct mailimap_set * imapset;
3362 imapset = cur->data;
3363 mailimap_set_free(imapset);
3366 session_set_access_time(SESSION(session));
3371 #define MAX_MSG_NUM 50
3373 static GSList *imap_get_uncached_messages(IMAPSession *session,
3375 MsgNumberList *numlist,
3378 GSList *result = NULL;
3380 uncached_data *data = g_new0(uncached_data, 1);
3383 data->total = g_slist_length(numlist);
3384 data->ok = MAILIMAP_NO_ERROR;
3385 debug_print("messages list : %i\n", data->total);
3387 while (cur != NULL) {
3388 GSList * partial_result;
3396 while (count < MAX_MSG_NUM) {
3401 if (newlist == NULL)
3402 llast = newlist = g_slist_append(newlist, p);
3404 llast = g_slist_append(llast, p);
3405 llast = llast->next;
3415 data->session = session;
3417 data->numlist = newlist;
3420 if (prefs_common.work_offline &&
3421 !inc_offline_should_override(FALSE,
3422 _("Claws Mail needs network access in order "
3423 "to access the IMAP server."))) {
3429 (GSList *)imap_get_uncached_messages_thread(data);
3431 if (data->ok != MAILIMAP_NO_ERROR) {
3434 statusbar_progress_all(data->cur,data->total, 1);
3436 g_slist_free(newlist);
3438 result = g_slist_concat(result, partial_result);
3443 statusbar_progress_all(0,0,0);
3444 statusbar_pop_all();
3449 static void imap_delete_all_cached_messages(FolderItem *item)
3453 g_return_if_fail(item != NULL);
3454 g_return_if_fail(item->folder != NULL);
3455 g_return_if_fail(FOLDER_CLASS(item->folder) == &imap_class);
3457 debug_print("Deleting all cached messages...\n");
3459 dir = folder_item_get_path(item);
3460 if (is_dir_exist(dir))
3461 remove_all_numbered_files(dir);
3464 debug_print("done.\n");
3467 gchar imap_get_path_separator_for_item(FolderItem *item)
3469 Folder *folder = NULL;
3470 IMAPFolder *imap_folder = NULL;
3471 IMAPSession *session = NULL;
3473 gint ok = MAILIMAP_NO_ERROR;
3476 folder = item->folder;
3481 imap_folder = IMAP_FOLDER(folder);
3486 debug_print("getting session...");
3487 session = imap_session_get(FOLDER(folder));
3488 result = imap_get_path_separator(session, imap_folder, item->path, &ok);
3492 static gchar imap_refresh_path_separator(IMAPSession *session, IMAPFolder *folder, const gchar *subfolder, gint *ok)
3496 gchar separator = '\0';
3498 g_return_val_if_fail(session != NULL, '/');
3499 r = imap_threaded_list((Folder *)folder, "", subfolder, &lep_list);
3501 if (r != MAILIMAP_NO_ERROR) {
3502 imap_handle_error(SESSION(session), NULL, r);
3503 log_warning(LOG_PROTOCOL, _("LIST failed\n"));
3508 if (lep_list != NULL && clist_count(lep_list) > 0) {
3509 clistiter * iter = clist_begin(lep_list);
3510 struct mailimap_mailbox_list * mb;
3511 mb = clist_content(iter);
3513 separator = mb->mb_delimiter;
3514 debug_print("got separator: %c\n", folder->last_seen_separator);
3516 *ok = MAILIMAP_NO_ERROR;
3517 mailimap_list_result_free(lep_list);
3521 static gchar imap_get_path_separator(IMAPSession *session, IMAPFolder *folder, const gchar *path, gint *ok)
3523 gchar separator = '/';
3524 *ok = MAILIMAP_NO_ERROR;
3525 if (folder->last_seen_separator == 0) {
3526 folder->last_seen_separator = imap_refresh_path_separator(session, folder, "", ok);
3529 if (folder->last_seen_separator == 0) {
3530 folder->last_seen_separator = imap_refresh_path_separator(session, folder, "INBOX", ok);
3533 if (folder->last_seen_separator != 0) {
3534 debug_print("using separator: %c\n", folder->last_seen_separator);
3535 return folder->last_seen_separator;
3541 static gchar *imap_get_real_path(IMAPSession *session, IMAPFolder *folder, const gchar *path, gint *ok)
3543 gchar *real_path = NULL;
3546 g_return_val_if_fail(folder != NULL, NULL);
3547 g_return_val_if_fail(path != NULL, NULL);
3549 *ok = MAILIMAP_NO_ERROR;
3551 real_path = imap_utf8_to_modified_utf7(path, FALSE);
3552 separator = imap_get_path_separator(session, folder, path, ok);
3553 if (*ok == MAILIMAP_NO_ERROR)
3554 imap_path_separator_subst(real_path, separator);
3559 static gint imap_set_message_flags(IMAPSession *session,
3560 IMAPFolderItem *item,
3561 MsgNumberList *numlist,
3570 IMAPFolder *folder = NULL;
3571 GSList *sorted_list = NULL;
3573 if (numlist == NULL || session == NULL)
3574 return MAILIMAP_ERROR_BAD_STATE;
3576 folder = IMAP_FOLDER(session->folder);
3578 sorted_list = g_slist_copy(numlist);
3579 sorted_list = g_slist_sort(sorted_list, g_int_compare);
3581 cur = g_slist_last(sorted_list);
3584 total = GPOINTER_TO_INT(cur->data);
3586 seq_list = imap_get_lep_set_from_numlist(IMAP_FOLDER(session->folder), sorted_list);
3588 statusbar_print_all(_("Flagging messages..."));
3590 for(cur = seq_list ; cur != NULL ; cur = g_slist_next(cur)) {
3591 struct mailimap_set * imapset = (struct mailimap_set *)cur->data;
3592 struct mailimap_set_item *set_item = NULL;
3594 if (imapset->set_list)
3595 set_item = clist_content(clist_begin(imapset->set_list));
3599 if (set_item == NULL)
3602 statusbar_progress_all(set_item->set_first, total, 1);
3604 ok = imap_cmd_store(session, item, imapset,
3605 flags, tags, is_set);
3606 statusbar_progress_all(set_item->set_last, total, 1);
3607 if (ok != MAILIMAP_NO_ERROR && folder->max_set_size > 20) {
3608 /* reduce max set size */
3609 folder->max_set_size /= 2;
3611 if (ok != MAILIMAP_NO_ERROR && is_fatal(ok)) {
3616 g_slist_free(sorted_list);
3618 statusbar_progress_all(0,0,0);
3619 statusbar_pop_all();
3621 imap_lep_set_free(seq_list);
3626 typedef struct _select_data {
3627 IMAPSession *session;
3632 guint32 *uid_validity;
3636 static gint imap_select(IMAPSession *session, IMAPFolder *folder,
3638 gint *exists, gint *recent, gint *unseen,
3639 guint32 *uid_validity, gint *can_create_flags,
3643 gint ok = MAILIMAP_NO_ERROR;
3644 gint exists_, recent_, unseen_;
3645 guint32 uid_validity_;
3646 gint can_create_flags_;
3647 const gchar *path = item ? item->path:NULL;
3650 return MAILIMAP_ERROR_BAD_STATE;
3653 if (!exists && !recent && !unseen && !uid_validity && !can_create_flags) {
3654 if (session->mbox && strcmp(session->mbox, path) == 0)
3655 return MAILIMAP_NO_ERROR;
3657 if (!exists && !recent && !unseen && !uid_validity && can_create_flags) {
3658 if (session->mbox && strcmp(session->mbox, path) == 0) {
3659 if (IMAP_FOLDER_ITEM(item)->can_create_flags != ITEM_CAN_CREATE_FLAGS_UNKNOWN)
3660 return MAILIMAP_NO_ERROR;
3670 uid_validity = &uid_validity_;
3671 if (!can_create_flags)
3672 can_create_flags = &can_create_flags_;
3674 g_free(session->mbox);
3675 session->mbox = NULL;
3676 session->exists = 0;
3677 session->recent = 0;
3678 session->expunge = 0;
3680 real_path = imap_get_real_path(session, folder, path, &ok);
3683 g_slist_free(IMAP_FOLDER_ITEM(item)->ok_flags);
3684 IMAP_FOLDER_ITEM(item)->ok_flags = NULL;
3685 ok = imap_cmd_select(session, real_path,
3686 exists, recent, unseen, uid_validity, can_create_flags,
3687 &(IMAP_FOLDER_ITEM(item)->ok_flags), block);
3688 if (ok != MAILIMAP_NO_ERROR) {
3689 log_warning(LOG_PROTOCOL, _("can't select folder: %s\n"), real_path);
3691 session->mbox = g_strdup(path);
3692 session->folder_content_changed = FALSE;
3693 session->exists = *exists;
3694 session->recent = *recent;
3695 session->expunge = 0;
3696 session->unseen = *unseen;
3697 session->uid_validity = *uid_validity;
3698 debug_print("select: exists %d recent %d expunge %d uid_validity %d can_create_flags %d\n",
3699 session->exists, session->recent, session->expunge,
3700 session->uid_validity, *can_create_flags);
3702 if (*can_create_flags) {
3703 IMAP_FOLDER_ITEM(item)->can_create_flags = ITEM_CAN_CREATE_FLAGS;
3705 IMAP_FOLDER_ITEM(item)->can_create_flags = ITEM_CANNOT_CREATE_FLAGS;
3712 static gint imap_status(IMAPSession *session, IMAPFolder *folder,
3713 const gchar *path, IMAPFolderItem *item,
3715 guint32 *uid_next, guint32 *uid_validity,
3716 gint *unseen, gboolean block)
3718 int r = MAILIMAP_NO_ERROR;
3720 struct mailimap_mailbox_data_status * data_status;
3725 real_path = imap_get_real_path(session, folder, path, &r);
3747 if (session->mbox != NULL &&
3748 !strcmp(session->mbox, item->item.path)) {
3749 r = imap_cmd_close(session);
3750 if (r != MAILIMAP_NO_ERROR) {
3751 debug_print("close err %d\n", r);
3757 r = imap_threaded_status(FOLDER(folder), real_path,
3758 &data_status, mask);
3761 if (r != MAILIMAP_NO_ERROR) {
3762 imap_handle_error(SESSION(session), NULL, r);
3763 debug_print("status err %d\n", r);
3767 if (data_status == NULL || data_status->st_info_list == NULL) {
3768 debug_print("data_status %p\n", data_status);
3770 debug_print("data_status->st_info_list %p\n", data_status->st_info_list);
3771 mailimap_mailbox_data_status_free(data_status);
3773 return MAILIMAP_ERROR_BAD_STATE;
3777 if (data_status->st_info_list) {
3778 for(iter = clist_begin(data_status->st_info_list) ; iter != NULL ;
3779 iter = clist_next(iter)) {
3780 struct mailimap_status_info * info;
3782 info = clist_content(iter);
3783 switch (info->st_att) {
3784 case MAILIMAP_STATUS_ATT_MESSAGES:
3786 * messages = info->st_value;
3787 got_values |= 1 << 0;
3791 case MAILIMAP_STATUS_ATT_UIDNEXT:
3793 * uid_next = info->st_value;
3794 got_values |= 1 << 2;
3798 case MAILIMAP_STATUS_ATT_UIDVALIDITY:
3800 * uid_validity = info->st_value;
3801 got_values |= 1 << 3;
3805 case MAILIMAP_STATUS_ATT_UNSEEN:
3807 * unseen = info->st_value;
3808 got_values |= 1 << 4;
3814 mailimap_mailbox_data_status_free(data_status);
3816 if (got_values != mask) {
3817 g_warning("status: incomplete values received (%d)\n", got_values);
3819 return MAILIMAP_NO_ERROR;
3822 static void imap_free_capabilities(IMAPSession *session)
3824 slist_free_strings(session->capability);
3825 g_slist_free(session->capability);
3826 session->capability = NULL;
3829 /* low-level IMAP4rev1 commands */
3831 static gint imap_cmd_login(IMAPSession *session,
3832 const gchar *user, const gchar *pass,
3838 if (!strcmp(type, "LOGIN") && imap_has_capability(session, "LOGINDISABLED")) {
3839 gint ok = MAILIMAP_ERROR_BAD_STATE;
3840 if (imap_has_capability(session, "STARTTLS")) {
3842 log_warning(LOG_PROTOCOL, _("Server requires TLS to log in.\n"));
3843 ok = imap_cmd_starttls(session);
3844 if (ok != MAILIMAP_NO_ERROR) {
3845 log_warning(LOG_PROTOCOL, _("Can't start TLS session.\n"));
3849 imap_free_capabilities(session);
3850 if ((r = imap_get_capabilities(session)) != MAILIMAP_NO_ERROR) {
3851 imap_handle_error(SESSION(session), NULL, r);
3852 log_warning(LOG_PROTOCOL, _("Can't refresh capabilities.\n"));
3857 log_error(LOG_PROTOCOL, _("Connection to %s failed: "
3858 "server requires TLS, but Claws Mail "
3859 "has been compiled without OpenSSL "
3861 SESSION(session)->server);
3862 return MAILIMAP_ERROR_LOGIN;
3865 log_error(LOG_PROTOCOL, _("Server logins are disabled.\n"));
3866 return MAILIMAP_ERROR_LOGIN;
3870 log_print(LOG_PROTOCOL, "IMAP4> Logging %s to %s using %s\n",
3872 SESSION(session)->server,
3874 r = imap_threaded_login(session->folder, user, pass, type);
3875 if (r != MAILIMAP_NO_ERROR) {
3876 imap_handle_error(SESSION(session), NULL, r);
3877 log_print(LOG_PROTOCOL, "IMAP4< Error logging in to %s\n",
3878 SESSION(session)->server);
3881 log_print(LOG_PROTOCOL, "IMAP4< Login to %s successful\n",
3882 SESSION(session)->server);
3883 ok = MAILIMAP_NO_ERROR;
3888 static gint imap_cmd_noop(IMAPSession *session)
3891 unsigned int exists, recent, expunge, unseen, uidnext, uidval;
3893 r = imap_threaded_noop(session->folder, &exists, &recent, &expunge, &unseen, &uidnext, &uidval);
3894 if (r != MAILIMAP_NO_ERROR) {
3895 imap_handle_error(SESSION(session), NULL, r);
3896 debug_print("noop err %d\n", r);
3900 session->folder_content_changed = FALSE;
3902 if ((exists && exists != session->exists)
3903 || (recent && recent != session->recent)
3904 || (expunge && expunge != session->expunge)
3905 || (unseen && unseen != session->unseen)) {
3906 session->folder_content_changed = TRUE;
3908 if (uidnext != 0 && uidnext != session->uid_next) {
3909 session->uid_next = uidnext;
3910 session->folder_content_changed = TRUE;
3912 if (uidval != 0 && uidval != session->uid_validity) {
3913 session->uid_validity = uidval;
3914 session->folder_content_changed = TRUE;
3917 session->exists = exists;
3918 session->recent = recent;
3919 session->expunge = expunge;
3920 session->unseen = unseen;
3922 session_set_access_time(SESSION(session));
3924 return MAILIMAP_NO_ERROR;
3928 static gint imap_cmd_starttls(IMAPSession *session)
3932 r = imap_threaded_starttls(session->folder,
3933 SESSION(session)->server, SESSION(session)->port);
3934 if (r != MAILIMAP_NO_ERROR) {
3935 imap_handle_error(SESSION(session), NULL, r);
3936 debug_print("starttls err %d\n", r);
3939 return MAILIMAP_NO_ERROR;
3943 static gint imap_cmd_select(IMAPSession *session, const gchar *folder,
3944 gint *exists, gint *recent, gint *unseen,
3945 guint32 *uid_validity, gint *can_create_flags,
3946 GSList **ok_flags, gboolean block)
3950 r = imap_threaded_select(session->folder, folder,
3951 exists, recent, unseen, uid_validity, can_create_flags, ok_flags);
3952 if (r != MAILIMAP_NO_ERROR) {
3953 imap_handle_error(SESSION(session), NULL, r);
3954 debug_print("select err %d\n", r);
3957 return MAILIMAP_NO_ERROR;
3960 static gint imap_cmd_close(IMAPSession *session)
3964 r = imap_threaded_close(session->folder);
3965 if (r != MAILIMAP_NO_ERROR) {
3966 imap_handle_error(SESSION(session), NULL, r);
3967 debug_print("close err %d\n", r);
3970 g_free(session->mbox);
3971 session->mbox = NULL;
3972 session->exists = 0;
3973 session->recent = 0;
3974 session->expunge = 0;
3975 return MAILIMAP_NO_ERROR;
3978 static gint imap_cmd_examine(IMAPSession *session, const gchar *folder,
3979 gint *exists, gint *recent, gint *unseen,
3980 guint32 *uid_validity, gboolean block)
3984 r = imap_threaded_examine(session->folder, folder,
3985 exists, recent, unseen, uid_validity);
3986 if (r != MAILIMAP_NO_ERROR) {
3987 imap_handle_error(SESSION(session), NULL, r);
3988 debug_print("examine err %d\n", r);
3992 return MAILIMAP_NO_ERROR;
3995 static gint imap_cmd_create(IMAPSession *session, const gchar *folder)
3999 r = imap_threaded_create(session->folder, folder);
4000 if (r != MAILIMAP_NO_ERROR) {
4001 imap_handle_error(SESSION(session), NULL, r);
4005 return MAILIMAP_NO_ERROR;
4008 static gint imap_cmd_rename(IMAPSession *session, const gchar *old_folder,
4009 const gchar *new_folder)
4013 r = imap_threaded_rename(session->folder, old_folder,
4015 if (r != MAILIMAP_NO_ERROR) {
4016 imap_handle_error(SESSION(session), NULL, r);
4020 return MAILIMAP_NO_ERROR;
4023 static gint imap_cmd_delete(IMAPSession *session, const gchar *folder)
4028 r = imap_threaded_delete(session->folder, folder);
4029 if (r != MAILIMAP_NO_ERROR) {
4030 imap_handle_error(SESSION(session), NULL, r);
4034 return MAILIMAP_NO_ERROR;
4037 typedef struct _fetch_data {
4038 IMAPSession *session;
4040 const gchar *filename;
4046 static void *imap_cmd_fetch_thread(void *data)
4048 fetch_data *stuff = (fetch_data *)data;
4049 IMAPSession *session = stuff->session;
4050 guint32 uid = stuff->uid;
4051 const gchar *filename = stuff->filename;
4055 r = imap_threaded_fetch_content(session->folder,
4059 r = imap_threaded_fetch_content(session->folder,
4062 if (r != MAILIMAP_NO_ERROR) {
4063 imap_handle_error(SESSION(session), NULL, r);
4064 debug_print("fetch err %d\n", r);
4065 return GINT_TO_POINTER(r);
4067 return GINT_TO_POINTER(MAILIMAP_NO_ERROR);
4070 static gint imap_cmd_fetch(IMAPSession *session, guint32 uid,
4071 const gchar *filename, gboolean headers,
4074 fetch_data *data = g_new0(fetch_data, 1);
4077 data->session = session;
4079 data->filename = filename;
4080 data->headers = headers;
4083 if (prefs_common.work_offline &&
4084 !inc_offline_should_override(FALSE,
4085 _("Claws Mail needs network access in order "
4086 "to access the IMAP server."))) {
4090 statusbar_print_all(_("Fetching message..."));
4091 result = GPOINTER_TO_INT(imap_cmd_fetch_thread(data));
4092 statusbar_pop_all();
4098 static gint imap_cmd_append(IMAPSession *session,
4099 IMAPFolderItem *item,
4100 const gchar *destfolder,
4101 const gchar *file, IMAPFlags flags,
4104 struct mailimap_flag_list * flag_list;
4107 cm_return_val_if_fail(file != NULL, MAILIMAP_ERROR_BAD_STATE);
4109 flag_list = imap_flag_to_lep(item, flags, NULL);
4110 lock_session(session);
4111 r = imap_threaded_append(session->folder, destfolder,
4112 file, flag_list, (int *)new_uid);
4113 mailimap_flag_list_free(flag_list);
4115 if (r != MAILIMAP_NO_ERROR) {
4116 imap_handle_error(SESSION(session), NULL, r);
4117 debug_print("append err %d\n", r);
4121 unlock_session(session);
4123 return MAILIMAP_NO_ERROR;
4126 static gint imap_cmd_copy(IMAPSession *session, struct mailimap_set * set,
4127 const gchar *destfolder,
4128 struct mailimap_set **source, struct mailimap_set **dest)
4132 g_return_val_if_fail(session != NULL, MAILIMAP_ERROR_BAD_STATE);
4133 g_return_val_if_fail(set != NULL, MAILIMAP_ERROR_BAD_STATE);
4134 g_return_val_if_fail(destfolder != NULL, MAILIMAP_ERROR_BAD_STATE);
4136 r = imap_threaded_copy(session->folder, set, destfolder, source, dest);
4137 if (r != MAILIMAP_NO_ERROR) {
4138 imap_handle_error(SESSION(session), NULL, r);
4142 return MAILIMAP_NO_ERROR;
4145 static gint imap_cmd_store(IMAPSession *session,
4146 IMAPFolderItem *item,
4147 struct mailimap_set * set,
4148 IMAPFlags flags, GSList *tags, int do_add)
4151 struct mailimap_flag_list * flag_list = NULL;
4152 struct mailimap_store_att_flags * store_att_flags;
4154 flag_list = imap_flag_to_lep(item, flags, tags);
4158 mailimap_store_att_flags_new_add_flags_silent(flag_list);
4161 mailimap_store_att_flags_new_remove_flags_silent(flag_list);
4163 r = imap_threaded_store(session->folder, set, store_att_flags);
4164 mailimap_store_att_flags_free(store_att_flags);
4165 if (r != MAILIMAP_NO_ERROR) {
4166 imap_handle_error(SESSION(session), NULL, r);
4170 return MAILIMAP_NO_ERROR;
4173 static gint imap_cmd_expunge(IMAPSession *session, gboolean do_expunge)
4178 return MAILIMAP_NO_ERROR;
4180 if (prefs_common.work_offline &&
4181 !inc_offline_should_override(FALSE,
4182 _("Claws Mail needs network access in order "
4183 "to access the IMAP server."))) {
4187 r = imap_threaded_expunge(session->folder);
4188 if (r != MAILIMAP_NO_ERROR) {
4189 imap_handle_error(SESSION(session), NULL, r);
4193 return MAILIMAP_NO_ERROR;
4196 gint imap_expunge(Folder *folder, FolderItem *item)
4198 IMAPSession *session = imap_session_get(folder);
4199 if (session == NULL)
4202 return imap_cmd_expunge(session, TRUE);
4205 static void imap_path_separator_subst(gchar *str, gchar separator)
4208 gboolean in_escape = FALSE;
4210 if (!separator || separator == '/') return;
4212 for (p = str; *p != '\0'; p++) {
4213 if (*p == '/' && !in_escape)
4215 else if (*p == '&' && *(p + 1) != '-' && !in_escape)
4217 else if (*p == '-' && in_escape)
4222 static gboolean imap_rename_folder_func(GNode *node, gpointer data)
4224 FolderItem *item = node->data;
4225 gchar **paths = data;
4226 const gchar *oldpath = paths[0];
4227 const gchar *newpath = paths[1];
4228 gchar *real_oldpath, *real_newpath;
4230 gchar *new_itempath;
4232 IMAPSession *session = imap_session_get(item->folder);
4233 gint ok = MAILIMAP_NO_ERROR;
4234 oldpathlen = strlen(oldpath);
4235 if (strncmp(oldpath, item->path, oldpathlen) != 0) {
4236 g_warning("path doesn't match: %s, %s\n", oldpath, item->path);
4240 base = item->path + oldpathlen;
4241 while (*base == G_DIR_SEPARATOR) base++;
4243 new_itempath = g_strdup(newpath);
4245 new_itempath = g_strconcat(newpath, G_DIR_SEPARATOR_S, base,
4248 real_oldpath = imap_get_real_path(session, IMAP_FOLDER(item->folder), item->path, &ok);
4250 item->path = new_itempath;
4252 real_newpath = imap_get_real_path(session, IMAP_FOLDER(item->folder), item->path, &ok);
4254 imap_threaded_subscribe(item->folder, real_oldpath, FALSE);
4255 imap_threaded_subscribe(item->folder, real_newpath, TRUE);
4257 g_free(real_oldpath);
4258 g_free(real_newpath);
4262 static gint get_list_of_uids(IMAPSession *session, Folder *folder, IMAPFolderItem *item, GSList **msgnum_list)
4264 GSList *uidlist, *elem;
4266 clist * lep_uidlist;
4267 gint ok, nummsgs = 0;
4269 if (session == NULL) {
4273 ok = imap_select(session, IMAP_FOLDER(folder), FOLDER_ITEM(item),
4274 NULL, NULL, NULL, NULL, NULL, TRUE);
4275 if (ok != MAILIMAP_NO_ERROR) {
4279 g_slist_free(item->uid_list);
4280 item->uid_list = NULL;
4284 if (folder->account && folder->account->low_bandwidth) {
4285 r = imap_threaded_search(folder, IMAP_SEARCH_TYPE_SIMPLE,
4286 NULL, NULL, &lep_uidlist);
4289 if (r == MAILIMAP_NO_ERROR) {
4290 GSList * fetchuid_list =
4291 imap_uid_list_from_lep(lep_uidlist, NULL);
4292 mailimap_search_result_free(lep_uidlist);
4294 uidlist = g_slist_concat(fetchuid_list, uidlist);
4296 carray * lep_uidtab;
4297 if (r != -1) { /* inited */
4298 imap_handle_error(SESSION(session), NULL, r);
4302 r = imap_threaded_fetch_uid(folder, 1,
4304 if (r == MAILIMAP_NO_ERROR) {
4305 GSList * fetchuid_list =
4306 imap_uid_list_from_lep_tab(lep_uidtab);
4307 imap_fetch_uid_list_free(lep_uidtab);
4308 uidlist = g_slist_concat(fetchuid_list, uidlist);
4312 if (r != MAILIMAP_NO_ERROR) {
4313 imap_handle_error(SESSION(session), NULL, r);
4317 for (elem = uidlist; elem != NULL; elem = g_slist_next(elem)) {
4320 msgnum = GPOINTER_TO_INT(elem->data);
4322 *msgnum_list = g_slist_prepend(*msgnum_list, GINT_TO_POINTER(msgnum));
4323 item->uid_list = g_slist_prepend(item->uid_list, GINT_TO_POINTER(msgnum));
4326 g_slist_free(uidlist);
4328 unlock_session(session); /* locked from imap_get_num_list */
4334 gint imap_get_num_list(Folder *folder, FolderItem *_item, GSList **msgnum_list, gboolean *old_uids_valid)
4336 IMAPFolderItem *item = (IMAPFolderItem *)_item;
4337 IMAPSession *session;
4339 GSList *uidlist = NULL;
4341 gint known_list_len = 0;
4342 debug_print("get_num_list\n");
4344 g_return_val_if_fail(folder != NULL, -1);
4345 g_return_val_if_fail(item != NULL, -1);
4346 g_return_val_if_fail(item->item.path != NULL, -1);
4347 g_return_val_if_fail(FOLDER_CLASS(folder) == &imap_class, -1);
4348 g_return_val_if_fail(folder->account != NULL, -1);
4350 known_list_len = g_slist_length(item->uid_list);
4351 if (!item->should_update) {
4352 debug_print("get_num_list: nothing to update\n");
4353 *old_uids_valid = TRUE;
4354 if (known_list_len == item->item.total_msgs
4355 && known_list_len > 0) {
4356 *msgnum_list = g_slist_copy(item->uid_list);
4357 return known_list_len;
4359 debug_print("don't know the list length...\n");
4363 if (prefs_common.work_offline &&
4364 !inc_offline_should_override(FALSE,
4365 _("Claws Mail needs network access in order "
4366 "to access the IMAP server."))) {
4370 debug_print("getting session...\n");
4371 session = imap_session_get(folder);
4372 g_return_val_if_fail(session != NULL, -1);
4374 lock_session(session); /* unlocked by get_list_of_uids */
4375 if (FOLDER_ITEM(item)->path)
4376 statusbar_print_all(_("Scanning folder %s%c%s ..."),
4377 FOLDER_ITEM(item)->folder->name,
4379 FOLDER_ITEM(item)->path);
4381 statusbar_print_all(_("Scanning folder %s ..."),
4382 FOLDER_ITEM(item)->folder->name);
4384 if (item->should_trash_cache) {
4385 *old_uids_valid = FALSE;
4386 debug_print("get_num_list: trashing num list\n");
4387 debug_print("Freeing imap uid cache\n");
4389 g_slist_free(item->uid_list);
4390 item->uid_list = NULL;
4392 imap_delete_all_cached_messages((FolderItem *)item);
4394 debug_print("get_num_list: updating num list\n");
4395 *old_uids_valid = TRUE;
4398 nummsgs = get_list_of_uids(session, folder, item, &uidlist);
4399 /* session could be broken now, in case of fatal error */
4401 debug_print("get_num_list: got %d msgs\n", nummsgs);
4404 statusbar_pop_all();
4408 *msgnum_list = uidlist;
4410 dir = folder_item_get_path((FolderItem *)item);
4411 debug_print("removing old messages from %s\n", dir);
4412 remove_numbered_files_not_in_list(dir, *msgnum_list);
4415 debug_print("get_num_list - ok - %i\n", nummsgs);
4416 statusbar_pop_all();
4417 item->should_trash_cache = FALSE;
4418 item->should_update = FALSE;
4422 static MsgInfo *imap_parse_msg(const gchar *file, FolderItem *item)
4427 flags.perm_flags = MSG_NEW|MSG_UNREAD;
4428 flags.tmp_flags = 0;
4430 g_return_val_if_fail(item != NULL, NULL);
4431 g_return_val_if_fail(file != NULL, NULL);
4433 if (folder_has_parent_of_type(item, F_QUEUE)) {
4434 MSG_SET_TMP_FLAGS(flags, MSG_QUEUED);
4435 } else if (folder_has_parent_of_type(item, F_DRAFT)) {
4436 MSG_SET_TMP_FLAGS(flags, MSG_DRAFT);
4439 msginfo = procheader_parse_file(file, flags, FALSE, FALSE);
4440 if (!msginfo) return NULL;
4442 msginfo->plaintext_file = g_strdup(file);
4443 msginfo->folder = item;
4448 GSList *imap_get_msginfos(Folder *folder, FolderItem *item,
4449 GSList *msgnum_list)
4451 IMAPSession *session;
4452 MsgInfoList *ret = NULL;
4455 debug_print("get_msginfos\n");
4457 g_return_val_if_fail(folder != NULL, NULL);
4458 g_return_val_if_fail(item != NULL, NULL);
4459 g_return_val_if_fail(msgnum_list != NULL, NULL);
4461 debug_print("getting session...\n");
4462 session = imap_session_get(folder);
4463 g_return_val_if_fail(session != NULL, NULL);
4465 lock_session(session); /* unlocked later in the function */
4467 debug_print("IMAP getting msginfos\n");
4468 ok = imap_select(session, IMAP_FOLDER(folder), item,
4469 NULL, NULL, NULL, NULL, NULL, FALSE);
4470 if (ok != MAILIMAP_NO_ERROR) {
4473 if (!(folder_has_parent_of_type(item, F_DRAFT) ||
4474 folder_has_parent_of_type(item, F_QUEUE))) {
4475 ret = g_slist_concat(ret,
4476 imap_get_uncached_messages(session, item,
4478 if (ok != MAILIMAP_NO_ERROR)
4480 unlock_session(session);
4482 MsgNumberList *sorted_list, *elem, *llast = NULL;
4483 gint startnum, lastnum;
4485 unlock_session(session);
4487 sorted_list = g_slist_sort(g_slist_copy(msgnum_list), g_int_compare);
4489 startnum = lastnum = GPOINTER_TO_INT(sorted_list->data);
4491 llast = g_slist_last(ret);
4492 for (elem = sorted_list;; elem = g_slist_next(elem)) {
4496 num = GPOINTER_TO_INT(elem->data);
4498 if (num > lastnum + 1 || elem == NULL) {
4500 for (i = startnum; i <= lastnum; ++i) {
4502 file = imap_fetch_msg(folder, item, i);
4504 MsgInfo *msginfo = imap_parse_msg(file, item);
4505 if (msginfo != NULL) {
4506 msginfo->msgnum = i;
4508 llast = ret = g_slist_append(ret, msginfo);
4510 llast = g_slist_append(llast, msginfo);
4511 llast = llast->next;
4526 g_slist_free(sorted_list);
4531 MsgInfo *imap_get_msginfo(Folder *folder, FolderItem *item, gint uid)
4533 MsgInfo *msginfo = NULL;
4534 MsgInfoList *msginfolist;
4535 MsgNumberList numlist;
4537 numlist.next = NULL;
4538 numlist.data = GINT_TO_POINTER(uid);
4540 msginfolist = imap_get_msginfos(folder, item, &numlist);
4541 if (msginfolist != NULL) {
4542 msginfo = msginfolist->data;
4543 g_slist_free(msginfolist);
4549 gboolean imap_scan_required(Folder *folder, FolderItem *_item)
4551 IMAPSession *session;
4552 IMAPFolderItem *item = (IMAPFolderItem *)_item;
4553 gint ok, exists = 0, unseen = 0;
4554 guint32 uid_next = 0, uid_val = 0;
4555 gboolean selected_folder;
4557 g_return_val_if_fail(folder != NULL, FALSE);
4558 g_return_val_if_fail(item != NULL, FALSE);
4559 g_return_val_if_fail(item->item.folder != NULL, FALSE);
4560 g_return_val_if_fail(FOLDER_CLASS(item->item.folder) == &imap_class, FALSE);
4562 if (item->item.path == NULL)
4565 if (item->should_update) {
4566 debug_print("scan already required\n");
4569 debug_print("getting session...\n");
4570 session = imap_session_get(folder);
4572 g_return_val_if_fail(session != NULL, FALSE);
4573 lock_session(session); /* unlocked later in the function */
4575 selected_folder = (session->mbox != NULL) &&
4576 (!strcmp(session->mbox, item->item.path));
4577 if (selected_folder) {
4578 if (!session->folder_content_changed) {
4579 ok = imap_cmd_noop(session);
4580 if (ok != MAILIMAP_NO_ERROR) {
4581 debug_print("disconnected!\n");
4583 session = imap_reconnect_if_possible(folder, session);
4585 session = imap_session_get(folder);
4586 if (session == NULL)
4590 if (session->folder_content_changed) {
4591 debug_print("CHANGED (self-noop)! scan_required\n");
4592 item->should_update = TRUE;
4593 if (session->uid_validity && session->uid_validity != item->item.mtime) {
4594 item->item.mtime = session->uid_validity;
4595 item->should_trash_cache = TRUE;
4597 unlock_session(session);
4601 debug_print("CHANGED (previous noop)! scan_required\n");
4602 item->should_update = TRUE;
4603 if (session->uid_validity && session->uid_validity != item->item.mtime) {
4604 item->item.mtime = session->uid_validity;
4605 item->should_trash_cache = TRUE;
4607 unlock_session(session);
4611 ok = imap_status(session, IMAP_FOLDER(folder), item->item.path, IMAP_FOLDER_ITEM(item),
4612 &exists, &uid_next, &uid_val, &unseen, FALSE);
4613 if (ok != MAILIMAP_NO_ERROR) {
4617 debug_print("exists %d, item->item.total_msgs %d\n",
4618 exists, item->item.total_msgs);
4619 if (exists != item->item.total_msgs
4620 || unseen != item->item.unread_msgs
4621 || uid_next != item->uid_next
4622 || uid_val != item->item.mtime) {
4623 debug_print("CHANGED (status)! scan_required\n");
4624 item->last_change = time(NULL);
4625 item->should_update = TRUE;
4626 item->uid_next = uid_next;
4627 if (uid_val != item->item.mtime) {
4628 item->item.mtime = uid_val;
4629 item->should_trash_cache = TRUE;
4631 unlock_session(session);
4635 unlock_session(session);
4637 item->should_update = FALSE;
4641 void imap_change_flags(Folder *folder, FolderItem *item, MsgInfo *msginfo, MsgPermFlags newflags)
4643 IMAPSession *session;
4644 IMAPFlags flags_set = 0, flags_unset = 0;
4645 gint ok = MAILIMAP_NO_ERROR;
4646 MsgNumberList numlist;
4647 hashtable_data *ht_data = NULL;
4649 g_return_if_fail(folder != NULL);
4650 g_return_if_fail(folder->klass == &imap_class);
4651 g_return_if_fail(item != NULL);
4652 g_return_if_fail(item->folder == folder);
4653 g_return_if_fail(msginfo != NULL);
4654 g_return_if_fail(msginfo->folder == item);
4656 if (!MSG_IS_MARKED(msginfo->flags) && (newflags & MSG_MARKED))
4657 flags_set |= IMAP_FLAG_FLAGGED;
4658 if ( MSG_IS_MARKED(msginfo->flags) && !(newflags & MSG_MARKED))
4659 flags_unset |= IMAP_FLAG_FLAGGED;
4661 if (!MSG_IS_UNREAD(msginfo->flags) && (newflags & MSG_UNREAD))
4662 flags_unset |= IMAP_FLAG_SEEN;
4663 if ( MSG_IS_UNREAD(msginfo->flags) && !(newflags & MSG_UNREAD))
4664 flags_set |= IMAP_FLAG_SEEN;
4666 if (!MSG_IS_REPLIED(msginfo->flags) && (newflags & MSG_REPLIED))
4667 flags_set |= IMAP_FLAG_ANSWERED;
4668 if ( MSG_IS_REPLIED(msginfo->flags) && !(newflags & MSG_REPLIED))
4669 flags_unset |= IMAP_FLAG_ANSWERED;
4671 if (!MSG_IS_FORWARDED(msginfo->flags) && (newflags & MSG_FORWARDED))
4672 flags_set |= IMAP_FLAG_FORWARDED;
4673 if ( MSG_IS_FORWARDED(msginfo->flags) && !(newflags & MSG_FORWARDED))
4674 flags_unset |= IMAP_FLAG_FORWARDED;
4676 if (!MSG_IS_SPAM(msginfo->flags) && (newflags & MSG_SPAM)) {
4677 flags_set |= IMAP_FLAG_SPAM;
4678 flags_unset |= IMAP_FLAG_HAM;
4680 if ( MSG_IS_SPAM(msginfo->flags) && !(newflags & MSG_SPAM)) {
4681 flags_set |= IMAP_FLAG_HAM;
4682 flags_unset |= IMAP_FLAG_SPAM;
4684 if (!MSG_IS_DELETED(msginfo->flags) && (newflags & MSG_DELETED))
4685 flags_set |= IMAP_FLAG_DELETED;
4686 if ( MSG_IS_DELETED(msginfo->flags) && !(newflags & MSG_DELETED))
4687 flags_unset |= IMAP_FLAG_DELETED;
4689 if (!flags_set && !flags_unset) {
4690 /* the changed flags were not translatable to IMAP-speak.
4691 * like MSG_POSTFILTERED, so just apply. */
4692 msginfo->flags.perm_flags = newflags;
4696 debug_print("getting session...\n");
4697 session = imap_session_get(folder);
4702 if ((ok = imap_select(session, IMAP_FOLDER(folder), msginfo->folder,
4703 NULL, NULL, NULL, NULL, NULL, FALSE)) != MAILIMAP_NO_ERROR) {
4706 numlist.next = NULL;
4707 numlist.data = GINT_TO_POINTER(msginfo->msgnum);
4709 if (IMAP_FOLDER_ITEM(item)->batching) {
4710 /* instead of performing an UID STORE command for each message change,
4711 * as a lot of them can change "together", we just fill in hashtables
4712 * and defer the treatment so that we're able to send only one
4715 debug_print("IMAP batch mode on, deferring flags change\n");
4717 ht_data = g_hash_table_lookup(IMAP_FOLDER_ITEM(item)->flags_set_table,
4718 GINT_TO_POINTER(flags_set));
4719 if (ht_data == NULL) {
4720 ht_data = g_new0(hashtable_data, 1);
4721 ht_data->item = IMAP_FOLDER_ITEM(item);
4722 g_hash_table_insert(IMAP_FOLDER_ITEM(item)->flags_set_table,
4723 GINT_TO_POINTER(flags_set), ht_data);
4725 ht_data->msglist = g_slist_prepend(ht_data->msglist, GINT_TO_POINTER(msginfo->msgnum));
4728 ht_data = g_hash_table_lookup(IMAP_FOLDER_ITEM(item)->flags_unset_table,
4729 GINT_TO_POINTER(flags_unset));
4730 if (ht_data == NULL) {
4731 ht_data = g_new0(hashtable_data, 1);
4732 ht_data->item = IMAP_FOLDER_ITEM(item);
4733 g_hash_table_insert(IMAP_FOLDER_ITEM(item)->flags_unset_table,
4734 GINT_TO_POINTER(flags_unset), ht_data);
4736 ht_data->msglist = g_slist_prepend(ht_data->msglist,
4737 GINT_TO_POINTER(msginfo->msgnum));
4740 debug_print("IMAP changing flags\n");
4742 ok = imap_set_message_flags(session, IMAP_FOLDER_ITEM(item), &numlist, flags_set, NULL, TRUE);
4743 if (ok != MAILIMAP_NO_ERROR) {
4749 ok = imap_set_message_flags(session, IMAP_FOLDER_ITEM(item), &numlist, flags_unset, NULL, FALSE);
4750 if (ok != MAILIMAP_NO_ERROR) {
4755 msginfo->flags.perm_flags = newflags;
4759 static gint imap_remove_msg(Folder *folder, FolderItem *item, gint uid)
4762 IMAPSession *session;
4764 MsgNumberList numlist;
4766 g_return_val_if_fail(folder != NULL, -1);
4767 g_return_val_if_fail(FOLDER_CLASS(folder) == &imap_class, -1);
4768 g_return_val_if_fail(item != NULL, -1);
4770 debug_print("getting session...\n");
4771 session = imap_session_get(folder);
4772 if (!session) return -1;
4774 ok = imap_select(session, IMAP_FOLDER(folder), item,
4775 NULL, NULL, NULL, NULL, NULL, FALSE);
4776 if (ok != MAILIMAP_NO_ERROR) {
4779 numlist.next = NULL;
4780 numlist.data = GINT_TO_POINTER(uid);
4782 ok = imap_set_message_flags
4783 (session, IMAP_FOLDER_ITEM(item), &numlist, IMAP_FLAG_DELETED, NULL, TRUE);
4784 if (ok != MAILIMAP_NO_ERROR) {
4785 log_warning(LOG_PROTOCOL, _("can't set deleted flags: %d\n"), uid);
4789 ok = imap_cmd_expunge(session, folder->account->imap_use_trash);
4791 if (ok != MAILIMAP_NO_ERROR) {
4792 log_warning(LOG_PROTOCOL, _("can't expunge\n"));
4796 IMAP_FOLDER_ITEM(item)->uid_list = g_slist_remove(
4797 IMAP_FOLDER_ITEM(item)->uid_list, numlist.data);
4798 dir = folder_item_get_path(item);
4799 if (is_dir_exist(dir))
4800 remove_numbered_files(dir, uid, uid);
4802 return MAILIMAP_NO_ERROR;
4805 static gint compare_msginfo(gconstpointer a, gconstpointer b)
4807 return ((MsgInfo *)a)->msgnum - ((MsgInfo *)b)->msgnum;
4810 static guint gslist_find_next_num(MsgNumberList **list, guint num)
4814 g_return_val_if_fail(list != NULL, -1);
4816 for (elem = *list; elem != NULL; elem = g_slist_next(elem))
4817 if (GPOINTER_TO_INT(elem->data) >= num)
4820 return elem != NULL ? GPOINTER_TO_INT(elem->data) : (gint)-1;
4823 static gboolean flag_ok(IMAPFolderItem *item, guint flag)
4825 if (item->ok_flags && g_slist_find(item->ok_flags, GUINT_TO_POINTER(flag))) {
4826 debug_print("flag %d is OK\n", flag);
4829 if (item->can_create_flags == ITEM_CAN_CREATE_FLAGS) {
4830 debug_print("creating flags is OK\n");
4837 * NEW and DELETED flags are not syncronized
4838 * - The NEW/RECENT flags in IMAP folders can not really be directly
4839 * modified by Sylpheed
4840 * - The DELETE/DELETED flag in IMAP and Sylpheed don't have the same
4841 * meaning, in IMAP it always removes the messages from the FolderItem
4842 * in Sylpheed it can mean to move the message to trash
4845 typedef struct _get_flags_data {
4848 MsgInfoList *msginfo_list;
4849 GHashTable *msgflags;
4850 gboolean full_search;
4854 static /*gint*/ void *imap_get_flags_thread(void *data)
4856 get_flags_data *stuff = (get_flags_data *)data;
4857 Folder *folder = stuff->folder;
4858 FolderItem *fitem = (FolderItem *) stuff->item;
4859 MsgInfoList *msginfo_list = stuff->msginfo_list;
4860 GHashTable *msgflags = stuff->msgflags;
4862 carray * lep_uidtab;
4863 IMAPSession *session;
4865 int r = MAILIMAP_NO_ERROR;
4866 GHashTable *flags_hash = NULL;
4867 GHashTable *tags_hash = NULL;
4868 gboolean full_search = stuff->full_search;
4869 GSList *sorted_list = NULL;
4870 GSList *unseen = NULL, *answered = NULL, *flagged = NULL, *deleted = NULL, *forwarded = NULL, *spam = NULL;
4871 GSList *seq_list, *cur;
4872 gboolean reverse_seen = FALSE;
4873 gboolean selected_folder;
4874 gint exists_cnt, unseen_cnt;
4875 gboolean got_alien_tags = FALSE;
4877 session = imap_session_get(folder);
4879 if (session == NULL) {
4881 return GINT_TO_POINTER(-1);
4883 selected_folder = (session->mbox != NULL) &&
4884 (!strcmp(session->mbox, fitem->path));
4886 lock_session(session);
4887 if (!selected_folder) {
4888 ok = imap_select(session, IMAP_FOLDER(folder), fitem,
4889 &exists_cnt, NULL, &unseen_cnt, NULL, NULL, TRUE);
4890 if (ok != MAILIMAP_NO_ERROR) {
4892 return GINT_TO_POINTER(-1);
4895 if (unseen_cnt > exists_cnt / 2)
4896 reverse_seen = TRUE;
4899 if (fitem->unread_msgs > fitem->total_msgs / 2)
4900 reverse_seen = TRUE;
4903 sorted_list = g_slist_sort(g_slist_copy(msginfo_list), compare_msginfo);
4905 seq_list = imap_get_lep_set_from_msglist(IMAP_FOLDER(folder), msginfo_list);
4907 struct mailimap_set * set;
4908 set = mailimap_set_new_interval(1, 0);
4909 seq_list = g_slist_append(NULL, set);
4912 if (folder->account && folder->account->low_bandwidth) {
4913 for (cur = seq_list; cur != NULL; cur = g_slist_next(cur)) {
4914 struct mailimap_set * imapset;
4915 clist * lep_uidlist;
4918 imapset = cur->data;
4920 r = imap_threaded_search(folder, IMAP_SEARCH_TYPE_SEEN, NULL,
4921 full_search ? NULL:imapset, &lep_uidlist);
4924 r = imap_threaded_search(folder,
4925 IMAP_SEARCH_TYPE_UNSEEN, NULL,
4926 full_search ? NULL:imapset, &lep_uidlist);
4928 if (r == MAILIMAP_NO_ERROR) {
4931 uidlist = imap_uid_list_from_lep(lep_uidlist, NULL);
4932 mailimap_search_result_free(lep_uidlist);
4934 unseen = g_slist_concat(unseen, uidlist);
4936 imap_handle_error(SESSION(session), NULL, r);
4940 r = imap_threaded_search(folder, IMAP_SEARCH_TYPE_FLAGGED, NULL,
4941 full_search ? NULL:imapset, &lep_uidlist);
4942 if (r == MAILIMAP_NO_ERROR) {
4945 uidlist = imap_uid_list_from_lep(lep_uidlist, NULL);
4946 mailimap_search_result_free(lep_uidlist);
4948 flagged = g_slist_concat(flagged, uidlist);
4950 imap_handle_error(SESSION(session), NULL, r);
4954 if (fitem->opened || fitem->processing_pending || fitem == folder->inbox) {
4955 r = imap_threaded_search(folder, IMAP_SEARCH_TYPE_ANSWERED, NULL,
4956 full_search ? NULL:imapset, &lep_uidlist);
4957 if (r == MAILIMAP_NO_ERROR) {
4960 uidlist = imap_uid_list_from_lep(lep_uidlist, NULL);
4961 mailimap_search_result_free(lep_uidlist);
4963 answered = g_slist_concat(answered, uidlist);
4965 imap_handle_error(SESSION(session), NULL, r);
4969 if (flag_ok(IMAP_FOLDER_ITEM(fitem), IMAP_FLAG_FORWARDED)) {
4970 r = imap_threaded_search(folder, IMAP_SEARCH_TYPE_FORWARDED, NULL,
4971 full_search ? NULL:imapset, &lep_uidlist);
4972 if (r == MAILIMAP_NO_ERROR) {
4975 uidlist = imap_uid_list_from_lep(lep_uidlist, NULL);
4976 mailimap_search_result_free(lep_uidlist);
4978 forwarded = g_slist_concat(forwarded, uidlist);
4980 imap_handle_error(SESSION(session), NULL, r);
4985 if (flag_ok(IMAP_FOLDER_ITEM(fitem), IMAP_FLAG_SPAM)) {
4986 r = imap_threaded_search(folder, IMAP_SEARCH_TYPE_SPAM, NULL,
4987 full_search ? NULL:imapset, &lep_uidlist);
4988 if (r == MAILIMAP_NO_ERROR) {
4991 uidlist = imap_uid_list_from_lep(lep_uidlist, NULL);
4992 mailimap_search_result_free(lep_uidlist);
4994 spam = g_slist_concat(spam, uidlist);
4996 imap_handle_error(SESSION(session), NULL, r);
5001 r = imap_threaded_search(folder, IMAP_SEARCH_TYPE_DELETED, NULL,
5002 full_search ? NULL:imapset, &lep_uidlist);
5003 if (r == MAILIMAP_NO_ERROR) {
5006 uidlist = imap_uid_list_from_lep(lep_uidlist, NULL);
5007 mailimap_search_result_free(lep_uidlist);
5009 deleted = g_slist_concat(deleted, uidlist);
5011 imap_handle_error(SESSION(session), NULL, r);
5018 r = imap_threaded_fetch_uid_flags(folder, 1, &lep_uidtab);
5019 if (r == MAILIMAP_NO_ERROR) {
5020 flags_hash = g_hash_table_new_full(g_direct_hash, g_direct_equal, NULL, NULL);
5021 tags_hash = g_hash_table_new_full(g_direct_hash, g_direct_equal, NULL, NULL);
5022 imap_flags_hash_from_lep_uid_flags_tab(lep_uidtab, flags_hash, tags_hash);
5023 imap_fetch_uid_flags_list_free(lep_uidtab);
5025 imap_handle_error(SESSION(session), NULL, r);
5031 if (r == MAILIMAP_NO_ERROR)
5032 unlock_session(session);
5034 for (elem = sorted_list; elem != NULL; elem = g_slist_next(elem)) {
5036 MsgPermFlags flags, oldflags;
5039 msginfo = (MsgInfo *) elem->data;
5040 flags = msginfo->flags.perm_flags;
5041 wasnew = (flags & MSG_NEW);
5042 oldflags = flags & ~(MSG_NEW|MSG_UNREAD|MSG_REPLIED|MSG_FORWARDED|MSG_MARKED|MSG_DELETED|MSG_SPAM);
5044 if (folder->account && folder->account->low_bandwidth) {
5045 if (fitem->opened || fitem->processing_pending || fitem == folder->inbox) {
5046 flags &= ~((reverse_seen ? 0 : MSG_UNREAD | MSG_NEW) | MSG_REPLIED | MSG_FORWARDED | MSG_MARKED | MSG_SPAM);
5048 flags &= ~((reverse_seen ? 0 : MSG_UNREAD | MSG_NEW | MSG_MARKED));
5051 flags |= MSG_UNREAD | (wasnew ? MSG_NEW : 0);
5052 if (gslist_find_next_num(&unseen, msginfo->msgnum) == msginfo->msgnum) {
5053 if (!reverse_seen) {
5054 flags |= MSG_UNREAD | (wasnew ? MSG_NEW : 0);
5056 flags &= ~(MSG_UNREAD | MSG_NEW);
5060 if (gslist_find_next_num(&flagged, msginfo->msgnum) == msginfo->msgnum)
5061 flags |= MSG_MARKED;
5063 flags &= ~MSG_MARKED;
5065 if (fitem->opened || fitem->processing_pending || fitem == folder->inbox) {
5066 if (gslist_find_next_num(&answered, msginfo->msgnum) == msginfo->msgnum)
5067 flags |= MSG_REPLIED;
5069 flags &= ~MSG_REPLIED;
5070 if (gslist_find_next_num(&forwarded, msginfo->msgnum) == msginfo->msgnum)
5071 flags |= MSG_FORWARDED;
5073 flags &= ~MSG_FORWARDED;
5074 if (gslist_find_next_num(&spam, msginfo->msgnum) == msginfo->msgnum)
5078 if (gslist_find_next_num(&deleted, msginfo->msgnum) == msginfo->msgnum)
5079 flags |= MSG_DELETED;
5081 flags &= ~MSG_DELETED;
5084 if (flags_hash != NULL) {
5086 flags = GPOINTER_TO_INT(g_hash_table_lookup(flags_hash,
5087 GINT_TO_POINTER(msginfo->msgnum)));
5090 if ((flags & MSG_UNREAD) == 0)
5096 if (tags_hash != NULL) {
5097 GSList *tags = g_hash_table_lookup(tags_hash, GINT_TO_POINTER(msginfo->msgnum));
5100 g_slist_free(msginfo->tags);
5101 msginfo->tags = NULL;
5103 for (cur = tags; cur; cur = cur->next) {
5104 gchar *real_tag = imap_modified_utf7_to_utf8(cur->data, TRUE);
5106 id = tags_get_id_for_str(real_tag);
5108 id = tags_add_tag(real_tag);
5109 got_alien_tags = TRUE;
5111 msginfo->tags = g_slist_append(
5113 GINT_TO_POINTER(id));
5116 slist_free_strings(tags);
5121 g_hash_table_insert(msgflags, msginfo, GINT_TO_POINTER(flags));
5124 if (got_alien_tags) {
5126 main_window_reflect_tags_changes(mainwindow_get_mainwindow());
5130 g_hash_table_destroy(flags_hash);
5132 g_hash_table_destroy(tags_hash);
5134 imap_lep_set_free(seq_list);
5135 g_slist_free(flagged);
5136 g_slist_free(deleted);
5137 g_slist_free(answered);
5138 g_slist_free(forwarded);
5140 g_slist_free(unseen);
5141 g_slist_free(sorted_list);
5144 return GINT_TO_POINTER(0);
5147 static gint imap_get_flags(Folder *folder, FolderItem *item,
5148 MsgInfoList *msginfo_list, GHashTable *msgflags)
5151 get_flags_data *data = g_new0(get_flags_data, 1);
5153 data->folder = folder;
5155 data->msginfo_list = msginfo_list;
5156 data->msgflags = msgflags;
5157 data->full_search = FALSE;
5159 GSList *tmp = NULL, *cur;
5161 if (prefs_common.work_offline &&
5162 !inc_offline_should_override(FALSE,
5163 _("Claws Mail needs network access in order "
5164 "to access the IMAP server."))) {
5169 tmp = folder_item_get_msg_list(item);
5171 if (g_slist_length(tmp) <= g_slist_length(msginfo_list))
5172 data->full_search = TRUE;
5174 for (cur = tmp; cur; cur = cur->next)
5175 procmsg_msginfo_free((MsgInfo *)cur->data);
5179 result = GPOINTER_TO_INT(imap_get_flags_thread(data));
5186 static gboolean process_flags(gpointer key, gpointer value, gpointer user_data)
5188 gboolean flags_set = GPOINTER_TO_INT(user_data);
5189 gint flags_value = GPOINTER_TO_INT(key);
5190 hashtable_data *data = (hashtable_data *)value;
5191 IMAPFolderItem *_item = data->item;
5192 FolderItem *item = (FolderItem *)_item;
5193 gint ok = MAILIMAP_ERROR_BAD_STATE;
5194 IMAPSession *session = NULL;
5196 debug_print("getting session...\n");
5197 session = imap_session_get(item->folder);
5199 data->msglist = g_slist_reverse(data->msglist);
5201 debug_print("IMAP %ssetting flags to %d for %d messages\n",
5204 g_slist_length(data->msglist));
5206 lock_session(session);
5208 ok = imap_select(session, IMAP_FOLDER(item->folder), item,
5209 NULL, NULL, NULL, NULL, NULL, FALSE);
5211 if (ok == MAILIMAP_NO_ERROR) {
5212 ok = imap_set_message_flags(session, IMAP_FOLDER_ITEM(item),
5213 data->msglist, flags_value, NULL, flags_set);
5215 g_warning("can't select mailbox %s\n", item->path);
5219 unlock_session(session);
5221 g_slist_free(data->msglist);
5226 static gboolean process_tags(gpointer key, gpointer value, gpointer user_data)
5228 gboolean tags_set = GPOINTER_TO_INT(user_data);
5229 TagsData *data = (TagsData *)value;
5230 IMAPFolderItem *_item = data->item;
5231 FolderItem *item = (FolderItem *)_item;
5232 gchar *str = data->str;
5233 gint ok = MAILIMAP_ERROR_BAD_STATE;
5234 IMAPSession *session = NULL;
5236 debug_print("getting session...\n");
5237 session = imap_session_get(item->folder);
5239 data->msglist = g_slist_reverse(data->msglist);
5241 debug_print("IMAP %ssetting tags %s for %d messages\n",
5244 g_slist_length(data->msglist));
5246 lock_session(session);
5248 ok = imap_select(session, IMAP_FOLDER(item->folder), item,
5249 NULL, NULL, NULL, NULL, NULL, FALSE);
5251 if (ok == MAILIMAP_NO_ERROR) {
5255 ok = imap_set_message_flags(session, IMAP_FOLDER_ITEM(item),
5256 data->msglist, 0, &list, tags_set);
5258 g_warning("can't select mailbox %s\n", item->path);
5262 unlock_session(session);
5264 g_slist_free(data->msglist);
5270 static void process_hashtable(IMAPFolderItem *item)
5272 if (item->flags_set_table) {
5273 g_hash_table_foreach_remove(item->flags_set_table, process_flags, GINT_TO_POINTER(TRUE));
5274 g_hash_table_destroy(item->flags_set_table);
5275 item->flags_set_table = NULL;
5277 if (item->flags_unset_table) {
5278 g_hash_table_foreach_remove(item->flags_unset_table, process_flags, GINT_TO_POINTER(FALSE));
5279 g_hash_table_destroy(item->flags_unset_table);
5280 item->flags_unset_table = NULL;
5282 if (item->tags_set_table) {
5283 g_hash_table_foreach_remove(item->tags_set_table, process_tags, GINT_TO_POINTER(TRUE));
5284 g_hash_table_destroy(item->tags_set_table);
5285 item->tags_set_table = NULL;
5287 if (item->tags_unset_table) {
5288 g_hash_table_foreach_remove(item->tags_unset_table, process_tags, GINT_TO_POINTER(FALSE));
5289 g_hash_table_destroy(item->tags_unset_table);
5290 item->tags_unset_table = NULL;
5295 static void imap_set_batch (Folder *folder, FolderItem *_item, gboolean batch)
5297 IMAPFolderItem *item = (IMAPFolderItem *)_item;
5298 IMAPSession *session;
5300 g_return_if_fail(item != NULL);
5302 if (item->batching == batch)
5306 item->batching = TRUE;
5307 debug_print("IMAP switching to batch mode\n");
5308 if (!item->flags_set_table) {
5309 item->flags_set_table = g_hash_table_new(NULL, g_direct_equal);
5311 if (!item->flags_unset_table) {
5312 item->flags_unset_table = g_hash_table_new(NULL, g_direct_equal);
5314 if (!item->tags_set_table) {
5315 item->tags_set_table = g_hash_table_new(NULL, g_direct_equal);
5317 if (!item->tags_unset_table) {
5318 item->tags_unset_table = g_hash_table_new(NULL, g_direct_equal);
5320 session = imap_session_get(folder);
5322 imap_refresh_sensitivity(session);
5323 session->sens_update_block = TRUE;
5326 debug_print("IMAP switching away from batch mode\n");
5328 process_hashtable(item);
5329 item->batching = FALSE;
5330 session = imap_session_get(folder);
5332 session->sens_update_block = FALSE;
5333 imap_refresh_sensitivity(session);
5340 /* data types conversion libetpan <-> claws */
5344 #define ETPAN_IMAP_MB_MARKED 1
5345 #define ETPAN_IMAP_MB_UNMARKED 2
5346 #define ETPAN_IMAP_MB_NOSELECT 4
5347 #define ETPAN_IMAP_MB_NOINFERIORS 8
5349 static int imap_flags_to_flags(struct mailimap_mbx_list_flags * imap_flags)
5355 if (imap_flags->mbf_type == MAILIMAP_MBX_LIST_FLAGS_SFLAG) {
5356 switch (imap_flags->mbf_sflag) {
5357 case MAILIMAP_MBX_LIST_SFLAG_MARKED:
5358 flags |= ETPAN_IMAP_MB_MARKED;
5360 case MAILIMAP_MBX_LIST_SFLAG_NOSELECT:
5361 flags |= ETPAN_IMAP_MB_NOSELECT;
5363 case MAILIMAP_MBX_LIST_SFLAG_UNMARKED:
5364 flags |= ETPAN_IMAP_MB_UNMARKED;
5369 if (imap_flags->mbf_oflags) {
5370 for(cur = clist_begin(imap_flags->mbf_oflags) ; cur != NULL ;
5371 cur = clist_next(cur)) {
5372 struct mailimap_mbx_list_oflag * oflag;
5374 oflag = clist_content(cur);
5376 switch (oflag->of_type) {
5377 case MAILIMAP_MBX_LIST_OFLAG_NOINFERIORS:
5378 flags |= ETPAN_IMAP_MB_NOINFERIORS;
5386 static GSList * imap_list_from_lep(IMAPFolder * folder,
5387 clist * list, const gchar * real_path, gboolean all)
5390 GSList * item_list = NULL, *llast = NULL;
5393 for(iter = clist_begin(list) ; iter != NULL ;
5394 iter = clist_next(iter)) {
5395 struct mailimap_mailbox_list * mb;
5403 FolderItem *new_item;
5405 mb = clist_content(iter);
5411 if (mb->mb_flag != NULL)
5412 flags = imap_flags_to_flags(mb->mb_flag);
5414 delimiter = mb->mb_delimiter;
5417 dup_name = strdup(name);
5418 if (delimiter != '\0')
5419 subst_char(dup_name, delimiter, '/');
5421 base = g_path_get_basename(dup_name);
5422 if (base[0] == '.') {
5427 if (!all && path_cmp(name, real_path) == 0) {
5433 if (!all && dup_name[strlen(dup_name)-1] == '/') {
5434 dup_name[strlen(dup_name)-1] = '\0';
5437 loc_name = imap_modified_utf7_to_utf8(base, FALSE);
5438 loc_path = imap_modified_utf7_to_utf8(dup_name, FALSE);
5440 new_item = folder_item_new(FOLDER(folder), loc_name, loc_path);
5441 if ((flags & ETPAN_IMAP_MB_NOINFERIORS) != 0)
5442 new_item->no_sub = TRUE;
5443 if (strcmp(dup_name, "INBOX") != 0 &&
5444 ((flags & ETPAN_IMAP_MB_NOSELECT) != 0))
5445 new_item->no_select = TRUE;
5447 if (item_list == NULL)
5448 llast = item_list = g_slist_append(item_list, new_item);
5450 llast = g_slist_append(llast, new_item);
5451 llast = llast->next;
5453 debug_print("folder '%s' found.\n", loc_path);
5464 static GSList * imap_get_lep_set_from_numlist(IMAPFolder *folder, MsgNumberList *numlist)
5466 GSList *sorted_list, *cur;
5467 guint first, last, next;
5468 GSList *ret_list = NULL, *llast = NULL;
5469 struct mailimap_set * current_set;
5470 unsigned int item_count;
5472 if (numlist == NULL)
5475 current_set = mailimap_set_new_empty();
5477 sorted_list = g_slist_copy(numlist);
5478 sorted_list = g_slist_sort(sorted_list, g_int_compare);
5480 first = GPOINTER_TO_INT(sorted_list->data);
5483 for (cur = sorted_list; cur != NULL; cur = g_slist_next(cur)) {
5484 if (GPOINTER_TO_INT(cur->data) == 0)
5489 last = GPOINTER_TO_INT(cur->data);
5491 next = GPOINTER_TO_INT(cur->next->data);
5495 if (last + 1 != next || next == 0 || item_count >= folder->max_set_size) {
5497 struct mailimap_set_item * item;
5498 item = mailimap_set_item_new(first, last);
5499 mailimap_set_add(current_set, item);
5503 if (item_count >= folder->max_set_size) {
5504 if (ret_list == NULL)
5505 llast = ret_list = g_slist_append(ret_list,
5508 llast = g_slist_append(llast, current_set);
5509 llast = llast->next;
5512 current_set = mailimap_set_new_empty();
5518 if (clist_count(current_set->set_list) > 0) {
5519 ret_list = g_slist_append(ret_list,
5523 g_slist_free(sorted_list);
5528 static GSList * imap_get_lep_set_from_msglist(IMAPFolder *folder, MsgInfoList *msglist)
5530 MsgNumberList *numlist = NULL;
5533 numlist = procmsg_get_number_list_for_msgs(msglist);
5535 seq_list = imap_get_lep_set_from_numlist(folder, numlist);
5536 g_slist_free(numlist);
5541 static GSList * imap_uid_list_from_lep(clist * list, gint* length)
5550 for(iter = clist_begin(list) ; iter != NULL ;
5551 iter = clist_next(iter)) {
5554 puid = clist_content(iter);
5555 result = g_slist_prepend(result, GINT_TO_POINTER(* puid));
5558 result = g_slist_reverse(result);
5565 static GSList * imap_uid_list_from_lep_tab(carray * list)
5572 for(i = 0 ; i < carray_count(list) ; i ++) {
5575 puid = carray_get(list, i);
5576 result = g_slist_prepend(result, GINT_TO_POINTER(* puid));
5578 result = g_slist_reverse(result);
5582 static void imap_flags_hash_from_lep_uid_flags_tab(carray * list,
5584 GHashTable * tags_hash)
5588 for(i = 0 ; i < carray_count(list) ; i += 3) {
5593 puid = carray_get(list, i);
5594 pflags = carray_get(list, i + 1);
5595 tags = carray_get(list, i + 2);
5597 g_hash_table_insert(hash, GINT_TO_POINTER(*puid), GINT_TO_POINTER(* pflags));
5598 g_hash_table_insert(tags_hash, GINT_TO_POINTER(*puid), tags);
5602 static MsgInfo *imap_envelope_from_lep(struct imap_fetch_env_info * info,
5605 MsgInfo *msginfo = NULL;
5608 MsgFlags flags = {0, 0};
5610 if (info->headers == NULL)
5613 MSG_SET_TMP_FLAGS(flags, MSG_IMAP);
5614 if (folder_has_parent_of_type(item, F_QUEUE)) {
5615 MSG_SET_TMP_FLAGS(flags, MSG_QUEUED);
5616 } else if (folder_has_parent_of_type(item, F_DRAFT)) {
5617 MSG_SET_TMP_FLAGS(flags, MSG_DRAFT);
5619 flags.perm_flags = info->flags;
5622 size = (goffset) info->size;
5623 msginfo = procheader_parse_str(info->headers, flags, FALSE, FALSE);
5626 msginfo->msgnum = uid;
5627 msginfo->size = size;
5633 static void imap_lep_set_free(GSList *seq_list)
5637 for(cur = seq_list ; cur != NULL ; cur = g_slist_next(cur)) {
5638 struct mailimap_set * imapset;
5640 imapset = cur->data;
5641 mailimap_set_free(imapset);
5643 g_slist_free(seq_list);
5646 static struct mailimap_flag_list * imap_flag_to_lep(IMAPFolderItem *item, IMAPFlags flags, GSList *tags)
5648 struct mailimap_flag_list * flag_list;
5651 flag_list = mailimap_flag_list_new_empty();
5653 if (IMAP_IS_SEEN(flags))
5654 mailimap_flag_list_add(flag_list,
5655 mailimap_flag_new_seen());
5656 if (IMAP_IS_ANSWERED(flags))
5657 mailimap_flag_list_add(flag_list,
5658 mailimap_flag_new_answered());
5659 if (IMAP_IS_FLAGGED(flags))
5660 mailimap_flag_list_add(flag_list,
5661 mailimap_flag_new_flagged());
5662 if (IMAP_IS_DELETED(flags))
5663 mailimap_flag_list_add(flag_list,
5664 mailimap_flag_new_deleted());
5665 if (IMAP_IS_DRAFT(flags))
5666 mailimap_flag_list_add(flag_list,
5667 mailimap_flag_new_draft());
5668 if (IMAP_IS_FORWARDED(flags) && flag_ok(item, IMAP_FLAG_FORWARDED))
5669 mailimap_flag_list_add(flag_list,
5670 mailimap_flag_new_flag_keyword(strdup(RTAG_FORWARDED)));
5671 if (IMAP_IS_SPAM(flags) && flag_ok(item, IMAP_FLAG_SPAM))
5672 mailimap_flag_list_add(flag_list,
5673 mailimap_flag_new_flag_keyword(strdup(RTAG_JUNK)));
5674 else if (IMAP_IS_HAM(flags) && flag_ok(item, IMAP_FLAG_HAM))
5675 mailimap_flag_list_add(flag_list,
5676 mailimap_flag_new_flag_keyword(strdup(RTAG_NON_JUNK)));
5678 for (; cur; cur = cur->next) {
5680 imap_utf8_to_modified_utf7(cur->data, TRUE);
5681 g_strstrip(enc_str);
5683 mailimap_flag_list_add(flag_list,
5684 mailimap_flag_new_flag_keyword(enc_str));
5690 guint imap_folder_get_refcnt(Folder *folder)
5692 return ((IMAPFolder *)folder)->refcnt;
5695 void imap_folder_ref(Folder *folder)
5697 ((IMAPFolder *)folder)->refcnt++;
5700 void imap_disconnect_all(gboolean have_connectivity)
5703 gboolean short_timeout;
5704 #ifdef HAVE_NETWORKMANAGER_SUPPORT
5708 #ifdef HAVE_NETWORKMANAGER_SUPPORT
5710 short_timeout = !networkmanager_is_online(&error);
5712 short_timeout = TRUE;
5713 g_error_free(error);
5716 short_timeout = TRUE;
5720 imap_main_set_timeout(1);
5722 for (list = account_get_list(); list != NULL; list = list->next) {
5723 PrefsAccount *account = list->data;
5724 if (account->protocol == A_IMAP4) {
5725 RemoteFolder *folder = (RemoteFolder *)account->folder;
5726 if (folder && folder->session) {
5727 IMAPSession *session = (IMAPSession *)folder->session;
5728 if (have_connectivity)
5729 imap_threaded_disconnect(FOLDER(folder));
5730 SESSION(session)->state = SESSION_DISCONNECTED;
5731 SESSION(session)->sock = NULL;
5732 session_destroy(SESSION(session));
5733 folder->session = NULL;
5739 imap_main_set_timeout(prefs_common.io_timeout_secs);
5742 void imap_folder_unref(Folder *folder)
5744 if (((IMAPFolder *)folder)->refcnt > 0)
5745 ((IMAPFolder *)folder)->refcnt--;
5748 void imap_cancel_all(void)
5753 folderlist = folder_get_list();
5754 for (cur = folderlist; cur != NULL; cur = g_list_next(cur)) {
5755 Folder *folder = (Folder *) cur->data;
5757 if (folder->klass == &imap_class) {
5758 if (imap_is_busy(folder)) {
5759 IMAPSession *imap_session;
5760 RemoteFolder *rfolder;
5762 g_printerr("cancelled\n");
5763 imap_threaded_cancel(folder);
5764 rfolder = (RemoteFolder *) folder;
5765 imap_session = (IMAPSession *) rfolder->session;
5767 imap_session->cancelled = 1;
5773 gboolean imap_cancel_all_enabled(void)
5778 folderlist = folder_get_list();
5779 for (cur = folderlist; cur != NULL; cur = g_list_next(cur)) {
5780 Folder *folder = (Folder *) cur->data;
5782 if (folder->klass == &imap_class) {
5783 if (imap_is_busy(folder)) {
5792 static gboolean imap_is_busy(Folder *folder)
5794 IMAPSession *imap_session;
5795 RemoteFolder *rfolder;
5797 rfolder = (RemoteFolder *) folder;
5798 imap_session = (IMAPSession *) rfolder->session;
5799 if (imap_session == NULL)
5802 return imap_session->busy;
5805 #else /* HAVE_LIBETPAN */
5807 static FolderClass imap_class;
5809 static XMLTag *imap_item_get_xml(Folder *folder, FolderItem *item);
5810 static void imap_item_set_xml(Folder *folder, FolderItem *item, XMLTag *tag);
5812 static Folder *imap_folder_new (const gchar *name,
5815 static gboolean missing_imap_warning = TRUE;
5816 if (missing_imap_warning) {
5817 missing_imap_warning = FALSE;
5819 _("You have one or more IMAP accounts "
5820 "defined. However this version of "
5821 "Claws Mail has been built without "
5822 "IMAP support; your IMAP account(s) are "
5824 "You probably need to "
5825 "install libetpan and recompile "
5830 static gint imap_create_tree (Folder *folder)
5834 static FolderItem *imap_create_folder (Folder *folder,
5840 static gint imap_rename_folder (Folder *folder,
5847 gchar imap_get_path_separator_for_item(FolderItem *item)
5852 FolderClass *imap_get_class(void)
5854 if (imap_class.idstr == NULL) {
5855 imap_class.type = F_IMAP;
5856 imap_class.idstr = "imap";
5857 imap_class.uistr = "IMAP4";
5859 imap_class.new_folder = imap_folder_new;
5860 imap_class.create_tree = imap_create_tree;
5861 imap_class.create_folder = imap_create_folder;
5862 imap_class.rename_folder = imap_rename_folder;
5864 imap_class.set_xml = folder_set_xml;
5865 imap_class.get_xml = folder_get_xml;
5866 imap_class.item_set_xml = imap_item_set_xml;
5867 imap_class.item_get_xml = imap_item_get_xml;
5868 /* nothing implemented */
5874 void imap_disconnect_all(gboolean have_connectivity)
5878 gint imap_subscribe(Folder *folder, FolderItem *item, gchar *rpath, gboolean sub)
5883 GList * imap_scan_subtree(Folder *folder, FolderItem *item, gboolean unsubs_only, gboolean recursive)
5888 void imap_cache_msg(FolderItem *item, gint msgnum)
5892 void imap_cancel_all(void)
5896 gboolean imap_cancel_all_enabled(void)
5903 #ifdef HAVE_LIBETPAN
5904 static void imap_synchronise(FolderItem *item, gint days)
5906 if (IMAP_FOLDER_ITEM(item)->last_sync == IMAP_FOLDER_ITEM(item)->last_change) {
5907 debug_print("%s already synced\n", item->path?item->path:item->name);
5910 debug_print("syncing %s\n", item->path?item->path:item->name);
5911 imap_gtk_synchronise(item, days);
5912 IMAP_FOLDER_ITEM(item)->last_sync = IMAP_FOLDER_ITEM(item)->last_change;
5916 static void imap_item_set_xml(Folder *folder, FolderItem *item, XMLTag *tag)
5918 #ifdef HAVE_LIBETPAN
5921 folder_item_set_xml(folder, item, tag);
5923 #ifdef HAVE_LIBETPAN
5924 for (cur = tag->attr; cur != NULL; cur = g_list_next(cur)) {
5925 XMLAttr *attr = (XMLAttr *) cur->data;
5927 if (!attr || !attr->name || !attr->value) continue;
5928 if (!strcmp(attr->name, "uidnext"))
5929 IMAP_FOLDER_ITEM(item)->uid_next = atoi(attr->value);
5930 if (!strcmp(attr->name, "last_sync"))
5931 IMAP_FOLDER_ITEM(item)->last_sync = atoi(attr->value);
5932 if (!strcmp(attr->name, "last_change"))
5933 IMAP_FOLDER_ITEM(item)->last_change = atoi(attr->value);
5935 if (IMAP_FOLDER_ITEM(item)->last_change == 0)
5936 IMAP_FOLDER_ITEM(item)->last_change = time(NULL);
5940 static XMLTag *imap_item_get_xml(Folder *folder, FolderItem *item)
5944 tag = folder_item_get_xml(folder, item);
5946 #ifdef HAVE_LIBETPAN
5947 xml_tag_add_attr(tag, xml_attr_new_int("uidnext",
5948 IMAP_FOLDER_ITEM(item)->uid_next));
5949 xml_tag_add_attr(tag, xml_attr_new_int("last_sync",
5950 IMAP_FOLDER_ITEM(item)->last_sync));
5951 xml_tag_add_attr(tag, xml_attr_new_int("last_change",
5952 IMAP_FOLDER_ITEM(item)->last_change));
5958 /* ===================================================================
5959 * UTF-7 conversion routines as in RFC 2192
5960 * ===================================================================
5961 * These two functions from:
5963 * Copyright (C) 2003-2004 Pawel Salek. */
5965 /* UTF7 modified base64 alphabet */
5966 static char base64chars[] =
5967 "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+,";
5968 #define UNDEFINED 64
5970 /* UTF16 definitions */
5971 #define UTF16MASK 0x03FFUL
5972 #define UTF16SHIFT 10
5973 #define UTF16BASE 0x10000UL
5974 #define UTF16HIGHSTART 0xD800UL
5975 #define UTF16HIGHEND 0xDBFFUL
5976 #define UTF16LOSTART 0xDC00UL
5977 #define UTF16LOEND 0xDFFFUL
5980 /* Convert an IMAP mailbox to a UTF-8 string.
5981 * dst needs to have roughly 4 times the storage space of src
5982 * Hex encoding can triple the size of the input
5983 * UTF-7 can be slightly denser than UTF-8
5984 * (worst case: 8 octets UTF-7 becomes 9 octets UTF-8)
5986 char* imap_modified_utf7_to_utf8(const char *mbox, gboolean change_spaces)
5988 unsigned c, i, bitcount;
5989 unsigned long ucs4, utf16, bitbuf;
5990 unsigned char base64[256];
5992 char *dst, *res = g_malloc(2*strlen(mbox)+1);
5996 if(!dst) return NULL;
5997 /* initialize modified base64 decoding table */
5998 memset(base64, UNDEFINED, sizeof (base64));
5999 for (i = 0; i < sizeof (base64chars); ++i) {
6000 base64[(unsigned)base64chars[i]] = i;
6003 /* loop until end of string */
6004 while (*src != '\0') {
6006 /* deal with literal characters and &- */
6007 if (c != '&' || *src == '-') {
6008 /* encode literally */
6009 if (change_spaces && c == '_')
6013 /* skip over the '-' if this is an &- sequence */
6014 if (c == '&') ++src;
6016 /* convert modified UTF-7 -> UTF-16 -> UCS-4 -> UTF-8 -> HEX */
6020 while ((c = base64[(unsigned char) *src]) != UNDEFINED) {
6022 bitbuf = (bitbuf << 6) | c;
6024 /* enough bits for a UTF-16 character? */
6025 if (bitcount >= 16) {
6027 utf16 = (bitcount ? bitbuf >> bitcount
6029 /* convert UTF16 to UCS4 */
6031 (utf16 >= UTF16HIGHSTART && utf16 <= UTF16HIGHEND) {
6032 ucs4 = (utf16 - UTF16HIGHSTART) << UTF16SHIFT;
6035 (utf16 >= UTF16LOSTART && utf16 <= UTF16LOEND) {
6036 ucs4 += utf16 - UTF16LOSTART + UTF16BASE;
6041 /* convert UTF-16 range of UCS4 to UTF-8 */
6042 if (ucs4 <= 0x7fUL) {
6045 } else if (ucs4 <= 0x7ffUL) {
6046 dst[0] = 0xc0 | (ucs4 >> 6);
6047 dst[1] = 0x80 | (ucs4 & 0x3f);
6049 } else if (ucs4 <= 0xffffUL) {
6050 dst[0] = 0xe0 | (ucs4 >> 12);
6051 dst[1] = 0x80 | ((ucs4 >> 6) & 0x3f);
6052 dst[2] = 0x80 | (ucs4 & 0x3f);
6055 dst[0] = 0xf0 | (ucs4 >> 18);
6056 dst[1] = 0x80 | ((ucs4 >> 12) & 0x3f);
6057 dst[2] = 0x80 | ((ucs4 >> 6) & 0x3f);
6058 dst[3] = 0x80 | (ucs4 & 0x3f);
6063 /* skip over trailing '-' in modified UTF-7 encoding */
6064 if (*src == '-') ++src;
6067 /* terminate destination string */
6072 /* Convert hex coded UTF-8 string to modified UTF-7 IMAP mailbox
6073 * dst should be about twice the length of src to deal with non-hex
6076 char* imap_utf8_to_modified_utf7(const char *src, gboolean change_spaces)
6078 unsigned int utf8pos, utf8total, c, utf7mode, bitstogo, utf16flag;
6079 unsigned long ucs4 = 0, bitbuf = 0;
6081 /* initialize hex lookup table */
6084 if (!src) return NULL;
6086 res = malloc(2*strlen(src)+1);
6088 if(!dst) return NULL;
6094 while ((c = (unsigned char)*src) != '\0') {
6096 /* normal character? */
6097 if (c >= ' ' && c <= '~' && (c != '_' || !change_spaces)) {
6098 /* switch out of UTF-7 mode */
6101 *dst++ = base64chars[(bitbuf << (6 - bitstogo)) & 0x3F];
6109 if (change_spaces && c == ' ')
6113 /* encode '&' as '&-' */
6119 /* switch to UTF-7 mode */
6124 /* Encode US-ASCII characters as themselves */
6127 } else if (utf8total) {
6128 /* save UTF8 bits into UCS4 */
6129 ucs4 = (ucs4 << 6) | (c & 0x3FUL);
6130 if (++utf8pos < utf8total) {
6138 } else if (c < 0xF0) {
6142 /* NOTE: can't convert UTF8 sequences longer than 4 */
6148 /* loop to split ucs4 into two utf16 chars if necessary */
6151 if (ucs4 >= UTF16BASE) {
6153 bitbuf = (bitbuf << 16) | ((ucs4 >> UTF16SHIFT)
6155 ucs4 = (ucs4 & UTF16MASK) + UTF16LOSTART;
6158 bitbuf = (bitbuf << 16) | ucs4;
6162 /* spew out base64 */
6163 while (bitstogo >= 6) {
6165 *dst++ = base64chars[(bitstogo ? (bitbuf >> bitstogo)
6169 } while (utf16flag);
6171 /* if in UTF-7 mode, finish in ASCII */
6174 *dst++ = base64chars[(bitbuf << (6 - bitstogo)) & 0x3F];
6178 /* tie off string */