2 * Sylpheed -- a GTK+ based, lightweight, and fast e-mail client
3 * Copyright (C) 1999-2009 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/>.
23 #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 static gint procmsg_send_message_queue_full(const gchar *file, gboolean keep_session, gchar **errstr,
53 FolderItem *queue, gint msgnum, gboolean *queued_removed);
54 static void procmsg_update_unread_children (MsgInfo *info,
55 gboolean newly_marked);
62 Q_MAIL_ACCOUNT_ID = 4,
63 Q_NEWS_ACCOUNT_ID = 5,
64 Q_SAVE_COPY_FOLDER = 6,
65 Q_REPLY_MESSAGE_ID = 7,
71 Q_PRIVACY_SYSTEM_OLD = 13,
73 Q_ENCRYPT_DATA_OLD = 15,
74 Q_CLAWS_HDRS_OLD = 16,
77 void procmsg_msg_list_free(GSList *mlist)
82 for (cur = mlist; cur != NULL; cur = cur->next) {
83 msginfo = (MsgInfo *)cur->data;
84 procmsg_msginfo_free(msginfo);
98 /* CLAWS subject threading:
100 in the first round it inserts subject lines in a
101 relation (subject <-> node)
103 the second round finishes the threads by attaching
104 matching subject lines to the one found in the
105 relation. will use the oldest node with the same
106 subject that is not more then thread_by_subject_max_age
107 days old (see subject_relation_lookup)
110 static void subject_relation_insert(GRelation *relation, GNode *node)
115 cm_return_if_fail(relation != NULL);
116 cm_return_if_fail(node != NULL);
117 msginfo = (MsgInfo *) node->data;
118 cm_return_if_fail(msginfo != NULL);
120 subject = msginfo->subject;
123 subject += subject_get_prefix_length(subject);
125 g_relation_insert(relation, subject, node);
128 static GNode *subject_relation_lookup(GRelation *relation, MsgInfo *msginfo)
135 cm_return_val_if_fail(relation != NULL, NULL);
137 subject = msginfo->subject;
140 prefix_length = subject_get_prefix_length(subject);
141 if (prefix_length <= 0)
143 subject += prefix_length;
145 tuples = g_relation_select(relation, subject, 0);
149 if (tuples->len > 0) {
151 GNode *relation_node;
152 MsgInfo *relation_msginfo = NULL, *best_msginfo = NULL;
155 /* check all nodes with the same subject to find the best parent */
156 for (i = 0; i < tuples->len; i++) {
157 relation_node = (GNode *) g_tuples_index(tuples, i, 1);
158 relation_msginfo = (MsgInfo *) relation_node->data;
161 /* best node should be the oldest in the found nodes */
162 /* parent node must not be older then msginfo */
163 if ((relation_msginfo->date_t < msginfo->date_t) &&
164 ((best_msginfo == NULL) ||
165 (best_msginfo->date_t > relation_msginfo->date_t)))
168 /* parent node must not be more then thread_by_subject_max_age
169 days older then msginfo */
170 if (abs(difftime(msginfo->date_t, relation_msginfo->date_t)) >
171 prefs_common.thread_by_subject_max_age * 3600 * 24)
174 /* can add new tests for all matching
175 nodes found by subject */
178 node = relation_node;
179 best_msginfo = relation_msginfo;
184 g_tuples_destroy(tuples);
188 /* return the reversed thread tree */
189 GNode *procmsg_get_thread_tree(GSList *mlist)
191 GNode *root, *parent, *node, *next;
192 GHashTable *msgid_table;
193 GRelation *subject_relation = NULL;
198 root = g_node_new(NULL);
199 msgid_table = g_hash_table_new(g_str_hash, g_str_equal);
201 if (prefs_common.thread_by_subject) {
202 subject_relation = g_relation_new(2);
203 g_relation_index(subject_relation, 0, g_str_hash, g_str_equal);
206 for (; mlist != NULL; mlist = mlist->next) {
207 msginfo = (MsgInfo *)mlist->data;
210 if (msginfo->inreplyto) {
211 parent = g_hash_table_lookup(msgid_table, msginfo->inreplyto);
212 if (parent == NULL) {
216 node = g_node_insert_data_before
217 (parent, parent == root ? parent->children : NULL,
219 if ((msgid = msginfo->msgid) && g_hash_table_lookup(msgid_table, msgid) == NULL)
220 g_hash_table_insert(msgid_table, (gchar *)msgid, node);
222 /* CLAWS: add subject to relation (without prefix) */
223 if (prefs_common.thread_by_subject) {
224 subject_relation_insert(subject_relation, node);
228 /* complete the unfinished threads */
229 for (node = root->children; node != NULL; ) {
231 msginfo = (MsgInfo *)node->data;
234 if (msginfo->inreplyto)
235 parent = g_hash_table_lookup(msgid_table, msginfo->inreplyto);
237 /* try looking for the indirect parent */
238 if (!parent && msginfo->references) {
239 for (reflist = msginfo->references;
240 reflist != NULL; reflist = reflist->next)
241 if ((parent = g_hash_table_lookup
242 (msgid_table, reflist->data)) != NULL)
246 /* node should not be the parent, and node should not
247 be an ancestor of parent (circular reference) */
248 if (parent && parent != node &&
249 !g_node_is_ancestor(node, parent)) {
252 (parent, parent->children, node);
258 if (prefs_common.thread_by_subject) {
259 START_TIMING("thread by subject");
260 for (node = root->children; node && node != NULL;) {
262 msginfo = (MsgInfo *) node->data;
264 parent = subject_relation_lookup(subject_relation, msginfo);
266 /* the node may already be threaded by IN-REPLY-TO, so go up
268 find the parent node */
269 if (parent != NULL) {
270 if (g_node_is_ancestor(node, parent))
278 g_node_append(parent, node);
286 if (prefs_common.thread_by_subject)
287 g_relation_destroy(subject_relation);
289 g_hash_table_destroy(msgid_table);
294 gint procmsg_move_messages(GSList *mlist)
296 GSList *cur, *movelist = NULL;
298 FolderItem *dest = NULL;
300 gboolean finished = TRUE;
301 if (!mlist) return 0;
303 folder_item_update_freeze();
306 for (cur = mlist; cur != NULL; cur = cur->next) {
307 msginfo = (MsgInfo *)cur->data;
308 if (!msginfo->to_folder) {
314 dest = msginfo->to_folder;
315 movelist = g_slist_prepend(movelist, msginfo);
316 } else if (dest == msginfo->to_folder) {
317 movelist = g_slist_prepend(movelist, msginfo);
321 procmsg_msginfo_set_to_folder(msginfo, NULL);
324 movelist = g_slist_reverse(movelist);
325 retval |= folder_item_move_msgs(dest, movelist);
326 g_slist_free(movelist);
329 if (finished == FALSE) {
335 folder_item_update_thaw();
339 void procmsg_copy_messages(GSList *mlist)
341 GSList *cur, *copylist = NULL;
343 FolderItem *dest = NULL;
344 gboolean finished = TRUE;
347 folder_item_update_freeze();
350 for (cur = mlist; cur != NULL; cur = cur->next) {
351 msginfo = (MsgInfo *)cur->data;
352 if (!msginfo->to_folder) {
358 dest = msginfo->to_folder;
359 copylist = g_slist_prepend(copylist, msginfo);
360 } else if (dest == msginfo->to_folder) {
361 copylist = g_slist_prepend(copylist, msginfo);
365 procmsg_msginfo_set_to_folder(msginfo, NULL);
368 copylist = g_slist_reverse(copylist);
369 folder_item_copy_msgs(dest, copylist);
370 g_slist_free(copylist);
373 if (finished == FALSE) {
379 folder_item_update_thaw();
382 gchar *procmsg_get_message_file_path(MsgInfo *msginfo)
386 cm_return_val_if_fail(msginfo != NULL, NULL);
388 if (msginfo->plaintext_file)
389 file = g_strdup(msginfo->plaintext_file);
391 file = folder_item_fetch_msg(msginfo->folder, msginfo->msgnum);
397 gchar *procmsg_get_message_file(MsgInfo *msginfo)
399 gchar *filename = NULL;
401 cm_return_val_if_fail(msginfo != NULL, NULL);
403 filename = folder_item_fetch_msg(msginfo->folder, msginfo->msgnum);
405 debug_print("can't fetch message %d\n", msginfo->msgnum);
410 gchar *procmsg_get_message_file_full(MsgInfo *msginfo, gboolean headers, gboolean body)
412 gchar *filename = NULL;
414 cm_return_val_if_fail(msginfo != NULL, NULL);
416 filename = folder_item_fetch_msg_full(msginfo->folder, msginfo->msgnum,
419 debug_print("can't fetch message %d\n", msginfo->msgnum);
424 GSList *procmsg_get_message_file_list(GSList *mlist)
426 GSList *file_list = NULL;
428 MsgFileInfo *fileinfo;
431 while (mlist != NULL) {
432 msginfo = (MsgInfo *)mlist->data;
433 file = procmsg_get_message_file(msginfo);
435 procmsg_message_file_list_free(file_list);
438 fileinfo = g_new(MsgFileInfo, 1);
439 fileinfo->msginfo = procmsg_msginfo_new_ref(msginfo);
440 fileinfo->file = file;
441 fileinfo->flags = g_new(MsgFlags, 1);
442 *fileinfo->flags = msginfo->flags;
443 file_list = g_slist_prepend(file_list, fileinfo);
447 file_list = g_slist_reverse(file_list);
452 void procmsg_message_file_list_free(MsgInfoList *file_list)
455 MsgFileInfo *fileinfo;
457 for (cur = file_list; cur != NULL; cur = cur->next) {
458 fileinfo = (MsgFileInfo *)cur->data;
459 procmsg_msginfo_free(fileinfo->msginfo);
460 g_free(fileinfo->file);
461 g_free(fileinfo->flags);
465 g_slist_free(file_list);
468 FILE *procmsg_open_message(MsgInfo *msginfo)
473 cm_return_val_if_fail(msginfo != NULL, NULL);
475 file = procmsg_get_message_file_path(msginfo);
476 cm_return_val_if_fail(file != NULL, NULL);
478 if (!is_file_exist(file)) {
480 file = procmsg_get_message_file(msginfo);
485 if ((fp = g_fopen(file, "rb")) == NULL) {
486 FILE_OP_ERROR(file, "fopen");
493 if (MSG_IS_QUEUED(msginfo->flags) || MSG_IS_DRAFT(msginfo->flags)) {
496 while (fgets(buf, sizeof(buf), fp) != NULL) {
498 if ((!strncmp(buf, "X-Claws-End-Special-Headers: 1",
499 strlen("X-Claws-End-Special-Headers:"))) ||
500 (!strncmp(buf, "X-Sylpheed-End-Special-Headers: 1",
501 strlen("X-Sylpheed-End-Special-Headers:"))))
504 if (buf[0] == '\r' || buf[0] == '\n') break;
505 /* from other mailers */
506 if (!strncmp(buf, "Date: ", 6)
507 || !strncmp(buf, "To: ", 4)
508 || !strncmp(buf, "From: ", 6)
509 || !strncmp(buf, "Subject: ", 9)) {
519 gboolean procmsg_msg_exist(MsgInfo *msginfo)
524 if (!msginfo) return FALSE;
526 path = folder_item_get_path(msginfo->folder);
528 ret = !folder_item_is_msg_changed(msginfo->folder, msginfo);
534 void procmsg_get_filter_keyword(MsgInfo *msginfo, gchar **header, gchar **key,
535 PrefsFilterType type)
537 static HeaderEntry hentry[] = {{"X-BeenThere:", NULL, TRUE},
538 {"X-ML-Name:", NULL, TRUE},
539 {"X-List:", NULL, TRUE},
540 {"X-Mailing-list:", NULL, TRUE},
541 {"List-Id:", NULL, TRUE},
542 {"X-Sequence:", NULL, TRUE},
543 {"Sender:", NULL, TRUE},
544 {"List-Post:", NULL, TRUE},
545 {NULL, NULL, FALSE}};
551 H_X_MAILING_LIST = 3,
560 cm_return_if_fail(msginfo != NULL);
561 cm_return_if_fail(header != NULL);
562 cm_return_if_fail(key != NULL);
571 if ((fp = procmsg_open_message(msginfo)) == NULL)
573 procheader_get_header_fields(fp, hentry);
576 #define SET_FILTER_KEY(hstr, idx) \
578 *header = g_strdup(hstr); \
579 *key = hentry[idx].body; \
580 hentry[idx].body = NULL; \
583 if (hentry[H_LIST_ID].body != NULL) {
584 SET_FILTER_KEY("header \"List-Id\"", H_LIST_ID);
585 extract_list_id_str(*key);
586 } else if (hentry[H_X_BEENTHERE].body != NULL) {
587 SET_FILTER_KEY("header \"X-BeenThere\"", H_X_BEENTHERE);
588 } else if (hentry[H_X_ML_NAME].body != NULL) {
589 SET_FILTER_KEY("header \"X-ML-Name\"", H_X_ML_NAME);
590 } else if (hentry[H_X_LIST].body != NULL) {
591 SET_FILTER_KEY("header \"X-List\"", H_X_LIST);
592 } else if (hentry[H_X_MAILING_LIST].body != NULL) {
593 SET_FILTER_KEY("header \"X-Mailing-List\"", H_X_MAILING_LIST);
594 } else if (hentry[H_X_SEQUENCE].body != NULL) {
597 SET_FILTER_KEY("X-Sequence", H_X_SEQUENCE);
600 while (*p != '\0' && !g_ascii_isspace(*p)) p++;
601 while (g_ascii_isspace(*p)) p++;
602 if (g_ascii_isdigit(*p)) {
608 } else if (hentry[H_SENDER].body != NULL) {
609 SET_FILTER_KEY("header \"Sender\"", H_SENDER);
610 } else if (hentry[H_LIST_POST].body != NULL) {
611 SET_FILTER_KEY("header \"List-Post\"", H_LIST_POST);
612 } else if (msginfo->to) {
613 *header = g_strdup("to");
614 *key = g_strdup(msginfo->to);
615 } else if (msginfo->subject) {
616 *header = g_strdup("subject");
617 *key = g_strdup(msginfo->subject);
620 #undef SET_FILTER_KEY
622 g_free(hentry[H_X_BEENTHERE].body);
623 hentry[H_X_BEENTHERE].body = NULL;
624 g_free(hentry[H_X_ML_NAME].body);
625 hentry[H_X_ML_NAME].body = NULL;
626 g_free(hentry[H_X_LIST].body);
627 hentry[H_X_LIST].body = NULL;
628 g_free(hentry[H_X_MAILING_LIST].body);
629 hentry[H_X_MAILING_LIST].body = NULL;
630 g_free(hentry[H_LIST_ID].body);
631 hentry[H_LIST_ID].body = NULL;
632 g_free(hentry[H_SENDER].body);
633 hentry[H_SENDER].body = NULL;
634 g_free(hentry[H_LIST_POST].body);
635 hentry[H_LIST_POST].body = NULL;
639 *header = g_strdup("from");
640 *key = g_strdup(msginfo->from);
643 *header = g_strdup("to");
644 *key = g_strdup(msginfo->to);
646 case FILTER_BY_SUBJECT:
647 *header = g_strdup("subject");
648 *key = g_strdup(msginfo->subject);
655 static void procmsg_empty_trash(FolderItem *trash)
660 (trash->stype != F_TRASH &&
661 !folder_has_parent_of_type(trash, F_TRASH)))
664 if (trash && trash->total_msgs > 0) {
665 GSList *mlist = folder_item_get_msg_list(trash);
667 for (cur = mlist ; cur != NULL ; cur = cur->next) {
668 MsgInfo * msginfo = (MsgInfo *) cur->data;
669 if (MSG_IS_LOCKED(msginfo->flags)) {
670 procmsg_msginfo_free(msginfo);
673 if (msginfo->total_size != 0 &&
674 msginfo->size != (off_t)msginfo->total_size)
675 partial_mark_for_delete(msginfo);
677 procmsg_msginfo_free(msginfo);
680 folder_item_remove_all_msg(trash);
683 if (!trash->node || !trash->node->children)
686 node = trash->node->children;
687 while (node != NULL) {
689 procmsg_empty_trash(FOLDER_ITEM(node->data));
694 void procmsg_empty_all_trash(void)
699 for (cur = folder_get_list(); cur != NULL; cur = cur->next) {
700 Folder *folder = FOLDER(cur->data);
701 trash = folder->trash;
702 procmsg_empty_trash(trash);
703 if (folder->account && folder->account->set_trash_folder &&
704 folder_find_item_from_identifier(folder->account->trash_folder))
706 folder_find_item_from_identifier(folder->account->trash_folder));
710 static PrefsAccount *procmsg_get_account_from_file(const gchar *file)
712 PrefsAccount *mailac = NULL;
716 static HeaderEntry qentry[] = {{"S:", NULL, FALSE},
717 {"SSV:", NULL, FALSE},
719 {"NG:", NULL, FALSE},
720 {"MAID:", NULL, FALSE},
721 {"NAID:", NULL, FALSE},
722 {"SCF:", NULL, FALSE},
723 {"RMID:", NULL, FALSE},
724 {"FMID:", NULL, FALSE},
725 {"X-Claws-Privacy-System:", NULL, FALSE},
726 {"X-Claws-Encrypt:", NULL, FALSE},
727 {"X-Claws-Encrypt-Data:", NULL, FALSE},
728 {"X-Claws-End-Special-Headers", NULL, FALSE},
729 {"X-Sylpheed-Privacy-System:", NULL, FALSE},
730 {"X-Sylpheed-Encrypt:", NULL, FALSE},
731 {"X-Sylpheed-Encrypt-Data:", NULL, FALSE},
732 {NULL, NULL, FALSE}};
734 cm_return_val_if_fail(file != NULL, NULL);
736 if ((fp = g_fopen(file, "rb")) == NULL) {
737 FILE_OP_ERROR(file, "fopen");
741 while ((hnum = procheader_get_one_field(buf, sizeof(buf), fp, qentry))
743 gchar *p = buf + strlen(qentry[hnum].name);
745 if (hnum == Q_MAIL_ACCOUNT_ID) {
746 mailac = account_find_from_id(atoi(p));
754 static GSList *procmsg_list_sort_by_account(FolderItem *queue, GSList *list)
756 GSList *result = NULL;
758 PrefsAccount *last_account = NULL;
761 gboolean nothing_to_sort = TRUE;
766 orig = g_slist_copy(list);
768 msg = (MsgInfo *)orig->data;
770 for (cur = orig; cur; cur = cur->next)
771 debug_print("sort before %s\n", ((MsgInfo *)cur->data)->from);
776 nothing_to_sort = TRUE;
780 PrefsAccount *ac = NULL;
781 msg = (MsgInfo *)cur->data;
782 file = folder_item_fetch_msg(queue, msg->msgnum);
783 ac = procmsg_get_account_from_file(file);
786 if (last_account == NULL || (ac != NULL && ac == last_account)) {
787 result = g_slist_append(result, msg);
788 orig = g_slist_remove(orig, msg);
790 nothing_to_sort = FALSE;
796 if (orig || g_slist_length(orig)) {
797 if (!last_account && nothing_to_sort) {
798 /* can't find an account for the rest of the list */
801 result = g_slist_append(result, cur->data);
812 for (cur = result; cur; cur = cur->next)
813 debug_print("sort after %s\n", ((MsgInfo *)cur->data)->from);
820 static gboolean procmsg_is_last_for_account(FolderItem *queue, MsgInfo *msginfo, GSList *elem)
822 gchar *file = folder_item_fetch_msg(queue, msginfo->msgnum);
823 PrefsAccount *ac = procmsg_get_account_from_file(file);
826 for (cur = elem; cur; cur = cur->next) {
827 MsgInfo *cur_msginfo = (MsgInfo *)cur->data;
828 file = folder_item_fetch_msg(queue, cur_msginfo->msgnum);
830 if (cur_msginfo != msginfo && !MSG_IS_LOCKED(cur_msginfo->flags)) {
831 if (procmsg_get_account_from_file(file) == ac) {
842 static gboolean send_queue_lock = FALSE;
844 *\brief Send messages in queue
846 *\param queue Queue folder to process
847 *\param save_msgs Unused
849 *\return Number of messages sent, negative if an error occurred
850 * positive if no error occurred
852 gint procmsg_send_queue(FolderItem *queue, gboolean save_msgs, gchar **errstr)
854 gint sent = 0, err = 0;
856 GSList *sorted_list = NULL;
859 if (send_queue_lock) {
860 /* Avoid having to translate two similar strings */
861 log_warning(LOG_PROTOCOL, "%s\n", _("Already trying to send."));
863 if (*errstr) g_free(*errstr);
864 *errstr = g_strdup_printf(_("Already trying to send."));
866 toolbar_main_set_sensitive(mainwindow_get_mainwindow());
869 send_queue_lock = TRUE;
872 queue = folder_get_default_queue();
875 send_queue_lock = FALSE;
880 toolbar_main_set_sensitive(mainwindow_get_mainwindow());
882 folder_item_scan(queue);
883 list = folder_item_get_msg_list(queue);
885 /* sort the list per sender account; this helps reusing the same SMTP server */
886 sorted_list = procmsg_list_sort_by_account(queue, list);
888 for (elem = sorted_list; elem != NULL; elem = elem->next) {
892 msginfo = (MsgInfo *)(elem->data);
893 if (!MSG_IS_LOCKED(msginfo->flags) && !MSG_IS_DELETED(msginfo->flags)) {
894 file = folder_item_fetch_msg(queue, msginfo->msgnum);
896 gboolean queued_removed = FALSE;
897 if (procmsg_send_message_queue_full(file,
898 !procmsg_is_last_for_account(queue, msginfo, elem),
899 errstr, queue, msginfo->msgnum, &queued_removed) < 0) {
900 g_warning("Sending queued message %d failed.\n",
906 folder_item_remove_msg(queue, msginfo->msgnum);
911 /* FIXME: supposedly if only one message is locked, and queue
912 * is being flushed, the following free says something like
913 * "freeing msg ## in folder (nil)". */
914 procmsg_msginfo_free(msginfo);
917 g_slist_free(sorted_list);
918 folder_item_scan(queue);
920 if (queue->node && queue->node->children) {
921 node = queue->node->children;
922 while (node != NULL) {
925 send_queue_lock = FALSE;
926 res = procmsg_send_queue(FOLDER_ITEM(node->data), save_msgs, errstr);
927 send_queue_lock = TRUE;
935 send_queue_lock = FALSE;
937 toolbar_main_set_sensitive(mainwindow_get_mainwindow());
939 return (err != 0 ? -err : sent);
942 gboolean procmsg_is_sending(void)
944 return send_queue_lock;
948 *\brief Determine if a queue folder is empty
950 *\param queue Queue folder to process
952 *\return TRUE if the queue folder is empty, otherwise return FALSE
954 gboolean procmsg_queue_is_empty(FolderItem *queue)
957 gboolean res = FALSE;
959 queue = folder_get_default_queue();
960 cm_return_val_if_fail(queue != NULL, TRUE);
962 folder_item_scan(queue);
963 list = folder_item_get_msg_list(queue);
964 res = (list == NULL);
965 procmsg_msg_list_free(list);
969 if (queue->node && queue->node->children) {
970 node = queue->node->children;
971 while (node != NULL) {
973 if (!procmsg_queue_is_empty(FOLDER_ITEM(node->data)))
982 gint procmsg_remove_special_headers(const gchar *in, const gchar *out)
987 if ((fp = g_fopen(in, "rb")) == NULL) {
988 FILE_OP_ERROR(in, "fopen");
991 if ((outfp = g_fopen(out, "wb")) == NULL) {
992 FILE_OP_ERROR(out, "fopen");
996 while (fgets(buf, sizeof(buf), fp) != NULL) {
998 if ((!strncmp(buf, "X-Claws-End-Special-Headers: 1",
999 strlen("X-Claws-End-Special-Headers:"))) ||
1000 (!strncmp(buf, "X-Sylpheed-End-Special-Headers: 1",
1001 strlen("X-Sylpheed-End-Special-Headers:"))))
1004 if (buf[0] == '\r' || buf[0] == '\n') break;
1005 /* from other mailers */
1006 if (!strncmp(buf, "Date: ", 6)
1007 || !strncmp(buf, "To: ", 4)
1008 || !strncmp(buf, "From: ", 6)
1009 || !strncmp(buf, "Subject: ", 9)) {
1014 while (fgets(buf, sizeof(buf), fp) != NULL)
1021 static gint procmsg_save_to_outbox(FolderItem *outbox, const gchar *file,
1025 MsgInfo *msginfo, *tmp_msginfo;
1026 MsgFlags flag = {0, 0};
1028 debug_print("saving sent message...\n");
1031 outbox = folder_get_default_outbox();
1032 cm_return_val_if_fail(outbox != NULL, -1);
1034 /* remove queueing headers */
1036 gchar tmp[MAXPATHLEN + 1];
1038 g_snprintf(tmp, sizeof(tmp), "%s%ctmpmsg.out.%08x",
1039 get_rc_dir(), G_DIR_SEPARATOR, (guint) rand());
1041 if (procmsg_remove_special_headers(file, tmp) !=0)
1044 folder_item_scan(outbox);
1045 if ((num = folder_item_add_msg(outbox, tmp, &flag, TRUE)) < 0) {
1046 g_warning("can't save message\n");
1051 folder_item_scan(outbox);
1052 if ((num = folder_item_add_msg
1053 (outbox, file, &flag, FALSE)) < 0) {
1054 g_warning("can't save message\n");
1058 msginfo = folder_item_get_msginfo(outbox, num); /* refcnt++ */
1059 tmp_msginfo = procmsg_msginfo_get_full_info(msginfo); /* refcnt++ */
1060 if (msginfo != NULL) {
1061 procmsg_msginfo_unset_flags(msginfo, ~0, 0);
1062 procmsg_msginfo_free(msginfo); /* refcnt-- */
1063 /* tmp_msginfo == msginfo */
1064 if (tmp_msginfo && msginfo->extradata &&
1065 (msginfo->extradata->dispositionnotificationto ||
1066 msginfo->extradata->returnreceiptto)) {
1067 procmsg_msginfo_set_flags(msginfo, MSG_RETRCPT_SENT, 0);
1069 procmsg_msginfo_free(tmp_msginfo); /* refcnt-- */
1075 void procmsg_print_message(MsgInfo *msginfo, const gchar *cmdline)
1077 static const gchar *def_cmd = "lpr %s";
1078 static guint id = 0;
1084 cm_return_if_fail(msginfo);
1086 if (procmime_msginfo_is_encrypted(msginfo))
1087 tmpfp = procmime_get_first_encrypted_text_content(msginfo);
1089 tmpfp = procmime_get_first_text_content(msginfo);
1090 if (tmpfp == NULL) {
1091 g_warning("Can't get text part\n");
1095 prtmp = g_strdup_printf("%s%cprinttmp.%08x",
1096 get_mime_tmp_dir(), G_DIR_SEPARATOR, id++);
1098 if ((prfp = g_fopen(prtmp, "wb")) == NULL) {
1099 FILE_OP_ERROR(prtmp, "fopen");
1105 if (msginfo->date) r = fprintf(prfp, "Date: %s\n", msginfo->date);
1106 if (msginfo->from) r = fprintf(prfp, "From: %s\n", msginfo->from);
1107 if (msginfo->to) r = fprintf(prfp, "To: %s\n", msginfo->to);
1108 if (msginfo->cc) r = fprintf(prfp, "Cc: %s\n", msginfo->cc);
1109 if (msginfo->newsgroups)
1110 r = fprintf(prfp, "Newsgroups: %s\n", msginfo->newsgroups);
1111 if (msginfo->subject) r = fprintf(prfp, "Subject: %s\n", msginfo->subject);
1114 while (fgets(buf, sizeof(buf), tmpfp) != NULL)
1115 r = fputs(buf, prfp);
1120 if (cmdline && (p = strchr(cmdline, '%')) && *(p + 1) == 's' &&
1121 !strchr(p + 2, '%'))
1122 g_snprintf(buf, sizeof(buf) - 1, cmdline, prtmp);
1125 g_warning("Print command-line is invalid: '%s'\n",
1127 g_snprintf(buf, sizeof(buf) - 1, def_cmd, prtmp);
1133 if (buf[strlen(buf) - 1] != '&') strcat(buf, "&");
1137 MsgInfo *procmsg_msginfo_new_ref(MsgInfo *msginfo)
1144 MsgInfo *procmsg_msginfo_new(void)
1146 MsgInfo *newmsginfo;
1148 newmsginfo = g_new0(MsgInfo, 1);
1149 newmsginfo->refcnt = 1;
1154 MsgInfo *procmsg_msginfo_copy(MsgInfo *msginfo)
1156 MsgInfo *newmsginfo;
1159 if (msginfo == NULL) return NULL;
1161 newmsginfo = g_new0(MsgInfo, 1);
1163 newmsginfo->refcnt = 1;
1165 #define MEMBCOPY(mmb) newmsginfo->mmb = msginfo->mmb
1166 #define MEMBDUP(mmb) newmsginfo->mmb = msginfo->mmb ? \
1167 g_strdup(msginfo->mmb) : NULL
1182 MEMBDUP(newsgroups);
1189 MEMBCOPY(to_folder);
1191 if (msginfo->extradata) {
1192 newmsginfo->extradata = g_new0(MsgInfoExtraData, 1);
1193 MEMBDUP(extradata->face);
1194 MEMBDUP(extradata->xface);
1195 MEMBDUP(extradata->dispositionnotificationto);
1196 MEMBDUP(extradata->returnreceiptto);
1197 MEMBDUP(extradata->partial_recv);
1198 MEMBDUP(extradata->account_server);
1199 MEMBDUP(extradata->account_login);
1200 MEMBDUP(extradata->list_post);
1201 MEMBDUP(extradata->list_subscribe);
1202 MEMBDUP(extradata->list_unsubscribe);
1203 MEMBDUP(extradata->list_help);
1204 MEMBDUP(extradata->list_archive);
1205 MEMBDUP(extradata->list_owner);
1208 refs = msginfo->references;
1209 for (refs = msginfo->references; refs != NULL; refs = refs->next) {
1210 newmsginfo->references = g_slist_prepend
1211 (newmsginfo->references, g_strdup(refs->data));
1213 newmsginfo->references = g_slist_reverse(newmsginfo->references);
1216 MEMBDUP(plaintext_file);
1221 MsgInfo *procmsg_msginfo_get_full_info_from_file(MsgInfo *msginfo, const gchar *file)
1223 MsgInfo *full_msginfo;
1225 if (msginfo == NULL) return NULL;
1227 if (!file || !is_file_exist(file)) {
1228 g_warning("procmsg_msginfo_get_full_info_from_file(): can't get message file.\n");
1232 full_msginfo = procheader_parse_file(file, msginfo->flags, TRUE, FALSE);
1233 if (!full_msginfo) return NULL;
1235 msginfo->total_size = full_msginfo->total_size;
1236 msginfo->planned_download = full_msginfo->planned_download;
1238 if (full_msginfo->extradata) {
1239 if (!msginfo->extradata)
1240 msginfo->extradata = g_new0(MsgInfoExtraData, 1);
1241 if (!msginfo->extradata->list_post)
1242 msginfo->extradata->list_post = g_strdup(full_msginfo->extradata->list_post);
1243 if (!msginfo->extradata->list_subscribe)
1244 msginfo->extradata->list_subscribe = g_strdup(full_msginfo->extradata->list_subscribe);
1245 if (!msginfo->extradata->list_unsubscribe)
1246 msginfo->extradata->list_unsubscribe = g_strdup(full_msginfo->extradata->list_unsubscribe);
1247 if (!msginfo->extradata->list_help)
1248 msginfo->extradata->list_help = g_strdup(full_msginfo->extradata->list_help);
1249 if (!msginfo->extradata->list_archive)
1250 msginfo->extradata->list_archive= g_strdup(full_msginfo->extradata->list_archive);
1251 if (!msginfo->extradata->list_owner)
1252 msginfo->extradata->list_owner = g_strdup(full_msginfo->extradata->list_owner);
1253 if (!msginfo->extradata->xface)
1254 msginfo->extradata->xface = g_strdup(full_msginfo->extradata->xface);
1255 if (!msginfo->extradata->face)
1256 msginfo->extradata->face = g_strdup(full_msginfo->extradata->face);
1257 if (!msginfo->extradata->dispositionnotificationto)
1258 msginfo->extradata->dispositionnotificationto =
1259 g_strdup(full_msginfo->extradata->dispositionnotificationto);
1260 if (!msginfo->extradata->returnreceiptto)
1261 msginfo->extradata->returnreceiptto = g_strdup
1262 (full_msginfo->extradata->returnreceiptto);
1263 if (!msginfo->extradata->partial_recv && full_msginfo->extradata->partial_recv)
1264 msginfo->extradata->partial_recv = g_strdup
1265 (full_msginfo->extradata->partial_recv);
1266 if (!msginfo->extradata->account_server && full_msginfo->extradata->account_server)
1267 msginfo->extradata->account_server = g_strdup
1268 (full_msginfo->extradata->account_server);
1269 if (!msginfo->extradata->account_login && full_msginfo->extradata->account_login)
1270 msginfo->extradata->account_login = g_strdup
1271 (full_msginfo->extradata->account_login);
1273 procmsg_msginfo_free(full_msginfo);
1275 return procmsg_msginfo_new_ref(msginfo);
1278 MsgInfo *procmsg_msginfo_get_full_info(MsgInfo *msginfo)
1280 MsgInfo *full_msginfo;
1283 if (msginfo == NULL) return NULL;
1285 file = procmsg_get_message_file_path(msginfo);
1286 if (!file || !is_file_exist(file)) {
1288 file = procmsg_get_message_file(msginfo);
1290 if (!file || !is_file_exist(file)) {
1291 g_warning("procmsg_msginfo_get_full_info(): can't get message file.\n");
1295 full_msginfo = procmsg_msginfo_get_full_info_from_file(msginfo, file);
1297 return full_msginfo;
1300 void procmsg_msginfo_free(MsgInfo *msginfo)
1302 if (msginfo == NULL) return;
1305 if (msginfo->refcnt > 0)
1308 if (msginfo->to_folder) {
1309 msginfo->to_folder->op_count--;
1310 folder_item_update(msginfo->to_folder, F_ITEM_UPDATE_MSGCNT);
1313 g_free(msginfo->fromspace);
1315 g_free(msginfo->fromname);
1317 g_free(msginfo->date);
1318 g_free(msginfo->from);
1319 g_free(msginfo->to);
1320 g_free(msginfo->cc);
1321 g_free(msginfo->newsgroups);
1322 g_free(msginfo->subject);
1323 g_free(msginfo->msgid);
1324 g_free(msginfo->inreplyto);
1325 g_free(msginfo->xref);
1327 if (msginfo->extradata) {
1328 g_free(msginfo->extradata->returnreceiptto);
1329 g_free(msginfo->extradata->dispositionnotificationto);
1330 g_free(msginfo->extradata->xface);
1331 g_free(msginfo->extradata->face);
1332 g_free(msginfo->extradata->list_post);
1333 g_free(msginfo->extradata->list_subscribe);
1334 g_free(msginfo->extradata->list_unsubscribe);
1335 g_free(msginfo->extradata->list_help);
1336 g_free(msginfo->extradata->list_archive);
1337 g_free(msginfo->extradata->list_owner);
1338 g_free(msginfo->extradata->partial_recv);
1339 g_free(msginfo->extradata->account_server);
1340 g_free(msginfo->extradata->account_login);
1341 g_free(msginfo->extradata);
1343 slist_free_strings(msginfo->references);
1344 g_slist_free(msginfo->references);
1345 g_slist_free(msginfo->tags);
1347 g_free(msginfo->plaintext_file);
1352 guint procmsg_msginfo_memusage(MsgInfo *msginfo)
1357 memusage += sizeof(MsgInfo);
1358 if (msginfo->fromname)
1359 memusage += strlen(msginfo->fromname);
1361 memusage += strlen(msginfo->date);
1363 memusage += strlen(msginfo->from);
1365 memusage += strlen(msginfo->to);
1367 memusage += strlen(msginfo->cc);
1368 if (msginfo->newsgroups)
1369 memusage += strlen(msginfo->newsgroups);
1370 if (msginfo->subject)
1371 memusage += strlen(msginfo->subject);
1373 memusage += strlen(msginfo->msgid);
1374 if (msginfo->inreplyto)
1375 memusage += strlen(msginfo->inreplyto);
1377 for (tmp = msginfo->references; tmp; tmp=tmp->next) {
1378 gchar *r = (gchar *)tmp->data;
1379 memusage += r?strlen(r):0 + sizeof(GSList);
1381 if (msginfo->fromspace)
1382 memusage += strlen(msginfo->fromspace);
1384 for (tmp = msginfo->tags; tmp; tmp=tmp->next) {
1385 memusage += sizeof(GSList);
1387 if (msginfo->extradata) {
1388 memusage += sizeof(MsgInfoExtraData);
1389 if (msginfo->extradata->xface)
1390 memusage += strlen(msginfo->extradata->xface);
1391 if (msginfo->extradata->face)
1392 memusage += strlen(msginfo->extradata->face);
1393 if (msginfo->extradata->dispositionnotificationto)
1394 memusage += strlen(msginfo->extradata->dispositionnotificationto);
1395 if (msginfo->extradata->returnreceiptto)
1396 memusage += strlen(msginfo->extradata->returnreceiptto);
1398 if (msginfo->extradata->partial_recv)
1399 memusage += strlen(msginfo->extradata->partial_recv);
1400 if (msginfo->extradata->account_server)
1401 memusage += strlen(msginfo->extradata->account_server);
1402 if (msginfo->extradata->account_login)
1403 memusage += strlen(msginfo->extradata->account_login);
1405 if (msginfo->extradata->list_post)
1406 memusage += strlen(msginfo->extradata->list_post);
1407 if (msginfo->extradata->list_subscribe)
1408 memusage += strlen(msginfo->extradata->list_subscribe);
1409 if (msginfo->extradata->list_unsubscribe)
1410 memusage += strlen(msginfo->extradata->list_unsubscribe);
1411 if (msginfo->extradata->list_help)
1412 memusage += strlen(msginfo->extradata->list_help);
1413 if (msginfo->extradata->list_archive)
1414 memusage += strlen(msginfo->extradata->list_archive);
1415 if (msginfo->extradata->list_owner)
1416 memusage += strlen(msginfo->extradata->list_owner);
1421 static gint procmsg_send_message_queue_full(const gchar *file, gboolean keep_session, gchar **errstr,
1422 FolderItem *queue, gint msgnum, gboolean *queued_removed)
1424 static HeaderEntry qentry[] = {{"S:", NULL, FALSE},
1425 {"SSV:", NULL, FALSE},
1426 {"R:", NULL, FALSE},
1427 {"NG:", NULL, FALSE},
1428 {"MAID:", NULL, FALSE},
1429 {"NAID:", NULL, FALSE},
1430 {"SCF:", NULL, FALSE},
1431 {"RMID:", NULL, FALSE},
1432 {"FMID:", NULL, FALSE},
1433 {"X-Claws-Privacy-System:", NULL, FALSE},
1434 {"X-Claws-Encrypt:", NULL, FALSE},
1435 {"X-Claws-Encrypt-Data:", NULL, FALSE},
1436 {"X-Claws-End-Special-Headers:", NULL, FALSE},
1437 {"X-Sylpheed-Privacy-System:", NULL, FALSE},
1438 {"X-Sylpheed-Encrypt:", NULL, FALSE},
1439 {"X-Sylpheed-Encrypt-Data:", NULL, FALSE},
1440 {"X-Sylpheed-End-Special-Headers:", NULL, FALSE},
1441 {NULL, NULL, FALSE}};
1444 gint mailval = 0, newsval = 0;
1446 gchar *smtpserver = NULL;
1447 GSList *to_list = NULL;
1448 GSList *newsgroup_list = NULL;
1449 gchar *savecopyfolder = NULL;
1450 gchar *replymessageid = NULL;
1451 gchar *fwdmessageid = NULL;
1452 gchar *privacy_system = NULL;
1453 gboolean encrypt = FALSE;
1454 gchar *encrypt_data = NULL;
1455 gchar buf[BUFFSIZE];
1457 PrefsAccount *mailac = NULL, *newsac = NULL;
1458 gboolean save_clear_text = TRUE;
1459 gchar *tmp_enc_file = NULL;
1463 cm_return_val_if_fail(file != NULL, -1);
1465 if ((fp = g_fopen(file, "rb")) == NULL) {
1466 FILE_OP_ERROR(file, "fopen");
1468 if (*errstr) g_free(*errstr);
1469 *errstr = g_strdup_printf(_("Couldn't open file %s."), file);
1474 while ((hnum = procheader_get_one_field(buf, sizeof(buf), fp, qentry))
1476 gchar *p = buf + strlen(qentry[hnum].name);
1484 if (smtpserver == NULL)
1485 smtpserver = g_strdup(p);
1488 to_list = address_list_append(to_list, p);
1491 newsgroup_list = newsgroup_list_append(newsgroup_list, p);
1493 case Q_MAIL_ACCOUNT_ID:
1494 mailac = account_find_from_id(atoi(p));
1496 case Q_NEWS_ACCOUNT_ID:
1497 newsac = account_find_from_id(atoi(p));
1499 case Q_SAVE_COPY_FOLDER:
1500 if (savecopyfolder == NULL)
1501 savecopyfolder = g_strdup(p);
1503 case Q_REPLY_MESSAGE_ID:
1504 if (replymessageid == NULL)
1505 replymessageid = g_strdup(p);
1507 case Q_FWD_MESSAGE_ID:
1508 if (fwdmessageid == NULL)
1509 fwdmessageid = g_strdup(p);
1511 case Q_PRIVACY_SYSTEM:
1512 case Q_PRIVACY_SYSTEM_OLD:
1513 if (privacy_system == NULL)
1514 privacy_system = g_strdup(p);
1521 case Q_ENCRYPT_DATA:
1522 case Q_ENCRYPT_DATA_OLD:
1523 if (encrypt_data == NULL)
1524 encrypt_data = g_strdup(p);
1527 case Q_CLAWS_HDRS_OLD:
1528 /* end of special headers reached */
1529 goto send_mail; /* can't "break;break;" */
1533 filepos = ftell(fp);
1538 if (mailac && mailac->save_encrypted_as_clear_text
1539 && !mailac->encrypt_to_self)
1540 save_clear_text = TRUE;
1542 save_clear_text = FALSE;
1547 mimeinfo = procmime_scan_queue_file(file);
1548 if (!privacy_encrypt(privacy_system, mimeinfo, encrypt_data)
1549 || (fp = my_tmpfile()) == NULL
1550 || procmime_write_mimeinfo(mimeinfo, fp) < 0) {
1553 procmime_mimeinfo_free_all(mimeinfo);
1556 slist_free_strings(to_list);
1557 g_slist_free(to_list);
1558 slist_free_strings(newsgroup_list);
1559 g_slist_free(newsgroup_list);
1560 g_free(savecopyfolder);
1561 g_free(replymessageid);
1562 g_free(fwdmessageid);
1563 g_free(privacy_system);
1564 g_free(encrypt_data);
1566 if (*errstr) g_free(*errstr);
1567 *errstr = g_strdup_printf(_("Couldn't encrypt the email: %s"),
1568 privacy_get_error());
1574 if (!save_clear_text) {
1575 gchar *content = NULL;
1576 FILE *tmpfp = get_tmpfile_in_dir(get_mime_tmp_dir(), &tmp_enc_file);
1580 content = file_read_stream_to_str(fp);
1583 str_write_to_file(content, tmp_enc_file);
1586 g_warning("couldn't get tempfile\n");
1590 procmime_mimeinfo_free_all(mimeinfo);
1596 debug_print("Sending message by mail\n");
1599 if (*errstr) g_free(*errstr);
1600 *errstr = g_strdup_printf(_("Queued message header is broken."));
1603 } else if (mailac && mailac->use_mail_command &&
1604 mailac->mail_command && (* mailac->mail_command)) {
1605 mailval = send_message_local(mailac->mail_command, fp);
1609 mailac = account_find_from_smtp_server(from, smtpserver);
1611 g_warning("Account not found. "
1612 "Using current account...\n");
1613 mailac = cur_account;
1618 mailval = send_message_smtp_full(mailac, to_list, fp, keep_session);
1619 if (mailval == -1 && errstr) {
1620 if (*errstr) g_free(*errstr);
1621 *errstr = g_strdup_printf(_("An error happened during SMTP session."));
1624 PrefsAccount tmp_ac;
1626 g_warning("Account not found.\n");
1628 memset(&tmp_ac, 0, sizeof(PrefsAccount));
1629 tmp_ac.address = from;
1630 tmp_ac.smtp_server = smtpserver;
1631 tmp_ac.smtpport = SMTP_PORT;
1632 mailval = send_message_smtp(&tmp_ac, to_list, fp);
1633 if (mailval == -1 && errstr) {
1634 if (*errstr) g_free(*errstr);
1635 *errstr = g_strdup_printf(_("No specific account has been found to "
1636 "send, and an error happened during SMTP session."));
1640 } else if (!to_list && !newsgroup_list) {
1642 if (*errstr) g_free(*errstr);
1643 *errstr = g_strdup(_("Couldn't determine sending informations. "
1644 "Maybe the email hasn't been generated by Claws Mail."));
1649 fseek(fp, filepos, SEEK_SET);
1650 if (newsgroup_list && newsac && (mailval == 0)) {
1655 /* write to temporary file */
1656 tmp = g_strdup_printf("%s%cnntp%p", get_tmp_dir(),
1657 G_DIR_SEPARATOR, file);
1658 if ((tmpfp = g_fopen(tmp, "wb")) == NULL) {
1659 FILE_OP_ERROR(tmp, "fopen");
1661 alertpanel_error(_("Couldn't create temporary file for news sending."));
1663 if (change_file_mode_rw(tmpfp, tmp) < 0) {
1664 FILE_OP_ERROR(tmp, "chmod");
1665 g_warning("can't change file mode\n");
1668 while ((newsval == 0) && fgets(buf, sizeof(buf), fp) != NULL) {
1669 if (fputs(buf, tmpfp) == EOF) {
1670 FILE_OP_ERROR(tmp, "fputs");
1673 if (*errstr) g_free(*errstr);
1674 *errstr = g_strdup_printf(_("Error when writing temporary file for news sending."));
1681 debug_print("Sending message by news\n");
1683 folder = FOLDER(newsac->folder);
1685 newsval = news_post(folder, tmp);
1686 if (newsval < 0 && errstr) {
1687 if (*errstr) g_free(*errstr);
1688 *errstr = g_strdup_printf(_("Error occurred while posting the message to %s."),
1689 newsac->nntp_server);
1699 /* save message to outbox */
1700 if (mailval == 0 && newsval == 0 && savecopyfolder) {
1703 debug_print("saving sent message...\n");
1705 outbox = folder_find_item_from_identifier(savecopyfolder);
1707 outbox = folder_get_default_outbox();
1709 if (save_clear_text || tmp_enc_file == NULL) {
1710 gboolean saved = FALSE;
1711 *queued_removed = FALSE;
1712 if (queue && msgnum > 0) {
1713 MsgInfo *queued_mail = folder_item_get_msginfo(queue, msgnum);
1714 if (folder_item_move_msg(outbox, queued_mail) >= 0) {
1715 debug_print("moved queued mail %d to sent folder\n", msgnum);
1717 *queued_removed = TRUE;
1718 } else if (folder_item_copy_msg(outbox, queued_mail) >= 0) {
1719 debug_print("copied queued mail %d to sent folder\n", msgnum);
1722 procmsg_msginfo_free(queued_mail);
1725 debug_print("resaving clear text queued mail to sent folder\n");
1726 procmsg_save_to_outbox(outbox, file, TRUE);
1729 debug_print("saving encrpyted queued mail to sent folder\n");
1730 procmsg_save_to_outbox(outbox, tmp_enc_file, FALSE);
1734 if (tmp_enc_file != NULL) {
1735 claws_unlink(tmp_enc_file);
1737 tmp_enc_file = NULL;
1740 if (replymessageid != NULL || fwdmessageid != NULL) {
1744 if (replymessageid != NULL)
1745 tokens = g_strsplit(replymessageid, "\t", 0);
1747 tokens = g_strsplit(fwdmessageid, "\t", 0);
1748 item = folder_find_item_from_identifier(tokens[0]);
1750 /* check if queued message has valid folder and message id */
1751 if (item != NULL && tokens[2] != NULL) {
1754 msginfo = folder_item_get_msginfo(item, atoi(tokens[1]));
1756 /* check if referring message exists and has a message id */
1757 if ((msginfo != NULL) &&
1758 (msginfo->msgid != NULL) &&
1759 (strcmp(msginfo->msgid, tokens[2]) != 0)) {
1760 procmsg_msginfo_free(msginfo);
1764 if (msginfo == NULL) {
1765 msginfo = folder_item_get_msginfo_by_msgid(item, tokens[2]);
1768 if (msginfo != NULL) {
1769 MsgPermFlags to_unset = 0;
1771 if (prefs_common.mark_as_read_on_new_window)
1772 to_unset = (MSG_NEW|MSG_UNREAD);
1774 if (replymessageid != NULL) {
1775 procmsg_msginfo_unset_flags(msginfo, to_unset|MSG_FORWARDED, 0);
1776 procmsg_msginfo_set_flags(msginfo, MSG_REPLIED, 0);
1778 procmsg_msginfo_unset_flags(msginfo, MSG_REPLIED, 0);
1779 procmsg_msginfo_set_flags(msginfo, MSG_FORWARDED, 0);
1781 procmsg_msginfo_free(msginfo);
1789 slist_free_strings(to_list);
1790 g_slist_free(to_list);
1791 slist_free_strings(newsgroup_list);
1792 g_slist_free(newsgroup_list);
1793 g_free(savecopyfolder);
1794 g_free(replymessageid);
1795 g_free(fwdmessageid);
1796 g_free(privacy_system);
1797 g_free(encrypt_data);
1799 return (newsval != 0 ? newsval : mailval);
1802 gint procmsg_send_message_queue(const gchar *file, gchar **errstr, FolderItem *queue, gint msgnum, gboolean *queued_removed)
1804 gint result = procmsg_send_message_queue_full(file, FALSE, errstr, queue, msgnum, queued_removed);
1805 toolbar_main_set_sensitive(mainwindow_get_mainwindow());
1809 static void update_folder_msg_counts(FolderItem *item, MsgInfo *msginfo, MsgPermFlags old_flags)
1811 MsgPermFlags new_flags = msginfo->flags.perm_flags;
1814 if (!(old_flags & MSG_NEW) && (new_flags & MSG_NEW)) {
1818 if ((old_flags & MSG_NEW) && !(new_flags & MSG_NEW)) {
1823 if (!(old_flags & MSG_UNREAD) && (new_flags & MSG_UNREAD)) {
1824 item->unread_msgs++;
1825 if (procmsg_msg_has_marked_parent(msginfo))
1826 item->unreadmarked_msgs++;
1829 if ((old_flags & MSG_UNREAD) && !(new_flags & MSG_UNREAD)) {
1830 item->unread_msgs--;
1831 if (procmsg_msg_has_marked_parent(msginfo))
1832 item->unreadmarked_msgs--;
1836 if (!(old_flags & MSG_MARKED) && (new_flags & MSG_MARKED)) {
1837 procmsg_update_unread_children(msginfo, TRUE);
1838 item->marked_msgs++;
1841 if ((old_flags & MSG_MARKED) && !(new_flags & MSG_MARKED)) {
1842 procmsg_update_unread_children(msginfo, FALSE);
1843 item->marked_msgs--;
1846 if (!(old_flags & MSG_REPLIED) && (new_flags & MSG_REPLIED)) {
1847 item->replied_msgs++;
1850 if ((old_flags & MSG_REPLIED) && !(new_flags & MSG_REPLIED)) {
1851 item->replied_msgs--;
1854 if (!(old_flags & MSG_FORWARDED) && (new_flags & MSG_FORWARDED)) {
1855 item->forwarded_msgs++;
1858 if ((old_flags & MSG_FORWARDED) && !(new_flags & MSG_FORWARDED)) {
1859 item->forwarded_msgs--;
1862 if (!(old_flags & MSG_LOCKED) && (new_flags & MSG_LOCKED)) {
1863 item->locked_msgs++;
1866 if ((old_flags & MSG_LOCKED) && !(new_flags & MSG_LOCKED)) {
1867 item->locked_msgs--;
1870 if ((old_flags & MSG_IGNORE_THREAD) && !(new_flags & MSG_IGNORE_THREAD)) {
1871 item->ignored_msgs--;
1874 if (!(old_flags & MSG_IGNORE_THREAD) && (new_flags & MSG_IGNORE_THREAD)) {
1875 item->ignored_msgs++;
1878 if ((old_flags & MSG_WATCH_THREAD) && !(new_flags & MSG_WATCH_THREAD)) {
1879 item->watched_msgs--;
1882 if (!(old_flags & MSG_WATCH_THREAD) && (new_flags & MSG_WATCH_THREAD)) {
1883 item->watched_msgs++;
1887 void procmsg_msginfo_set_flags(MsgInfo *msginfo, MsgPermFlags perm_flags, MsgTmpFlags tmp_flags)
1890 MsgInfoUpdate msginfo_update;
1891 MsgPermFlags perm_flags_new, perm_flags_old;
1892 MsgTmpFlags tmp_flags_old;
1894 cm_return_if_fail(msginfo != NULL);
1895 item = msginfo->folder;
1896 cm_return_if_fail(item != NULL);
1898 debug_print("Setting flags for message %d in folder %s\n", msginfo->msgnum, item->path);
1900 /* Perm Flags handling */
1901 perm_flags_old = msginfo->flags.perm_flags;
1902 perm_flags_new = msginfo->flags.perm_flags | perm_flags;
1903 if ((perm_flags & MSG_IGNORE_THREAD) || (perm_flags_old & MSG_IGNORE_THREAD)) {
1904 perm_flags_new &= ~(MSG_NEW | MSG_UNREAD);
1906 if ((perm_flags & MSG_WATCH_THREAD) || (perm_flags_old & MSG_WATCH_THREAD)) {
1907 perm_flags_new &= ~(MSG_IGNORE_THREAD);
1910 if (perm_flags_old != perm_flags_new) {
1911 folder_item_change_msg_flags(msginfo->folder, msginfo, perm_flags_new);
1913 update_folder_msg_counts(item, msginfo, perm_flags_old);
1914 summary_update_unread(mainwindow_get_mainwindow()->summaryview, NULL);
1917 /* Tmp flags handling */
1918 tmp_flags_old = msginfo->flags.tmp_flags;
1919 msginfo->flags.tmp_flags |= tmp_flags;
1921 /* update notification */
1922 if ((perm_flags_old != perm_flags_new) || (tmp_flags_old != msginfo->flags.tmp_flags)) {
1923 msginfo_update.msginfo = msginfo;
1924 msginfo_update.flags = MSGINFO_UPDATE_FLAGS;
1925 hooks_invoke(MSGINFO_UPDATE_HOOKLIST, &msginfo_update);
1926 folder_item_update(msginfo->folder, F_ITEM_UPDATE_MSGCNT);
1930 void procmsg_msginfo_unset_flags(MsgInfo *msginfo, MsgPermFlags perm_flags, MsgTmpFlags tmp_flags)
1933 MsgInfoUpdate msginfo_update;
1934 MsgPermFlags perm_flags_new, perm_flags_old;
1935 MsgTmpFlags tmp_flags_old;
1937 cm_return_if_fail(msginfo != NULL);
1938 item = msginfo->folder;
1939 cm_return_if_fail(item != NULL);
1941 debug_print("Unsetting flags for message %d in folder %s\n", msginfo->msgnum, item->path);
1943 /* Perm Flags handling */
1944 perm_flags_old = msginfo->flags.perm_flags;
1945 perm_flags_new = msginfo->flags.perm_flags & ~perm_flags;
1947 if (perm_flags_old != perm_flags_new) {
1948 folder_item_change_msg_flags(msginfo->folder, msginfo, perm_flags_new);
1950 update_folder_msg_counts(item, msginfo, perm_flags_old);
1953 /* Tmp flags hanlding */
1954 tmp_flags_old = msginfo->flags.tmp_flags;
1955 msginfo->flags.tmp_flags &= ~tmp_flags;
1957 /* update notification */
1958 if ((perm_flags_old != perm_flags_new) || (tmp_flags_old != msginfo->flags.tmp_flags)) {
1959 msginfo_update.msginfo = msginfo;
1960 msginfo_update.flags = MSGINFO_UPDATE_FLAGS;
1961 hooks_invoke(MSGINFO_UPDATE_HOOKLIST, &msginfo_update);
1962 folder_item_update(msginfo->folder, F_ITEM_UPDATE_MSGCNT);
1966 void procmsg_msginfo_change_flags(MsgInfo *msginfo,
1967 MsgPermFlags add_perm_flags, MsgTmpFlags add_tmp_flags,
1968 MsgPermFlags rem_perm_flags, MsgTmpFlags rem_tmp_flags)
1971 MsgInfoUpdate msginfo_update;
1972 MsgPermFlags perm_flags_new, perm_flags_old;
1973 MsgTmpFlags tmp_flags_old;
1975 cm_return_if_fail(msginfo != NULL);
1976 item = msginfo->folder;
1977 cm_return_if_fail(item != NULL);
1979 debug_print("Changing flags for message %d in folder %s\n", msginfo->msgnum, item->path);
1981 /* Perm Flags handling */
1982 perm_flags_old = msginfo->flags.perm_flags;
1983 perm_flags_new = (msginfo->flags.perm_flags & ~rem_perm_flags) | add_perm_flags;
1984 if ((add_perm_flags & MSG_IGNORE_THREAD) || (perm_flags_old & MSG_IGNORE_THREAD)) {
1985 perm_flags_new &= ~(MSG_NEW | MSG_UNREAD);
1987 if ((add_perm_flags & MSG_WATCH_THREAD) || (perm_flags_old & MSG_WATCH_THREAD)) {
1988 perm_flags_new &= ~(MSG_IGNORE_THREAD);
1991 if (perm_flags_old != perm_flags_new) {
1992 folder_item_change_msg_flags(msginfo->folder, msginfo, perm_flags_new);
1994 update_folder_msg_counts(item, msginfo, perm_flags_old);
1998 /* Tmp flags handling */
1999 tmp_flags_old = msginfo->flags.tmp_flags;
2000 msginfo->flags.tmp_flags &= ~rem_tmp_flags;
2001 msginfo->flags.tmp_flags |= add_tmp_flags;
2003 /* update notification */
2004 if ((perm_flags_old != perm_flags_new) || (tmp_flags_old != msginfo->flags.tmp_flags)) {
2005 msginfo_update.msginfo = msginfo;
2006 msginfo_update.flags = MSGINFO_UPDATE_FLAGS;
2007 hooks_invoke(MSGINFO_UPDATE_HOOKLIST, &msginfo_update);
2008 folder_item_update(msginfo->folder, F_ITEM_UPDATE_MSGCNT);
2013 *\brief check for flags (e.g. mark) in prior msgs of current thread
2015 *\param info Current message
2016 *\param perm_flags Flags to be checked
2017 *\param parentmsgs Hash of prior msgs to avoid loops
2019 *\return gboolean TRUE if perm_flags are found
2021 static gboolean procmsg_msg_has_flagged_parent_real(MsgInfo *info,
2022 MsgPermFlags perm_flags, GHashTable *parentmsgs)
2026 cm_return_val_if_fail(info != NULL, FALSE);
2028 if (info != NULL && info->folder != NULL && info->inreplyto != NULL) {
2029 tmp = folder_item_get_msginfo_by_msgid(info->folder,
2031 if (tmp && (tmp->flags.perm_flags & perm_flags)) {
2032 procmsg_msginfo_free(tmp);
2034 } else if (tmp != NULL) {
2037 if (g_hash_table_lookup(parentmsgs, info)) {
2038 debug_print("loop detected: %d\n",
2042 g_hash_table_insert(parentmsgs, info, "1");
2043 result = procmsg_msg_has_flagged_parent_real(
2044 tmp, perm_flags, parentmsgs);
2046 procmsg_msginfo_free(tmp);
2056 *\brief Callback for cleaning up hash of parentmsgs
2058 static gboolean parentmsgs_hash_remove(gpointer key,
2066 *\brief Set up list of parentmsgs
2067 * See procmsg_msg_has_flagged_parent_real()
2069 gboolean procmsg_msg_has_flagged_parent(MsgInfo *info, MsgPermFlags perm_flags)
2072 static GHashTable *parentmsgs = NULL;
2074 if (parentmsgs == NULL)
2075 parentmsgs = g_hash_table_new(NULL, NULL);
2077 result = procmsg_msg_has_flagged_parent_real(info, perm_flags, parentmsgs);
2078 g_hash_table_foreach_remove(parentmsgs, parentmsgs_hash_remove, NULL);
2084 *\brief Check if msgs prior in thread are marked
2085 * See procmsg_msg_has_flagged_parent_real()
2087 gboolean procmsg_msg_has_marked_parent(MsgInfo *info)
2089 return procmsg_msg_has_flagged_parent(info, MSG_MARKED);
2093 static GSList *procmsg_find_children_func(MsgInfo *info,
2094 GSList *children, GSList *all)
2098 cm_return_val_if_fail(info!=NULL, children);
2099 if (info->msgid == NULL)
2102 for (cur = all; cur != NULL; cur = g_slist_next(cur)) {
2103 MsgInfo *tmp = (MsgInfo *)cur->data;
2104 if (tmp->inreplyto && !strcmp(tmp->inreplyto, info->msgid)) {
2105 /* Check if message is already in the list */
2106 if ((children == NULL) ||
2107 (g_slist_index(children, tmp) == -1)) {
2108 children = g_slist_prepend(children,
2109 procmsg_msginfo_new_ref(tmp));
2110 children = procmsg_find_children_func(tmp,
2119 static GSList *procmsg_find_children (MsgInfo *info)
2124 cm_return_val_if_fail(info!=NULL, NULL);
2125 all = folder_item_get_msg_list(info->folder);
2126 children = procmsg_find_children_func(info, NULL, all);
2127 if (children != NULL) {
2128 for (cur = all; cur != NULL; cur = g_slist_next(cur)) {
2129 /* this will not free the used pointers
2130 created with procmsg_msginfo_new_ref */
2131 procmsg_msginfo_free((MsgInfo *)cur->data);
2139 static void procmsg_update_unread_children(MsgInfo *info, gboolean newly_marked)
2141 GSList *children = procmsg_find_children(info);
2143 for (cur = children; cur != NULL; cur = g_slist_next(cur)) {
2144 MsgInfo *tmp = (MsgInfo *)cur->data;
2145 if(MSG_IS_UNREAD(tmp->flags) && !MSG_IS_IGNORE_THREAD(tmp->flags)) {
2147 info->folder->unreadmarked_msgs++;
2149 info->folder->unreadmarked_msgs--;
2150 folder_item_update(info->folder, F_ITEM_UPDATE_MSGCNT);
2152 procmsg_msginfo_free(tmp);
2154 g_slist_free(children);
2158 * Set the destination folder for a copy or move operation
2160 * \param msginfo The message which's destination folder is changed
2161 * \param to_folder The destination folder for the operation
2163 void procmsg_msginfo_set_to_folder(MsgInfo *msginfo, FolderItem *to_folder)
2165 if(msginfo->to_folder != NULL) {
2166 msginfo->to_folder->op_count--;
2167 folder_item_update(msginfo->to_folder, F_ITEM_UPDATE_MSGCNT);
2169 msginfo->to_folder = to_folder;
2170 if(to_folder != NULL) {
2171 to_folder->op_count++;
2172 folder_item_update(msginfo->to_folder, F_ITEM_UPDATE_MSGCNT);
2177 * Apply filtering actions to the msginfo
2179 * \param msginfo The MsgInfo describing the message that should be filtered
2180 * \return TRUE if the message was moved and MsgInfo is now invalid,
2183 static gboolean procmsg_msginfo_filter(MsgInfo *msginfo, PrefsAccount* ac_prefs)
2185 MailFilteringData mail_filtering_data;
2187 mail_filtering_data.msginfo = msginfo;
2188 mail_filtering_data.msglist = NULL;
2189 mail_filtering_data.filtered = NULL;
2190 mail_filtering_data.unfiltered = NULL;
2191 mail_filtering_data.account = ac_prefs;
2193 if (!ac_prefs || ac_prefs->filterhook_on_recv)
2194 if (hooks_invoke(MAIL_FILTERING_HOOKLIST, &mail_filtering_data))
2197 /* filter if enabled in prefs or move to inbox if not */
2198 if((filtering_rules != NULL) &&
2199 filter_message_by_msginfo(filtering_rules, msginfo, ac_prefs,
2200 FILTERING_INCORPORATION, NULL)) {
2207 void procmsg_msglist_filter(GSList *list, PrefsAccount *ac,
2208 GSList **filtered, GSList **unfiltered,
2211 GSList *cur, *to_do = NULL;
2212 gint total = 0, curnum = 0;
2213 MailFilteringData mail_filtering_data;
2215 cm_return_if_fail(filtered != NULL);
2216 cm_return_if_fail(unfiltered != NULL);
2224 total = g_slist_length(list);
2228 *unfiltered = g_slist_copy(list);
2232 statusbar_print_all(_("Filtering messages...\n"));
2234 mail_filtering_data.msginfo = NULL;
2235 mail_filtering_data.msglist = list;
2236 mail_filtering_data.filtered = NULL;
2237 mail_filtering_data.unfiltered = NULL;
2238 mail_filtering_data.account = ac;
2240 if (!ac || ac->filterhook_on_recv)
2241 hooks_invoke(MAIL_LISTFILTERING_HOOKLIST, &mail_filtering_data);
2243 if (mail_filtering_data.filtered == NULL &&
2244 mail_filtering_data.unfiltered == NULL) {
2245 /* nothing happened */
2246 debug_print(MAIL_LISTFILTERING_HOOKLIST " did nothing. filtering whole list normally.\n");
2249 if (mail_filtering_data.filtered != NULL) {
2250 /* keep track of what's been filtered by the hooks */
2251 debug_print(MAIL_LISTFILTERING_HOOKLIST " filtered some stuff. total %d filtered %d unfilt %d.\n",
2252 g_slist_length(list),
2253 g_slist_length(mail_filtering_data.filtered),
2254 g_slist_length(mail_filtering_data.unfiltered));
2256 *filtered = g_slist_copy(mail_filtering_data.filtered);
2258 if (mail_filtering_data.unfiltered != NULL) {
2259 /* what the hooks didn't handle will go in filtered or
2260 * unfiltered in the next loop */
2261 debug_print(MAIL_LISTFILTERING_HOOKLIST " left unfiltered stuff. total %d filtered %d unfilt %d.\n",
2262 g_slist_length(list),
2263 g_slist_length(mail_filtering_data.filtered),
2264 g_slist_length(mail_filtering_data.unfiltered));
2265 to_do = mail_filtering_data.unfiltered;
2268 for (cur = to_do; cur; cur = cur->next) {
2269 MsgInfo *info = (MsgInfo *)cur->data;
2270 if (procmsg_msginfo_filter(info, ac))
2271 *filtered = g_slist_prepend(*filtered, info);
2273 *unfiltered = g_slist_prepend(*unfiltered, info);
2274 statusbar_progress_all(curnum++, total, prefs_common.statusbar_update_step);
2277 g_slist_free(mail_filtering_data.filtered);
2278 g_slist_free(mail_filtering_data.unfiltered);
2280 *filtered = g_slist_reverse(*filtered);
2281 *unfiltered = g_slist_reverse(*unfiltered);
2283 statusbar_progress_all(0,0,0);
2284 statusbar_pop_all();
2287 MsgInfo *procmsg_msginfo_new_from_mimeinfo(MsgInfo *src_msginfo, MimeInfo *mimeinfo)
2289 MsgInfo *tmp_msginfo = NULL;
2290 MsgFlags flags = {0, 0};
2291 gchar *tmpfile = get_tmp_file();
2292 FILE *fp = g_fopen(tmpfile, "wb");
2294 if (!mimeinfo || mimeinfo->type != MIMETYPE_MESSAGE ||
2295 g_ascii_strcasecmp(mimeinfo->subtype, "rfc822")) {
2296 g_warning("procmsg_msginfo_new_from_mimeinfo(): unsuitable mimeinfo");
2303 if (fp && procmime_write_mimeinfo(mimeinfo, fp) >= 0) {
2306 tmp_msginfo = procheader_parse_file(
2313 if (tmp_msginfo != NULL) {
2315 tmp_msginfo->folder = src_msginfo->folder;
2316 tmp_msginfo->plaintext_file = g_strdup(tmpfile);
2318 g_warning("procmsg_msginfo_new_from_mimeinfo(): Can't generate new msginfo");
2326 static GSList *spam_learners = NULL;
2328 void procmsg_register_spam_learner (int (*learn_func)(MsgInfo *info, GSList *list, gboolean spam))
2330 if (!g_slist_find(spam_learners, learn_func))
2331 spam_learners = g_slist_append(spam_learners, learn_func);
2332 if (mainwindow_get_mainwindow()) {
2333 main_window_set_menu_sensitive(mainwindow_get_mainwindow());
2334 summary_set_menu_sensitive(
2335 mainwindow_get_mainwindow()->summaryview);
2336 toolbar_main_set_sensitive(mainwindow_get_mainwindow());
2340 void procmsg_unregister_spam_learner (int (*learn_func)(MsgInfo *info, GSList *list, gboolean spam))
2342 spam_learners = g_slist_remove(spam_learners, learn_func);
2343 if (mainwindow_get_mainwindow()) {
2344 main_window_set_menu_sensitive(mainwindow_get_mainwindow());
2345 summary_set_menu_sensitive(
2346 mainwindow_get_mainwindow()->summaryview);
2347 toolbar_main_set_sensitive(mainwindow_get_mainwindow());
2351 gboolean procmsg_spam_can_learn(void)
2353 return g_slist_length(spam_learners) > 0;
2356 int procmsg_spam_learner_learn (MsgInfo *info, GSList *list, gboolean spam)
2358 GSList *cur = spam_learners;
2360 for (; cur; cur = cur->next) {
2361 int ((*func)(MsgInfo *info, GSList *list, gboolean spam)) = cur->data;
2362 ret |= func(info, list, spam);
2367 static gchar *spam_folder_item = NULL;
2368 static FolderItem * (*procmsg_spam_get_folder_func)(MsgInfo *msginfo) = NULL;
2369 void procmsg_spam_set_folder (const char *item_identifier, FolderItem *(*spam_get_folder_func)(MsgInfo *info))
2371 g_free(spam_folder_item);
2372 if (item_identifier)
2373 spam_folder_item = g_strdup(item_identifier);
2375 spam_folder_item = NULL;
2376 if (spam_get_folder_func != NULL)
2377 procmsg_spam_get_folder_func = spam_get_folder_func;
2379 procmsg_spam_get_folder_func = NULL;
2382 FolderItem *procmsg_spam_get_folder (MsgInfo *msginfo)
2384 FolderItem *item = NULL;
2386 if (procmsg_spam_get_folder_func)
2387 item = procmsg_spam_get_folder_func(msginfo);
2388 if (item == NULL && spam_folder_item)
2389 item = folder_find_item_from_identifier(spam_folder_item);
2391 item = folder_get_default_trash();
2395 static void item_has_queued_mails(FolderItem *item, gpointer data)
2397 gboolean *result = (gboolean *)data;
2398 if (*result == TRUE)
2400 if (folder_has_parent_of_type(item, F_QUEUE)) {
2401 if (item->total_msgs == 0)
2404 GSList *msglist = folder_item_get_msg_list(item);
2406 for (cur = msglist; cur; cur = cur->next) {
2407 MsgInfo *msginfo = (MsgInfo *)cur->data;
2408 if (!MSG_IS_DELETED(msginfo->flags) &&
2409 !MSG_IS_LOCKED(msginfo->flags)) {
2414 procmsg_msg_list_free(msglist);
2419 gboolean procmsg_have_queued_mails_fast (void)
2421 gboolean result = FALSE;
2422 folder_func_to_all_folders(item_has_queued_mails, &result);
2426 static void item_has_trashed_mails(FolderItem *item, gpointer data)
2428 gboolean *result = (gboolean *)data;
2429 if (*result == TRUE)
2431 if (folder_has_parent_of_type(item, F_TRASH) && item->total_msgs > 0)
2435 gboolean procmsg_have_trashed_mails_fast (void)
2437 gboolean result = FALSE;
2438 folder_func_to_all_folders(item_has_trashed_mails, &result);
2442 gchar *procmsg_msginfo_get_tags_str(MsgInfo *msginfo)
2450 if (msginfo->tags == NULL)
2452 for (cur = msginfo->tags; cur; cur = cur->next) {
2453 const gchar *tag = tags_get_tag(GPOINTER_TO_INT(cur->data));
2457 tags = g_strdup(tag);
2459 int olen = strlen(tags);
2460 int nlen = olen + strlen(tag) + 2 /* strlen(", ") */;
2461 tags = g_realloc(tags, nlen+1);
2464 strcpy(tags+olen, ", ");
2465 strcpy(tags+olen+2, tag);
2472 void procmsg_msginfo_update_tags(MsgInfo *msginfo, gboolean set, gint id)
2480 msginfo->tags = g_slist_remove(
2482 GINT_TO_POINTER(id));
2483 changed.data = GINT_TO_POINTER(id);
2484 changed.next = NULL;
2485 folder_item_commit_tags(msginfo->folder, msginfo, NULL, &changed);
2487 if (!g_slist_find(msginfo->tags, GINT_TO_POINTER(id))) {
2488 msginfo->tags = g_slist_append(
2490 GINT_TO_POINTER(id));
2492 changed.data = GINT_TO_POINTER(id);
2493 changed.next = NULL;
2494 folder_item_commit_tags(msginfo->folder, msginfo, &changed, NULL);
2499 void procmsg_msginfo_clear_tags(MsgInfo *msginfo)
2501 GSList *unset = msginfo->tags;
2502 msginfo->tags = NULL;
2503 folder_item_commit_tags(msginfo->folder, msginfo, NULL, unset);
2504 g_slist_free(unset);