2 * Sylpheed -- a GTK+ based, lightweight, and fast e-mail client
3 * Copyright (C) 1999-2005 Hiroyuki Yamamoto
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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, 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"
46 static gint procmsg_send_message_queue_full(const gchar *file, gboolean keep_session);
54 Q_MAIL_ACCOUNT_ID = 4,
55 Q_NEWS_ACCOUNT_ID = 5,
56 Q_SAVE_COPY_FOLDER = 6,
57 Q_REPLY_MESSAGE_ID = 7,
64 GHashTable *procmsg_msg_hash_table_create(GSList *mlist)
66 GHashTable *msg_table;
68 if (mlist == NULL) return NULL;
70 msg_table = g_hash_table_new(NULL, g_direct_equal);
71 procmsg_msg_hash_table_append(msg_table, mlist);
76 void procmsg_msg_hash_table_append(GHashTable *msg_table, GSList *mlist)
81 if (msg_table == NULL || mlist == NULL) return;
83 for (cur = mlist; cur != NULL; cur = cur->next) {
84 msginfo = (MsgInfo *)cur->data;
86 g_hash_table_insert(msg_table,
87 GUINT_TO_POINTER(msginfo->msgnum),
92 GHashTable *procmsg_to_folder_hash_table_create(GSList *mlist)
94 GHashTable *msg_table;
98 if (mlist == NULL) return NULL;
100 msg_table = g_hash_table_new(NULL, g_direct_equal);
102 for (cur = mlist; cur != NULL; cur = cur->next) {
103 msginfo = (MsgInfo *)cur->data;
104 g_hash_table_insert(msg_table, msginfo->to_folder, msginfo);
110 gint procmsg_get_last_num_in_msg_list(GSList *mlist)
116 for (cur = mlist; cur != NULL; cur = cur->next) {
117 msginfo = (MsgInfo *)cur->data;
118 if (msginfo && msginfo->msgnum > last)
119 last = msginfo->msgnum;
125 void procmsg_msg_list_free(GSList *mlist)
130 for (cur = mlist; cur != NULL; cur = cur->next) {
131 msginfo = (MsgInfo *)cur->data;
132 procmsg_msginfo_free(msginfo);
146 /* CLAWS subject threading:
148 in the first round it inserts subject lines in a
149 relation (subject <-> node)
151 the second round finishes the threads by attaching
152 matching subject lines to the one found in the
153 relation. will use the oldest node with the same
154 subject that is not more then thread_by_subject_max_age
155 days old (see subject_relation_lookup)
158 static void subject_relation_insert(GRelation *relation, GNode *node)
163 g_return_if_fail(relation != NULL);
164 g_return_if_fail(node != NULL);
165 msginfo = (MsgInfo *) node->data;
166 g_return_if_fail(msginfo != NULL);
168 subject = msginfo->subject;
171 subject += subject_get_prefix_length(subject);
173 g_relation_insert(relation, subject, node);
176 static GNode *subject_relation_lookup(GRelation *relation, MsgInfo *msginfo)
183 g_return_val_if_fail(relation != NULL, NULL);
185 subject = msginfo->subject;
188 prefix_length = subject_get_prefix_length(subject);
189 if (prefix_length <= 0)
191 subject += prefix_length;
193 tuples = g_relation_select(relation, subject, 0);
197 if (tuples->len > 0) {
199 GNode *relation_node;
200 MsgInfo *relation_msginfo = NULL, *best_msginfo = NULL;
203 /* check all nodes with the same subject to find the best parent */
204 for (i = 0; i < tuples->len; i++) {
205 relation_node = (GNode *) g_tuples_index(tuples, i, 1);
206 relation_msginfo = (MsgInfo *) relation_node->data;
209 /* best node should be the oldest in the found nodes */
210 /* parent node must not be older then msginfo */
211 if ((relation_msginfo->date_t < msginfo->date_t) &&
212 ((best_msginfo == NULL) ||
213 (best_msginfo->date_t > relation_msginfo->date_t)))
216 /* parent node must not be more then thread_by_subject_max_age
217 days older then msginfo */
218 if (abs(difftime(msginfo->date_t, relation_msginfo->date_t)) >
219 prefs_common.thread_by_subject_max_age * 3600 * 24)
222 /* can add new tests for all matching
223 nodes found by subject */
226 node = relation_node;
227 best_msginfo = relation_msginfo;
232 g_tuples_destroy(tuples);
236 /* return the reversed thread tree */
237 GNode *procmsg_get_thread_tree(GSList *mlist)
239 GNode *root, *parent, *node, *next;
240 GHashTable *msgid_table;
241 GRelation *subject_relation;
246 root = g_node_new(NULL);
247 msgid_table = g_hash_table_new(g_str_hash, g_str_equal);
248 subject_relation = g_relation_new(2);
249 g_relation_index(subject_relation, 0, g_str_hash, g_str_equal);
251 for (; mlist != NULL; mlist = mlist->next) {
252 msginfo = (MsgInfo *)mlist->data;
255 if (msginfo->inreplyto) {
256 parent = g_hash_table_lookup(msgid_table, msginfo->inreplyto);
257 if (parent == NULL) {
261 node = g_node_insert_data_before
262 (parent, parent == root ? parent->children : NULL,
264 if ((msgid = msginfo->msgid) && g_hash_table_lookup(msgid_table, msgid) == NULL)
265 g_hash_table_insert(msgid_table, (gchar *)msgid, node);
267 /* CLAWS: add subject to relation (without prefix) */
268 if (prefs_common.thread_by_subject) {
269 subject_relation_insert(subject_relation, node);
273 /* complete the unfinished threads */
274 for (node = root->children; node != NULL; ) {
276 msginfo = (MsgInfo *)node->data;
279 if (msginfo->inreplyto)
280 parent = g_hash_table_lookup(msgid_table, msginfo->inreplyto);
282 /* try looking for the indirect parent */
283 if (!parent && msginfo->references) {
284 for (reflist = msginfo->references;
285 reflist != NULL; reflist = reflist->next)
286 if ((parent = g_hash_table_lookup
287 (msgid_table, reflist->data)) != NULL)
291 /* node should not be the parent, and node should not
292 be an ancestor of parent (circular reference) */
293 if (parent && parent != node &&
294 !g_node_is_ancestor(node, parent)) {
297 (parent, parent->children, node);
303 if (prefs_common.thread_by_subject) {
304 for (node = root->children; node && node != NULL;) {
306 msginfo = (MsgInfo *) node->data;
308 parent = subject_relation_lookup(subject_relation, msginfo);
310 /* the node may already be threaded by IN-REPLY-TO, so go up
312 find the parent node */
313 if (parent != NULL) {
314 if (g_node_is_ancestor(node, parent))
322 g_node_append(parent, node);
329 g_relation_destroy(subject_relation);
330 g_hash_table_destroy(msgid_table);
335 void procmsg_move_messages(GSList *mlist)
337 GSList *cur, *movelist = NULL;
339 FolderItem *dest = NULL;
343 folder_item_update_freeze();
345 for (cur = mlist; cur != NULL; cur = cur->next) {
346 msginfo = (MsgInfo *)cur->data;
348 dest = msginfo->to_folder;
349 movelist = g_slist_append(movelist, msginfo);
350 } else if (dest == msginfo->to_folder) {
351 movelist = g_slist_append(movelist, msginfo);
353 folder_item_move_msgs(dest, movelist);
354 g_slist_free(movelist);
356 dest = msginfo->to_folder;
357 movelist = g_slist_append(movelist, msginfo);
359 procmsg_msginfo_set_to_folder(msginfo, NULL);
363 folder_item_move_msgs(dest, movelist);
364 g_slist_free(movelist);
367 folder_item_update_thaw();
370 void procmsg_copy_messages(GSList *mlist)
372 GSList *cur, *copylist = NULL;
374 FolderItem *dest = NULL;
378 folder_item_update_freeze();
380 for (cur = mlist; cur != NULL; cur = cur->next) {
381 msginfo = (MsgInfo *)cur->data;
383 dest = msginfo->to_folder;
384 copylist = g_slist_append(copylist, msginfo);
385 } else if (dest == msginfo->to_folder) {
386 copylist = g_slist_append(copylist, msginfo);
388 folder_item_copy_msgs(dest, copylist);
389 g_slist_free(copylist);
391 dest = msginfo->to_folder;
392 copylist = g_slist_append(copylist, msginfo);
394 procmsg_msginfo_set_to_folder(msginfo, NULL);
398 folder_item_copy_msgs(dest, copylist);
399 g_slist_free(copylist);
402 folder_item_update_thaw();
405 gchar *procmsg_get_message_file_path(MsgInfo *msginfo)
409 g_return_val_if_fail(msginfo != NULL, NULL);
411 if (msginfo->plaintext_file)
412 file = g_strdup(msginfo->plaintext_file);
414 file = folder_item_fetch_msg(msginfo->folder, msginfo->msgnum);
420 gchar *procmsg_get_message_file(MsgInfo *msginfo)
422 gchar *filename = NULL;
424 g_return_val_if_fail(msginfo != NULL, NULL);
426 filename = folder_item_fetch_msg(msginfo->folder, msginfo->msgnum);
428 debug_print("can't fetch message %d\n", msginfo->msgnum);
433 gchar *procmsg_get_message_file_full(MsgInfo *msginfo, gboolean headers, gboolean body)
435 gchar *filename = NULL;
437 g_return_val_if_fail(msginfo != NULL, NULL);
439 filename = folder_item_fetch_msg_full(msginfo->folder, msginfo->msgnum,
442 debug_print("can't fetch message %d\n", msginfo->msgnum);
447 GSList *procmsg_get_message_file_list(GSList *mlist)
449 GSList *file_list = NULL;
451 MsgFileInfo *fileinfo;
454 while (mlist != NULL) {
455 msginfo = (MsgInfo *)mlist->data;
456 file = procmsg_get_message_file(msginfo);
458 procmsg_message_file_list_free(file_list);
461 fileinfo = g_new(MsgFileInfo, 1);
462 fileinfo->msginfo = procmsg_msginfo_new_ref(msginfo);
463 fileinfo->file = file;
464 fileinfo->flags = g_new(MsgFlags, 1);
465 *fileinfo->flags = msginfo->flags;
466 file_list = g_slist_prepend(file_list, fileinfo);
470 file_list = g_slist_reverse(file_list);
475 void procmsg_message_file_list_free(MsgInfoList *file_list)
478 MsgFileInfo *fileinfo;
480 for (cur = file_list; cur != NULL; cur = cur->next) {
481 fileinfo = (MsgFileInfo *)cur->data;
482 procmsg_msginfo_free(fileinfo->msginfo);
483 g_free(fileinfo->file);
484 g_free(fileinfo->flags);
488 g_slist_free(file_list);
491 FILE *procmsg_open_message(MsgInfo *msginfo)
496 g_return_val_if_fail(msginfo != NULL, NULL);
498 file = procmsg_get_message_file_path(msginfo);
499 g_return_val_if_fail(file != NULL, NULL);
501 if (!is_file_exist(file)) {
503 file = procmsg_get_message_file(msginfo);
508 if ((fp = g_fopen(file, "rb")) == NULL) {
509 FILE_OP_ERROR(file, "fopen");
516 if (MSG_IS_QUEUED(msginfo->flags) || MSG_IS_DRAFT(msginfo->flags)) {
519 while (fgets(buf, sizeof(buf), fp) != NULL)
520 if (buf[0] == '\r' || buf[0] == '\n') break;
526 gboolean procmsg_msg_exist(MsgInfo *msginfo)
531 if (!msginfo) return FALSE;
533 path = folder_item_get_path(msginfo->folder);
535 ret = !folder_item_is_msg_changed(msginfo->folder, msginfo);
541 void procmsg_get_filter_keyword(MsgInfo *msginfo, gchar **header, gchar **key,
542 PrefsFilterType type)
544 static HeaderEntry hentry[] = {{"X-BeenThere:", NULL, TRUE},
545 {"X-ML-Name:", NULL, TRUE},
546 {"X-List:", NULL, TRUE},
547 {"X-Mailing-list:", NULL, TRUE},
548 {"List-Id:", NULL, TRUE},
549 {"X-Sequence:", NULL, TRUE},
550 {NULL, NULL, FALSE}};
556 H_X_MAILING_LIST = 3,
563 g_return_if_fail(msginfo != NULL);
564 g_return_if_fail(header != NULL);
565 g_return_if_fail(key != NULL);
574 if ((fp = procmsg_open_message(msginfo)) == NULL)
576 procheader_get_header_fields(fp, hentry);
579 #define SET_FILTER_KEY(hstr, idx) \
581 *header = g_strdup(hstr); \
582 *key = hentry[idx].body; \
583 hentry[idx].body = NULL; \
586 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_LIST_ID].body != NULL) {
595 SET_FILTER_KEY("header \"List-Id\"", H_LIST_ID);
596 extract_list_id_str(*key);
597 } else if (hentry[H_X_SEQUENCE].body != NULL) {
600 SET_FILTER_KEY("X-Sequence", H_X_SEQUENCE);
603 while (*p != '\0' && !g_ascii_isspace(*p)) p++;
604 while (g_ascii_isspace(*p)) p++;
605 if (g_ascii_isdigit(*p)) {
611 } else if (msginfo->subject) {
612 *header = g_strdup("subject");
613 *key = g_strdup(msginfo->subject);
616 #undef SET_FILTER_KEY
618 g_free(hentry[H_X_BEENTHERE].body);
619 hentry[H_X_BEENTHERE].body = NULL;
620 g_free(hentry[H_X_ML_NAME].body);
621 hentry[H_X_ML_NAME].body = NULL;
622 g_free(hentry[H_X_LIST].body);
623 hentry[H_X_LIST].body = NULL;
624 g_free(hentry[H_X_MAILING_LIST].body);
625 hentry[H_X_MAILING_LIST].body = NULL;
626 g_free(hentry[H_LIST_ID].body);
627 hentry[H_LIST_ID].body = NULL;
631 *header = g_strdup("from");
632 *key = g_strdup(msginfo->from);
635 *header = g_strdup("to");
636 *key = g_strdup(msginfo->to);
638 case FILTER_BY_SUBJECT:
639 *header = g_strdup("subject");
640 *key = g_strdup(msginfo->subject);
647 void procmsg_empty_trash(FolderItem *trash)
649 if (trash && trash->total_msgs > 0) {
650 GSList *mlist = folder_item_get_msg_list(trash);
652 for (cur = mlist ; cur != NULL ; cur = cur->next) {
653 MsgInfo * msginfo = (MsgInfo *) cur->data;
654 if (MSG_IS_LOCKED(msginfo->flags))
656 if (msginfo->total_size != 0 &&
657 msginfo->size != (off_t)msginfo->total_size)
658 partial_mark_for_delete(msginfo);
660 procmsg_msginfo_free(msginfo);
663 folder_item_remove_all_msg(trash);
667 void procmsg_empty_all_trash(void)
672 for (cur = folder_get_list(); cur != NULL; cur = cur->next) {
673 Folder *folder = FOLDER(cur->data);
674 trash = folder->trash;
675 procmsg_empty_trash(trash);
676 if (folder->account && folder->account->set_trash_folder &&
677 folder_find_item_from_identifier(folder->account->trash_folder))
679 folder_find_item_from_identifier(folder->account->trash_folder));
683 static PrefsAccount *procmsg_get_account_from_file(const gchar *file)
685 PrefsAccount *mailac = NULL;
689 static HeaderEntry qentry[] = {{"S:", NULL, FALSE},
690 {"SSV:", NULL, FALSE},
692 {"NG:", NULL, FALSE},
693 {"MAID:", NULL, FALSE},
694 {"NAID:", NULL, FALSE},
695 {"SCF:", NULL, FALSE},
696 {"RMID:", NULL, FALSE},
697 {"FMID:", NULL, FALSE},
698 {"X-Sylpheed-Privacy-System:", NULL, FALSE},
699 {"X-Sylpheed-Encrypt:", NULL, FALSE},
700 {"X-Sylpheed-Encrypt-Data:", NULL, FALSE},
701 {NULL, NULL, FALSE}};
703 g_return_val_if_fail(file != NULL, NULL);
705 if ((fp = g_fopen(file, "rb")) == NULL) {
706 FILE_OP_ERROR(file, "fopen");
710 while ((hnum = procheader_get_one_field(buf, sizeof(buf), fp, qentry))
712 gchar *p = buf + strlen(qentry[hnum].name);
714 if (hnum == Q_MAIL_ACCOUNT_ID) {
715 mailac = account_find_from_id(atoi(p));
723 static GSList *procmsg_list_sort_by_account(FolderItem *queue, GSList *list)
725 GSList *result = NULL;
727 PrefsAccount *last_account = NULL;
730 gboolean nothing_to_sort = TRUE;
735 orig = g_slist_copy(list);
737 msg = (MsgInfo *)orig->data;
739 for (cur = orig; cur; cur = cur->next)
740 debug_print("sort before %s\n", ((MsgInfo *)cur->data)->from);
745 nothing_to_sort = TRUE;
749 PrefsAccount *ac = procmsg_get_account_from_file(file);
750 msg = (MsgInfo *)cur->data;
751 file = folder_item_fetch_msg(queue, msg->msgnum);
754 if (last_account == NULL || (ac != NULL && ac == last_account)) {
755 result = g_slist_append(result, msg);
756 orig = g_slist_remove(orig, msg);
758 nothing_to_sort = FALSE;
764 if (orig || g_slist_length(orig)) {
765 if (!last_account && nothing_to_sort) {
766 /* can't find an account for the rest of the list */
769 result = g_slist_append(result, cur->data);
780 for (cur = result; cur; cur = cur->next)
781 debug_print("sort after %s\n", ((MsgInfo *)cur->data)->from);
788 static gboolean procmsg_is_last_for_account(FolderItem *queue, MsgInfo *msginfo, GSList *elem)
790 gchar *file = folder_item_fetch_msg(queue, msginfo->msgnum);
791 PrefsAccount *ac = procmsg_get_account_from_file(file);
794 for (cur = elem; cur; cur = cur->next) {
795 MsgInfo *cur_msginfo = (MsgInfo *)cur->data;
796 file = folder_item_fetch_msg(queue, cur_msginfo->msgnum);
798 if (cur_msginfo != msginfo && !MSG_IS_LOCKED(cur_msginfo->flags)) {
799 if (procmsg_get_account_from_file(file) == ac) {
811 *\brief Send messages in queue
813 *\param queue Queue folder to process
814 *\param save_msgs Unused
816 *\return Number of messages sent, negative if an error occurred
817 * positive if no error occurred
819 gint procmsg_send_queue(FolderItem *queue, gboolean save_msgs)
821 gint sent = 0, err = 0;
823 GSList *sorted_list = NULL;
826 queue = folder_get_default_queue();
827 g_return_val_if_fail(queue != NULL, -1);
829 folder_item_scan(queue);
830 list = folder_item_get_msg_list(queue);
832 /* sort the list per sender account; this helps reusing the same SMTP server */
833 sorted_list = procmsg_list_sort_by_account(queue, list);
835 for (elem = sorted_list; elem != NULL; elem = elem->next) {
839 msginfo = (MsgInfo *)(elem->data);
840 if (!MSG_IS_LOCKED(msginfo->flags)) {
841 file = folder_item_fetch_msg(queue, msginfo->msgnum);
843 if (procmsg_send_message_queue_full(file,
844 !procmsg_is_last_for_account(queue, msginfo, elem)) < 0) {
845 g_warning("Sending queued message %d failed.\n",
850 * We save in procmsg_send_message_queue because
851 * we need the destination folder from the queue
855 procmsg_save_to_outbox
856 (queue->folder->outbox,
860 folder_item_remove_msg(queue, msginfo->msgnum);
865 /* FIXME: supposedly if only one message is locked, and queue
866 * is being flushed, the following free says something like
867 * "freeing msg ## in folder (nil)". */
868 procmsg_msginfo_free(msginfo);
871 g_slist_free(sorted_list);
873 return (err != 0 ? -err : sent);
877 *\brief Determine if a queue folder is empty
879 *\param queue Queue folder to process
881 *\return TRUE if the queue folder is empty, otherwise return FALSE
883 gboolean procmsg_queue_is_empty(FolderItem *queue)
886 gboolean res = FALSE;
888 queue = folder_get_default_queue();
889 g_return_val_if_fail(queue != NULL, TRUE);
891 folder_item_scan(queue);
892 list = folder_item_get_msg_list(queue);
893 res = (list == NULL);
894 procmsg_msg_list_free(list);
898 gint procmsg_remove_special_headers(const gchar *in, const gchar *out)
903 if ((fp = g_fopen(in, "rb")) == NULL) {
904 FILE_OP_ERROR(in, "fopen");
907 if ((outfp = g_fopen(out, "wb")) == NULL) {
908 FILE_OP_ERROR(out, "fopen");
912 while (fgets(buf, sizeof(buf), fp) != NULL)
913 if (buf[0] == '\r' || buf[0] == '\n') break;
914 while (fgets(buf, sizeof(buf), fp) != NULL)
921 gint procmsg_save_to_outbox(FolderItem *outbox, const gchar *file,
925 MsgInfo *msginfo, *tmp_msginfo;
926 MsgFlags flag = {0, 0};
928 debug_print("saving sent message...\n");
931 outbox = folder_get_default_outbox();
932 g_return_val_if_fail(outbox != NULL, -1);
934 /* remove queueing headers */
936 gchar tmp[MAXPATHLEN + 1];
938 g_snprintf(tmp, sizeof(tmp), "%s%ctmpmsg.out.%08x",
939 get_rc_dir(), G_DIR_SEPARATOR, (guint) rand());
941 if (procmsg_remove_special_headers(file, tmp) !=0)
944 folder_item_scan(outbox);
945 if ((num = folder_item_add_msg(outbox, tmp, &flag, TRUE)) < 0) {
946 g_warning("can't save message\n");
951 folder_item_scan(outbox);
952 if ((num = folder_item_add_msg
953 (outbox, file, &flag, FALSE)) < 0) {
954 g_warning("can't save message\n");
959 msginfo = folder_item_get_msginfo(outbox, num); /* refcnt++ */
960 tmp_msginfo = procmsg_msginfo_get_full_info(msginfo); /* refcnt++ */
961 if (msginfo != NULL) {
962 procmsg_msginfo_unset_flags(msginfo, ~0, 0);
963 procmsg_msginfo_free(msginfo); /* refcnt-- */
964 /* tmp_msginfo == msginfo */
965 if (tmp_msginfo && (msginfo->dispositionnotificationto ||
966 msginfo->returnreceiptto)) {
967 procmsg_msginfo_set_flags(msginfo, MSG_RETRCPT_SENT, 0);
968 procmsg_msginfo_free(msginfo); /* refcnt-- */
975 void procmsg_print_message(MsgInfo *msginfo, const gchar *cmdline)
977 static const gchar *def_cmd = "lpr %s";
984 g_return_if_fail(msginfo);
986 if (procmime_msginfo_is_encrypted(msginfo))
987 tmpfp = procmime_get_first_encrypted_text_content(msginfo);
989 tmpfp = procmime_get_first_text_content(msginfo);
991 g_warning("Can't get text part\n");
995 prtmp = g_strdup_printf("%s%cprinttmp.%08x",
996 get_mime_tmp_dir(), G_DIR_SEPARATOR, id++);
998 if ((prfp = g_fopen(prtmp, "wb")) == NULL) {
999 FILE_OP_ERROR(prtmp, "fopen");
1005 if (msginfo->date) fprintf(prfp, "Date: %s\n", msginfo->date);
1006 if (msginfo->from) fprintf(prfp, "From: %s\n", msginfo->from);
1007 if (msginfo->to) fprintf(prfp, "To: %s\n", msginfo->to);
1008 if (msginfo->cc) fprintf(prfp, "Cc: %s\n", msginfo->cc);
1009 if (msginfo->newsgroups)
1010 fprintf(prfp, "Newsgroups: %s\n", msginfo->newsgroups);
1011 if (msginfo->subject) fprintf(prfp, "Subject: %s\n", msginfo->subject);
1014 while (fgets(buf, sizeof(buf), tmpfp) != NULL)
1020 if (cmdline && (p = strchr(cmdline, '%')) && *(p + 1) == 's' &&
1021 !strchr(p + 2, '%'))
1022 g_snprintf(buf, sizeof(buf) - 1, cmdline, prtmp);
1025 g_warning("Print command line is invalid: '%s'\n",
1027 g_snprintf(buf, sizeof(buf) - 1, def_cmd, prtmp);
1033 if (buf[strlen(buf) - 1] != '&') strcat(buf, "&");
1037 MsgInfo *procmsg_msginfo_new_ref(MsgInfo *msginfo)
1044 MsgInfo *procmsg_msginfo_new(void)
1046 MsgInfo *newmsginfo;
1048 newmsginfo = g_new0(MsgInfo, 1);
1049 newmsginfo->refcnt = 1;
1054 MsgInfo *procmsg_msginfo_copy(MsgInfo *msginfo)
1056 MsgInfo *newmsginfo;
1059 if (msginfo == NULL) return NULL;
1061 newmsginfo = g_new0(MsgInfo, 1);
1063 newmsginfo->refcnt = 1;
1065 #define MEMBCOPY(mmb) newmsginfo->mmb = msginfo->mmb
1066 #define MEMBDUP(mmb) newmsginfo->mmb = msginfo->mmb ? \
1067 g_strdup(msginfo->mmb) : NULL
1082 MEMBDUP(newsgroups);
1089 MEMBCOPY(to_folder);
1092 MEMBDUP(dispositionnotificationto);
1093 MEMBDUP(returnreceiptto);
1095 refs = msginfo->references;
1096 for (refs = msginfo->references; refs != NULL; refs = refs->next) {
1097 newmsginfo->references = g_slist_prepend
1098 (newmsginfo->references, g_strdup(refs->data));
1100 newmsginfo->references = g_slist_reverse(newmsginfo->references);
1103 MEMBCOPY(threadscore);
1104 MEMBDUP(plaintext_file);
1109 MsgInfo *procmsg_msginfo_get_full_info(MsgInfo *msginfo)
1111 MsgInfo *full_msginfo;
1114 if (msginfo == NULL) return NULL;
1116 file = procmsg_get_message_file_path(msginfo);
1117 if (!file || !is_file_exist(file)) {
1119 file = procmsg_get_message_file(msginfo);
1121 if (!file || !is_file_exist(file)) {
1122 g_warning("procmsg_msginfo_get_full_info(): can't get message file.\n");
1126 full_msginfo = procheader_parse_file(file, msginfo->flags, TRUE, FALSE);
1128 if (!full_msginfo) return NULL;
1130 /* CLAWS: make sure we add the missing members; see:
1131 * procheader.c::procheader_get_headernames() */
1132 if (!msginfo->xface)
1133 msginfo->xface = g_strdup(full_msginfo->xface);
1134 if (!msginfo->dispositionnotificationto)
1135 msginfo->dispositionnotificationto =
1136 g_strdup(full_msginfo->dispositionnotificationto);
1137 if (!msginfo->returnreceiptto)
1138 msginfo->returnreceiptto = g_strdup
1139 (full_msginfo->returnreceiptto);
1140 if (!msginfo->partial_recv && full_msginfo->partial_recv)
1141 msginfo->partial_recv = g_strdup
1142 (full_msginfo->partial_recv);
1143 msginfo->total_size = full_msginfo->total_size;
1144 if (!msginfo->account_server && full_msginfo->account_server)
1145 msginfo->account_server = g_strdup
1146 (full_msginfo->account_server);
1147 if (!msginfo->account_login && full_msginfo->account_login)
1148 msginfo->account_login = g_strdup
1149 (full_msginfo->account_login);
1150 msginfo->planned_download = full_msginfo->planned_download;
1151 procmsg_msginfo_free(full_msginfo);
1153 return procmsg_msginfo_new_ref(msginfo);
1156 void procmsg_msginfo_free(MsgInfo *msginfo)
1158 if (msginfo == NULL) return;
1161 if (msginfo->refcnt > 0)
1164 if (msginfo->to_folder) {
1165 msginfo->to_folder->op_count--;
1166 folder_item_update(msginfo->to_folder, F_ITEM_UPDATE_MSGCNT);
1169 g_free(msginfo->fromspace);
1170 g_free(msginfo->returnreceiptto);
1171 g_free(msginfo->dispositionnotificationto);
1172 g_free(msginfo->xface);
1174 g_free(msginfo->fromname);
1176 g_free(msginfo->date);
1177 g_free(msginfo->from);
1178 g_free(msginfo->to);
1179 g_free(msginfo->cc);
1180 g_free(msginfo->newsgroups);
1181 g_free(msginfo->subject);
1182 g_free(msginfo->msgid);
1183 g_free(msginfo->inreplyto);
1184 g_free(msginfo->xref);
1186 g_free(msginfo->partial_recv);
1187 g_free(msginfo->account_server);
1188 g_free(msginfo->account_login);
1190 slist_free_strings(msginfo->references);
1191 g_slist_free(msginfo->references);
1193 g_free(msginfo->plaintext_file);
1198 guint procmsg_msginfo_memusage(MsgInfo *msginfo)
1203 memusage += sizeof(MsgInfo);
1204 if (msginfo->fromname)
1205 memusage += strlen(msginfo->fromname);
1207 memusage += strlen(msginfo->date);
1209 memusage += strlen(msginfo->from);
1211 memusage += strlen(msginfo->to);
1213 memusage += strlen(msginfo->cc);
1214 if (msginfo->newsgroups)
1215 memusage += strlen(msginfo->newsgroups);
1216 if (msginfo->subject)
1217 memusage += strlen(msginfo->subject);
1219 memusage += strlen(msginfo->msgid);
1220 if (msginfo->inreplyto)
1221 memusage += strlen(msginfo->inreplyto);
1223 memusage += strlen(msginfo->xface);
1224 if (msginfo->dispositionnotificationto)
1225 memusage += strlen(msginfo->dispositionnotificationto);
1226 if (msginfo->returnreceiptto)
1227 memusage += strlen(msginfo->returnreceiptto);
1228 for (refs = msginfo->references; refs; refs=refs->next) {
1229 gchar *r = (gchar *)refs->data;
1230 memusage += r?strlen(r):0;
1232 if (msginfo->fromspace)
1233 memusage += strlen(msginfo->fromspace);
1238 gint procmsg_cmp_msgnum_for_sort(gconstpointer a, gconstpointer b)
1240 const MsgInfo *msginfo1 = a;
1241 const MsgInfo *msginfo2 = b;
1248 return msginfo1->msgnum - msginfo2->msgnum;
1251 static gint procmsg_send_message_queue_full(const gchar *file, gboolean keep_session)
1253 static HeaderEntry qentry[] = {{"S:", NULL, FALSE},
1254 {"SSV:", NULL, FALSE},
1255 {"R:", NULL, FALSE},
1256 {"NG:", NULL, FALSE},
1257 {"MAID:", NULL, FALSE},
1258 {"NAID:", NULL, FALSE},
1259 {"SCF:", NULL, FALSE},
1260 {"RMID:", NULL, FALSE},
1261 {"FMID:", NULL, FALSE},
1262 {"X-Sylpheed-Privacy-System:", NULL, FALSE},
1263 {"X-Sylpheed-Encrypt:", NULL, FALSE},
1264 {"X-Sylpheed-Encrypt-Data:", NULL, FALSE},
1265 {NULL, NULL, FALSE}};
1268 gint mailval = 0, newsval = 0;
1270 gchar *smtpserver = NULL;
1271 GSList *to_list = NULL;
1272 GSList *newsgroup_list = NULL;
1273 gchar *savecopyfolder = NULL;
1274 gchar *replymessageid = NULL;
1275 gchar *fwdmessageid = NULL;
1276 gchar *privacy_system = NULL;
1277 gboolean encrypt = FALSE;
1278 gchar *encrypt_data = NULL;
1279 gchar buf[BUFFSIZE];
1281 PrefsAccount *mailac = NULL, *newsac = NULL;
1282 gboolean save_clear_text = TRUE;
1283 gchar *tmp_enc_file = NULL;
1287 g_return_val_if_fail(file != NULL, -1);
1289 if ((fp = g_fopen(file, "rb")) == NULL) {
1290 FILE_OP_ERROR(file, "fopen");
1294 while ((hnum = procheader_get_one_field(buf, sizeof(buf), fp, qentry))
1296 gchar *p = buf + strlen(qentry[hnum].name);
1304 if (smtpserver == NULL)
1305 smtpserver = g_strdup(p);
1308 to_list = address_list_append(to_list, p);
1311 newsgroup_list = newsgroup_list_append(newsgroup_list, p);
1313 case Q_MAIL_ACCOUNT_ID:
1314 mailac = account_find_from_id(atoi(p));
1316 case Q_NEWS_ACCOUNT_ID:
1317 newsac = account_find_from_id(atoi(p));
1319 case Q_SAVE_COPY_FOLDER:
1320 if (savecopyfolder == NULL)
1321 savecopyfolder = g_strdup(p);
1323 case Q_REPLY_MESSAGE_ID:
1324 if (replymessageid == NULL)
1325 replymessageid = g_strdup(p);
1327 case Q_FWD_MESSAGE_ID:
1328 if (fwdmessageid == NULL)
1329 fwdmessageid = g_strdup(p);
1331 case Q_PRIVACY_SYSTEM:
1332 if (privacy_system == NULL)
1333 privacy_system = g_strdup(p);
1339 case Q_ENCRYPT_DATA:
1340 if (encrypt_data == NULL)
1341 encrypt_data = g_strdup(p);
1345 filepos = ftell(fp);
1350 save_clear_text = (mailac != NULL && mailac->save_encrypted_as_clear_text);
1355 mimeinfo = procmime_scan_queue_file(file);
1356 if (!privacy_encrypt(privacy_system, mimeinfo, encrypt_data)
1357 || (fp = my_tmpfile()) == NULL
1358 || procmime_write_mimeinfo(mimeinfo, fp) < 0) {
1361 procmime_mimeinfo_free_all(mimeinfo);
1364 slist_free_strings(to_list);
1365 g_slist_free(to_list);
1366 slist_free_strings(newsgroup_list);
1367 g_slist_free(newsgroup_list);
1368 g_free(savecopyfolder);
1369 g_free(replymessageid);
1370 g_free(fwdmessageid);
1371 g_free(privacy_system);
1372 g_free(encrypt_data);
1377 if (!save_clear_text) {
1378 gchar *content = NULL;
1379 FILE *tmpfp = get_tmpfile_in_dir(get_mime_tmp_dir(), &tmp_enc_file);
1383 content = file_read_stream_to_str(fp);
1386 str_write_to_file(content, tmp_enc_file);
1389 g_warning("couldn't get tempfile\n");
1393 procmime_mimeinfo_free_all(mimeinfo);
1399 debug_print("Sending message by mail\n");
1401 g_warning("Queued message header is broken.\n");
1403 } else if (mailac && mailac->use_mail_command &&
1404 mailac->mail_command && (* mailac->mail_command)) {
1405 mailval = send_message_local(mailac->mail_command, fp);
1409 mailac = account_find_from_smtp_server(from, smtpserver);
1411 g_warning("Account not found. "
1412 "Using current account...\n");
1413 mailac = cur_account;
1418 mailval = send_message_smtp_full(mailac, to_list, fp, keep_session);
1420 PrefsAccount tmp_ac;
1422 g_warning("Account not found.\n");
1424 memset(&tmp_ac, 0, sizeof(PrefsAccount));
1425 tmp_ac.address = from;
1426 tmp_ac.smtp_server = smtpserver;
1427 tmp_ac.smtpport = SMTP_PORT;
1428 mailval = send_message_smtp(&tmp_ac, to_list, fp);
1433 fseek(fp, filepos, SEEK_SET);
1434 if (newsgroup_list && (mailval == 0)) {
1439 /* write to temporary file */
1440 tmp = g_strdup_printf("%s%ctmp%d", g_get_tmp_dir(),
1441 G_DIR_SEPARATOR, (gint)file);
1442 if ((tmpfp = g_fopen(tmp, "wb")) == NULL) {
1443 FILE_OP_ERROR(tmp, "fopen");
1445 alertpanel_error(_("Could not create temporary file for news sending."));
1447 if (change_file_mode_rw(tmpfp, tmp) < 0) {
1448 FILE_OP_ERROR(tmp, "chmod");
1449 g_warning("can't change file mode\n");
1452 while ((newsval == 0) && fgets(buf, sizeof(buf), fp) != NULL) {
1453 if (fputs(buf, tmpfp) == EOF) {
1454 FILE_OP_ERROR(tmp, "fputs");
1456 alertpanel_error(_("Error when writing temporary file for news sending."));
1462 debug_print("Sending message by news\n");
1464 folder = FOLDER(newsac->folder);
1466 newsval = news_post(folder, tmp);
1468 alertpanel_error(_("Error occurred while posting the message to %s ."),
1469 newsac->nntp_server);
1479 /* save message to outbox */
1480 if (mailval == 0 && newsval == 0 && savecopyfolder) {
1483 debug_print("saving sent message...\n");
1485 outbox = folder_find_item_from_identifier(savecopyfolder);
1487 outbox = folder_get_default_outbox();
1489 if (save_clear_text || tmp_enc_file == NULL) {
1490 procmsg_save_to_outbox(outbox, file, TRUE);
1492 procmsg_save_to_outbox(outbox, tmp_enc_file, FALSE);
1496 if (tmp_enc_file != NULL) {
1497 g_unlink(tmp_enc_file);
1499 tmp_enc_file = NULL;
1502 if (replymessageid != NULL || fwdmessageid != NULL) {
1506 if (replymessageid != NULL)
1507 tokens = g_strsplit(replymessageid, "\x7f", 0);
1509 tokens = g_strsplit(fwdmessageid, "\x7f", 0);
1510 item = folder_find_item_from_identifier(tokens[0]);
1512 /* check if queued message has valid folder and message id */
1513 if (item != NULL && tokens[2] != NULL) {
1516 msginfo = folder_item_get_msginfo(item, atoi(tokens[1]));
1518 /* check if referring message exists and has a message id */
1519 if ((msginfo != NULL) &&
1520 (msginfo->msgid != NULL) &&
1521 (strcmp(msginfo->msgid, tokens[2]) != 0)) {
1522 procmsg_msginfo_free(msginfo);
1526 if (msginfo == NULL) {
1527 msginfo = folder_item_get_msginfo_by_msgid(item, tokens[2]);
1530 if (msginfo != NULL) {
1531 if (replymessageid != NULL) {
1532 procmsg_msginfo_unset_flags(msginfo, MSG_FORWARDED, 0);
1533 procmsg_msginfo_set_flags(msginfo, MSG_REPLIED, 0);
1535 procmsg_msginfo_unset_flags(msginfo, MSG_REPLIED, 0);
1536 procmsg_msginfo_set_flags(msginfo, MSG_FORWARDED, 0);
1538 procmsg_msginfo_free(msginfo);
1546 slist_free_strings(to_list);
1547 g_slist_free(to_list);
1548 slist_free_strings(newsgroup_list);
1549 g_slist_free(newsgroup_list);
1550 g_free(savecopyfolder);
1551 g_free(replymessageid);
1552 g_free(fwdmessageid);
1553 g_free(privacy_system);
1554 g_free(encrypt_data);
1556 return (newsval != 0 ? newsval : mailval);
1559 gint procmsg_send_message_queue(const gchar *file)
1561 return procmsg_send_message_queue_full(file, FALSE);
1564 static void update_folder_msg_counts(FolderItem *item, MsgInfo *msginfo, MsgPermFlags old_flags)
1566 MsgPermFlags new_flags = msginfo->flags.perm_flags;
1569 if (!(old_flags & MSG_NEW) && (new_flags & MSG_NEW)) {
1573 if ((old_flags & MSG_NEW) && !(new_flags & MSG_NEW)) {
1578 if (!(old_flags & MSG_UNREAD) && (new_flags & MSG_UNREAD)) {
1579 item->unread_msgs++;
1580 if (procmsg_msg_has_marked_parent(msginfo))
1581 item->unreadmarked_msgs++;
1584 if ((old_flags & MSG_UNREAD) && !(new_flags & MSG_UNREAD)) {
1585 item->unread_msgs--;
1586 if (procmsg_msg_has_marked_parent(msginfo))
1587 item->unreadmarked_msgs--;
1591 if (!(old_flags & MSG_MARKED) && (new_flags & MSG_MARKED)) {
1592 procmsg_update_unread_children(msginfo, TRUE);
1593 item->marked_msgs++;
1596 if ((old_flags & MSG_MARKED) && !(new_flags & MSG_MARKED)) {
1597 procmsg_update_unread_children(msginfo, FALSE);
1598 item->marked_msgs--;
1602 void procmsg_msginfo_set_flags(MsgInfo *msginfo, MsgPermFlags perm_flags, MsgTmpFlags tmp_flags)
1605 MsgInfoUpdate msginfo_update;
1606 MsgPermFlags perm_flags_new, perm_flags_old;
1607 MsgTmpFlags tmp_flags_old;
1609 g_return_if_fail(msginfo != NULL);
1610 item = msginfo->folder;
1611 g_return_if_fail(item != NULL);
1613 debug_print("Setting flags for message %d in folder %s\n", msginfo->msgnum, item->path);
1615 /* Perm Flags handling */
1616 perm_flags_old = msginfo->flags.perm_flags;
1617 perm_flags_new = msginfo->flags.perm_flags | perm_flags;
1618 if ((perm_flags & MSG_IGNORE_THREAD) || (perm_flags_old & MSG_IGNORE_THREAD)) {
1619 perm_flags_new &= ~(MSG_NEW | MSG_UNREAD);
1622 if (perm_flags_old != perm_flags_new) {
1623 folder_item_change_msg_flags(msginfo->folder, msginfo, perm_flags_new);
1625 update_folder_msg_counts(item, msginfo, perm_flags_old);
1629 /* Tmp flags handling */
1630 tmp_flags_old = msginfo->flags.tmp_flags;
1631 msginfo->flags.tmp_flags |= tmp_flags;
1633 /* update notification */
1634 if ((perm_flags_old != perm_flags_new) || (tmp_flags_old != msginfo->flags.tmp_flags)) {
1635 msginfo_update.msginfo = msginfo;
1636 msginfo_update.flags = MSGINFO_UPDATE_FLAGS;
1637 hooks_invoke(MSGINFO_UPDATE_HOOKLIST, &msginfo_update);
1638 folder_item_update(msginfo->folder, F_ITEM_UPDATE_MSGCNT);
1642 void procmsg_msginfo_unset_flags(MsgInfo *msginfo, MsgPermFlags perm_flags, MsgTmpFlags tmp_flags)
1645 MsgInfoUpdate msginfo_update;
1646 MsgPermFlags perm_flags_new, perm_flags_old;
1647 MsgTmpFlags tmp_flags_old;
1649 g_return_if_fail(msginfo != NULL);
1650 item = msginfo->folder;
1651 g_return_if_fail(item != NULL);
1653 debug_print("Unsetting flags for message %d in folder %s\n", msginfo->msgnum, item->path);
1655 /* Perm Flags handling */
1656 perm_flags_old = msginfo->flags.perm_flags;
1657 perm_flags_new = msginfo->flags.perm_flags & ~perm_flags;
1659 if (perm_flags_old != perm_flags_new) {
1660 folder_item_change_msg_flags(msginfo->folder, msginfo, perm_flags_new);
1662 update_folder_msg_counts(item, msginfo, perm_flags_old);
1664 msginfo_update.msginfo = msginfo;
1665 msginfo_update.flags = MSGINFO_UPDATE_FLAGS;
1666 hooks_invoke(MSGINFO_UPDATE_HOOKLIST, &msginfo_update);
1667 folder_item_update(msginfo->folder, F_ITEM_UPDATE_MSGCNT);
1670 /* Tmp flags hanlding */
1671 tmp_flags_old = msginfo->flags.tmp_flags;
1672 msginfo->flags.tmp_flags &= ~tmp_flags;
1674 /* update notification */
1675 if ((perm_flags_old != perm_flags_new) || (tmp_flags_old != msginfo->flags.tmp_flags)) {
1676 msginfo_update.msginfo = msginfo;
1677 msginfo_update.flags = MSGINFO_UPDATE_FLAGS;
1678 hooks_invoke(MSGINFO_UPDATE_HOOKLIST, &msginfo_update);
1679 folder_item_update(msginfo->folder, F_ITEM_UPDATE_MSGCNT);
1683 void procmsg_msginfo_change_flags(MsgInfo *msginfo,
1684 MsgPermFlags add_perm_flags, MsgTmpFlags add_tmp_flags,
1685 MsgPermFlags rem_perm_flags, MsgTmpFlags rem_tmp_flags)
1688 MsgInfoUpdate msginfo_update;
1689 MsgPermFlags perm_flags_new, perm_flags_old;
1690 MsgTmpFlags tmp_flags_old;
1692 g_return_if_fail(msginfo != NULL);
1693 item = msginfo->folder;
1694 g_return_if_fail(item != NULL);
1696 debug_print("Changing flags for message %d in folder %s\n", msginfo->msgnum, item->path);
1698 /* Perm Flags handling */
1699 perm_flags_old = msginfo->flags.perm_flags;
1700 perm_flags_new = (msginfo->flags.perm_flags & ~rem_perm_flags) | add_perm_flags;
1701 if ((add_perm_flags & MSG_IGNORE_THREAD) || (perm_flags_old & MSG_IGNORE_THREAD)) {
1702 perm_flags_new &= ~(MSG_NEW | MSG_UNREAD);
1705 if (perm_flags_old != perm_flags_new) {
1706 folder_item_change_msg_flags(msginfo->folder, msginfo, perm_flags_new);
1708 update_folder_msg_counts(item, msginfo, perm_flags_old);
1712 /* Tmp flags handling */
1713 tmp_flags_old = msginfo->flags.tmp_flags;
1714 msginfo->flags.tmp_flags &= ~rem_tmp_flags;
1715 msginfo->flags.tmp_flags |= add_tmp_flags;
1717 /* update notification */
1718 if ((perm_flags_old != perm_flags_new) || (tmp_flags_old != msginfo->flags.tmp_flags)) {
1719 msginfo_update.msginfo = msginfo;
1720 msginfo_update.flags = MSGINFO_UPDATE_FLAGS;
1721 hooks_invoke(MSGINFO_UPDATE_HOOKLIST, &msginfo_update);
1722 folder_item_update(msginfo->folder, F_ITEM_UPDATE_MSGCNT);
1727 *\brief check for flags (e.g. mark) in prior msgs of current thread
1729 *\param info Current message
1730 *\param perm_flags Flags to be checked
1731 *\param parentmsgs Hash of prior msgs to avoid loops
1733 *\return gboolean TRUE if perm_flags are found
1735 gboolean procmsg_msg_has_flagged_parent_real(MsgInfo *info,
1736 MsgPermFlags perm_flags, GHashTable *parentmsgs)
1740 g_return_val_if_fail(info != NULL, FALSE);
1742 if (info != NULL && info->folder != NULL && info->inreplyto != NULL) {
1743 tmp = folder_item_get_msginfo_by_msgid(info->folder,
1745 if (tmp && (tmp->flags.perm_flags & perm_flags)) {
1746 procmsg_msginfo_free(tmp);
1748 } else if (tmp != NULL) {
1751 if (g_hash_table_lookup(parentmsgs, info)) {
1752 debug_print("loop detected: %s%c%d\n",
1753 folder_item_get_path(info->folder),
1754 G_DIR_SEPARATOR, info->msgnum);
1757 g_hash_table_insert(parentmsgs, info, "1");
1758 result = procmsg_msg_has_flagged_parent_real(
1759 tmp, perm_flags, parentmsgs);
1761 procmsg_msginfo_free(tmp);
1771 *\brief Callback for cleaning up hash of parentmsgs
1773 gboolean parentmsgs_hash_remove(gpointer key,
1781 *\brief Set up list of parentmsgs
1782 * See procmsg_msg_has_flagged_parent_real()
1784 gboolean procmsg_msg_has_flagged_parent(MsgInfo *info, MsgPermFlags perm_flags)
1787 GHashTable *parentmsgs = g_hash_table_new(NULL, NULL);
1789 result = procmsg_msg_has_flagged_parent_real(info, perm_flags, parentmsgs);
1790 g_hash_table_foreach_remove(parentmsgs, parentmsgs_hash_remove, NULL);
1791 g_hash_table_destroy(parentmsgs);
1796 *\brief Check if msgs prior in thread are marked
1797 * See procmsg_msg_has_flagged_parent_real()
1799 gboolean procmsg_msg_has_marked_parent(MsgInfo *info)
1801 return procmsg_msg_has_flagged_parent(info, MSG_MARKED);
1805 GSList *procmsg_find_children_func(MsgInfo *info,
1806 GSList *children, GSList *all)
1810 g_return_val_if_fail(info!=NULL, children);
1811 if (info->msgid == NULL)
1814 for (cur = all; cur != NULL; cur = g_slist_next(cur)) {
1815 MsgInfo *tmp = (MsgInfo *)cur->data;
1816 if (tmp->inreplyto && !strcmp(tmp->inreplyto, info->msgid)) {
1817 /* Check if message is already in the list */
1818 if ((children == NULL) ||
1819 (g_slist_index(children, tmp) == -1)) {
1820 children = g_slist_prepend(children,
1821 procmsg_msginfo_new_ref(tmp));
1822 children = procmsg_find_children_func(tmp,
1831 GSList *procmsg_find_children (MsgInfo *info)
1836 g_return_val_if_fail(info!=NULL, NULL);
1837 all = folder_item_get_msg_list(info->folder);
1838 children = procmsg_find_children_func(info, NULL, all);
1839 if (children != NULL) {
1840 for (cur = all; cur != NULL; cur = g_slist_next(cur)) {
1841 /* this will not free the used pointers
1842 created with procmsg_msginfo_new_ref */
1843 procmsg_msginfo_free((MsgInfo *)cur->data);
1851 void procmsg_update_unread_children(MsgInfo *info, gboolean newly_marked)
1853 GSList *children = procmsg_find_children(info);
1855 for (cur = children; cur != NULL; cur = g_slist_next(cur)) {
1856 MsgInfo *tmp = (MsgInfo *)cur->data;
1857 if(MSG_IS_UNREAD(tmp->flags) && !MSG_IS_IGNORE_THREAD(tmp->flags)) {
1859 info->folder->unreadmarked_msgs++;
1861 info->folder->unreadmarked_msgs--;
1862 folder_item_update(info->folder, F_ITEM_UPDATE_MSGCNT);
1864 procmsg_msginfo_free(tmp);
1866 g_slist_free(children);
1870 * Set the destination folder for a copy or move operation
1872 * \param msginfo The message which's destination folder is changed
1873 * \param to_folder The destination folder for the operation
1875 void procmsg_msginfo_set_to_folder(MsgInfo *msginfo, FolderItem *to_folder)
1877 if(msginfo->to_folder != NULL) {
1878 msginfo->to_folder->op_count--;
1879 folder_item_update(msginfo->to_folder, F_ITEM_UPDATE_MSGCNT);
1881 msginfo->to_folder = to_folder;
1882 if(to_folder != NULL) {
1883 to_folder->op_count++;
1884 folder_item_update(msginfo->to_folder, F_ITEM_UPDATE_MSGCNT);
1889 * Apply filtering actions to the msginfo
1891 * \param msginfo The MsgInfo describing the message that should be filtered
1892 * \return TRUE if the message was moved and MsgInfo is now invalid,
1895 gboolean procmsg_msginfo_filter(MsgInfo *msginfo)
1897 MailFilteringData mail_filtering_data;
1899 mail_filtering_data.msginfo = msginfo;
1900 if (hooks_invoke(MAIL_FILTERING_HOOKLIST, &mail_filtering_data))
1903 /* filter if enabled in prefs or move to inbox if not */
1904 if((filtering_rules != NULL) &&
1905 filter_message_by_msginfo(filtering_rules, msginfo))
1908 hooks_invoke(MAIL_POSTFILTERING_HOOKLIST, msginfo);
1913 MsgInfo *procmsg_msginfo_new_from_mimeinfo(MsgInfo *src_msginfo, MimeInfo *mimeinfo)
1915 MsgInfo *tmp_msginfo = NULL;
1916 MsgFlags flags = {0, 0};
1917 gchar *tmpfile = get_tmp_file();
1918 FILE *fp = g_fopen(tmpfile, "wb");
1920 if (!mimeinfo || mimeinfo->type != MIMETYPE_MESSAGE ||
1921 g_ascii_strcasecmp(mimeinfo->subtype, "rfc822")) {
1922 g_warning("procmsg_msginfo_new_from_mimeinfo(): unsuitable mimeinfo");
1929 if (fp && procmime_write_mimeinfo(mimeinfo, fp) >= 0) {
1932 tmp_msginfo = procheader_parse_file(
1939 if (tmp_msginfo != NULL) {
1940 tmp_msginfo->folder = src_msginfo->folder;
1941 tmp_msginfo->plaintext_file = g_strdup(tmpfile);
1943 g_warning("procmsg_msginfo_new_from_mimeinfo(): Can't generate new msginfo");