2 * Sylpheed -- a GTK+ based, lightweight, and fast e-mail client
3 * Copyright (C) 1999-2006 Hiroyuki Yamamoto and the Sylpheed-Claws team
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation; either version 2 of the License, or
8 * (at your option) any later version.
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
15 * You should have received a copy of the GNU General Public License
16 * along with this program; if not, write to the Free Software
17 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
23 #include <glib/gi18n.h>
31 #include "procheader.h"
32 #include "send_message.h"
34 #include "statusbar.h"
35 #include "prefs_filtering.h"
36 #include "filtering.h"
38 #include "prefs_common.h"
40 #include "alertpanel.h"
44 #include "partial_download.h"
45 #include "mainwindow.h"
46 #include "summaryview.h"
48 static gint procmsg_send_message_queue_full(const gchar *file, gboolean keep_session);
56 Q_MAIL_ACCOUNT_ID = 4,
57 Q_NEWS_ACCOUNT_ID = 5,
58 Q_SAVE_COPY_FOLDER = 6,
59 Q_REPLY_MESSAGE_ID = 7,
66 GHashTable *procmsg_msg_hash_table_create(GSList *mlist)
68 GHashTable *msg_table;
70 if (mlist == NULL) return NULL;
72 msg_table = g_hash_table_new(NULL, g_direct_equal);
73 procmsg_msg_hash_table_append(msg_table, mlist);
78 void procmsg_msg_hash_table_append(GHashTable *msg_table, GSList *mlist)
83 if (msg_table == NULL || mlist == NULL) return;
85 for (cur = mlist; cur != NULL; cur = cur->next) {
86 msginfo = (MsgInfo *)cur->data;
88 g_hash_table_insert(msg_table,
89 GUINT_TO_POINTER(msginfo->msgnum),
94 GHashTable *procmsg_to_folder_hash_table_create(GSList *mlist)
96 GHashTable *msg_table;
100 if (mlist == NULL) return NULL;
102 msg_table = g_hash_table_new(NULL, g_direct_equal);
104 for (cur = mlist; cur != NULL; cur = cur->next) {
105 msginfo = (MsgInfo *)cur->data;
106 g_hash_table_insert(msg_table, msginfo->to_folder, msginfo);
112 gint procmsg_get_last_num_in_msg_list(GSList *mlist)
118 for (cur = mlist; cur != NULL; cur = cur->next) {
119 msginfo = (MsgInfo *)cur->data;
120 if (msginfo && msginfo->msgnum > last)
121 last = msginfo->msgnum;
127 void procmsg_msg_list_free(GSList *mlist)
132 for (cur = mlist; cur != NULL; cur = cur->next) {
133 msginfo = (MsgInfo *)cur->data;
134 procmsg_msginfo_free(msginfo);
148 /* CLAWS subject threading:
150 in the first round it inserts subject lines in a
151 relation (subject <-> node)
153 the second round finishes the threads by attaching
154 matching subject lines to the one found in the
155 relation. will use the oldest node with the same
156 subject that is not more then thread_by_subject_max_age
157 days old (see subject_relation_lookup)
160 static void subject_relation_insert(GRelation *relation, GNode *node)
165 g_return_if_fail(relation != NULL);
166 g_return_if_fail(node != NULL);
167 msginfo = (MsgInfo *) node->data;
168 g_return_if_fail(msginfo != NULL);
170 subject = msginfo->subject;
173 subject += subject_get_prefix_length(subject);
175 g_relation_insert(relation, subject, node);
178 static GNode *subject_relation_lookup(GRelation *relation, MsgInfo *msginfo)
185 g_return_val_if_fail(relation != NULL, NULL);
187 subject = msginfo->subject;
190 prefix_length = subject_get_prefix_length(subject);
191 if (prefix_length <= 0)
193 subject += prefix_length;
195 tuples = g_relation_select(relation, subject, 0);
199 if (tuples->len > 0) {
201 GNode *relation_node;
202 MsgInfo *relation_msginfo = NULL, *best_msginfo = NULL;
205 /* check all nodes with the same subject to find the best parent */
206 for (i = 0; i < tuples->len; i++) {
207 relation_node = (GNode *) g_tuples_index(tuples, i, 1);
208 relation_msginfo = (MsgInfo *) relation_node->data;
211 /* best node should be the oldest in the found nodes */
212 /* parent node must not be older then msginfo */
213 if ((relation_msginfo->date_t < msginfo->date_t) &&
214 ((best_msginfo == NULL) ||
215 (best_msginfo->date_t > relation_msginfo->date_t)))
218 /* parent node must not be more then thread_by_subject_max_age
219 days older then msginfo */
220 if (abs(difftime(msginfo->date_t, relation_msginfo->date_t)) >
221 prefs_common.thread_by_subject_max_age * 3600 * 24)
224 /* can add new tests for all matching
225 nodes found by subject */
228 node = relation_node;
229 best_msginfo = relation_msginfo;
234 g_tuples_destroy(tuples);
238 /* return the reversed thread tree */
239 GNode *procmsg_get_thread_tree(GSList *mlist)
241 GNode *root, *parent, *node, *next;
242 GHashTable *msgid_table;
243 GRelation *subject_relation;
248 root = g_node_new(NULL);
249 msgid_table = g_hash_table_new(g_str_hash, g_str_equal);
250 subject_relation = g_relation_new(2);
251 g_relation_index(subject_relation, 0, g_str_hash, g_str_equal);
253 for (; mlist != NULL; mlist = mlist->next) {
254 msginfo = (MsgInfo *)mlist->data;
257 if (msginfo->inreplyto) {
258 parent = g_hash_table_lookup(msgid_table, msginfo->inreplyto);
259 if (parent == NULL) {
263 node = g_node_insert_data_before
264 (parent, parent == root ? parent->children : NULL,
266 if ((msgid = msginfo->msgid) && g_hash_table_lookup(msgid_table, msgid) == NULL)
267 g_hash_table_insert(msgid_table, (gchar *)msgid, node);
269 /* CLAWS: add subject to relation (without prefix) */
270 if (prefs_common.thread_by_subject) {
271 subject_relation_insert(subject_relation, node);
275 /* complete the unfinished threads */
276 for (node = root->children; node != NULL; ) {
278 msginfo = (MsgInfo *)node->data;
281 if (msginfo->inreplyto)
282 parent = g_hash_table_lookup(msgid_table, msginfo->inreplyto);
284 /* try looking for the indirect parent */
285 if (!parent && msginfo->references) {
286 for (reflist = msginfo->references;
287 reflist != NULL; reflist = reflist->next)
288 if ((parent = g_hash_table_lookup
289 (msgid_table, reflist->data)) != NULL)
293 /* node should not be the parent, and node should not
294 be an ancestor of parent (circular reference) */
295 if (parent && parent != node &&
296 !g_node_is_ancestor(node, parent)) {
299 (parent, parent->children, node);
305 if (prefs_common.thread_by_subject) {
306 for (node = root->children; node && node != NULL;) {
308 msginfo = (MsgInfo *) node->data;
310 parent = subject_relation_lookup(subject_relation, msginfo);
312 /* the node may already be threaded by IN-REPLY-TO, so go up
314 find the parent node */
315 if (parent != NULL) {
316 if (g_node_is_ancestor(node, parent))
324 g_node_append(parent, node);
331 g_relation_destroy(subject_relation);
332 g_hash_table_destroy(msgid_table);
337 gint procmsg_move_messages(GSList *mlist)
339 GSList *cur, *movelist = NULL;
341 FolderItem *dest = NULL;
343 gboolean finished = TRUE;
344 if (!mlist) return 0;
346 folder_item_update_freeze();
349 for (cur = mlist; cur != NULL; cur = cur->next) {
350 msginfo = (MsgInfo *)cur->data;
351 if (!msginfo->to_folder) {
357 dest = msginfo->to_folder;
358 movelist = g_slist_append(movelist, msginfo);
359 } else if (dest == msginfo->to_folder) {
360 movelist = g_slist_append(movelist, msginfo);
364 procmsg_msginfo_set_to_folder(msginfo, NULL);
367 retval |= folder_item_move_msgs(dest, movelist);
368 g_slist_free(movelist);
371 if (finished == FALSE) {
377 folder_item_update_thaw();
381 void procmsg_copy_messages(GSList *mlist)
383 GSList *cur, *copylist = NULL;
385 FolderItem *dest = NULL;
386 gboolean finished = TRUE;
389 folder_item_update_freeze();
392 for (cur = mlist; cur != NULL; cur = cur->next) {
393 msginfo = (MsgInfo *)cur->data;
394 if (!msginfo->to_folder) {
400 dest = msginfo->to_folder;
401 copylist = g_slist_append(copylist, msginfo);
402 } else if (dest == msginfo->to_folder) {
403 copylist = g_slist_append(copylist, msginfo);
407 procmsg_msginfo_set_to_folder(msginfo, NULL);
410 folder_item_copy_msgs(dest, copylist);
411 g_slist_free(copylist);
414 if (finished == FALSE) {
420 folder_item_update_thaw();
423 gchar *procmsg_get_message_file_path(MsgInfo *msginfo)
427 g_return_val_if_fail(msginfo != NULL, NULL);
429 if (msginfo->plaintext_file)
430 file = g_strdup(msginfo->plaintext_file);
432 file = folder_item_fetch_msg(msginfo->folder, msginfo->msgnum);
438 gchar *procmsg_get_message_file(MsgInfo *msginfo)
440 gchar *filename = NULL;
442 g_return_val_if_fail(msginfo != NULL, NULL);
444 filename = folder_item_fetch_msg(msginfo->folder, msginfo->msgnum);
446 debug_print("can't fetch message %d\n", msginfo->msgnum);
451 gchar *procmsg_get_message_file_full(MsgInfo *msginfo, gboolean headers, gboolean body)
453 gchar *filename = NULL;
455 g_return_val_if_fail(msginfo != NULL, NULL);
457 filename = folder_item_fetch_msg_full(msginfo->folder, msginfo->msgnum,
460 debug_print("can't fetch message %d\n", msginfo->msgnum);
465 GSList *procmsg_get_message_file_list(GSList *mlist)
467 GSList *file_list = NULL;
469 MsgFileInfo *fileinfo;
472 while (mlist != NULL) {
473 msginfo = (MsgInfo *)mlist->data;
474 file = procmsg_get_message_file(msginfo);
476 procmsg_message_file_list_free(file_list);
479 fileinfo = g_new(MsgFileInfo, 1);
480 fileinfo->msginfo = procmsg_msginfo_new_ref(msginfo);
481 fileinfo->file = file;
482 fileinfo->flags = g_new(MsgFlags, 1);
483 *fileinfo->flags = msginfo->flags;
484 file_list = g_slist_prepend(file_list, fileinfo);
488 file_list = g_slist_reverse(file_list);
493 void procmsg_message_file_list_free(MsgInfoList *file_list)
496 MsgFileInfo *fileinfo;
498 for (cur = file_list; cur != NULL; cur = cur->next) {
499 fileinfo = (MsgFileInfo *)cur->data;
500 procmsg_msginfo_free(fileinfo->msginfo);
501 g_free(fileinfo->file);
502 g_free(fileinfo->flags);
506 g_slist_free(file_list);
509 FILE *procmsg_open_message(MsgInfo *msginfo)
514 g_return_val_if_fail(msginfo != NULL, NULL);
516 file = procmsg_get_message_file_path(msginfo);
517 g_return_val_if_fail(file != NULL, NULL);
519 if (!is_file_exist(file)) {
521 file = procmsg_get_message_file(msginfo);
526 if ((fp = g_fopen(file, "rb")) == NULL) {
527 FILE_OP_ERROR(file, "fopen");
534 if (MSG_IS_QUEUED(msginfo->flags) || MSG_IS_DRAFT(msginfo->flags)) {
537 while (fgets(buf, sizeof(buf), fp) != NULL)
538 if (buf[0] == '\r' || buf[0] == '\n') break;
544 gboolean procmsg_msg_exist(MsgInfo *msginfo)
549 if (!msginfo) return FALSE;
551 path = folder_item_get_path(msginfo->folder);
553 ret = !folder_item_is_msg_changed(msginfo->folder, msginfo);
559 void procmsg_get_filter_keyword(MsgInfo *msginfo, gchar **header, gchar **key,
560 PrefsFilterType type)
562 static HeaderEntry hentry[] = {{"X-BeenThere:", NULL, TRUE},
563 {"X-ML-Name:", NULL, TRUE},
564 {"X-List:", NULL, TRUE},
565 {"X-Mailing-list:", NULL, TRUE},
566 {"List-Id:", NULL, TRUE},
567 {"X-Sequence:", NULL, TRUE},
568 {"Sender:", NULL, TRUE},
569 {"List-Post:", NULL, TRUE},
570 {NULL, NULL, FALSE}};
576 H_X_MAILING_LIST = 3,
585 g_return_if_fail(msginfo != NULL);
586 g_return_if_fail(header != NULL);
587 g_return_if_fail(key != NULL);
596 if ((fp = procmsg_open_message(msginfo)) == NULL)
598 procheader_get_header_fields(fp, hentry);
601 #define SET_FILTER_KEY(hstr, idx) \
603 *header = g_strdup(hstr); \
604 *key = hentry[idx].body; \
605 hentry[idx].body = NULL; \
608 if (hentry[H_X_BEENTHERE].body != NULL) {
609 SET_FILTER_KEY("header \"X-BeenThere\"", H_X_BEENTHERE);
610 } else if (hentry[H_X_ML_NAME].body != NULL) {
611 SET_FILTER_KEY("header \"X-ML-Name\"", H_X_ML_NAME);
612 } else if (hentry[H_X_LIST].body != NULL) {
613 SET_FILTER_KEY("header \"X-List\"", H_X_LIST);
614 } else if (hentry[H_X_MAILING_LIST].body != NULL) {
615 SET_FILTER_KEY("header \"X-Mailing-List\"", H_X_MAILING_LIST);
616 } else if (hentry[H_LIST_ID].body != NULL) {
617 SET_FILTER_KEY("header \"List-Id\"", H_LIST_ID);
618 extract_list_id_str(*key);
619 } else if (hentry[H_X_SEQUENCE].body != NULL) {
622 SET_FILTER_KEY("X-Sequence", H_X_SEQUENCE);
625 while (*p != '\0' && !g_ascii_isspace(*p)) p++;
626 while (g_ascii_isspace(*p)) p++;
627 if (g_ascii_isdigit(*p)) {
633 } else if (hentry[H_SENDER].body != NULL) {
634 SET_FILTER_KEY("header \"Sender\"", H_SENDER);
635 } else if (hentry[H_LIST_POST].body != NULL) {
636 SET_FILTER_KEY("header \"Sender\"", H_LIST_POST);
637 } else if (msginfo->to) {
638 *header = g_strdup("to");
639 *key = g_strdup(msginfo->to);
640 } else if (msginfo->subject) {
641 *header = g_strdup("subject");
642 *key = g_strdup(msginfo->subject);
645 #undef SET_FILTER_KEY
647 g_free(hentry[H_X_BEENTHERE].body);
648 hentry[H_X_BEENTHERE].body = NULL;
649 g_free(hentry[H_X_ML_NAME].body);
650 hentry[H_X_ML_NAME].body = NULL;
651 g_free(hentry[H_X_LIST].body);
652 hentry[H_X_LIST].body = NULL;
653 g_free(hentry[H_X_MAILING_LIST].body);
654 hentry[H_X_MAILING_LIST].body = NULL;
655 g_free(hentry[H_LIST_ID].body);
656 hentry[H_LIST_ID].body = NULL;
657 g_free(hentry[H_SENDER].body);
658 hentry[H_SENDER].body = NULL;
659 g_free(hentry[H_LIST_POST].body);
660 hentry[H_LIST_POST].body = NULL;
664 *header = g_strdup("from");
665 *key = g_strdup(msginfo->from);
668 *header = g_strdup("to");
669 *key = g_strdup(msginfo->to);
671 case FILTER_BY_SUBJECT:
672 *header = g_strdup("subject");
673 *key = g_strdup(msginfo->subject);
680 void procmsg_empty_trash(FolderItem *trash)
685 (trash->stype != F_TRASH &&
686 !folder_has_parent_of_type(trash, F_TRASH)))
689 if (trash && trash->total_msgs > 0) {
690 GSList *mlist = folder_item_get_msg_list(trash);
692 for (cur = mlist ; cur != NULL ; cur = cur->next) {
693 MsgInfo * msginfo = (MsgInfo *) cur->data;
694 if (MSG_IS_LOCKED(msginfo->flags))
696 if (msginfo->total_size != 0 &&
697 msginfo->size != (off_t)msginfo->total_size)
698 partial_mark_for_delete(msginfo);
700 procmsg_msginfo_free(msginfo);
703 folder_item_remove_all_msg(trash);
706 if (!trash->node || !trash->node->children)
709 node = trash->node->children;
710 while (node != NULL) {
712 procmsg_empty_trash(FOLDER_ITEM(node->data));
717 void procmsg_empty_all_trash(void)
722 for (cur = folder_get_list(); cur != NULL; cur = cur->next) {
723 Folder *folder = FOLDER(cur->data);
724 trash = folder->trash;
725 procmsg_empty_trash(trash);
726 if (folder->account && folder->account->set_trash_folder &&
727 folder_find_item_from_identifier(folder->account->trash_folder))
729 folder_find_item_from_identifier(folder->account->trash_folder));
733 static PrefsAccount *procmsg_get_account_from_file(const gchar *file)
735 PrefsAccount *mailac = NULL;
739 static HeaderEntry qentry[] = {{"S:", NULL, FALSE},
740 {"SSV:", NULL, FALSE},
742 {"NG:", NULL, FALSE},
743 {"MAID:", NULL, FALSE},
744 {"NAID:", NULL, FALSE},
745 {"SCF:", NULL, FALSE},
746 {"RMID:", NULL, FALSE},
747 {"FMID:", NULL, FALSE},
748 {"X-Sylpheed-Privacy-System:", NULL, FALSE},
749 {"X-Sylpheed-Encrypt:", NULL, FALSE},
750 {"X-Sylpheed-Encrypt-Data:", NULL, FALSE},
751 {NULL, NULL, FALSE}};
753 g_return_val_if_fail(file != NULL, NULL);
755 if ((fp = g_fopen(file, "rb")) == NULL) {
756 FILE_OP_ERROR(file, "fopen");
760 while ((hnum = procheader_get_one_field(buf, sizeof(buf), fp, qentry))
762 gchar *p = buf + strlen(qentry[hnum].name);
764 if (hnum == Q_MAIL_ACCOUNT_ID) {
765 mailac = account_find_from_id(atoi(p));
773 static GSList *procmsg_list_sort_by_account(FolderItem *queue, GSList *list)
775 GSList *result = NULL;
777 PrefsAccount *last_account = NULL;
780 gboolean nothing_to_sort = TRUE;
785 orig = g_slist_copy(list);
787 msg = (MsgInfo *)orig->data;
789 for (cur = orig; cur; cur = cur->next)
790 debug_print("sort before %s\n", ((MsgInfo *)cur->data)->from);
795 nothing_to_sort = TRUE;
799 PrefsAccount *ac = NULL;
800 msg = (MsgInfo *)cur->data;
801 file = folder_item_fetch_msg(queue, msg->msgnum);
802 ac = procmsg_get_account_from_file(file);
805 if (last_account == NULL || (ac != NULL && ac == last_account)) {
806 result = g_slist_append(result, msg);
807 orig = g_slist_remove(orig, msg);
809 nothing_to_sort = FALSE;
815 if (orig || g_slist_length(orig)) {
816 if (!last_account && nothing_to_sort) {
817 /* can't find an account for the rest of the list */
820 result = g_slist_append(result, cur->data);
831 for (cur = result; cur; cur = cur->next)
832 debug_print("sort after %s\n", ((MsgInfo *)cur->data)->from);
839 static gboolean procmsg_is_last_for_account(FolderItem *queue, MsgInfo *msginfo, GSList *elem)
841 gchar *file = folder_item_fetch_msg(queue, msginfo->msgnum);
842 PrefsAccount *ac = procmsg_get_account_from_file(file);
845 for (cur = elem; cur; cur = cur->next) {
846 MsgInfo *cur_msginfo = (MsgInfo *)cur->data;
847 file = folder_item_fetch_msg(queue, cur_msginfo->msgnum);
849 if (cur_msginfo != msginfo && !MSG_IS_LOCKED(cur_msginfo->flags)) {
850 if (procmsg_get_account_from_file(file) == ac) {
862 *\brief Send messages in queue
864 *\param queue Queue folder to process
865 *\param save_msgs Unused
867 *\return Number of messages sent, negative if an error occurred
868 * positive if no error occurred
870 gint procmsg_send_queue(FolderItem *queue, gboolean save_msgs)
872 gint sent = 0, err = 0;
874 GSList *sorted_list = NULL;
878 queue = folder_get_default_queue();
879 g_return_val_if_fail(queue != NULL, -1);
881 folder_item_scan(queue);
882 list = folder_item_get_msg_list(queue);
884 /* sort the list per sender account; this helps reusing the same SMTP server */
885 sorted_list = procmsg_list_sort_by_account(queue, list);
887 for (elem = sorted_list; elem != NULL; elem = elem->next) {
891 msginfo = (MsgInfo *)(elem->data);
892 if (!MSG_IS_LOCKED(msginfo->flags)) {
893 file = folder_item_fetch_msg(queue, msginfo->msgnum);
895 if (procmsg_send_message_queue_full(file,
896 !procmsg_is_last_for_account(queue, msginfo, elem)) < 0) {
897 g_warning("Sending queued message %d failed.\n",
902 * We save in procmsg_send_message_queue because
903 * we need the destination folder from the queue
907 procmsg_save_to_outbox
908 (queue->folder->outbox,
912 folder_item_remove_msg(queue, msginfo->msgnum);
917 /* FIXME: supposedly if only one message is locked, and queue
918 * is being flushed, the following free says something like
919 * "freeing msg ## in folder (nil)". */
920 procmsg_msginfo_free(msginfo);
923 g_slist_free(sorted_list);
924 folder_item_scan(queue);
926 if (queue->node && queue->node->children) {
927 node = queue->node->children;
928 while (node != NULL) {
931 res = procmsg_send_queue(FOLDER_ITEM(node->data), save_msgs);
940 return (err != 0 ? -err : sent);
944 *\brief Determine if a queue folder is empty
946 *\param queue Queue folder to process
948 *\return TRUE if the queue folder is empty, otherwise return FALSE
950 gboolean procmsg_queue_is_empty(FolderItem *queue)
953 gboolean res = FALSE;
955 queue = folder_get_default_queue();
956 g_return_val_if_fail(queue != NULL, TRUE);
958 folder_item_scan(queue);
959 list = folder_item_get_msg_list(queue);
960 res = (list == NULL);
961 procmsg_msg_list_free(list);
965 if (queue->node && queue->node->children) {
966 node = queue->node->children;
967 while (node != NULL) {
969 if (!procmsg_queue_is_empty(FOLDER_ITEM(node->data)))
978 gint procmsg_remove_special_headers(const gchar *in, const gchar *out)
983 if ((fp = g_fopen(in, "rb")) == NULL) {
984 FILE_OP_ERROR(in, "fopen");
987 if ((outfp = g_fopen(out, "wb")) == NULL) {
988 FILE_OP_ERROR(out, "fopen");
992 while (fgets(buf, sizeof(buf), fp) != NULL)
993 if (buf[0] == '\r' || buf[0] == '\n') break;
994 while (fgets(buf, sizeof(buf), fp) != NULL)
1001 gint procmsg_save_to_outbox(FolderItem *outbox, const gchar *file,
1005 MsgInfo *msginfo, *tmp_msginfo;
1006 MsgFlags flag = {0, 0};
1008 debug_print("saving sent message...\n");
1011 outbox = folder_get_default_outbox();
1012 g_return_val_if_fail(outbox != NULL, -1);
1014 /* remove queueing headers */
1016 gchar tmp[MAXPATHLEN + 1];
1018 g_snprintf(tmp, sizeof(tmp), "%s%ctmpmsg.out.%08x",
1019 get_rc_dir(), G_DIR_SEPARATOR, (guint) rand());
1021 if (procmsg_remove_special_headers(file, tmp) !=0)
1024 folder_item_scan(outbox);
1025 if ((num = folder_item_add_msg(outbox, tmp, &flag, TRUE)) < 0) {
1026 g_warning("can't save message\n");
1031 folder_item_scan(outbox);
1032 if ((num = folder_item_add_msg
1033 (outbox, file, &flag, FALSE)) < 0) {
1034 g_warning("can't save message\n");
1039 msginfo = folder_item_get_msginfo(outbox, num); /* refcnt++ */
1040 tmp_msginfo = procmsg_msginfo_get_full_info(msginfo); /* refcnt++ */
1041 if (msginfo != NULL) {
1042 procmsg_msginfo_unset_flags(msginfo, ~0, 0);
1043 procmsg_msginfo_free(msginfo); /* refcnt-- */
1044 /* tmp_msginfo == msginfo */
1045 if (tmp_msginfo && (msginfo->dispositionnotificationto ||
1046 msginfo->returnreceiptto)) {
1047 procmsg_msginfo_set_flags(msginfo, MSG_RETRCPT_SENT, 0);
1048 procmsg_msginfo_free(msginfo); /* refcnt-- */
1055 void procmsg_print_message(MsgInfo *msginfo, const gchar *cmdline)
1057 static const gchar *def_cmd = "lpr %s";
1058 static guint id = 0;
1064 g_return_if_fail(msginfo);
1066 if (procmime_msginfo_is_encrypted(msginfo))
1067 tmpfp = procmime_get_first_encrypted_text_content(msginfo);
1069 tmpfp = procmime_get_first_text_content(msginfo);
1070 if (tmpfp == NULL) {
1071 g_warning("Can't get text part\n");
1075 prtmp = g_strdup_printf("%s%cprinttmp.%08x",
1076 get_mime_tmp_dir(), G_DIR_SEPARATOR, id++);
1078 if ((prfp = g_fopen(prtmp, "wb")) == NULL) {
1079 FILE_OP_ERROR(prtmp, "fopen");
1085 if (msginfo->date) fprintf(prfp, "Date: %s\n", msginfo->date);
1086 if (msginfo->from) fprintf(prfp, "From: %s\n", msginfo->from);
1087 if (msginfo->to) fprintf(prfp, "To: %s\n", msginfo->to);
1088 if (msginfo->cc) fprintf(prfp, "Cc: %s\n", msginfo->cc);
1089 if (msginfo->newsgroups)
1090 fprintf(prfp, "Newsgroups: %s\n", msginfo->newsgroups);
1091 if (msginfo->subject) fprintf(prfp, "Subject: %s\n", msginfo->subject);
1094 while (fgets(buf, sizeof(buf), tmpfp) != NULL)
1100 if (cmdline && (p = strchr(cmdline, '%')) && *(p + 1) == 's' &&
1101 !strchr(p + 2, '%'))
1102 g_snprintf(buf, sizeof(buf) - 1, cmdline, prtmp);
1105 g_warning("Print command line is invalid: '%s'\n",
1107 g_snprintf(buf, sizeof(buf) - 1, def_cmd, prtmp);
1113 if (buf[strlen(buf) - 1] != '&') strcat(buf, "&");
1117 MsgInfo *procmsg_msginfo_new_ref(MsgInfo *msginfo)
1124 MsgInfo *procmsg_msginfo_new(void)
1126 MsgInfo *newmsginfo;
1128 newmsginfo = g_new0(MsgInfo, 1);
1129 newmsginfo->refcnt = 1;
1134 MsgInfo *procmsg_msginfo_copy(MsgInfo *msginfo)
1136 MsgInfo *newmsginfo;
1139 if (msginfo == NULL) return NULL;
1141 newmsginfo = g_new0(MsgInfo, 1);
1143 newmsginfo->refcnt = 1;
1145 #define MEMBCOPY(mmb) newmsginfo->mmb = msginfo->mmb
1146 #define MEMBDUP(mmb) newmsginfo->mmb = msginfo->mmb ? \
1147 g_strdup(msginfo->mmb) : NULL
1162 MEMBDUP(newsgroups);
1169 MEMBCOPY(to_folder);
1173 MEMBDUP(dispositionnotificationto);
1174 MEMBDUP(returnreceiptto);
1176 refs = msginfo->references;
1177 for (refs = msginfo->references; refs != NULL; refs = refs->next) {
1178 newmsginfo->references = g_slist_prepend
1179 (newmsginfo->references, g_strdup(refs->data));
1181 newmsginfo->references = g_slist_reverse(newmsginfo->references);
1184 MEMBDUP(plaintext_file);
1189 MsgInfo *procmsg_msginfo_get_full_info(MsgInfo *msginfo)
1191 MsgInfo *full_msginfo;
1194 if (msginfo == NULL) return NULL;
1196 file = procmsg_get_message_file_path(msginfo);
1197 if (!file || !is_file_exist(file)) {
1199 file = procmsg_get_message_file(msginfo);
1201 if (!file || !is_file_exist(file)) {
1202 g_warning("procmsg_msginfo_get_full_info(): can't get message file.\n");
1206 full_msginfo = procheader_parse_file(file, msginfo->flags, TRUE, FALSE);
1208 if (!full_msginfo) return NULL;
1210 /* CLAWS: make sure we add the missing members; see:
1211 * procheader.c::procheader_get_headernames() */
1212 if (!msginfo->xface)
1213 msginfo->xface = g_strdup(full_msginfo->xface);
1215 msginfo->face = g_strdup(full_msginfo->face);
1216 if (!msginfo->dispositionnotificationto)
1217 msginfo->dispositionnotificationto =
1218 g_strdup(full_msginfo->dispositionnotificationto);
1219 if (!msginfo->returnreceiptto)
1220 msginfo->returnreceiptto = g_strdup
1221 (full_msginfo->returnreceiptto);
1222 if (!msginfo->partial_recv && full_msginfo->partial_recv)
1223 msginfo->partial_recv = g_strdup
1224 (full_msginfo->partial_recv);
1225 msginfo->total_size = full_msginfo->total_size;
1226 if (!msginfo->account_server && full_msginfo->account_server)
1227 msginfo->account_server = g_strdup
1228 (full_msginfo->account_server);
1229 if (!msginfo->account_login && full_msginfo->account_login)
1230 msginfo->account_login = g_strdup
1231 (full_msginfo->account_login);
1232 msginfo->planned_download = full_msginfo->planned_download;
1233 procmsg_msginfo_free(full_msginfo);
1235 return procmsg_msginfo_new_ref(msginfo);
1238 void procmsg_msginfo_free(MsgInfo *msginfo)
1240 if (msginfo == NULL) return;
1243 if (msginfo->refcnt > 0)
1246 if (msginfo->to_folder) {
1247 msginfo->to_folder->op_count--;
1248 folder_item_update(msginfo->to_folder, F_ITEM_UPDATE_MSGCNT);
1251 g_free(msginfo->fromspace);
1252 g_free(msginfo->returnreceiptto);
1253 g_free(msginfo->dispositionnotificationto);
1254 g_free(msginfo->xface);
1255 g_free(msginfo->face);
1257 g_free(msginfo->fromname);
1259 g_free(msginfo->date);
1260 g_free(msginfo->from);
1261 g_free(msginfo->to);
1262 g_free(msginfo->cc);
1263 g_free(msginfo->newsgroups);
1264 g_free(msginfo->subject);
1265 g_free(msginfo->msgid);
1266 g_free(msginfo->inreplyto);
1267 g_free(msginfo->xref);
1269 g_free(msginfo->partial_recv);
1270 g_free(msginfo->account_server);
1271 g_free(msginfo->account_login);
1273 slist_free_strings(msginfo->references);
1274 g_slist_free(msginfo->references);
1276 g_free(msginfo->plaintext_file);
1281 guint procmsg_msginfo_memusage(MsgInfo *msginfo)
1286 memusage += sizeof(MsgInfo);
1287 if (msginfo->fromname)
1288 memusage += strlen(msginfo->fromname);
1290 memusage += strlen(msginfo->date);
1292 memusage += strlen(msginfo->from);
1294 memusage += strlen(msginfo->to);
1296 memusage += strlen(msginfo->cc);
1297 if (msginfo->newsgroups)
1298 memusage += strlen(msginfo->newsgroups);
1299 if (msginfo->subject)
1300 memusage += strlen(msginfo->subject);
1302 memusage += strlen(msginfo->msgid);
1303 if (msginfo->inreplyto)
1304 memusage += strlen(msginfo->inreplyto);
1306 memusage += strlen(msginfo->xface);
1308 memusage += strlen(msginfo->face);
1309 if (msginfo->dispositionnotificationto)
1310 memusage += strlen(msginfo->dispositionnotificationto);
1311 if (msginfo->returnreceiptto)
1312 memusage += strlen(msginfo->returnreceiptto);
1313 for (refs = msginfo->references; refs; refs=refs->next) {
1314 gchar *r = (gchar *)refs->data;
1315 memusage += r?strlen(r):0;
1317 if (msginfo->fromspace)
1318 memusage += strlen(msginfo->fromspace);
1323 gint procmsg_cmp_msgnum_for_sort(gconstpointer a, gconstpointer b)
1325 const MsgInfo *msginfo1 = a;
1326 const MsgInfo *msginfo2 = b;
1333 return msginfo1->msgnum - msginfo2->msgnum;
1336 static gint procmsg_send_message_queue_full(const gchar *file, gboolean keep_session)
1338 static HeaderEntry qentry[] = {{"S:", NULL, FALSE},
1339 {"SSV:", NULL, FALSE},
1340 {"R:", NULL, FALSE},
1341 {"NG:", NULL, FALSE},
1342 {"MAID:", NULL, FALSE},
1343 {"NAID:", NULL, FALSE},
1344 {"SCF:", NULL, FALSE},
1345 {"RMID:", NULL, FALSE},
1346 {"FMID:", NULL, FALSE},
1347 {"X-Sylpheed-Privacy-System:", NULL, FALSE},
1348 {"X-Sylpheed-Encrypt:", NULL, FALSE},
1349 {"X-Sylpheed-Encrypt-Data:", NULL, FALSE},
1350 {NULL, NULL, FALSE}};
1353 gint mailval = 0, newsval = 0;
1355 gchar *smtpserver = NULL;
1356 GSList *to_list = NULL;
1357 GSList *newsgroup_list = NULL;
1358 gchar *savecopyfolder = NULL;
1359 gchar *replymessageid = NULL;
1360 gchar *fwdmessageid = NULL;
1361 gchar *privacy_system = NULL;
1362 gboolean encrypt = FALSE;
1363 gchar *encrypt_data = NULL;
1364 gchar buf[BUFFSIZE];
1366 PrefsAccount *mailac = NULL, *newsac = NULL;
1367 gboolean save_clear_text = TRUE;
1368 gchar *tmp_enc_file = NULL;
1372 g_return_val_if_fail(file != NULL, -1);
1374 if ((fp = g_fopen(file, "rb")) == NULL) {
1375 FILE_OP_ERROR(file, "fopen");
1379 while ((hnum = procheader_get_one_field(buf, sizeof(buf), fp, qentry))
1381 gchar *p = buf + strlen(qentry[hnum].name);
1389 if (smtpserver == NULL)
1390 smtpserver = g_strdup(p);
1393 to_list = address_list_append(to_list, p);
1396 newsgroup_list = newsgroup_list_append(newsgroup_list, p);
1398 case Q_MAIL_ACCOUNT_ID:
1399 mailac = account_find_from_id(atoi(p));
1401 case Q_NEWS_ACCOUNT_ID:
1402 newsac = account_find_from_id(atoi(p));
1404 case Q_SAVE_COPY_FOLDER:
1405 if (savecopyfolder == NULL)
1406 savecopyfolder = g_strdup(p);
1408 case Q_REPLY_MESSAGE_ID:
1409 if (replymessageid == NULL)
1410 replymessageid = g_strdup(p);
1412 case Q_FWD_MESSAGE_ID:
1413 if (fwdmessageid == NULL)
1414 fwdmessageid = g_strdup(p);
1416 case Q_PRIVACY_SYSTEM:
1417 if (privacy_system == NULL)
1418 privacy_system = g_strdup(p);
1424 case Q_ENCRYPT_DATA:
1425 if (encrypt_data == NULL)
1426 encrypt_data = g_strdup(p);
1430 filepos = ftell(fp);
1435 save_clear_text = (mailac != NULL && mailac->save_encrypted_as_clear_text);
1440 mimeinfo = procmime_scan_queue_file(file);
1441 if (!privacy_encrypt(privacy_system, mimeinfo, encrypt_data)
1442 || (fp = my_tmpfile()) == NULL
1443 || procmime_write_mimeinfo(mimeinfo, fp) < 0) {
1446 procmime_mimeinfo_free_all(mimeinfo);
1449 slist_free_strings(to_list);
1450 g_slist_free(to_list);
1451 slist_free_strings(newsgroup_list);
1452 g_slist_free(newsgroup_list);
1453 g_free(savecopyfolder);
1454 g_free(replymessageid);
1455 g_free(fwdmessageid);
1456 g_free(privacy_system);
1457 g_free(encrypt_data);
1462 if (!save_clear_text) {
1463 gchar *content = NULL;
1464 FILE *tmpfp = get_tmpfile_in_dir(get_mime_tmp_dir(), &tmp_enc_file);
1468 content = file_read_stream_to_str(fp);
1471 str_write_to_file(content, tmp_enc_file);
1474 g_warning("couldn't get tempfile\n");
1478 procmime_mimeinfo_free_all(mimeinfo);
1484 debug_print("Sending message by mail\n");
1486 g_warning("Queued message header is broken.\n");
1488 } else if (mailac && mailac->use_mail_command &&
1489 mailac->mail_command && (* mailac->mail_command)) {
1490 mailval = send_message_local(mailac->mail_command, fp);
1494 mailac = account_find_from_smtp_server(from, smtpserver);
1496 g_warning("Account not found. "
1497 "Using current account...\n");
1498 mailac = cur_account;
1503 mailval = send_message_smtp_full(mailac, to_list, fp, keep_session);
1505 PrefsAccount tmp_ac;
1507 g_warning("Account not found.\n");
1509 memset(&tmp_ac, 0, sizeof(PrefsAccount));
1510 tmp_ac.address = from;
1511 tmp_ac.smtp_server = smtpserver;
1512 tmp_ac.smtpport = SMTP_PORT;
1513 mailval = send_message_smtp(&tmp_ac, to_list, fp);
1518 fseek(fp, filepos, SEEK_SET);
1519 if (newsgroup_list && (mailval == 0)) {
1524 /* write to temporary file */
1525 tmp = g_strdup_printf("%s%ctmp%d", g_get_tmp_dir(),
1526 G_DIR_SEPARATOR, (gint)file);
1527 if ((tmpfp = g_fopen(tmp, "wb")) == NULL) {
1528 FILE_OP_ERROR(tmp, "fopen");
1530 alertpanel_error(_("Could not create temporary file for news sending."));
1532 if (change_file_mode_rw(tmpfp, tmp) < 0) {
1533 FILE_OP_ERROR(tmp, "chmod");
1534 g_warning("can't change file mode\n");
1537 while ((newsval == 0) && fgets(buf, sizeof(buf), fp) != NULL) {
1538 if (fputs(buf, tmpfp) == EOF) {
1539 FILE_OP_ERROR(tmp, "fputs");
1541 alertpanel_error(_("Error when writing temporary file for news sending."));
1547 debug_print("Sending message by news\n");
1549 folder = FOLDER(newsac->folder);
1551 newsval = news_post(folder, tmp);
1553 alertpanel_error(_("Error occurred while posting the message to %s ."),
1554 newsac->nntp_server);
1564 /* save message to outbox */
1565 if (mailval == 0 && newsval == 0 && savecopyfolder) {
1568 debug_print("saving sent message...\n");
1570 outbox = folder_find_item_from_identifier(savecopyfolder);
1572 outbox = folder_get_default_outbox();
1574 if (save_clear_text || tmp_enc_file == NULL) {
1575 procmsg_save_to_outbox(outbox, file, TRUE);
1577 procmsg_save_to_outbox(outbox, tmp_enc_file, FALSE);
1581 if (tmp_enc_file != NULL) {
1582 g_unlink(tmp_enc_file);
1584 tmp_enc_file = NULL;
1587 if (replymessageid != NULL || fwdmessageid != NULL) {
1591 if (replymessageid != NULL)
1592 tokens = g_strsplit(replymessageid, "\x7f", 0);
1594 tokens = g_strsplit(fwdmessageid, "\x7f", 0);
1595 item = folder_find_item_from_identifier(tokens[0]);
1597 /* check if queued message has valid folder and message id */
1598 if (item != NULL && tokens[2] != NULL) {
1601 msginfo = folder_item_get_msginfo(item, atoi(tokens[1]));
1603 /* check if referring message exists and has a message id */
1604 if ((msginfo != NULL) &&
1605 (msginfo->msgid != NULL) &&
1606 (strcmp(msginfo->msgid, tokens[2]) != 0)) {
1607 procmsg_msginfo_free(msginfo);
1611 if (msginfo == NULL) {
1612 msginfo = folder_item_get_msginfo_by_msgid(item, tokens[2]);
1615 if (msginfo != NULL) {
1616 if (replymessageid != NULL) {
1617 procmsg_msginfo_unset_flags(msginfo, MSG_FORWARDED, 0);
1618 procmsg_msginfo_set_flags(msginfo, MSG_REPLIED, 0);
1620 procmsg_msginfo_unset_flags(msginfo, MSG_REPLIED, 0);
1621 procmsg_msginfo_set_flags(msginfo, MSG_FORWARDED, 0);
1623 procmsg_msginfo_free(msginfo);
1631 slist_free_strings(to_list);
1632 g_slist_free(to_list);
1633 slist_free_strings(newsgroup_list);
1634 g_slist_free(newsgroup_list);
1635 g_free(savecopyfolder);
1636 g_free(replymessageid);
1637 g_free(fwdmessageid);
1638 g_free(privacy_system);
1639 g_free(encrypt_data);
1641 return (newsval != 0 ? newsval : mailval);
1644 gint procmsg_send_message_queue(const gchar *file)
1646 return procmsg_send_message_queue_full(file, FALSE);
1649 static void update_folder_msg_counts(FolderItem *item, MsgInfo *msginfo, MsgPermFlags old_flags)
1651 MsgPermFlags new_flags = msginfo->flags.perm_flags;
1654 if (!(old_flags & MSG_NEW) && (new_flags & MSG_NEW)) {
1658 if ((old_flags & MSG_NEW) && !(new_flags & MSG_NEW)) {
1663 if (!(old_flags & MSG_UNREAD) && (new_flags & MSG_UNREAD)) {
1664 item->unread_msgs++;
1665 if (procmsg_msg_has_marked_parent(msginfo))
1666 item->unreadmarked_msgs++;
1669 if ((old_flags & MSG_UNREAD) && !(new_flags & MSG_UNREAD)) {
1670 item->unread_msgs--;
1671 if (procmsg_msg_has_marked_parent(msginfo))
1672 item->unreadmarked_msgs--;
1676 if (!(old_flags & MSG_MARKED) && (new_flags & MSG_MARKED)) {
1677 procmsg_update_unread_children(msginfo, TRUE);
1678 item->marked_msgs++;
1681 if ((old_flags & MSG_MARKED) && !(new_flags & MSG_MARKED)) {
1682 procmsg_update_unread_children(msginfo, FALSE);
1683 item->marked_msgs--;
1687 void procmsg_msginfo_set_flags(MsgInfo *msginfo, MsgPermFlags perm_flags, MsgTmpFlags 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("Setting 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 | perm_flags;
1703 if ((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 |= tmp_flags;
1718 /* update notification */
1719 if ((perm_flags_old != perm_flags_new) || (tmp_flags_old != msginfo->flags.tmp_flags)) {
1720 msginfo_update.msginfo = msginfo;
1721 msginfo_update.flags = MSGINFO_UPDATE_FLAGS;
1722 hooks_invoke(MSGINFO_UPDATE_HOOKLIST, &msginfo_update);
1723 folder_item_update(msginfo->folder, F_ITEM_UPDATE_MSGCNT);
1727 void procmsg_msginfo_unset_flags(MsgInfo *msginfo, MsgPermFlags perm_flags, MsgTmpFlags tmp_flags)
1730 MsgInfoUpdate msginfo_update;
1731 MsgPermFlags perm_flags_new, perm_flags_old;
1732 MsgTmpFlags tmp_flags_old;
1734 g_return_if_fail(msginfo != NULL);
1735 item = msginfo->folder;
1736 g_return_if_fail(item != NULL);
1738 debug_print("Unsetting flags for message %d in folder %s\n", msginfo->msgnum, item->path);
1740 /* Perm Flags handling */
1741 perm_flags_old = msginfo->flags.perm_flags;
1742 perm_flags_new = msginfo->flags.perm_flags & ~perm_flags;
1744 if (perm_flags_old != perm_flags_new) {
1745 folder_item_change_msg_flags(msginfo->folder, msginfo, perm_flags_new);
1747 update_folder_msg_counts(item, msginfo, perm_flags_old);
1749 msginfo_update.msginfo = msginfo;
1750 msginfo_update.flags = MSGINFO_UPDATE_FLAGS;
1751 hooks_invoke(MSGINFO_UPDATE_HOOKLIST, &msginfo_update);
1752 folder_item_update(msginfo->folder, F_ITEM_UPDATE_MSGCNT);
1755 /* Tmp flags hanlding */
1756 tmp_flags_old = msginfo->flags.tmp_flags;
1757 msginfo->flags.tmp_flags &= ~tmp_flags;
1759 /* update notification */
1760 if ((perm_flags_old != perm_flags_new) || (tmp_flags_old != msginfo->flags.tmp_flags)) {
1761 msginfo_update.msginfo = msginfo;
1762 msginfo_update.flags = MSGINFO_UPDATE_FLAGS;
1763 hooks_invoke(MSGINFO_UPDATE_HOOKLIST, &msginfo_update);
1764 folder_item_update(msginfo->folder, F_ITEM_UPDATE_MSGCNT);
1768 void procmsg_msginfo_change_flags(MsgInfo *msginfo,
1769 MsgPermFlags add_perm_flags, MsgTmpFlags add_tmp_flags,
1770 MsgPermFlags rem_perm_flags, MsgTmpFlags rem_tmp_flags)
1773 MsgInfoUpdate msginfo_update;
1774 MsgPermFlags perm_flags_new, perm_flags_old;
1775 MsgTmpFlags tmp_flags_old;
1777 g_return_if_fail(msginfo != NULL);
1778 item = msginfo->folder;
1779 g_return_if_fail(item != NULL);
1781 debug_print("Changing flags for message %d in folder %s\n", msginfo->msgnum, item->path);
1783 /* Perm Flags handling */
1784 perm_flags_old = msginfo->flags.perm_flags;
1785 perm_flags_new = (msginfo->flags.perm_flags & ~rem_perm_flags) | add_perm_flags;
1786 if ((add_perm_flags & MSG_IGNORE_THREAD) || (perm_flags_old & MSG_IGNORE_THREAD)) {
1787 perm_flags_new &= ~(MSG_NEW | MSG_UNREAD);
1790 if (perm_flags_old != perm_flags_new) {
1791 folder_item_change_msg_flags(msginfo->folder, msginfo, perm_flags_new);
1793 update_folder_msg_counts(item, msginfo, perm_flags_old);
1797 /* Tmp flags handling */
1798 tmp_flags_old = msginfo->flags.tmp_flags;
1799 msginfo->flags.tmp_flags &= ~rem_tmp_flags;
1800 msginfo->flags.tmp_flags |= add_tmp_flags;
1802 /* update notification */
1803 if ((perm_flags_old != perm_flags_new) || (tmp_flags_old != msginfo->flags.tmp_flags)) {
1804 msginfo_update.msginfo = msginfo;
1805 msginfo_update.flags = MSGINFO_UPDATE_FLAGS;
1806 hooks_invoke(MSGINFO_UPDATE_HOOKLIST, &msginfo_update);
1807 folder_item_update(msginfo->folder, F_ITEM_UPDATE_MSGCNT);
1812 *\brief check for flags (e.g. mark) in prior msgs of current thread
1814 *\param info Current message
1815 *\param perm_flags Flags to be checked
1816 *\param parentmsgs Hash of prior msgs to avoid loops
1818 *\return gboolean TRUE if perm_flags are found
1820 gboolean procmsg_msg_has_flagged_parent_real(MsgInfo *info,
1821 MsgPermFlags perm_flags, GHashTable *parentmsgs)
1825 g_return_val_if_fail(info != NULL, FALSE);
1827 if (info != NULL && info->folder != NULL && info->inreplyto != NULL) {
1828 tmp = folder_item_get_msginfo_by_msgid(info->folder,
1830 if (tmp && (tmp->flags.perm_flags & perm_flags)) {
1831 procmsg_msginfo_free(tmp);
1833 } else if (tmp != NULL) {
1836 if (g_hash_table_lookup(parentmsgs, info)) {
1837 debug_print("loop detected: %s%c%d\n",
1838 folder_item_get_path(info->folder),
1839 G_DIR_SEPARATOR, info->msgnum);
1842 g_hash_table_insert(parentmsgs, info, "1");
1843 result = procmsg_msg_has_flagged_parent_real(
1844 tmp, perm_flags, parentmsgs);
1846 procmsg_msginfo_free(tmp);
1856 *\brief Callback for cleaning up hash of parentmsgs
1858 gboolean parentmsgs_hash_remove(gpointer key,
1866 *\brief Set up list of parentmsgs
1867 * See procmsg_msg_has_flagged_parent_real()
1869 gboolean procmsg_msg_has_flagged_parent(MsgInfo *info, MsgPermFlags perm_flags)
1872 GHashTable *parentmsgs = g_hash_table_new(NULL, NULL);
1874 result = procmsg_msg_has_flagged_parent_real(info, perm_flags, parentmsgs);
1875 g_hash_table_foreach_remove(parentmsgs, parentmsgs_hash_remove, NULL);
1876 g_hash_table_destroy(parentmsgs);
1881 *\brief Check if msgs prior in thread are marked
1882 * See procmsg_msg_has_flagged_parent_real()
1884 gboolean procmsg_msg_has_marked_parent(MsgInfo *info)
1886 return procmsg_msg_has_flagged_parent(info, MSG_MARKED);
1890 GSList *procmsg_find_children_func(MsgInfo *info,
1891 GSList *children, GSList *all)
1895 g_return_val_if_fail(info!=NULL, children);
1896 if (info->msgid == NULL)
1899 for (cur = all; cur != NULL; cur = g_slist_next(cur)) {
1900 MsgInfo *tmp = (MsgInfo *)cur->data;
1901 if (tmp->inreplyto && !strcmp(tmp->inreplyto, info->msgid)) {
1902 /* Check if message is already in the list */
1903 if ((children == NULL) ||
1904 (g_slist_index(children, tmp) == -1)) {
1905 children = g_slist_prepend(children,
1906 procmsg_msginfo_new_ref(tmp));
1907 children = procmsg_find_children_func(tmp,
1916 GSList *procmsg_find_children (MsgInfo *info)
1921 g_return_val_if_fail(info!=NULL, NULL);
1922 all = folder_item_get_msg_list(info->folder);
1923 children = procmsg_find_children_func(info, NULL, all);
1924 if (children != NULL) {
1925 for (cur = all; cur != NULL; cur = g_slist_next(cur)) {
1926 /* this will not free the used pointers
1927 created with procmsg_msginfo_new_ref */
1928 procmsg_msginfo_free((MsgInfo *)cur->data);
1936 void procmsg_update_unread_children(MsgInfo *info, gboolean newly_marked)
1938 GSList *children = procmsg_find_children(info);
1940 for (cur = children; cur != NULL; cur = g_slist_next(cur)) {
1941 MsgInfo *tmp = (MsgInfo *)cur->data;
1942 if(MSG_IS_UNREAD(tmp->flags) && !MSG_IS_IGNORE_THREAD(tmp->flags)) {
1944 info->folder->unreadmarked_msgs++;
1946 info->folder->unreadmarked_msgs--;
1947 folder_item_update(info->folder, F_ITEM_UPDATE_MSGCNT);
1949 procmsg_msginfo_free(tmp);
1951 g_slist_free(children);
1955 * Set the destination folder for a copy or move operation
1957 * \param msginfo The message which's destination folder is changed
1958 * \param to_folder The destination folder for the operation
1960 void procmsg_msginfo_set_to_folder(MsgInfo *msginfo, FolderItem *to_folder)
1962 if(msginfo->to_folder != NULL) {
1963 msginfo->to_folder->op_count--;
1964 folder_item_update(msginfo->to_folder, F_ITEM_UPDATE_MSGCNT);
1966 msginfo->to_folder = to_folder;
1967 if(to_folder != NULL) {
1968 to_folder->op_count++;
1969 folder_item_update(msginfo->to_folder, F_ITEM_UPDATE_MSGCNT);
1974 * Apply filtering actions to the msginfo
1976 * \param msginfo The MsgInfo describing the message that should be filtered
1977 * \return TRUE if the message was moved and MsgInfo is now invalid,
1980 gboolean procmsg_msginfo_filter(MsgInfo *msginfo)
1982 MailFilteringData mail_filtering_data;
1984 mail_filtering_data.msginfo = msginfo;
1985 if (hooks_invoke(MAIL_FILTERING_HOOKLIST, &mail_filtering_data)) {
1989 /* filter if enabled in prefs or move to inbox if not */
1990 if((filtering_rules != NULL) &&
1991 filter_message_by_msginfo(filtering_rules, msginfo)) {
1998 MsgInfo *procmsg_msginfo_new_from_mimeinfo(MsgInfo *src_msginfo, MimeInfo *mimeinfo)
2000 MsgInfo *tmp_msginfo = NULL;
2001 MsgFlags flags = {0, 0};
2002 gchar *tmpfile = get_tmp_file();
2003 FILE *fp = g_fopen(tmpfile, "wb");
2005 if (!mimeinfo || mimeinfo->type != MIMETYPE_MESSAGE ||
2006 g_ascii_strcasecmp(mimeinfo->subtype, "rfc822")) {
2007 g_warning("procmsg_msginfo_new_from_mimeinfo(): unsuitable mimeinfo");
2014 if (fp && procmime_write_mimeinfo(mimeinfo, fp) >= 0) {
2017 tmp_msginfo = procheader_parse_file(
2024 if (tmp_msginfo != NULL) {
2026 tmp_msginfo->folder = src_msginfo->folder;
2027 tmp_msginfo->plaintext_file = g_strdup(tmpfile);
2029 g_warning("procmsg_msginfo_new_from_mimeinfo(): Can't generate new msginfo");
2037 static GSList *spam_learners = NULL;
2039 void procmsg_register_spam_learner (int (*learn_func)(MsgInfo *info, GSList *list, gboolean spam))
2041 if (!g_slist_find(spam_learners, learn_func))
2042 spam_learners = g_slist_append(spam_learners, learn_func);
2043 if (mainwindow_get_mainwindow()) {
2044 main_window_set_menu_sensitive(mainwindow_get_mainwindow());
2045 summary_set_menu_sensitive(
2046 mainwindow_get_mainwindow()->summaryview);
2047 toolbar_main_set_sensitive(mainwindow_get_mainwindow());
2051 void procmsg_unregister_spam_learner (int (*learn_func)(MsgInfo *info, GSList *list, gboolean spam))
2053 spam_learners = g_slist_remove(spam_learners, learn_func);
2054 if (mainwindow_get_mainwindow()) {
2055 main_window_set_menu_sensitive(mainwindow_get_mainwindow());
2056 summary_set_menu_sensitive(
2057 mainwindow_get_mainwindow()->summaryview);
2058 toolbar_main_set_sensitive(mainwindow_get_mainwindow());
2062 gboolean procmsg_spam_can_learn(void)
2064 return g_slist_length(spam_learners) > 0;
2067 int procmsg_spam_learner_learn (MsgInfo *info, GSList *list, gboolean spam)
2069 GSList *cur = spam_learners;
2071 for (; cur; cur = cur->next) {
2072 int ((*func)(MsgInfo *info, GSList *list, gboolean spam)) = cur->data;
2073 ret |= func(info, list, spam);
2078 static gchar *spam_folder_item = NULL;
2079 void procmsg_spam_set_folder (const char *item_identifier)
2081 if (spam_folder_item)
2082 g_free(spam_folder_item);
2083 if (item_identifier)
2084 spam_folder_item = g_strdup(item_identifier);
2086 spam_folder_item = NULL;
2089 FolderItem *procmsg_spam_get_folder (void)
2091 FolderItem *item = spam_folder_item ? folder_find_item_from_identifier(spam_folder_item) : NULL;
2092 return item ? item : folder_get_default_trash();