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};
1136 debug_print("saving sent message...\n");
1139 outbox = folder_get_default_outbox();
1140 cm_return_val_if_fail(outbox != NULL, -1);
1142 /* remove queueing headers */
1144 gchar tmp[MAXPATHLEN + 1];
1146 g_snprintf(tmp, sizeof(tmp), "%s%ctmpmsg.out.%08x",
1147 get_rc_dir(), G_DIR_SEPARATOR, (guint) rand());
1149 if (procmsg_remove_special_headers(file, tmp) !=0)
1152 folder_item_scan(outbox);
1153 if ((num = folder_item_add_msg(outbox, tmp, &flag, TRUE)) < 0) {
1154 g_warning("can't save message");
1159 folder_item_scan(outbox);
1160 if ((num = folder_item_add_msg
1161 (outbox, file, &flag, FALSE)) < 0) {
1162 g_warning("can't save message");
1166 msginfo = folder_item_get_msginfo(outbox, num); /* refcnt++ */
1167 tmp_msginfo = procmsg_msginfo_get_full_info(msginfo); /* refcnt++ */
1168 if (msginfo != NULL) {
1169 procmsg_msginfo_unset_flags(msginfo, ~0, 0);
1170 procmsg_msginfo_free(&msginfo); /* refcnt-- */
1171 /* tmp_msginfo == msginfo */
1172 if (tmp_msginfo && msginfo->extradata &&
1173 (msginfo->extradata->dispositionnotificationto ||
1174 msginfo->extradata->returnreceiptto)) {
1175 procmsg_msginfo_set_flags(msginfo, MSG_RETRCPT_SENT, 0);
1177 procmsg_msginfo_free(&tmp_msginfo); /* refcnt-- */
1184 MsgInfo *procmsg_msginfo_new_ref(MsgInfo *msginfo)
1191 MsgInfo *procmsg_msginfo_new(void)
1193 MsgInfo *newmsginfo;
1195 newmsginfo = g_new0(MsgInfo, 1);
1196 newmsginfo->refcnt = 1;
1201 static MsgInfoAvatar *procmsg_msginfoavatar_copy(MsgInfoAvatar *avatar)
1203 MsgInfoAvatar *newavatar;
1205 if (avatar == NULL) return NULL;
1207 newavatar = g_new0(MsgInfoAvatar, 1);
1208 newavatar->avatar_id = avatar->avatar_id;
1209 newavatar->avatar_src = g_strdup(avatar->avatar_src);
1214 static void procmsg_msginfoavatar_free(MsgInfoAvatar *avatar)
1216 if (avatar != NULL) {
1217 if (avatar->avatar_src != NULL)
1218 g_free(avatar->avatar_src);
1223 MsgInfo *procmsg_msginfo_copy(MsgInfo *msginfo)
1225 MsgInfo *newmsginfo;
1228 if (msginfo == NULL) return NULL;
1230 newmsginfo = g_new0(MsgInfo, 1);
1232 newmsginfo->refcnt = 1;
1234 #define MEMBCOPY(mmb) newmsginfo->mmb = msginfo->mmb
1235 #define MEMBDUP(mmb) newmsginfo->mmb = msginfo->mmb ? \
1236 g_strdup(msginfo->mmb) : NULL
1251 MEMBDUP(newsgroups);
1258 MEMBCOPY(to_folder);
1260 if (msginfo->extradata) {
1261 newmsginfo->extradata = g_new0(MsgInfoExtraData, 1);
1262 if (msginfo->extradata->avatars) {
1263 newmsginfo->extradata->avatars = slist_copy_deep(msginfo->extradata->avatars,
1264 (GCopyFunc) procmsg_msginfoavatar_copy);
1266 MEMBDUP(extradata->dispositionnotificationto);
1267 MEMBDUP(extradata->returnreceiptto);
1268 MEMBDUP(extradata->partial_recv);
1269 MEMBDUP(extradata->account_server);
1270 MEMBDUP(extradata->account_login);
1271 MEMBDUP(extradata->list_post);
1272 MEMBDUP(extradata->list_subscribe);
1273 MEMBDUP(extradata->list_unsubscribe);
1274 MEMBDUP(extradata->list_help);
1275 MEMBDUP(extradata->list_archive);
1276 MEMBDUP(extradata->list_owner);
1277 MEMBDUP(extradata->resent_from);
1280 refs = msginfo->references;
1281 for (refs = msginfo->references; refs != NULL; refs = refs->next) {
1282 newmsginfo->references = g_slist_prepend
1283 (newmsginfo->references, g_strdup(refs->data));
1285 newmsginfo->references = g_slist_reverse(newmsginfo->references);
1288 MEMBDUP(plaintext_file);
1293 MsgInfo *procmsg_msginfo_get_full_info_from_file(MsgInfo *msginfo, const gchar *file)
1295 MsgInfo *full_msginfo;
1297 if (msginfo == NULL) return NULL;
1299 if (!file || !is_file_exist(file)) {
1300 g_warning("procmsg_msginfo_get_full_info_from_file(): can't get message file.");
1304 full_msginfo = procheader_parse_file(file, msginfo->flags, TRUE, FALSE);
1305 if (!full_msginfo) return NULL;
1307 msginfo->total_size = full_msginfo->total_size;
1308 msginfo->planned_download = full_msginfo->planned_download;
1310 if (full_msginfo->extradata) {
1311 if (!msginfo->extradata)
1312 msginfo->extradata = g_new0(MsgInfoExtraData, 1);
1313 if (!msginfo->extradata->list_post)
1314 msginfo->extradata->list_post = g_strdup(full_msginfo->extradata->list_post);
1315 if (!msginfo->extradata->list_subscribe)
1316 msginfo->extradata->list_subscribe = g_strdup(full_msginfo->extradata->list_subscribe);
1317 if (!msginfo->extradata->list_unsubscribe)
1318 msginfo->extradata->list_unsubscribe = g_strdup(full_msginfo->extradata->list_unsubscribe);
1319 if (!msginfo->extradata->list_help)
1320 msginfo->extradata->list_help = g_strdup(full_msginfo->extradata->list_help);
1321 if (!msginfo->extradata->list_archive)
1322 msginfo->extradata->list_archive= g_strdup(full_msginfo->extradata->list_archive);
1323 if (!msginfo->extradata->list_owner)
1324 msginfo->extradata->list_owner = g_strdup(full_msginfo->extradata->list_owner);
1325 if (!msginfo->extradata->avatars)
1326 msginfo->extradata->avatars = slist_copy_deep(full_msginfo->extradata->avatars,
1327 (GCopyFunc) procmsg_msginfoavatar_copy);
1328 if (!msginfo->extradata->dispositionnotificationto)
1329 msginfo->extradata->dispositionnotificationto =
1330 g_strdup(full_msginfo->extradata->dispositionnotificationto);
1331 if (!msginfo->extradata->returnreceiptto)
1332 msginfo->extradata->returnreceiptto = g_strdup
1333 (full_msginfo->extradata->returnreceiptto);
1334 if (!msginfo->extradata->partial_recv && full_msginfo->extradata->partial_recv)
1335 msginfo->extradata->partial_recv = g_strdup
1336 (full_msginfo->extradata->partial_recv);
1337 if (!msginfo->extradata->account_server && full_msginfo->extradata->account_server)
1338 msginfo->extradata->account_server = g_strdup
1339 (full_msginfo->extradata->account_server);
1340 if (!msginfo->extradata->account_login && full_msginfo->extradata->account_login)
1341 msginfo->extradata->account_login = g_strdup
1342 (full_msginfo->extradata->account_login);
1343 if (!msginfo->extradata->resent_from && full_msginfo->extradata->resent_from)
1344 msginfo->extradata->resent_from = g_strdup
1345 (full_msginfo->extradata->resent_from);
1347 procmsg_msginfo_free(&full_msginfo);
1349 return procmsg_msginfo_new_ref(msginfo);
1352 MsgInfo *procmsg_msginfo_get_full_info(MsgInfo *msginfo)
1354 MsgInfo *full_msginfo;
1357 if (msginfo == NULL) return NULL;
1359 file = procmsg_get_message_file_path(msginfo);
1360 if (!file || !is_file_exist(file)) {
1362 file = procmsg_get_message_file(msginfo);
1364 if (!file || !is_file_exist(file)) {
1365 g_warning("procmsg_msginfo_get_full_info(): can't get message file.");
1369 full_msginfo = procmsg_msginfo_get_full_info_from_file(msginfo, file);
1371 return full_msginfo;
1374 #define FREENULL(n) { g_free(n); n = NULL; }
1375 void procmsg_msginfo_free(MsgInfo **msginfo_ptr)
1377 MsgInfo *msginfo = *msginfo_ptr;
1379 if (msginfo == NULL) return;
1382 if (msginfo->refcnt > 0)
1385 if (msginfo->to_folder) {
1386 msginfo->to_folder->op_count--;
1387 folder_item_update(msginfo->to_folder, F_ITEM_UPDATE_MSGCNT);
1390 FREENULL(msginfo->fromspace);
1392 FREENULL(msginfo->fromname);
1394 FREENULL(msginfo->date);
1395 FREENULL(msginfo->from);
1396 FREENULL(msginfo->to);
1397 FREENULL(msginfo->cc);
1398 FREENULL(msginfo->newsgroups);
1399 FREENULL(msginfo->subject);
1400 FREENULL(msginfo->msgid);
1401 FREENULL(msginfo->inreplyto);
1402 FREENULL(msginfo->xref);
1404 if (msginfo->extradata) {
1405 if (msginfo->extradata->avatars) {
1406 g_slist_foreach(msginfo->extradata->avatars,
1407 (GFunc)procmsg_msginfoavatar_free,
1409 g_slist_free(msginfo->extradata->avatars);
1410 msginfo->extradata->avatars = NULL;
1412 FREENULL(msginfo->extradata->returnreceiptto);
1413 FREENULL(msginfo->extradata->dispositionnotificationto);
1414 FREENULL(msginfo->extradata->list_post);
1415 FREENULL(msginfo->extradata->list_subscribe);
1416 FREENULL(msginfo->extradata->list_unsubscribe);
1417 FREENULL(msginfo->extradata->list_help);
1418 FREENULL(msginfo->extradata->list_archive);
1419 FREENULL(msginfo->extradata->list_owner);
1420 FREENULL(msginfo->extradata->partial_recv);
1421 FREENULL(msginfo->extradata->account_server);
1422 FREENULL(msginfo->extradata->account_login);
1423 FREENULL(msginfo->extradata->resent_from);
1424 FREENULL(msginfo->extradata);
1426 slist_free_strings_full(msginfo->references);
1427 msginfo->references = NULL;
1428 g_slist_free(msginfo->tags);
1429 msginfo->tags = NULL;
1431 FREENULL(msginfo->plaintext_file);
1434 *msginfo_ptr = NULL;
1438 guint procmsg_msginfo_memusage(MsgInfo *msginfo)
1443 memusage += sizeof(MsgInfo);
1444 if (msginfo->fromname)
1445 memusage += strlen(msginfo->fromname);
1447 memusage += strlen(msginfo->date);
1449 memusage += strlen(msginfo->from);
1451 memusage += strlen(msginfo->to);
1453 memusage += strlen(msginfo->cc);
1454 if (msginfo->newsgroups)
1455 memusage += strlen(msginfo->newsgroups);
1456 if (msginfo->subject)
1457 memusage += strlen(msginfo->subject);
1459 memusage += strlen(msginfo->msgid);
1460 if (msginfo->inreplyto)
1461 memusage += strlen(msginfo->inreplyto);
1463 for (tmp = msginfo->references; tmp; tmp=tmp->next) {
1464 gchar *r = (gchar *)tmp->data;
1465 memusage += r?strlen(r):0 + sizeof(GSList);
1467 if (msginfo->fromspace)
1468 memusage += strlen(msginfo->fromspace);
1470 for (tmp = msginfo->tags; tmp; tmp=tmp->next) {
1471 memusage += sizeof(GSList);
1473 if (msginfo->extradata) {
1474 memusage += sizeof(MsgInfoExtraData);
1475 if (msginfo->extradata->avatars) {
1476 for (tmp = msginfo->extradata->avatars; tmp; tmp = tmp->next) {
1477 MsgInfoAvatar *avt = (MsgInfoAvatar *)tmp->data;
1478 memusage += (avt->avatar_src)? strlen(avt->avatar_src): 0;
1479 memusage += sizeof(MsgInfoAvatar) + sizeof(GSList);
1482 if (msginfo->extradata->dispositionnotificationto)
1483 memusage += strlen(msginfo->extradata->dispositionnotificationto);
1484 if (msginfo->extradata->returnreceiptto)
1485 memusage += strlen(msginfo->extradata->returnreceiptto);
1487 if (msginfo->extradata->partial_recv)
1488 memusage += strlen(msginfo->extradata->partial_recv);
1489 if (msginfo->extradata->account_server)
1490 memusage += strlen(msginfo->extradata->account_server);
1491 if (msginfo->extradata->account_login)
1492 memusage += strlen(msginfo->extradata->account_login);
1493 if (msginfo->extradata->resent_from)
1494 memusage += strlen(msginfo->extradata->resent_from);
1496 if (msginfo->extradata->list_post)
1497 memusage += strlen(msginfo->extradata->list_post);
1498 if (msginfo->extradata->list_subscribe)
1499 memusage += strlen(msginfo->extradata->list_subscribe);
1500 if (msginfo->extradata->list_unsubscribe)
1501 memusage += strlen(msginfo->extradata->list_unsubscribe);
1502 if (msginfo->extradata->list_help)
1503 memusage += strlen(msginfo->extradata->list_help);
1504 if (msginfo->extradata->list_archive)
1505 memusage += strlen(msginfo->extradata->list_archive);
1506 if (msginfo->extradata->list_owner)
1507 memusage += strlen(msginfo->extradata->list_owner);
1512 static gint procmsg_send_message_queue_full(const gchar *file, gboolean keep_session, gchar **errstr,
1513 FolderItem *queue, gint msgnum, gboolean *queued_removed)
1515 static HeaderEntry qentry[] = {
1516 {"S:", NULL, FALSE}, /* 0 */
1517 {"SSV:", NULL, FALSE},
1518 {"R:", NULL, FALSE},
1519 {"NG:", NULL, FALSE},
1520 {"MAID:", NULL, FALSE},
1521 {"NAID:", NULL, FALSE}, /* 5 */
1522 {"SCF:", NULL, FALSE},
1523 {"RMID:", NULL, FALSE},
1524 {"FMID:", NULL, FALSE},
1525 {"X-Claws-Privacy-System:", NULL, FALSE},
1526 {"X-Claws-Encrypt:", NULL, FALSE}, /* 10 */
1527 {"X-Claws-Encrypt-Data:", NULL, FALSE},
1528 {"X-Claws-End-Special-Headers:", NULL, FALSE},
1529 {"X-Sylpheed-Privacy-System:", NULL, FALSE},
1530 {"X-Sylpheed-Encrypt:", NULL, FALSE},
1531 {"X-Sylpheed-Encrypt-Data:", NULL, FALSE}, /* 15 */
1532 {"X-Sylpheed-End-Special-Headers:", NULL, FALSE},
1533 {NULL, NULL, FALSE}};
1536 gint mailval = 0, newsval = 0;
1538 gchar *smtpserver = NULL;
1539 GSList *to_list = NULL;
1540 GSList *newsgroup_list = NULL;
1541 gchar *savecopyfolder = NULL;
1542 gchar *replymessageid = NULL;
1543 gchar *fwdmessageid = NULL;
1546 PrefsAccount *mailac = NULL, *newsac = NULL;
1547 gboolean encrypt = FALSE;
1550 cm_return_val_if_fail(file != NULL, -1);
1552 if ((fp = claws_fopen(file, "rb")) == NULL) {
1553 FILE_OP_ERROR(file, "claws_fopen");
1555 if (*errstr) g_free(*errstr);
1556 *errstr = g_strdup_printf(_("Couldn't open file %s."), file);
1561 while ((hnum = procheader_get_one_field(&buf, fp, qentry)) != -1 && buf != NULL) {
1562 gchar *p = buf + strlen(qentry[hnum].name);
1570 if (smtpserver == NULL)
1571 smtpserver = g_strdup(p);
1574 to_list = address_list_append(to_list, p);
1577 newsgroup_list = newsgroup_list_append(newsgroup_list, p);
1579 case Q_MAIL_ACCOUNT_ID:
1580 mailac = account_find_from_id(atoi(p));
1582 case Q_NEWS_ACCOUNT_ID:
1583 newsac = account_find_from_id(atoi(p));
1585 case Q_SAVE_COPY_FOLDER:
1586 if (savecopyfolder == NULL)
1587 savecopyfolder = g_strdup(p);
1589 case Q_REPLY_MESSAGE_ID:
1590 if (replymessageid == NULL)
1591 replymessageid = g_strdup(p);
1593 case Q_FWD_MESSAGE_ID:
1594 if (fwdmessageid == NULL)
1595 fwdmessageid = g_strdup(p);
1603 case Q_CLAWS_HDRS_OLD:
1604 /* end of special headers reached */
1606 goto send_mail; /* can't "break;break;" */
1612 filepos = ftell(fp);
1614 FILE_OP_ERROR(file, "ftell");
1616 if (*errstr) g_free(*errstr);
1617 *errstr = g_strdup_printf(_("Couldn't open file %s."), file);
1623 debug_print("Sending message by mail\n");
1626 if (*errstr) g_free(*errstr);
1627 *errstr = g_strdup_printf(_("Queued message header is broken."));
1630 } else if (mailac && mailac->use_mail_command &&
1631 mailac->mail_command && (* mailac->mail_command)) {
1632 mailval = send_message_local(mailac->mail_command, fp);
1635 mailac = account_find_from_smtp_server(from, smtpserver);
1637 g_warning("Account not found. "
1638 "Using current account...");
1639 mailac = cur_account;
1644 mailval = send_message_smtp_full(mailac, to_list, fp, keep_session);
1645 if (mailval == -1 && errstr) {
1646 if (*errstr) g_free(*errstr);
1647 *errstr = g_strdup_printf(_("An error happened during SMTP session."));
1650 PrefsAccount tmp_ac;
1652 g_warning("Account not found.");
1654 memset(&tmp_ac, 0, sizeof(PrefsAccount));
1655 tmp_ac.address = from;
1656 tmp_ac.smtp_server = smtpserver;
1657 tmp_ac.smtpport = SMTP_PORT;
1658 mailval = send_message_smtp(&tmp_ac, to_list, fp);
1659 if (mailval == -1 && errstr) {
1660 if (*errstr) g_free(*errstr);
1661 *errstr = g_strdup_printf(_("No specific account has been found to "
1662 "send, and an error happened during SMTP session."));
1666 } else if (!to_list && !newsgroup_list) {
1668 if (*errstr) g_free(*errstr);
1669 *errstr = g_strdup(_("Couldn't determine sending information. "
1670 "Maybe the email hasn't been generated by Claws Mail."));
1675 if (fseek(fp, filepos, SEEK_SET) < 0) {
1676 FILE_OP_ERROR(file, "fseek");
1680 if (newsgroup_list && newsac && (mailval == 0)) {
1683 gchar buf[BUFFSIZE];
1686 /* write to temporary file */
1687 tmp = g_strdup_printf("%s%cnntp%p", get_tmp_dir(),
1688 G_DIR_SEPARATOR, file);
1689 if ((tmpfp = claws_fopen(tmp, "wb")) == NULL) {
1690 FILE_OP_ERROR(tmp, "claws_fopen");
1692 alertpanel_error(_("Couldn't create temporary file for news sending."));
1694 if (change_file_mode_rw(tmpfp, tmp) < 0) {
1695 FILE_OP_ERROR(tmp, "chmod");
1696 g_warning("can't change file mode");
1699 while ((newsval == 0) && claws_fgets(buf, sizeof(buf), fp) != NULL) {
1700 if (claws_fputs(buf, tmpfp) == EOF) {
1701 FILE_OP_ERROR(tmp, "claws_fputs");
1704 if (*errstr) g_free(*errstr);
1705 *errstr = g_strdup_printf(_("Error when writing temporary file for news sending."));
1709 claws_safe_fclose(tmpfp);
1712 debug_print("Sending message by news\n");
1714 folder = FOLDER(newsac->folder);
1716 newsval = news_post(folder, tmp);
1717 if (newsval < 0 && errstr) {
1718 if (*errstr) g_free(*errstr);
1719 *errstr = g_strdup_printf(_("Error occurred while posting the message to %s."),
1720 newsac->nntp_server);
1730 /* update session statistics */
1731 if (mailval == 0 && newsval == 0) {
1732 /* update session stats */
1734 session_stats.replied++;
1735 else if (fwdmessageid)
1736 session_stats.forwarded++;
1738 session_stats.sent++;
1741 /* save message to outbox */
1742 if (mailval == 0 && newsval == 0 && savecopyfolder) {
1743 debug_print("saving sent message...\n");
1745 if (!encrypt || !mailac->save_encrypted_as_clear_text) {
1746 outbox = folder_find_item_from_identifier(savecopyfolder);
1748 outbox = folder_get_default_outbox();
1750 /* Mail was not saved to outbox before encrypting, save it now. */
1751 gboolean saved = FALSE;
1752 *queued_removed = FALSE;
1753 if (queue && msgnum > 0) {
1754 MsgInfo *queued_mail = folder_item_get_msginfo(queue, msgnum);
1755 if (folder_item_move_msg(outbox, queued_mail) >= 0) {
1756 debug_print("moved queued mail %d to sent folder\n", msgnum);
1758 *queued_removed = TRUE;
1759 } else if (folder_item_copy_msg(outbox, queued_mail) >= 0) {
1760 debug_print("copied queued mail %d to sent folder\n", msgnum);
1763 procmsg_msginfo_free(&queued_mail);
1766 debug_print("resaving queued mail to sent folder\n");
1767 procmsg_save_to_outbox(outbox, file, TRUE);
1772 if (replymessageid != NULL || fwdmessageid != NULL) {
1776 if (replymessageid != NULL)
1777 tokens = g_strsplit(replymessageid, "\t", 0);
1779 tokens = g_strsplit(fwdmessageid, "\t", 0);
1780 item = folder_find_item_from_identifier(tokens[0]);
1782 /* check if queued message has valid folder and message id */
1783 if (item != NULL && tokens[2] != NULL) {
1786 msginfo = folder_item_get_msginfo(item, atoi(tokens[1]));
1788 /* check if referring message exists and has a message id */
1789 if ((msginfo != NULL) &&
1790 (msginfo->msgid != NULL) &&
1791 (strcmp(msginfo->msgid, tokens[2]) != 0)) {
1792 procmsg_msginfo_free(&msginfo);
1796 if (msginfo == NULL) {
1797 msginfo = folder_item_get_msginfo_by_msgid(item, tokens[2]);
1800 if (msginfo != NULL) {
1801 if (replymessageid != NULL) {
1802 MsgPermFlags to_unset = 0;
1804 if (prefs_common.mark_as_read_on_new_window)
1805 to_unset = (MSG_NEW|MSG_UNREAD);
1807 procmsg_msginfo_unset_flags(msginfo, to_unset, 0);
1808 procmsg_msginfo_set_flags(msginfo, MSG_REPLIED, 0);
1810 procmsg_msginfo_set_flags(msginfo, MSG_FORWARDED, 0);
1812 procmsg_msginfo_free(&msginfo);
1820 slist_free_strings_full(to_list);
1821 slist_free_strings_full(newsgroup_list);
1822 g_free(savecopyfolder);
1823 g_free(replymessageid);
1824 g_free(fwdmessageid);
1826 return (newsval != 0 ? newsval : mailval);
1829 gint procmsg_send_message_queue(const gchar *file, gchar **errstr, FolderItem *queue, gint msgnum, gboolean *queued_removed)
1831 gint result = procmsg_send_message_queue_full(file, FALSE, errstr, queue, msgnum, queued_removed);
1832 main_window_set_menu_sensitive(mainwindow_get_mainwindow());
1833 toolbar_main_set_sensitive(mainwindow_get_mainwindow());
1837 gint procmsg_send_message_queue_with_lock(const gchar *file, gchar **errstr, FolderItem *queue, gint msgnum, gboolean *queued_removed)
1840 if (procmsg_queue_lock(errstr)) {
1841 val = procmsg_send_message_queue(file, errstr, queue, msgnum, queued_removed);
1842 procmsg_queue_unlock();
1848 static void update_folder_msg_counts(FolderItem *item, MsgInfo *msginfo, MsgPermFlags old_flags)
1850 MsgPermFlags new_flags = msginfo->flags.perm_flags;
1853 if (!(old_flags & MSG_NEW) && (new_flags & MSG_NEW)) {
1857 if ((old_flags & MSG_NEW) && !(new_flags & MSG_NEW)) {
1862 if (!(old_flags & MSG_UNREAD) && (new_flags & MSG_UNREAD)) {
1863 item->unread_msgs++;
1864 if (procmsg_msg_has_marked_parent(msginfo))
1865 item->unreadmarked_msgs++;
1868 if ((old_flags & MSG_UNREAD) && !(new_flags & MSG_UNREAD)) {
1869 item->unread_msgs--;
1870 if (procmsg_msg_has_marked_parent(msginfo))
1871 item->unreadmarked_msgs--;
1875 if (!(old_flags & MSG_MARKED) && (new_flags & MSG_MARKED)) {
1876 procmsg_update_unread_children(msginfo, TRUE);
1877 item->marked_msgs++;
1880 if ((old_flags & MSG_MARKED) && !(new_flags & MSG_MARKED)) {
1881 procmsg_update_unread_children(msginfo, FALSE);
1882 item->marked_msgs--;
1885 if (!(old_flags & MSG_REPLIED) && (new_flags & MSG_REPLIED)) {
1886 item->replied_msgs++;
1889 if ((old_flags & MSG_REPLIED) && !(new_flags & MSG_REPLIED)) {
1890 item->replied_msgs--;
1893 if (!(old_flags & MSG_FORWARDED) && (new_flags & MSG_FORWARDED)) {
1894 item->forwarded_msgs++;
1897 if ((old_flags & MSG_FORWARDED) && !(new_flags & MSG_FORWARDED)) {
1898 item->forwarded_msgs--;
1901 if (!(old_flags & MSG_LOCKED) && (new_flags & MSG_LOCKED)) {
1902 item->locked_msgs++;
1905 if ((old_flags & MSG_LOCKED) && !(new_flags & MSG_LOCKED)) {
1906 item->locked_msgs--;
1909 if ((old_flags & MSG_IGNORE_THREAD) && !(new_flags & MSG_IGNORE_THREAD)) {
1910 item->ignored_msgs--;
1913 if (!(old_flags & MSG_IGNORE_THREAD) && (new_flags & MSG_IGNORE_THREAD)) {
1914 item->ignored_msgs++;
1917 if ((old_flags & MSG_WATCH_THREAD) && !(new_flags & MSG_WATCH_THREAD)) {
1918 item->watched_msgs--;
1921 if (!(old_flags & MSG_WATCH_THREAD) && (new_flags & MSG_WATCH_THREAD)) {
1922 item->watched_msgs++;
1926 void procmsg_msginfo_set_flags(MsgInfo *msginfo, MsgPermFlags perm_flags, MsgTmpFlags tmp_flags)
1929 MsgInfoUpdate msginfo_update;
1930 MsgPermFlags perm_flags_new, perm_flags_old;
1931 MsgTmpFlags tmp_flags_old;
1933 cm_return_if_fail(msginfo != NULL);
1934 item = msginfo->folder;
1935 cm_return_if_fail(item != NULL);
1937 debug_print("Setting flags for message %d in folder %s\n", msginfo->msgnum, item->path);
1939 /* Perm Flags handling */
1940 perm_flags_old = msginfo->flags.perm_flags;
1941 perm_flags_new = msginfo->flags.perm_flags | perm_flags;
1942 if ((perm_flags & MSG_IGNORE_THREAD) || (perm_flags_old & MSG_IGNORE_THREAD)) {
1943 perm_flags_new &= ~(MSG_NEW | MSG_UNREAD);
1945 if ((perm_flags & MSG_WATCH_THREAD) || (perm_flags_old & MSG_WATCH_THREAD)) {
1946 perm_flags_new &= ~(MSG_IGNORE_THREAD);
1949 if (perm_flags_old != perm_flags_new) {
1950 folder_item_change_msg_flags(msginfo->folder, msginfo, perm_flags_new);
1952 update_folder_msg_counts(item, msginfo, perm_flags_old);
1953 summary_update_unread(mainwindow_get_mainwindow()->summaryview, NULL);
1956 /* Tmp flags handling */
1957 tmp_flags_old = msginfo->flags.tmp_flags;
1958 msginfo->flags.tmp_flags |= tmp_flags;
1960 /* update notification */
1961 if ((perm_flags_old != perm_flags_new) || (tmp_flags_old != msginfo->flags.tmp_flags)) {
1962 msginfo_update.msginfo = msginfo;
1963 msginfo_update.flags = MSGINFO_UPDATE_FLAGS;
1964 hooks_invoke(MSGINFO_UPDATE_HOOKLIST, &msginfo_update);
1965 folder_item_update(msginfo->folder, F_ITEM_UPDATE_MSGCNT);
1969 void procmsg_msginfo_unset_flags(MsgInfo *msginfo, MsgPermFlags perm_flags, MsgTmpFlags tmp_flags)
1972 MsgInfoUpdate msginfo_update;
1973 MsgPermFlags perm_flags_new, perm_flags_old;
1974 MsgTmpFlags tmp_flags_old;
1976 cm_return_if_fail(msginfo != NULL);
1977 item = msginfo->folder;
1978 cm_return_if_fail(item != NULL);
1980 debug_print("Unsetting flags for message %d in folder %s\n", msginfo->msgnum, item->path);
1982 /* Perm Flags handling */
1983 perm_flags_old = msginfo->flags.perm_flags;
1984 perm_flags_new = msginfo->flags.perm_flags & ~perm_flags;
1986 if (perm_flags_old != perm_flags_new) {
1987 folder_item_change_msg_flags(msginfo->folder, msginfo, perm_flags_new);
1989 update_folder_msg_counts(item, msginfo, perm_flags_old);
1992 /* Tmp flags hanlding */
1993 tmp_flags_old = msginfo->flags.tmp_flags;
1994 msginfo->flags.tmp_flags &= ~tmp_flags;
1996 /* update notification */
1997 if ((perm_flags_old != perm_flags_new) || (tmp_flags_old != msginfo->flags.tmp_flags)) {
1998 msginfo_update.msginfo = msginfo;
1999 msginfo_update.flags = MSGINFO_UPDATE_FLAGS;
2000 hooks_invoke(MSGINFO_UPDATE_HOOKLIST, &msginfo_update);
2001 folder_item_update(msginfo->folder, F_ITEM_UPDATE_MSGCNT);
2005 void procmsg_msginfo_change_flags(MsgInfo *msginfo,
2006 MsgPermFlags add_perm_flags, MsgTmpFlags add_tmp_flags,
2007 MsgPermFlags rem_perm_flags, MsgTmpFlags rem_tmp_flags)
2010 MsgInfoUpdate msginfo_update;
2011 MsgPermFlags perm_flags_new, perm_flags_old;
2012 MsgTmpFlags tmp_flags_old;
2014 cm_return_if_fail(msginfo != NULL);
2015 item = msginfo->folder;
2016 cm_return_if_fail(item != NULL);
2018 debug_print("Changing flags for message %d in folder %s\n", msginfo->msgnum, item->path);
2020 /* Perm Flags handling */
2021 perm_flags_old = msginfo->flags.perm_flags;
2022 perm_flags_new = (msginfo->flags.perm_flags & ~rem_perm_flags) | add_perm_flags;
2023 if ((add_perm_flags & MSG_IGNORE_THREAD) || (perm_flags_old & MSG_IGNORE_THREAD)) {
2024 perm_flags_new &= ~(MSG_NEW | MSG_UNREAD);
2026 if ((add_perm_flags & MSG_WATCH_THREAD) || (perm_flags_old & MSG_WATCH_THREAD)) {
2027 perm_flags_new &= ~(MSG_IGNORE_THREAD);
2030 if (perm_flags_old != perm_flags_new) {
2031 folder_item_change_msg_flags(msginfo->folder, msginfo, perm_flags_new);
2033 update_folder_msg_counts(item, msginfo, perm_flags_old);
2037 /* Tmp flags handling */
2038 tmp_flags_old = msginfo->flags.tmp_flags;
2039 msginfo->flags.tmp_flags &= ~rem_tmp_flags;
2040 msginfo->flags.tmp_flags |= add_tmp_flags;
2042 /* update notification */
2043 if ((perm_flags_old != perm_flags_new) || (tmp_flags_old != msginfo->flags.tmp_flags)) {
2044 msginfo_update.msginfo = msginfo;
2045 msginfo_update.flags = MSGINFO_UPDATE_FLAGS;
2046 hooks_invoke(MSGINFO_UPDATE_HOOKLIST, &msginfo_update);
2047 folder_item_update(msginfo->folder, F_ITEM_UPDATE_MSGCNT);
2052 *\brief check for flags (e.g. mark) in prior msgs of current thread
2054 *\param info Current message
2055 *\param perm_flags Flags to be checked
2056 *\param parentmsgs Hash of prior msgs to avoid loops
2058 *\return gboolean TRUE if perm_flags are found
2060 static gboolean procmsg_msg_has_flagged_parent_real(MsgInfo *info,
2061 MsgPermFlags perm_flags, GHashTable *parentmsgs)
2065 cm_return_val_if_fail(info != NULL, FALSE);
2067 if (info != NULL && info->folder != NULL && info->inreplyto != NULL) {
2068 tmp = folder_item_get_msginfo_by_msgid(info->folder,
2070 if (tmp && (tmp->flags.perm_flags & perm_flags)) {
2071 procmsg_msginfo_free(&tmp);
2073 } else if (tmp != NULL) {
2076 if (g_hash_table_lookup(parentmsgs, info)) {
2077 debug_print("loop detected: %d\n",
2081 g_hash_table_insert(parentmsgs, info, "1");
2082 result = procmsg_msg_has_flagged_parent_real(
2083 tmp, perm_flags, parentmsgs);
2085 procmsg_msginfo_free(&tmp);
2095 *\brief Callback for cleaning up hash of parentmsgs
2097 static gboolean parentmsgs_hash_remove(gpointer key,
2105 *\brief Set up list of parentmsgs
2106 * See procmsg_msg_has_flagged_parent_real()
2108 gboolean procmsg_msg_has_flagged_parent(MsgInfo *info, MsgPermFlags perm_flags)
2111 static GHashTable *parentmsgs = NULL;
2113 if (parentmsgs == NULL)
2114 parentmsgs = g_hash_table_new(NULL, NULL);
2116 result = procmsg_msg_has_flagged_parent_real(info, perm_flags, parentmsgs);
2117 g_hash_table_foreach_remove(parentmsgs, parentmsgs_hash_remove, NULL);
2123 *\brief Check if msgs prior in thread are marked
2124 * See procmsg_msg_has_flagged_parent_real()
2126 gboolean procmsg_msg_has_marked_parent(MsgInfo *info)
2128 return procmsg_msg_has_flagged_parent(info, MSG_MARKED);
2132 static GSList *procmsg_find_children_func(MsgInfo *info,
2133 GSList *children, GSList *all)
2137 cm_return_val_if_fail(info!=NULL, children);
2138 if (info->msgid == NULL)
2141 for (cur = all; cur != NULL; cur = g_slist_next(cur)) {
2142 MsgInfo *tmp = (MsgInfo *)cur->data;
2143 if (tmp->inreplyto && !strcmp(tmp->inreplyto, info->msgid)) {
2144 /* Check if message is already in the list */
2145 if ((children == NULL) ||
2146 (g_slist_index(children, tmp) == -1)) {
2147 children = g_slist_prepend(children,
2148 procmsg_msginfo_new_ref(tmp));
2149 children = procmsg_find_children_func(tmp,
2158 static GSList *procmsg_find_children (MsgInfo *info)
2163 cm_return_val_if_fail(info!=NULL, NULL);
2164 all = folder_item_get_msg_list(info->folder);
2165 children = procmsg_find_children_func(info, NULL, all);
2166 if (children != NULL) {
2167 for (cur = all; cur != NULL; cur = g_slist_next(cur)) {
2168 /* this will not free the used pointers
2169 created with procmsg_msginfo_new_ref */
2170 procmsg_msginfo_free((MsgInfo **)&(cur->data));
2178 static void procmsg_update_unread_children(MsgInfo *info, gboolean newly_marked)
2180 GSList *children = procmsg_find_children(info);
2182 for (cur = children; cur != NULL; cur = g_slist_next(cur)) {
2183 MsgInfo *tmp = (MsgInfo *)cur->data;
2184 if(MSG_IS_UNREAD(tmp->flags) && !MSG_IS_IGNORE_THREAD(tmp->flags)) {
2186 info->folder->unreadmarked_msgs++;
2188 info->folder->unreadmarked_msgs--;
2189 folder_item_update(info->folder, F_ITEM_UPDATE_MSGCNT);
2191 procmsg_msginfo_free(&tmp);
2193 g_slist_free(children);
2197 * Set the destination folder for a copy or move operation
2199 * \param msginfo The message which's destination folder is changed
2200 * \param to_folder The destination folder for the operation
2202 void procmsg_msginfo_set_to_folder(MsgInfo *msginfo, FolderItem *to_folder)
2204 if(msginfo->to_folder != NULL) {
2205 msginfo->to_folder->op_count--;
2206 folder_item_update(msginfo->to_folder, F_ITEM_UPDATE_MSGCNT);
2208 msginfo->to_folder = to_folder;
2209 if(to_folder != NULL) {
2210 to_folder->op_count++;
2211 folder_item_update(msginfo->to_folder, F_ITEM_UPDATE_MSGCNT);
2216 * Apply filtering actions to the msginfo
2218 * \param msginfo The MsgInfo describing the message that should be filtered
2219 * \return TRUE if the message was moved and MsgInfo is now invalid,
2222 static gboolean procmsg_msginfo_filter(MsgInfo *msginfo, PrefsAccount* ac_prefs)
2224 MailFilteringData mail_filtering_data;
2226 mail_filtering_data.msginfo = msginfo;
2227 mail_filtering_data.msglist = NULL;
2228 mail_filtering_data.filtered = NULL;
2229 mail_filtering_data.unfiltered = NULL;
2230 mail_filtering_data.account = ac_prefs;
2232 if (!ac_prefs || ac_prefs->filterhook_on_recv)
2233 if (hooks_invoke(MAIL_FILTERING_HOOKLIST, &mail_filtering_data))
2236 /* filter if enabled in prefs or move to inbox if not */
2237 if((filtering_rules != NULL) &&
2238 filter_message_by_msginfo(filtering_rules, msginfo, ac_prefs,
2239 FILTERING_INCORPORATION, NULL)) {
2246 void procmsg_msglist_filter(GSList *list, PrefsAccount *ac,
2247 GSList **filtered, GSList **unfiltered,
2250 GSList *cur, *to_do = NULL;
2251 gint total = 0, curnum = 0;
2252 MailFilteringData mail_filtering_data;
2254 cm_return_if_fail(filtered != NULL);
2255 cm_return_if_fail(unfiltered != NULL);
2263 total = g_slist_length(list);
2267 *unfiltered = g_slist_copy(list);
2271 statusbar_print_all(_("Filtering messages...\n"));
2273 mail_filtering_data.msginfo = NULL;
2274 mail_filtering_data.msglist = list;
2275 mail_filtering_data.filtered = NULL;
2276 mail_filtering_data.unfiltered = NULL;
2277 mail_filtering_data.account = ac;
2279 if (!ac || ac->filterhook_on_recv)
2280 hooks_invoke(MAIL_LISTFILTERING_HOOKLIST, &mail_filtering_data);
2282 if (mail_filtering_data.filtered == NULL &&
2283 mail_filtering_data.unfiltered == NULL) {
2284 /* nothing happened */
2285 debug_print(MAIL_LISTFILTERING_HOOKLIST " did nothing. filtering whole list normally.\n");
2288 if (mail_filtering_data.filtered != NULL) {
2289 /* keep track of what's been filtered by the hooks */
2290 debug_print(MAIL_LISTFILTERING_HOOKLIST " filtered some stuff. total %d filtered %d unfilt %d.\n",
2291 g_slist_length(list),
2292 g_slist_length(mail_filtering_data.filtered),
2293 g_slist_length(mail_filtering_data.unfiltered));
2295 *filtered = g_slist_copy(mail_filtering_data.filtered);
2297 if (mail_filtering_data.unfiltered != NULL) {
2298 /* what the hooks didn't handle will go in filtered or
2299 * unfiltered in the next loop */
2300 debug_print(MAIL_LISTFILTERING_HOOKLIST " left unfiltered stuff. total %d filtered %d unfilt %d.\n",
2301 g_slist_length(list),
2302 g_slist_length(mail_filtering_data.filtered),
2303 g_slist_length(mail_filtering_data.unfiltered));
2304 to_do = mail_filtering_data.unfiltered;
2307 for (cur = to_do; cur; cur = cur->next) {
2308 MsgInfo *info = (MsgInfo *)cur->data;
2309 if (procmsg_msginfo_filter(info, ac))
2310 *filtered = g_slist_prepend(*filtered, info);
2312 *unfiltered = g_slist_prepend(*unfiltered, info);
2313 statusbar_progress_all(curnum++, total, prefs_common.statusbar_update_step);
2316 g_slist_free(mail_filtering_data.filtered);
2317 g_slist_free(mail_filtering_data.unfiltered);
2319 *filtered = g_slist_reverse(*filtered);
2320 *unfiltered = g_slist_reverse(*unfiltered);
2322 statusbar_progress_all(0,0,0);
2323 statusbar_pop_all();
2326 MsgInfo *procmsg_msginfo_new_from_mimeinfo(MsgInfo *src_msginfo, MimeInfo *mimeinfo)
2328 MsgInfo *tmp_msginfo = NULL;
2329 MsgFlags flags = {0, 0};
2330 gchar *tmpfile = get_tmp_file();
2331 FILE *fp = claws_fopen(tmpfile, "wb");
2333 if (!mimeinfo || mimeinfo->type != MIMETYPE_MESSAGE ||
2334 g_ascii_strcasecmp(mimeinfo->subtype, "rfc822")) {
2335 g_warning("procmsg_msginfo_new_from_mimeinfo(): unsuitable mimeinfo");
2342 if (fp && procmime_write_mimeinfo(mimeinfo, fp) >= 0) {
2343 claws_safe_fclose(fp);
2345 tmp_msginfo = procheader_parse_file(
2350 claws_safe_fclose(fp);
2352 if (tmp_msginfo != NULL) {
2354 tmp_msginfo->folder = src_msginfo->folder;
2355 tmp_msginfo->plaintext_file = g_strdup(tmpfile);
2357 g_warning("procmsg_msginfo_new_from_mimeinfo(): Can't generate new msginfo");
2365 static GSList *spam_learners = NULL;
2367 void procmsg_register_spam_learner (int (*learn_func)(MsgInfo *info, GSList *list, gboolean spam))
2369 if (!g_slist_find(spam_learners, learn_func))
2370 spam_learners = g_slist_append(spam_learners, learn_func);
2371 if (mainwindow_get_mainwindow()) {
2372 main_window_set_menu_sensitive(mainwindow_get_mainwindow());
2373 summary_set_menu_sensitive(
2374 mainwindow_get_mainwindow()->summaryview);
2375 toolbar_main_set_sensitive(mainwindow_get_mainwindow());
2379 void procmsg_unregister_spam_learner (int (*learn_func)(MsgInfo *info, GSList *list, gboolean spam))
2381 spam_learners = g_slist_remove(spam_learners, learn_func);
2382 if (mainwindow_get_mainwindow()) {
2383 main_window_set_menu_sensitive(mainwindow_get_mainwindow());
2384 summary_set_menu_sensitive(
2385 mainwindow_get_mainwindow()->summaryview);
2386 toolbar_main_set_sensitive(mainwindow_get_mainwindow());
2390 gboolean procmsg_spam_can_learn(void)
2392 return g_slist_length(spam_learners) > 0;
2395 int procmsg_spam_learner_learn (MsgInfo *info, GSList *list, gboolean spam)
2397 GSList *cur = spam_learners;
2399 for (; cur; cur = cur->next) {
2400 int ((*func)(MsgInfo *info, GSList *list, gboolean spam)) = cur->data;
2401 ret |= func(info, list, spam);
2406 static gchar *spam_folder_item = NULL;
2407 static FolderItem * (*procmsg_spam_get_folder_func)(MsgInfo *msginfo) = NULL;
2408 void procmsg_spam_set_folder (const char *item_identifier, FolderItem *(*spam_get_folder_func)(MsgInfo *info))
2410 g_free(spam_folder_item);
2411 if (item_identifier)
2412 spam_folder_item = g_strdup(item_identifier);
2414 spam_folder_item = NULL;
2415 if (spam_get_folder_func != NULL)
2416 procmsg_spam_get_folder_func = spam_get_folder_func;
2418 procmsg_spam_get_folder_func = NULL;
2421 FolderItem *procmsg_spam_get_folder (MsgInfo *msginfo)
2423 FolderItem *item = NULL;
2425 if (procmsg_spam_get_folder_func)
2426 item = procmsg_spam_get_folder_func(msginfo);
2427 if (item == NULL && spam_folder_item)
2428 item = folder_find_item_from_identifier(spam_folder_item);
2430 item = folder_get_default_trash();
2434 static void item_has_queued_mails(FolderItem *item, gpointer data)
2436 gboolean *result = (gboolean *)data;
2437 if (*result == TRUE)
2439 if (folder_has_parent_of_type(item, F_QUEUE)) {
2440 if (item->total_msgs == 0)
2443 GSList *msglist = folder_item_get_msg_list(item);
2445 for (cur = msglist; cur; cur = cur->next) {
2446 MsgInfo *msginfo = (MsgInfo *)cur->data;
2447 if (!MSG_IS_DELETED(msginfo->flags) &&
2448 !MSG_IS_LOCKED(msginfo->flags)) {
2453 procmsg_msg_list_free(msglist);
2458 gboolean procmsg_have_queued_mails_fast (void)
2460 gboolean result = FALSE;
2461 folder_func_to_all_folders(item_has_queued_mails, &result);
2465 static void item_has_trashed_mails(FolderItem *item, gpointer data)
2467 gboolean *result = (gboolean *)data;
2468 if (*result == TRUE)
2470 if (folder_has_parent_of_type(item, F_TRASH) && item->total_msgs > 0)
2474 gboolean procmsg_have_trashed_mails_fast (void)
2476 gboolean result = FALSE;
2477 folder_func_to_all_folders(item_has_trashed_mails, &result);
2481 gchar *procmsg_msginfo_get_tags_str(MsgInfo *msginfo)
2489 if (msginfo->tags == NULL)
2491 for (cur = msginfo->tags; cur; cur = cur->next) {
2492 const gchar *tag = tags_get_tag(GPOINTER_TO_INT(cur->data));
2496 tags = g_strdup(tag);
2498 int olen = strlen(tags);
2499 int nlen = olen + strlen(tag) + 2 /* strlen(", ") */;
2500 tags = g_realloc(tags, nlen+1);
2503 strcpy(tags+olen, ", ");
2504 strcpy(tags+olen+2, tag);
2511 void procmsg_msginfo_update_tags(MsgInfo *msginfo, gboolean set, gint id)
2519 msginfo->tags = g_slist_remove(
2521 GINT_TO_POINTER(id));
2522 changed.data = GINT_TO_POINTER(id);
2523 changed.next = NULL;
2524 folder_item_commit_tags(msginfo->folder, msginfo, NULL, &changed);
2526 if (!g_slist_find(msginfo->tags, GINT_TO_POINTER(id))) {
2527 msginfo->tags = g_slist_append(
2529 GINT_TO_POINTER(id));
2531 changed.data = GINT_TO_POINTER(id);
2532 changed.next = NULL;
2533 folder_item_commit_tags(msginfo->folder, msginfo, &changed, NULL);
2538 void procmsg_msginfo_clear_tags(MsgInfo *msginfo)
2540 GSList *unset = msginfo->tags;
2541 msginfo->tags = NULL;
2542 folder_item_commit_tags(msginfo->folder, msginfo, NULL, unset);
2543 g_slist_free(unset);