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 gint procmsg_move_messages(GSList *mlist)
337 GSList *cur, *movelist = NULL;
339 FolderItem *dest = NULL;
342 if (!mlist) return 0;
344 folder_item_update_freeze();
346 for (cur = mlist; cur != NULL; cur = cur->next) {
347 msginfo = (MsgInfo *)cur->data;
349 dest = msginfo->to_folder;
350 movelist = g_slist_append(movelist, msginfo);
351 } else if (dest == msginfo->to_folder) {
352 movelist = g_slist_append(movelist, msginfo);
354 folder_item_move_msgs(dest, movelist);
355 g_slist_free(movelist);
357 dest = msginfo->to_folder;
358 movelist = g_slist_append(movelist, msginfo);
360 procmsg_msginfo_set_to_folder(msginfo, NULL);
364 retval = folder_item_move_msgs(dest, movelist);
365 g_slist_free(movelist);
368 folder_item_update_thaw();
372 void procmsg_copy_messages(GSList *mlist)
374 GSList *cur, *copylist = NULL;
376 FolderItem *dest = NULL;
380 folder_item_update_freeze();
382 for (cur = mlist; cur != NULL; cur = cur->next) {
383 msginfo = (MsgInfo *)cur->data;
385 dest = msginfo->to_folder;
386 copylist = g_slist_append(copylist, msginfo);
387 } else if (dest == msginfo->to_folder) {
388 copylist = g_slist_append(copylist, msginfo);
390 folder_item_copy_msgs(dest, copylist);
391 g_slist_free(copylist);
393 dest = msginfo->to_folder;
394 copylist = g_slist_append(copylist, msginfo);
396 procmsg_msginfo_set_to_folder(msginfo, NULL);
400 folder_item_copy_msgs(dest, copylist);
401 g_slist_free(copylist);
404 folder_item_update_thaw();
407 gchar *procmsg_get_message_file_path(MsgInfo *msginfo)
411 g_return_val_if_fail(msginfo != NULL, NULL);
413 if (msginfo->plaintext_file)
414 file = g_strdup(msginfo->plaintext_file);
416 file = folder_item_fetch_msg(msginfo->folder, msginfo->msgnum);
422 gchar *procmsg_get_message_file(MsgInfo *msginfo)
424 gchar *filename = NULL;
426 g_return_val_if_fail(msginfo != NULL, NULL);
428 filename = folder_item_fetch_msg(msginfo->folder, msginfo->msgnum);
430 debug_print("can't fetch message %d\n", msginfo->msgnum);
435 gchar *procmsg_get_message_file_full(MsgInfo *msginfo, gboolean headers, gboolean body)
437 gchar *filename = NULL;
439 g_return_val_if_fail(msginfo != NULL, NULL);
441 filename = folder_item_fetch_msg_full(msginfo->folder, msginfo->msgnum,
444 debug_print("can't fetch message %d\n", msginfo->msgnum);
449 GSList *procmsg_get_message_file_list(GSList *mlist)
451 GSList *file_list = NULL;
453 MsgFileInfo *fileinfo;
456 while (mlist != NULL) {
457 msginfo = (MsgInfo *)mlist->data;
458 file = procmsg_get_message_file(msginfo);
460 procmsg_message_file_list_free(file_list);
463 fileinfo = g_new(MsgFileInfo, 1);
464 fileinfo->msginfo = procmsg_msginfo_new_ref(msginfo);
465 fileinfo->file = file;
466 fileinfo->flags = g_new(MsgFlags, 1);
467 *fileinfo->flags = msginfo->flags;
468 file_list = g_slist_prepend(file_list, fileinfo);
472 file_list = g_slist_reverse(file_list);
477 void procmsg_message_file_list_free(MsgInfoList *file_list)
480 MsgFileInfo *fileinfo;
482 for (cur = file_list; cur != NULL; cur = cur->next) {
483 fileinfo = (MsgFileInfo *)cur->data;
484 procmsg_msginfo_free(fileinfo->msginfo);
485 g_free(fileinfo->file);
486 g_free(fileinfo->flags);
490 g_slist_free(file_list);
493 FILE *procmsg_open_message(MsgInfo *msginfo)
498 g_return_val_if_fail(msginfo != NULL, NULL);
500 file = procmsg_get_message_file_path(msginfo);
501 g_return_val_if_fail(file != NULL, NULL);
503 if (!is_file_exist(file)) {
505 file = procmsg_get_message_file(msginfo);
510 if ((fp = g_fopen(file, "rb")) == NULL) {
511 FILE_OP_ERROR(file, "fopen");
518 if (MSG_IS_QUEUED(msginfo->flags) || MSG_IS_DRAFT(msginfo->flags)) {
521 while (fgets(buf, sizeof(buf), fp) != NULL)
522 if (buf[0] == '\r' || buf[0] == '\n') break;
528 gboolean procmsg_msg_exist(MsgInfo *msginfo)
533 if (!msginfo) return FALSE;
535 path = folder_item_get_path(msginfo->folder);
537 ret = !folder_item_is_msg_changed(msginfo->folder, msginfo);
543 void procmsg_get_filter_keyword(MsgInfo *msginfo, gchar **header, gchar **key,
544 PrefsFilterType type)
546 static HeaderEntry hentry[] = {{"X-BeenThere:", NULL, TRUE},
547 {"X-ML-Name:", NULL, TRUE},
548 {"X-List:", NULL, TRUE},
549 {"X-Mailing-list:", NULL, TRUE},
550 {"List-Id:", NULL, TRUE},
551 {"X-Sequence:", NULL, TRUE},
552 {NULL, NULL, FALSE}};
558 H_X_MAILING_LIST = 3,
565 g_return_if_fail(msginfo != NULL);
566 g_return_if_fail(header != NULL);
567 g_return_if_fail(key != NULL);
576 if ((fp = procmsg_open_message(msginfo)) == NULL)
578 procheader_get_header_fields(fp, hentry);
581 #define SET_FILTER_KEY(hstr, idx) \
583 *header = g_strdup(hstr); \
584 *key = hentry[idx].body; \
585 hentry[idx].body = NULL; \
588 if (hentry[H_X_BEENTHERE].body != NULL) {
589 SET_FILTER_KEY("header \"X-BeenThere\"", H_X_BEENTHERE);
590 } else if (hentry[H_X_ML_NAME].body != NULL) {
591 SET_FILTER_KEY("header \"X-ML-Name\"", H_X_ML_NAME);
592 } else if (hentry[H_X_LIST].body != NULL) {
593 SET_FILTER_KEY("header \"X-List\"", H_X_LIST);
594 } else if (hentry[H_X_MAILING_LIST].body != NULL) {
595 SET_FILTER_KEY("header \"X-Mailing-List\"", H_X_MAILING_LIST);
596 } else if (hentry[H_LIST_ID].body != NULL) {
597 SET_FILTER_KEY("header \"List-Id\"", H_LIST_ID);
598 extract_list_id_str(*key);
599 } else if (hentry[H_X_SEQUENCE].body != NULL) {
602 SET_FILTER_KEY("X-Sequence", H_X_SEQUENCE);
605 while (*p != '\0' && !g_ascii_isspace(*p)) p++;
606 while (g_ascii_isspace(*p)) p++;
607 if (g_ascii_isdigit(*p)) {
613 } else if (msginfo->subject) {
614 *header = g_strdup("subject");
615 *key = g_strdup(msginfo->subject);
618 #undef SET_FILTER_KEY
620 g_free(hentry[H_X_BEENTHERE].body);
621 hentry[H_X_BEENTHERE].body = NULL;
622 g_free(hentry[H_X_ML_NAME].body);
623 hentry[H_X_ML_NAME].body = NULL;
624 g_free(hentry[H_X_LIST].body);
625 hentry[H_X_LIST].body = NULL;
626 g_free(hentry[H_X_MAILING_LIST].body);
627 hentry[H_X_MAILING_LIST].body = NULL;
628 g_free(hentry[H_LIST_ID].body);
629 hentry[H_LIST_ID].body = NULL;
633 *header = g_strdup("from");
634 *key = g_strdup(msginfo->from);
637 *header = g_strdup("to");
638 *key = g_strdup(msginfo->to);
640 case FILTER_BY_SUBJECT:
641 *header = g_strdup("subject");
642 *key = g_strdup(msginfo->subject);
649 void procmsg_empty_trash(FolderItem *trash)
651 if (trash && trash->total_msgs > 0) {
652 GSList *mlist = folder_item_get_msg_list(trash);
654 for (cur = mlist ; cur != NULL ; cur = cur->next) {
655 MsgInfo * msginfo = (MsgInfo *) cur->data;
656 if (MSG_IS_LOCKED(msginfo->flags))
658 if (msginfo->total_size != 0 &&
659 msginfo->size != (off_t)msginfo->total_size)
660 partial_mark_for_delete(msginfo);
662 procmsg_msginfo_free(msginfo);
665 folder_item_remove_all_msg(trash);
669 void procmsg_empty_all_trash(void)
674 for (cur = folder_get_list(); cur != NULL; cur = cur->next) {
675 Folder *folder = FOLDER(cur->data);
676 trash = folder->trash;
677 procmsg_empty_trash(trash);
678 if (folder->account && folder->account->set_trash_folder &&
679 folder_find_item_from_identifier(folder->account->trash_folder))
681 folder_find_item_from_identifier(folder->account->trash_folder));
685 static PrefsAccount *procmsg_get_account_from_file(const gchar *file)
687 PrefsAccount *mailac = NULL;
691 static HeaderEntry qentry[] = {{"S:", NULL, FALSE},
692 {"SSV:", NULL, FALSE},
694 {"NG:", NULL, FALSE},
695 {"MAID:", NULL, FALSE},
696 {"NAID:", NULL, FALSE},
697 {"SCF:", NULL, FALSE},
698 {"RMID:", NULL, FALSE},
699 {"FMID:", NULL, FALSE},
700 {"X-Sylpheed-Privacy-System:", NULL, FALSE},
701 {"X-Sylpheed-Encrypt:", NULL, FALSE},
702 {"X-Sylpheed-Encrypt-Data:", NULL, FALSE},
703 {NULL, NULL, FALSE}};
705 g_return_val_if_fail(file != NULL, NULL);
707 if ((fp = g_fopen(file, "rb")) == NULL) {
708 FILE_OP_ERROR(file, "fopen");
712 while ((hnum = procheader_get_one_field(buf, sizeof(buf), fp, qentry))
714 gchar *p = buf + strlen(qentry[hnum].name);
716 if (hnum == Q_MAIL_ACCOUNT_ID) {
717 mailac = account_find_from_id(atoi(p));
725 static GSList *procmsg_list_sort_by_account(FolderItem *queue, GSList *list)
727 GSList *result = NULL;
729 PrefsAccount *last_account = NULL;
732 gboolean nothing_to_sort = TRUE;
737 orig = g_slist_copy(list);
739 msg = (MsgInfo *)orig->data;
741 for (cur = orig; cur; cur = cur->next)
742 debug_print("sort before %s\n", ((MsgInfo *)cur->data)->from);
747 nothing_to_sort = TRUE;
751 PrefsAccount *ac = procmsg_get_account_from_file(file);
752 msg = (MsgInfo *)cur->data;
753 file = folder_item_fetch_msg(queue, msg->msgnum);
756 if (last_account == NULL || (ac != NULL && ac == last_account)) {
757 result = g_slist_append(result, msg);
758 orig = g_slist_remove(orig, msg);
760 nothing_to_sort = FALSE;
766 if (orig || g_slist_length(orig)) {
767 if (!last_account && nothing_to_sort) {
768 /* can't find an account for the rest of the list */
771 result = g_slist_append(result, cur->data);
782 for (cur = result; cur; cur = cur->next)
783 debug_print("sort after %s\n", ((MsgInfo *)cur->data)->from);
790 static gboolean procmsg_is_last_for_account(FolderItem *queue, MsgInfo *msginfo, GSList *elem)
792 gchar *file = folder_item_fetch_msg(queue, msginfo->msgnum);
793 PrefsAccount *ac = procmsg_get_account_from_file(file);
796 for (cur = elem; cur; cur = cur->next) {
797 MsgInfo *cur_msginfo = (MsgInfo *)cur->data;
798 file = folder_item_fetch_msg(queue, cur_msginfo->msgnum);
800 if (cur_msginfo != msginfo && !MSG_IS_LOCKED(cur_msginfo->flags)) {
801 if (procmsg_get_account_from_file(file) == ac) {
813 *\brief Send messages in queue
815 *\param queue Queue folder to process
816 *\param save_msgs Unused
818 *\return Number of messages sent, negative if an error occurred
819 * positive if no error occurred
821 gint procmsg_send_queue(FolderItem *queue, gboolean save_msgs)
823 gint sent = 0, err = 0;
825 GSList *sorted_list = NULL;
828 queue = folder_get_default_queue();
829 g_return_val_if_fail(queue != NULL, -1);
831 folder_item_scan(queue);
832 list = folder_item_get_msg_list(queue);
834 /* sort the list per sender account; this helps reusing the same SMTP server */
835 sorted_list = procmsg_list_sort_by_account(queue, list);
837 for (elem = sorted_list; elem != NULL; elem = elem->next) {
841 msginfo = (MsgInfo *)(elem->data);
842 if (!MSG_IS_LOCKED(msginfo->flags)) {
843 file = folder_item_fetch_msg(queue, msginfo->msgnum);
845 if (procmsg_send_message_queue_full(file,
846 !procmsg_is_last_for_account(queue, msginfo, elem)) < 0) {
847 g_warning("Sending queued message %d failed.\n",
852 * We save in procmsg_send_message_queue because
853 * we need the destination folder from the queue
857 procmsg_save_to_outbox
858 (queue->folder->outbox,
862 folder_item_remove_msg(queue, msginfo->msgnum);
867 /* FIXME: supposedly if only one message is locked, and queue
868 * is being flushed, the following free says something like
869 * "freeing msg ## in folder (nil)". */
870 procmsg_msginfo_free(msginfo);
873 g_slist_free(sorted_list);
875 return (err != 0 ? -err : sent);
879 *\brief Determine if a queue folder is empty
881 *\param queue Queue folder to process
883 *\return TRUE if the queue folder is empty, otherwise return FALSE
885 gboolean procmsg_queue_is_empty(FolderItem *queue)
888 gboolean res = FALSE;
890 queue = folder_get_default_queue();
891 g_return_val_if_fail(queue != NULL, TRUE);
893 folder_item_scan(queue);
894 list = folder_item_get_msg_list(queue);
895 res = (list == NULL);
896 procmsg_msg_list_free(list);
900 gint procmsg_remove_special_headers(const gchar *in, const gchar *out)
905 if ((fp = g_fopen(in, "rb")) == NULL) {
906 FILE_OP_ERROR(in, "fopen");
909 if ((outfp = g_fopen(out, "wb")) == NULL) {
910 FILE_OP_ERROR(out, "fopen");
914 while (fgets(buf, sizeof(buf), fp) != NULL)
915 if (buf[0] == '\r' || buf[0] == '\n') break;
916 while (fgets(buf, sizeof(buf), fp) != NULL)
923 gint procmsg_save_to_outbox(FolderItem *outbox, const gchar *file,
927 MsgInfo *msginfo, *tmp_msginfo;
928 MsgFlags flag = {0, 0};
930 debug_print("saving sent message...\n");
933 outbox = folder_get_default_outbox();
934 g_return_val_if_fail(outbox != NULL, -1);
936 /* remove queueing headers */
938 gchar tmp[MAXPATHLEN + 1];
940 g_snprintf(tmp, sizeof(tmp), "%s%ctmpmsg.out.%08x",
941 get_rc_dir(), G_DIR_SEPARATOR, (guint) rand());
943 if (procmsg_remove_special_headers(file, tmp) !=0)
946 folder_item_scan(outbox);
947 if ((num = folder_item_add_msg(outbox, tmp, &flag, TRUE)) < 0) {
948 g_warning("can't save message\n");
953 folder_item_scan(outbox);
954 if ((num = folder_item_add_msg
955 (outbox, file, &flag, FALSE)) < 0) {
956 g_warning("can't save message\n");
961 msginfo = folder_item_get_msginfo(outbox, num); /* refcnt++ */
962 tmp_msginfo = procmsg_msginfo_get_full_info(msginfo); /* refcnt++ */
963 if (msginfo != NULL) {
964 procmsg_msginfo_unset_flags(msginfo, ~0, 0);
965 procmsg_msginfo_free(msginfo); /* refcnt-- */
966 /* tmp_msginfo == msginfo */
967 if (tmp_msginfo && (msginfo->dispositionnotificationto ||
968 msginfo->returnreceiptto)) {
969 procmsg_msginfo_set_flags(msginfo, MSG_RETRCPT_SENT, 0);
970 procmsg_msginfo_free(msginfo); /* refcnt-- */
977 void procmsg_print_message(MsgInfo *msginfo, const gchar *cmdline)
979 static const gchar *def_cmd = "lpr %s";
986 g_return_if_fail(msginfo);
988 if (procmime_msginfo_is_encrypted(msginfo))
989 tmpfp = procmime_get_first_encrypted_text_content(msginfo);
991 tmpfp = procmime_get_first_text_content(msginfo);
993 g_warning("Can't get text part\n");
997 prtmp = g_strdup_printf("%s%cprinttmp.%08x",
998 get_mime_tmp_dir(), G_DIR_SEPARATOR, id++);
1000 if ((prfp = g_fopen(prtmp, "wb")) == NULL) {
1001 FILE_OP_ERROR(prtmp, "fopen");
1007 if (msginfo->date) fprintf(prfp, "Date: %s\n", msginfo->date);
1008 if (msginfo->from) fprintf(prfp, "From: %s\n", msginfo->from);
1009 if (msginfo->to) fprintf(prfp, "To: %s\n", msginfo->to);
1010 if (msginfo->cc) fprintf(prfp, "Cc: %s\n", msginfo->cc);
1011 if (msginfo->newsgroups)
1012 fprintf(prfp, "Newsgroups: %s\n", msginfo->newsgroups);
1013 if (msginfo->subject) fprintf(prfp, "Subject: %s\n", msginfo->subject);
1016 while (fgets(buf, sizeof(buf), tmpfp) != NULL)
1022 if (cmdline && (p = strchr(cmdline, '%')) && *(p + 1) == 's' &&
1023 !strchr(p + 2, '%'))
1024 g_snprintf(buf, sizeof(buf) - 1, cmdline, prtmp);
1027 g_warning("Print command line is invalid: '%s'\n",
1029 g_snprintf(buf, sizeof(buf) - 1, def_cmd, prtmp);
1035 if (buf[strlen(buf) - 1] != '&') strcat(buf, "&");
1039 MsgInfo *procmsg_msginfo_new_ref(MsgInfo *msginfo)
1046 MsgInfo *procmsg_msginfo_new(void)
1048 MsgInfo *newmsginfo;
1050 newmsginfo = g_new0(MsgInfo, 1);
1051 newmsginfo->refcnt = 1;
1056 MsgInfo *procmsg_msginfo_copy(MsgInfo *msginfo)
1058 MsgInfo *newmsginfo;
1061 if (msginfo == NULL) return NULL;
1063 newmsginfo = g_new0(MsgInfo, 1);
1065 newmsginfo->refcnt = 1;
1067 #define MEMBCOPY(mmb) newmsginfo->mmb = msginfo->mmb
1068 #define MEMBDUP(mmb) newmsginfo->mmb = msginfo->mmb ? \
1069 g_strdup(msginfo->mmb) : NULL
1084 MEMBDUP(newsgroups);
1091 MEMBCOPY(to_folder);
1094 MEMBDUP(dispositionnotificationto);
1095 MEMBDUP(returnreceiptto);
1097 refs = msginfo->references;
1098 for (refs = msginfo->references; refs != NULL; refs = refs->next) {
1099 newmsginfo->references = g_slist_prepend
1100 (newmsginfo->references, g_strdup(refs->data));
1102 newmsginfo->references = g_slist_reverse(newmsginfo->references);
1105 MEMBCOPY(threadscore);
1106 MEMBDUP(plaintext_file);
1111 MsgInfo *procmsg_msginfo_get_full_info(MsgInfo *msginfo)
1113 MsgInfo *full_msginfo;
1116 if (msginfo == NULL) return NULL;
1118 file = procmsg_get_message_file_path(msginfo);
1119 if (!file || !is_file_exist(file)) {
1121 file = procmsg_get_message_file(msginfo);
1123 if (!file || !is_file_exist(file)) {
1124 g_warning("procmsg_msginfo_get_full_info(): can't get message file.\n");
1128 full_msginfo = procheader_parse_file(file, msginfo->flags, TRUE, FALSE);
1130 if (!full_msginfo) return NULL;
1132 /* CLAWS: make sure we add the missing members; see:
1133 * procheader.c::procheader_get_headernames() */
1134 if (!msginfo->xface)
1135 msginfo->xface = g_strdup(full_msginfo->xface);
1136 if (!msginfo->dispositionnotificationto)
1137 msginfo->dispositionnotificationto =
1138 g_strdup(full_msginfo->dispositionnotificationto);
1139 if (!msginfo->returnreceiptto)
1140 msginfo->returnreceiptto = g_strdup
1141 (full_msginfo->returnreceiptto);
1142 if (!msginfo->partial_recv && full_msginfo->partial_recv)
1143 msginfo->partial_recv = g_strdup
1144 (full_msginfo->partial_recv);
1145 msginfo->total_size = full_msginfo->total_size;
1146 if (!msginfo->account_server && full_msginfo->account_server)
1147 msginfo->account_server = g_strdup
1148 (full_msginfo->account_server);
1149 if (!msginfo->account_login && full_msginfo->account_login)
1150 msginfo->account_login = g_strdup
1151 (full_msginfo->account_login);
1152 msginfo->planned_download = full_msginfo->planned_download;
1153 procmsg_msginfo_free(full_msginfo);
1155 return procmsg_msginfo_new_ref(msginfo);
1158 void procmsg_msginfo_free(MsgInfo *msginfo)
1160 if (msginfo == NULL) return;
1163 if (msginfo->refcnt > 0)
1166 if (msginfo->to_folder) {
1167 msginfo->to_folder->op_count--;
1168 folder_item_update(msginfo->to_folder, F_ITEM_UPDATE_MSGCNT);
1171 g_free(msginfo->fromspace);
1172 g_free(msginfo->returnreceiptto);
1173 g_free(msginfo->dispositionnotificationto);
1174 g_free(msginfo->xface);
1176 g_free(msginfo->fromname);
1178 g_free(msginfo->date);
1179 g_free(msginfo->from);
1180 g_free(msginfo->to);
1181 g_free(msginfo->cc);
1182 g_free(msginfo->newsgroups);
1183 g_free(msginfo->subject);
1184 g_free(msginfo->msgid);
1185 g_free(msginfo->inreplyto);
1186 g_free(msginfo->xref);
1188 g_free(msginfo->partial_recv);
1189 g_free(msginfo->account_server);
1190 g_free(msginfo->account_login);
1192 slist_free_strings(msginfo->references);
1193 g_slist_free(msginfo->references);
1195 g_free(msginfo->plaintext_file);
1200 guint procmsg_msginfo_memusage(MsgInfo *msginfo)
1205 memusage += sizeof(MsgInfo);
1206 if (msginfo->fromname)
1207 memusage += strlen(msginfo->fromname);
1209 memusage += strlen(msginfo->date);
1211 memusage += strlen(msginfo->from);
1213 memusage += strlen(msginfo->to);
1215 memusage += strlen(msginfo->cc);
1216 if (msginfo->newsgroups)
1217 memusage += strlen(msginfo->newsgroups);
1218 if (msginfo->subject)
1219 memusage += strlen(msginfo->subject);
1221 memusage += strlen(msginfo->msgid);
1222 if (msginfo->inreplyto)
1223 memusage += strlen(msginfo->inreplyto);
1225 memusage += strlen(msginfo->xface);
1226 if (msginfo->dispositionnotificationto)
1227 memusage += strlen(msginfo->dispositionnotificationto);
1228 if (msginfo->returnreceiptto)
1229 memusage += strlen(msginfo->returnreceiptto);
1230 for (refs = msginfo->references; refs; refs=refs->next) {
1231 gchar *r = (gchar *)refs->data;
1232 memusage += r?strlen(r):0;
1234 if (msginfo->fromspace)
1235 memusage += strlen(msginfo->fromspace);
1240 gint procmsg_cmp_msgnum_for_sort(gconstpointer a, gconstpointer b)
1242 const MsgInfo *msginfo1 = a;
1243 const MsgInfo *msginfo2 = b;
1250 return msginfo1->msgnum - msginfo2->msgnum;
1253 static gint procmsg_send_message_queue_full(const gchar *file, gboolean keep_session)
1255 static HeaderEntry qentry[] = {{"S:", NULL, FALSE},
1256 {"SSV:", NULL, FALSE},
1257 {"R:", NULL, FALSE},
1258 {"NG:", NULL, FALSE},
1259 {"MAID:", NULL, FALSE},
1260 {"NAID:", NULL, FALSE},
1261 {"SCF:", NULL, FALSE},
1262 {"RMID:", NULL, FALSE},
1263 {"FMID:", NULL, FALSE},
1264 {"X-Sylpheed-Privacy-System:", NULL, FALSE},
1265 {"X-Sylpheed-Encrypt:", NULL, FALSE},
1266 {"X-Sylpheed-Encrypt-Data:", NULL, FALSE},
1267 {NULL, NULL, FALSE}};
1270 gint mailval = 0, newsval = 0;
1272 gchar *smtpserver = NULL;
1273 GSList *to_list = NULL;
1274 GSList *newsgroup_list = NULL;
1275 gchar *savecopyfolder = NULL;
1276 gchar *replymessageid = NULL;
1277 gchar *fwdmessageid = NULL;
1278 gchar *privacy_system = NULL;
1279 gboolean encrypt = FALSE;
1280 gchar *encrypt_data = NULL;
1281 gchar buf[BUFFSIZE];
1283 PrefsAccount *mailac = NULL, *newsac = NULL;
1284 gboolean save_clear_text = TRUE;
1285 gchar *tmp_enc_file = NULL;
1289 g_return_val_if_fail(file != NULL, -1);
1291 if ((fp = g_fopen(file, "rb")) == NULL) {
1292 FILE_OP_ERROR(file, "fopen");
1296 while ((hnum = procheader_get_one_field(buf, sizeof(buf), fp, qentry))
1298 gchar *p = buf + strlen(qentry[hnum].name);
1306 if (smtpserver == NULL)
1307 smtpserver = g_strdup(p);
1310 to_list = address_list_append(to_list, p);
1313 newsgroup_list = newsgroup_list_append(newsgroup_list, p);
1315 case Q_MAIL_ACCOUNT_ID:
1316 mailac = account_find_from_id(atoi(p));
1318 case Q_NEWS_ACCOUNT_ID:
1319 newsac = account_find_from_id(atoi(p));
1321 case Q_SAVE_COPY_FOLDER:
1322 if (savecopyfolder == NULL)
1323 savecopyfolder = g_strdup(p);
1325 case Q_REPLY_MESSAGE_ID:
1326 if (replymessageid == NULL)
1327 replymessageid = g_strdup(p);
1329 case Q_FWD_MESSAGE_ID:
1330 if (fwdmessageid == NULL)
1331 fwdmessageid = g_strdup(p);
1333 case Q_PRIVACY_SYSTEM:
1334 if (privacy_system == NULL)
1335 privacy_system = g_strdup(p);
1341 case Q_ENCRYPT_DATA:
1342 if (encrypt_data == NULL)
1343 encrypt_data = g_strdup(p);
1347 filepos = ftell(fp);
1352 save_clear_text = (mailac != NULL && mailac->save_encrypted_as_clear_text);
1357 mimeinfo = procmime_scan_queue_file(file);
1358 if (!privacy_encrypt(privacy_system, mimeinfo, encrypt_data)
1359 || (fp = my_tmpfile()) == NULL
1360 || procmime_write_mimeinfo(mimeinfo, fp) < 0) {
1363 procmime_mimeinfo_free_all(mimeinfo);
1366 slist_free_strings(to_list);
1367 g_slist_free(to_list);
1368 slist_free_strings(newsgroup_list);
1369 g_slist_free(newsgroup_list);
1370 g_free(savecopyfolder);
1371 g_free(replymessageid);
1372 g_free(fwdmessageid);
1373 g_free(privacy_system);
1374 g_free(encrypt_data);
1379 if (!save_clear_text) {
1380 gchar *content = NULL;
1381 FILE *tmpfp = get_tmpfile_in_dir(get_mime_tmp_dir(), &tmp_enc_file);
1385 content = file_read_stream_to_str(fp);
1388 str_write_to_file(content, tmp_enc_file);
1391 g_warning("couldn't get tempfile\n");
1395 procmime_mimeinfo_free_all(mimeinfo);
1401 debug_print("Sending message by mail\n");
1403 g_warning("Queued message header is broken.\n");
1405 } else if (mailac && mailac->use_mail_command &&
1406 mailac->mail_command && (* mailac->mail_command)) {
1407 mailval = send_message_local(mailac->mail_command, fp);
1411 mailac = account_find_from_smtp_server(from, smtpserver);
1413 g_warning("Account not found. "
1414 "Using current account...\n");
1415 mailac = cur_account;
1420 mailval = send_message_smtp_full(mailac, to_list, fp, keep_session);
1422 PrefsAccount tmp_ac;
1424 g_warning("Account not found.\n");
1426 memset(&tmp_ac, 0, sizeof(PrefsAccount));
1427 tmp_ac.address = from;
1428 tmp_ac.smtp_server = smtpserver;
1429 tmp_ac.smtpport = SMTP_PORT;
1430 mailval = send_message_smtp(&tmp_ac, to_list, fp);
1435 fseek(fp, filepos, SEEK_SET);
1436 if (newsgroup_list && (mailval == 0)) {
1441 /* write to temporary file */
1442 tmp = g_strdup_printf("%s%ctmp%d", g_get_tmp_dir(),
1443 G_DIR_SEPARATOR, (gint)file);
1444 if ((tmpfp = g_fopen(tmp, "wb")) == NULL) {
1445 FILE_OP_ERROR(tmp, "fopen");
1447 alertpanel_error(_("Could not create temporary file for news sending."));
1449 if (change_file_mode_rw(tmpfp, tmp) < 0) {
1450 FILE_OP_ERROR(tmp, "chmod");
1451 g_warning("can't change file mode\n");
1454 while ((newsval == 0) && fgets(buf, sizeof(buf), fp) != NULL) {
1455 if (fputs(buf, tmpfp) == EOF) {
1456 FILE_OP_ERROR(tmp, "fputs");
1458 alertpanel_error(_("Error when writing temporary file for news sending."));
1464 debug_print("Sending message by news\n");
1466 folder = FOLDER(newsac->folder);
1468 newsval = news_post(folder, tmp);
1470 alertpanel_error(_("Error occurred while posting the message to %s ."),
1471 newsac->nntp_server);
1481 /* save message to outbox */
1482 if (mailval == 0 && newsval == 0 && savecopyfolder) {
1485 debug_print("saving sent message...\n");
1487 outbox = folder_find_item_from_identifier(savecopyfolder);
1489 outbox = folder_get_default_outbox();
1491 if (save_clear_text || tmp_enc_file == NULL) {
1492 procmsg_save_to_outbox(outbox, file, TRUE);
1494 procmsg_save_to_outbox(outbox, tmp_enc_file, FALSE);
1498 if (tmp_enc_file != NULL) {
1499 g_unlink(tmp_enc_file);
1501 tmp_enc_file = NULL;
1504 if (replymessageid != NULL || fwdmessageid != NULL) {
1508 if (replymessageid != NULL)
1509 tokens = g_strsplit(replymessageid, "\x7f", 0);
1511 tokens = g_strsplit(fwdmessageid, "\x7f", 0);
1512 item = folder_find_item_from_identifier(tokens[0]);
1514 /* check if queued message has valid folder and message id */
1515 if (item != NULL && tokens[2] != NULL) {
1518 msginfo = folder_item_get_msginfo(item, atoi(tokens[1]));
1520 /* check if referring message exists and has a message id */
1521 if ((msginfo != NULL) &&
1522 (msginfo->msgid != NULL) &&
1523 (strcmp(msginfo->msgid, tokens[2]) != 0)) {
1524 procmsg_msginfo_free(msginfo);
1528 if (msginfo == NULL) {
1529 msginfo = folder_item_get_msginfo_by_msgid(item, tokens[2]);
1532 if (msginfo != NULL) {
1533 if (replymessageid != NULL) {
1534 procmsg_msginfo_unset_flags(msginfo, MSG_FORWARDED, 0);
1535 procmsg_msginfo_set_flags(msginfo, MSG_REPLIED, 0);
1537 procmsg_msginfo_unset_flags(msginfo, MSG_REPLIED, 0);
1538 procmsg_msginfo_set_flags(msginfo, MSG_FORWARDED, 0);
1540 procmsg_msginfo_free(msginfo);
1548 slist_free_strings(to_list);
1549 g_slist_free(to_list);
1550 slist_free_strings(newsgroup_list);
1551 g_slist_free(newsgroup_list);
1552 g_free(savecopyfolder);
1553 g_free(replymessageid);
1554 g_free(fwdmessageid);
1555 g_free(privacy_system);
1556 g_free(encrypt_data);
1558 return (newsval != 0 ? newsval : mailval);
1561 gint procmsg_send_message_queue(const gchar *file)
1563 return procmsg_send_message_queue_full(file, FALSE);
1566 static void update_folder_msg_counts(FolderItem *item, MsgInfo *msginfo, MsgPermFlags old_flags)
1568 MsgPermFlags new_flags = msginfo->flags.perm_flags;
1571 if (!(old_flags & MSG_NEW) && (new_flags & MSG_NEW)) {
1575 if ((old_flags & MSG_NEW) && !(new_flags & MSG_NEW)) {
1580 if (!(old_flags & MSG_UNREAD) && (new_flags & MSG_UNREAD)) {
1581 item->unread_msgs++;
1582 if (procmsg_msg_has_marked_parent(msginfo))
1583 item->unreadmarked_msgs++;
1586 if ((old_flags & MSG_UNREAD) && !(new_flags & MSG_UNREAD)) {
1587 item->unread_msgs--;
1588 if (procmsg_msg_has_marked_parent(msginfo))
1589 item->unreadmarked_msgs--;
1593 if (!(old_flags & MSG_MARKED) && (new_flags & MSG_MARKED)) {
1594 procmsg_update_unread_children(msginfo, TRUE);
1595 item->marked_msgs++;
1598 if ((old_flags & MSG_MARKED) && !(new_flags & MSG_MARKED)) {
1599 procmsg_update_unread_children(msginfo, FALSE);
1600 item->marked_msgs--;
1604 void procmsg_msginfo_set_flags(MsgInfo *msginfo, MsgPermFlags perm_flags, MsgTmpFlags tmp_flags)
1607 MsgInfoUpdate msginfo_update;
1608 MsgPermFlags perm_flags_new, perm_flags_old;
1609 MsgTmpFlags tmp_flags_old;
1611 g_return_if_fail(msginfo != NULL);
1612 item = msginfo->folder;
1613 g_return_if_fail(item != NULL);
1615 debug_print("Setting flags for message %d in folder %s\n", msginfo->msgnum, item->path);
1617 /* Perm Flags handling */
1618 perm_flags_old = msginfo->flags.perm_flags;
1619 perm_flags_new = msginfo->flags.perm_flags | perm_flags;
1620 if ((perm_flags & MSG_IGNORE_THREAD) || (perm_flags_old & MSG_IGNORE_THREAD)) {
1621 perm_flags_new &= ~(MSG_NEW | MSG_UNREAD);
1624 if (perm_flags_old != perm_flags_new) {
1625 folder_item_change_msg_flags(msginfo->folder, msginfo, perm_flags_new);
1627 update_folder_msg_counts(item, msginfo, perm_flags_old);
1631 /* Tmp flags handling */
1632 tmp_flags_old = msginfo->flags.tmp_flags;
1633 msginfo->flags.tmp_flags |= tmp_flags;
1635 /* update notification */
1636 if ((perm_flags_old != perm_flags_new) || (tmp_flags_old != msginfo->flags.tmp_flags)) {
1637 msginfo_update.msginfo = msginfo;
1638 msginfo_update.flags = MSGINFO_UPDATE_FLAGS;
1639 hooks_invoke(MSGINFO_UPDATE_HOOKLIST, &msginfo_update);
1640 folder_item_update(msginfo->folder, F_ITEM_UPDATE_MSGCNT);
1644 void procmsg_msginfo_unset_flags(MsgInfo *msginfo, MsgPermFlags perm_flags, MsgTmpFlags tmp_flags)
1647 MsgInfoUpdate msginfo_update;
1648 MsgPermFlags perm_flags_new, perm_flags_old;
1649 MsgTmpFlags tmp_flags_old;
1651 g_return_if_fail(msginfo != NULL);
1652 item = msginfo->folder;
1653 g_return_if_fail(item != NULL);
1655 debug_print("Unsetting flags for message %d in folder %s\n", msginfo->msgnum, item->path);
1657 /* Perm Flags handling */
1658 perm_flags_old = msginfo->flags.perm_flags;
1659 perm_flags_new = msginfo->flags.perm_flags & ~perm_flags;
1661 if (perm_flags_old != perm_flags_new) {
1662 folder_item_change_msg_flags(msginfo->folder, msginfo, perm_flags_new);
1664 update_folder_msg_counts(item, msginfo, perm_flags_old);
1666 msginfo_update.msginfo = msginfo;
1667 msginfo_update.flags = MSGINFO_UPDATE_FLAGS;
1668 hooks_invoke(MSGINFO_UPDATE_HOOKLIST, &msginfo_update);
1669 folder_item_update(msginfo->folder, F_ITEM_UPDATE_MSGCNT);
1672 /* Tmp flags hanlding */
1673 tmp_flags_old = msginfo->flags.tmp_flags;
1674 msginfo->flags.tmp_flags &= ~tmp_flags;
1676 /* update notification */
1677 if ((perm_flags_old != perm_flags_new) || (tmp_flags_old != msginfo->flags.tmp_flags)) {
1678 msginfo_update.msginfo = msginfo;
1679 msginfo_update.flags = MSGINFO_UPDATE_FLAGS;
1680 hooks_invoke(MSGINFO_UPDATE_HOOKLIST, &msginfo_update);
1681 folder_item_update(msginfo->folder, F_ITEM_UPDATE_MSGCNT);
1685 void procmsg_msginfo_change_flags(MsgInfo *msginfo,
1686 MsgPermFlags add_perm_flags, MsgTmpFlags add_tmp_flags,
1687 MsgPermFlags rem_perm_flags, MsgTmpFlags rem_tmp_flags)
1690 MsgInfoUpdate msginfo_update;
1691 MsgPermFlags perm_flags_new, perm_flags_old;
1692 MsgTmpFlags tmp_flags_old;
1694 g_return_if_fail(msginfo != NULL);
1695 item = msginfo->folder;
1696 g_return_if_fail(item != NULL);
1698 debug_print("Changing flags for message %d in folder %s\n", msginfo->msgnum, item->path);
1700 /* Perm Flags handling */
1701 perm_flags_old = msginfo->flags.perm_flags;
1702 perm_flags_new = (msginfo->flags.perm_flags & ~rem_perm_flags) | add_perm_flags;
1703 if ((add_perm_flags & MSG_IGNORE_THREAD) || (perm_flags_old & MSG_IGNORE_THREAD)) {
1704 perm_flags_new &= ~(MSG_NEW | MSG_UNREAD);
1707 if (perm_flags_old != perm_flags_new) {
1708 folder_item_change_msg_flags(msginfo->folder, msginfo, perm_flags_new);
1710 update_folder_msg_counts(item, msginfo, perm_flags_old);
1714 /* Tmp flags handling */
1715 tmp_flags_old = msginfo->flags.tmp_flags;
1716 msginfo->flags.tmp_flags &= ~rem_tmp_flags;
1717 msginfo->flags.tmp_flags |= add_tmp_flags;
1719 /* update notification */
1720 if ((perm_flags_old != perm_flags_new) || (tmp_flags_old != msginfo->flags.tmp_flags)) {
1721 msginfo_update.msginfo = msginfo;
1722 msginfo_update.flags = MSGINFO_UPDATE_FLAGS;
1723 hooks_invoke(MSGINFO_UPDATE_HOOKLIST, &msginfo_update);
1724 folder_item_update(msginfo->folder, F_ITEM_UPDATE_MSGCNT);
1729 *\brief check for flags (e.g. mark) in prior msgs of current thread
1731 *\param info Current message
1732 *\param perm_flags Flags to be checked
1733 *\param parentmsgs Hash of prior msgs to avoid loops
1735 *\return gboolean TRUE if perm_flags are found
1737 gboolean procmsg_msg_has_flagged_parent_real(MsgInfo *info,
1738 MsgPermFlags perm_flags, GHashTable *parentmsgs)
1742 g_return_val_if_fail(info != NULL, FALSE);
1744 if (info != NULL && info->folder != NULL && info->inreplyto != NULL) {
1745 tmp = folder_item_get_msginfo_by_msgid(info->folder,
1747 if (tmp && (tmp->flags.perm_flags & perm_flags)) {
1748 procmsg_msginfo_free(tmp);
1750 } else if (tmp != NULL) {
1753 if (g_hash_table_lookup(parentmsgs, info)) {
1754 debug_print("loop detected: %s%c%d\n",
1755 folder_item_get_path(info->folder),
1756 G_DIR_SEPARATOR, info->msgnum);
1759 g_hash_table_insert(parentmsgs, info, "1");
1760 result = procmsg_msg_has_flagged_parent_real(
1761 tmp, perm_flags, parentmsgs);
1763 procmsg_msginfo_free(tmp);
1773 *\brief Callback for cleaning up hash of parentmsgs
1775 gboolean parentmsgs_hash_remove(gpointer key,
1783 *\brief Set up list of parentmsgs
1784 * See procmsg_msg_has_flagged_parent_real()
1786 gboolean procmsg_msg_has_flagged_parent(MsgInfo *info, MsgPermFlags perm_flags)
1789 GHashTable *parentmsgs = g_hash_table_new(NULL, NULL);
1791 result = procmsg_msg_has_flagged_parent_real(info, perm_flags, parentmsgs);
1792 g_hash_table_foreach_remove(parentmsgs, parentmsgs_hash_remove, NULL);
1793 g_hash_table_destroy(parentmsgs);
1798 *\brief Check if msgs prior in thread are marked
1799 * See procmsg_msg_has_flagged_parent_real()
1801 gboolean procmsg_msg_has_marked_parent(MsgInfo *info)
1803 return procmsg_msg_has_flagged_parent(info, MSG_MARKED);
1807 GSList *procmsg_find_children_func(MsgInfo *info,
1808 GSList *children, GSList *all)
1812 g_return_val_if_fail(info!=NULL, children);
1813 if (info->msgid == NULL)
1816 for (cur = all; cur != NULL; cur = g_slist_next(cur)) {
1817 MsgInfo *tmp = (MsgInfo *)cur->data;
1818 if (tmp->inreplyto && !strcmp(tmp->inreplyto, info->msgid)) {
1819 /* Check if message is already in the list */
1820 if ((children == NULL) ||
1821 (g_slist_index(children, tmp) == -1)) {
1822 children = g_slist_prepend(children,
1823 procmsg_msginfo_new_ref(tmp));
1824 children = procmsg_find_children_func(tmp,
1833 GSList *procmsg_find_children (MsgInfo *info)
1838 g_return_val_if_fail(info!=NULL, NULL);
1839 all = folder_item_get_msg_list(info->folder);
1840 children = procmsg_find_children_func(info, NULL, all);
1841 if (children != NULL) {
1842 for (cur = all; cur != NULL; cur = g_slist_next(cur)) {
1843 /* this will not free the used pointers
1844 created with procmsg_msginfo_new_ref */
1845 procmsg_msginfo_free((MsgInfo *)cur->data);
1853 void procmsg_update_unread_children(MsgInfo *info, gboolean newly_marked)
1855 GSList *children = procmsg_find_children(info);
1857 for (cur = children; cur != NULL; cur = g_slist_next(cur)) {
1858 MsgInfo *tmp = (MsgInfo *)cur->data;
1859 if(MSG_IS_UNREAD(tmp->flags) && !MSG_IS_IGNORE_THREAD(tmp->flags)) {
1861 info->folder->unreadmarked_msgs++;
1863 info->folder->unreadmarked_msgs--;
1864 folder_item_update(info->folder, F_ITEM_UPDATE_MSGCNT);
1866 procmsg_msginfo_free(tmp);
1868 g_slist_free(children);
1872 * Set the destination folder for a copy or move operation
1874 * \param msginfo The message which's destination folder is changed
1875 * \param to_folder The destination folder for the operation
1877 void procmsg_msginfo_set_to_folder(MsgInfo *msginfo, FolderItem *to_folder)
1879 if(msginfo->to_folder != NULL) {
1880 msginfo->to_folder->op_count--;
1881 folder_item_update(msginfo->to_folder, F_ITEM_UPDATE_MSGCNT);
1883 msginfo->to_folder = to_folder;
1884 if(to_folder != NULL) {
1885 to_folder->op_count++;
1886 folder_item_update(msginfo->to_folder, F_ITEM_UPDATE_MSGCNT);
1891 * Apply filtering actions to the msginfo
1893 * \param msginfo The MsgInfo describing the message that should be filtered
1894 * \return TRUE if the message was moved and MsgInfo is now invalid,
1897 gboolean procmsg_msginfo_filter(MsgInfo *msginfo)
1899 MailFilteringData mail_filtering_data;
1901 mail_filtering_data.msginfo = msginfo;
1902 if (hooks_invoke(MAIL_FILTERING_HOOKLIST, &mail_filtering_data))
1905 /* filter if enabled in prefs or move to inbox if not */
1906 if((filtering_rules != NULL) &&
1907 filter_message_by_msginfo(filtering_rules, msginfo))
1910 hooks_invoke(MAIL_POSTFILTERING_HOOKLIST, msginfo);
1915 MsgInfo *procmsg_msginfo_new_from_mimeinfo(MsgInfo *src_msginfo, MimeInfo *mimeinfo)
1917 MsgInfo *tmp_msginfo = NULL;
1918 MsgFlags flags = {0, 0};
1919 gchar *tmpfile = get_tmp_file();
1920 FILE *fp = g_fopen(tmpfile, "wb");
1922 if (!mimeinfo || mimeinfo->type != MIMETYPE_MESSAGE ||
1923 g_ascii_strcasecmp(mimeinfo->subtype, "rfc822")) {
1924 g_warning("procmsg_msginfo_new_from_mimeinfo(): unsuitable mimeinfo");
1931 if (fp && procmime_write_mimeinfo(mimeinfo, fp) >= 0) {
1934 tmp_msginfo = procheader_parse_file(
1941 if (tmp_msginfo != NULL) {
1942 tmp_msginfo->folder = src_msginfo->folder;
1943 tmp_msginfo->plaintext_file = g_strdup(tmpfile);
1945 g_warning("procmsg_msginfo_new_from_mimeinfo(): Can't generate new msginfo");