2 * Sylpheed -- a GTK+ based, lightweight, and fast e-mail client
3 * Copyright (C) 1999-2006 Hiroyuki Yamamoto and the Sylpheed-Claws 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 2 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, write to the Free Software
17 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
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"
48 static gint procmsg_send_message_queue_full(const gchar *file, gboolean keep_session);
56 Q_MAIL_ACCOUNT_ID = 4,
57 Q_NEWS_ACCOUNT_ID = 5,
58 Q_SAVE_COPY_FOLDER = 6,
59 Q_REPLY_MESSAGE_ID = 7,
66 GHashTable *procmsg_msg_hash_table_create(GSList *mlist)
68 GHashTable *msg_table;
70 if (mlist == NULL) return NULL;
72 msg_table = g_hash_table_new(NULL, g_direct_equal);
73 procmsg_msg_hash_table_append(msg_table, mlist);
78 void procmsg_msg_hash_table_append(GHashTable *msg_table, GSList *mlist)
83 if (msg_table == NULL || mlist == NULL) return;
85 for (cur = mlist; cur != NULL; cur = cur->next) {
86 msginfo = (MsgInfo *)cur->data;
88 g_hash_table_insert(msg_table,
89 GUINT_TO_POINTER(msginfo->msgnum),
94 GHashTable *procmsg_to_folder_hash_table_create(GSList *mlist)
96 GHashTable *msg_table;
100 if (mlist == NULL) return NULL;
102 msg_table = g_hash_table_new(NULL, g_direct_equal);
104 for (cur = mlist; cur != NULL; cur = cur->next) {
105 msginfo = (MsgInfo *)cur->data;
106 g_hash_table_insert(msg_table, msginfo->to_folder, msginfo);
112 gint procmsg_get_last_num_in_msg_list(GSList *mlist)
118 for (cur = mlist; cur != NULL; cur = cur->next) {
119 msginfo = (MsgInfo *)cur->data;
120 if (msginfo && msginfo->msgnum > last)
121 last = msginfo->msgnum;
127 void procmsg_msg_list_free(GSList *mlist)
132 for (cur = mlist; cur != NULL; cur = cur->next) {
133 msginfo = (MsgInfo *)cur->data;
134 procmsg_msginfo_free(msginfo);
148 /* CLAWS subject threading:
150 in the first round it inserts subject lines in a
151 relation (subject <-> node)
153 the second round finishes the threads by attaching
154 matching subject lines to the one found in the
155 relation. will use the oldest node with the same
156 subject that is not more then thread_by_subject_max_age
157 days old (see subject_relation_lookup)
160 static void subject_relation_insert(GRelation *relation, GNode *node)
165 g_return_if_fail(relation != NULL);
166 g_return_if_fail(node != NULL);
167 msginfo = (MsgInfo *) node->data;
168 g_return_if_fail(msginfo != NULL);
170 subject = msginfo->subject;
173 subject += subject_get_prefix_length(subject);
175 g_relation_insert(relation, subject, node);
178 static GNode *subject_relation_lookup(GRelation *relation, MsgInfo *msginfo)
185 g_return_val_if_fail(relation != NULL, NULL);
187 subject = msginfo->subject;
190 prefix_length = subject_get_prefix_length(subject);
191 if (prefix_length <= 0)
193 subject += prefix_length;
195 tuples = g_relation_select(relation, subject, 0);
199 if (tuples->len > 0) {
201 GNode *relation_node;
202 MsgInfo *relation_msginfo = NULL, *best_msginfo = NULL;
205 /* check all nodes with the same subject to find the best parent */
206 for (i = 0; i < tuples->len; i++) {
207 relation_node = (GNode *) g_tuples_index(tuples, i, 1);
208 relation_msginfo = (MsgInfo *) relation_node->data;
211 /* best node should be the oldest in the found nodes */
212 /* parent node must not be older then msginfo */
213 if ((relation_msginfo->date_t < msginfo->date_t) &&
214 ((best_msginfo == NULL) ||
215 (best_msginfo->date_t > relation_msginfo->date_t)))
218 /* parent node must not be more then thread_by_subject_max_age
219 days older then msginfo */
220 if (abs(difftime(msginfo->date_t, relation_msginfo->date_t)) >
221 prefs_common.thread_by_subject_max_age * 3600 * 24)
224 /* can add new tests for all matching
225 nodes found by subject */
228 node = relation_node;
229 best_msginfo = relation_msginfo;
234 g_tuples_destroy(tuples);
238 /* return the reversed thread tree */
239 GNode *procmsg_get_thread_tree(GSList *mlist)
241 GNode *root, *parent, *node, *next;
242 GHashTable *msgid_table;
243 GRelation *subject_relation;
248 root = g_node_new(NULL);
249 msgid_table = g_hash_table_new(g_str_hash, g_str_equal);
250 subject_relation = g_relation_new(2);
251 g_relation_index(subject_relation, 0, g_str_hash, g_str_equal);
253 for (; mlist != NULL; mlist = mlist->next) {
254 msginfo = (MsgInfo *)mlist->data;
257 if (msginfo->inreplyto) {
258 parent = g_hash_table_lookup(msgid_table, msginfo->inreplyto);
259 if (parent == NULL) {
263 node = g_node_insert_data_before
264 (parent, parent == root ? parent->children : NULL,
266 if ((msgid = msginfo->msgid) && g_hash_table_lookup(msgid_table, msgid) == NULL)
267 g_hash_table_insert(msgid_table, (gchar *)msgid, node);
269 /* CLAWS: add subject to relation (without prefix) */
270 if (prefs_common.thread_by_subject) {
271 subject_relation_insert(subject_relation, node);
275 /* complete the unfinished threads */
276 for (node = root->children; node != NULL; ) {
278 msginfo = (MsgInfo *)node->data;
281 if (msginfo->inreplyto)
282 parent = g_hash_table_lookup(msgid_table, msginfo->inreplyto);
284 /* try looking for the indirect parent */
285 if (!parent && msginfo->references) {
286 for (reflist = msginfo->references;
287 reflist != NULL; reflist = reflist->next)
288 if ((parent = g_hash_table_lookup
289 (msgid_table, reflist->data)) != NULL)
293 /* node should not be the parent, and node should not
294 be an ancestor of parent (circular reference) */
295 if (parent && parent != node &&
296 !g_node_is_ancestor(node, parent)) {
299 (parent, parent->children, node);
305 if (prefs_common.thread_by_subject) {
306 for (node = root->children; node && node != NULL;) {
308 msginfo = (MsgInfo *) node->data;
310 parent = subject_relation_lookup(subject_relation, msginfo);
312 /* the node may already be threaded by IN-REPLY-TO, so go up
314 find the parent node */
315 if (parent != NULL) {
316 if (g_node_is_ancestor(node, parent))
324 g_node_append(parent, node);
331 g_relation_destroy(subject_relation);
332 g_hash_table_destroy(msgid_table);
337 gint procmsg_move_messages(GSList *mlist)
339 GSList *cur, *movelist = NULL;
341 FolderItem *dest = NULL;
343 gboolean finished = TRUE;
344 if (!mlist) return 0;
346 folder_item_update_freeze();
349 for (cur = mlist; cur != NULL; cur = cur->next) {
350 msginfo = (MsgInfo *)cur->data;
351 if (!msginfo->to_folder) {
357 dest = msginfo->to_folder;
358 movelist = g_slist_append(movelist, msginfo);
359 } else if (dest == msginfo->to_folder) {
360 movelist = g_slist_append(movelist, msginfo);
364 procmsg_msginfo_set_to_folder(msginfo, NULL);
367 retval |= folder_item_move_msgs(dest, movelist);
368 g_slist_free(movelist);
371 if (finished == FALSE) {
377 folder_item_update_thaw();
381 void procmsg_copy_messages(GSList *mlist)
383 GSList *cur, *copylist = NULL;
385 FolderItem *dest = NULL;
386 gboolean finished = TRUE;
389 folder_item_update_freeze();
392 for (cur = mlist; cur != NULL; cur = cur->next) {
393 msginfo = (MsgInfo *)cur->data;
394 if (!msginfo->to_folder) {
400 dest = msginfo->to_folder;
401 copylist = g_slist_append(copylist, msginfo);
402 } else if (dest == msginfo->to_folder) {
403 copylist = g_slist_append(copylist, msginfo);
407 procmsg_msginfo_set_to_folder(msginfo, NULL);
410 folder_item_copy_msgs(dest, copylist);
411 g_slist_free(copylist);
414 if (finished == FALSE) {
420 folder_item_update_thaw();
423 gchar *procmsg_get_message_file_path(MsgInfo *msginfo)
427 g_return_val_if_fail(msginfo != NULL, NULL);
429 if (msginfo->plaintext_file)
430 file = g_strdup(msginfo->plaintext_file);
432 file = folder_item_fetch_msg(msginfo->folder, msginfo->msgnum);
438 gchar *procmsg_get_message_file(MsgInfo *msginfo)
440 gchar *filename = NULL;
442 g_return_val_if_fail(msginfo != NULL, NULL);
444 filename = folder_item_fetch_msg(msginfo->folder, msginfo->msgnum);
446 debug_print("can't fetch message %d\n", msginfo->msgnum);
451 gchar *procmsg_get_message_file_full(MsgInfo *msginfo, gboolean headers, gboolean body)
453 gchar *filename = NULL;
455 g_return_val_if_fail(msginfo != NULL, NULL);
457 filename = folder_item_fetch_msg_full(msginfo->folder, msginfo->msgnum,
460 debug_print("can't fetch message %d\n", msginfo->msgnum);
465 GSList *procmsg_get_message_file_list(GSList *mlist)
467 GSList *file_list = NULL;
469 MsgFileInfo *fileinfo;
472 while (mlist != NULL) {
473 msginfo = (MsgInfo *)mlist->data;
474 file = procmsg_get_message_file(msginfo);
476 procmsg_message_file_list_free(file_list);
479 fileinfo = g_new(MsgFileInfo, 1);
480 fileinfo->msginfo = procmsg_msginfo_new_ref(msginfo);
481 fileinfo->file = file;
482 fileinfo->flags = g_new(MsgFlags, 1);
483 *fileinfo->flags = msginfo->flags;
484 file_list = g_slist_prepend(file_list, fileinfo);
488 file_list = g_slist_reverse(file_list);
493 void procmsg_message_file_list_free(MsgInfoList *file_list)
496 MsgFileInfo *fileinfo;
498 for (cur = file_list; cur != NULL; cur = cur->next) {
499 fileinfo = (MsgFileInfo *)cur->data;
500 procmsg_msginfo_free(fileinfo->msginfo);
501 g_free(fileinfo->file);
502 g_free(fileinfo->flags);
506 g_slist_free(file_list);
509 FILE *procmsg_open_message(MsgInfo *msginfo)
514 g_return_val_if_fail(msginfo != NULL, NULL);
516 file = procmsg_get_message_file_path(msginfo);
517 g_return_val_if_fail(file != NULL, NULL);
519 if (!is_file_exist(file)) {
521 file = procmsg_get_message_file(msginfo);
526 if ((fp = g_fopen(file, "rb")) == NULL) {
527 FILE_OP_ERROR(file, "fopen");
534 if (MSG_IS_QUEUED(msginfo->flags) || MSG_IS_DRAFT(msginfo->flags)) {
537 while (fgets(buf, sizeof(buf), fp) != NULL)
538 if (buf[0] == '\r' || buf[0] == '\n') break;
544 gboolean procmsg_msg_exist(MsgInfo *msginfo)
549 if (!msginfo) return FALSE;
551 path = folder_item_get_path(msginfo->folder);
553 ret = !folder_item_is_msg_changed(msginfo->folder, msginfo);
559 void procmsg_get_filter_keyword(MsgInfo *msginfo, gchar **header, gchar **key,
560 PrefsFilterType type)
562 static HeaderEntry hentry[] = {{"X-BeenThere:", NULL, TRUE},
563 {"X-ML-Name:", NULL, TRUE},
564 {"X-List:", NULL, TRUE},
565 {"X-Mailing-list:", NULL, TRUE},
566 {"List-Id:", NULL, TRUE},
567 {"X-Sequence:", NULL, TRUE},
568 {"Sender:", NULL, TRUE},
569 {"List-Post:", NULL, TRUE},
570 {NULL, NULL, FALSE}};
576 H_X_MAILING_LIST = 3,
585 g_return_if_fail(msginfo != NULL);
586 g_return_if_fail(header != NULL);
587 g_return_if_fail(key != NULL);
596 if ((fp = procmsg_open_message(msginfo)) == NULL)
598 procheader_get_header_fields(fp, hentry);
601 #define SET_FILTER_KEY(hstr, idx) \
603 *header = g_strdup(hstr); \
604 *key = hentry[idx].body; \
605 hentry[idx].body = NULL; \
608 if (hentry[H_X_BEENTHERE].body != NULL) {
609 SET_FILTER_KEY("header \"X-BeenThere\"", H_X_BEENTHERE);
610 } else if (hentry[H_X_ML_NAME].body != NULL) {
611 SET_FILTER_KEY("header \"X-ML-Name\"", H_X_ML_NAME);
612 } else if (hentry[H_X_LIST].body != NULL) {
613 SET_FILTER_KEY("header \"X-List\"", H_X_LIST);
614 } else if (hentry[H_X_MAILING_LIST].body != NULL) {
615 SET_FILTER_KEY("header \"X-Mailing-List\"", H_X_MAILING_LIST);
616 } else if (hentry[H_LIST_ID].body != NULL) {
617 SET_FILTER_KEY("header \"List-Id\"", H_LIST_ID);
618 extract_list_id_str(*key);
619 } else if (hentry[H_X_SEQUENCE].body != NULL) {
622 SET_FILTER_KEY("X-Sequence", H_X_SEQUENCE);
625 while (*p != '\0' && !g_ascii_isspace(*p)) p++;
626 while (g_ascii_isspace(*p)) p++;
627 if (g_ascii_isdigit(*p)) {
633 } else if (hentry[H_SENDER].body != NULL) {
634 SET_FILTER_KEY("header \"Sender\"", H_SENDER);
635 } else if (hentry[H_LIST_POST].body != NULL) {
636 SET_FILTER_KEY("header \"Sender\"", H_LIST_POST);
637 } else if (msginfo->to) {
638 *header = g_strdup("to");
639 *key = g_strdup(msginfo->to);
640 } else if (msginfo->subject) {
641 *header = g_strdup("subject");
642 *key = g_strdup(msginfo->subject);
645 #undef SET_FILTER_KEY
647 g_free(hentry[H_X_BEENTHERE].body);
648 hentry[H_X_BEENTHERE].body = NULL;
649 g_free(hentry[H_X_ML_NAME].body);
650 hentry[H_X_ML_NAME].body = NULL;
651 g_free(hentry[H_X_LIST].body);
652 hentry[H_X_LIST].body = NULL;
653 g_free(hentry[H_X_MAILING_LIST].body);
654 hentry[H_X_MAILING_LIST].body = NULL;
655 g_free(hentry[H_LIST_ID].body);
656 hentry[H_LIST_ID].body = NULL;
657 g_free(hentry[H_SENDER].body);
658 hentry[H_SENDER].body = NULL;
659 g_free(hentry[H_LIST_POST].body);
660 hentry[H_LIST_POST].body = NULL;
664 *header = g_strdup("from");
665 *key = g_strdup(msginfo->from);
668 *header = g_strdup("to");
669 *key = g_strdup(msginfo->to);
671 case FILTER_BY_SUBJECT:
672 *header = g_strdup("subject");
673 *key = g_strdup(msginfo->subject);
680 void procmsg_empty_trash(FolderItem *trash)
685 (trash->stype != F_TRASH &&
686 !folder_has_parent_of_type(trash, F_TRASH)))
689 if (trash && trash->total_msgs > 0) {
690 GSList *mlist = folder_item_get_msg_list(trash);
692 for (cur = mlist ; cur != NULL ; cur = cur->next) {
693 MsgInfo * msginfo = (MsgInfo *) cur->data;
694 if (MSG_IS_LOCKED(msginfo->flags))
696 if (msginfo->total_size != 0 &&
697 msginfo->size != (off_t)msginfo->total_size)
698 partial_mark_for_delete(msginfo);
700 procmsg_msginfo_free(msginfo);
703 folder_item_remove_all_msg(trash);
706 if (!trash->node || !trash->node->children)
709 node = trash->node->children;
710 while (node != NULL) {
712 procmsg_empty_trash(FOLDER_ITEM(node->data));
717 void procmsg_empty_all_trash(void)
722 for (cur = folder_get_list(); cur != NULL; cur = cur->next) {
723 Folder *folder = FOLDER(cur->data);
724 trash = folder->trash;
725 procmsg_empty_trash(trash);
726 if (folder->account && folder->account->set_trash_folder &&
727 folder_find_item_from_identifier(folder->account->trash_folder))
729 folder_find_item_from_identifier(folder->account->trash_folder));
733 static PrefsAccount *procmsg_get_account_from_file(const gchar *file)
735 PrefsAccount *mailac = NULL;
739 static HeaderEntry qentry[] = {{"S:", NULL, FALSE},
740 {"SSV:", NULL, FALSE},
742 {"NG:", NULL, FALSE},
743 {"MAID:", NULL, FALSE},
744 {"NAID:", NULL, FALSE},
745 {"SCF:", NULL, FALSE},
746 {"RMID:", NULL, FALSE},
747 {"FMID:", NULL, FALSE},
748 {"X-Sylpheed-Privacy-System:", NULL, FALSE},
749 {"X-Sylpheed-Encrypt:", NULL, FALSE},
750 {"X-Sylpheed-Encrypt-Data:", NULL, FALSE},
751 {NULL, NULL, FALSE}};
753 g_return_val_if_fail(file != NULL, NULL);
755 if ((fp = g_fopen(file, "rb")) == NULL) {
756 FILE_OP_ERROR(file, "fopen");
760 while ((hnum = procheader_get_one_field(buf, sizeof(buf), fp, qentry))
762 gchar *p = buf + strlen(qentry[hnum].name);
764 if (hnum == Q_MAIL_ACCOUNT_ID) {
765 mailac = account_find_from_id(atoi(p));
773 static GSList *procmsg_list_sort_by_account(FolderItem *queue, GSList *list)
775 GSList *result = NULL;
777 PrefsAccount *last_account = NULL;
780 gboolean nothing_to_sort = TRUE;
785 orig = g_slist_copy(list);
787 msg = (MsgInfo *)orig->data;
789 for (cur = orig; cur; cur = cur->next)
790 debug_print("sort before %s\n", ((MsgInfo *)cur->data)->from);
795 nothing_to_sort = TRUE;
799 PrefsAccount *ac = NULL;
800 msg = (MsgInfo *)cur->data;
801 file = folder_item_fetch_msg(queue, msg->msgnum);
802 ac = procmsg_get_account_from_file(file);
805 if (last_account == NULL || (ac != NULL && ac == last_account)) {
806 result = g_slist_append(result, msg);
807 orig = g_slist_remove(orig, msg);
809 nothing_to_sort = FALSE;
815 if (orig || g_slist_length(orig)) {
816 if (!last_account && nothing_to_sort) {
817 /* can't find an account for the rest of the list */
820 result = g_slist_append(result, cur->data);
831 for (cur = result; cur; cur = cur->next)
832 debug_print("sort after %s\n", ((MsgInfo *)cur->data)->from);
839 static gboolean procmsg_is_last_for_account(FolderItem *queue, MsgInfo *msginfo, GSList *elem)
841 gchar *file = folder_item_fetch_msg(queue, msginfo->msgnum);
842 PrefsAccount *ac = procmsg_get_account_from_file(file);
845 for (cur = elem; cur; cur = cur->next) {
846 MsgInfo *cur_msginfo = (MsgInfo *)cur->data;
847 file = folder_item_fetch_msg(queue, cur_msginfo->msgnum);
849 if (cur_msginfo != msginfo && !MSG_IS_LOCKED(cur_msginfo->flags)) {
850 if (procmsg_get_account_from_file(file) == ac) {
862 *\brief Send messages in queue
864 *\param queue Queue folder to process
865 *\param save_msgs Unused
867 *\return Number of messages sent, negative if an error occurred
868 * positive if no error occurred
870 gint procmsg_send_queue(FolderItem *queue, gboolean save_msgs)
872 gint sent = 0, err = 0;
874 GSList *sorted_list = NULL;
878 queue = folder_get_default_queue();
879 g_return_val_if_fail(queue != NULL, -1);
881 folder_item_scan(queue);
882 list = folder_item_get_msg_list(queue);
884 /* sort the list per sender account; this helps reusing the same SMTP server */
885 sorted_list = procmsg_list_sort_by_account(queue, list);
887 for (elem = sorted_list; elem != NULL; elem = elem->next) {
891 msginfo = (MsgInfo *)(elem->data);
892 if (!MSG_IS_LOCKED(msginfo->flags)) {
893 file = folder_item_fetch_msg(queue, msginfo->msgnum);
895 if (procmsg_send_message_queue_full(file,
896 !procmsg_is_last_for_account(queue, msginfo, elem)) < 0) {
897 g_warning("Sending queued message %d failed.\n",
902 * We save in procmsg_send_message_queue because
903 * we need the destination folder from the queue
907 procmsg_save_to_outbox
908 (queue->folder->outbox,
912 folder_item_remove_msg(queue, msginfo->msgnum);
917 /* FIXME: supposedly if only one message is locked, and queue
918 * is being flushed, the following free says something like
919 * "freeing msg ## in folder (nil)". */
920 procmsg_msginfo_free(msginfo);
923 g_slist_free(sorted_list);
924 folder_item_scan(queue);
926 if (queue->node && queue->node->children) {
927 node = queue->node->children;
928 while (node != NULL) {
931 res = procmsg_send_queue(FOLDER_ITEM(node->data), save_msgs);
940 return (err != 0 ? -err : sent);
944 *\brief Determine if a queue folder is empty
946 *\param queue Queue folder to process
948 *\return TRUE if the queue folder is empty, otherwise return FALSE
950 gboolean procmsg_queue_is_empty(FolderItem *queue)
953 gboolean res = FALSE;
955 queue = folder_get_default_queue();
956 g_return_val_if_fail(queue != NULL, TRUE);
958 folder_item_scan(queue);
959 list = folder_item_get_msg_list(queue);
960 res = (list == NULL);
961 procmsg_msg_list_free(list);
965 if (queue->node && queue->node->children) {
966 node = queue->node->children;
967 while (node != NULL) {
969 if (!procmsg_queue_is_empty(FOLDER_ITEM(node->data)))
978 gint procmsg_remove_special_headers(const gchar *in, const gchar *out)
983 if ((fp = g_fopen(in, "rb")) == NULL) {
984 FILE_OP_ERROR(in, "fopen");
987 if ((outfp = g_fopen(out, "wb")) == NULL) {
988 FILE_OP_ERROR(out, "fopen");
992 while (fgets(buf, sizeof(buf), fp) != NULL)
993 if (buf[0] == '\r' || buf[0] == '\n') break;
994 while (fgets(buf, sizeof(buf), fp) != NULL)
1001 gint procmsg_save_to_outbox(FolderItem *outbox, const gchar *file,
1005 MsgInfo *msginfo, *tmp_msginfo;
1006 MsgFlags flag = {0, 0};
1008 debug_print("saving sent message...\n");
1011 outbox = folder_get_default_outbox();
1012 g_return_val_if_fail(outbox != NULL, -1);
1014 /* remove queueing headers */
1016 gchar tmp[MAXPATHLEN + 1];
1018 g_snprintf(tmp, sizeof(tmp), "%s%ctmpmsg.out.%08x",
1019 get_rc_dir(), G_DIR_SEPARATOR, (guint) rand());
1021 if (procmsg_remove_special_headers(file, tmp) !=0)
1024 folder_item_scan(outbox);
1025 if ((num = folder_item_add_msg(outbox, tmp, &flag, TRUE)) < 0) {
1026 g_warning("can't save message\n");
1031 folder_item_scan(outbox);
1032 if ((num = folder_item_add_msg
1033 (outbox, file, &flag, FALSE)) < 0) {
1034 g_warning("can't save message\n");
1039 msginfo = folder_item_get_msginfo(outbox, num); /* refcnt++ */
1040 tmp_msginfo = procmsg_msginfo_get_full_info(msginfo); /* refcnt++ */
1041 if (msginfo != NULL) {
1042 procmsg_msginfo_unset_flags(msginfo, ~0, 0);
1043 procmsg_msginfo_free(msginfo); /* refcnt-- */
1044 /* tmp_msginfo == msginfo */
1045 if (tmp_msginfo && (msginfo->dispositionnotificationto ||
1046 msginfo->returnreceiptto)) {
1047 procmsg_msginfo_set_flags(msginfo, MSG_RETRCPT_SENT, 0);
1048 procmsg_msginfo_free(msginfo); /* refcnt-- */
1055 void procmsg_print_message(MsgInfo *msginfo, const gchar *cmdline)
1057 static const gchar *def_cmd = "lpr %s";
1058 static guint id = 0;
1064 g_return_if_fail(msginfo);
1066 if (procmime_msginfo_is_encrypted(msginfo))
1067 tmpfp = procmime_get_first_encrypted_text_content(msginfo);
1069 tmpfp = procmime_get_first_text_content(msginfo);
1070 if (tmpfp == NULL) {
1071 g_warning("Can't get text part\n");
1075 prtmp = g_strdup_printf("%s%cprinttmp.%08x",
1076 get_mime_tmp_dir(), G_DIR_SEPARATOR, id++);
1078 if ((prfp = g_fopen(prtmp, "wb")) == NULL) {
1079 FILE_OP_ERROR(prtmp, "fopen");
1085 if (msginfo->date) fprintf(prfp, "Date: %s\n", msginfo->date);
1086 if (msginfo->from) fprintf(prfp, "From: %s\n", msginfo->from);
1087 if (msginfo->to) fprintf(prfp, "To: %s\n", msginfo->to);
1088 if (msginfo->cc) fprintf(prfp, "Cc: %s\n", msginfo->cc);
1089 if (msginfo->newsgroups)
1090 fprintf(prfp, "Newsgroups: %s\n", msginfo->newsgroups);
1091 if (msginfo->subject) fprintf(prfp, "Subject: %s\n", msginfo->subject);
1094 while (fgets(buf, sizeof(buf), tmpfp) != NULL)
1100 if (cmdline && (p = strchr(cmdline, '%')) && *(p + 1) == 's' &&
1101 !strchr(p + 2, '%'))
1102 g_snprintf(buf, sizeof(buf) - 1, cmdline, prtmp);
1105 g_warning("Print command line is invalid: '%s'\n",
1107 g_snprintf(buf, sizeof(buf) - 1, def_cmd, prtmp);
1113 if (buf[strlen(buf) - 1] != '&') strcat(buf, "&");
1117 MsgInfo *procmsg_msginfo_new_ref(MsgInfo *msginfo)
1124 MsgInfo *procmsg_msginfo_new(void)
1126 MsgInfo *newmsginfo;
1128 newmsginfo = g_new0(MsgInfo, 1);
1129 newmsginfo->refcnt = 1;
1134 MsgInfo *procmsg_msginfo_copy(MsgInfo *msginfo)
1136 MsgInfo *newmsginfo;
1139 if (msginfo == NULL) return NULL;
1141 newmsginfo = g_new0(MsgInfo, 1);
1143 newmsginfo->refcnt = 1;
1145 #define MEMBCOPY(mmb) newmsginfo->mmb = msginfo->mmb
1146 #define MEMBDUP(mmb) newmsginfo->mmb = msginfo->mmb ? \
1147 g_strdup(msginfo->mmb) : NULL
1162 MEMBDUP(newsgroups);
1169 MEMBCOPY(to_folder);
1173 MEMBDUP(dispositionnotificationto);
1174 MEMBDUP(returnreceiptto);
1176 refs = msginfo->references;
1177 for (refs = msginfo->references; refs != NULL; refs = refs->next) {
1178 newmsginfo->references = g_slist_prepend
1179 (newmsginfo->references, g_strdup(refs->data));
1181 newmsginfo->references = g_slist_reverse(newmsginfo->references);
1184 MEMBDUP(plaintext_file);
1189 MsgInfo *procmsg_msginfo_get_full_info(MsgInfo *msginfo)
1191 MsgInfo *full_msginfo;
1194 if (msginfo == NULL) return NULL;
1196 file = procmsg_get_message_file_path(msginfo);
1197 if (!file || !is_file_exist(file)) {
1199 file = procmsg_get_message_file(msginfo);
1201 if (!file || !is_file_exist(file)) {
1202 g_warning("procmsg_msginfo_get_full_info(): can't get message file.\n");
1206 full_msginfo = procheader_parse_file(file, msginfo->flags, TRUE, FALSE);
1208 if (!full_msginfo) return NULL;
1210 /* CLAWS: make sure we add the missing members; see:
1211 * procheader.c::procheader_get_headernames() */
1212 if (!msginfo->list_post)
1213 msginfo->list_post = g_strdup(full_msginfo->list_post);
1214 if (!msginfo->list_subscribe)
1215 msginfo->list_subscribe = g_strdup(full_msginfo->list_subscribe);
1216 if (!msginfo->list_unsubscribe)
1217 msginfo->list_unsubscribe = g_strdup(full_msginfo->list_unsubscribe);
1218 if (!msginfo->list_help)
1219 msginfo->list_help = g_strdup(full_msginfo->list_help);
1220 if (!msginfo->list_archive)
1221 msginfo->list_archive= g_strdup(full_msginfo->list_archive);
1222 if (!msginfo->list_owner)
1223 msginfo->list_owner = g_strdup(full_msginfo->list_owner);
1224 if (!msginfo->xface)
1225 msginfo->xface = g_strdup(full_msginfo->xface);
1227 msginfo->face = g_strdup(full_msginfo->face);
1228 if (!msginfo->dispositionnotificationto)
1229 msginfo->dispositionnotificationto =
1230 g_strdup(full_msginfo->dispositionnotificationto);
1231 if (!msginfo->returnreceiptto)
1232 msginfo->returnreceiptto = g_strdup
1233 (full_msginfo->returnreceiptto);
1234 if (!msginfo->partial_recv && full_msginfo->partial_recv)
1235 msginfo->partial_recv = g_strdup
1236 (full_msginfo->partial_recv);
1237 msginfo->total_size = full_msginfo->total_size;
1238 if (!msginfo->account_server && full_msginfo->account_server)
1239 msginfo->account_server = g_strdup
1240 (full_msginfo->account_server);
1241 if (!msginfo->account_login && full_msginfo->account_login)
1242 msginfo->account_login = g_strdup
1243 (full_msginfo->account_login);
1244 msginfo->planned_download = full_msginfo->planned_download;
1245 procmsg_msginfo_free(full_msginfo);
1247 return procmsg_msginfo_new_ref(msginfo);
1250 void procmsg_msginfo_free(MsgInfo *msginfo)
1252 if (msginfo == NULL) return;
1255 if (msginfo->refcnt > 0)
1258 if (msginfo->to_folder) {
1259 msginfo->to_folder->op_count--;
1260 folder_item_update(msginfo->to_folder, F_ITEM_UPDATE_MSGCNT);
1263 g_free(msginfo->fromspace);
1264 g_free(msginfo->returnreceiptto);
1265 g_free(msginfo->dispositionnotificationto);
1266 g_free(msginfo->xface);
1267 g_free(msginfo->face);
1269 g_free(msginfo->fromname);
1271 g_free(msginfo->date);
1272 g_free(msginfo->from);
1273 g_free(msginfo->to);
1274 g_free(msginfo->cc);
1275 g_free(msginfo->newsgroups);
1276 g_free(msginfo->subject);
1277 g_free(msginfo->msgid);
1278 g_free(msginfo->inreplyto);
1279 g_free(msginfo->xref);
1281 g_free(msginfo->list_post);
1282 g_free(msginfo->list_subscribe);
1283 g_free(msginfo->list_unsubscribe);
1284 g_free(msginfo->list_help);
1285 g_free(msginfo->list_archive);
1286 g_free(msginfo->list_owner);
1288 g_free(msginfo->partial_recv);
1289 g_free(msginfo->account_server);
1290 g_free(msginfo->account_login);
1292 slist_free_strings(msginfo->references);
1293 g_slist_free(msginfo->references);
1295 g_free(msginfo->plaintext_file);
1300 guint procmsg_msginfo_memusage(MsgInfo *msginfo)
1305 memusage += sizeof(MsgInfo);
1306 if (msginfo->fromname)
1307 memusage += strlen(msginfo->fromname);
1309 memusage += strlen(msginfo->date);
1311 memusage += strlen(msginfo->from);
1313 memusage += strlen(msginfo->to);
1315 memusage += strlen(msginfo->cc);
1316 if (msginfo->newsgroups)
1317 memusage += strlen(msginfo->newsgroups);
1318 if (msginfo->subject)
1319 memusage += strlen(msginfo->subject);
1321 memusage += strlen(msginfo->msgid);
1322 if (msginfo->inreplyto)
1323 memusage += strlen(msginfo->inreplyto);
1325 memusage += strlen(msginfo->xface);
1327 memusage += strlen(msginfo->face);
1328 if (msginfo->dispositionnotificationto)
1329 memusage += strlen(msginfo->dispositionnotificationto);
1330 if (msginfo->returnreceiptto)
1331 memusage += strlen(msginfo->returnreceiptto);
1332 for (refs = msginfo->references; refs; refs=refs->next) {
1333 gchar *r = (gchar *)refs->data;
1334 memusage += r?strlen(r):0;
1336 if (msginfo->fromspace)
1337 memusage += strlen(msginfo->fromspace);
1342 gint procmsg_cmp_msgnum_for_sort(gconstpointer a, gconstpointer b)
1344 const MsgInfo *msginfo1 = a;
1345 const MsgInfo *msginfo2 = b;
1352 return msginfo1->msgnum - msginfo2->msgnum;
1355 static gint procmsg_send_message_queue_full(const gchar *file, gboolean keep_session)
1357 static HeaderEntry qentry[] = {{"S:", NULL, FALSE},
1358 {"SSV:", NULL, FALSE},
1359 {"R:", NULL, FALSE},
1360 {"NG:", NULL, FALSE},
1361 {"MAID:", NULL, FALSE},
1362 {"NAID:", NULL, FALSE},
1363 {"SCF:", NULL, FALSE},
1364 {"RMID:", NULL, FALSE},
1365 {"FMID:", NULL, FALSE},
1366 {"X-Sylpheed-Privacy-System:", NULL, FALSE},
1367 {"X-Sylpheed-Encrypt:", NULL, FALSE},
1368 {"X-Sylpheed-Encrypt-Data:", NULL, FALSE},
1369 {NULL, NULL, FALSE}};
1372 gint mailval = 0, newsval = 0;
1374 gchar *smtpserver = NULL;
1375 GSList *to_list = NULL;
1376 GSList *newsgroup_list = NULL;
1377 gchar *savecopyfolder = NULL;
1378 gchar *replymessageid = NULL;
1379 gchar *fwdmessageid = NULL;
1380 gchar *privacy_system = NULL;
1381 gboolean encrypt = FALSE;
1382 gchar *encrypt_data = NULL;
1383 gchar buf[BUFFSIZE];
1385 PrefsAccount *mailac = NULL, *newsac = NULL;
1386 gboolean save_clear_text = TRUE;
1387 gchar *tmp_enc_file = NULL;
1391 g_return_val_if_fail(file != NULL, -1);
1393 if ((fp = g_fopen(file, "rb")) == NULL) {
1394 FILE_OP_ERROR(file, "fopen");
1398 while ((hnum = procheader_get_one_field(buf, sizeof(buf), fp, qentry))
1400 gchar *p = buf + strlen(qentry[hnum].name);
1408 if (smtpserver == NULL)
1409 smtpserver = g_strdup(p);
1412 to_list = address_list_append(to_list, p);
1415 newsgroup_list = newsgroup_list_append(newsgroup_list, p);
1417 case Q_MAIL_ACCOUNT_ID:
1418 mailac = account_find_from_id(atoi(p));
1420 case Q_NEWS_ACCOUNT_ID:
1421 newsac = account_find_from_id(atoi(p));
1423 case Q_SAVE_COPY_FOLDER:
1424 if (savecopyfolder == NULL)
1425 savecopyfolder = g_strdup(p);
1427 case Q_REPLY_MESSAGE_ID:
1428 if (replymessageid == NULL)
1429 replymessageid = g_strdup(p);
1431 case Q_FWD_MESSAGE_ID:
1432 if (fwdmessageid == NULL)
1433 fwdmessageid = g_strdup(p);
1435 case Q_PRIVACY_SYSTEM:
1436 if (privacy_system == NULL)
1437 privacy_system = g_strdup(p);
1443 case Q_ENCRYPT_DATA:
1444 if (encrypt_data == NULL)
1445 encrypt_data = g_strdup(p);
1449 filepos = ftell(fp);
1454 if (mailac && mailac->save_encrypted_as_clear_text
1455 && !mailac->encrypt_to_self)
1456 save_clear_text = TRUE;
1458 save_clear_text = FALSE;
1463 mimeinfo = procmime_scan_queue_file(file);
1464 if (!privacy_encrypt(privacy_system, mimeinfo, encrypt_data)
1465 || (fp = my_tmpfile()) == NULL
1466 || procmime_write_mimeinfo(mimeinfo, fp) < 0) {
1469 procmime_mimeinfo_free_all(mimeinfo);
1472 slist_free_strings(to_list);
1473 g_slist_free(to_list);
1474 slist_free_strings(newsgroup_list);
1475 g_slist_free(newsgroup_list);
1476 g_free(savecopyfolder);
1477 g_free(replymessageid);
1478 g_free(fwdmessageid);
1479 g_free(privacy_system);
1480 g_free(encrypt_data);
1485 if (!save_clear_text) {
1486 gchar *content = NULL;
1487 FILE *tmpfp = get_tmpfile_in_dir(get_mime_tmp_dir(), &tmp_enc_file);
1491 content = file_read_stream_to_str(fp);
1494 str_write_to_file(content, tmp_enc_file);
1497 g_warning("couldn't get tempfile\n");
1501 procmime_mimeinfo_free_all(mimeinfo);
1507 debug_print("Sending message by mail\n");
1509 g_warning("Queued message header is broken.\n");
1511 } else if (mailac && mailac->use_mail_command &&
1512 mailac->mail_command && (* mailac->mail_command)) {
1513 mailval = send_message_local(mailac->mail_command, fp);
1517 mailac = account_find_from_smtp_server(from, smtpserver);
1519 g_warning("Account not found. "
1520 "Using current account...\n");
1521 mailac = cur_account;
1526 mailval = send_message_smtp_full(mailac, to_list, fp, keep_session);
1528 PrefsAccount tmp_ac;
1530 g_warning("Account not found.\n");
1532 memset(&tmp_ac, 0, sizeof(PrefsAccount));
1533 tmp_ac.address = from;
1534 tmp_ac.smtp_server = smtpserver;
1535 tmp_ac.smtpport = SMTP_PORT;
1536 mailval = send_message_smtp(&tmp_ac, to_list, fp);
1539 } else if (!to_list && !newsgroup_list)
1543 fseek(fp, filepos, SEEK_SET);
1544 if (newsgroup_list && (mailval == 0)) {
1549 /* write to temporary file */
1550 tmp = g_strdup_printf("%s%ctmp%d", g_get_tmp_dir(),
1551 G_DIR_SEPARATOR, (gint)file);
1552 if ((tmpfp = g_fopen(tmp, "wb")) == NULL) {
1553 FILE_OP_ERROR(tmp, "fopen");
1555 alertpanel_error(_("Could not create temporary file for news sending."));
1557 if (change_file_mode_rw(tmpfp, tmp) < 0) {
1558 FILE_OP_ERROR(tmp, "chmod");
1559 g_warning("can't change file mode\n");
1562 while ((newsval == 0) && fgets(buf, sizeof(buf), fp) != NULL) {
1563 if (fputs(buf, tmpfp) == EOF) {
1564 FILE_OP_ERROR(tmp, "fputs");
1566 alertpanel_error(_("Error when writing temporary file for news sending."));
1572 debug_print("Sending message by news\n");
1574 folder = FOLDER(newsac->folder);
1576 newsval = news_post(folder, tmp);
1578 alertpanel_error(_("Error occurred while posting the message to %s ."),
1579 newsac->nntp_server);
1589 /* save message to outbox */
1590 if (mailval == 0 && newsval == 0 && savecopyfolder) {
1593 debug_print("saving sent message...\n");
1595 outbox = folder_find_item_from_identifier(savecopyfolder);
1597 outbox = folder_get_default_outbox();
1599 if (save_clear_text || tmp_enc_file == NULL) {
1600 procmsg_save_to_outbox(outbox, file, TRUE);
1602 procmsg_save_to_outbox(outbox, tmp_enc_file, FALSE);
1606 if (tmp_enc_file != NULL) {
1607 g_unlink(tmp_enc_file);
1609 tmp_enc_file = NULL;
1612 if (replymessageid != NULL || fwdmessageid != NULL) {
1616 if (replymessageid != NULL)
1617 tokens = g_strsplit(replymessageid, "\t", 0);
1619 tokens = g_strsplit(fwdmessageid, "\t", 0);
1620 item = folder_find_item_from_identifier(tokens[0]);
1622 /* check if queued message has valid folder and message id */
1623 if (item != NULL && tokens[2] != NULL) {
1626 msginfo = folder_item_get_msginfo(item, atoi(tokens[1]));
1628 /* check if referring message exists and has a message id */
1629 if ((msginfo != NULL) &&
1630 (msginfo->msgid != NULL) &&
1631 (strcmp(msginfo->msgid, tokens[2]) != 0)) {
1632 procmsg_msginfo_free(msginfo);
1636 if (msginfo == NULL) {
1637 msginfo = folder_item_get_msginfo_by_msgid(item, tokens[2]);
1640 if (msginfo != NULL) {
1641 if (replymessageid != NULL) {
1642 procmsg_msginfo_unset_flags(msginfo, MSG_FORWARDED, 0);
1643 procmsg_msginfo_set_flags(msginfo, MSG_REPLIED, 0);
1645 procmsg_msginfo_unset_flags(msginfo, MSG_REPLIED, 0);
1646 procmsg_msginfo_set_flags(msginfo, MSG_FORWARDED, 0);
1648 procmsg_msginfo_free(msginfo);
1656 slist_free_strings(to_list);
1657 g_slist_free(to_list);
1658 slist_free_strings(newsgroup_list);
1659 g_slist_free(newsgroup_list);
1660 g_free(savecopyfolder);
1661 g_free(replymessageid);
1662 g_free(fwdmessageid);
1663 g_free(privacy_system);
1664 g_free(encrypt_data);
1666 return (newsval != 0 ? newsval : mailval);
1669 gint procmsg_send_message_queue(const gchar *file)
1671 return procmsg_send_message_queue_full(file, FALSE);
1674 static void update_folder_msg_counts(FolderItem *item, MsgInfo *msginfo, MsgPermFlags old_flags)
1676 MsgPermFlags new_flags = msginfo->flags.perm_flags;
1679 if (!(old_flags & MSG_NEW) && (new_flags & MSG_NEW)) {
1683 if ((old_flags & MSG_NEW) && !(new_flags & MSG_NEW)) {
1688 if (!(old_flags & MSG_UNREAD) && (new_flags & MSG_UNREAD)) {
1689 item->unread_msgs++;
1690 if (procmsg_msg_has_marked_parent(msginfo))
1691 item->unreadmarked_msgs++;
1694 if ((old_flags & MSG_UNREAD) && !(new_flags & MSG_UNREAD)) {
1695 item->unread_msgs--;
1696 if (procmsg_msg_has_marked_parent(msginfo))
1697 item->unreadmarked_msgs--;
1701 if (!(old_flags & MSG_MARKED) && (new_flags & MSG_MARKED)) {
1702 procmsg_update_unread_children(msginfo, TRUE);
1703 item->marked_msgs++;
1706 if ((old_flags & MSG_MARKED) && !(new_flags & MSG_MARKED)) {
1707 procmsg_update_unread_children(msginfo, FALSE);
1708 item->marked_msgs--;
1712 void procmsg_msginfo_set_flags(MsgInfo *msginfo, MsgPermFlags perm_flags, MsgTmpFlags tmp_flags)
1715 MsgInfoUpdate msginfo_update;
1716 MsgPermFlags perm_flags_new, perm_flags_old;
1717 MsgTmpFlags tmp_flags_old;
1719 g_return_if_fail(msginfo != NULL);
1720 item = msginfo->folder;
1721 g_return_if_fail(item != NULL);
1723 debug_print("Setting flags for message %d in folder %s\n", msginfo->msgnum, item->path);
1725 /* Perm Flags handling */
1726 perm_flags_old = msginfo->flags.perm_flags;
1727 perm_flags_new = msginfo->flags.perm_flags | perm_flags;
1728 if ((perm_flags & MSG_IGNORE_THREAD) || (perm_flags_old & MSG_IGNORE_THREAD)) {
1729 perm_flags_new &= ~(MSG_NEW | MSG_UNREAD);
1732 if (perm_flags_old != perm_flags_new) {
1733 folder_item_change_msg_flags(msginfo->folder, msginfo, perm_flags_new);
1735 update_folder_msg_counts(item, msginfo, perm_flags_old);
1739 /* Tmp flags handling */
1740 tmp_flags_old = msginfo->flags.tmp_flags;
1741 msginfo->flags.tmp_flags |= tmp_flags;
1743 /* update notification */
1744 if ((perm_flags_old != perm_flags_new) || (tmp_flags_old != msginfo->flags.tmp_flags)) {
1745 msginfo_update.msginfo = msginfo;
1746 msginfo_update.flags = MSGINFO_UPDATE_FLAGS;
1747 hooks_invoke(MSGINFO_UPDATE_HOOKLIST, &msginfo_update);
1748 folder_item_update(msginfo->folder, F_ITEM_UPDATE_MSGCNT);
1752 void procmsg_msginfo_unset_flags(MsgInfo *msginfo, MsgPermFlags perm_flags, MsgTmpFlags tmp_flags)
1755 MsgInfoUpdate msginfo_update;
1756 MsgPermFlags perm_flags_new, perm_flags_old;
1757 MsgTmpFlags tmp_flags_old;
1759 g_return_if_fail(msginfo != NULL);
1760 item = msginfo->folder;
1761 g_return_if_fail(item != NULL);
1763 debug_print("Unsetting flags for message %d in folder %s\n", msginfo->msgnum, item->path);
1765 /* Perm Flags handling */
1766 perm_flags_old = msginfo->flags.perm_flags;
1767 perm_flags_new = msginfo->flags.perm_flags & ~perm_flags;
1769 if (perm_flags_old != perm_flags_new) {
1770 folder_item_change_msg_flags(msginfo->folder, msginfo, perm_flags_new);
1772 update_folder_msg_counts(item, msginfo, perm_flags_old);
1774 msginfo_update.msginfo = msginfo;
1775 msginfo_update.flags = MSGINFO_UPDATE_FLAGS;
1776 hooks_invoke(MSGINFO_UPDATE_HOOKLIST, &msginfo_update);
1777 folder_item_update(msginfo->folder, F_ITEM_UPDATE_MSGCNT);
1780 /* Tmp flags hanlding */
1781 tmp_flags_old = msginfo->flags.tmp_flags;
1782 msginfo->flags.tmp_flags &= ~tmp_flags;
1784 /* update notification */
1785 if ((perm_flags_old != perm_flags_new) || (tmp_flags_old != msginfo->flags.tmp_flags)) {
1786 msginfo_update.msginfo = msginfo;
1787 msginfo_update.flags = MSGINFO_UPDATE_FLAGS;
1788 hooks_invoke(MSGINFO_UPDATE_HOOKLIST, &msginfo_update);
1789 folder_item_update(msginfo->folder, F_ITEM_UPDATE_MSGCNT);
1793 void procmsg_msginfo_change_flags(MsgInfo *msginfo,
1794 MsgPermFlags add_perm_flags, MsgTmpFlags add_tmp_flags,
1795 MsgPermFlags rem_perm_flags, MsgTmpFlags rem_tmp_flags)
1798 MsgInfoUpdate msginfo_update;
1799 MsgPermFlags perm_flags_new, perm_flags_old;
1800 MsgTmpFlags tmp_flags_old;
1802 g_return_if_fail(msginfo != NULL);
1803 item = msginfo->folder;
1804 g_return_if_fail(item != NULL);
1806 debug_print("Changing flags for message %d in folder %s\n", msginfo->msgnum, item->path);
1808 /* Perm Flags handling */
1809 perm_flags_old = msginfo->flags.perm_flags;
1810 perm_flags_new = (msginfo->flags.perm_flags & ~rem_perm_flags) | add_perm_flags;
1811 if ((add_perm_flags & MSG_IGNORE_THREAD) || (perm_flags_old & MSG_IGNORE_THREAD)) {
1812 perm_flags_new &= ~(MSG_NEW | MSG_UNREAD);
1815 if (perm_flags_old != perm_flags_new) {
1816 folder_item_change_msg_flags(msginfo->folder, msginfo, perm_flags_new);
1818 update_folder_msg_counts(item, msginfo, perm_flags_old);
1822 /* Tmp flags handling */
1823 tmp_flags_old = msginfo->flags.tmp_flags;
1824 msginfo->flags.tmp_flags &= ~rem_tmp_flags;
1825 msginfo->flags.tmp_flags |= add_tmp_flags;
1827 /* update notification */
1828 if ((perm_flags_old != perm_flags_new) || (tmp_flags_old != msginfo->flags.tmp_flags)) {
1829 msginfo_update.msginfo = msginfo;
1830 msginfo_update.flags = MSGINFO_UPDATE_FLAGS;
1831 hooks_invoke(MSGINFO_UPDATE_HOOKLIST, &msginfo_update);
1832 folder_item_update(msginfo->folder, F_ITEM_UPDATE_MSGCNT);
1837 *\brief check for flags (e.g. mark) in prior msgs of current thread
1839 *\param info Current message
1840 *\param perm_flags Flags to be checked
1841 *\param parentmsgs Hash of prior msgs to avoid loops
1843 *\return gboolean TRUE if perm_flags are found
1845 gboolean procmsg_msg_has_flagged_parent_real(MsgInfo *info,
1846 MsgPermFlags perm_flags, GHashTable *parentmsgs)
1850 g_return_val_if_fail(info != NULL, FALSE);
1852 if (info != NULL && info->folder != NULL && info->inreplyto != NULL) {
1853 tmp = folder_item_get_msginfo_by_msgid(info->folder,
1855 if (tmp && (tmp->flags.perm_flags & perm_flags)) {
1856 procmsg_msginfo_free(tmp);
1858 } else if (tmp != NULL) {
1861 if (g_hash_table_lookup(parentmsgs, info)) {
1862 debug_print("loop detected: %s%c%d\n",
1863 folder_item_get_path(info->folder),
1864 G_DIR_SEPARATOR, info->msgnum);
1867 g_hash_table_insert(parentmsgs, info, "1");
1868 result = procmsg_msg_has_flagged_parent_real(
1869 tmp, perm_flags, parentmsgs);
1871 procmsg_msginfo_free(tmp);
1881 *\brief Callback for cleaning up hash of parentmsgs
1883 gboolean parentmsgs_hash_remove(gpointer key,
1891 *\brief Set up list of parentmsgs
1892 * See procmsg_msg_has_flagged_parent_real()
1894 gboolean procmsg_msg_has_flagged_parent(MsgInfo *info, MsgPermFlags perm_flags)
1897 GHashTable *parentmsgs = g_hash_table_new(NULL, NULL);
1899 result = procmsg_msg_has_flagged_parent_real(info, perm_flags, parentmsgs);
1900 g_hash_table_foreach_remove(parentmsgs, parentmsgs_hash_remove, NULL);
1901 g_hash_table_destroy(parentmsgs);
1906 *\brief Check if msgs prior in thread are marked
1907 * See procmsg_msg_has_flagged_parent_real()
1909 gboolean procmsg_msg_has_marked_parent(MsgInfo *info)
1911 return procmsg_msg_has_flagged_parent(info, MSG_MARKED);
1915 GSList *procmsg_find_children_func(MsgInfo *info,
1916 GSList *children, GSList *all)
1920 g_return_val_if_fail(info!=NULL, children);
1921 if (info->msgid == NULL)
1924 for (cur = all; cur != NULL; cur = g_slist_next(cur)) {
1925 MsgInfo *tmp = (MsgInfo *)cur->data;
1926 if (tmp->inreplyto && !strcmp(tmp->inreplyto, info->msgid)) {
1927 /* Check if message is already in the list */
1928 if ((children == NULL) ||
1929 (g_slist_index(children, tmp) == -1)) {
1930 children = g_slist_prepend(children,
1931 procmsg_msginfo_new_ref(tmp));
1932 children = procmsg_find_children_func(tmp,
1941 GSList *procmsg_find_children (MsgInfo *info)
1946 g_return_val_if_fail(info!=NULL, NULL);
1947 all = folder_item_get_msg_list(info->folder);
1948 children = procmsg_find_children_func(info, NULL, all);
1949 if (children != NULL) {
1950 for (cur = all; cur != NULL; cur = g_slist_next(cur)) {
1951 /* this will not free the used pointers
1952 created with procmsg_msginfo_new_ref */
1953 procmsg_msginfo_free((MsgInfo *)cur->data);
1961 void procmsg_update_unread_children(MsgInfo *info, gboolean newly_marked)
1963 GSList *children = procmsg_find_children(info);
1965 for (cur = children; cur != NULL; cur = g_slist_next(cur)) {
1966 MsgInfo *tmp = (MsgInfo *)cur->data;
1967 if(MSG_IS_UNREAD(tmp->flags) && !MSG_IS_IGNORE_THREAD(tmp->flags)) {
1969 info->folder->unreadmarked_msgs++;
1971 info->folder->unreadmarked_msgs--;
1972 folder_item_update(info->folder, F_ITEM_UPDATE_MSGCNT);
1974 procmsg_msginfo_free(tmp);
1976 g_slist_free(children);
1980 * Set the destination folder for a copy or move operation
1982 * \param msginfo The message which's destination folder is changed
1983 * \param to_folder The destination folder for the operation
1985 void procmsg_msginfo_set_to_folder(MsgInfo *msginfo, FolderItem *to_folder)
1987 if(msginfo->to_folder != NULL) {
1988 msginfo->to_folder->op_count--;
1989 folder_item_update(msginfo->to_folder, F_ITEM_UPDATE_MSGCNT);
1991 msginfo->to_folder = to_folder;
1992 if(to_folder != NULL) {
1993 to_folder->op_count++;
1994 folder_item_update(msginfo->to_folder, F_ITEM_UPDATE_MSGCNT);
1999 * Apply filtering actions to the msginfo
2001 * \param msginfo The MsgInfo describing the message that should be filtered
2002 * \return TRUE if the message was moved and MsgInfo is now invalid,
2005 gboolean procmsg_msginfo_filter(MsgInfo *msginfo)
2007 MailFilteringData mail_filtering_data;
2009 mail_filtering_data.msginfo = msginfo;
2010 if (hooks_invoke(MAIL_FILTERING_HOOKLIST, &mail_filtering_data)) {
2014 /* filter if enabled in prefs or move to inbox if not */
2015 if((filtering_rules != NULL) &&
2016 filter_message_by_msginfo(filtering_rules, msginfo)) {
2023 MsgInfo *procmsg_msginfo_new_from_mimeinfo(MsgInfo *src_msginfo, MimeInfo *mimeinfo)
2025 MsgInfo *tmp_msginfo = NULL;
2026 MsgFlags flags = {0, 0};
2027 gchar *tmpfile = get_tmp_file();
2028 FILE *fp = g_fopen(tmpfile, "wb");
2030 if (!mimeinfo || mimeinfo->type != MIMETYPE_MESSAGE ||
2031 g_ascii_strcasecmp(mimeinfo->subtype, "rfc822")) {
2032 g_warning("procmsg_msginfo_new_from_mimeinfo(): unsuitable mimeinfo");
2039 if (fp && procmime_write_mimeinfo(mimeinfo, fp) >= 0) {
2042 tmp_msginfo = procheader_parse_file(
2049 if (tmp_msginfo != NULL) {
2051 tmp_msginfo->folder = src_msginfo->folder;
2052 tmp_msginfo->plaintext_file = g_strdup(tmpfile);
2054 g_warning("procmsg_msginfo_new_from_mimeinfo(): Can't generate new msginfo");
2062 static GSList *spam_learners = NULL;
2064 void procmsg_register_spam_learner (int (*learn_func)(MsgInfo *info, GSList *list, gboolean spam))
2066 if (!g_slist_find(spam_learners, learn_func))
2067 spam_learners = g_slist_append(spam_learners, learn_func);
2068 if (mainwindow_get_mainwindow()) {
2069 main_window_set_menu_sensitive(mainwindow_get_mainwindow());
2070 summary_set_menu_sensitive(
2071 mainwindow_get_mainwindow()->summaryview);
2072 toolbar_main_set_sensitive(mainwindow_get_mainwindow());
2076 void procmsg_unregister_spam_learner (int (*learn_func)(MsgInfo *info, GSList *list, gboolean spam))
2078 spam_learners = g_slist_remove(spam_learners, learn_func);
2079 if (mainwindow_get_mainwindow()) {
2080 main_window_set_menu_sensitive(mainwindow_get_mainwindow());
2081 summary_set_menu_sensitive(
2082 mainwindow_get_mainwindow()->summaryview);
2083 toolbar_main_set_sensitive(mainwindow_get_mainwindow());
2087 gboolean procmsg_spam_can_learn(void)
2089 return g_slist_length(spam_learners) > 0;
2092 int procmsg_spam_learner_learn (MsgInfo *info, GSList *list, gboolean spam)
2094 GSList *cur = spam_learners;
2096 for (; cur; cur = cur->next) {
2097 int ((*func)(MsgInfo *info, GSList *list, gboolean spam)) = cur->data;
2098 ret |= func(info, list, spam);
2103 static gchar *spam_folder_item = NULL;
2104 void procmsg_spam_set_folder (const char *item_identifier)
2106 g_free(spam_folder_item);
2107 if (item_identifier)
2108 spam_folder_item = g_strdup(item_identifier);
2110 spam_folder_item = NULL;
2113 FolderItem *procmsg_spam_get_folder (void)
2115 FolderItem *item = spam_folder_item ? folder_find_item_from_identifier(spam_folder_item) : NULL;
2116 return item ? item : folder_get_default_trash();