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"
52 #include "file-utils.h"
54 extern SessionStats session_stats;
56 static gint procmsg_send_message_queue_full(const gchar *file, gboolean keep_session, gchar **errstr,
57 FolderItem *queue, gint msgnum, gboolean *queued_removed);
58 static void procmsg_update_unread_children (MsgInfo *info,
59 gboolean newly_marked);
66 Q_MAIL_ACCOUNT_ID = 4,
67 Q_NEWS_ACCOUNT_ID = 5,
68 Q_SAVE_COPY_FOLDER = 6,
69 Q_REPLY_MESSAGE_ID = 7,
75 Q_PRIVACY_SYSTEM_OLD = 13,
77 Q_ENCRYPT_DATA_OLD = 15,
78 Q_CLAWS_HDRS_OLD = 16,
81 void procmsg_msg_list_free(GSList *mlist)
86 for (cur = mlist; cur != NULL; cur = cur->next) {
87 msginfo = (MsgInfo *)cur->data;
88 procmsg_msginfo_free(&msginfo);
93 MsgNumberList *procmsg_get_number_list_for_msgs(MsgInfoList *msglist)
98 for (cur = msglist; cur; cur = cur->next) {
99 MsgInfo *msg = (MsgInfo *)cur->data;
100 nums = g_slist_prepend(nums, GUINT_TO_POINTER(msg->msgnum));
103 return g_slist_reverse(nums);
115 /* CLAWS subject threading:
117 in the first round it inserts subject lines in a
118 hashtable (subject <-> node)
120 the second round finishes the threads by attaching
121 matching subject lines to the one found in the
122 hashtable. will use the oldest node with the same
123 subject that is not more then thread_by_subject_max_age
124 days old (see subject_hashtable_lookup)
127 static void subject_hashtable_insert(GHashTable *hashtable, GNode *node)
133 cm_return_if_fail(hashtable != NULL);
134 cm_return_if_fail(node != NULL);
135 msginfo = (MsgInfo *) node->data;
136 cm_return_if_fail(msginfo != NULL);
138 subject = msginfo->subject;
142 subject += subject_get_prefix_length(subject);
144 list = g_hash_table_lookup(hashtable, subject);
145 list = g_slist_prepend(list, node);
146 g_hash_table_insert(hashtable, subject, list);
149 static GNode *subject_hashtable_lookup(GHashTable *hashtable, MsgInfo *msginfo)
153 GNode *node = NULL, *hashtable_node = NULL;
155 MsgInfo *hashtable_msginfo = NULL, *best_msginfo = NULL;
158 cm_return_val_if_fail(hashtable != NULL, NULL);
160 subject = msginfo->subject;
163 prefix_length = subject_get_prefix_length(subject);
164 if (prefix_length <= 0)
166 subject += prefix_length;
168 list = g_hash_table_lookup(hashtable, subject);
172 /* check all nodes with the same subject to find the best parent */
173 for (cur = list; cur; cur = cur->next) {
174 hashtable_node = (GNode *)cur->data;
175 hashtable_msginfo = (MsgInfo *) hashtable_node->data;
178 /* best node should be the oldest in the found nodes */
179 /* parent node must not be older then msginfo */
180 if ((hashtable_msginfo->date_t < msginfo->date_t) &&
181 ((best_msginfo == NULL) ||
182 (best_msginfo->date_t > hashtable_msginfo->date_t)))
185 /* parent node must not be more then thread_by_subject_max_age
186 days older then msginfo */
187 if (fabs(difftime(msginfo->date_t, hashtable_msginfo->date_t)) >
188 prefs_common.thread_by_subject_max_age * 3600 * 24)
191 /* can add new tests for all matching
192 nodes found by subject */
195 node = hashtable_node;
196 best_msginfo = hashtable_msginfo;
203 static void subject_hashtable_free(gpointer key, gpointer value, gpointer data)
208 /* return the reversed thread tree */
209 GNode *procmsg_get_thread_tree(GSList *mlist)
211 GNode *root, *parent, *node, *next;
212 GHashTable *msgid_table;
213 GHashTable *subject_hashtable = NULL;
218 root = g_node_new(NULL);
219 msgid_table = g_hash_table_new(g_str_hash, g_str_equal);
221 if (prefs_common.thread_by_subject) {
222 subject_hashtable = g_hash_table_new(g_str_hash, g_str_equal);
225 for (; mlist != NULL; mlist = mlist->next) {
226 msginfo = (MsgInfo *)mlist->data;
229 if (msginfo->inreplyto) {
230 parent = g_hash_table_lookup(msgid_table, msginfo->inreplyto);
231 if (parent == NULL) {
235 node = g_node_insert_data_before
236 (parent, parent == root ? parent->children : NULL,
238 if ((msgid = msginfo->msgid) && g_hash_table_lookup(msgid_table, msgid) == NULL)
239 g_hash_table_insert(msgid_table, (gchar *)msgid, node);
241 /* CLAWS: add subject to hashtable (without prefix) */
242 if (prefs_common.thread_by_subject) {
243 subject_hashtable_insert(subject_hashtable, node);
247 /* complete the unfinished threads */
248 for (node = root->children; node != NULL; ) {
250 msginfo = (MsgInfo *)node->data;
253 if (msginfo->inreplyto)
254 parent = g_hash_table_lookup(msgid_table, msginfo->inreplyto);
256 /* try looking for the indirect parent */
257 if (!parent && msginfo->references) {
258 for (reflist = msginfo->references;
259 reflist != NULL; reflist = reflist->next)
260 if ((parent = g_hash_table_lookup
261 (msgid_table, reflist->data)) != NULL)
265 /* node should not be the parent, and node should not
266 be an ancestor of parent (circular reference) */
267 if (parent && parent != node &&
268 !g_node_is_ancestor(node, parent)) {
271 (parent, parent->children, node);
277 if (prefs_common.thread_by_subject) {
278 START_TIMING("thread by subject");
279 for (node = root->children; node && node != NULL;) {
281 msginfo = (MsgInfo *) node->data;
283 parent = subject_hashtable_lookup(subject_hashtable, msginfo);
285 /* the node may already be threaded by IN-REPLY-TO, so go up
287 find the parent node */
288 if (parent != NULL) {
289 if (g_node_is_ancestor(node, parent))
297 g_node_append(parent, node);
305 if (prefs_common.thread_by_subject)
307 g_hash_table_foreach(subject_hashtable, subject_hashtable_free, NULL);
308 g_hash_table_destroy(subject_hashtable);
311 g_hash_table_destroy(msgid_table);
316 gint procmsg_move_messages(GSList *mlist)
318 GSList *cur, *movelist = NULL;
320 FolderItem *dest = NULL;
322 gboolean finished = TRUE;
323 if (!mlist) return 0;
325 folder_item_update_freeze();
328 for (cur = mlist; cur != NULL; cur = cur->next) {
329 msginfo = (MsgInfo *)cur->data;
330 if (!msginfo->to_folder) {
336 dest = msginfo->to_folder;
337 movelist = g_slist_prepend(movelist, msginfo);
338 } else if (dest == msginfo->to_folder) {
339 movelist = g_slist_prepend(movelist, msginfo);
343 procmsg_msginfo_set_to_folder(msginfo, NULL);
346 movelist = g_slist_reverse(movelist);
347 retval |= folder_item_move_msgs(dest, movelist);
348 g_slist_free(movelist);
351 if (finished == FALSE) {
357 folder_item_update_thaw();
361 void procmsg_copy_messages(GSList *mlist)
363 GSList *cur, *copylist = NULL;
365 FolderItem *dest = NULL;
366 gboolean finished = TRUE;
369 folder_item_update_freeze();
372 for (cur = mlist; cur != NULL; cur = cur->next) {
373 msginfo = (MsgInfo *)cur->data;
374 if (!msginfo->to_folder) {
380 dest = msginfo->to_folder;
381 copylist = g_slist_prepend(copylist, msginfo);
382 } else if (dest == msginfo->to_folder) {
383 copylist = g_slist_prepend(copylist, msginfo);
387 procmsg_msginfo_set_to_folder(msginfo, NULL);
390 copylist = g_slist_reverse(copylist);
391 folder_item_copy_msgs(dest, copylist);
392 g_slist_free(copylist);
395 if (finished == FALSE) {
401 folder_item_update_thaw();
404 gchar *procmsg_get_message_file_path(MsgInfo *msginfo)
406 cm_return_val_if_fail(msginfo != NULL, NULL);
408 return folder_item_fetch_msg(msginfo->folder, msginfo->msgnum);
411 gchar *procmsg_get_message_file(MsgInfo *msginfo)
413 gchar *filename = NULL;
415 cm_return_val_if_fail(msginfo != NULL, NULL);
417 filename = folder_item_fetch_msg(msginfo->folder, msginfo->msgnum);
419 debug_print("can't fetch message %d\n", msginfo->msgnum);
424 gchar *procmsg_get_message_file_full(MsgInfo *msginfo, gboolean headers, gboolean body)
426 gchar *filename = NULL;
428 cm_return_val_if_fail(msginfo != NULL, NULL);
430 filename = folder_item_fetch_msg_full(msginfo->folder, msginfo->msgnum,
433 debug_print("can't fetch message %d\n", msginfo->msgnum);
438 GSList *procmsg_get_message_file_list(GSList *mlist)
440 GSList *file_list = NULL;
442 MsgFileInfo *fileinfo;
445 while (mlist != NULL) {
446 msginfo = (MsgInfo *)mlist->data;
447 file = procmsg_get_message_file(msginfo);
449 procmsg_message_file_list_free(file_list);
452 fileinfo = g_new(MsgFileInfo, 1);
453 fileinfo->msginfo = procmsg_msginfo_new_ref(msginfo);
454 fileinfo->file = file;
455 fileinfo->flags = g_new(MsgFlags, 1);
456 *fileinfo->flags = msginfo->flags;
457 file_list = g_slist_prepend(file_list, fileinfo);
461 file_list = g_slist_reverse(file_list);
466 void procmsg_message_file_list_free(MsgInfoList *file_list)
469 MsgFileInfo *fileinfo;
471 for (cur = file_list; cur != NULL; cur = cur->next) {
472 fileinfo = (MsgFileInfo *)cur->data;
473 procmsg_msginfo_free(&(fileinfo->msginfo));
474 g_free(fileinfo->file);
475 g_free(fileinfo->flags);
479 g_slist_free(file_list);
482 FILE *procmsg_open_message(MsgInfo *msginfo)
487 cm_return_val_if_fail(msginfo != NULL, NULL);
489 file = procmsg_get_message_file_path(msginfo);
490 cm_return_val_if_fail(file != NULL, NULL);
492 if (!is_file_exist(file)) {
494 file = procmsg_get_message_file(msginfo);
499 if ((fp = claws_fopen(file, "rb")) == NULL) {
500 FILE_OP_ERROR(file, "claws_fopen");
507 if (MSG_IS_QUEUED(msginfo->flags) || MSG_IS_DRAFT(msginfo->flags)) {
510 while (claws_fgets(buf, sizeof(buf), fp) != NULL) {
512 if ((!strncmp(buf, "X-Claws-End-Special-Headers: 1",
513 strlen("X-Claws-End-Special-Headers:"))) ||
514 (!strncmp(buf, "X-Sylpheed-End-Special-Headers: 1",
515 strlen("X-Sylpheed-End-Special-Headers:"))))
518 if (buf[0] == '\r' || buf[0] == '\n') break;
519 /* from other mailers */
520 if (!strncmp(buf, "Date: ", 6)
521 || !strncmp(buf, "To: ", 4)
522 || !strncmp(buf, "From: ", 6)
523 || !strncmp(buf, "Subject: ", 9)) {
533 gboolean procmsg_msg_exist(MsgInfo *msginfo)
537 if (!msginfo) return FALSE;
539 ret = !folder_item_is_msg_changed(msginfo->folder, msginfo);
544 void procmsg_get_filter_keyword(MsgInfo *msginfo, gchar **header, gchar **key,
545 PrefsFilterType type)
547 static HeaderEntry hentry[] = {{"X-BeenThere:", NULL, TRUE},
548 {"X-ML-Name:", NULL, TRUE},
549 {"X-List:", NULL, TRUE},
550 {"X-Mailing-list:", NULL, TRUE},
551 {"List-Id:", NULL, TRUE},
552 {"X-Sequence:", NULL, TRUE},
553 {"Sender:", NULL, TRUE},
554 {"List-Post:", NULL, TRUE},
555 {NULL, NULL, FALSE}};
561 H_X_MAILING_LIST = 3,
570 cm_return_if_fail(msginfo != NULL);
571 cm_return_if_fail(header != NULL);
572 cm_return_if_fail(key != NULL);
581 if ((fp = procmsg_open_message(msginfo)) == NULL)
583 procheader_get_header_fields(fp, hentry);
586 #define SET_FILTER_KEY(hstr, idx) \
588 *header = g_strdup(hstr); \
589 *key = hentry[idx].body; \
590 hentry[idx].body = NULL; \
593 if (hentry[H_LIST_ID].body != NULL) {
594 SET_FILTER_KEY("header \"List-Id\"", H_LIST_ID);
595 extract_list_id_str(*key);
596 } else if (hentry[H_X_BEENTHERE].body != NULL) {
597 SET_FILTER_KEY("header \"X-BeenThere\"", H_X_BEENTHERE);
598 } else if (hentry[H_X_ML_NAME].body != NULL) {
599 SET_FILTER_KEY("header \"X-ML-Name\"", H_X_ML_NAME);
600 } else if (hentry[H_X_LIST].body != NULL) {
601 SET_FILTER_KEY("header \"X-List\"", H_X_LIST);
602 } else if (hentry[H_X_MAILING_LIST].body != NULL) {
603 SET_FILTER_KEY("header \"X-Mailing-List\"", H_X_MAILING_LIST);
604 } else if (hentry[H_X_SEQUENCE].body != NULL) {
607 SET_FILTER_KEY("X-Sequence", H_X_SEQUENCE);
610 while (*p != '\0' && !g_ascii_isspace(*p)) p++;
611 while (g_ascii_isspace(*p)) p++;
612 if (g_ascii_isdigit(*p)) {
618 } else if (hentry[H_SENDER].body != NULL) {
619 SET_FILTER_KEY("header \"Sender\"", H_SENDER);
620 } else if (hentry[H_LIST_POST].body != NULL) {
621 SET_FILTER_KEY("header \"List-Post\"", H_LIST_POST);
622 } else if (msginfo->to) {
623 *header = g_strdup("to");
624 *key = g_strdup(msginfo->to);
625 } else if (msginfo->subject) {
626 *header = g_strdup("subject");
627 *key = g_strdup(msginfo->subject);
630 #undef SET_FILTER_KEY
632 g_free(hentry[H_X_BEENTHERE].body);
633 hentry[H_X_BEENTHERE].body = NULL;
634 g_free(hentry[H_X_ML_NAME].body);
635 hentry[H_X_ML_NAME].body = NULL;
636 g_free(hentry[H_X_LIST].body);
637 hentry[H_X_LIST].body = NULL;
638 g_free(hentry[H_X_MAILING_LIST].body);
639 hentry[H_X_MAILING_LIST].body = NULL;
640 g_free(hentry[H_LIST_ID].body);
641 hentry[H_LIST_ID].body = NULL;
642 g_free(hentry[H_SENDER].body);
643 hentry[H_SENDER].body = NULL;
644 g_free(hentry[H_LIST_POST].body);
645 hentry[H_LIST_POST].body = NULL;
649 *header = g_strdup("from");
650 *key = g_strdup(msginfo->from);
653 *header = g_strdup("to");
654 *key = g_strdup(msginfo->to);
656 case FILTER_BY_SUBJECT:
657 *header = g_strdup("subject");
658 *key = g_strdup(msginfo->subject);
665 static void procmsg_empty_trash(FolderItem *trash)
670 (trash->stype != F_TRASH &&
671 !folder_has_parent_of_type(trash, F_TRASH)))
674 if (trash && trash->total_msgs > 0) {
675 GSList *mlist = folder_item_get_msg_list(trash);
677 for (cur = mlist ; cur != NULL ; cur = cur->next) {
678 MsgInfo * msginfo = (MsgInfo *) cur->data;
679 if (MSG_IS_LOCKED(msginfo->flags)) {
680 procmsg_msginfo_free(&msginfo);
683 if (msginfo->total_size != 0 &&
684 msginfo->size != (off_t)msginfo->total_size)
685 partial_mark_for_delete(msginfo);
687 procmsg_msginfo_free(&msginfo);
690 folder_item_remove_all_msg(trash);
693 if (!trash->node || !trash->node->children)
696 node = trash->node->children;
697 while (node != NULL) {
699 procmsg_empty_trash(FOLDER_ITEM(node->data));
704 void procmsg_empty_all_trash(void)
709 for (cur = folder_get_list(); cur != NULL; cur = cur->next) {
710 Folder *folder = FOLDER(cur->data);
711 trash = folder->trash;
712 procmsg_empty_trash(trash);
713 if (folder->account && folder->account->set_trash_folder &&
714 folder_find_item_from_identifier(folder->account->trash_folder))
716 folder_find_item_from_identifier(folder->account->trash_folder));
720 static PrefsAccount *procmsg_get_account_from_file(const gchar *file)
722 PrefsAccount *mailac = NULL;
726 static HeaderEntry qentry[] = {{"S:", NULL, FALSE},
727 {"SSV:", NULL, FALSE},
729 {"NG:", NULL, FALSE},
730 {"MAID:", NULL, FALSE},
731 {"NAID:", NULL, FALSE},
732 {"SCF:", NULL, FALSE},
733 {"RMID:", NULL, FALSE},
734 {"FMID:", NULL, FALSE},
735 {"X-Claws-Privacy-System:", NULL, FALSE},
736 {"X-Claws-Encrypt:", NULL, FALSE},
737 {"X-Claws-Encrypt-Data:", NULL, FALSE},
738 {"X-Claws-End-Special-Headers", NULL, FALSE},
739 {"X-Sylpheed-Privacy-System:", NULL, FALSE},
740 {"X-Sylpheed-Encrypt:", NULL, FALSE},
741 {"X-Sylpheed-Encrypt-Data:", NULL, FALSE},
742 {NULL, NULL, FALSE}};
744 cm_return_val_if_fail(file != NULL, NULL);
746 if ((fp = claws_fopen(file, "rb")) == NULL) {
747 FILE_OP_ERROR(file, "claws_fopen");
751 while ((hnum = procheader_get_one_field(&buf, fp, qentry)) != -1 && buf != NULL) {
752 gchar *p = buf + strlen(qentry[hnum].name);
754 if (hnum == Q_MAIL_ACCOUNT_ID) {
755 mailac = account_find_from_id(atoi(p));
765 gchar *procmsg_msginfo_get_avatar(MsgInfo *msginfo, gint type)
769 if (!msginfo || !msginfo->extradata || !msginfo->extradata->avatars)
772 for (mia = msginfo->extradata->avatars; mia; mia = mia->next) {
773 MsgInfoAvatar *avatar = (MsgInfoAvatar *)mia->data;
774 if (avatar->avatar_id == type)
775 return avatar->avatar_src;
781 void procmsg_msginfo_add_avatar(MsgInfo *msginfo, gint type, const gchar *data)
785 if (!msginfo->extradata)
786 msginfo->extradata = g_new0(MsgInfoExtraData, 1);
788 av = g_new0(MsgInfoAvatar, 1);
789 av->avatar_id = type;
790 av->avatar_src = g_strdup(data);
792 msginfo->extradata->avatars = g_slist_append(msginfo->extradata->avatars, av);
795 gchar *procmsg_msginfo_get_identifier(MsgInfo *msginfo)
801 cm_return_val_if_fail(msginfo != NULL, NULL);
802 folder_id = folder_item_get_identifier(msginfo->folder);
803 msgid = msginfo->msgid;
805 id = g_strconcat(folder_id, G_DIR_SEPARATOR_S, msgid, NULL);
812 MsgInfo *procmsg_get_msginfo_from_identifier(const gchar *id)
814 gchar *folder_id = g_strdup(id);
815 gchar *separator = strrchr(folder_id, G_DIR_SEPARATOR);
820 if (separator == NULL) {
826 msgid = separator + 1;
828 item = folder_find_item_from_identifier(folder_id);
835 msginfo = folder_item_get_msginfo_by_msgid(item, msgid);
841 static GSList *procmsg_list_sort_by_account(FolderItem *queue, GSList *list)
843 GSList *result = NULL;
845 PrefsAccount *last_account = NULL;
848 gboolean nothing_to_sort = TRUE;
853 orig = g_slist_copy(list);
855 msg = (MsgInfo *)orig->data;
857 for (cur = orig; cur; cur = cur->next)
858 debug_print("sort before %s\n", ((MsgInfo *)cur->data)->from);
863 nothing_to_sort = TRUE;
867 PrefsAccount *ac = NULL;
868 msg = (MsgInfo *)cur->data;
869 file = folder_item_fetch_msg(queue, msg->msgnum);
870 ac = procmsg_get_account_from_file(file);
873 if (last_account == NULL || (ac != NULL && ac == last_account)) {
874 result = g_slist_append(result, msg);
875 orig = g_slist_remove(orig, msg);
877 nothing_to_sort = FALSE;
883 if (orig && g_slist_length(orig)) {
884 if (!last_account && nothing_to_sort) {
885 /* can't find an account for the rest of the list */
888 result = g_slist_append(result, cur->data);
899 for (cur = result; cur; cur = cur->next)
900 debug_print("sort after %s\n", ((MsgInfo *)cur->data)->from);
907 static gboolean procmsg_is_last_for_account(FolderItem *queue, MsgInfo *msginfo, GSList *elem)
909 gchar *file = folder_item_fetch_msg(queue, msginfo->msgnum);
910 PrefsAccount *ac = procmsg_get_account_from_file(file);
913 for (cur = elem; cur; cur = cur->next) {
914 MsgInfo *cur_msginfo = (MsgInfo *)cur->data;
915 file = folder_item_fetch_msg(queue, cur_msginfo->msgnum);
917 if (cur_msginfo != msginfo && !MSG_IS_LOCKED(cur_msginfo->flags)) {
918 if (procmsg_get_account_from_file(file) == ac) {
929 static gboolean send_queue_lock = FALSE;
931 gboolean procmsg_queue_lock(char **errstr)
933 if (send_queue_lock) {
934 /* Avoid having to translate two similar strings */
935 log_warning(LOG_PROTOCOL, "%s\n", _("Already trying to send."));
937 if (*errstr) g_free(*errstr);
938 *errstr = g_strdup_printf(_("Already trying to send."));
942 send_queue_lock = TRUE;
945 void procmsg_queue_unlock(void)
947 send_queue_lock = FALSE;
950 *\brief Send messages in queue
952 *\param queue Queue folder to process
953 *\param save_msgs Unused
955 *\return Number of messages sent, negative if an error occurred
956 * positive if no error occurred
958 gint procmsg_send_queue(FolderItem *queue, gboolean save_msgs, gchar **errstr)
960 gint sent = 0, err = 0;
962 GSList *sorted_list = NULL;
965 if (!procmsg_queue_lock(errstr)) {
966 main_window_set_menu_sensitive(mainwindow_get_mainwindow());
967 toolbar_main_set_sensitive(mainwindow_get_mainwindow());
972 queue = folder_get_default_queue();
975 procmsg_queue_unlock();
980 main_window_set_menu_sensitive(mainwindow_get_mainwindow());
981 toolbar_main_set_sensitive(mainwindow_get_mainwindow());
983 folder_item_scan(queue);
984 list = folder_item_get_msg_list(queue);
986 /* sort the list per sender account; this helps reusing the same SMTP server */
987 sorted_list = procmsg_list_sort_by_account(queue, list);
989 for (elem = sorted_list; elem != NULL; elem = elem->next) {
993 msginfo = (MsgInfo *)(elem->data);
994 if (!MSG_IS_LOCKED(msginfo->flags) && !MSG_IS_DELETED(msginfo->flags)) {
995 file = folder_item_fetch_msg(queue, msginfo->msgnum);
997 gboolean queued_removed = FALSE;
998 if (procmsg_send_message_queue_full(file,
999 !procmsg_is_last_for_account(queue, msginfo, elem),
1000 errstr, queue, msginfo->msgnum, &queued_removed) < 0) {
1001 g_warning("Sending queued message %d failed.",
1006 if (!queued_removed)
1007 folder_item_remove_msg(queue, msginfo->msgnum);
1012 /* FIXME: supposedly if only one message is locked, and queue
1013 * is being flushed, the following free says something like
1014 * "freeing msg ## in folder (nil)". */
1015 procmsg_msginfo_free(&msginfo);
1018 g_slist_free(sorted_list);
1019 folder_item_scan(queue);
1021 if (queue->node && queue->node->children) {
1022 node = queue->node->children;
1023 while (node != NULL) {
1026 send_queue_lock = FALSE;
1027 res = procmsg_send_queue(FOLDER_ITEM(node->data), save_msgs, errstr);
1028 send_queue_lock = TRUE;
1036 procmsg_queue_unlock();
1038 main_window_set_menu_sensitive(mainwindow_get_mainwindow());
1039 toolbar_main_set_sensitive(mainwindow_get_mainwindow());
1041 return (err != 0 ? -err : sent);
1044 gboolean procmsg_is_sending(void)
1046 return send_queue_lock;
1050 *\brief Determine if a queue folder is empty
1052 *\param queue Queue folder to process
1054 *\return TRUE if the queue folder is empty, otherwise return FALSE
1056 gboolean procmsg_queue_is_empty(FolderItem *queue)
1059 gboolean res = FALSE;
1061 queue = folder_get_default_queue();
1062 cm_return_val_if_fail(queue != NULL, TRUE);
1064 folder_item_scan(queue);
1065 list = folder_item_get_msg_list(queue);
1066 res = (list == NULL);
1067 procmsg_msg_list_free(list);
1071 if (queue->node && queue->node->children) {
1072 node = queue->node->children;
1073 while (node != NULL) {
1075 if (!procmsg_queue_is_empty(FOLDER_ITEM(node->data)))
1084 gint procmsg_remove_special_headers(const gchar *in, const gchar *out)
1087 gchar buf[BUFFSIZE];
1089 if ((fp = claws_fopen(in, "rb")) == NULL) {
1090 FILE_OP_ERROR(in, "claws_fopen");
1093 if ((outfp = claws_fopen(out, "wb")) == NULL) {
1094 FILE_OP_ERROR(out, "claws_fopen");
1098 while (claws_fgets(buf, sizeof(buf), fp) != NULL) {
1100 if ((!strncmp(buf, "X-Claws-End-Special-Headers: 1",
1101 strlen("X-Claws-End-Special-Headers:"))) ||
1102 (!strncmp(buf, "X-Sylpheed-End-Special-Headers: 1",
1103 strlen("X-Sylpheed-End-Special-Headers:"))))
1106 if (buf[0] == '\r' || buf[0] == '\n') break;
1107 /* from other mailers */
1108 if (!strncmp(buf, "Date: ", 6)
1109 || !strncmp(buf, "To: ", 4)
1110 || !strncmp(buf, "From: ", 6)
1111 || !strncmp(buf, "Subject: ", 9)) {
1116 while (claws_fgets(buf, sizeof(buf), fp) != NULL) {
1117 if (claws_fputs(buf, outfp) == EOF) {
1118 FILE_OP_ERROR(out, "claws_fputs");
1119 claws_fclose(outfp);
1124 claws_safe_fclose(outfp);
1129 gint procmsg_save_to_outbox(FolderItem *outbox, const gchar *file,
1133 MsgInfo *msginfo, *tmp_msginfo;
1134 MsgFlags flag = {0, 0};
1135 gchar *outbox_path = NULL;
1138 debug_print("using default outbox\n");
1139 outbox = folder_get_default_outbox();
1142 cm_return_val_if_fail(outbox != NULL, -1);
1144 outbox_path = folder_item_get_path(outbox);
1145 debug_print("saving sent message to %s...\n", outbox_path);
1146 g_free(outbox_path);
1148 /* remove queueing headers */
1150 gchar tmp[MAXPATHLEN + 1];
1152 g_snprintf(tmp, sizeof(tmp), "%s%ctmpmsg.out.%08x",
1153 get_rc_dir(), G_DIR_SEPARATOR, (guint) rand());
1155 if (procmsg_remove_special_headers(file, tmp) !=0)
1158 folder_item_scan(outbox);
1159 if ((num = folder_item_add_msg(outbox, tmp, &flag, TRUE)) < 0) {
1160 g_warning("can't save message");
1165 folder_item_scan(outbox);
1166 if ((num = folder_item_add_msg
1167 (outbox, file, &flag, FALSE)) < 0) {
1168 g_warning("can't save message");
1172 msginfo = folder_item_get_msginfo(outbox, num); /* refcnt++ */
1173 tmp_msginfo = procmsg_msginfo_get_full_info(msginfo); /* refcnt++ */
1174 if (msginfo != NULL) {
1175 procmsg_msginfo_unset_flags(msginfo, ~0, 0);
1176 procmsg_msginfo_free(&msginfo); /* refcnt-- */
1177 /* tmp_msginfo == msginfo */
1178 if (tmp_msginfo && msginfo->extradata &&
1179 (msginfo->extradata->dispositionnotificationto ||
1180 msginfo->extradata->returnreceiptto)) {
1181 procmsg_msginfo_set_flags(msginfo, MSG_RETRCPT_SENT, 0);
1183 procmsg_msginfo_free(&tmp_msginfo); /* refcnt-- */
1190 MsgInfo *procmsg_msginfo_new_ref(MsgInfo *msginfo)
1197 MsgInfo *procmsg_msginfo_new(void)
1199 MsgInfo *newmsginfo;
1201 newmsginfo = g_new0(MsgInfo, 1);
1202 newmsginfo->refcnt = 1;
1207 static MsgInfoAvatar *procmsg_msginfoavatar_copy(MsgInfoAvatar *avatar)
1209 MsgInfoAvatar *newavatar;
1211 if (avatar == NULL) return NULL;
1213 newavatar = g_new0(MsgInfoAvatar, 1);
1214 newavatar->avatar_id = avatar->avatar_id;
1215 newavatar->avatar_src = g_strdup(avatar->avatar_src);
1220 static void procmsg_msginfoavatar_free(MsgInfoAvatar *avatar)
1222 if (avatar != NULL) {
1223 if (avatar->avatar_src != NULL)
1224 g_free(avatar->avatar_src);
1229 MsgInfo *procmsg_msginfo_copy(MsgInfo *msginfo)
1231 MsgInfo *newmsginfo;
1234 if (msginfo == NULL) return NULL;
1236 newmsginfo = g_new0(MsgInfo, 1);
1238 newmsginfo->refcnt = 1;
1240 #define MEMBCOPY(mmb) newmsginfo->mmb = msginfo->mmb
1241 #define MEMBDUP(mmb) newmsginfo->mmb = msginfo->mmb ? \
1242 g_strdup(msginfo->mmb) : NULL
1257 MEMBDUP(newsgroups);
1264 MEMBCOPY(to_folder);
1266 if (msginfo->extradata) {
1267 newmsginfo->extradata = g_new0(MsgInfoExtraData, 1);
1268 if (msginfo->extradata->avatars) {
1269 newmsginfo->extradata->avatars = slist_copy_deep(msginfo->extradata->avatars,
1270 (GCopyFunc) procmsg_msginfoavatar_copy);
1272 MEMBDUP(extradata->dispositionnotificationto);
1273 MEMBDUP(extradata->returnreceiptto);
1274 MEMBDUP(extradata->partial_recv);
1275 MEMBDUP(extradata->account_server);
1276 MEMBDUP(extradata->account_login);
1277 MEMBDUP(extradata->list_post);
1278 MEMBDUP(extradata->list_subscribe);
1279 MEMBDUP(extradata->list_unsubscribe);
1280 MEMBDUP(extradata->list_help);
1281 MEMBDUP(extradata->list_archive);
1282 MEMBDUP(extradata->list_owner);
1283 MEMBDUP(extradata->resent_from);
1286 refs = msginfo->references;
1287 for (refs = msginfo->references; refs != NULL; refs = refs->next) {
1288 newmsginfo->references = g_slist_prepend
1289 (newmsginfo->references, g_strdup(refs->data));
1291 newmsginfo->references = g_slist_reverse(newmsginfo->references);
1294 MEMBDUP(plaintext_file);
1299 MsgInfo *procmsg_msginfo_get_full_info_from_file(MsgInfo *msginfo, const gchar *file)
1301 MsgInfo *full_msginfo;
1303 if (msginfo == NULL) return NULL;
1305 if (!file || !is_file_exist(file)) {
1306 g_warning("procmsg_msginfo_get_full_info_from_file(): can't get message file.");
1310 full_msginfo = procheader_parse_file(file, msginfo->flags, TRUE, FALSE);
1311 if (!full_msginfo) return NULL;
1313 msginfo->total_size = full_msginfo->total_size;
1314 msginfo->planned_download = full_msginfo->planned_download;
1316 if (full_msginfo->extradata) {
1317 if (!msginfo->extradata)
1318 msginfo->extradata = g_new0(MsgInfoExtraData, 1);
1319 if (!msginfo->extradata->list_post)
1320 msginfo->extradata->list_post = g_strdup(full_msginfo->extradata->list_post);
1321 if (!msginfo->extradata->list_subscribe)
1322 msginfo->extradata->list_subscribe = g_strdup(full_msginfo->extradata->list_subscribe);
1323 if (!msginfo->extradata->list_unsubscribe)
1324 msginfo->extradata->list_unsubscribe = g_strdup(full_msginfo->extradata->list_unsubscribe);
1325 if (!msginfo->extradata->list_help)
1326 msginfo->extradata->list_help = g_strdup(full_msginfo->extradata->list_help);
1327 if (!msginfo->extradata->list_archive)
1328 msginfo->extradata->list_archive= g_strdup(full_msginfo->extradata->list_archive);
1329 if (!msginfo->extradata->list_owner)
1330 msginfo->extradata->list_owner = g_strdup(full_msginfo->extradata->list_owner);
1331 if (!msginfo->extradata->avatars)
1332 msginfo->extradata->avatars = slist_copy_deep(full_msginfo->extradata->avatars,
1333 (GCopyFunc) procmsg_msginfoavatar_copy);
1334 if (!msginfo->extradata->dispositionnotificationto)
1335 msginfo->extradata->dispositionnotificationto =
1336 g_strdup(full_msginfo->extradata->dispositionnotificationto);
1337 if (!msginfo->extradata->returnreceiptto)
1338 msginfo->extradata->returnreceiptto = g_strdup
1339 (full_msginfo->extradata->returnreceiptto);
1340 if (!msginfo->extradata->partial_recv && full_msginfo->extradata->partial_recv)
1341 msginfo->extradata->partial_recv = g_strdup
1342 (full_msginfo->extradata->partial_recv);
1343 if (!msginfo->extradata->account_server && full_msginfo->extradata->account_server)
1344 msginfo->extradata->account_server = g_strdup
1345 (full_msginfo->extradata->account_server);
1346 if (!msginfo->extradata->account_login && full_msginfo->extradata->account_login)
1347 msginfo->extradata->account_login = g_strdup
1348 (full_msginfo->extradata->account_login);
1349 if (!msginfo->extradata->resent_from && full_msginfo->extradata->resent_from)
1350 msginfo->extradata->resent_from = g_strdup
1351 (full_msginfo->extradata->resent_from);
1353 procmsg_msginfo_free(&full_msginfo);
1355 return procmsg_msginfo_new_ref(msginfo);
1358 MsgInfo *procmsg_msginfo_get_full_info(MsgInfo *msginfo)
1360 MsgInfo *full_msginfo;
1363 if (msginfo == NULL) return NULL;
1365 file = procmsg_get_message_file_path(msginfo);
1366 if (!file || !is_file_exist(file)) {
1368 file = procmsg_get_message_file(msginfo);
1370 if (!file || !is_file_exist(file)) {
1371 g_warning("procmsg_msginfo_get_full_info(): can't get message file.");
1375 full_msginfo = procmsg_msginfo_get_full_info_from_file(msginfo, file);
1377 return full_msginfo;
1380 #define FREENULL(n) { g_free(n); n = NULL; }
1381 void procmsg_msginfo_free(MsgInfo **msginfo_ptr)
1383 MsgInfo *msginfo = *msginfo_ptr;
1385 if (msginfo == NULL) return;
1388 if (msginfo->refcnt > 0)
1391 if (msginfo->to_folder) {
1392 msginfo->to_folder->op_count--;
1393 folder_item_update(msginfo->to_folder, F_ITEM_UPDATE_MSGCNT);
1396 FREENULL(msginfo->fromspace);
1398 FREENULL(msginfo->fromname);
1400 FREENULL(msginfo->date);
1401 FREENULL(msginfo->from);
1402 FREENULL(msginfo->to);
1403 FREENULL(msginfo->cc);
1404 FREENULL(msginfo->newsgroups);
1405 FREENULL(msginfo->subject);
1406 FREENULL(msginfo->msgid);
1407 FREENULL(msginfo->inreplyto);
1408 FREENULL(msginfo->xref);
1410 if (msginfo->extradata) {
1411 if (msginfo->extradata->avatars) {
1412 g_slist_foreach(msginfo->extradata->avatars,
1413 (GFunc)procmsg_msginfoavatar_free,
1415 g_slist_free(msginfo->extradata->avatars);
1416 msginfo->extradata->avatars = NULL;
1418 FREENULL(msginfo->extradata->returnreceiptto);
1419 FREENULL(msginfo->extradata->dispositionnotificationto);
1420 FREENULL(msginfo->extradata->list_post);
1421 FREENULL(msginfo->extradata->list_subscribe);
1422 FREENULL(msginfo->extradata->list_unsubscribe);
1423 FREENULL(msginfo->extradata->list_help);
1424 FREENULL(msginfo->extradata->list_archive);
1425 FREENULL(msginfo->extradata->list_owner);
1426 FREENULL(msginfo->extradata->partial_recv);
1427 FREENULL(msginfo->extradata->account_server);
1428 FREENULL(msginfo->extradata->account_login);
1429 FREENULL(msginfo->extradata->resent_from);
1430 FREENULL(msginfo->extradata);
1432 slist_free_strings_full(msginfo->references);
1433 msginfo->references = NULL;
1434 g_slist_free(msginfo->tags);
1435 msginfo->tags = NULL;
1437 FREENULL(msginfo->plaintext_file);
1440 *msginfo_ptr = NULL;
1444 guint procmsg_msginfo_memusage(MsgInfo *msginfo)
1449 memusage += sizeof(MsgInfo);
1450 if (msginfo->fromname)
1451 memusage += strlen(msginfo->fromname);
1453 memusage += strlen(msginfo->date);
1455 memusage += strlen(msginfo->from);
1457 memusage += strlen(msginfo->to);
1459 memusage += strlen(msginfo->cc);
1460 if (msginfo->newsgroups)
1461 memusage += strlen(msginfo->newsgroups);
1462 if (msginfo->subject)
1463 memusage += strlen(msginfo->subject);
1465 memusage += strlen(msginfo->msgid);
1466 if (msginfo->inreplyto)
1467 memusage += strlen(msginfo->inreplyto);
1469 for (tmp = msginfo->references; tmp; tmp=tmp->next) {
1470 gchar *r = (gchar *)tmp->data;
1471 memusage += r?strlen(r):0 + sizeof(GSList);
1473 if (msginfo->fromspace)
1474 memusage += strlen(msginfo->fromspace);
1476 for (tmp = msginfo->tags; tmp; tmp=tmp->next) {
1477 memusage += sizeof(GSList);
1479 if (msginfo->extradata) {
1480 memusage += sizeof(MsgInfoExtraData);
1481 if (msginfo->extradata->avatars) {
1482 for (tmp = msginfo->extradata->avatars; tmp; tmp = tmp->next) {
1483 MsgInfoAvatar *avt = (MsgInfoAvatar *)tmp->data;
1484 memusage += (avt->avatar_src)? strlen(avt->avatar_src): 0;
1485 memusage += sizeof(MsgInfoAvatar) + sizeof(GSList);
1488 if (msginfo->extradata->dispositionnotificationto)
1489 memusage += strlen(msginfo->extradata->dispositionnotificationto);
1490 if (msginfo->extradata->returnreceiptto)
1491 memusage += strlen(msginfo->extradata->returnreceiptto);
1493 if (msginfo->extradata->partial_recv)
1494 memusage += strlen(msginfo->extradata->partial_recv);
1495 if (msginfo->extradata->account_server)
1496 memusage += strlen(msginfo->extradata->account_server);
1497 if (msginfo->extradata->account_login)
1498 memusage += strlen(msginfo->extradata->account_login);
1499 if (msginfo->extradata->resent_from)
1500 memusage += strlen(msginfo->extradata->resent_from);
1502 if (msginfo->extradata->list_post)
1503 memusage += strlen(msginfo->extradata->list_post);
1504 if (msginfo->extradata->list_subscribe)
1505 memusage += strlen(msginfo->extradata->list_subscribe);
1506 if (msginfo->extradata->list_unsubscribe)
1507 memusage += strlen(msginfo->extradata->list_unsubscribe);
1508 if (msginfo->extradata->list_help)
1509 memusage += strlen(msginfo->extradata->list_help);
1510 if (msginfo->extradata->list_archive)
1511 memusage += strlen(msginfo->extradata->list_archive);
1512 if (msginfo->extradata->list_owner)
1513 memusage += strlen(msginfo->extradata->list_owner);
1518 static gint procmsg_send_message_queue_full(const gchar *file, gboolean keep_session, gchar **errstr,
1519 FolderItem *queue, gint msgnum, gboolean *queued_removed)
1521 static HeaderEntry qentry[] = {
1522 {"S:", NULL, FALSE}, /* 0 */
1523 {"SSV:", NULL, FALSE},
1524 {"R:", NULL, FALSE},
1525 {"NG:", NULL, FALSE},
1526 {"MAID:", NULL, FALSE},
1527 {"NAID:", NULL, FALSE}, /* 5 */
1528 {"SCF:", NULL, FALSE},
1529 {"RMID:", NULL, FALSE},
1530 {"FMID:", NULL, FALSE},
1531 {"X-Claws-Privacy-System:", NULL, FALSE},
1532 {"X-Claws-Encrypt:", NULL, FALSE}, /* 10 */
1533 {"X-Claws-Encrypt-Data:", NULL, FALSE},
1534 {"X-Claws-End-Special-Headers:", NULL, FALSE},
1535 {"X-Sylpheed-Privacy-System:", NULL, FALSE},
1536 {"X-Sylpheed-Encrypt:", NULL, FALSE},
1537 {"X-Sylpheed-Encrypt-Data:", NULL, FALSE}, /* 15 */
1538 {"X-Sylpheed-End-Special-Headers:", NULL, FALSE},
1539 {NULL, NULL, FALSE}};
1542 gint mailval = 0, newsval = 0;
1544 gchar *smtpserver = NULL;
1545 GSList *to_list = NULL;
1546 GSList *newsgroup_list = NULL;
1547 gchar *savecopyfolder = NULL;
1548 gchar *replymessageid = NULL;
1549 gchar *fwdmessageid = NULL;
1552 PrefsAccount *mailac = NULL, *newsac = NULL;
1553 gboolean encrypt = FALSE;
1556 cm_return_val_if_fail(file != NULL, -1);
1558 if ((fp = claws_fopen(file, "rb")) == NULL) {
1559 FILE_OP_ERROR(file, "claws_fopen");
1561 if (*errstr) g_free(*errstr);
1562 *errstr = g_strdup_printf(_("Couldn't open file %s."), file);
1567 while ((hnum = procheader_get_one_field(&buf, fp, qentry)) != -1 && buf != NULL) {
1568 gchar *p = buf + strlen(qentry[hnum].name);
1576 if (smtpserver == NULL)
1577 smtpserver = g_strdup(p);
1580 to_list = address_list_append(to_list, p);
1583 newsgroup_list = newsgroup_list_append(newsgroup_list, p);
1585 case Q_MAIL_ACCOUNT_ID:
1586 mailac = account_find_from_id(atoi(p));
1588 case Q_NEWS_ACCOUNT_ID:
1589 newsac = account_find_from_id(atoi(p));
1591 case Q_SAVE_COPY_FOLDER:
1592 if (savecopyfolder == NULL)
1593 savecopyfolder = g_strdup(p);
1595 case Q_REPLY_MESSAGE_ID:
1596 if (replymessageid == NULL)
1597 replymessageid = g_strdup(p);
1599 case Q_FWD_MESSAGE_ID:
1600 if (fwdmessageid == NULL)
1601 fwdmessageid = g_strdup(p);
1609 case Q_CLAWS_HDRS_OLD:
1610 /* end of special headers reached */
1612 goto send_mail; /* can't "break;break;" */
1618 filepos = ftell(fp);
1620 FILE_OP_ERROR(file, "ftell");
1622 if (*errstr) g_free(*errstr);
1623 *errstr = g_strdup_printf(_("Couldn't open file %s."), file);
1629 debug_print("Sending message by mail\n");
1632 if (*errstr) g_free(*errstr);
1633 *errstr = g_strdup_printf(_("Queued message header is broken."));
1636 } else if (mailac && mailac->use_mail_command &&
1637 mailac->mail_command && (* mailac->mail_command)) {
1638 mailval = send_message_local(mailac->mail_command, fp);
1641 mailac = account_find_from_smtp_server(from, smtpserver);
1643 g_warning("Account not found. "
1644 "Using current account...");
1645 mailac = cur_account;
1650 mailval = send_message_smtp_full(mailac, to_list, fp, keep_session);
1651 if (mailval == -1 && errstr) {
1652 if (*errstr) g_free(*errstr);
1653 *errstr = g_strdup_printf(_("An error happened during SMTP session."));
1656 PrefsAccount tmp_ac;
1658 g_warning("Account not found.");
1660 memset(&tmp_ac, 0, sizeof(PrefsAccount));
1661 tmp_ac.address = from;
1662 tmp_ac.smtp_server = smtpserver;
1663 tmp_ac.smtpport = SMTP_PORT;
1664 mailval = send_message_smtp(&tmp_ac, to_list, fp);
1665 if (mailval == -1 && errstr) {
1666 if (*errstr) g_free(*errstr);
1667 *errstr = g_strdup_printf(_("No specific account has been found to "
1668 "send, and an error happened during SMTP session."));
1672 } else if (!to_list && !newsgroup_list) {
1674 if (*errstr) g_free(*errstr);
1675 *errstr = g_strdup(_("Couldn't determine sending information. "
1676 "Maybe the email hasn't been generated by Claws Mail."));
1681 if (fseek(fp, filepos, SEEK_SET) < 0) {
1682 FILE_OP_ERROR(file, "fseek");
1686 if (newsgroup_list && newsac && (mailval == 0)) {
1689 gchar buf[BUFFSIZE];
1692 /* write to temporary file */
1693 tmp = g_strdup_printf("%s%cnntp%p", get_tmp_dir(),
1694 G_DIR_SEPARATOR, file);
1695 if ((tmpfp = claws_fopen(tmp, "wb")) == NULL) {
1696 FILE_OP_ERROR(tmp, "claws_fopen");
1698 alertpanel_error(_("Couldn't create temporary file for news sending."));
1700 if (change_file_mode_rw(tmpfp, tmp) < 0) {
1701 FILE_OP_ERROR(tmp, "chmod");
1702 g_warning("can't change file mode");
1705 while ((newsval == 0) && claws_fgets(buf, sizeof(buf), fp) != NULL) {
1706 if (claws_fputs(buf, tmpfp) == EOF) {
1707 FILE_OP_ERROR(tmp, "claws_fputs");
1710 if (*errstr) g_free(*errstr);
1711 *errstr = g_strdup_printf(_("Error when writing temporary file for news sending."));
1715 claws_safe_fclose(tmpfp);
1718 debug_print("Sending message by news\n");
1720 folder = FOLDER(newsac->folder);
1722 newsval = news_post(folder, tmp);
1723 if (newsval < 0 && errstr) {
1724 if (*errstr) g_free(*errstr);
1725 *errstr = g_strdup_printf(_("Error occurred while posting the message to %s."),
1726 newsac->nntp_server);
1736 /* update session statistics */
1737 if (mailval == 0 && newsval == 0) {
1738 /* update session stats */
1740 session_stats.replied++;
1741 else if (fwdmessageid)
1742 session_stats.forwarded++;
1744 session_stats.sent++;
1747 /* save message to outbox */
1748 if (mailval == 0 && newsval == 0 && savecopyfolder) {
1749 debug_print("saving sent message to %s...\n", savecopyfolder);
1751 if (!encrypt || !mailac->save_encrypted_as_clear_text) {
1752 outbox = folder_find_item_from_identifier(savecopyfolder);
1755 outbox = folder_get_default_outbox();
1756 if (outbox != NULL) {
1757 id = folder_item_get_identifier(outbox);
1758 debug_print("%s not found, using %s\n", savecopyfolder, id);
1761 debug_print("could not find outbox\n");
1764 /* Mail was not saved to outbox before encrypting, save it now. */
1765 gboolean saved = FALSE;
1766 *queued_removed = FALSE;
1767 if (queue && msgnum > 0) {
1768 MsgInfo *queued_mail = folder_item_get_msginfo(queue, msgnum);
1769 if (folder_item_move_msg(outbox, queued_mail) >= 0) {
1770 debug_print("moved queued mail %d to sent folder\n", msgnum);
1772 *queued_removed = TRUE;
1773 } else if (folder_item_copy_msg(outbox, queued_mail) >= 0) {
1774 debug_print("copied queued mail %d to sent folder\n", msgnum);
1777 procmsg_msginfo_free(&queued_mail);
1780 debug_print("resaving queued mail to sent folder\n");
1781 procmsg_save_to_outbox(outbox, file, TRUE);
1786 if (replymessageid != NULL || fwdmessageid != NULL) {
1790 if (replymessageid != NULL)
1791 tokens = g_strsplit(replymessageid, "\t", 0);
1793 tokens = g_strsplit(fwdmessageid, "\t", 0);
1794 item = folder_find_item_from_identifier(tokens[0]);
1796 /* check if queued message has valid folder and message id */
1797 if (item != NULL && tokens[2] != NULL) {
1800 msginfo = folder_item_get_msginfo(item, atoi(tokens[1]));
1802 /* check if referring message exists and has a message id */
1803 if ((msginfo != NULL) &&
1804 (msginfo->msgid != NULL) &&
1805 (strcmp(msginfo->msgid, tokens[2]) != 0)) {
1806 procmsg_msginfo_free(&msginfo);
1810 if (msginfo == NULL) {
1811 msginfo = folder_item_get_msginfo_by_msgid(item, tokens[2]);
1814 if (msginfo != NULL) {
1815 if (replymessageid != NULL) {
1816 MsgPermFlags to_unset = 0;
1818 if (prefs_common.mark_as_read_on_new_window)
1819 to_unset = (MSG_NEW|MSG_UNREAD);
1821 procmsg_msginfo_unset_flags(msginfo, to_unset, 0);
1822 procmsg_msginfo_set_flags(msginfo, MSG_REPLIED, 0);
1824 procmsg_msginfo_set_flags(msginfo, MSG_FORWARDED, 0);
1826 procmsg_msginfo_free(&msginfo);
1834 slist_free_strings_full(to_list);
1835 slist_free_strings_full(newsgroup_list);
1836 g_free(savecopyfolder);
1837 g_free(replymessageid);
1838 g_free(fwdmessageid);
1840 return (newsval != 0 ? newsval : mailval);
1843 gint procmsg_send_message_queue(const gchar *file, gchar **errstr, FolderItem *queue, gint msgnum, gboolean *queued_removed)
1845 gint result = procmsg_send_message_queue_full(file, FALSE, errstr, queue, msgnum, queued_removed);
1846 main_window_set_menu_sensitive(mainwindow_get_mainwindow());
1847 toolbar_main_set_sensitive(mainwindow_get_mainwindow());
1851 gint procmsg_send_message_queue_with_lock(const gchar *file, gchar **errstr, FolderItem *queue, gint msgnum, gboolean *queued_removed)
1854 if (procmsg_queue_lock(errstr)) {
1855 val = procmsg_send_message_queue(file, errstr, queue, msgnum, queued_removed);
1856 procmsg_queue_unlock();
1862 static void update_folder_msg_counts(FolderItem *item, MsgInfo *msginfo, MsgPermFlags old_flags)
1864 MsgPermFlags new_flags = msginfo->flags.perm_flags;
1867 if (!(old_flags & MSG_NEW) && (new_flags & MSG_NEW)) {
1871 if ((old_flags & MSG_NEW) && !(new_flags & MSG_NEW)) {
1876 if (!(old_flags & MSG_UNREAD) && (new_flags & MSG_UNREAD)) {
1877 item->unread_msgs++;
1878 if (procmsg_msg_has_marked_parent(msginfo))
1879 item->unreadmarked_msgs++;
1882 if ((old_flags & MSG_UNREAD) && !(new_flags & MSG_UNREAD)) {
1883 item->unread_msgs--;
1884 if (procmsg_msg_has_marked_parent(msginfo))
1885 item->unreadmarked_msgs--;
1889 if (!(old_flags & MSG_MARKED) && (new_flags & MSG_MARKED)) {
1890 procmsg_update_unread_children(msginfo, TRUE);
1891 item->marked_msgs++;
1894 if ((old_flags & MSG_MARKED) && !(new_flags & MSG_MARKED)) {
1895 procmsg_update_unread_children(msginfo, FALSE);
1896 item->marked_msgs--;
1899 if (!(old_flags & MSG_REPLIED) && (new_flags & MSG_REPLIED)) {
1900 item->replied_msgs++;
1903 if ((old_flags & MSG_REPLIED) && !(new_flags & MSG_REPLIED)) {
1904 item->replied_msgs--;
1907 if (!(old_flags & MSG_FORWARDED) && (new_flags & MSG_FORWARDED)) {
1908 item->forwarded_msgs++;
1911 if ((old_flags & MSG_FORWARDED) && !(new_flags & MSG_FORWARDED)) {
1912 item->forwarded_msgs--;
1915 if (!(old_flags & MSG_LOCKED) && (new_flags & MSG_LOCKED)) {
1916 item->locked_msgs++;
1919 if ((old_flags & MSG_LOCKED) && !(new_flags & MSG_LOCKED)) {
1920 item->locked_msgs--;
1923 if ((old_flags & MSG_IGNORE_THREAD) && !(new_flags & MSG_IGNORE_THREAD)) {
1924 item->ignored_msgs--;
1927 if (!(old_flags & MSG_IGNORE_THREAD) && (new_flags & MSG_IGNORE_THREAD)) {
1928 item->ignored_msgs++;
1931 if ((old_flags & MSG_WATCH_THREAD) && !(new_flags & MSG_WATCH_THREAD)) {
1932 item->watched_msgs--;
1935 if (!(old_flags & MSG_WATCH_THREAD) && (new_flags & MSG_WATCH_THREAD)) {
1936 item->watched_msgs++;
1940 void procmsg_msginfo_set_flags(MsgInfo *msginfo, MsgPermFlags perm_flags, MsgTmpFlags tmp_flags)
1943 MsgInfoUpdate msginfo_update;
1944 MsgPermFlags perm_flags_new, perm_flags_old;
1945 MsgTmpFlags tmp_flags_old;
1947 cm_return_if_fail(msginfo != NULL);
1948 item = msginfo->folder;
1949 cm_return_if_fail(item != NULL);
1951 debug_print("Setting flags for message %d in folder %s\n", msginfo->msgnum, item->path);
1953 /* Perm Flags handling */
1954 perm_flags_old = msginfo->flags.perm_flags;
1955 perm_flags_new = msginfo->flags.perm_flags | perm_flags;
1956 if ((perm_flags & MSG_IGNORE_THREAD) || (perm_flags_old & MSG_IGNORE_THREAD)) {
1957 perm_flags_new &= ~(MSG_NEW | MSG_UNREAD);
1959 if ((perm_flags & MSG_WATCH_THREAD) || (perm_flags_old & MSG_WATCH_THREAD)) {
1960 perm_flags_new &= ~(MSG_IGNORE_THREAD);
1963 if (perm_flags_old != perm_flags_new) {
1964 folder_item_change_msg_flags(msginfo->folder, msginfo, perm_flags_new);
1966 update_folder_msg_counts(item, msginfo, perm_flags_old);
1967 summary_update_unread(mainwindow_get_mainwindow()->summaryview, NULL);
1970 /* Tmp flags handling */
1971 tmp_flags_old = msginfo->flags.tmp_flags;
1972 msginfo->flags.tmp_flags |= tmp_flags;
1974 /* update notification */
1975 if ((perm_flags_old != perm_flags_new) || (tmp_flags_old != msginfo->flags.tmp_flags)) {
1976 msginfo_update.msginfo = msginfo;
1977 msginfo_update.flags = MSGINFO_UPDATE_FLAGS;
1978 hooks_invoke(MSGINFO_UPDATE_HOOKLIST, &msginfo_update);
1979 folder_item_update(msginfo->folder, F_ITEM_UPDATE_MSGCNT);
1983 void procmsg_msginfo_unset_flags(MsgInfo *msginfo, MsgPermFlags perm_flags, MsgTmpFlags tmp_flags)
1986 MsgInfoUpdate msginfo_update;
1987 MsgPermFlags perm_flags_new, perm_flags_old;
1988 MsgTmpFlags tmp_flags_old;
1990 cm_return_if_fail(msginfo != NULL);
1991 item = msginfo->folder;
1992 cm_return_if_fail(item != NULL);
1994 debug_print("Unsetting flags for message %d in folder %s\n", msginfo->msgnum, item->path);
1996 /* Perm Flags handling */
1997 perm_flags_old = msginfo->flags.perm_flags;
1998 perm_flags_new = msginfo->flags.perm_flags & ~perm_flags;
2000 if (perm_flags_old != perm_flags_new) {
2001 folder_item_change_msg_flags(msginfo->folder, msginfo, perm_flags_new);
2003 update_folder_msg_counts(item, msginfo, perm_flags_old);
2006 /* Tmp flags hanlding */
2007 tmp_flags_old = msginfo->flags.tmp_flags;
2008 msginfo->flags.tmp_flags &= ~tmp_flags;
2010 /* update notification */
2011 if ((perm_flags_old != perm_flags_new) || (tmp_flags_old != msginfo->flags.tmp_flags)) {
2012 msginfo_update.msginfo = msginfo;
2013 msginfo_update.flags = MSGINFO_UPDATE_FLAGS;
2014 hooks_invoke(MSGINFO_UPDATE_HOOKLIST, &msginfo_update);
2015 folder_item_update(msginfo->folder, F_ITEM_UPDATE_MSGCNT);
2019 void procmsg_msginfo_change_flags(MsgInfo *msginfo,
2020 MsgPermFlags add_perm_flags, MsgTmpFlags add_tmp_flags,
2021 MsgPermFlags rem_perm_flags, MsgTmpFlags rem_tmp_flags)
2024 MsgInfoUpdate msginfo_update;
2025 MsgPermFlags perm_flags_new, perm_flags_old;
2026 MsgTmpFlags tmp_flags_old;
2028 cm_return_if_fail(msginfo != NULL);
2029 item = msginfo->folder;
2030 cm_return_if_fail(item != NULL);
2032 debug_print("Changing flags for message %d in folder %s\n", msginfo->msgnum, item->path);
2034 /* Perm Flags handling */
2035 perm_flags_old = msginfo->flags.perm_flags;
2036 perm_flags_new = (msginfo->flags.perm_flags & ~rem_perm_flags) | add_perm_flags;
2037 if ((add_perm_flags & MSG_IGNORE_THREAD) || (perm_flags_old & MSG_IGNORE_THREAD)) {
2038 perm_flags_new &= ~(MSG_NEW | MSG_UNREAD);
2040 if ((add_perm_flags & MSG_WATCH_THREAD) || (perm_flags_old & MSG_WATCH_THREAD)) {
2041 perm_flags_new &= ~(MSG_IGNORE_THREAD);
2044 if (perm_flags_old != perm_flags_new) {
2045 folder_item_change_msg_flags(msginfo->folder, msginfo, perm_flags_new);
2047 update_folder_msg_counts(item, msginfo, perm_flags_old);
2051 /* Tmp flags handling */
2052 tmp_flags_old = msginfo->flags.tmp_flags;
2053 msginfo->flags.tmp_flags &= ~rem_tmp_flags;
2054 msginfo->flags.tmp_flags |= add_tmp_flags;
2056 /* update notification */
2057 if ((perm_flags_old != perm_flags_new) || (tmp_flags_old != msginfo->flags.tmp_flags)) {
2058 msginfo_update.msginfo = msginfo;
2059 msginfo_update.flags = MSGINFO_UPDATE_FLAGS;
2060 hooks_invoke(MSGINFO_UPDATE_HOOKLIST, &msginfo_update);
2061 folder_item_update(msginfo->folder, F_ITEM_UPDATE_MSGCNT);
2066 *\brief check for flags (e.g. mark) in prior msgs of current thread
2068 *\param info Current message
2069 *\param perm_flags Flags to be checked
2070 *\param parentmsgs Hash of prior msgs to avoid loops
2072 *\return gboolean TRUE if perm_flags are found
2074 static gboolean procmsg_msg_has_flagged_parent_real(MsgInfo *info,
2075 MsgPermFlags perm_flags, GHashTable *parentmsgs)
2079 cm_return_val_if_fail(info != NULL, FALSE);
2081 if (info != NULL && info->folder != NULL && info->inreplyto != NULL) {
2082 tmp = folder_item_get_msginfo_by_msgid(info->folder,
2084 if (tmp && (tmp->flags.perm_flags & perm_flags)) {
2085 procmsg_msginfo_free(&tmp);
2087 } else if (tmp != NULL) {
2090 if (g_hash_table_lookup(parentmsgs, info)) {
2091 debug_print("loop detected: %d\n",
2095 g_hash_table_insert(parentmsgs, info, "1");
2096 result = procmsg_msg_has_flagged_parent_real(
2097 tmp, perm_flags, parentmsgs);
2099 procmsg_msginfo_free(&tmp);
2109 *\brief Callback for cleaning up hash of parentmsgs
2111 static gboolean parentmsgs_hash_remove(gpointer key,
2119 *\brief Set up list of parentmsgs
2120 * See procmsg_msg_has_flagged_parent_real()
2122 gboolean procmsg_msg_has_flagged_parent(MsgInfo *info, MsgPermFlags perm_flags)
2125 static GHashTable *parentmsgs = NULL;
2127 if (parentmsgs == NULL)
2128 parentmsgs = g_hash_table_new(NULL, NULL);
2130 result = procmsg_msg_has_flagged_parent_real(info, perm_flags, parentmsgs);
2131 g_hash_table_foreach_remove(parentmsgs, parentmsgs_hash_remove, NULL);
2137 *\brief Check if msgs prior in thread are marked
2138 * See procmsg_msg_has_flagged_parent_real()
2140 gboolean procmsg_msg_has_marked_parent(MsgInfo *info)
2142 return procmsg_msg_has_flagged_parent(info, MSG_MARKED);
2146 static GSList *procmsg_find_children_func(MsgInfo *info,
2147 GSList *children, GSList *all)
2151 cm_return_val_if_fail(info!=NULL, children);
2152 if (info->msgid == NULL)
2155 for (cur = all; cur != NULL; cur = g_slist_next(cur)) {
2156 MsgInfo *tmp = (MsgInfo *)cur->data;
2157 if (tmp->inreplyto && !strcmp(tmp->inreplyto, info->msgid)) {
2158 /* Check if message is already in the list */
2159 if ((children == NULL) ||
2160 (g_slist_index(children, tmp) == -1)) {
2161 children = g_slist_prepend(children,
2162 procmsg_msginfo_new_ref(tmp));
2163 children = procmsg_find_children_func(tmp,
2172 static GSList *procmsg_find_children (MsgInfo *info)
2177 cm_return_val_if_fail(info!=NULL, NULL);
2178 all = folder_item_get_msg_list(info->folder);
2179 children = procmsg_find_children_func(info, NULL, all);
2180 if (children != NULL) {
2181 for (cur = all; cur != NULL; cur = g_slist_next(cur)) {
2182 /* this will not free the used pointers
2183 created with procmsg_msginfo_new_ref */
2184 procmsg_msginfo_free((MsgInfo **)&(cur->data));
2192 static void procmsg_update_unread_children(MsgInfo *info, gboolean newly_marked)
2194 GSList *children = procmsg_find_children(info);
2196 for (cur = children; cur != NULL; cur = g_slist_next(cur)) {
2197 MsgInfo *tmp = (MsgInfo *)cur->data;
2198 if(MSG_IS_UNREAD(tmp->flags) && !MSG_IS_IGNORE_THREAD(tmp->flags)) {
2200 info->folder->unreadmarked_msgs++;
2202 info->folder->unreadmarked_msgs--;
2203 folder_item_update(info->folder, F_ITEM_UPDATE_MSGCNT);
2205 procmsg_msginfo_free(&tmp);
2207 g_slist_free(children);
2211 * Set the destination folder for a copy or move operation
2213 * \param msginfo The message which's destination folder is changed
2214 * \param to_folder The destination folder for the operation
2216 void procmsg_msginfo_set_to_folder(MsgInfo *msginfo, FolderItem *to_folder)
2218 if(msginfo->to_folder != NULL) {
2219 msginfo->to_folder->op_count--;
2220 folder_item_update(msginfo->to_folder, F_ITEM_UPDATE_MSGCNT);
2222 msginfo->to_folder = to_folder;
2223 if(to_folder != NULL) {
2224 to_folder->op_count++;
2225 folder_item_update(msginfo->to_folder, F_ITEM_UPDATE_MSGCNT);
2230 * Apply filtering actions to the msginfo
2232 * \param msginfo The MsgInfo describing the message that should be filtered
2233 * \return TRUE if the message was moved and MsgInfo is now invalid,
2236 static gboolean procmsg_msginfo_filter(MsgInfo *msginfo, PrefsAccount* ac_prefs)
2238 MailFilteringData mail_filtering_data;
2240 mail_filtering_data.msginfo = msginfo;
2241 mail_filtering_data.msglist = NULL;
2242 mail_filtering_data.filtered = NULL;
2243 mail_filtering_data.unfiltered = NULL;
2244 mail_filtering_data.account = ac_prefs;
2246 if (!ac_prefs || ac_prefs->filterhook_on_recv)
2247 if (hooks_invoke(MAIL_FILTERING_HOOKLIST, &mail_filtering_data))
2250 /* filter if enabled in prefs or move to inbox if not */
2251 if((filtering_rules != NULL) &&
2252 filter_message_by_msginfo(filtering_rules, msginfo, ac_prefs,
2253 FILTERING_INCORPORATION, NULL)) {
2260 void procmsg_msglist_filter(GSList *list, PrefsAccount *ac,
2261 GSList **filtered, GSList **unfiltered,
2264 GSList *cur, *to_do = NULL;
2265 gint total = 0, curnum = 0;
2266 MailFilteringData mail_filtering_data;
2268 cm_return_if_fail(filtered != NULL);
2269 cm_return_if_fail(unfiltered != NULL);
2277 total = g_slist_length(list);
2281 *unfiltered = g_slist_copy(list);
2285 statusbar_print_all(_("Filtering messages...\n"));
2287 mail_filtering_data.msginfo = NULL;
2288 mail_filtering_data.msglist = list;
2289 mail_filtering_data.filtered = NULL;
2290 mail_filtering_data.unfiltered = NULL;
2291 mail_filtering_data.account = ac;
2293 if (!ac || ac->filterhook_on_recv)
2294 hooks_invoke(MAIL_LISTFILTERING_HOOKLIST, &mail_filtering_data);
2296 if (mail_filtering_data.filtered == NULL &&
2297 mail_filtering_data.unfiltered == NULL) {
2298 /* nothing happened */
2299 debug_print(MAIL_LISTFILTERING_HOOKLIST " did nothing. filtering whole list normally.\n");
2302 if (mail_filtering_data.filtered != NULL) {
2303 /* keep track of what's been filtered by the hooks */
2304 debug_print(MAIL_LISTFILTERING_HOOKLIST " filtered some stuff. total %d filtered %d unfilt %d.\n",
2305 g_slist_length(list),
2306 g_slist_length(mail_filtering_data.filtered),
2307 g_slist_length(mail_filtering_data.unfiltered));
2309 *filtered = g_slist_copy(mail_filtering_data.filtered);
2311 if (mail_filtering_data.unfiltered != NULL) {
2312 /* what the hooks didn't handle will go in filtered or
2313 * unfiltered in the next loop */
2314 debug_print(MAIL_LISTFILTERING_HOOKLIST " left unfiltered stuff. total %d filtered %d unfilt %d.\n",
2315 g_slist_length(list),
2316 g_slist_length(mail_filtering_data.filtered),
2317 g_slist_length(mail_filtering_data.unfiltered));
2318 to_do = mail_filtering_data.unfiltered;
2321 for (cur = to_do; cur; cur = cur->next) {
2322 MsgInfo *info = (MsgInfo *)cur->data;
2323 if (procmsg_msginfo_filter(info, ac))
2324 *filtered = g_slist_prepend(*filtered, info);
2326 *unfiltered = g_slist_prepend(*unfiltered, info);
2327 statusbar_progress_all(curnum++, total, prefs_common.statusbar_update_step);
2330 g_slist_free(mail_filtering_data.filtered);
2331 g_slist_free(mail_filtering_data.unfiltered);
2333 *filtered = g_slist_reverse(*filtered);
2334 *unfiltered = g_slist_reverse(*unfiltered);
2336 statusbar_progress_all(0,0,0);
2337 statusbar_pop_all();
2340 MsgInfo *procmsg_msginfo_new_from_mimeinfo(MsgInfo *src_msginfo, MimeInfo *mimeinfo)
2342 MsgInfo *tmp_msginfo = NULL;
2343 MsgFlags flags = {0, 0};
2344 gchar *tmpfile = get_tmp_file();
2345 FILE *fp = claws_fopen(tmpfile, "wb");
2347 if (!mimeinfo || mimeinfo->type != MIMETYPE_MESSAGE ||
2348 g_ascii_strcasecmp(mimeinfo->subtype, "rfc822")) {
2349 g_warning("procmsg_msginfo_new_from_mimeinfo(): unsuitable mimeinfo");
2356 if (fp && procmime_write_mimeinfo(mimeinfo, fp) >= 0) {
2357 claws_safe_fclose(fp);
2359 tmp_msginfo = procheader_parse_file(
2364 claws_safe_fclose(fp);
2366 if (tmp_msginfo != NULL) {
2368 tmp_msginfo->folder = src_msginfo->folder;
2369 tmp_msginfo->plaintext_file = g_strdup(tmpfile);
2371 g_warning("procmsg_msginfo_new_from_mimeinfo(): Can't generate new msginfo");
2379 static GSList *spam_learners = NULL;
2381 void procmsg_register_spam_learner (int (*learn_func)(MsgInfo *info, GSList *list, gboolean spam))
2383 if (!g_slist_find(spam_learners, learn_func))
2384 spam_learners = g_slist_append(spam_learners, learn_func);
2385 if (mainwindow_get_mainwindow()) {
2386 main_window_set_menu_sensitive(mainwindow_get_mainwindow());
2387 summary_set_menu_sensitive(
2388 mainwindow_get_mainwindow()->summaryview);
2389 toolbar_main_set_sensitive(mainwindow_get_mainwindow());
2393 void procmsg_unregister_spam_learner (int (*learn_func)(MsgInfo *info, GSList *list, gboolean spam))
2395 spam_learners = g_slist_remove(spam_learners, learn_func);
2396 if (mainwindow_get_mainwindow()) {
2397 main_window_set_menu_sensitive(mainwindow_get_mainwindow());
2398 summary_set_menu_sensitive(
2399 mainwindow_get_mainwindow()->summaryview);
2400 toolbar_main_set_sensitive(mainwindow_get_mainwindow());
2404 gboolean procmsg_spam_can_learn(void)
2406 return g_slist_length(spam_learners) > 0;
2409 int procmsg_spam_learner_learn (MsgInfo *info, GSList *list, gboolean spam)
2411 GSList *cur = spam_learners;
2413 for (; cur; cur = cur->next) {
2414 int ((*func)(MsgInfo *info, GSList *list, gboolean spam)) = cur->data;
2415 ret |= func(info, list, spam);
2420 static gchar *spam_folder_item = NULL;
2421 static FolderItem * (*procmsg_spam_get_folder_func)(MsgInfo *msginfo) = NULL;
2422 void procmsg_spam_set_folder (const char *item_identifier, FolderItem *(*spam_get_folder_func)(MsgInfo *info))
2424 g_free(spam_folder_item);
2425 if (item_identifier)
2426 spam_folder_item = g_strdup(item_identifier);
2428 spam_folder_item = NULL;
2429 if (spam_get_folder_func != NULL)
2430 procmsg_spam_get_folder_func = spam_get_folder_func;
2432 procmsg_spam_get_folder_func = NULL;
2435 FolderItem *procmsg_spam_get_folder (MsgInfo *msginfo)
2437 FolderItem *item = NULL;
2439 if (procmsg_spam_get_folder_func)
2440 item = procmsg_spam_get_folder_func(msginfo);
2441 if (item == NULL && spam_folder_item)
2442 item = folder_find_item_from_identifier(spam_folder_item);
2444 item = folder_get_default_trash();
2448 static void item_has_queued_mails(FolderItem *item, gpointer data)
2450 gboolean *result = (gboolean *)data;
2451 if (*result == TRUE)
2453 if (folder_has_parent_of_type(item, F_QUEUE)) {
2454 if (item->total_msgs == 0)
2457 GSList *msglist = folder_item_get_msg_list(item);
2459 for (cur = msglist; cur; cur = cur->next) {
2460 MsgInfo *msginfo = (MsgInfo *)cur->data;
2461 if (!MSG_IS_DELETED(msginfo->flags) &&
2462 !MSG_IS_LOCKED(msginfo->flags)) {
2467 procmsg_msg_list_free(msglist);
2472 gboolean procmsg_have_queued_mails_fast (void)
2474 gboolean result = FALSE;
2475 folder_func_to_all_folders(item_has_queued_mails, &result);
2479 static void item_has_trashed_mails(FolderItem *item, gpointer data)
2481 gboolean *result = (gboolean *)data;
2482 if (*result == TRUE)
2484 if (folder_has_parent_of_type(item, F_TRASH) && item->total_msgs > 0)
2488 gboolean procmsg_have_trashed_mails_fast (void)
2490 gboolean result = FALSE;
2491 folder_func_to_all_folders(item_has_trashed_mails, &result);
2495 gchar *procmsg_msginfo_get_tags_str(MsgInfo *msginfo)
2503 if (msginfo->tags == NULL)
2505 for (cur = msginfo->tags; cur; cur = cur->next) {
2506 const gchar *tag = tags_get_tag(GPOINTER_TO_INT(cur->data));
2510 tags = g_strdup(tag);
2512 int olen = strlen(tags);
2513 int nlen = olen + strlen(tag) + 2 /* strlen(", ") */;
2514 tags = g_realloc(tags, nlen+1);
2517 strcpy(tags+olen, ", ");
2518 strcpy(tags+olen+2, tag);
2525 void procmsg_msginfo_update_tags(MsgInfo *msginfo, gboolean set, gint id)
2533 msginfo->tags = g_slist_remove(
2535 GINT_TO_POINTER(id));
2536 changed.data = GINT_TO_POINTER(id);
2537 changed.next = NULL;
2538 folder_item_commit_tags(msginfo->folder, msginfo, NULL, &changed);
2540 if (!g_slist_find(msginfo->tags, GINT_TO_POINTER(id))) {
2541 msginfo->tags = g_slist_append(
2543 GINT_TO_POINTER(id));
2545 changed.data = GINT_TO_POINTER(id);
2546 changed.next = NULL;
2547 folder_item_commit_tags(msginfo->folder, msginfo, &changed, NULL);
2552 void procmsg_msginfo_clear_tags(MsgInfo *msginfo)
2554 GSList *unset = msginfo->tags;
2555 msginfo->tags = NULL;
2556 folder_item_commit_tags(msginfo->folder, msginfo, NULL, unset);
2557 g_slist_free(unset);