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 {"Sender:", NULL, TRUE},
553 {"List-Post:", NULL, TRUE},
554 {NULL, NULL, FALSE}};
560 H_X_MAILING_LIST = 3,
569 g_return_if_fail(msginfo != NULL);
570 g_return_if_fail(header != NULL);
571 g_return_if_fail(key != NULL);
580 if ((fp = procmsg_open_message(msginfo)) == NULL)
582 procheader_get_header_fields(fp, hentry);
585 #define SET_FILTER_KEY(hstr, idx) \
587 *header = g_strdup(hstr); \
588 *key = hentry[idx].body; \
589 hentry[idx].body = NULL; \
592 if (hentry[H_X_BEENTHERE].body != NULL) {
593 SET_FILTER_KEY("header \"X-BeenThere\"", H_X_BEENTHERE);
594 } else if (hentry[H_X_ML_NAME].body != NULL) {
595 SET_FILTER_KEY("header \"X-ML-Name\"", H_X_ML_NAME);
596 } else if (hentry[H_X_LIST].body != NULL) {
597 SET_FILTER_KEY("header \"X-List\"", H_X_LIST);
598 } else if (hentry[H_X_MAILING_LIST].body != NULL) {
599 SET_FILTER_KEY("header \"X-Mailing-List\"", H_X_MAILING_LIST);
600 } else if (hentry[H_LIST_ID].body != NULL) {
601 SET_FILTER_KEY("header \"List-Id\"", H_LIST_ID);
602 extract_list_id_str(*key);
603 } else if (hentry[H_X_SEQUENCE].body != NULL) {
606 SET_FILTER_KEY("X-Sequence", H_X_SEQUENCE);
609 while (*p != '\0' && !g_ascii_isspace(*p)) p++;
610 while (g_ascii_isspace(*p)) p++;
611 if (g_ascii_isdigit(*p)) {
617 } else if (hentry[H_SENDER].body != NULL) {
618 SET_FILTER_KEY("header \"Sender\"", H_SENDER);
619 } else if (hentry[H_LIST_POST].body != NULL) {
620 SET_FILTER_KEY("header \"Sender\"", H_LIST_POST);
621 } else if (msginfo->to) {
622 *header = g_strdup("to");
623 *key = g_strdup(msginfo->to);
624 } else if (msginfo->subject) {
625 *header = g_strdup("subject");
626 *key = g_strdup(msginfo->subject);
629 #undef SET_FILTER_KEY
631 g_free(hentry[H_X_BEENTHERE].body);
632 hentry[H_X_BEENTHERE].body = NULL;
633 g_free(hentry[H_X_ML_NAME].body);
634 hentry[H_X_ML_NAME].body = NULL;
635 g_free(hentry[H_X_LIST].body);
636 hentry[H_X_LIST].body = NULL;
637 g_free(hentry[H_X_MAILING_LIST].body);
638 hentry[H_X_MAILING_LIST].body = NULL;
639 g_free(hentry[H_LIST_ID].body);
640 hentry[H_LIST_ID].body = NULL;
641 g_free(hentry[H_SENDER].body);
642 hentry[H_SENDER].body = NULL;
643 g_free(hentry[H_LIST_POST].body);
644 hentry[H_LIST_POST].body = NULL;
648 *header = g_strdup("from");
649 *key = g_strdup(msginfo->from);
652 *header = g_strdup("to");
653 *key = g_strdup(msginfo->to);
655 case FILTER_BY_SUBJECT:
656 *header = g_strdup("subject");
657 *key = g_strdup(msginfo->subject);
664 void procmsg_empty_trash(FolderItem *trash)
669 (trash->stype != F_TRASH &&
670 !folder_has_parent_of_type(trash, F_TRASH)))
673 if (trash && trash->total_msgs > 0) {
674 GSList *mlist = folder_item_get_msg_list(trash);
676 for (cur = mlist ; cur != NULL ; cur = cur->next) {
677 MsgInfo * msginfo = (MsgInfo *) cur->data;
678 if (MSG_IS_LOCKED(msginfo->flags))
680 if (msginfo->total_size != 0 &&
681 msginfo->size != (off_t)msginfo->total_size)
682 partial_mark_for_delete(msginfo);
684 procmsg_msginfo_free(msginfo);
687 folder_item_remove_all_msg(trash);
690 if (!trash->node || !trash->node->children)
693 node = trash->node->children;
694 while (node != NULL) {
696 procmsg_empty_trash(FOLDER_ITEM(node->data));
701 void procmsg_empty_all_trash(void)
706 for (cur = folder_get_list(); cur != NULL; cur = cur->next) {
707 Folder *folder = FOLDER(cur->data);
708 trash = folder->trash;
709 procmsg_empty_trash(trash);
710 if (folder->account && folder->account->set_trash_folder &&
711 folder_find_item_from_identifier(folder->account->trash_folder))
713 folder_find_item_from_identifier(folder->account->trash_folder));
717 static PrefsAccount *procmsg_get_account_from_file(const gchar *file)
719 PrefsAccount *mailac = NULL;
723 static HeaderEntry qentry[] = {{"S:", NULL, FALSE},
724 {"SSV:", NULL, FALSE},
726 {"NG:", NULL, FALSE},
727 {"MAID:", NULL, FALSE},
728 {"NAID:", NULL, FALSE},
729 {"SCF:", NULL, FALSE},
730 {"RMID:", NULL, FALSE},
731 {"FMID:", NULL, FALSE},
732 {"X-Sylpheed-Privacy-System:", NULL, FALSE},
733 {"X-Sylpheed-Encrypt:", NULL, FALSE},
734 {"X-Sylpheed-Encrypt-Data:", NULL, FALSE},
735 {NULL, NULL, FALSE}};
737 g_return_val_if_fail(file != NULL, NULL);
739 if ((fp = g_fopen(file, "rb")) == NULL) {
740 FILE_OP_ERROR(file, "fopen");
744 while ((hnum = procheader_get_one_field(buf, sizeof(buf), fp, qentry))
746 gchar *p = buf + strlen(qentry[hnum].name);
748 if (hnum == Q_MAIL_ACCOUNT_ID) {
749 mailac = account_find_from_id(atoi(p));
757 static GSList *procmsg_list_sort_by_account(FolderItem *queue, GSList *list)
759 GSList *result = NULL;
761 PrefsAccount *last_account = NULL;
764 gboolean nothing_to_sort = TRUE;
769 orig = g_slist_copy(list);
771 msg = (MsgInfo *)orig->data;
773 for (cur = orig; cur; cur = cur->next)
774 debug_print("sort before %s\n", ((MsgInfo *)cur->data)->from);
779 nothing_to_sort = TRUE;
783 PrefsAccount *ac = NULL;
784 msg = (MsgInfo *)cur->data;
785 file = folder_item_fetch_msg(queue, msg->msgnum);
786 ac = procmsg_get_account_from_file(file);
789 if (last_account == NULL || (ac != NULL && ac == last_account)) {
790 result = g_slist_append(result, msg);
791 orig = g_slist_remove(orig, msg);
793 nothing_to_sort = FALSE;
799 if (orig || g_slist_length(orig)) {
800 if (!last_account && nothing_to_sort) {
801 /* can't find an account for the rest of the list */
804 result = g_slist_append(result, cur->data);
815 for (cur = result; cur; cur = cur->next)
816 debug_print("sort after %s\n", ((MsgInfo *)cur->data)->from);
823 static gboolean procmsg_is_last_for_account(FolderItem *queue, MsgInfo *msginfo, GSList *elem)
825 gchar *file = folder_item_fetch_msg(queue, msginfo->msgnum);
826 PrefsAccount *ac = procmsg_get_account_from_file(file);
829 for (cur = elem; cur; cur = cur->next) {
830 MsgInfo *cur_msginfo = (MsgInfo *)cur->data;
831 file = folder_item_fetch_msg(queue, cur_msginfo->msgnum);
833 if (cur_msginfo != msginfo && !MSG_IS_LOCKED(cur_msginfo->flags)) {
834 if (procmsg_get_account_from_file(file) == ac) {
846 *\brief Send messages in queue
848 *\param queue Queue folder to process
849 *\param save_msgs Unused
851 *\return Number of messages sent, negative if an error occurred
852 * positive if no error occurred
854 gint procmsg_send_queue(FolderItem *queue, gboolean save_msgs)
856 gint sent = 0, err = 0;
858 GSList *sorted_list = NULL;
862 queue = folder_get_default_queue();
863 g_return_val_if_fail(queue != NULL, -1);
865 folder_item_scan(queue);
866 list = folder_item_get_msg_list(queue);
868 /* sort the list per sender account; this helps reusing the same SMTP server */
869 sorted_list = procmsg_list_sort_by_account(queue, list);
871 for (elem = sorted_list; elem != NULL; elem = elem->next) {
875 msginfo = (MsgInfo *)(elem->data);
876 if (!MSG_IS_LOCKED(msginfo->flags)) {
877 file = folder_item_fetch_msg(queue, msginfo->msgnum);
879 if (procmsg_send_message_queue_full(file,
880 !procmsg_is_last_for_account(queue, msginfo, elem)) < 0) {
881 g_warning("Sending queued message %d failed.\n",
886 * We save in procmsg_send_message_queue because
887 * we need the destination folder from the queue
891 procmsg_save_to_outbox
892 (queue->folder->outbox,
896 folder_item_remove_msg(queue, msginfo->msgnum);
901 /* FIXME: supposedly if only one message is locked, and queue
902 * is being flushed, the following free says something like
903 * "freeing msg ## in folder (nil)". */
904 procmsg_msginfo_free(msginfo);
907 g_slist_free(sorted_list);
908 folder_item_scan(queue);
910 if (queue->node && queue->node->children) {
911 node = queue->node->children;
912 while (node != NULL) {
915 res = procmsg_send_queue(FOLDER_ITEM(node->data), save_msgs);
924 return (err != 0 ? -err : sent);
928 *\brief Determine if a queue folder is empty
930 *\param queue Queue folder to process
932 *\return TRUE if the queue folder is empty, otherwise return FALSE
934 gboolean procmsg_queue_is_empty(FolderItem *queue)
937 gboolean res = FALSE;
939 queue = folder_get_default_queue();
940 g_return_val_if_fail(queue != NULL, TRUE);
942 folder_item_scan(queue);
943 list = folder_item_get_msg_list(queue);
944 res = (list == NULL);
945 procmsg_msg_list_free(list);
949 if (queue->node && queue->node->children) {
950 node = queue->node->children;
951 while (node != NULL) {
953 if (!procmsg_queue_is_empty(FOLDER_ITEM(node->data)))
962 gint procmsg_remove_special_headers(const gchar *in, const gchar *out)
967 if ((fp = g_fopen(in, "rb")) == NULL) {
968 FILE_OP_ERROR(in, "fopen");
971 if ((outfp = g_fopen(out, "wb")) == NULL) {
972 FILE_OP_ERROR(out, "fopen");
976 while (fgets(buf, sizeof(buf), fp) != NULL)
977 if (buf[0] == '\r' || buf[0] == '\n') break;
978 while (fgets(buf, sizeof(buf), fp) != NULL)
985 gint procmsg_save_to_outbox(FolderItem *outbox, const gchar *file,
989 MsgInfo *msginfo, *tmp_msginfo;
990 MsgFlags flag = {0, 0};
992 debug_print("saving sent message...\n");
995 outbox = folder_get_default_outbox();
996 g_return_val_if_fail(outbox != NULL, -1);
998 /* remove queueing headers */
1000 gchar tmp[MAXPATHLEN + 1];
1002 g_snprintf(tmp, sizeof(tmp), "%s%ctmpmsg.out.%08x",
1003 get_rc_dir(), G_DIR_SEPARATOR, (guint) rand());
1005 if (procmsg_remove_special_headers(file, tmp) !=0)
1008 folder_item_scan(outbox);
1009 if ((num = folder_item_add_msg(outbox, tmp, &flag, TRUE)) < 0) {
1010 g_warning("can't save message\n");
1015 folder_item_scan(outbox);
1016 if ((num = folder_item_add_msg
1017 (outbox, file, &flag, FALSE)) < 0) {
1018 g_warning("can't save message\n");
1023 msginfo = folder_item_get_msginfo(outbox, num); /* refcnt++ */
1024 tmp_msginfo = procmsg_msginfo_get_full_info(msginfo); /* refcnt++ */
1025 if (msginfo != NULL) {
1026 procmsg_msginfo_unset_flags(msginfo, ~0, 0);
1027 procmsg_msginfo_free(msginfo); /* refcnt-- */
1028 /* tmp_msginfo == msginfo */
1029 if (tmp_msginfo && (msginfo->dispositionnotificationto ||
1030 msginfo->returnreceiptto)) {
1031 procmsg_msginfo_set_flags(msginfo, MSG_RETRCPT_SENT, 0);
1032 procmsg_msginfo_free(msginfo); /* refcnt-- */
1039 void procmsg_print_message(MsgInfo *msginfo, const gchar *cmdline)
1041 static const gchar *def_cmd = "lpr %s";
1042 static guint id = 0;
1048 g_return_if_fail(msginfo);
1050 if (procmime_msginfo_is_encrypted(msginfo))
1051 tmpfp = procmime_get_first_encrypted_text_content(msginfo);
1053 tmpfp = procmime_get_first_text_content(msginfo);
1054 if (tmpfp == NULL) {
1055 g_warning("Can't get text part\n");
1059 prtmp = g_strdup_printf("%s%cprinttmp.%08x",
1060 get_mime_tmp_dir(), G_DIR_SEPARATOR, id++);
1062 if ((prfp = g_fopen(prtmp, "wb")) == NULL) {
1063 FILE_OP_ERROR(prtmp, "fopen");
1069 if (msginfo->date) fprintf(prfp, "Date: %s\n", msginfo->date);
1070 if (msginfo->from) fprintf(prfp, "From: %s\n", msginfo->from);
1071 if (msginfo->to) fprintf(prfp, "To: %s\n", msginfo->to);
1072 if (msginfo->cc) fprintf(prfp, "Cc: %s\n", msginfo->cc);
1073 if (msginfo->newsgroups)
1074 fprintf(prfp, "Newsgroups: %s\n", msginfo->newsgroups);
1075 if (msginfo->subject) fprintf(prfp, "Subject: %s\n", msginfo->subject);
1078 while (fgets(buf, sizeof(buf), tmpfp) != NULL)
1084 if (cmdline && (p = strchr(cmdline, '%')) && *(p + 1) == 's' &&
1085 !strchr(p + 2, '%'))
1086 g_snprintf(buf, sizeof(buf) - 1, cmdline, prtmp);
1089 g_warning("Print command line is invalid: '%s'\n",
1091 g_snprintf(buf, sizeof(buf) - 1, def_cmd, prtmp);
1097 if (buf[strlen(buf) - 1] != '&') strcat(buf, "&");
1101 MsgInfo *procmsg_msginfo_new_ref(MsgInfo *msginfo)
1108 MsgInfo *procmsg_msginfo_new(void)
1110 MsgInfo *newmsginfo;
1112 newmsginfo = g_new0(MsgInfo, 1);
1113 newmsginfo->refcnt = 1;
1118 MsgInfo *procmsg_msginfo_copy(MsgInfo *msginfo)
1120 MsgInfo *newmsginfo;
1123 if (msginfo == NULL) return NULL;
1125 newmsginfo = g_new0(MsgInfo, 1);
1127 newmsginfo->refcnt = 1;
1129 #define MEMBCOPY(mmb) newmsginfo->mmb = msginfo->mmb
1130 #define MEMBDUP(mmb) newmsginfo->mmb = msginfo->mmb ? \
1131 g_strdup(msginfo->mmb) : NULL
1146 MEMBDUP(newsgroups);
1153 MEMBCOPY(to_folder);
1156 MEMBDUP(dispositionnotificationto);
1157 MEMBDUP(returnreceiptto);
1159 refs = msginfo->references;
1160 for (refs = msginfo->references; refs != NULL; refs = refs->next) {
1161 newmsginfo->references = g_slist_prepend
1162 (newmsginfo->references, g_strdup(refs->data));
1164 newmsginfo->references = g_slist_reverse(newmsginfo->references);
1167 MEMBDUP(plaintext_file);
1172 MsgInfo *procmsg_msginfo_get_full_info(MsgInfo *msginfo)
1174 MsgInfo *full_msginfo;
1177 if (msginfo == NULL) return NULL;
1179 file = procmsg_get_message_file_path(msginfo);
1180 if (!file || !is_file_exist(file)) {
1182 file = procmsg_get_message_file(msginfo);
1184 if (!file || !is_file_exist(file)) {
1185 g_warning("procmsg_msginfo_get_full_info(): can't get message file.\n");
1189 full_msginfo = procheader_parse_file(file, msginfo->flags, TRUE, FALSE);
1191 if (!full_msginfo) return NULL;
1193 /* CLAWS: make sure we add the missing members; see:
1194 * procheader.c::procheader_get_headernames() */
1195 if (!msginfo->xface)
1196 msginfo->xface = g_strdup(full_msginfo->xface);
1197 if (!msginfo->dispositionnotificationto)
1198 msginfo->dispositionnotificationto =
1199 g_strdup(full_msginfo->dispositionnotificationto);
1200 if (!msginfo->returnreceiptto)
1201 msginfo->returnreceiptto = g_strdup
1202 (full_msginfo->returnreceiptto);
1203 if (!msginfo->partial_recv && full_msginfo->partial_recv)
1204 msginfo->partial_recv = g_strdup
1205 (full_msginfo->partial_recv);
1206 msginfo->total_size = full_msginfo->total_size;
1207 if (!msginfo->account_server && full_msginfo->account_server)
1208 msginfo->account_server = g_strdup
1209 (full_msginfo->account_server);
1210 if (!msginfo->account_login && full_msginfo->account_login)
1211 msginfo->account_login = g_strdup
1212 (full_msginfo->account_login);
1213 msginfo->planned_download = full_msginfo->planned_download;
1214 procmsg_msginfo_free(full_msginfo);
1216 return procmsg_msginfo_new_ref(msginfo);
1219 void procmsg_msginfo_free(MsgInfo *msginfo)
1221 if (msginfo == NULL) return;
1224 if (msginfo->refcnt > 0)
1227 if (msginfo->to_folder) {
1228 msginfo->to_folder->op_count--;
1229 folder_item_update(msginfo->to_folder, F_ITEM_UPDATE_MSGCNT);
1232 g_free(msginfo->fromspace);
1233 g_free(msginfo->returnreceiptto);
1234 g_free(msginfo->dispositionnotificationto);
1235 g_free(msginfo->xface);
1237 g_free(msginfo->fromname);
1239 g_free(msginfo->date);
1240 g_free(msginfo->from);
1241 g_free(msginfo->to);
1242 g_free(msginfo->cc);
1243 g_free(msginfo->newsgroups);
1244 g_free(msginfo->subject);
1245 g_free(msginfo->msgid);
1246 g_free(msginfo->inreplyto);
1247 g_free(msginfo->xref);
1249 g_free(msginfo->partial_recv);
1250 g_free(msginfo->account_server);
1251 g_free(msginfo->account_login);
1253 slist_free_strings(msginfo->references);
1254 g_slist_free(msginfo->references);
1256 g_free(msginfo->plaintext_file);
1261 guint procmsg_msginfo_memusage(MsgInfo *msginfo)
1266 memusage += sizeof(MsgInfo);
1267 if (msginfo->fromname)
1268 memusage += strlen(msginfo->fromname);
1270 memusage += strlen(msginfo->date);
1272 memusage += strlen(msginfo->from);
1274 memusage += strlen(msginfo->to);
1276 memusage += strlen(msginfo->cc);
1277 if (msginfo->newsgroups)
1278 memusage += strlen(msginfo->newsgroups);
1279 if (msginfo->subject)
1280 memusage += strlen(msginfo->subject);
1282 memusage += strlen(msginfo->msgid);
1283 if (msginfo->inreplyto)
1284 memusage += strlen(msginfo->inreplyto);
1286 memusage += strlen(msginfo->xface);
1287 if (msginfo->dispositionnotificationto)
1288 memusage += strlen(msginfo->dispositionnotificationto);
1289 if (msginfo->returnreceiptto)
1290 memusage += strlen(msginfo->returnreceiptto);
1291 for (refs = msginfo->references; refs; refs=refs->next) {
1292 gchar *r = (gchar *)refs->data;
1293 memusage += r?strlen(r):0;
1295 if (msginfo->fromspace)
1296 memusage += strlen(msginfo->fromspace);
1301 gint procmsg_cmp_msgnum_for_sort(gconstpointer a, gconstpointer b)
1303 const MsgInfo *msginfo1 = a;
1304 const MsgInfo *msginfo2 = b;
1311 return msginfo1->msgnum - msginfo2->msgnum;
1314 static gint procmsg_send_message_queue_full(const gchar *file, gboolean keep_session)
1316 static HeaderEntry qentry[] = {{"S:", NULL, FALSE},
1317 {"SSV:", NULL, FALSE},
1318 {"R:", NULL, FALSE},
1319 {"NG:", NULL, FALSE},
1320 {"MAID:", NULL, FALSE},
1321 {"NAID:", NULL, FALSE},
1322 {"SCF:", NULL, FALSE},
1323 {"RMID:", NULL, FALSE},
1324 {"FMID:", NULL, FALSE},
1325 {"X-Sylpheed-Privacy-System:", NULL, FALSE},
1326 {"X-Sylpheed-Encrypt:", NULL, FALSE},
1327 {"X-Sylpheed-Encrypt-Data:", NULL, FALSE},
1328 {NULL, NULL, FALSE}};
1331 gint mailval = 0, newsval = 0;
1333 gchar *smtpserver = NULL;
1334 GSList *to_list = NULL;
1335 GSList *newsgroup_list = NULL;
1336 gchar *savecopyfolder = NULL;
1337 gchar *replymessageid = NULL;
1338 gchar *fwdmessageid = NULL;
1339 gchar *privacy_system = NULL;
1340 gboolean encrypt = FALSE;
1341 gchar *encrypt_data = NULL;
1342 gchar buf[BUFFSIZE];
1344 PrefsAccount *mailac = NULL, *newsac = NULL;
1345 gboolean save_clear_text = TRUE;
1346 gchar *tmp_enc_file = NULL;
1350 g_return_val_if_fail(file != NULL, -1);
1352 if ((fp = g_fopen(file, "rb")) == NULL) {
1353 FILE_OP_ERROR(file, "fopen");
1357 while ((hnum = procheader_get_one_field(buf, sizeof(buf), fp, qentry))
1359 gchar *p = buf + strlen(qentry[hnum].name);
1367 if (smtpserver == NULL)
1368 smtpserver = g_strdup(p);
1371 to_list = address_list_append(to_list, p);
1374 newsgroup_list = newsgroup_list_append(newsgroup_list, p);
1376 case Q_MAIL_ACCOUNT_ID:
1377 mailac = account_find_from_id(atoi(p));
1379 case Q_NEWS_ACCOUNT_ID:
1380 newsac = account_find_from_id(atoi(p));
1382 case Q_SAVE_COPY_FOLDER:
1383 if (savecopyfolder == NULL)
1384 savecopyfolder = g_strdup(p);
1386 case Q_REPLY_MESSAGE_ID:
1387 if (replymessageid == NULL)
1388 replymessageid = g_strdup(p);
1390 case Q_FWD_MESSAGE_ID:
1391 if (fwdmessageid == NULL)
1392 fwdmessageid = g_strdup(p);
1394 case Q_PRIVACY_SYSTEM:
1395 if (privacy_system == NULL)
1396 privacy_system = g_strdup(p);
1402 case Q_ENCRYPT_DATA:
1403 if (encrypt_data == NULL)
1404 encrypt_data = g_strdup(p);
1408 filepos = ftell(fp);
1413 save_clear_text = (mailac != NULL && mailac->save_encrypted_as_clear_text);
1418 mimeinfo = procmime_scan_queue_file(file);
1419 if (!privacy_encrypt(privacy_system, mimeinfo, encrypt_data)
1420 || (fp = my_tmpfile()) == NULL
1421 || procmime_write_mimeinfo(mimeinfo, fp) < 0) {
1424 procmime_mimeinfo_free_all(mimeinfo);
1427 slist_free_strings(to_list);
1428 g_slist_free(to_list);
1429 slist_free_strings(newsgroup_list);
1430 g_slist_free(newsgroup_list);
1431 g_free(savecopyfolder);
1432 g_free(replymessageid);
1433 g_free(fwdmessageid);
1434 g_free(privacy_system);
1435 g_free(encrypt_data);
1440 if (!save_clear_text) {
1441 gchar *content = NULL;
1442 FILE *tmpfp = get_tmpfile_in_dir(get_mime_tmp_dir(), &tmp_enc_file);
1446 content = file_read_stream_to_str(fp);
1449 str_write_to_file(content, tmp_enc_file);
1452 g_warning("couldn't get tempfile\n");
1456 procmime_mimeinfo_free_all(mimeinfo);
1462 debug_print("Sending message by mail\n");
1464 g_warning("Queued message header is broken.\n");
1466 } else if (mailac && mailac->use_mail_command &&
1467 mailac->mail_command && (* mailac->mail_command)) {
1468 mailval = send_message_local(mailac->mail_command, fp);
1472 mailac = account_find_from_smtp_server(from, smtpserver);
1474 g_warning("Account not found. "
1475 "Using current account...\n");
1476 mailac = cur_account;
1481 mailval = send_message_smtp_full(mailac, to_list, fp, keep_session);
1483 PrefsAccount tmp_ac;
1485 g_warning("Account not found.\n");
1487 memset(&tmp_ac, 0, sizeof(PrefsAccount));
1488 tmp_ac.address = from;
1489 tmp_ac.smtp_server = smtpserver;
1490 tmp_ac.smtpport = SMTP_PORT;
1491 mailval = send_message_smtp(&tmp_ac, to_list, fp);
1496 fseek(fp, filepos, SEEK_SET);
1497 if (newsgroup_list && (mailval == 0)) {
1502 /* write to temporary file */
1503 tmp = g_strdup_printf("%s%ctmp%d", g_get_tmp_dir(),
1504 G_DIR_SEPARATOR, (gint)file);
1505 if ((tmpfp = g_fopen(tmp, "wb")) == NULL) {
1506 FILE_OP_ERROR(tmp, "fopen");
1508 alertpanel_error(_("Could not create temporary file for news sending."));
1510 if (change_file_mode_rw(tmpfp, tmp) < 0) {
1511 FILE_OP_ERROR(tmp, "chmod");
1512 g_warning("can't change file mode\n");
1515 while ((newsval == 0) && fgets(buf, sizeof(buf), fp) != NULL) {
1516 if (fputs(buf, tmpfp) == EOF) {
1517 FILE_OP_ERROR(tmp, "fputs");
1519 alertpanel_error(_("Error when writing temporary file for news sending."));
1525 debug_print("Sending message by news\n");
1527 folder = FOLDER(newsac->folder);
1529 newsval = news_post(folder, tmp);
1531 alertpanel_error(_("Error occurred while posting the message to %s ."),
1532 newsac->nntp_server);
1542 /* save message to outbox */
1543 if (mailval == 0 && newsval == 0 && savecopyfolder) {
1546 debug_print("saving sent message...\n");
1548 outbox = folder_find_item_from_identifier(savecopyfolder);
1550 outbox = folder_get_default_outbox();
1552 if (save_clear_text || tmp_enc_file == NULL) {
1553 procmsg_save_to_outbox(outbox, file, TRUE);
1555 procmsg_save_to_outbox(outbox, tmp_enc_file, FALSE);
1559 if (tmp_enc_file != NULL) {
1560 g_unlink(tmp_enc_file);
1562 tmp_enc_file = NULL;
1565 if (replymessageid != NULL || fwdmessageid != NULL) {
1569 if (replymessageid != NULL)
1570 tokens = g_strsplit(replymessageid, "\x7f", 0);
1572 tokens = g_strsplit(fwdmessageid, "\x7f", 0);
1573 item = folder_find_item_from_identifier(tokens[0]);
1575 /* check if queued message has valid folder and message id */
1576 if (item != NULL && tokens[2] != NULL) {
1579 msginfo = folder_item_get_msginfo(item, atoi(tokens[1]));
1581 /* check if referring message exists and has a message id */
1582 if ((msginfo != NULL) &&
1583 (msginfo->msgid != NULL) &&
1584 (strcmp(msginfo->msgid, tokens[2]) != 0)) {
1585 procmsg_msginfo_free(msginfo);
1589 if (msginfo == NULL) {
1590 msginfo = folder_item_get_msginfo_by_msgid(item, tokens[2]);
1593 if (msginfo != NULL) {
1594 if (replymessageid != NULL) {
1595 procmsg_msginfo_unset_flags(msginfo, MSG_FORWARDED, 0);
1596 procmsg_msginfo_set_flags(msginfo, MSG_REPLIED, 0);
1598 procmsg_msginfo_unset_flags(msginfo, MSG_REPLIED, 0);
1599 procmsg_msginfo_set_flags(msginfo, MSG_FORWARDED, 0);
1601 procmsg_msginfo_free(msginfo);
1609 slist_free_strings(to_list);
1610 g_slist_free(to_list);
1611 slist_free_strings(newsgroup_list);
1612 g_slist_free(newsgroup_list);
1613 g_free(savecopyfolder);
1614 g_free(replymessageid);
1615 g_free(fwdmessageid);
1616 g_free(privacy_system);
1617 g_free(encrypt_data);
1619 return (newsval != 0 ? newsval : mailval);
1622 gint procmsg_send_message_queue(const gchar *file)
1624 return procmsg_send_message_queue_full(file, FALSE);
1627 static void update_folder_msg_counts(FolderItem *item, MsgInfo *msginfo, MsgPermFlags old_flags)
1629 MsgPermFlags new_flags = msginfo->flags.perm_flags;
1632 if (!(old_flags & MSG_NEW) && (new_flags & MSG_NEW)) {
1636 if ((old_flags & MSG_NEW) && !(new_flags & MSG_NEW)) {
1641 if (!(old_flags & MSG_UNREAD) && (new_flags & MSG_UNREAD)) {
1642 item->unread_msgs++;
1643 if (procmsg_msg_has_marked_parent(msginfo))
1644 item->unreadmarked_msgs++;
1647 if ((old_flags & MSG_UNREAD) && !(new_flags & MSG_UNREAD)) {
1648 item->unread_msgs--;
1649 if (procmsg_msg_has_marked_parent(msginfo))
1650 item->unreadmarked_msgs--;
1654 if (!(old_flags & MSG_MARKED) && (new_flags & MSG_MARKED)) {
1655 procmsg_update_unread_children(msginfo, TRUE);
1656 item->marked_msgs++;
1659 if ((old_flags & MSG_MARKED) && !(new_flags & MSG_MARKED)) {
1660 procmsg_update_unread_children(msginfo, FALSE);
1661 item->marked_msgs--;
1665 void procmsg_msginfo_set_flags(MsgInfo *msginfo, MsgPermFlags perm_flags, MsgTmpFlags tmp_flags)
1668 MsgInfoUpdate msginfo_update;
1669 MsgPermFlags perm_flags_new, perm_flags_old;
1670 MsgTmpFlags tmp_flags_old;
1672 g_return_if_fail(msginfo != NULL);
1673 item = msginfo->folder;
1674 g_return_if_fail(item != NULL);
1676 debug_print("Setting flags for message %d in folder %s\n", msginfo->msgnum, item->path);
1678 /* Perm Flags handling */
1679 perm_flags_old = msginfo->flags.perm_flags;
1680 perm_flags_new = msginfo->flags.perm_flags | perm_flags;
1681 if ((perm_flags & MSG_IGNORE_THREAD) || (perm_flags_old & MSG_IGNORE_THREAD)) {
1682 perm_flags_new &= ~(MSG_NEW | MSG_UNREAD);
1685 if (perm_flags_old != perm_flags_new) {
1686 folder_item_change_msg_flags(msginfo->folder, msginfo, perm_flags_new);
1688 update_folder_msg_counts(item, msginfo, perm_flags_old);
1692 /* Tmp flags handling */
1693 tmp_flags_old = msginfo->flags.tmp_flags;
1694 msginfo->flags.tmp_flags |= tmp_flags;
1696 /* update notification */
1697 if ((perm_flags_old != perm_flags_new) || (tmp_flags_old != msginfo->flags.tmp_flags)) {
1698 msginfo_update.msginfo = msginfo;
1699 msginfo_update.flags = MSGINFO_UPDATE_FLAGS;
1700 hooks_invoke(MSGINFO_UPDATE_HOOKLIST, &msginfo_update);
1701 folder_item_update(msginfo->folder, F_ITEM_UPDATE_MSGCNT);
1705 void procmsg_msginfo_unset_flags(MsgInfo *msginfo, MsgPermFlags perm_flags, MsgTmpFlags tmp_flags)
1708 MsgInfoUpdate msginfo_update;
1709 MsgPermFlags perm_flags_new, perm_flags_old;
1710 MsgTmpFlags tmp_flags_old;
1712 g_return_if_fail(msginfo != NULL);
1713 item = msginfo->folder;
1714 g_return_if_fail(item != NULL);
1716 debug_print("Unsetting flags for message %d in folder %s\n", msginfo->msgnum, item->path);
1718 /* Perm Flags handling */
1719 perm_flags_old = msginfo->flags.perm_flags;
1720 perm_flags_new = msginfo->flags.perm_flags & ~perm_flags;
1722 if (perm_flags_old != perm_flags_new) {
1723 folder_item_change_msg_flags(msginfo->folder, msginfo, perm_flags_new);
1725 update_folder_msg_counts(item, msginfo, perm_flags_old);
1727 msginfo_update.msginfo = msginfo;
1728 msginfo_update.flags = MSGINFO_UPDATE_FLAGS;
1729 hooks_invoke(MSGINFO_UPDATE_HOOKLIST, &msginfo_update);
1730 folder_item_update(msginfo->folder, F_ITEM_UPDATE_MSGCNT);
1733 /* Tmp flags hanlding */
1734 tmp_flags_old = msginfo->flags.tmp_flags;
1735 msginfo->flags.tmp_flags &= ~tmp_flags;
1737 /* update notification */
1738 if ((perm_flags_old != perm_flags_new) || (tmp_flags_old != msginfo->flags.tmp_flags)) {
1739 msginfo_update.msginfo = msginfo;
1740 msginfo_update.flags = MSGINFO_UPDATE_FLAGS;
1741 hooks_invoke(MSGINFO_UPDATE_HOOKLIST, &msginfo_update);
1742 folder_item_update(msginfo->folder, F_ITEM_UPDATE_MSGCNT);
1746 void procmsg_msginfo_change_flags(MsgInfo *msginfo,
1747 MsgPermFlags add_perm_flags, MsgTmpFlags add_tmp_flags,
1748 MsgPermFlags rem_perm_flags, MsgTmpFlags rem_tmp_flags)
1751 MsgInfoUpdate msginfo_update;
1752 MsgPermFlags perm_flags_new, perm_flags_old;
1753 MsgTmpFlags tmp_flags_old;
1755 g_return_if_fail(msginfo != NULL);
1756 item = msginfo->folder;
1757 g_return_if_fail(item != NULL);
1759 debug_print("Changing flags for message %d in folder %s\n", msginfo->msgnum, item->path);
1761 /* Perm Flags handling */
1762 perm_flags_old = msginfo->flags.perm_flags;
1763 perm_flags_new = (msginfo->flags.perm_flags & ~rem_perm_flags) | add_perm_flags;
1764 if ((add_perm_flags & MSG_IGNORE_THREAD) || (perm_flags_old & MSG_IGNORE_THREAD)) {
1765 perm_flags_new &= ~(MSG_NEW | MSG_UNREAD);
1768 if (perm_flags_old != perm_flags_new) {
1769 folder_item_change_msg_flags(msginfo->folder, msginfo, perm_flags_new);
1771 update_folder_msg_counts(item, msginfo, perm_flags_old);
1775 /* Tmp flags handling */
1776 tmp_flags_old = msginfo->flags.tmp_flags;
1777 msginfo->flags.tmp_flags &= ~rem_tmp_flags;
1778 msginfo->flags.tmp_flags |= add_tmp_flags;
1780 /* update notification */
1781 if ((perm_flags_old != perm_flags_new) || (tmp_flags_old != msginfo->flags.tmp_flags)) {
1782 msginfo_update.msginfo = msginfo;
1783 msginfo_update.flags = MSGINFO_UPDATE_FLAGS;
1784 hooks_invoke(MSGINFO_UPDATE_HOOKLIST, &msginfo_update);
1785 folder_item_update(msginfo->folder, F_ITEM_UPDATE_MSGCNT);
1790 *\brief check for flags (e.g. mark) in prior msgs of current thread
1792 *\param info Current message
1793 *\param perm_flags Flags to be checked
1794 *\param parentmsgs Hash of prior msgs to avoid loops
1796 *\return gboolean TRUE if perm_flags are found
1798 gboolean procmsg_msg_has_flagged_parent_real(MsgInfo *info,
1799 MsgPermFlags perm_flags, GHashTable *parentmsgs)
1803 g_return_val_if_fail(info != NULL, FALSE);
1805 if (info != NULL && info->folder != NULL && info->inreplyto != NULL) {
1806 tmp = folder_item_get_msginfo_by_msgid(info->folder,
1808 if (tmp && (tmp->flags.perm_flags & perm_flags)) {
1809 procmsg_msginfo_free(tmp);
1811 } else if (tmp != NULL) {
1814 if (g_hash_table_lookup(parentmsgs, info)) {
1815 debug_print("loop detected: %s%c%d\n",
1816 folder_item_get_path(info->folder),
1817 G_DIR_SEPARATOR, info->msgnum);
1820 g_hash_table_insert(parentmsgs, info, "1");
1821 result = procmsg_msg_has_flagged_parent_real(
1822 tmp, perm_flags, parentmsgs);
1824 procmsg_msginfo_free(tmp);
1834 *\brief Callback for cleaning up hash of parentmsgs
1836 gboolean parentmsgs_hash_remove(gpointer key,
1844 *\brief Set up list of parentmsgs
1845 * See procmsg_msg_has_flagged_parent_real()
1847 gboolean procmsg_msg_has_flagged_parent(MsgInfo *info, MsgPermFlags perm_flags)
1850 GHashTable *parentmsgs = g_hash_table_new(NULL, NULL);
1852 result = procmsg_msg_has_flagged_parent_real(info, perm_flags, parentmsgs);
1853 g_hash_table_foreach_remove(parentmsgs, parentmsgs_hash_remove, NULL);
1854 g_hash_table_destroy(parentmsgs);
1859 *\brief Check if msgs prior in thread are marked
1860 * See procmsg_msg_has_flagged_parent_real()
1862 gboolean procmsg_msg_has_marked_parent(MsgInfo *info)
1864 return procmsg_msg_has_flagged_parent(info, MSG_MARKED);
1868 GSList *procmsg_find_children_func(MsgInfo *info,
1869 GSList *children, GSList *all)
1873 g_return_val_if_fail(info!=NULL, children);
1874 if (info->msgid == NULL)
1877 for (cur = all; cur != NULL; cur = g_slist_next(cur)) {
1878 MsgInfo *tmp = (MsgInfo *)cur->data;
1879 if (tmp->inreplyto && !strcmp(tmp->inreplyto, info->msgid)) {
1880 /* Check if message is already in the list */
1881 if ((children == NULL) ||
1882 (g_slist_index(children, tmp) == -1)) {
1883 children = g_slist_prepend(children,
1884 procmsg_msginfo_new_ref(tmp));
1885 children = procmsg_find_children_func(tmp,
1894 GSList *procmsg_find_children (MsgInfo *info)
1899 g_return_val_if_fail(info!=NULL, NULL);
1900 all = folder_item_get_msg_list(info->folder);
1901 children = procmsg_find_children_func(info, NULL, all);
1902 if (children != NULL) {
1903 for (cur = all; cur != NULL; cur = g_slist_next(cur)) {
1904 /* this will not free the used pointers
1905 created with procmsg_msginfo_new_ref */
1906 procmsg_msginfo_free((MsgInfo *)cur->data);
1914 void procmsg_update_unread_children(MsgInfo *info, gboolean newly_marked)
1916 GSList *children = procmsg_find_children(info);
1918 for (cur = children; cur != NULL; cur = g_slist_next(cur)) {
1919 MsgInfo *tmp = (MsgInfo *)cur->data;
1920 if(MSG_IS_UNREAD(tmp->flags) && !MSG_IS_IGNORE_THREAD(tmp->flags)) {
1922 info->folder->unreadmarked_msgs++;
1924 info->folder->unreadmarked_msgs--;
1925 folder_item_update(info->folder, F_ITEM_UPDATE_MSGCNT);
1927 procmsg_msginfo_free(tmp);
1929 g_slist_free(children);
1933 * Set the destination folder for a copy or move operation
1935 * \param msginfo The message which's destination folder is changed
1936 * \param to_folder The destination folder for the operation
1938 void procmsg_msginfo_set_to_folder(MsgInfo *msginfo, FolderItem *to_folder)
1940 if(msginfo->to_folder != NULL) {
1941 msginfo->to_folder->op_count--;
1942 folder_item_update(msginfo->to_folder, F_ITEM_UPDATE_MSGCNT);
1944 msginfo->to_folder = to_folder;
1945 if(to_folder != NULL) {
1946 to_folder->op_count++;
1947 folder_item_update(msginfo->to_folder, F_ITEM_UPDATE_MSGCNT);
1952 * Apply filtering actions to the msginfo
1954 * \param msginfo The MsgInfo describing the message that should be filtered
1955 * \return TRUE if the message was moved and MsgInfo is now invalid,
1958 gboolean procmsg_msginfo_filter(MsgInfo *msginfo)
1960 MailFilteringData mail_filtering_data;
1962 mail_filtering_data.msginfo = msginfo;
1963 if (hooks_invoke(MAIL_FILTERING_HOOKLIST, &mail_filtering_data))
1966 /* filter if enabled in prefs or move to inbox if not */
1967 if((filtering_rules != NULL) &&
1968 filter_message_by_msginfo(filtering_rules, msginfo))
1971 hooks_invoke(MAIL_POSTFILTERING_HOOKLIST, msginfo);
1976 MsgInfo *procmsg_msginfo_new_from_mimeinfo(MsgInfo *src_msginfo, MimeInfo *mimeinfo)
1978 MsgInfo *tmp_msginfo = NULL;
1979 MsgFlags flags = {0, 0};
1980 gchar *tmpfile = get_tmp_file();
1981 FILE *fp = g_fopen(tmpfile, "wb");
1983 if (!mimeinfo || mimeinfo->type != MIMETYPE_MESSAGE ||
1984 g_ascii_strcasecmp(mimeinfo->subtype, "rfc822")) {
1985 g_warning("procmsg_msginfo_new_from_mimeinfo(): unsuitable mimeinfo");
1992 if (fp && procmime_write_mimeinfo(mimeinfo, fp) >= 0) {
1995 tmp_msginfo = procheader_parse_file(
2002 if (tmp_msginfo != NULL) {
2003 tmp_msginfo->folder = src_msginfo->folder;
2004 tmp_msginfo->plaintext_file = g_strdup(tmpfile);
2006 g_warning("procmsg_msginfo_new_from_mimeinfo(): Can't generate new msginfo");