2 * Claws Mail -- a GTK+ based, lightweight, and fast e-mail client
3 * Copyright (C) 1999-2018 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 <glib/gi18n.h>
30 #include "procheader.h"
31 #include "send_message.h"
33 #include "statusbar.h"
34 #include "prefs_filtering.h"
35 #include "filtering.h"
37 #include "prefs_common.h"
39 #include "alertpanel.h"
43 #include "partial_download.h"
44 #include "mainwindow.h"
45 #include "summaryview.h"
52 extern SessionStats session_stats;
54 static gint procmsg_send_message_queue_full(const gchar *file, gboolean keep_session, gchar **errstr,
55 FolderItem *queue, gint msgnum, gboolean *queued_removed);
56 static void procmsg_update_unread_children (MsgInfo *info,
57 gboolean newly_marked);
64 Q_MAIL_ACCOUNT_ID = 4,
65 Q_NEWS_ACCOUNT_ID = 5,
66 Q_SAVE_COPY_FOLDER = 6,
67 Q_REPLY_MESSAGE_ID = 7,
73 Q_PRIVACY_SYSTEM_OLD = 13,
75 Q_ENCRYPT_DATA_OLD = 15,
76 Q_CLAWS_HDRS_OLD = 16,
79 void procmsg_msg_list_free(GSList *mlist)
84 for (cur = mlist; cur != NULL; cur = cur->next) {
85 msginfo = (MsgInfo *)cur->data;
86 procmsg_msginfo_free(&msginfo);
91 MsgNumberList *procmsg_get_number_list_for_msgs(MsgInfoList *msglist)
96 for (cur = msglist; cur; cur = cur->next) {
97 MsgInfo *msg = (MsgInfo *)cur->data;
98 nums = g_slist_prepend(nums, GUINT_TO_POINTER(msg->msgnum));
101 return g_slist_reverse(nums);
113 /* CLAWS subject threading:
115 in the first round it inserts subject lines in a
116 hashtable (subject <-> node)
118 the second round finishes the threads by attaching
119 matching subject lines to the one found in the
120 hashtable. will use the oldest node with the same
121 subject that is not more then thread_by_subject_max_age
122 days old (see subject_hashtable_lookup)
125 static void subject_hashtable_insert(GHashTable *hashtable, GNode *node)
131 cm_return_if_fail(hashtable != NULL);
132 cm_return_if_fail(node != NULL);
133 msginfo = (MsgInfo *) node->data;
134 cm_return_if_fail(msginfo != NULL);
136 subject = msginfo->subject;
140 subject += subject_get_prefix_length(subject);
142 list = g_hash_table_lookup(hashtable, subject);
143 list = g_slist_prepend(list, node);
144 g_hash_table_insert(hashtable, subject, list);
147 static GNode *subject_hashtable_lookup(GHashTable *hashtable, MsgInfo *msginfo)
151 GNode *node = NULL, *hashtable_node = NULL;
153 MsgInfo *hashtable_msginfo = NULL, *best_msginfo = NULL;
156 cm_return_val_if_fail(hashtable != NULL, NULL);
158 subject = msginfo->subject;
161 prefix_length = subject_get_prefix_length(subject);
162 if (prefix_length <= 0)
164 subject += prefix_length;
166 list = g_hash_table_lookup(hashtable, subject);
170 /* check all nodes with the same subject to find the best parent */
171 for (cur = list; cur; cur = cur->next) {
172 hashtable_node = (GNode *)cur->data;
173 hashtable_msginfo = (MsgInfo *) hashtable_node->data;
176 /* best node should be the oldest in the found nodes */
177 /* parent node must not be older then msginfo */
178 if ((hashtable_msginfo->date_t < msginfo->date_t) &&
179 ((best_msginfo == NULL) ||
180 (best_msginfo->date_t > hashtable_msginfo->date_t)))
183 /* parent node must not be more then thread_by_subject_max_age
184 days older then msginfo */
185 if (abs(difftime(msginfo->date_t, hashtable_msginfo->date_t)) >
186 prefs_common.thread_by_subject_max_age * 3600 * 24)
189 /* can add new tests for all matching
190 nodes found by subject */
193 node = hashtable_node;
194 best_msginfo = hashtable_msginfo;
201 static void subject_hashtable_free(gpointer key, gpointer value, gpointer data)
206 /* return the reversed thread tree */
207 GNode *procmsg_get_thread_tree(GSList *mlist)
209 GNode *root, *parent, *node, *next;
210 GHashTable *msgid_table;
211 GHashTable *subject_hashtable = NULL;
216 root = g_node_new(NULL);
217 msgid_table = g_hash_table_new(g_str_hash, g_str_equal);
219 if (prefs_common.thread_by_subject) {
220 subject_hashtable = g_hash_table_new(g_str_hash, g_str_equal);
223 for (; mlist != NULL; mlist = mlist->next) {
224 msginfo = (MsgInfo *)mlist->data;
227 if (msginfo->inreplyto) {
228 parent = g_hash_table_lookup(msgid_table, msginfo->inreplyto);
229 if (parent == NULL) {
233 node = g_node_insert_data_before
234 (parent, parent == root ? parent->children : NULL,
236 if ((msgid = msginfo->msgid) && g_hash_table_lookup(msgid_table, msgid) == NULL)
237 g_hash_table_insert(msgid_table, (gchar *)msgid, node);
239 /* CLAWS: add subject to hashtable (without prefix) */
240 if (prefs_common.thread_by_subject) {
241 subject_hashtable_insert(subject_hashtable, node);
245 /* complete the unfinished threads */
246 for (node = root->children; node != NULL; ) {
248 msginfo = (MsgInfo *)node->data;
251 if (msginfo->inreplyto)
252 parent = g_hash_table_lookup(msgid_table, msginfo->inreplyto);
254 /* try looking for the indirect parent */
255 if (!parent && msginfo->references) {
256 for (reflist = msginfo->references;
257 reflist != NULL; reflist = reflist->next)
258 if ((parent = g_hash_table_lookup
259 (msgid_table, reflist->data)) != NULL)
263 /* node should not be the parent, and node should not
264 be an ancestor of parent (circular reference) */
265 if (parent && parent != node &&
266 !g_node_is_ancestor(node, parent)) {
269 (parent, parent->children, node);
275 if (prefs_common.thread_by_subject) {
276 START_TIMING("thread by subject");
277 for (node = root->children; node && node != NULL;) {
279 msginfo = (MsgInfo *) node->data;
281 parent = subject_hashtable_lookup(subject_hashtable, msginfo);
283 /* the node may already be threaded by IN-REPLY-TO, so go up
285 find the parent node */
286 if (parent != NULL) {
287 if (g_node_is_ancestor(node, parent))
295 g_node_append(parent, node);
303 if (prefs_common.thread_by_subject)
305 g_hash_table_foreach(subject_hashtable, subject_hashtable_free, NULL);
306 g_hash_table_destroy(subject_hashtable);
309 g_hash_table_destroy(msgid_table);
314 gint procmsg_move_messages(GSList *mlist)
316 GSList *cur, *movelist = NULL;
318 FolderItem *dest = NULL;
320 gboolean finished = TRUE;
321 if (!mlist) return 0;
323 folder_item_update_freeze();
326 for (cur = mlist; cur != NULL; cur = cur->next) {
327 msginfo = (MsgInfo *)cur->data;
328 if (!msginfo->to_folder) {
334 dest = msginfo->to_folder;
335 movelist = g_slist_prepend(movelist, msginfo);
336 } else if (dest == msginfo->to_folder) {
337 movelist = g_slist_prepend(movelist, msginfo);
341 procmsg_msginfo_set_to_folder(msginfo, NULL);
344 movelist = g_slist_reverse(movelist);
345 retval |= folder_item_move_msgs(dest, movelist);
346 g_slist_free(movelist);
349 if (finished == FALSE) {
355 folder_item_update_thaw();
359 void procmsg_copy_messages(GSList *mlist)
361 GSList *cur, *copylist = NULL;
363 FolderItem *dest = NULL;
364 gboolean finished = TRUE;
367 folder_item_update_freeze();
370 for (cur = mlist; cur != NULL; cur = cur->next) {
371 msginfo = (MsgInfo *)cur->data;
372 if (!msginfo->to_folder) {
378 dest = msginfo->to_folder;
379 copylist = g_slist_prepend(copylist, msginfo);
380 } else if (dest == msginfo->to_folder) {
381 copylist = g_slist_prepend(copylist, msginfo);
385 procmsg_msginfo_set_to_folder(msginfo, NULL);
388 copylist = g_slist_reverse(copylist);
389 folder_item_copy_msgs(dest, copylist);
390 g_slist_free(copylist);
393 if (finished == FALSE) {
399 folder_item_update_thaw();
402 gchar *procmsg_get_message_file_path(MsgInfo *msginfo)
404 cm_return_val_if_fail(msginfo != NULL, NULL);
406 return folder_item_fetch_msg(msginfo->folder, msginfo->msgnum);
409 gchar *procmsg_get_message_file(MsgInfo *msginfo)
411 gchar *filename = NULL;
413 cm_return_val_if_fail(msginfo != NULL, NULL);
415 filename = folder_item_fetch_msg(msginfo->folder, msginfo->msgnum);
417 debug_print("can't fetch message %d\n", msginfo->msgnum);
422 gchar *procmsg_get_message_file_full(MsgInfo *msginfo, gboolean headers, gboolean body)
424 gchar *filename = NULL;
426 cm_return_val_if_fail(msginfo != NULL, NULL);
428 filename = folder_item_fetch_msg_full(msginfo->folder, msginfo->msgnum,
431 debug_print("can't fetch message %d\n", msginfo->msgnum);
436 GSList *procmsg_get_message_file_list(GSList *mlist)
438 GSList *file_list = NULL;
440 MsgFileInfo *fileinfo;
443 while (mlist != NULL) {
444 msginfo = (MsgInfo *)mlist->data;
445 file = procmsg_get_message_file(msginfo);
447 procmsg_message_file_list_free(file_list);
450 fileinfo = g_new(MsgFileInfo, 1);
451 fileinfo->msginfo = procmsg_msginfo_new_ref(msginfo);
452 fileinfo->file = file;
453 fileinfo->flags = g_new(MsgFlags, 1);
454 *fileinfo->flags = msginfo->flags;
455 file_list = g_slist_prepend(file_list, fileinfo);
459 file_list = g_slist_reverse(file_list);
464 void procmsg_message_file_list_free(MsgInfoList *file_list)
467 MsgFileInfo *fileinfo;
469 for (cur = file_list; cur != NULL; cur = cur->next) {
470 fileinfo = (MsgFileInfo *)cur->data;
471 procmsg_msginfo_free(&(fileinfo->msginfo));
472 g_free(fileinfo->file);
473 g_free(fileinfo->flags);
477 g_slist_free(file_list);
480 FILE *procmsg_open_message(MsgInfo *msginfo)
485 cm_return_val_if_fail(msginfo != NULL, NULL);
487 file = procmsg_get_message_file_path(msginfo);
488 cm_return_val_if_fail(file != NULL, NULL);
490 if (!is_file_exist(file)) {
492 file = procmsg_get_message_file(msginfo);
497 if ((fp = g_fopen(file, "rb")) == NULL) {
498 FILE_OP_ERROR(file, "fopen");
505 if (MSG_IS_QUEUED(msginfo->flags) || MSG_IS_DRAFT(msginfo->flags)) {
508 while (fgets(buf, sizeof(buf), fp) != NULL) {
510 if ((!strncmp(buf, "X-Claws-End-Special-Headers: 1",
511 strlen("X-Claws-End-Special-Headers:"))) ||
512 (!strncmp(buf, "X-Sylpheed-End-Special-Headers: 1",
513 strlen("X-Sylpheed-End-Special-Headers:"))))
516 if (buf[0] == '\r' || buf[0] == '\n') break;
517 /* from other mailers */
518 if (!strncmp(buf, "Date: ", 6)
519 || !strncmp(buf, "To: ", 4)
520 || !strncmp(buf, "From: ", 6)
521 || !strncmp(buf, "Subject: ", 9)) {
531 gboolean procmsg_msg_exist(MsgInfo *msginfo)
536 if (!msginfo) return FALSE;
538 path = folder_item_get_path(msginfo->folder);
539 ret = !folder_item_is_msg_changed(msginfo->folder, msginfo);
545 void procmsg_get_filter_keyword(MsgInfo *msginfo, gchar **header, gchar **key,
546 PrefsFilterType type)
548 static HeaderEntry hentry[] = {{"X-BeenThere:", NULL, TRUE},
549 {"X-ML-Name:", NULL, TRUE},
550 {"X-List:", NULL, TRUE},
551 {"X-Mailing-list:", NULL, TRUE},
552 {"List-Id:", NULL, TRUE},
553 {"X-Sequence:", NULL, TRUE},
554 {"Sender:", NULL, TRUE},
555 {"List-Post:", NULL, TRUE},
556 {NULL, NULL, FALSE}};
562 H_X_MAILING_LIST = 3,
571 cm_return_if_fail(msginfo != NULL);
572 cm_return_if_fail(header != NULL);
573 cm_return_if_fail(key != NULL);
582 if ((fp = procmsg_open_message(msginfo)) == NULL)
584 procheader_get_header_fields(fp, hentry);
587 #define SET_FILTER_KEY(hstr, idx) \
589 *header = g_strdup(hstr); \
590 *key = hentry[idx].body; \
591 hentry[idx].body = NULL; \
594 if (hentry[H_LIST_ID].body != NULL) {
595 SET_FILTER_KEY("header \"List-Id\"", H_LIST_ID);
596 extract_list_id_str(*key);
597 } else if (hentry[H_X_BEENTHERE].body != NULL) {
598 SET_FILTER_KEY("header \"X-BeenThere\"", H_X_BEENTHERE);
599 } else if (hentry[H_X_ML_NAME].body != NULL) {
600 SET_FILTER_KEY("header \"X-ML-Name\"", H_X_ML_NAME);
601 } else if (hentry[H_X_LIST].body != NULL) {
602 SET_FILTER_KEY("header \"X-List\"", H_X_LIST);
603 } else if (hentry[H_X_MAILING_LIST].body != NULL) {
604 SET_FILTER_KEY("header \"X-Mailing-List\"", H_X_MAILING_LIST);
605 } else if (hentry[H_X_SEQUENCE].body != NULL) {
608 SET_FILTER_KEY("X-Sequence", H_X_SEQUENCE);
611 while (*p != '\0' && !g_ascii_isspace(*p)) p++;
612 while (g_ascii_isspace(*p)) p++;
613 if (g_ascii_isdigit(*p)) {
619 } else if (hentry[H_SENDER].body != NULL) {
620 SET_FILTER_KEY("header \"Sender\"", H_SENDER);
621 } else if (hentry[H_LIST_POST].body != NULL) {
622 SET_FILTER_KEY("header \"List-Post\"", H_LIST_POST);
623 } else if (msginfo->to) {
624 *header = g_strdup("to");
625 *key = g_strdup(msginfo->to);
626 } else if (msginfo->subject) {
627 *header = g_strdup("subject");
628 *key = g_strdup(msginfo->subject);
631 #undef SET_FILTER_KEY
633 g_free(hentry[H_X_BEENTHERE].body);
634 hentry[H_X_BEENTHERE].body = NULL;
635 g_free(hentry[H_X_ML_NAME].body);
636 hentry[H_X_ML_NAME].body = NULL;
637 g_free(hentry[H_X_LIST].body);
638 hentry[H_X_LIST].body = NULL;
639 g_free(hentry[H_X_MAILING_LIST].body);
640 hentry[H_X_MAILING_LIST].body = NULL;
641 g_free(hentry[H_LIST_ID].body);
642 hentry[H_LIST_ID].body = NULL;
643 g_free(hentry[H_SENDER].body);
644 hentry[H_SENDER].body = NULL;
645 g_free(hentry[H_LIST_POST].body);
646 hentry[H_LIST_POST].body = NULL;
650 *header = g_strdup("from");
651 *key = g_strdup(msginfo->from);
654 *header = g_strdup("to");
655 *key = g_strdup(msginfo->to);
657 case FILTER_BY_SUBJECT:
658 *header = g_strdup("subject");
659 *key = g_strdup(msginfo->subject);
666 static void procmsg_empty_trash(FolderItem *trash)
671 (trash->stype != F_TRASH &&
672 !folder_has_parent_of_type(trash, F_TRASH)))
675 if (trash && trash->total_msgs > 0) {
676 GSList *mlist = folder_item_get_msg_list(trash);
678 for (cur = mlist ; cur != NULL ; cur = cur->next) {
679 MsgInfo * msginfo = (MsgInfo *) cur->data;
680 if (MSG_IS_LOCKED(msginfo->flags)) {
681 procmsg_msginfo_free(&msginfo);
684 if (msginfo->total_size != 0 &&
685 msginfo->size != (off_t)msginfo->total_size)
686 partial_mark_for_delete(msginfo);
688 procmsg_msginfo_free(&msginfo);
691 folder_item_remove_all_msg(trash);
694 if (!trash->node || !trash->node->children)
697 node = trash->node->children;
698 while (node != NULL) {
700 procmsg_empty_trash(FOLDER_ITEM(node->data));
705 void procmsg_empty_all_trash(void)
710 for (cur = folder_get_list(); cur != NULL; cur = cur->next) {
711 Folder *folder = FOLDER(cur->data);
712 trash = folder->trash;
713 procmsg_empty_trash(trash);
714 if (folder->account && folder->account->set_trash_folder &&
715 folder_find_item_from_identifier(folder->account->trash_folder))
717 folder_find_item_from_identifier(folder->account->trash_folder));
721 static PrefsAccount *procmsg_get_account_from_file(const gchar *file)
723 PrefsAccount *mailac = NULL;
727 static HeaderEntry qentry[] = {{"S:", NULL, FALSE},
728 {"SSV:", NULL, FALSE},
730 {"NG:", NULL, FALSE},
731 {"MAID:", NULL, FALSE},
732 {"NAID:", NULL, FALSE},
733 {"SCF:", NULL, FALSE},
734 {"RMID:", NULL, FALSE},
735 {"FMID:", NULL, FALSE},
736 {"X-Claws-Privacy-System:", NULL, FALSE},
737 {"X-Claws-Encrypt:", NULL, FALSE},
738 {"X-Claws-Encrypt-Data:", NULL, FALSE},
739 {"X-Claws-End-Special-Headers", NULL, FALSE},
740 {"X-Sylpheed-Privacy-System:", NULL, FALSE},
741 {"X-Sylpheed-Encrypt:", NULL, FALSE},
742 {"X-Sylpheed-Encrypt-Data:", NULL, FALSE},
743 {NULL, NULL, FALSE}};
745 cm_return_val_if_fail(file != NULL, NULL);
747 if ((fp = g_fopen(file, "rb")) == NULL) {
748 FILE_OP_ERROR(file, "fopen");
752 while ((hnum = procheader_get_one_field(&buf, fp, qentry)) != -1 && buf != NULL) {
753 gchar *p = buf + strlen(qentry[hnum].name);
755 if (hnum == Q_MAIL_ACCOUNT_ID) {
756 mailac = account_find_from_id(atoi(p));
766 gchar *procmsg_msginfo_get_avatar(MsgInfo *msginfo, gint type)
770 if (!msginfo || !msginfo->extradata || !msginfo->extradata->avatars)
773 for (mia = msginfo->extradata->avatars; mia; mia = mia->next) {
774 MsgInfoAvatar *avatar = (MsgInfoAvatar *)mia->data;
775 if (avatar->avatar_id == type)
776 return avatar->avatar_src;
782 void procmsg_msginfo_add_avatar(MsgInfo *msginfo, gint type, const gchar *data)
786 if (!msginfo->extradata)
787 msginfo->extradata = g_new0(MsgInfoExtraData, 1);
789 av = g_new0(MsgInfoAvatar, 1);
790 av->avatar_id = type;
791 av->avatar_src = g_strdup(data);
793 msginfo->extradata->avatars = g_slist_append(msginfo->extradata->avatars, av);
796 gchar *procmsg_msginfo_get_identifier(MsgInfo *msginfo)
802 cm_return_val_if_fail(msginfo != NULL, NULL);
803 folder_id = folder_item_get_identifier(msginfo->folder);
804 msgid = msginfo->msgid;
806 id = g_strconcat(folder_id, G_DIR_SEPARATOR_S, msgid, NULL);
813 MsgInfo *procmsg_get_msginfo_from_identifier(const gchar *id)
815 gchar *folder_id = g_strdup(id);
816 gchar *separator = strrchr(folder_id, G_DIR_SEPARATOR);
821 if (separator == NULL) {
827 msgid = separator + 1;
829 item = folder_find_item_from_identifier(folder_id);
836 msginfo = folder_item_get_msginfo_by_msgid(item, msgid);
842 static GSList *procmsg_list_sort_by_account(FolderItem *queue, GSList *list)
844 GSList *result = NULL;
846 PrefsAccount *last_account = NULL;
849 gboolean nothing_to_sort = TRUE;
854 orig = g_slist_copy(list);
856 msg = (MsgInfo *)orig->data;
858 for (cur = orig; cur; cur = cur->next)
859 debug_print("sort before %s\n", ((MsgInfo *)cur->data)->from);
864 nothing_to_sort = TRUE;
868 PrefsAccount *ac = NULL;
869 msg = (MsgInfo *)cur->data;
870 file = folder_item_fetch_msg(queue, msg->msgnum);
871 ac = procmsg_get_account_from_file(file);
874 if (last_account == NULL || (ac != NULL && ac == last_account)) {
875 result = g_slist_append(result, msg);
876 orig = g_slist_remove(orig, msg);
878 nothing_to_sort = FALSE;
884 if (orig && g_slist_length(orig)) {
885 if (!last_account && nothing_to_sort) {
886 /* can't find an account for the rest of the list */
889 result = g_slist_append(result, cur->data);
900 for (cur = result; cur; cur = cur->next)
901 debug_print("sort after %s\n", ((MsgInfo *)cur->data)->from);
908 static gboolean procmsg_is_last_for_account(FolderItem *queue, MsgInfo *msginfo, GSList *elem)
910 gchar *file = folder_item_fetch_msg(queue, msginfo->msgnum);
911 PrefsAccount *ac = procmsg_get_account_from_file(file);
914 for (cur = elem; cur; cur = cur->next) {
915 MsgInfo *cur_msginfo = (MsgInfo *)cur->data;
916 file = folder_item_fetch_msg(queue, cur_msginfo->msgnum);
918 if (cur_msginfo != msginfo && !MSG_IS_LOCKED(cur_msginfo->flags)) {
919 if (procmsg_get_account_from_file(file) == ac) {
930 static gboolean send_queue_lock = FALSE;
932 gboolean procmsg_queue_lock(char **errstr)
934 if (send_queue_lock) {
935 /* Avoid having to translate two similar strings */
936 log_warning(LOG_PROTOCOL, "%s\n", _("Already trying to send."));
938 if (*errstr) g_free(*errstr);
939 *errstr = g_strdup_printf(_("Already trying to send."));
943 send_queue_lock = TRUE;
946 void procmsg_queue_unlock(void)
948 send_queue_lock = FALSE;
951 *\brief Send messages in queue
953 *\param queue Queue folder to process
954 *\param save_msgs Unused
956 *\return Number of messages sent, negative if an error occurred
957 * positive if no error occurred
959 gint procmsg_send_queue(FolderItem *queue, gboolean save_msgs, gchar **errstr)
961 gint sent = 0, err = 0;
963 GSList *sorted_list = NULL;
966 if (!procmsg_queue_lock(errstr)) {
967 main_window_set_menu_sensitive(mainwindow_get_mainwindow());
968 toolbar_main_set_sensitive(mainwindow_get_mainwindow());
973 queue = folder_get_default_queue();
976 procmsg_queue_unlock();
981 main_window_set_menu_sensitive(mainwindow_get_mainwindow());
982 toolbar_main_set_sensitive(mainwindow_get_mainwindow());
984 folder_item_scan(queue);
985 list = folder_item_get_msg_list(queue);
987 /* sort the list per sender account; this helps reusing the same SMTP server */
988 sorted_list = procmsg_list_sort_by_account(queue, list);
990 for (elem = sorted_list; elem != NULL; elem = elem->next) {
994 msginfo = (MsgInfo *)(elem->data);
995 if (!MSG_IS_LOCKED(msginfo->flags) && !MSG_IS_DELETED(msginfo->flags)) {
996 file = folder_item_fetch_msg(queue, msginfo->msgnum);
998 gboolean queued_removed = FALSE;
999 if (procmsg_send_message_queue_full(file,
1000 !procmsg_is_last_for_account(queue, msginfo, elem),
1001 errstr, queue, msginfo->msgnum, &queued_removed) < 0) {
1002 g_warning("Sending queued message %d failed.",
1007 if (!queued_removed)
1008 folder_item_remove_msg(queue, msginfo->msgnum);
1013 /* FIXME: supposedly if only one message is locked, and queue
1014 * is being flushed, the following free says something like
1015 * "freeing msg ## in folder (nil)". */
1016 procmsg_msginfo_free(&msginfo);
1019 g_slist_free(sorted_list);
1020 folder_item_scan(queue);
1022 if (queue->node && queue->node->children) {
1023 node = queue->node->children;
1024 while (node != NULL) {
1027 send_queue_lock = FALSE;
1028 res = procmsg_send_queue(FOLDER_ITEM(node->data), save_msgs, errstr);
1029 send_queue_lock = TRUE;
1037 procmsg_queue_unlock();
1039 main_window_set_menu_sensitive(mainwindow_get_mainwindow());
1040 toolbar_main_set_sensitive(mainwindow_get_mainwindow());
1042 return (err != 0 ? -err : sent);
1045 gboolean procmsg_is_sending(void)
1047 return send_queue_lock;
1051 *\brief Determine if a queue folder is empty
1053 *\param queue Queue folder to process
1055 *\return TRUE if the queue folder is empty, otherwise return FALSE
1057 gboolean procmsg_queue_is_empty(FolderItem *queue)
1060 gboolean res = FALSE;
1062 queue = folder_get_default_queue();
1063 cm_return_val_if_fail(queue != NULL, TRUE);
1065 folder_item_scan(queue);
1066 list = folder_item_get_msg_list(queue);
1067 res = (list == NULL);
1068 procmsg_msg_list_free(list);
1072 if (queue->node && queue->node->children) {
1073 node = queue->node->children;
1074 while (node != NULL) {
1076 if (!procmsg_queue_is_empty(FOLDER_ITEM(node->data)))
1085 gint procmsg_remove_special_headers(const gchar *in, const gchar *out)
1088 gchar buf[BUFFSIZE];
1090 if ((fp = g_fopen(in, "rb")) == NULL) {
1091 FILE_OP_ERROR(in, "fopen");
1094 if ((outfp = g_fopen(out, "wb")) == NULL) {
1095 FILE_OP_ERROR(out, "fopen");
1099 while (fgets(buf, sizeof(buf), fp) != NULL) {
1101 if ((!strncmp(buf, "X-Claws-End-Special-Headers: 1",
1102 strlen("X-Claws-End-Special-Headers:"))) ||
1103 (!strncmp(buf, "X-Sylpheed-End-Special-Headers: 1",
1104 strlen("X-Sylpheed-End-Special-Headers:"))))
1107 if (buf[0] == '\r' || buf[0] == '\n') break;
1108 /* from other mailers */
1109 if (!strncmp(buf, "Date: ", 6)
1110 || !strncmp(buf, "To: ", 4)
1111 || !strncmp(buf, "From: ", 6)
1112 || !strncmp(buf, "Subject: ", 9)) {
1117 while (fgets(buf, sizeof(buf), fp) != NULL) {
1118 if (fputs(buf, outfp) == EOF) {
1119 FILE_OP_ERROR(out, "fputs");
1130 gint procmsg_save_to_outbox(FolderItem *outbox, const gchar *file,
1134 MsgInfo *msginfo, *tmp_msginfo;
1135 MsgFlags flag = {0, 0};
1137 debug_print("saving sent message...\n");
1140 outbox = folder_get_default_outbox();
1141 cm_return_val_if_fail(outbox != NULL, -1);
1143 /* remove queueing headers */
1145 gchar tmp[MAXPATHLEN + 1];
1147 g_snprintf(tmp, sizeof(tmp), "%s%ctmpmsg.out.%08x",
1148 get_rc_dir(), G_DIR_SEPARATOR, (guint) rand());
1150 if (procmsg_remove_special_headers(file, tmp) !=0)
1153 folder_item_scan(outbox);
1154 if ((num = folder_item_add_msg(outbox, tmp, &flag, TRUE)) < 0) {
1155 g_warning("can't save message");
1160 folder_item_scan(outbox);
1161 if ((num = folder_item_add_msg
1162 (outbox, file, &flag, FALSE)) < 0) {
1163 g_warning("can't save message");
1167 msginfo = folder_item_get_msginfo(outbox, num); /* refcnt++ */
1168 tmp_msginfo = procmsg_msginfo_get_full_info(msginfo); /* refcnt++ */
1169 if (msginfo != NULL) {
1170 procmsg_msginfo_unset_flags(msginfo, ~0, 0);
1171 procmsg_msginfo_free(&msginfo); /* refcnt-- */
1172 /* tmp_msginfo == msginfo */
1173 if (tmp_msginfo && msginfo->extradata &&
1174 (msginfo->extradata->dispositionnotificationto ||
1175 msginfo->extradata->returnreceiptto)) {
1176 procmsg_msginfo_set_flags(msginfo, MSG_RETRCPT_SENT, 0);
1178 procmsg_msginfo_free(&tmp_msginfo); /* refcnt-- */
1185 MsgInfo *procmsg_msginfo_new_ref(MsgInfo *msginfo)
1192 MsgInfo *procmsg_msginfo_new(void)
1194 MsgInfo *newmsginfo;
1196 newmsginfo = g_new0(MsgInfo, 1);
1197 newmsginfo->refcnt = 1;
1202 static MsgInfoAvatar *procmsg_msginfoavatar_copy(MsgInfoAvatar *avatar)
1204 MsgInfoAvatar *newavatar;
1206 if (avatar == NULL) return NULL;
1208 newavatar = g_new0(MsgInfoAvatar, 1);
1209 newavatar->avatar_id = avatar->avatar_id;
1210 newavatar->avatar_src = g_strdup(avatar->avatar_src);
1215 static void procmsg_msginfoavatar_free(MsgInfoAvatar *avatar)
1217 if (avatar != NULL) {
1218 if (avatar->avatar_src != NULL)
1219 g_free(avatar->avatar_src);
1224 MsgInfo *procmsg_msginfo_copy(MsgInfo *msginfo)
1226 MsgInfo *newmsginfo;
1229 if (msginfo == NULL) return NULL;
1231 newmsginfo = g_new0(MsgInfo, 1);
1233 newmsginfo->refcnt = 1;
1235 #define MEMBCOPY(mmb) newmsginfo->mmb = msginfo->mmb
1236 #define MEMBDUP(mmb) newmsginfo->mmb = msginfo->mmb ? \
1237 g_strdup(msginfo->mmb) : NULL
1252 MEMBDUP(newsgroups);
1259 MEMBCOPY(to_folder);
1261 if (msginfo->extradata) {
1262 newmsginfo->extradata = g_new0(MsgInfoExtraData, 1);
1263 if (msginfo->extradata->avatars) {
1264 newmsginfo->extradata->avatars = slist_copy_deep(msginfo->extradata->avatars,
1265 (GCopyFunc) procmsg_msginfoavatar_copy);
1267 MEMBDUP(extradata->dispositionnotificationto);
1268 MEMBDUP(extradata->returnreceiptto);
1269 MEMBDUP(extradata->partial_recv);
1270 MEMBDUP(extradata->account_server);
1271 MEMBDUP(extradata->account_login);
1272 MEMBDUP(extradata->list_post);
1273 MEMBDUP(extradata->list_subscribe);
1274 MEMBDUP(extradata->list_unsubscribe);
1275 MEMBDUP(extradata->list_help);
1276 MEMBDUP(extradata->list_archive);
1277 MEMBDUP(extradata->list_owner);
1278 MEMBDUP(extradata->resent_from);
1281 refs = msginfo->references;
1282 for (refs = msginfo->references; refs != NULL; refs = refs->next) {
1283 newmsginfo->references = g_slist_prepend
1284 (newmsginfo->references, g_strdup(refs->data));
1286 newmsginfo->references = g_slist_reverse(newmsginfo->references);
1289 MEMBDUP(plaintext_file);
1294 MsgInfo *procmsg_msginfo_get_full_info_from_file(MsgInfo *msginfo, const gchar *file)
1296 MsgInfo *full_msginfo;
1298 if (msginfo == NULL) return NULL;
1300 if (!file || !is_file_exist(file)) {
1301 g_warning("procmsg_msginfo_get_full_info_from_file(): can't get message file.");
1305 full_msginfo = procheader_parse_file(file, msginfo->flags, TRUE, FALSE);
1306 if (!full_msginfo) return NULL;
1308 msginfo->total_size = full_msginfo->total_size;
1309 msginfo->planned_download = full_msginfo->planned_download;
1311 if (full_msginfo->extradata) {
1312 if (!msginfo->extradata)
1313 msginfo->extradata = g_new0(MsgInfoExtraData, 1);
1314 if (!msginfo->extradata->list_post)
1315 msginfo->extradata->list_post = g_strdup(full_msginfo->extradata->list_post);
1316 if (!msginfo->extradata->list_subscribe)
1317 msginfo->extradata->list_subscribe = g_strdup(full_msginfo->extradata->list_subscribe);
1318 if (!msginfo->extradata->list_unsubscribe)
1319 msginfo->extradata->list_unsubscribe = g_strdup(full_msginfo->extradata->list_unsubscribe);
1320 if (!msginfo->extradata->list_help)
1321 msginfo->extradata->list_help = g_strdup(full_msginfo->extradata->list_help);
1322 if (!msginfo->extradata->list_archive)
1323 msginfo->extradata->list_archive= g_strdup(full_msginfo->extradata->list_archive);
1324 if (!msginfo->extradata->list_owner)
1325 msginfo->extradata->list_owner = g_strdup(full_msginfo->extradata->list_owner);
1326 if (!msginfo->extradata->avatars)
1327 msginfo->extradata->avatars = slist_copy_deep(full_msginfo->extradata->avatars,
1328 (GCopyFunc) procmsg_msginfoavatar_copy);
1329 if (!msginfo->extradata->dispositionnotificationto)
1330 msginfo->extradata->dispositionnotificationto =
1331 g_strdup(full_msginfo->extradata->dispositionnotificationto);
1332 if (!msginfo->extradata->returnreceiptto)
1333 msginfo->extradata->returnreceiptto = g_strdup
1334 (full_msginfo->extradata->returnreceiptto);
1335 if (!msginfo->extradata->partial_recv && full_msginfo->extradata->partial_recv)
1336 msginfo->extradata->partial_recv = g_strdup
1337 (full_msginfo->extradata->partial_recv);
1338 if (!msginfo->extradata->account_server && full_msginfo->extradata->account_server)
1339 msginfo->extradata->account_server = g_strdup
1340 (full_msginfo->extradata->account_server);
1341 if (!msginfo->extradata->account_login && full_msginfo->extradata->account_login)
1342 msginfo->extradata->account_login = g_strdup
1343 (full_msginfo->extradata->account_login);
1344 if (!msginfo->extradata->resent_from && full_msginfo->extradata->resent_from)
1345 msginfo->extradata->resent_from = g_strdup
1346 (full_msginfo->extradata->resent_from);
1348 procmsg_msginfo_free(&full_msginfo);
1350 return procmsg_msginfo_new_ref(msginfo);
1353 MsgInfo *procmsg_msginfo_get_full_info(MsgInfo *msginfo)
1355 MsgInfo *full_msginfo;
1358 if (msginfo == NULL) return NULL;
1360 file = procmsg_get_message_file_path(msginfo);
1361 if (!file || !is_file_exist(file)) {
1363 file = procmsg_get_message_file(msginfo);
1365 if (!file || !is_file_exist(file)) {
1366 g_warning("procmsg_msginfo_get_full_info(): can't get message file.");
1370 full_msginfo = procmsg_msginfo_get_full_info_from_file(msginfo, file);
1372 return full_msginfo;
1375 #define FREENULL(n) { g_free(n); n = NULL; }
1376 void procmsg_msginfo_free(MsgInfo **msginfo_ptr)
1378 MsgInfo *msginfo = *msginfo_ptr;
1380 if (msginfo == NULL) return;
1383 if (msginfo->refcnt > 0)
1386 if (msginfo->to_folder) {
1387 msginfo->to_folder->op_count--;
1388 folder_item_update(msginfo->to_folder, F_ITEM_UPDATE_MSGCNT);
1391 FREENULL(msginfo->fromspace);
1393 FREENULL(msginfo->fromname);
1395 FREENULL(msginfo->date);
1396 FREENULL(msginfo->from);
1397 FREENULL(msginfo->to);
1398 FREENULL(msginfo->cc);
1399 FREENULL(msginfo->newsgroups);
1400 FREENULL(msginfo->subject);
1401 FREENULL(msginfo->msgid);
1402 FREENULL(msginfo->inreplyto);
1403 FREENULL(msginfo->xref);
1405 if (msginfo->extradata) {
1406 if (msginfo->extradata->avatars) {
1407 g_slist_foreach(msginfo->extradata->avatars,
1408 (GFunc)procmsg_msginfoavatar_free,
1410 g_slist_free(msginfo->extradata->avatars);
1411 msginfo->extradata->avatars = NULL;
1413 FREENULL(msginfo->extradata->returnreceiptto);
1414 FREENULL(msginfo->extradata->dispositionnotificationto);
1415 FREENULL(msginfo->extradata->list_post);
1416 FREENULL(msginfo->extradata->list_subscribe);
1417 FREENULL(msginfo->extradata->list_unsubscribe);
1418 FREENULL(msginfo->extradata->list_help);
1419 FREENULL(msginfo->extradata->list_archive);
1420 FREENULL(msginfo->extradata->list_owner);
1421 FREENULL(msginfo->extradata->partial_recv);
1422 FREENULL(msginfo->extradata->account_server);
1423 FREENULL(msginfo->extradata->account_login);
1424 FREENULL(msginfo->extradata->resent_from);
1425 FREENULL(msginfo->extradata);
1427 slist_free_strings_full(msginfo->references);
1428 msginfo->references = NULL;
1429 g_slist_free(msginfo->tags);
1430 msginfo->tags = NULL;
1432 FREENULL(msginfo->plaintext_file);
1435 *msginfo_ptr = NULL;
1439 guint procmsg_msginfo_memusage(MsgInfo *msginfo)
1444 memusage += sizeof(MsgInfo);
1445 if (msginfo->fromname)
1446 memusage += strlen(msginfo->fromname);
1448 memusage += strlen(msginfo->date);
1450 memusage += strlen(msginfo->from);
1452 memusage += strlen(msginfo->to);
1454 memusage += strlen(msginfo->cc);
1455 if (msginfo->newsgroups)
1456 memusage += strlen(msginfo->newsgroups);
1457 if (msginfo->subject)
1458 memusage += strlen(msginfo->subject);
1460 memusage += strlen(msginfo->msgid);
1461 if (msginfo->inreplyto)
1462 memusage += strlen(msginfo->inreplyto);
1464 for (tmp = msginfo->references; tmp; tmp=tmp->next) {
1465 gchar *r = (gchar *)tmp->data;
1466 memusage += r?strlen(r):0 + sizeof(GSList);
1468 if (msginfo->fromspace)
1469 memusage += strlen(msginfo->fromspace);
1471 for (tmp = msginfo->tags; tmp; tmp=tmp->next) {
1472 memusage += sizeof(GSList);
1474 if (msginfo->extradata) {
1475 memusage += sizeof(MsgInfoExtraData);
1476 if (msginfo->extradata->avatars) {
1477 for (tmp = msginfo->extradata->avatars; tmp; tmp = tmp->next) {
1478 MsgInfoAvatar *avt = (MsgInfoAvatar *)tmp->data;
1479 memusage += (avt->avatar_src)? strlen(avt->avatar_src): 0;
1480 memusage += sizeof(MsgInfoAvatar) + sizeof(GSList);
1483 if (msginfo->extradata->dispositionnotificationto)
1484 memusage += strlen(msginfo->extradata->dispositionnotificationto);
1485 if (msginfo->extradata->returnreceiptto)
1486 memusage += strlen(msginfo->extradata->returnreceiptto);
1488 if (msginfo->extradata->partial_recv)
1489 memusage += strlen(msginfo->extradata->partial_recv);
1490 if (msginfo->extradata->account_server)
1491 memusage += strlen(msginfo->extradata->account_server);
1492 if (msginfo->extradata->account_login)
1493 memusage += strlen(msginfo->extradata->account_login);
1494 if (msginfo->extradata->resent_from)
1495 memusage += strlen(msginfo->extradata->resent_from);
1497 if (msginfo->extradata->list_post)
1498 memusage += strlen(msginfo->extradata->list_post);
1499 if (msginfo->extradata->list_subscribe)
1500 memusage += strlen(msginfo->extradata->list_subscribe);
1501 if (msginfo->extradata->list_unsubscribe)
1502 memusage += strlen(msginfo->extradata->list_unsubscribe);
1503 if (msginfo->extradata->list_help)
1504 memusage += strlen(msginfo->extradata->list_help);
1505 if (msginfo->extradata->list_archive)
1506 memusage += strlen(msginfo->extradata->list_archive);
1507 if (msginfo->extradata->list_owner)
1508 memusage += strlen(msginfo->extradata->list_owner);
1513 static gint procmsg_send_message_queue_full(const gchar *file, gboolean keep_session, gchar **errstr,
1514 FolderItem *queue, gint msgnum, gboolean *queued_removed)
1516 static HeaderEntry qentry[] = {
1517 {"S:", NULL, FALSE}, /* 0 */
1518 {"SSV:", NULL, FALSE},
1519 {"R:", NULL, FALSE},
1520 {"NG:", NULL, FALSE},
1521 {"MAID:", NULL, FALSE},
1522 {"NAID:", NULL, FALSE}, /* 5 */
1523 {"SCF:", NULL, FALSE},
1524 {"RMID:", NULL, FALSE},
1525 {"FMID:", NULL, FALSE},
1526 {"X-Claws-Privacy-System:", NULL, FALSE},
1527 {"X-Claws-Encrypt:", NULL, FALSE}, /* 10 */
1528 {"X-Claws-Encrypt-Data:", NULL, FALSE},
1529 {"X-Claws-End-Special-Headers:", NULL, FALSE},
1530 {"X-Sylpheed-Privacy-System:", NULL, FALSE},
1531 {"X-Sylpheed-Encrypt:", NULL, FALSE},
1532 {"X-Sylpheed-Encrypt-Data:", NULL, FALSE}, /* 15 */
1533 {"X-Sylpheed-End-Special-Headers:", NULL, FALSE},
1534 {NULL, NULL, FALSE}};
1537 gint mailval = 0, newsval = 0;
1539 gchar *smtpserver = NULL;
1540 GSList *to_list = NULL;
1541 GSList *newsgroup_list = NULL;
1542 gchar *savecopyfolder = NULL;
1543 gchar *replymessageid = NULL;
1544 gchar *fwdmessageid = NULL;
1547 PrefsAccount *mailac = NULL, *newsac = NULL;
1548 gboolean encrypt = FALSE;
1551 cm_return_val_if_fail(file != NULL, -1);
1553 if ((fp = g_fopen(file, "rb")) == NULL) {
1554 FILE_OP_ERROR(file, "fopen");
1556 if (*errstr) g_free(*errstr);
1557 *errstr = g_strdup_printf(_("Couldn't open file %s."), file);
1562 while ((hnum = procheader_get_one_field(&buf, fp, qentry)) != -1 && buf != NULL) {
1563 gchar *p = buf + strlen(qentry[hnum].name);
1571 if (smtpserver == NULL)
1572 smtpserver = g_strdup(p);
1575 to_list = address_list_append(to_list, p);
1578 newsgroup_list = newsgroup_list_append(newsgroup_list, p);
1580 case Q_MAIL_ACCOUNT_ID:
1581 mailac = account_find_from_id(atoi(p));
1583 case Q_NEWS_ACCOUNT_ID:
1584 newsac = account_find_from_id(atoi(p));
1586 case Q_SAVE_COPY_FOLDER:
1587 if (savecopyfolder == NULL)
1588 savecopyfolder = g_strdup(p);
1590 case Q_REPLY_MESSAGE_ID:
1591 if (replymessageid == NULL)
1592 replymessageid = g_strdup(p);
1594 case Q_FWD_MESSAGE_ID:
1595 if (fwdmessageid == NULL)
1596 fwdmessageid = g_strdup(p);
1604 case Q_CLAWS_HDRS_OLD:
1605 /* end of special headers reached */
1607 goto send_mail; /* can't "break;break;" */
1613 filepos = ftell(fp);
1615 FILE_OP_ERROR(file, "ftell");
1617 if (*errstr) g_free(*errstr);
1618 *errstr = g_strdup_printf(_("Couldn't open file %s."), file);
1624 debug_print("Sending message by mail\n");
1627 if (*errstr) g_free(*errstr);
1628 *errstr = g_strdup_printf(_("Queued message header is broken."));
1631 } else if (mailac && mailac->use_mail_command &&
1632 mailac->mail_command && (* mailac->mail_command)) {
1633 mailval = send_message_local(mailac->mail_command, fp);
1636 mailac = account_find_from_smtp_server(from, smtpserver);
1638 g_warning("Account not found. "
1639 "Using current account...");
1640 mailac = cur_account;
1645 mailval = send_message_smtp_full(mailac, to_list, fp, keep_session);
1646 if (mailval == -1 && errstr) {
1647 if (*errstr) g_free(*errstr);
1648 *errstr = g_strdup_printf(_("An error happened during SMTP session."));
1651 PrefsAccount tmp_ac;
1653 g_warning("Account not found.");
1655 memset(&tmp_ac, 0, sizeof(PrefsAccount));
1656 tmp_ac.address = from;
1657 tmp_ac.smtp_server = smtpserver;
1658 tmp_ac.smtpport = SMTP_PORT;
1659 mailval = send_message_smtp(&tmp_ac, to_list, fp);
1660 if (mailval == -1 && errstr) {
1661 if (*errstr) g_free(*errstr);
1662 *errstr = g_strdup_printf(_("No specific account has been found to "
1663 "send, and an error happened during SMTP session."));
1667 } else if (!to_list && !newsgroup_list) {
1669 if (*errstr) g_free(*errstr);
1670 *errstr = g_strdup(_("Couldn't determine sending information. "
1671 "Maybe the email hasn't been generated by Claws Mail."));
1676 if (fseek(fp, filepos, SEEK_SET) < 0) {
1677 FILE_OP_ERROR(file, "fseek");
1681 if (newsgroup_list && newsac && (mailval == 0)) {
1684 gchar buf[BUFFSIZE];
1687 /* write to temporary file */
1688 tmp = g_strdup_printf("%s%cnntp%p", get_tmp_dir(),
1689 G_DIR_SEPARATOR, file);
1690 if ((tmpfp = g_fopen(tmp, "wb")) == NULL) {
1691 FILE_OP_ERROR(tmp, "fopen");
1693 alertpanel_error(_("Couldn't create temporary file for news sending."));
1695 if (change_file_mode_rw(tmpfp, tmp) < 0) {
1696 FILE_OP_ERROR(tmp, "chmod");
1697 g_warning("can't change file mode");
1700 while ((newsval == 0) && fgets(buf, sizeof(buf), fp) != NULL) {
1701 if (fputs(buf, tmpfp) == EOF) {
1702 FILE_OP_ERROR(tmp, "fputs");
1705 if (*errstr) g_free(*errstr);
1706 *errstr = g_strdup_printf(_("Error when writing temporary file for news sending."));
1713 debug_print("Sending message by news\n");
1715 folder = FOLDER(newsac->folder);
1717 newsval = news_post(folder, tmp);
1718 if (newsval < 0 && errstr) {
1719 if (*errstr) g_free(*errstr);
1720 *errstr = g_strdup_printf(_("Error occurred while posting the message to %s."),
1721 newsac->nntp_server);
1731 /* update session statistics */
1732 if (mailval == 0 && newsval == 0) {
1733 /* update session stats */
1735 session_stats.replied++;
1736 else if (fwdmessageid)
1737 session_stats.forwarded++;
1739 session_stats.sent++;
1742 /* save message to outbox */
1743 if (mailval == 0 && newsval == 0 && savecopyfolder) {
1744 debug_print("saving sent message...\n");
1746 if (!encrypt || !mailac->save_encrypted_as_clear_text) {
1747 outbox = folder_find_item_from_identifier(savecopyfolder);
1749 outbox = folder_get_default_outbox();
1751 /* Mail was not saved to outbox before encrypting, save it now. */
1752 gboolean saved = FALSE;
1753 *queued_removed = FALSE;
1754 if (queue && msgnum > 0) {
1755 MsgInfo *queued_mail = folder_item_get_msginfo(queue, msgnum);
1756 if (folder_item_move_msg(outbox, queued_mail) >= 0) {
1757 debug_print("moved queued mail %d to sent folder\n", msgnum);
1759 *queued_removed = TRUE;
1760 } else if (folder_item_copy_msg(outbox, queued_mail) >= 0) {
1761 debug_print("copied queued mail %d to sent folder\n", msgnum);
1764 procmsg_msginfo_free(&queued_mail);
1767 debug_print("resaving queued mail to sent folder\n");
1768 procmsg_save_to_outbox(outbox, file, TRUE);
1773 if (replymessageid != NULL || fwdmessageid != NULL) {
1777 if (replymessageid != NULL)
1778 tokens = g_strsplit(replymessageid, "\t", 0);
1780 tokens = g_strsplit(fwdmessageid, "\t", 0);
1781 item = folder_find_item_from_identifier(tokens[0]);
1783 /* check if queued message has valid folder and message id */
1784 if (item != NULL && tokens[2] != NULL) {
1787 msginfo = folder_item_get_msginfo(item, atoi(tokens[1]));
1789 /* check if referring message exists and has a message id */
1790 if ((msginfo != NULL) &&
1791 (msginfo->msgid != NULL) &&
1792 (strcmp(msginfo->msgid, tokens[2]) != 0)) {
1793 procmsg_msginfo_free(&msginfo);
1797 if (msginfo == NULL) {
1798 msginfo = folder_item_get_msginfo_by_msgid(item, tokens[2]);
1801 if (msginfo != NULL) {
1802 if (replymessageid != NULL) {
1803 MsgPermFlags to_unset = 0;
1805 if (prefs_common.mark_as_read_on_new_window)
1806 to_unset = (MSG_NEW|MSG_UNREAD);
1808 procmsg_msginfo_unset_flags(msginfo, to_unset, 0);
1809 procmsg_msginfo_set_flags(msginfo, MSG_REPLIED, 0);
1811 procmsg_msginfo_set_flags(msginfo, MSG_FORWARDED, 0);
1813 procmsg_msginfo_free(&msginfo);
1821 slist_free_strings_full(to_list);
1822 slist_free_strings_full(newsgroup_list);
1823 g_free(savecopyfolder);
1824 g_free(replymessageid);
1825 g_free(fwdmessageid);
1827 return (newsval != 0 ? newsval : mailval);
1830 gint procmsg_send_message_queue(const gchar *file, gchar **errstr, FolderItem *queue, gint msgnum, gboolean *queued_removed)
1832 gint result = procmsg_send_message_queue_full(file, FALSE, errstr, queue, msgnum, queued_removed);
1833 main_window_set_menu_sensitive(mainwindow_get_mainwindow());
1834 toolbar_main_set_sensitive(mainwindow_get_mainwindow());
1838 gint procmsg_send_message_queue_with_lock(const gchar *file, gchar **errstr, FolderItem *queue, gint msgnum, gboolean *queued_removed)
1841 if (procmsg_queue_lock(errstr)) {
1842 val = procmsg_send_message_queue(file, errstr, queue, msgnum, queued_removed);
1843 procmsg_queue_unlock();
1849 static void update_folder_msg_counts(FolderItem *item, MsgInfo *msginfo, MsgPermFlags old_flags)
1851 MsgPermFlags new_flags = msginfo->flags.perm_flags;
1854 if (!(old_flags & MSG_NEW) && (new_flags & MSG_NEW)) {
1858 if ((old_flags & MSG_NEW) && !(new_flags & MSG_NEW)) {
1863 if (!(old_flags & MSG_UNREAD) && (new_flags & MSG_UNREAD)) {
1864 item->unread_msgs++;
1865 if (procmsg_msg_has_marked_parent(msginfo))
1866 item->unreadmarked_msgs++;
1869 if ((old_flags & MSG_UNREAD) && !(new_flags & MSG_UNREAD)) {
1870 item->unread_msgs--;
1871 if (procmsg_msg_has_marked_parent(msginfo))
1872 item->unreadmarked_msgs--;
1876 if (!(old_flags & MSG_MARKED) && (new_flags & MSG_MARKED)) {
1877 procmsg_update_unread_children(msginfo, TRUE);
1878 item->marked_msgs++;
1881 if ((old_flags & MSG_MARKED) && !(new_flags & MSG_MARKED)) {
1882 procmsg_update_unread_children(msginfo, FALSE);
1883 item->marked_msgs--;
1886 if (!(old_flags & MSG_REPLIED) && (new_flags & MSG_REPLIED)) {
1887 item->replied_msgs++;
1890 if ((old_flags & MSG_REPLIED) && !(new_flags & MSG_REPLIED)) {
1891 item->replied_msgs--;
1894 if (!(old_flags & MSG_FORWARDED) && (new_flags & MSG_FORWARDED)) {
1895 item->forwarded_msgs++;
1898 if ((old_flags & MSG_FORWARDED) && !(new_flags & MSG_FORWARDED)) {
1899 item->forwarded_msgs--;
1902 if (!(old_flags & MSG_LOCKED) && (new_flags & MSG_LOCKED)) {
1903 item->locked_msgs++;
1906 if ((old_flags & MSG_LOCKED) && !(new_flags & MSG_LOCKED)) {
1907 item->locked_msgs--;
1910 if ((old_flags & MSG_IGNORE_THREAD) && !(new_flags & MSG_IGNORE_THREAD)) {
1911 item->ignored_msgs--;
1914 if (!(old_flags & MSG_IGNORE_THREAD) && (new_flags & MSG_IGNORE_THREAD)) {
1915 item->ignored_msgs++;
1918 if ((old_flags & MSG_WATCH_THREAD) && !(new_flags & MSG_WATCH_THREAD)) {
1919 item->watched_msgs--;
1922 if (!(old_flags & MSG_WATCH_THREAD) && (new_flags & MSG_WATCH_THREAD)) {
1923 item->watched_msgs++;
1927 void procmsg_msginfo_set_flags(MsgInfo *msginfo, MsgPermFlags perm_flags, MsgTmpFlags tmp_flags)
1930 MsgInfoUpdate msginfo_update;
1931 MsgPermFlags perm_flags_new, perm_flags_old;
1932 MsgTmpFlags tmp_flags_old;
1934 cm_return_if_fail(msginfo != NULL);
1935 item = msginfo->folder;
1936 cm_return_if_fail(item != NULL);
1938 debug_print("Setting flags for message %d in folder %s\n", msginfo->msgnum, item->path);
1940 /* Perm Flags handling */
1941 perm_flags_old = msginfo->flags.perm_flags;
1942 perm_flags_new = msginfo->flags.perm_flags | perm_flags;
1943 if ((perm_flags & MSG_IGNORE_THREAD) || (perm_flags_old & MSG_IGNORE_THREAD)) {
1944 perm_flags_new &= ~(MSG_NEW | MSG_UNREAD);
1946 if ((perm_flags & MSG_WATCH_THREAD) || (perm_flags_old & MSG_WATCH_THREAD)) {
1947 perm_flags_new &= ~(MSG_IGNORE_THREAD);
1950 if (perm_flags_old != perm_flags_new) {
1951 folder_item_change_msg_flags(msginfo->folder, msginfo, perm_flags_new);
1953 update_folder_msg_counts(item, msginfo, perm_flags_old);
1954 summary_update_unread(mainwindow_get_mainwindow()->summaryview, NULL);
1957 /* Tmp flags handling */
1958 tmp_flags_old = msginfo->flags.tmp_flags;
1959 msginfo->flags.tmp_flags |= tmp_flags;
1961 /* update notification */
1962 if ((perm_flags_old != perm_flags_new) || (tmp_flags_old != msginfo->flags.tmp_flags)) {
1963 msginfo_update.msginfo = msginfo;
1964 msginfo_update.flags = MSGINFO_UPDATE_FLAGS;
1965 hooks_invoke(MSGINFO_UPDATE_HOOKLIST, &msginfo_update);
1966 folder_item_update(msginfo->folder, F_ITEM_UPDATE_MSGCNT);
1970 void procmsg_msginfo_unset_flags(MsgInfo *msginfo, MsgPermFlags perm_flags, MsgTmpFlags tmp_flags)
1973 MsgInfoUpdate msginfo_update;
1974 MsgPermFlags perm_flags_new, perm_flags_old;
1975 MsgTmpFlags tmp_flags_old;
1977 cm_return_if_fail(msginfo != NULL);
1978 item = msginfo->folder;
1979 cm_return_if_fail(item != NULL);
1981 debug_print("Unsetting flags for message %d in folder %s\n", msginfo->msgnum, item->path);
1983 /* Perm Flags handling */
1984 perm_flags_old = msginfo->flags.perm_flags;
1985 perm_flags_new = msginfo->flags.perm_flags & ~perm_flags;
1987 if (perm_flags_old != perm_flags_new) {
1988 folder_item_change_msg_flags(msginfo->folder, msginfo, perm_flags_new);
1990 update_folder_msg_counts(item, msginfo, perm_flags_old);
1993 /* Tmp flags hanlding */
1994 tmp_flags_old = msginfo->flags.tmp_flags;
1995 msginfo->flags.tmp_flags &= ~tmp_flags;
1997 /* update notification */
1998 if ((perm_flags_old != perm_flags_new) || (tmp_flags_old != msginfo->flags.tmp_flags)) {
1999 msginfo_update.msginfo = msginfo;
2000 msginfo_update.flags = MSGINFO_UPDATE_FLAGS;
2001 hooks_invoke(MSGINFO_UPDATE_HOOKLIST, &msginfo_update);
2002 folder_item_update(msginfo->folder, F_ITEM_UPDATE_MSGCNT);
2006 void procmsg_msginfo_change_flags(MsgInfo *msginfo,
2007 MsgPermFlags add_perm_flags, MsgTmpFlags add_tmp_flags,
2008 MsgPermFlags rem_perm_flags, MsgTmpFlags rem_tmp_flags)
2011 MsgInfoUpdate msginfo_update;
2012 MsgPermFlags perm_flags_new, perm_flags_old;
2013 MsgTmpFlags tmp_flags_old;
2015 cm_return_if_fail(msginfo != NULL);
2016 item = msginfo->folder;
2017 cm_return_if_fail(item != NULL);
2019 debug_print("Changing flags for message %d in folder %s\n", msginfo->msgnum, item->path);
2021 /* Perm Flags handling */
2022 perm_flags_old = msginfo->flags.perm_flags;
2023 perm_flags_new = (msginfo->flags.perm_flags & ~rem_perm_flags) | add_perm_flags;
2024 if ((add_perm_flags & MSG_IGNORE_THREAD) || (perm_flags_old & MSG_IGNORE_THREAD)) {
2025 perm_flags_new &= ~(MSG_NEW | MSG_UNREAD);
2027 if ((add_perm_flags & MSG_WATCH_THREAD) || (perm_flags_old & MSG_WATCH_THREAD)) {
2028 perm_flags_new &= ~(MSG_IGNORE_THREAD);
2031 if (perm_flags_old != perm_flags_new) {
2032 folder_item_change_msg_flags(msginfo->folder, msginfo, perm_flags_new);
2034 update_folder_msg_counts(item, msginfo, perm_flags_old);
2038 /* Tmp flags handling */
2039 tmp_flags_old = msginfo->flags.tmp_flags;
2040 msginfo->flags.tmp_flags &= ~rem_tmp_flags;
2041 msginfo->flags.tmp_flags |= add_tmp_flags;
2043 /* update notification */
2044 if ((perm_flags_old != perm_flags_new) || (tmp_flags_old != msginfo->flags.tmp_flags)) {
2045 msginfo_update.msginfo = msginfo;
2046 msginfo_update.flags = MSGINFO_UPDATE_FLAGS;
2047 hooks_invoke(MSGINFO_UPDATE_HOOKLIST, &msginfo_update);
2048 folder_item_update(msginfo->folder, F_ITEM_UPDATE_MSGCNT);
2053 *\brief check for flags (e.g. mark) in prior msgs of current thread
2055 *\param info Current message
2056 *\param perm_flags Flags to be checked
2057 *\param parentmsgs Hash of prior msgs to avoid loops
2059 *\return gboolean TRUE if perm_flags are found
2061 static gboolean procmsg_msg_has_flagged_parent_real(MsgInfo *info,
2062 MsgPermFlags perm_flags, GHashTable *parentmsgs)
2066 cm_return_val_if_fail(info != NULL, FALSE);
2068 if (info != NULL && info->folder != NULL && info->inreplyto != NULL) {
2069 tmp = folder_item_get_msginfo_by_msgid(info->folder,
2071 if (tmp && (tmp->flags.perm_flags & perm_flags)) {
2072 procmsg_msginfo_free(&tmp);
2074 } else if (tmp != NULL) {
2077 if (g_hash_table_lookup(parentmsgs, info)) {
2078 debug_print("loop detected: %d\n",
2082 g_hash_table_insert(parentmsgs, info, "1");
2083 result = procmsg_msg_has_flagged_parent_real(
2084 tmp, perm_flags, parentmsgs);
2086 procmsg_msginfo_free(&tmp);
2096 *\brief Callback for cleaning up hash of parentmsgs
2098 static gboolean parentmsgs_hash_remove(gpointer key,
2106 *\brief Set up list of parentmsgs
2107 * See procmsg_msg_has_flagged_parent_real()
2109 gboolean procmsg_msg_has_flagged_parent(MsgInfo *info, MsgPermFlags perm_flags)
2112 static GHashTable *parentmsgs = NULL;
2114 if (parentmsgs == NULL)
2115 parentmsgs = g_hash_table_new(NULL, NULL);
2117 result = procmsg_msg_has_flagged_parent_real(info, perm_flags, parentmsgs);
2118 g_hash_table_foreach_remove(parentmsgs, parentmsgs_hash_remove, NULL);
2124 *\brief Check if msgs prior in thread are marked
2125 * See procmsg_msg_has_flagged_parent_real()
2127 gboolean procmsg_msg_has_marked_parent(MsgInfo *info)
2129 return procmsg_msg_has_flagged_parent(info, MSG_MARKED);
2133 static GSList *procmsg_find_children_func(MsgInfo *info,
2134 GSList *children, GSList *all)
2138 cm_return_val_if_fail(info!=NULL, children);
2139 if (info->msgid == NULL)
2142 for (cur = all; cur != NULL; cur = g_slist_next(cur)) {
2143 MsgInfo *tmp = (MsgInfo *)cur->data;
2144 if (tmp->inreplyto && !strcmp(tmp->inreplyto, info->msgid)) {
2145 /* Check if message is already in the list */
2146 if ((children == NULL) ||
2147 (g_slist_index(children, tmp) == -1)) {
2148 children = g_slist_prepend(children,
2149 procmsg_msginfo_new_ref(tmp));
2150 children = procmsg_find_children_func(tmp,
2159 static GSList *procmsg_find_children (MsgInfo *info)
2164 cm_return_val_if_fail(info!=NULL, NULL);
2165 all = folder_item_get_msg_list(info->folder);
2166 children = procmsg_find_children_func(info, NULL, all);
2167 if (children != NULL) {
2168 for (cur = all; cur != NULL; cur = g_slist_next(cur)) {
2169 /* this will not free the used pointers
2170 created with procmsg_msginfo_new_ref */
2171 procmsg_msginfo_free((MsgInfo **)&(cur->data));
2179 static void procmsg_update_unread_children(MsgInfo *info, gboolean newly_marked)
2181 GSList *children = procmsg_find_children(info);
2183 for (cur = children; cur != NULL; cur = g_slist_next(cur)) {
2184 MsgInfo *tmp = (MsgInfo *)cur->data;
2185 if(MSG_IS_UNREAD(tmp->flags) && !MSG_IS_IGNORE_THREAD(tmp->flags)) {
2187 info->folder->unreadmarked_msgs++;
2189 info->folder->unreadmarked_msgs--;
2190 folder_item_update(info->folder, F_ITEM_UPDATE_MSGCNT);
2192 procmsg_msginfo_free(&tmp);
2194 g_slist_free(children);
2198 * Set the destination folder for a copy or move operation
2200 * \param msginfo The message which's destination folder is changed
2201 * \param to_folder The destination folder for the operation
2203 void procmsg_msginfo_set_to_folder(MsgInfo *msginfo, FolderItem *to_folder)
2205 if(msginfo->to_folder != NULL) {
2206 msginfo->to_folder->op_count--;
2207 folder_item_update(msginfo->to_folder, F_ITEM_UPDATE_MSGCNT);
2209 msginfo->to_folder = to_folder;
2210 if(to_folder != NULL) {
2211 to_folder->op_count++;
2212 folder_item_update(msginfo->to_folder, F_ITEM_UPDATE_MSGCNT);
2217 * Apply filtering actions to the msginfo
2219 * \param msginfo The MsgInfo describing the message that should be filtered
2220 * \return TRUE if the message was moved and MsgInfo is now invalid,
2223 static gboolean procmsg_msginfo_filter(MsgInfo *msginfo, PrefsAccount* ac_prefs)
2225 MailFilteringData mail_filtering_data;
2227 mail_filtering_data.msginfo = msginfo;
2228 mail_filtering_data.msglist = NULL;
2229 mail_filtering_data.filtered = NULL;
2230 mail_filtering_data.unfiltered = NULL;
2231 mail_filtering_data.account = ac_prefs;
2233 if (!ac_prefs || ac_prefs->filterhook_on_recv)
2234 if (hooks_invoke(MAIL_FILTERING_HOOKLIST, &mail_filtering_data))
2237 /* filter if enabled in prefs or move to inbox if not */
2238 if((filtering_rules != NULL) &&
2239 filter_message_by_msginfo(filtering_rules, msginfo, ac_prefs,
2240 FILTERING_INCORPORATION, NULL)) {
2247 void procmsg_msglist_filter(GSList *list, PrefsAccount *ac,
2248 GSList **filtered, GSList **unfiltered,
2251 GSList *cur, *to_do = NULL;
2252 gint total = 0, curnum = 0;
2253 MailFilteringData mail_filtering_data;
2255 cm_return_if_fail(filtered != NULL);
2256 cm_return_if_fail(unfiltered != NULL);
2264 total = g_slist_length(list);
2268 *unfiltered = g_slist_copy(list);
2272 statusbar_print_all(_("Filtering messages...\n"));
2274 mail_filtering_data.msginfo = NULL;
2275 mail_filtering_data.msglist = list;
2276 mail_filtering_data.filtered = NULL;
2277 mail_filtering_data.unfiltered = NULL;
2278 mail_filtering_data.account = ac;
2280 if (!ac || ac->filterhook_on_recv)
2281 hooks_invoke(MAIL_LISTFILTERING_HOOKLIST, &mail_filtering_data);
2283 if (mail_filtering_data.filtered == NULL &&
2284 mail_filtering_data.unfiltered == NULL) {
2285 /* nothing happened */
2286 debug_print(MAIL_LISTFILTERING_HOOKLIST " did nothing. filtering whole list normally.\n");
2289 if (mail_filtering_data.filtered != NULL) {
2290 /* keep track of what's been filtered by the hooks */
2291 debug_print(MAIL_LISTFILTERING_HOOKLIST " filtered some stuff. total %d filtered %d unfilt %d.\n",
2292 g_slist_length(list),
2293 g_slist_length(mail_filtering_data.filtered),
2294 g_slist_length(mail_filtering_data.unfiltered));
2296 *filtered = g_slist_copy(mail_filtering_data.filtered);
2298 if (mail_filtering_data.unfiltered != NULL) {
2299 /* what the hooks didn't handle will go in filtered or
2300 * unfiltered in the next loop */
2301 debug_print(MAIL_LISTFILTERING_HOOKLIST " left unfiltered stuff. total %d filtered %d unfilt %d.\n",
2302 g_slist_length(list),
2303 g_slist_length(mail_filtering_data.filtered),
2304 g_slist_length(mail_filtering_data.unfiltered));
2305 to_do = mail_filtering_data.unfiltered;
2308 for (cur = to_do; cur; cur = cur->next) {
2309 MsgInfo *info = (MsgInfo *)cur->data;
2310 if (procmsg_msginfo_filter(info, ac))
2311 *filtered = g_slist_prepend(*filtered, info);
2313 *unfiltered = g_slist_prepend(*unfiltered, info);
2314 statusbar_progress_all(curnum++, total, prefs_common.statusbar_update_step);
2317 g_slist_free(mail_filtering_data.filtered);
2318 g_slist_free(mail_filtering_data.unfiltered);
2320 *filtered = g_slist_reverse(*filtered);
2321 *unfiltered = g_slist_reverse(*unfiltered);
2323 statusbar_progress_all(0,0,0);
2324 statusbar_pop_all();
2327 MsgInfo *procmsg_msginfo_new_from_mimeinfo(MsgInfo *src_msginfo, MimeInfo *mimeinfo)
2329 MsgInfo *tmp_msginfo = NULL;
2330 MsgFlags flags = {0, 0};
2331 gchar *tmpfile = get_tmp_file();
2332 FILE *fp = g_fopen(tmpfile, "wb");
2334 if (!mimeinfo || mimeinfo->type != MIMETYPE_MESSAGE ||
2335 g_ascii_strcasecmp(mimeinfo->subtype, "rfc822")) {
2336 g_warning("procmsg_msginfo_new_from_mimeinfo(): unsuitable mimeinfo");
2343 if (fp && procmime_write_mimeinfo(mimeinfo, fp) >= 0) {
2346 tmp_msginfo = procheader_parse_file(
2353 if (tmp_msginfo != NULL) {
2355 tmp_msginfo->folder = src_msginfo->folder;
2356 tmp_msginfo->plaintext_file = g_strdup(tmpfile);
2358 g_warning("procmsg_msginfo_new_from_mimeinfo(): Can't generate new msginfo");
2366 static GSList *spam_learners = NULL;
2368 void procmsg_register_spam_learner (int (*learn_func)(MsgInfo *info, GSList *list, gboolean spam))
2370 if (!g_slist_find(spam_learners, learn_func))
2371 spam_learners = g_slist_append(spam_learners, learn_func);
2372 if (mainwindow_get_mainwindow()) {
2373 main_window_set_menu_sensitive(mainwindow_get_mainwindow());
2374 summary_set_menu_sensitive(
2375 mainwindow_get_mainwindow()->summaryview);
2376 toolbar_main_set_sensitive(mainwindow_get_mainwindow());
2380 void procmsg_unregister_spam_learner (int (*learn_func)(MsgInfo *info, GSList *list, gboolean spam))
2382 spam_learners = g_slist_remove(spam_learners, learn_func);
2383 if (mainwindow_get_mainwindow()) {
2384 main_window_set_menu_sensitive(mainwindow_get_mainwindow());
2385 summary_set_menu_sensitive(
2386 mainwindow_get_mainwindow()->summaryview);
2387 toolbar_main_set_sensitive(mainwindow_get_mainwindow());
2391 gboolean procmsg_spam_can_learn(void)
2393 return g_slist_length(spam_learners) > 0;
2396 int procmsg_spam_learner_learn (MsgInfo *info, GSList *list, gboolean spam)
2398 GSList *cur = spam_learners;
2400 for (; cur; cur = cur->next) {
2401 int ((*func)(MsgInfo *info, GSList *list, gboolean spam)) = cur->data;
2402 ret |= func(info, list, spam);
2407 static gchar *spam_folder_item = NULL;
2408 static FolderItem * (*procmsg_spam_get_folder_func)(MsgInfo *msginfo) = NULL;
2409 void procmsg_spam_set_folder (const char *item_identifier, FolderItem *(*spam_get_folder_func)(MsgInfo *info))
2411 g_free(spam_folder_item);
2412 if (item_identifier)
2413 spam_folder_item = g_strdup(item_identifier);
2415 spam_folder_item = NULL;
2416 if (spam_get_folder_func != NULL)
2417 procmsg_spam_get_folder_func = spam_get_folder_func;
2419 procmsg_spam_get_folder_func = NULL;
2422 FolderItem *procmsg_spam_get_folder (MsgInfo *msginfo)
2424 FolderItem *item = NULL;
2426 if (procmsg_spam_get_folder_func)
2427 item = procmsg_spam_get_folder_func(msginfo);
2428 if (item == NULL && spam_folder_item)
2429 item = folder_find_item_from_identifier(spam_folder_item);
2431 item = folder_get_default_trash();
2435 static void item_has_queued_mails(FolderItem *item, gpointer data)
2437 gboolean *result = (gboolean *)data;
2438 if (*result == TRUE)
2440 if (folder_has_parent_of_type(item, F_QUEUE)) {
2441 if (item->total_msgs == 0)
2444 GSList *msglist = folder_item_get_msg_list(item);
2446 for (cur = msglist; cur; cur = cur->next) {
2447 MsgInfo *msginfo = (MsgInfo *)cur->data;
2448 if (!MSG_IS_DELETED(msginfo->flags) &&
2449 !MSG_IS_LOCKED(msginfo->flags)) {
2454 procmsg_msg_list_free(msglist);
2459 gboolean procmsg_have_queued_mails_fast (void)
2461 gboolean result = FALSE;
2462 folder_func_to_all_folders(item_has_queued_mails, &result);
2466 static void item_has_trashed_mails(FolderItem *item, gpointer data)
2468 gboolean *result = (gboolean *)data;
2469 if (*result == TRUE)
2471 if (folder_has_parent_of_type(item, F_TRASH) && item->total_msgs > 0)
2475 gboolean procmsg_have_trashed_mails_fast (void)
2477 gboolean result = FALSE;
2478 folder_func_to_all_folders(item_has_trashed_mails, &result);
2482 gchar *procmsg_msginfo_get_tags_str(MsgInfo *msginfo)
2490 if (msginfo->tags == NULL)
2492 for (cur = msginfo->tags; cur; cur = cur->next) {
2493 const gchar *tag = tags_get_tag(GPOINTER_TO_INT(cur->data));
2497 tags = g_strdup(tag);
2499 int olen = strlen(tags);
2500 int nlen = olen + strlen(tag) + 2 /* strlen(", ") */;
2501 tags = g_realloc(tags, nlen+1);
2504 strcpy(tags+olen, ", ");
2505 strcpy(tags+olen+2, tag);
2512 void procmsg_msginfo_update_tags(MsgInfo *msginfo, gboolean set, gint id)
2520 msginfo->tags = g_slist_remove(
2522 GINT_TO_POINTER(id));
2523 changed.data = GINT_TO_POINTER(id);
2524 changed.next = NULL;
2525 folder_item_commit_tags(msginfo->folder, msginfo, NULL, &changed);
2527 if (!g_slist_find(msginfo->tags, GINT_TO_POINTER(id))) {
2528 msginfo->tags = g_slist_append(
2530 GINT_TO_POINTER(id));
2532 changed.data = GINT_TO_POINTER(id);
2533 changed.next = NULL;
2534 folder_item_commit_tags(msginfo->folder, msginfo, &changed, NULL);
2539 void procmsg_msginfo_clear_tags(MsgInfo *msginfo)
2541 GSList *unset = msginfo->tags;
2542 msginfo->tags = NULL;
2543 folder_item_commit_tags(msginfo->folder, msginfo, NULL, unset);
2544 g_slist_free(unset);