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>
31 #include "procheader.h"
32 #include "send_message.h"
34 #include "statusbar.h"
35 #include "prefs_filtering.h"
36 #include "filtering.h"
38 #include "prefs_common.h"
40 #include "alertpanel.h"
44 #include "partial_download.h"
45 #include "mainwindow.h"
46 #include "summaryview.h"
53 extern SessionStats session_stats;
55 static gint procmsg_send_message_queue_full(const gchar *file, gboolean keep_session, gchar **errstr,
56 FolderItem *queue, gint msgnum, gboolean *queued_removed);
57 static void procmsg_update_unread_children (MsgInfo *info,
58 gboolean newly_marked);
65 Q_MAIL_ACCOUNT_ID = 4,
66 Q_NEWS_ACCOUNT_ID = 5,
67 Q_SAVE_COPY_FOLDER = 6,
68 Q_REPLY_MESSAGE_ID = 7,
74 Q_PRIVACY_SYSTEM_OLD = 13,
76 Q_ENCRYPT_DATA_OLD = 15,
77 Q_CLAWS_HDRS_OLD = 16,
80 void procmsg_msg_list_free(GSList *mlist)
85 for (cur = mlist; cur != NULL; cur = cur->next) {
86 msginfo = (MsgInfo *)cur->data;
87 procmsg_msginfo_free(&msginfo);
92 MsgNumberList *procmsg_get_number_list_for_msgs(MsgInfoList *msglist)
97 for (cur = msglist; cur; cur = cur->next) {
98 MsgInfo *msg = (MsgInfo *)cur->data;
99 nums = g_slist_prepend(nums, GUINT_TO_POINTER(msg->msgnum));
102 return g_slist_reverse(nums);
114 /* CLAWS subject threading:
116 in the first round it inserts subject lines in a
117 hashtable (subject <-> node)
119 the second round finishes the threads by attaching
120 matching subject lines to the one found in the
121 hashtable. will use the oldest node with the same
122 subject that is not more then thread_by_subject_max_age
123 days old (see subject_hashtable_lookup)
126 static void subject_hashtable_insert(GHashTable *hashtable, GNode *node)
132 cm_return_if_fail(hashtable != NULL);
133 cm_return_if_fail(node != NULL);
134 msginfo = (MsgInfo *) node->data;
135 cm_return_if_fail(msginfo != NULL);
137 subject = msginfo->subject;
141 subject += subject_get_prefix_length(subject);
143 list = g_hash_table_lookup(hashtable, subject);
144 list = g_slist_prepend(list, node);
145 g_hash_table_insert(hashtable, subject, list);
148 static GNode *subject_hashtable_lookup(GHashTable *hashtable, MsgInfo *msginfo)
152 GNode *node = NULL, *hashtable_node = NULL;
154 MsgInfo *hashtable_msginfo = NULL, *best_msginfo = NULL;
157 cm_return_val_if_fail(hashtable != NULL, NULL);
159 subject = msginfo->subject;
162 prefix_length = subject_get_prefix_length(subject);
163 if (prefix_length <= 0)
165 subject += prefix_length;
167 list = g_hash_table_lookup(hashtable, subject);
171 /* check all nodes with the same subject to find the best parent */
172 for (cur = list; cur; cur = cur->next) {
173 hashtable_node = (GNode *)cur->data;
174 hashtable_msginfo = (MsgInfo *) hashtable_node->data;
177 /* best node should be the oldest in the found nodes */
178 /* parent node must not be older then msginfo */
179 if ((hashtable_msginfo->date_t < msginfo->date_t) &&
180 ((best_msginfo == NULL) ||
181 (best_msginfo->date_t > hashtable_msginfo->date_t)))
184 /* parent node must not be more then thread_by_subject_max_age
185 days older then msginfo */
186 if (fabs(difftime(msginfo->date_t, hashtable_msginfo->date_t)) >
187 prefs_common.thread_by_subject_max_age * 3600 * 24)
190 /* can add new tests for all matching
191 nodes found by subject */
194 node = hashtable_node;
195 best_msginfo = hashtable_msginfo;
202 static void subject_hashtable_free(gpointer key, gpointer value, gpointer data)
207 /* return the reversed thread tree */
208 GNode *procmsg_get_thread_tree(GSList *mlist)
210 GNode *root, *parent, *node, *next;
211 GHashTable *msgid_table;
212 GHashTable *subject_hashtable = NULL;
217 root = g_node_new(NULL);
218 msgid_table = g_hash_table_new(g_str_hash, g_str_equal);
220 if (prefs_common.thread_by_subject) {
221 subject_hashtable = g_hash_table_new(g_str_hash, g_str_equal);
224 for (; mlist != NULL; mlist = mlist->next) {
225 msginfo = (MsgInfo *)mlist->data;
228 if (msginfo->inreplyto) {
229 parent = g_hash_table_lookup(msgid_table, msginfo->inreplyto);
230 if (parent == NULL) {
234 node = g_node_insert_data_before
235 (parent, parent == root ? parent->children : NULL,
237 if ((msgid = msginfo->msgid) && g_hash_table_lookup(msgid_table, msgid) == NULL)
238 g_hash_table_insert(msgid_table, (gchar *)msgid, node);
240 /* CLAWS: add subject to hashtable (without prefix) */
241 if (prefs_common.thread_by_subject) {
242 subject_hashtable_insert(subject_hashtable, node);
246 /* complete the unfinished threads */
247 for (node = root->children; node != NULL; ) {
249 msginfo = (MsgInfo *)node->data;
252 if (msginfo->inreplyto)
253 parent = g_hash_table_lookup(msgid_table, msginfo->inreplyto);
255 /* try looking for the indirect parent */
256 if (!parent && msginfo->references) {
257 for (reflist = msginfo->references;
258 reflist != NULL; reflist = reflist->next)
259 if ((parent = g_hash_table_lookup
260 (msgid_table, reflist->data)) != NULL)
264 /* node should not be the parent, and node should not
265 be an ancestor of parent (circular reference) */
266 if (parent && parent != node &&
267 !g_node_is_ancestor(node, parent)) {
270 (parent, parent->children, node);
276 if (prefs_common.thread_by_subject) {
277 START_TIMING("thread by subject");
278 for (node = root->children; node && node != NULL;) {
280 msginfo = (MsgInfo *) node->data;
282 parent = subject_hashtable_lookup(subject_hashtable, msginfo);
284 /* the node may already be threaded by IN-REPLY-TO, so go up
286 find the parent node */
287 if (parent != NULL) {
288 if (g_node_is_ancestor(node, parent))
296 g_node_append(parent, node);
304 if (prefs_common.thread_by_subject)
306 g_hash_table_foreach(subject_hashtable, subject_hashtable_free, NULL);
307 g_hash_table_destroy(subject_hashtable);
310 g_hash_table_destroy(msgid_table);
315 gint procmsg_move_messages(GSList *mlist)
317 GSList *cur, *movelist = NULL;
319 FolderItem *dest = NULL;
321 gboolean finished = TRUE;
322 if (!mlist) return 0;
324 folder_item_update_freeze();
327 for (cur = mlist; cur != NULL; cur = cur->next) {
328 msginfo = (MsgInfo *)cur->data;
329 if (!msginfo->to_folder) {
335 dest = msginfo->to_folder;
336 movelist = g_slist_prepend(movelist, msginfo);
337 } else if (dest == msginfo->to_folder) {
338 movelist = g_slist_prepend(movelist, msginfo);
342 procmsg_msginfo_set_to_folder(msginfo, NULL);
345 movelist = g_slist_reverse(movelist);
346 retval |= folder_item_move_msgs(dest, movelist);
347 g_slist_free(movelist);
350 if (finished == FALSE) {
356 folder_item_update_thaw();
360 void procmsg_copy_messages(GSList *mlist)
362 GSList *cur, *copylist = NULL;
364 FolderItem *dest = NULL;
365 gboolean finished = TRUE;
368 folder_item_update_freeze();
371 for (cur = mlist; cur != NULL; cur = cur->next) {
372 msginfo = (MsgInfo *)cur->data;
373 if (!msginfo->to_folder) {
379 dest = msginfo->to_folder;
380 copylist = g_slist_prepend(copylist, msginfo);
381 } else if (dest == msginfo->to_folder) {
382 copylist = g_slist_prepend(copylist, msginfo);
386 procmsg_msginfo_set_to_folder(msginfo, NULL);
389 copylist = g_slist_reverse(copylist);
390 folder_item_copy_msgs(dest, copylist);
391 g_slist_free(copylist);
394 if (finished == FALSE) {
400 folder_item_update_thaw();
403 gchar *procmsg_get_message_file_path(MsgInfo *msginfo)
405 cm_return_val_if_fail(msginfo != NULL, NULL);
407 return folder_item_fetch_msg(msginfo->folder, msginfo->msgnum);
410 gchar *procmsg_get_message_file(MsgInfo *msginfo)
412 gchar *filename = NULL;
414 cm_return_val_if_fail(msginfo != NULL, NULL);
416 filename = folder_item_fetch_msg(msginfo->folder, msginfo->msgnum);
418 debug_print("can't fetch message %d\n", msginfo->msgnum);
423 gchar *procmsg_get_message_file_full(MsgInfo *msginfo, gboolean headers, gboolean body)
425 gchar *filename = NULL;
427 cm_return_val_if_fail(msginfo != NULL, NULL);
429 filename = folder_item_fetch_msg_full(msginfo->folder, msginfo->msgnum,
432 debug_print("can't fetch message %d\n", msginfo->msgnum);
437 GSList *procmsg_get_message_file_list(GSList *mlist)
439 GSList *file_list = NULL;
441 MsgFileInfo *fileinfo;
444 while (mlist != NULL) {
445 msginfo = (MsgInfo *)mlist->data;
446 file = procmsg_get_message_file(msginfo);
448 procmsg_message_file_list_free(file_list);
451 fileinfo = g_new(MsgFileInfo, 1);
452 fileinfo->msginfo = procmsg_msginfo_new_ref(msginfo);
453 fileinfo->file = file;
454 fileinfo->flags = g_new(MsgFlags, 1);
455 *fileinfo->flags = msginfo->flags;
456 file_list = g_slist_prepend(file_list, fileinfo);
460 file_list = g_slist_reverse(file_list);
465 void procmsg_message_file_list_free(MsgInfoList *file_list)
468 MsgFileInfo *fileinfo;
470 for (cur = file_list; cur != NULL; cur = cur->next) {
471 fileinfo = (MsgFileInfo *)cur->data;
472 procmsg_msginfo_free(&(fileinfo->msginfo));
473 g_free(fileinfo->file);
474 g_free(fileinfo->flags);
478 g_slist_free(file_list);
481 FILE *procmsg_open_message(MsgInfo *msginfo)
486 cm_return_val_if_fail(msginfo != NULL, NULL);
488 file = procmsg_get_message_file_path(msginfo);
489 cm_return_val_if_fail(file != NULL, NULL);
491 if (!is_file_exist(file)) {
493 file = procmsg_get_message_file(msginfo);
498 if ((fp = g_fopen(file, "rb")) == NULL) {
499 FILE_OP_ERROR(file, "fopen");
506 if (MSG_IS_QUEUED(msginfo->flags) || MSG_IS_DRAFT(msginfo->flags)) {
509 while (fgets(buf, sizeof(buf), fp) != NULL) {
511 if ((!strncmp(buf, "X-Claws-End-Special-Headers: 1",
512 strlen("X-Claws-End-Special-Headers:"))) ||
513 (!strncmp(buf, "X-Sylpheed-End-Special-Headers: 1",
514 strlen("X-Sylpheed-End-Special-Headers:"))))
517 if (buf[0] == '\r' || buf[0] == '\n') break;
518 /* from other mailers */
519 if (!strncmp(buf, "Date: ", 6)
520 || !strncmp(buf, "To: ", 4)
521 || !strncmp(buf, "From: ", 6)
522 || !strncmp(buf, "Subject: ", 9)) {
532 gboolean procmsg_msg_exist(MsgInfo *msginfo)
536 if (!msginfo) return FALSE;
538 ret = !folder_item_is_msg_changed(msginfo->folder, msginfo);
543 void procmsg_get_filter_keyword(MsgInfo *msginfo, gchar **header, gchar **key,
544 PrefsFilterType type)
546 static HeaderEntry hentry[] = {{"X-BeenThere:", NULL, TRUE},
547 {"X-ML-Name:", NULL, TRUE},
548 {"X-List:", NULL, TRUE},
549 {"X-Mailing-list:", NULL, TRUE},
550 {"List-Id:", NULL, TRUE},
551 {"X-Sequence:", NULL, TRUE},
552 {"Sender:", NULL, TRUE},
553 {"List-Post:", NULL, TRUE},
554 {NULL, NULL, FALSE}};
560 H_X_MAILING_LIST = 3,
569 cm_return_if_fail(msginfo != NULL);
570 cm_return_if_fail(header != NULL);
571 cm_return_if_fail(key != NULL);
580 if ((fp = procmsg_open_message(msginfo)) == NULL)
582 procheader_get_header_fields(fp, hentry);
585 #define SET_FILTER_KEY(hstr, idx) \
587 *header = g_strdup(hstr); \
588 *key = hentry[idx].body; \
589 hentry[idx].body = NULL; \
592 if (hentry[H_LIST_ID].body != NULL) {
593 SET_FILTER_KEY("header \"List-Id\"", H_LIST_ID);
594 extract_list_id_str(*key);
595 } else if (hentry[H_X_BEENTHERE].body != NULL) {
596 SET_FILTER_KEY("header \"X-BeenThere\"", H_X_BEENTHERE);
597 } else if (hentry[H_X_ML_NAME].body != NULL) {
598 SET_FILTER_KEY("header \"X-ML-Name\"", H_X_ML_NAME);
599 } else if (hentry[H_X_LIST].body != NULL) {
600 SET_FILTER_KEY("header \"X-List\"", H_X_LIST);
601 } else if (hentry[H_X_MAILING_LIST].body != NULL) {
602 SET_FILTER_KEY("header \"X-Mailing-List\"", H_X_MAILING_LIST);
603 } else if (hentry[H_X_SEQUENCE].body != NULL) {
606 SET_FILTER_KEY("X-Sequence", H_X_SEQUENCE);
609 while (*p != '\0' && !g_ascii_isspace(*p)) p++;
610 while (g_ascii_isspace(*p)) p++;
611 if (g_ascii_isdigit(*p)) {
617 } else if (hentry[H_SENDER].body != NULL) {
618 SET_FILTER_KEY("header \"Sender\"", H_SENDER);
619 } else if (hentry[H_LIST_POST].body != NULL) {
620 SET_FILTER_KEY("header \"List-Post\"", H_LIST_POST);
621 } else if (msginfo->to) {
622 *header = g_strdup("to");
623 *key = g_strdup(msginfo->to);
624 } else if (msginfo->subject) {
625 *header = g_strdup("subject");
626 *key = g_strdup(msginfo->subject);
629 #undef SET_FILTER_KEY
631 g_free(hentry[H_X_BEENTHERE].body);
632 hentry[H_X_BEENTHERE].body = NULL;
633 g_free(hentry[H_X_ML_NAME].body);
634 hentry[H_X_ML_NAME].body = NULL;
635 g_free(hentry[H_X_LIST].body);
636 hentry[H_X_LIST].body = NULL;
637 g_free(hentry[H_X_MAILING_LIST].body);
638 hentry[H_X_MAILING_LIST].body = NULL;
639 g_free(hentry[H_LIST_ID].body);
640 hentry[H_LIST_ID].body = NULL;
641 g_free(hentry[H_SENDER].body);
642 hentry[H_SENDER].body = NULL;
643 g_free(hentry[H_LIST_POST].body);
644 hentry[H_LIST_POST].body = NULL;
648 *header = g_strdup("from");
649 *key = g_strdup(msginfo->from);
652 *header = g_strdup("to");
653 *key = g_strdup(msginfo->to);
655 case FILTER_BY_SUBJECT:
656 *header = g_strdup("subject");
657 *key = g_strdup(msginfo->subject);
664 static void procmsg_empty_trash(FolderItem *trash)
669 (trash->stype != F_TRASH &&
670 !folder_has_parent_of_type(trash, F_TRASH)))
673 if (trash && trash->total_msgs > 0) {
674 GSList *mlist = folder_item_get_msg_list(trash);
676 for (cur = mlist ; cur != NULL ; cur = cur->next) {
677 MsgInfo * msginfo = (MsgInfo *) cur->data;
678 if (MSG_IS_LOCKED(msginfo->flags)) {
679 procmsg_msginfo_free(&msginfo);
682 if (msginfo->total_size != 0 &&
683 msginfo->size != (off_t)msginfo->total_size)
684 partial_mark_for_delete(msginfo);
686 procmsg_msginfo_free(&msginfo);
689 folder_item_remove_all_msg(trash);
692 if (!trash->node || !trash->node->children)
695 node = trash->node->children;
696 while (node != NULL) {
698 procmsg_empty_trash(FOLDER_ITEM(node->data));
703 void procmsg_empty_all_trash(void)
708 for (cur = folder_get_list(); cur != NULL; cur = cur->next) {
709 Folder *folder = FOLDER(cur->data);
710 trash = folder->trash;
711 procmsg_empty_trash(trash);
712 if (folder->account && folder->account->set_trash_folder &&
713 folder_find_item_from_identifier(folder->account->trash_folder))
715 folder_find_item_from_identifier(folder->account->trash_folder));
719 static PrefsAccount *procmsg_get_account_from_file(const gchar *file)
721 PrefsAccount *mailac = NULL;
725 static HeaderEntry qentry[] = {{"S:", NULL, FALSE},
726 {"SSV:", NULL, FALSE},
728 {"NG:", NULL, FALSE},
729 {"MAID:", NULL, FALSE},
730 {"NAID:", NULL, FALSE},
731 {"SCF:", NULL, FALSE},
732 {"RMID:", NULL, FALSE},
733 {"FMID:", NULL, FALSE},
734 {"X-Claws-Privacy-System:", NULL, FALSE},
735 {"X-Claws-Encrypt:", NULL, FALSE},
736 {"X-Claws-Encrypt-Data:", NULL, FALSE},
737 {"X-Claws-End-Special-Headers", NULL, FALSE},
738 {"X-Sylpheed-Privacy-System:", NULL, FALSE},
739 {"X-Sylpheed-Encrypt:", NULL, FALSE},
740 {"X-Sylpheed-Encrypt-Data:", NULL, FALSE},
741 {NULL, NULL, FALSE}};
743 cm_return_val_if_fail(file != NULL, NULL);
745 if ((fp = g_fopen(file, "rb")) == NULL) {
746 FILE_OP_ERROR(file, "fopen");
750 while ((hnum = procheader_get_one_field(&buf, fp, qentry)) != -1 && buf != NULL) {
751 gchar *p = buf + strlen(qentry[hnum].name);
753 if (hnum == Q_MAIL_ACCOUNT_ID) {
754 mailac = account_find_from_id(atoi(p));
764 gchar *procmsg_msginfo_get_avatar(MsgInfo *msginfo, gint type)
768 if (!msginfo || !msginfo->extradata || !msginfo->extradata->avatars)
771 for (mia = msginfo->extradata->avatars; mia; mia = mia->next) {
772 MsgInfoAvatar *avatar = (MsgInfoAvatar *)mia->data;
773 if (avatar->avatar_id == type)
774 return avatar->avatar_src;
780 void procmsg_msginfo_add_avatar(MsgInfo *msginfo, gint type, const gchar *data)
784 if (!msginfo->extradata)
785 msginfo->extradata = g_new0(MsgInfoExtraData, 1);
787 av = g_new0(MsgInfoAvatar, 1);
788 av->avatar_id = type;
789 av->avatar_src = g_strdup(data);
791 msginfo->extradata->avatars = g_slist_append(msginfo->extradata->avatars, av);
794 gchar *procmsg_msginfo_get_identifier(MsgInfo *msginfo)
800 cm_return_val_if_fail(msginfo != NULL, NULL);
801 folder_id = folder_item_get_identifier(msginfo->folder);
802 msgid = msginfo->msgid;
804 id = g_strconcat(folder_id, G_DIR_SEPARATOR_S, msgid, NULL);
811 MsgInfo *procmsg_get_msginfo_from_identifier(const gchar *id)
813 gchar *folder_id = g_strdup(id);
814 gchar *separator = strrchr(folder_id, G_DIR_SEPARATOR);
819 if (separator == NULL) {
825 msgid = separator + 1;
827 item = folder_find_item_from_identifier(folder_id);
834 msginfo = folder_item_get_msginfo_by_msgid(item, msgid);
840 static GSList *procmsg_list_sort_by_account(FolderItem *queue, GSList *list)
842 GSList *result = NULL;
844 PrefsAccount *last_account = NULL;
847 gboolean nothing_to_sort = TRUE;
852 orig = g_slist_copy(list);
854 msg = (MsgInfo *)orig->data;
856 for (cur = orig; cur; cur = cur->next)
857 debug_print("sort before %s\n", ((MsgInfo *)cur->data)->from);
862 nothing_to_sort = TRUE;
866 PrefsAccount *ac = NULL;
867 msg = (MsgInfo *)cur->data;
868 file = folder_item_fetch_msg(queue, msg->msgnum);
869 ac = procmsg_get_account_from_file(file);
872 if (last_account == NULL || (ac != NULL && ac == last_account)) {
873 result = g_slist_append(result, msg);
874 orig = g_slist_remove(orig, msg);
876 nothing_to_sort = FALSE;
882 if (orig && g_slist_length(orig)) {
883 if (!last_account && nothing_to_sort) {
884 /* can't find an account for the rest of the list */
887 result = g_slist_append(result, cur->data);
898 for (cur = result; cur; cur = cur->next)
899 debug_print("sort after %s\n", ((MsgInfo *)cur->data)->from);
906 static gboolean procmsg_is_last_for_account(FolderItem *queue, MsgInfo *msginfo, GSList *elem)
908 gchar *file = folder_item_fetch_msg(queue, msginfo->msgnum);
909 PrefsAccount *ac = procmsg_get_account_from_file(file);
912 for (cur = elem; cur; cur = cur->next) {
913 MsgInfo *cur_msginfo = (MsgInfo *)cur->data;
914 file = folder_item_fetch_msg(queue, cur_msginfo->msgnum);
916 if (cur_msginfo != msginfo && !MSG_IS_LOCKED(cur_msginfo->flags)) {
917 if (procmsg_get_account_from_file(file) == ac) {
928 static gboolean send_queue_lock = FALSE;
930 gboolean procmsg_queue_lock(char **errstr)
932 if (send_queue_lock) {
933 /* Avoid having to translate two similar strings */
934 log_warning(LOG_PROTOCOL, "%s\n", _("Already trying to send."));
936 if (*errstr) g_free(*errstr);
937 *errstr = g_strdup_printf(_("Already trying to send."));
941 send_queue_lock = TRUE;
944 void procmsg_queue_unlock(void)
946 send_queue_lock = FALSE;
949 *\brief Send messages in queue
951 *\param queue Queue folder to process
952 *\param save_msgs Unused
954 *\return Number of messages sent, negative if an error occurred
955 * positive if no error occurred
957 gint procmsg_send_queue(FolderItem *queue, gboolean save_msgs, gchar **errstr)
959 gint sent = 0, err = 0;
961 GSList *sorted_list = NULL;
964 if (!procmsg_queue_lock(errstr)) {
965 main_window_set_menu_sensitive(mainwindow_get_mainwindow());
966 toolbar_main_set_sensitive(mainwindow_get_mainwindow());
971 queue = folder_get_default_queue();
974 procmsg_queue_unlock();
979 main_window_set_menu_sensitive(mainwindow_get_mainwindow());
980 toolbar_main_set_sensitive(mainwindow_get_mainwindow());
982 folder_item_scan(queue);
983 list = folder_item_get_msg_list(queue);
985 /* sort the list per sender account; this helps reusing the same SMTP server */
986 sorted_list = procmsg_list_sort_by_account(queue, list);
988 for (elem = sorted_list; elem != NULL; elem = elem->next) {
992 msginfo = (MsgInfo *)(elem->data);
993 if (!MSG_IS_LOCKED(msginfo->flags) && !MSG_IS_DELETED(msginfo->flags)) {
994 file = folder_item_fetch_msg(queue, msginfo->msgnum);
996 gboolean queued_removed = FALSE;
997 if (procmsg_send_message_queue_full(file,
998 !procmsg_is_last_for_account(queue, msginfo, elem),
999 errstr, queue, msginfo->msgnum, &queued_removed) < 0) {
1000 g_warning("Sending queued message %d failed.",
1005 if (!queued_removed)
1006 folder_item_remove_msg(queue, msginfo->msgnum);
1011 /* FIXME: supposedly if only one message is locked, and queue
1012 * is being flushed, the following free says something like
1013 * "freeing msg ## in folder (nil)". */
1014 procmsg_msginfo_free(&msginfo);
1017 g_slist_free(sorted_list);
1018 folder_item_scan(queue);
1020 if (queue->node && queue->node->children) {
1021 node = queue->node->children;
1022 while (node != NULL) {
1025 send_queue_lock = FALSE;
1026 res = procmsg_send_queue(FOLDER_ITEM(node->data), save_msgs, errstr);
1027 send_queue_lock = TRUE;
1035 procmsg_queue_unlock();
1037 main_window_set_menu_sensitive(mainwindow_get_mainwindow());
1038 toolbar_main_set_sensitive(mainwindow_get_mainwindow());
1040 return (err != 0 ? -err : sent);
1043 gboolean procmsg_is_sending(void)
1045 return send_queue_lock;
1049 *\brief Determine if a queue folder is empty
1051 *\param queue Queue folder to process
1053 *\return TRUE if the queue folder is empty, otherwise return FALSE
1055 gboolean procmsg_queue_is_empty(FolderItem *queue)
1058 gboolean res = FALSE;
1060 queue = folder_get_default_queue();
1061 cm_return_val_if_fail(queue != NULL, TRUE);
1063 folder_item_scan(queue);
1064 list = folder_item_get_msg_list(queue);
1065 res = (list == NULL);
1066 procmsg_msg_list_free(list);
1070 if (queue->node && queue->node->children) {
1071 node = queue->node->children;
1072 while (node != NULL) {
1074 if (!procmsg_queue_is_empty(FOLDER_ITEM(node->data)))
1083 gint procmsg_remove_special_headers(const gchar *in, const gchar *out)
1086 gchar buf[BUFFSIZE];
1088 if ((fp = g_fopen(in, "rb")) == NULL) {
1089 FILE_OP_ERROR(in, "fopen");
1092 if ((outfp = g_fopen(out, "wb")) == NULL) {
1093 FILE_OP_ERROR(out, "fopen");
1097 while (fgets(buf, sizeof(buf), fp) != NULL) {
1099 if ((!strncmp(buf, "X-Claws-End-Special-Headers: 1",
1100 strlen("X-Claws-End-Special-Headers:"))) ||
1101 (!strncmp(buf, "X-Sylpheed-End-Special-Headers: 1",
1102 strlen("X-Sylpheed-End-Special-Headers:"))))
1105 if (buf[0] == '\r' || buf[0] == '\n') break;
1106 /* from other mailers */
1107 if (!strncmp(buf, "Date: ", 6)
1108 || !strncmp(buf, "To: ", 4)
1109 || !strncmp(buf, "From: ", 6)
1110 || !strncmp(buf, "Subject: ", 9)) {
1115 while (fgets(buf, sizeof(buf), fp) != NULL) {
1116 if (fputs(buf, outfp) == EOF) {
1117 FILE_OP_ERROR(out, "fputs");
1128 gint procmsg_save_to_outbox(FolderItem *outbox, const gchar *file,
1132 MsgInfo *msginfo, *tmp_msginfo;
1133 MsgFlags flag = {0, 0};
1135 debug_print("saving sent message...\n");
1138 outbox = folder_get_default_outbox();
1139 cm_return_val_if_fail(outbox != NULL, -1);
1141 /* remove queueing headers */
1143 gchar tmp[MAXPATHLEN + 1];
1145 g_snprintf(tmp, sizeof(tmp), "%s%ctmpmsg.out.%08x",
1146 get_rc_dir(), G_DIR_SEPARATOR, (guint) rand());
1148 if (procmsg_remove_special_headers(file, tmp) !=0)
1151 folder_item_scan(outbox);
1152 if ((num = folder_item_add_msg(outbox, tmp, &flag, TRUE)) < 0) {
1153 g_warning("can't save message");
1158 folder_item_scan(outbox);
1159 if ((num = folder_item_add_msg
1160 (outbox, file, &flag, FALSE)) < 0) {
1161 g_warning("can't save message");
1165 msginfo = folder_item_get_msginfo(outbox, num); /* refcnt++ */
1166 tmp_msginfo = procmsg_msginfo_get_full_info(msginfo); /* refcnt++ */
1167 if (msginfo != NULL) {
1168 procmsg_msginfo_unset_flags(msginfo, ~0, 0);
1169 procmsg_msginfo_free(&msginfo); /* refcnt-- */
1170 /* tmp_msginfo == msginfo */
1171 if (tmp_msginfo && msginfo->extradata &&
1172 (msginfo->extradata->dispositionnotificationto ||
1173 msginfo->extradata->returnreceiptto)) {
1174 procmsg_msginfo_set_flags(msginfo, MSG_RETRCPT_SENT, 0);
1176 procmsg_msginfo_free(&tmp_msginfo); /* refcnt-- */
1183 MsgInfo *procmsg_msginfo_new_ref(MsgInfo *msginfo)
1190 MsgInfo *procmsg_msginfo_new(void)
1192 MsgInfo *newmsginfo;
1194 newmsginfo = g_new0(MsgInfo, 1);
1195 newmsginfo->refcnt = 1;
1200 static MsgInfoAvatar *procmsg_msginfoavatar_copy(MsgInfoAvatar *avatar)
1202 MsgInfoAvatar *newavatar;
1204 if (avatar == NULL) return NULL;
1206 newavatar = g_new0(MsgInfoAvatar, 1);
1207 newavatar->avatar_id = avatar->avatar_id;
1208 newavatar->avatar_src = g_strdup(avatar->avatar_src);
1213 static void procmsg_msginfoavatar_free(MsgInfoAvatar *avatar)
1215 if (avatar != NULL) {
1216 if (avatar->avatar_src != NULL)
1217 g_free(avatar->avatar_src);
1222 MsgInfo *procmsg_msginfo_copy(MsgInfo *msginfo)
1224 MsgInfo *newmsginfo;
1227 if (msginfo == NULL) return NULL;
1229 newmsginfo = g_new0(MsgInfo, 1);
1231 newmsginfo->refcnt = 1;
1233 #define MEMBCOPY(mmb) newmsginfo->mmb = msginfo->mmb
1234 #define MEMBDUP(mmb) newmsginfo->mmb = msginfo->mmb ? \
1235 g_strdup(msginfo->mmb) : NULL
1250 MEMBDUP(newsgroups);
1257 MEMBCOPY(to_folder);
1259 if (msginfo->extradata) {
1260 newmsginfo->extradata = g_new0(MsgInfoExtraData, 1);
1261 if (msginfo->extradata->avatars) {
1262 newmsginfo->extradata->avatars = slist_copy_deep(msginfo->extradata->avatars,
1263 (GCopyFunc) procmsg_msginfoavatar_copy);
1265 MEMBDUP(extradata->dispositionnotificationto);
1266 MEMBDUP(extradata->returnreceiptto);
1267 MEMBDUP(extradata->partial_recv);
1268 MEMBDUP(extradata->account_server);
1269 MEMBDUP(extradata->account_login);
1270 MEMBDUP(extradata->list_post);
1271 MEMBDUP(extradata->list_subscribe);
1272 MEMBDUP(extradata->list_unsubscribe);
1273 MEMBDUP(extradata->list_help);
1274 MEMBDUP(extradata->list_archive);
1275 MEMBDUP(extradata->list_owner);
1276 MEMBDUP(extradata->resent_from);
1279 refs = msginfo->references;
1280 for (refs = msginfo->references; refs != NULL; refs = refs->next) {
1281 newmsginfo->references = g_slist_prepend
1282 (newmsginfo->references, g_strdup(refs->data));
1284 newmsginfo->references = g_slist_reverse(newmsginfo->references);
1287 MEMBDUP(plaintext_file);
1292 MsgInfo *procmsg_msginfo_get_full_info_from_file(MsgInfo *msginfo, const gchar *file)
1294 MsgInfo *full_msginfo;
1296 if (msginfo == NULL) return NULL;
1298 if (!file || !is_file_exist(file)) {
1299 g_warning("procmsg_msginfo_get_full_info_from_file(): can't get message file.");
1303 full_msginfo = procheader_parse_file(file, msginfo->flags, TRUE, FALSE);
1304 if (!full_msginfo) return NULL;
1306 msginfo->total_size = full_msginfo->total_size;
1307 msginfo->planned_download = full_msginfo->planned_download;
1309 if (full_msginfo->extradata) {
1310 if (!msginfo->extradata)
1311 msginfo->extradata = g_new0(MsgInfoExtraData, 1);
1312 if (!msginfo->extradata->list_post)
1313 msginfo->extradata->list_post = g_strdup(full_msginfo->extradata->list_post);
1314 if (!msginfo->extradata->list_subscribe)
1315 msginfo->extradata->list_subscribe = g_strdup(full_msginfo->extradata->list_subscribe);
1316 if (!msginfo->extradata->list_unsubscribe)
1317 msginfo->extradata->list_unsubscribe = g_strdup(full_msginfo->extradata->list_unsubscribe);
1318 if (!msginfo->extradata->list_help)
1319 msginfo->extradata->list_help = g_strdup(full_msginfo->extradata->list_help);
1320 if (!msginfo->extradata->list_archive)
1321 msginfo->extradata->list_archive= g_strdup(full_msginfo->extradata->list_archive);
1322 if (!msginfo->extradata->list_owner)
1323 msginfo->extradata->list_owner = g_strdup(full_msginfo->extradata->list_owner);
1324 if (!msginfo->extradata->avatars)
1325 msginfo->extradata->avatars = slist_copy_deep(full_msginfo->extradata->avatars,
1326 (GCopyFunc) procmsg_msginfoavatar_copy);
1327 if (!msginfo->extradata->dispositionnotificationto)
1328 msginfo->extradata->dispositionnotificationto =
1329 g_strdup(full_msginfo->extradata->dispositionnotificationto);
1330 if (!msginfo->extradata->returnreceiptto)
1331 msginfo->extradata->returnreceiptto = g_strdup
1332 (full_msginfo->extradata->returnreceiptto);
1333 if (!msginfo->extradata->partial_recv && full_msginfo->extradata->partial_recv)
1334 msginfo->extradata->partial_recv = g_strdup
1335 (full_msginfo->extradata->partial_recv);
1336 if (!msginfo->extradata->account_server && full_msginfo->extradata->account_server)
1337 msginfo->extradata->account_server = g_strdup
1338 (full_msginfo->extradata->account_server);
1339 if (!msginfo->extradata->account_login && full_msginfo->extradata->account_login)
1340 msginfo->extradata->account_login = g_strdup
1341 (full_msginfo->extradata->account_login);
1342 if (!msginfo->extradata->resent_from && full_msginfo->extradata->resent_from)
1343 msginfo->extradata->resent_from = g_strdup
1344 (full_msginfo->extradata->resent_from);
1346 procmsg_msginfo_free(&full_msginfo);
1348 return procmsg_msginfo_new_ref(msginfo);
1351 MsgInfo *procmsg_msginfo_get_full_info(MsgInfo *msginfo)
1353 MsgInfo *full_msginfo;
1356 if (msginfo == NULL) return NULL;
1358 file = procmsg_get_message_file_path(msginfo);
1359 if (!file || !is_file_exist(file)) {
1361 file = procmsg_get_message_file(msginfo);
1363 if (!file || !is_file_exist(file)) {
1364 g_warning("procmsg_msginfo_get_full_info(): can't get message file.");
1368 full_msginfo = procmsg_msginfo_get_full_info_from_file(msginfo, file);
1370 return full_msginfo;
1373 #define FREENULL(n) { g_free(n); n = NULL; }
1374 void procmsg_msginfo_free(MsgInfo **msginfo_ptr)
1376 MsgInfo *msginfo = *msginfo_ptr;
1378 if (msginfo == NULL) return;
1381 if (msginfo->refcnt > 0)
1384 if (msginfo->to_folder) {
1385 msginfo->to_folder->op_count--;
1386 folder_item_update(msginfo->to_folder, F_ITEM_UPDATE_MSGCNT);
1389 FREENULL(msginfo->fromspace);
1391 FREENULL(msginfo->fromname);
1393 FREENULL(msginfo->date);
1394 FREENULL(msginfo->from);
1395 FREENULL(msginfo->to);
1396 FREENULL(msginfo->cc);
1397 FREENULL(msginfo->newsgroups);
1398 FREENULL(msginfo->subject);
1399 FREENULL(msginfo->msgid);
1400 FREENULL(msginfo->inreplyto);
1401 FREENULL(msginfo->xref);
1403 if (msginfo->extradata) {
1404 if (msginfo->extradata->avatars) {
1405 g_slist_foreach(msginfo->extradata->avatars,
1406 (GFunc)procmsg_msginfoavatar_free,
1408 g_slist_free(msginfo->extradata->avatars);
1409 msginfo->extradata->avatars = NULL;
1411 FREENULL(msginfo->extradata->returnreceiptto);
1412 FREENULL(msginfo->extradata->dispositionnotificationto);
1413 FREENULL(msginfo->extradata->list_post);
1414 FREENULL(msginfo->extradata->list_subscribe);
1415 FREENULL(msginfo->extradata->list_unsubscribe);
1416 FREENULL(msginfo->extradata->list_help);
1417 FREENULL(msginfo->extradata->list_archive);
1418 FREENULL(msginfo->extradata->list_owner);
1419 FREENULL(msginfo->extradata->partial_recv);
1420 FREENULL(msginfo->extradata->account_server);
1421 FREENULL(msginfo->extradata->account_login);
1422 FREENULL(msginfo->extradata->resent_from);
1423 FREENULL(msginfo->extradata);
1425 slist_free_strings_full(msginfo->references);
1426 msginfo->references = NULL;
1427 g_slist_free(msginfo->tags);
1428 msginfo->tags = NULL;
1430 FREENULL(msginfo->plaintext_file);
1433 *msginfo_ptr = NULL;
1437 guint procmsg_msginfo_memusage(MsgInfo *msginfo)
1442 memusage += sizeof(MsgInfo);
1443 if (msginfo->fromname)
1444 memusage += strlen(msginfo->fromname);
1446 memusage += strlen(msginfo->date);
1448 memusage += strlen(msginfo->from);
1450 memusage += strlen(msginfo->to);
1452 memusage += strlen(msginfo->cc);
1453 if (msginfo->newsgroups)
1454 memusage += strlen(msginfo->newsgroups);
1455 if (msginfo->subject)
1456 memusage += strlen(msginfo->subject);
1458 memusage += strlen(msginfo->msgid);
1459 if (msginfo->inreplyto)
1460 memusage += strlen(msginfo->inreplyto);
1462 for (tmp = msginfo->references; tmp; tmp=tmp->next) {
1463 gchar *r = (gchar *)tmp->data;
1464 memusage += r?strlen(r):0 + sizeof(GSList);
1466 if (msginfo->fromspace)
1467 memusage += strlen(msginfo->fromspace);
1469 for (tmp = msginfo->tags; tmp; tmp=tmp->next) {
1470 memusage += sizeof(GSList);
1472 if (msginfo->extradata) {
1473 memusage += sizeof(MsgInfoExtraData);
1474 if (msginfo->extradata->avatars) {
1475 for (tmp = msginfo->extradata->avatars; tmp; tmp = tmp->next) {
1476 MsgInfoAvatar *avt = (MsgInfoAvatar *)tmp->data;
1477 memusage += (avt->avatar_src)? strlen(avt->avatar_src): 0;
1478 memusage += sizeof(MsgInfoAvatar) + sizeof(GSList);
1481 if (msginfo->extradata->dispositionnotificationto)
1482 memusage += strlen(msginfo->extradata->dispositionnotificationto);
1483 if (msginfo->extradata->returnreceiptto)
1484 memusage += strlen(msginfo->extradata->returnreceiptto);
1486 if (msginfo->extradata->partial_recv)
1487 memusage += strlen(msginfo->extradata->partial_recv);
1488 if (msginfo->extradata->account_server)
1489 memusage += strlen(msginfo->extradata->account_server);
1490 if (msginfo->extradata->account_login)
1491 memusage += strlen(msginfo->extradata->account_login);
1492 if (msginfo->extradata->resent_from)
1493 memusage += strlen(msginfo->extradata->resent_from);
1495 if (msginfo->extradata->list_post)
1496 memusage += strlen(msginfo->extradata->list_post);
1497 if (msginfo->extradata->list_subscribe)
1498 memusage += strlen(msginfo->extradata->list_subscribe);
1499 if (msginfo->extradata->list_unsubscribe)
1500 memusage += strlen(msginfo->extradata->list_unsubscribe);
1501 if (msginfo->extradata->list_help)
1502 memusage += strlen(msginfo->extradata->list_help);
1503 if (msginfo->extradata->list_archive)
1504 memusage += strlen(msginfo->extradata->list_archive);
1505 if (msginfo->extradata->list_owner)
1506 memusage += strlen(msginfo->extradata->list_owner);
1511 static gint procmsg_send_message_queue_full(const gchar *file, gboolean keep_session, gchar **errstr,
1512 FolderItem *queue, gint msgnum, gboolean *queued_removed)
1514 static HeaderEntry qentry[] = {
1515 {"S:", NULL, FALSE}, /* 0 */
1516 {"SSV:", NULL, FALSE},
1517 {"R:", NULL, FALSE},
1518 {"NG:", NULL, FALSE},
1519 {"MAID:", NULL, FALSE},
1520 {"NAID:", NULL, FALSE}, /* 5 */
1521 {"SCF:", NULL, FALSE},
1522 {"RMID:", NULL, FALSE},
1523 {"FMID:", NULL, FALSE},
1524 {"X-Claws-Privacy-System:", NULL, FALSE},
1525 {"X-Claws-Encrypt:", NULL, FALSE}, /* 10 */
1526 {"X-Claws-Encrypt-Data:", NULL, FALSE},
1527 {"X-Claws-End-Special-Headers:", NULL, FALSE},
1528 {"X-Sylpheed-Privacy-System:", NULL, FALSE},
1529 {"X-Sylpheed-Encrypt:", NULL, FALSE},
1530 {"X-Sylpheed-Encrypt-Data:", NULL, FALSE}, /* 15 */
1531 {"X-Sylpheed-End-Special-Headers:", NULL, FALSE},
1532 {NULL, NULL, FALSE}};
1535 gint mailval = 0, newsval = 0;
1537 gchar *smtpserver = NULL;
1538 GSList *to_list = NULL;
1539 GSList *newsgroup_list = NULL;
1540 gchar *savecopyfolder = NULL;
1541 gchar *replymessageid = NULL;
1542 gchar *fwdmessageid = NULL;
1545 PrefsAccount *mailac = NULL, *newsac = NULL;
1546 gboolean encrypt = FALSE;
1549 cm_return_val_if_fail(file != NULL, -1);
1551 if ((fp = g_fopen(file, "rb")) == NULL) {
1552 FILE_OP_ERROR(file, "fopen");
1554 if (*errstr) g_free(*errstr);
1555 *errstr = g_strdup_printf(_("Couldn't open file %s."), file);
1560 while ((hnum = procheader_get_one_field(&buf, fp, qentry)) != -1 && buf != NULL) {
1561 gchar *p = buf + strlen(qentry[hnum].name);
1569 if (smtpserver == NULL)
1570 smtpserver = g_strdup(p);
1573 to_list = address_list_append(to_list, p);
1576 newsgroup_list = newsgroup_list_append(newsgroup_list, p);
1578 case Q_MAIL_ACCOUNT_ID:
1579 mailac = account_find_from_id(atoi(p));
1581 case Q_NEWS_ACCOUNT_ID:
1582 newsac = account_find_from_id(atoi(p));
1584 case Q_SAVE_COPY_FOLDER:
1585 if (savecopyfolder == NULL)
1586 savecopyfolder = g_strdup(p);
1588 case Q_REPLY_MESSAGE_ID:
1589 if (replymessageid == NULL)
1590 replymessageid = g_strdup(p);
1592 case Q_FWD_MESSAGE_ID:
1593 if (fwdmessageid == NULL)
1594 fwdmessageid = g_strdup(p);
1602 case Q_CLAWS_HDRS_OLD:
1603 /* end of special headers reached */
1605 goto send_mail; /* can't "break;break;" */
1611 filepos = ftell(fp);
1613 FILE_OP_ERROR(file, "ftell");
1615 if (*errstr) g_free(*errstr);
1616 *errstr = g_strdup_printf(_("Couldn't open file %s."), file);
1622 debug_print("Sending message by mail\n");
1625 if (*errstr) g_free(*errstr);
1626 *errstr = g_strdup_printf(_("Queued message header is broken."));
1629 } else if (mailac && mailac->use_mail_command &&
1630 mailac->mail_command && (* mailac->mail_command)) {
1631 mailval = send_message_local(mailac->mail_command, fp);
1634 mailac = account_find_from_smtp_server(from, smtpserver);
1636 g_warning("Account not found. "
1637 "Using current account...");
1638 mailac = cur_account;
1643 mailval = send_message_smtp_full(mailac, to_list, fp, keep_session);
1644 if (mailval == -1 && errstr) {
1645 if (*errstr) g_free(*errstr);
1646 *errstr = g_strdup_printf(_("An error happened during SMTP session."));
1649 PrefsAccount tmp_ac;
1651 g_warning("Account not found.");
1653 memset(&tmp_ac, 0, sizeof(PrefsAccount));
1654 tmp_ac.address = from;
1655 tmp_ac.smtp_server = smtpserver;
1656 tmp_ac.smtpport = SMTP_PORT;
1657 mailval = send_message_smtp(&tmp_ac, to_list, fp);
1658 if (mailval == -1 && errstr) {
1659 if (*errstr) g_free(*errstr);
1660 *errstr = g_strdup_printf(_("No specific account has been found to "
1661 "send, and an error happened during SMTP session."));
1665 } else if (!to_list && !newsgroup_list) {
1667 if (*errstr) g_free(*errstr);
1668 *errstr = g_strdup(_("Couldn't determine sending information. "
1669 "Maybe the email hasn't been generated by Claws Mail."));
1674 if (fseek(fp, filepos, SEEK_SET) < 0) {
1675 FILE_OP_ERROR(file, "fseek");
1679 if (newsgroup_list && newsac && (mailval == 0)) {
1682 gchar buf[BUFFSIZE];
1685 /* write to temporary file */
1686 tmp = g_strdup_printf("%s%cnntp%p", get_tmp_dir(),
1687 G_DIR_SEPARATOR, file);
1688 if ((tmpfp = g_fopen(tmp, "wb")) == NULL) {
1689 FILE_OP_ERROR(tmp, "fopen");
1691 alertpanel_error(_("Couldn't create temporary file for news sending."));
1693 if (change_file_mode_rw(tmpfp, tmp) < 0) {
1694 FILE_OP_ERROR(tmp, "chmod");
1695 g_warning("can't change file mode");
1698 while ((newsval == 0) && fgets(buf, sizeof(buf), fp) != NULL) {
1699 if (fputs(buf, tmpfp) == EOF) {
1700 FILE_OP_ERROR(tmp, "fputs");
1703 if (*errstr) g_free(*errstr);
1704 *errstr = g_strdup_printf(_("Error when writing temporary file for news sending."));
1711 debug_print("Sending message by news\n");
1713 folder = FOLDER(newsac->folder);
1715 newsval = news_post(folder, tmp);
1716 if (newsval < 0 && errstr) {
1717 if (*errstr) g_free(*errstr);
1718 *errstr = g_strdup_printf(_("Error occurred while posting the message to %s."),
1719 newsac->nntp_server);
1729 /* update session statistics */
1730 if (mailval == 0 && newsval == 0) {
1731 /* update session stats */
1733 session_stats.replied++;
1734 else if (fwdmessageid)
1735 session_stats.forwarded++;
1737 session_stats.sent++;
1740 /* save message to outbox */
1741 if (mailval == 0 && newsval == 0 && savecopyfolder) {
1742 debug_print("saving sent message...\n");
1744 if (!encrypt || !mailac->save_encrypted_as_clear_text) {
1745 outbox = folder_find_item_from_identifier(savecopyfolder);
1747 outbox = folder_get_default_outbox();
1749 /* Mail was not saved to outbox before encrypting, save it now. */
1750 gboolean saved = FALSE;
1751 *queued_removed = FALSE;
1752 if (queue && msgnum > 0) {
1753 MsgInfo *queued_mail = folder_item_get_msginfo(queue, msgnum);
1754 if (folder_item_move_msg(outbox, queued_mail) >= 0) {
1755 debug_print("moved queued mail %d to sent folder\n", msgnum);
1757 *queued_removed = TRUE;
1758 } else if (folder_item_copy_msg(outbox, queued_mail) >= 0) {
1759 debug_print("copied queued mail %d to sent folder\n", msgnum);
1762 procmsg_msginfo_free(&queued_mail);
1765 debug_print("resaving queued mail to sent folder\n");
1766 procmsg_save_to_outbox(outbox, file, TRUE);
1771 if (replymessageid != NULL || fwdmessageid != NULL) {
1775 if (replymessageid != NULL)
1776 tokens = g_strsplit(replymessageid, "\t", 0);
1778 tokens = g_strsplit(fwdmessageid, "\t", 0);
1779 item = folder_find_item_from_identifier(tokens[0]);
1781 /* check if queued message has valid folder and message id */
1782 if (item != NULL && tokens[2] != NULL) {
1785 msginfo = folder_item_get_msginfo(item, atoi(tokens[1]));
1787 /* check if referring message exists and has a message id */
1788 if ((msginfo != NULL) &&
1789 (msginfo->msgid != NULL) &&
1790 (strcmp(msginfo->msgid, tokens[2]) != 0)) {
1791 procmsg_msginfo_free(&msginfo);
1795 if (msginfo == NULL) {
1796 msginfo = folder_item_get_msginfo_by_msgid(item, tokens[2]);
1799 if (msginfo != NULL) {
1800 if (replymessageid != NULL) {
1801 MsgPermFlags to_unset = 0;
1803 if (prefs_common.mark_as_read_on_new_window)
1804 to_unset = (MSG_NEW|MSG_UNREAD);
1806 procmsg_msginfo_unset_flags(msginfo, to_unset, 0);
1807 procmsg_msginfo_set_flags(msginfo, MSG_REPLIED, 0);
1809 procmsg_msginfo_set_flags(msginfo, MSG_FORWARDED, 0);
1811 procmsg_msginfo_free(&msginfo);
1819 slist_free_strings_full(to_list);
1820 slist_free_strings_full(newsgroup_list);
1821 g_free(savecopyfolder);
1822 g_free(replymessageid);
1823 g_free(fwdmessageid);
1825 return (newsval != 0 ? newsval : mailval);
1828 gint procmsg_send_message_queue(const gchar *file, gchar **errstr, FolderItem *queue, gint msgnum, gboolean *queued_removed)
1830 gint result = procmsg_send_message_queue_full(file, FALSE, errstr, queue, msgnum, queued_removed);
1831 main_window_set_menu_sensitive(mainwindow_get_mainwindow());
1832 toolbar_main_set_sensitive(mainwindow_get_mainwindow());
1836 gint procmsg_send_message_queue_with_lock(const gchar *file, gchar **errstr, FolderItem *queue, gint msgnum, gboolean *queued_removed)
1839 if (procmsg_queue_lock(errstr)) {
1840 val = procmsg_send_message_queue(file, errstr, queue, msgnum, queued_removed);
1841 procmsg_queue_unlock();
1847 static void update_folder_msg_counts(FolderItem *item, MsgInfo *msginfo, MsgPermFlags old_flags)
1849 MsgPermFlags new_flags = msginfo->flags.perm_flags;
1852 if (!(old_flags & MSG_NEW) && (new_flags & MSG_NEW)) {
1856 if ((old_flags & MSG_NEW) && !(new_flags & MSG_NEW)) {
1861 if (!(old_flags & MSG_UNREAD) && (new_flags & MSG_UNREAD)) {
1862 item->unread_msgs++;
1863 if (procmsg_msg_has_marked_parent(msginfo))
1864 item->unreadmarked_msgs++;
1867 if ((old_flags & MSG_UNREAD) && !(new_flags & MSG_UNREAD)) {
1868 item->unread_msgs--;
1869 if (procmsg_msg_has_marked_parent(msginfo))
1870 item->unreadmarked_msgs--;
1874 if (!(old_flags & MSG_MARKED) && (new_flags & MSG_MARKED)) {
1875 procmsg_update_unread_children(msginfo, TRUE);
1876 item->marked_msgs++;
1879 if ((old_flags & MSG_MARKED) && !(new_flags & MSG_MARKED)) {
1880 procmsg_update_unread_children(msginfo, FALSE);
1881 item->marked_msgs--;
1884 if (!(old_flags & MSG_REPLIED) && (new_flags & MSG_REPLIED)) {
1885 item->replied_msgs++;
1888 if ((old_flags & MSG_REPLIED) && !(new_flags & MSG_REPLIED)) {
1889 item->replied_msgs--;
1892 if (!(old_flags & MSG_FORWARDED) && (new_flags & MSG_FORWARDED)) {
1893 item->forwarded_msgs++;
1896 if ((old_flags & MSG_FORWARDED) && !(new_flags & MSG_FORWARDED)) {
1897 item->forwarded_msgs--;
1900 if (!(old_flags & MSG_LOCKED) && (new_flags & MSG_LOCKED)) {
1901 item->locked_msgs++;
1904 if ((old_flags & MSG_LOCKED) && !(new_flags & MSG_LOCKED)) {
1905 item->locked_msgs--;
1908 if ((old_flags & MSG_IGNORE_THREAD) && !(new_flags & MSG_IGNORE_THREAD)) {
1909 item->ignored_msgs--;
1912 if (!(old_flags & MSG_IGNORE_THREAD) && (new_flags & MSG_IGNORE_THREAD)) {
1913 item->ignored_msgs++;
1916 if ((old_flags & MSG_WATCH_THREAD) && !(new_flags & MSG_WATCH_THREAD)) {
1917 item->watched_msgs--;
1920 if (!(old_flags & MSG_WATCH_THREAD) && (new_flags & MSG_WATCH_THREAD)) {
1921 item->watched_msgs++;
1925 void procmsg_msginfo_set_flags(MsgInfo *msginfo, MsgPermFlags perm_flags, MsgTmpFlags tmp_flags)
1928 MsgInfoUpdate msginfo_update;
1929 MsgPermFlags perm_flags_new, perm_flags_old;
1930 MsgTmpFlags tmp_flags_old;
1932 cm_return_if_fail(msginfo != NULL);
1933 item = msginfo->folder;
1934 cm_return_if_fail(item != NULL);
1936 debug_print("Setting flags for message %d in folder %s\n", msginfo->msgnum, item->path);
1938 /* Perm Flags handling */
1939 perm_flags_old = msginfo->flags.perm_flags;
1940 perm_flags_new = msginfo->flags.perm_flags | perm_flags;
1941 if ((perm_flags & MSG_IGNORE_THREAD) || (perm_flags_old & MSG_IGNORE_THREAD)) {
1942 perm_flags_new &= ~(MSG_NEW | MSG_UNREAD);
1944 if ((perm_flags & MSG_WATCH_THREAD) || (perm_flags_old & MSG_WATCH_THREAD)) {
1945 perm_flags_new &= ~(MSG_IGNORE_THREAD);
1948 if (perm_flags_old != perm_flags_new) {
1949 folder_item_change_msg_flags(msginfo->folder, msginfo, perm_flags_new);
1951 update_folder_msg_counts(item, msginfo, perm_flags_old);
1952 summary_update_unread(mainwindow_get_mainwindow()->summaryview, NULL);
1955 /* Tmp flags handling */
1956 tmp_flags_old = msginfo->flags.tmp_flags;
1957 msginfo->flags.tmp_flags |= tmp_flags;
1959 /* update notification */
1960 if ((perm_flags_old != perm_flags_new) || (tmp_flags_old != msginfo->flags.tmp_flags)) {
1961 msginfo_update.msginfo = msginfo;
1962 msginfo_update.flags = MSGINFO_UPDATE_FLAGS;
1963 hooks_invoke(MSGINFO_UPDATE_HOOKLIST, &msginfo_update);
1964 folder_item_update(msginfo->folder, F_ITEM_UPDATE_MSGCNT);
1968 void procmsg_msginfo_unset_flags(MsgInfo *msginfo, MsgPermFlags perm_flags, MsgTmpFlags tmp_flags)
1971 MsgInfoUpdate msginfo_update;
1972 MsgPermFlags perm_flags_new, perm_flags_old;
1973 MsgTmpFlags tmp_flags_old;
1975 cm_return_if_fail(msginfo != NULL);
1976 item = msginfo->folder;
1977 cm_return_if_fail(item != NULL);
1979 debug_print("Unsetting flags for message %d in folder %s\n", msginfo->msgnum, item->path);
1981 /* Perm Flags handling */
1982 perm_flags_old = msginfo->flags.perm_flags;
1983 perm_flags_new = msginfo->flags.perm_flags & ~perm_flags;
1985 if (perm_flags_old != perm_flags_new) {
1986 folder_item_change_msg_flags(msginfo->folder, msginfo, perm_flags_new);
1988 update_folder_msg_counts(item, msginfo, perm_flags_old);
1991 /* Tmp flags hanlding */
1992 tmp_flags_old = msginfo->flags.tmp_flags;
1993 msginfo->flags.tmp_flags &= ~tmp_flags;
1995 /* update notification */
1996 if ((perm_flags_old != perm_flags_new) || (tmp_flags_old != msginfo->flags.tmp_flags)) {
1997 msginfo_update.msginfo = msginfo;
1998 msginfo_update.flags = MSGINFO_UPDATE_FLAGS;
1999 hooks_invoke(MSGINFO_UPDATE_HOOKLIST, &msginfo_update);
2000 folder_item_update(msginfo->folder, F_ITEM_UPDATE_MSGCNT);
2004 void procmsg_msginfo_change_flags(MsgInfo *msginfo,
2005 MsgPermFlags add_perm_flags, MsgTmpFlags add_tmp_flags,
2006 MsgPermFlags rem_perm_flags, MsgTmpFlags rem_tmp_flags)
2009 MsgInfoUpdate msginfo_update;
2010 MsgPermFlags perm_flags_new, perm_flags_old;
2011 MsgTmpFlags tmp_flags_old;
2013 cm_return_if_fail(msginfo != NULL);
2014 item = msginfo->folder;
2015 cm_return_if_fail(item != NULL);
2017 debug_print("Changing flags for message %d in folder %s\n", msginfo->msgnum, item->path);
2019 /* Perm Flags handling */
2020 perm_flags_old = msginfo->flags.perm_flags;
2021 perm_flags_new = (msginfo->flags.perm_flags & ~rem_perm_flags) | add_perm_flags;
2022 if ((add_perm_flags & MSG_IGNORE_THREAD) || (perm_flags_old & MSG_IGNORE_THREAD)) {
2023 perm_flags_new &= ~(MSG_NEW | MSG_UNREAD);
2025 if ((add_perm_flags & MSG_WATCH_THREAD) || (perm_flags_old & MSG_WATCH_THREAD)) {
2026 perm_flags_new &= ~(MSG_IGNORE_THREAD);
2029 if (perm_flags_old != perm_flags_new) {
2030 folder_item_change_msg_flags(msginfo->folder, msginfo, perm_flags_new);
2032 update_folder_msg_counts(item, msginfo, perm_flags_old);
2036 /* Tmp flags handling */
2037 tmp_flags_old = msginfo->flags.tmp_flags;
2038 msginfo->flags.tmp_flags &= ~rem_tmp_flags;
2039 msginfo->flags.tmp_flags |= add_tmp_flags;
2041 /* update notification */
2042 if ((perm_flags_old != perm_flags_new) || (tmp_flags_old != msginfo->flags.tmp_flags)) {
2043 msginfo_update.msginfo = msginfo;
2044 msginfo_update.flags = MSGINFO_UPDATE_FLAGS;
2045 hooks_invoke(MSGINFO_UPDATE_HOOKLIST, &msginfo_update);
2046 folder_item_update(msginfo->folder, F_ITEM_UPDATE_MSGCNT);
2051 *\brief check for flags (e.g. mark) in prior msgs of current thread
2053 *\param info Current message
2054 *\param perm_flags Flags to be checked
2055 *\param parentmsgs Hash of prior msgs to avoid loops
2057 *\return gboolean TRUE if perm_flags are found
2059 static gboolean procmsg_msg_has_flagged_parent_real(MsgInfo *info,
2060 MsgPermFlags perm_flags, GHashTable *parentmsgs)
2064 cm_return_val_if_fail(info != NULL, FALSE);
2066 if (info != NULL && info->folder != NULL && info->inreplyto != NULL) {
2067 tmp = folder_item_get_msginfo_by_msgid(info->folder,
2069 if (tmp && (tmp->flags.perm_flags & perm_flags)) {
2070 procmsg_msginfo_free(&tmp);
2072 } else if (tmp != NULL) {
2075 if (g_hash_table_lookup(parentmsgs, info)) {
2076 debug_print("loop detected: %d\n",
2080 g_hash_table_insert(parentmsgs, info, "1");
2081 result = procmsg_msg_has_flagged_parent_real(
2082 tmp, perm_flags, parentmsgs);
2084 procmsg_msginfo_free(&tmp);
2094 *\brief Callback for cleaning up hash of parentmsgs
2096 static gboolean parentmsgs_hash_remove(gpointer key,
2104 *\brief Set up list of parentmsgs
2105 * See procmsg_msg_has_flagged_parent_real()
2107 gboolean procmsg_msg_has_flagged_parent(MsgInfo *info, MsgPermFlags perm_flags)
2110 static GHashTable *parentmsgs = NULL;
2112 if (parentmsgs == NULL)
2113 parentmsgs = g_hash_table_new(NULL, NULL);
2115 result = procmsg_msg_has_flagged_parent_real(info, perm_flags, parentmsgs);
2116 g_hash_table_foreach_remove(parentmsgs, parentmsgs_hash_remove, NULL);
2122 *\brief Check if msgs prior in thread are marked
2123 * See procmsg_msg_has_flagged_parent_real()
2125 gboolean procmsg_msg_has_marked_parent(MsgInfo *info)
2127 return procmsg_msg_has_flagged_parent(info, MSG_MARKED);
2131 static GSList *procmsg_find_children_func(MsgInfo *info,
2132 GSList *children, GSList *all)
2136 cm_return_val_if_fail(info!=NULL, children);
2137 if (info->msgid == NULL)
2140 for (cur = all; cur != NULL; cur = g_slist_next(cur)) {
2141 MsgInfo *tmp = (MsgInfo *)cur->data;
2142 if (tmp->inreplyto && !strcmp(tmp->inreplyto, info->msgid)) {
2143 /* Check if message is already in the list */
2144 if ((children == NULL) ||
2145 (g_slist_index(children, tmp) == -1)) {
2146 children = g_slist_prepend(children,
2147 procmsg_msginfo_new_ref(tmp));
2148 children = procmsg_find_children_func(tmp,
2157 static GSList *procmsg_find_children (MsgInfo *info)
2162 cm_return_val_if_fail(info!=NULL, NULL);
2163 all = folder_item_get_msg_list(info->folder);
2164 children = procmsg_find_children_func(info, NULL, all);
2165 if (children != NULL) {
2166 for (cur = all; cur != NULL; cur = g_slist_next(cur)) {
2167 /* this will not free the used pointers
2168 created with procmsg_msginfo_new_ref */
2169 procmsg_msginfo_free((MsgInfo **)&(cur->data));
2177 static void procmsg_update_unread_children(MsgInfo *info, gboolean newly_marked)
2179 GSList *children = procmsg_find_children(info);
2181 for (cur = children; cur != NULL; cur = g_slist_next(cur)) {
2182 MsgInfo *tmp = (MsgInfo *)cur->data;
2183 if(MSG_IS_UNREAD(tmp->flags) && !MSG_IS_IGNORE_THREAD(tmp->flags)) {
2185 info->folder->unreadmarked_msgs++;
2187 info->folder->unreadmarked_msgs--;
2188 folder_item_update(info->folder, F_ITEM_UPDATE_MSGCNT);
2190 procmsg_msginfo_free(&tmp);
2192 g_slist_free(children);
2196 * Set the destination folder for a copy or move operation
2198 * \param msginfo The message which's destination folder is changed
2199 * \param to_folder The destination folder for the operation
2201 void procmsg_msginfo_set_to_folder(MsgInfo *msginfo, FolderItem *to_folder)
2203 if(msginfo->to_folder != NULL) {
2204 msginfo->to_folder->op_count--;
2205 folder_item_update(msginfo->to_folder, F_ITEM_UPDATE_MSGCNT);
2207 msginfo->to_folder = to_folder;
2208 if(to_folder != NULL) {
2209 to_folder->op_count++;
2210 folder_item_update(msginfo->to_folder, F_ITEM_UPDATE_MSGCNT);
2215 * Apply filtering actions to the msginfo
2217 * \param msginfo The MsgInfo describing the message that should be filtered
2218 * \return TRUE if the message was moved and MsgInfo is now invalid,
2221 static gboolean procmsg_msginfo_filter(MsgInfo *msginfo, PrefsAccount* ac_prefs)
2223 MailFilteringData mail_filtering_data;
2225 mail_filtering_data.msginfo = msginfo;
2226 mail_filtering_data.msglist = NULL;
2227 mail_filtering_data.filtered = NULL;
2228 mail_filtering_data.unfiltered = NULL;
2229 mail_filtering_data.account = ac_prefs;
2231 if (!ac_prefs || ac_prefs->filterhook_on_recv)
2232 if (hooks_invoke(MAIL_FILTERING_HOOKLIST, &mail_filtering_data))
2235 /* filter if enabled in prefs or move to inbox if not */
2236 if((filtering_rules != NULL) &&
2237 filter_message_by_msginfo(filtering_rules, msginfo, ac_prefs,
2238 FILTERING_INCORPORATION, NULL)) {
2245 void procmsg_msglist_filter(GSList *list, PrefsAccount *ac,
2246 GSList **filtered, GSList **unfiltered,
2249 GSList *cur, *to_do = NULL;
2250 gint total = 0, curnum = 0;
2251 MailFilteringData mail_filtering_data;
2253 cm_return_if_fail(filtered != NULL);
2254 cm_return_if_fail(unfiltered != NULL);
2262 total = g_slist_length(list);
2266 *unfiltered = g_slist_copy(list);
2270 statusbar_print_all(_("Filtering messages...\n"));
2272 mail_filtering_data.msginfo = NULL;
2273 mail_filtering_data.msglist = list;
2274 mail_filtering_data.filtered = NULL;
2275 mail_filtering_data.unfiltered = NULL;
2276 mail_filtering_data.account = ac;
2278 if (!ac || ac->filterhook_on_recv)
2279 hooks_invoke(MAIL_LISTFILTERING_HOOKLIST, &mail_filtering_data);
2281 if (mail_filtering_data.filtered == NULL &&
2282 mail_filtering_data.unfiltered == NULL) {
2283 /* nothing happened */
2284 debug_print(MAIL_LISTFILTERING_HOOKLIST " did nothing. filtering whole list normally.\n");
2287 if (mail_filtering_data.filtered != NULL) {
2288 /* keep track of what's been filtered by the hooks */
2289 debug_print(MAIL_LISTFILTERING_HOOKLIST " filtered some stuff. total %d filtered %d unfilt %d.\n",
2290 g_slist_length(list),
2291 g_slist_length(mail_filtering_data.filtered),
2292 g_slist_length(mail_filtering_data.unfiltered));
2294 *filtered = g_slist_copy(mail_filtering_data.filtered);
2296 if (mail_filtering_data.unfiltered != NULL) {
2297 /* what the hooks didn't handle will go in filtered or
2298 * unfiltered in the next loop */
2299 debug_print(MAIL_LISTFILTERING_HOOKLIST " left unfiltered stuff. total %d filtered %d unfilt %d.\n",
2300 g_slist_length(list),
2301 g_slist_length(mail_filtering_data.filtered),
2302 g_slist_length(mail_filtering_data.unfiltered));
2303 to_do = mail_filtering_data.unfiltered;
2306 for (cur = to_do; cur; cur = cur->next) {
2307 MsgInfo *info = (MsgInfo *)cur->data;
2308 if (procmsg_msginfo_filter(info, ac))
2309 *filtered = g_slist_prepend(*filtered, info);
2311 *unfiltered = g_slist_prepend(*unfiltered, info);
2312 statusbar_progress_all(curnum++, total, prefs_common.statusbar_update_step);
2315 g_slist_free(mail_filtering_data.filtered);
2316 g_slist_free(mail_filtering_data.unfiltered);
2318 *filtered = g_slist_reverse(*filtered);
2319 *unfiltered = g_slist_reverse(*unfiltered);
2321 statusbar_progress_all(0,0,0);
2322 statusbar_pop_all();
2325 MsgInfo *procmsg_msginfo_new_from_mimeinfo(MsgInfo *src_msginfo, MimeInfo *mimeinfo)
2327 MsgInfo *tmp_msginfo = NULL;
2328 MsgFlags flags = {0, 0};
2329 gchar *tmpfile = get_tmp_file();
2330 FILE *fp = g_fopen(tmpfile, "wb");
2332 if (!mimeinfo || mimeinfo->type != MIMETYPE_MESSAGE ||
2333 g_ascii_strcasecmp(mimeinfo->subtype, "rfc822")) {
2334 g_warning("procmsg_msginfo_new_from_mimeinfo(): unsuitable mimeinfo");
2341 if (fp && procmime_write_mimeinfo(mimeinfo, fp) >= 0) {
2344 tmp_msginfo = procheader_parse_file(
2351 if (tmp_msginfo != NULL) {
2353 tmp_msginfo->folder = src_msginfo->folder;
2354 tmp_msginfo->plaintext_file = g_strdup(tmpfile);
2356 g_warning("procmsg_msginfo_new_from_mimeinfo(): Can't generate new msginfo");
2364 static GSList *spam_learners = NULL;
2366 void procmsg_register_spam_learner (int (*learn_func)(MsgInfo *info, GSList *list, gboolean spam))
2368 if (!g_slist_find(spam_learners, learn_func))
2369 spam_learners = g_slist_append(spam_learners, learn_func);
2370 if (mainwindow_get_mainwindow()) {
2371 main_window_set_menu_sensitive(mainwindow_get_mainwindow());
2372 summary_set_menu_sensitive(
2373 mainwindow_get_mainwindow()->summaryview);
2374 toolbar_main_set_sensitive(mainwindow_get_mainwindow());
2378 void procmsg_unregister_spam_learner (int (*learn_func)(MsgInfo *info, GSList *list, gboolean spam))
2380 spam_learners = g_slist_remove(spam_learners, learn_func);
2381 if (mainwindow_get_mainwindow()) {
2382 main_window_set_menu_sensitive(mainwindow_get_mainwindow());
2383 summary_set_menu_sensitive(
2384 mainwindow_get_mainwindow()->summaryview);
2385 toolbar_main_set_sensitive(mainwindow_get_mainwindow());
2389 gboolean procmsg_spam_can_learn(void)
2391 return g_slist_length(spam_learners) > 0;
2394 int procmsg_spam_learner_learn (MsgInfo *info, GSList *list, gboolean spam)
2396 GSList *cur = spam_learners;
2398 for (; cur; cur = cur->next) {
2399 int ((*func)(MsgInfo *info, GSList *list, gboolean spam)) = cur->data;
2400 ret |= func(info, list, spam);
2405 static gchar *spam_folder_item = NULL;
2406 static FolderItem * (*procmsg_spam_get_folder_func)(MsgInfo *msginfo) = NULL;
2407 void procmsg_spam_set_folder (const char *item_identifier, FolderItem *(*spam_get_folder_func)(MsgInfo *info))
2409 g_free(spam_folder_item);
2410 if (item_identifier)
2411 spam_folder_item = g_strdup(item_identifier);
2413 spam_folder_item = NULL;
2414 if (spam_get_folder_func != NULL)
2415 procmsg_spam_get_folder_func = spam_get_folder_func;
2417 procmsg_spam_get_folder_func = NULL;
2420 FolderItem *procmsg_spam_get_folder (MsgInfo *msginfo)
2422 FolderItem *item = NULL;
2424 if (procmsg_spam_get_folder_func)
2425 item = procmsg_spam_get_folder_func(msginfo);
2426 if (item == NULL && spam_folder_item)
2427 item = folder_find_item_from_identifier(spam_folder_item);
2429 item = folder_get_default_trash();
2433 static void item_has_queued_mails(FolderItem *item, gpointer data)
2435 gboolean *result = (gboolean *)data;
2436 if (*result == TRUE)
2438 if (folder_has_parent_of_type(item, F_QUEUE)) {
2439 if (item->total_msgs == 0)
2442 GSList *msglist = folder_item_get_msg_list(item);
2444 for (cur = msglist; cur; cur = cur->next) {
2445 MsgInfo *msginfo = (MsgInfo *)cur->data;
2446 if (!MSG_IS_DELETED(msginfo->flags) &&
2447 !MSG_IS_LOCKED(msginfo->flags)) {
2452 procmsg_msg_list_free(msglist);
2457 gboolean procmsg_have_queued_mails_fast (void)
2459 gboolean result = FALSE;
2460 folder_func_to_all_folders(item_has_queued_mails, &result);
2464 static void item_has_trashed_mails(FolderItem *item, gpointer data)
2466 gboolean *result = (gboolean *)data;
2467 if (*result == TRUE)
2469 if (folder_has_parent_of_type(item, F_TRASH) && item->total_msgs > 0)
2473 gboolean procmsg_have_trashed_mails_fast (void)
2475 gboolean result = FALSE;
2476 folder_func_to_all_folders(item_has_trashed_mails, &result);
2480 gchar *procmsg_msginfo_get_tags_str(MsgInfo *msginfo)
2488 if (msginfo->tags == NULL)
2490 for (cur = msginfo->tags; cur; cur = cur->next) {
2491 const gchar *tag = tags_get_tag(GPOINTER_TO_INT(cur->data));
2495 tags = g_strdup(tag);
2497 int olen = strlen(tags);
2498 int nlen = olen + strlen(tag) + 2 /* strlen(", ") */;
2499 tags = g_realloc(tags, nlen+1);
2502 strcpy(tags+olen, ", ");
2503 strcpy(tags+olen+2, tag);
2510 void procmsg_msginfo_update_tags(MsgInfo *msginfo, gboolean set, gint id)
2518 msginfo->tags = g_slist_remove(
2520 GINT_TO_POINTER(id));
2521 changed.data = GINT_TO_POINTER(id);
2522 changed.next = NULL;
2523 folder_item_commit_tags(msginfo->folder, msginfo, NULL, &changed);
2525 if (!g_slist_find(msginfo->tags, GINT_TO_POINTER(id))) {
2526 msginfo->tags = g_slist_append(
2528 GINT_TO_POINTER(id));
2530 changed.data = GINT_TO_POINTER(id);
2531 changed.next = NULL;
2532 folder_item_commit_tags(msginfo->folder, msginfo, &changed, NULL);
2537 void procmsg_msginfo_clear_tags(MsgInfo *msginfo)
2539 GSList *unset = msginfo->tags;
2540 msginfo->tags = NULL;
2541 folder_item_commit_tags(msginfo->folder, msginfo, NULL, unset);
2542 g_slist_free(unset);