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 if (mailac && mailac->save_encrypted_as_clear_text
1436 && !mailac->encrypt_to_self)
1437 save_clear_text = TRUE;
1439 save_clear_text = FALSE;
1444 mimeinfo = procmime_scan_queue_file(file);
1445 if (!privacy_encrypt(privacy_system, mimeinfo, encrypt_data)
1446 || (fp = my_tmpfile()) == NULL
1447 || procmime_write_mimeinfo(mimeinfo, fp) < 0) {
1450 procmime_mimeinfo_free_all(mimeinfo);
1453 slist_free_strings(to_list);
1454 g_slist_free(to_list);
1455 slist_free_strings(newsgroup_list);
1456 g_slist_free(newsgroup_list);
1457 g_free(savecopyfolder);
1458 g_free(replymessageid);
1459 g_free(fwdmessageid);
1460 g_free(privacy_system);
1461 g_free(encrypt_data);
1466 if (!save_clear_text) {
1467 gchar *content = NULL;
1468 FILE *tmpfp = get_tmpfile_in_dir(get_mime_tmp_dir(), &tmp_enc_file);
1472 content = file_read_stream_to_str(fp);
1475 str_write_to_file(content, tmp_enc_file);
1478 g_warning("couldn't get tempfile\n");
1482 procmime_mimeinfo_free_all(mimeinfo);
1488 debug_print("Sending message by mail\n");
1490 g_warning("Queued message header is broken.\n");
1492 } else if (mailac && mailac->use_mail_command &&
1493 mailac->mail_command && (* mailac->mail_command)) {
1494 mailval = send_message_local(mailac->mail_command, fp);
1498 mailac = account_find_from_smtp_server(from, smtpserver);
1500 g_warning("Account not found. "
1501 "Using current account...\n");
1502 mailac = cur_account;
1507 mailval = send_message_smtp_full(mailac, to_list, fp, keep_session);
1509 PrefsAccount tmp_ac;
1511 g_warning("Account not found.\n");
1513 memset(&tmp_ac, 0, sizeof(PrefsAccount));
1514 tmp_ac.address = from;
1515 tmp_ac.smtp_server = smtpserver;
1516 tmp_ac.smtpport = SMTP_PORT;
1517 mailval = send_message_smtp(&tmp_ac, to_list, fp);
1522 fseek(fp, filepos, SEEK_SET);
1523 if (newsgroup_list && (mailval == 0)) {
1528 /* write to temporary file */
1529 tmp = g_strdup_printf("%s%ctmp%d", g_get_tmp_dir(),
1530 G_DIR_SEPARATOR, (gint)file);
1531 if ((tmpfp = g_fopen(tmp, "wb")) == NULL) {
1532 FILE_OP_ERROR(tmp, "fopen");
1534 alertpanel_error(_("Could not create temporary file for news sending."));
1536 if (change_file_mode_rw(tmpfp, tmp) < 0) {
1537 FILE_OP_ERROR(tmp, "chmod");
1538 g_warning("can't change file mode\n");
1541 while ((newsval == 0) && fgets(buf, sizeof(buf), fp) != NULL) {
1542 if (fputs(buf, tmpfp) == EOF) {
1543 FILE_OP_ERROR(tmp, "fputs");
1545 alertpanel_error(_("Error when writing temporary file for news sending."));
1551 debug_print("Sending message by news\n");
1553 folder = FOLDER(newsac->folder);
1555 newsval = news_post(folder, tmp);
1557 alertpanel_error(_("Error occurred while posting the message to %s ."),
1558 newsac->nntp_server);
1568 /* save message to outbox */
1569 if (mailval == 0 && newsval == 0 && savecopyfolder) {
1572 debug_print("saving sent message...\n");
1574 outbox = folder_find_item_from_identifier(savecopyfolder);
1576 outbox = folder_get_default_outbox();
1578 if (save_clear_text || tmp_enc_file == NULL) {
1579 procmsg_save_to_outbox(outbox, file, TRUE);
1581 procmsg_save_to_outbox(outbox, tmp_enc_file, FALSE);
1585 if (tmp_enc_file != NULL) {
1586 g_unlink(tmp_enc_file);
1588 tmp_enc_file = NULL;
1591 if (replymessageid != NULL || fwdmessageid != NULL) {
1595 if (replymessageid != NULL)
1596 tokens = g_strsplit(replymessageid, "\t", 0);
1598 tokens = g_strsplit(fwdmessageid, "\t", 0);
1599 item = folder_find_item_from_identifier(tokens[0]);
1601 /* check if queued message has valid folder and message id */
1602 if (item != NULL && tokens[2] != NULL) {
1605 msginfo = folder_item_get_msginfo(item, atoi(tokens[1]));
1607 /* check if referring message exists and has a message id */
1608 if ((msginfo != NULL) &&
1609 (msginfo->msgid != NULL) &&
1610 (strcmp(msginfo->msgid, tokens[2]) != 0)) {
1611 procmsg_msginfo_free(msginfo);
1615 if (msginfo == NULL) {
1616 msginfo = folder_item_get_msginfo_by_msgid(item, tokens[2]);
1619 if (msginfo != NULL) {
1620 if (replymessageid != NULL) {
1621 procmsg_msginfo_unset_flags(msginfo, MSG_FORWARDED, 0);
1622 procmsg_msginfo_set_flags(msginfo, MSG_REPLIED, 0);
1624 procmsg_msginfo_unset_flags(msginfo, MSG_REPLIED, 0);
1625 procmsg_msginfo_set_flags(msginfo, MSG_FORWARDED, 0);
1627 procmsg_msginfo_free(msginfo);
1635 slist_free_strings(to_list);
1636 g_slist_free(to_list);
1637 slist_free_strings(newsgroup_list);
1638 g_slist_free(newsgroup_list);
1639 g_free(savecopyfolder);
1640 g_free(replymessageid);
1641 g_free(fwdmessageid);
1642 g_free(privacy_system);
1643 g_free(encrypt_data);
1645 return (newsval != 0 ? newsval : mailval);
1648 gint procmsg_send_message_queue(const gchar *file)
1650 return procmsg_send_message_queue_full(file, FALSE);
1653 static void update_folder_msg_counts(FolderItem *item, MsgInfo *msginfo, MsgPermFlags old_flags)
1655 MsgPermFlags new_flags = msginfo->flags.perm_flags;
1658 if (!(old_flags & MSG_NEW) && (new_flags & MSG_NEW)) {
1662 if ((old_flags & MSG_NEW) && !(new_flags & MSG_NEW)) {
1667 if (!(old_flags & MSG_UNREAD) && (new_flags & MSG_UNREAD)) {
1668 item->unread_msgs++;
1669 if (procmsg_msg_has_marked_parent(msginfo))
1670 item->unreadmarked_msgs++;
1673 if ((old_flags & MSG_UNREAD) && !(new_flags & MSG_UNREAD)) {
1674 item->unread_msgs--;
1675 if (procmsg_msg_has_marked_parent(msginfo))
1676 item->unreadmarked_msgs--;
1680 if (!(old_flags & MSG_MARKED) && (new_flags & MSG_MARKED)) {
1681 procmsg_update_unread_children(msginfo, TRUE);
1682 item->marked_msgs++;
1685 if ((old_flags & MSG_MARKED) && !(new_flags & MSG_MARKED)) {
1686 procmsg_update_unread_children(msginfo, FALSE);
1687 item->marked_msgs--;
1691 void procmsg_msginfo_set_flags(MsgInfo *msginfo, MsgPermFlags perm_flags, MsgTmpFlags tmp_flags)
1694 MsgInfoUpdate msginfo_update;
1695 MsgPermFlags perm_flags_new, perm_flags_old;
1696 MsgTmpFlags tmp_flags_old;
1698 g_return_if_fail(msginfo != NULL);
1699 item = msginfo->folder;
1700 g_return_if_fail(item != NULL);
1702 debug_print("Setting flags for message %d in folder %s\n", msginfo->msgnum, item->path);
1704 /* Perm Flags handling */
1705 perm_flags_old = msginfo->flags.perm_flags;
1706 perm_flags_new = msginfo->flags.perm_flags | perm_flags;
1707 if ((perm_flags & MSG_IGNORE_THREAD) || (perm_flags_old & MSG_IGNORE_THREAD)) {
1708 perm_flags_new &= ~(MSG_NEW | MSG_UNREAD);
1711 if (perm_flags_old != perm_flags_new) {
1712 folder_item_change_msg_flags(msginfo->folder, msginfo, perm_flags_new);
1714 update_folder_msg_counts(item, msginfo, perm_flags_old);
1718 /* Tmp flags handling */
1719 tmp_flags_old = msginfo->flags.tmp_flags;
1720 msginfo->flags.tmp_flags |= tmp_flags;
1722 /* update notification */
1723 if ((perm_flags_old != perm_flags_new) || (tmp_flags_old != msginfo->flags.tmp_flags)) {
1724 msginfo_update.msginfo = msginfo;
1725 msginfo_update.flags = MSGINFO_UPDATE_FLAGS;
1726 hooks_invoke(MSGINFO_UPDATE_HOOKLIST, &msginfo_update);
1727 folder_item_update(msginfo->folder, F_ITEM_UPDATE_MSGCNT);
1731 void procmsg_msginfo_unset_flags(MsgInfo *msginfo, MsgPermFlags perm_flags, MsgTmpFlags tmp_flags)
1734 MsgInfoUpdate msginfo_update;
1735 MsgPermFlags perm_flags_new, perm_flags_old;
1736 MsgTmpFlags tmp_flags_old;
1738 g_return_if_fail(msginfo != NULL);
1739 item = msginfo->folder;
1740 g_return_if_fail(item != NULL);
1742 debug_print("Unsetting flags for message %d in folder %s\n", msginfo->msgnum, item->path);
1744 /* Perm Flags handling */
1745 perm_flags_old = msginfo->flags.perm_flags;
1746 perm_flags_new = msginfo->flags.perm_flags & ~perm_flags;
1748 if (perm_flags_old != perm_flags_new) {
1749 folder_item_change_msg_flags(msginfo->folder, msginfo, perm_flags_new);
1751 update_folder_msg_counts(item, msginfo, perm_flags_old);
1753 msginfo_update.msginfo = msginfo;
1754 msginfo_update.flags = MSGINFO_UPDATE_FLAGS;
1755 hooks_invoke(MSGINFO_UPDATE_HOOKLIST, &msginfo_update);
1756 folder_item_update(msginfo->folder, F_ITEM_UPDATE_MSGCNT);
1759 /* Tmp flags hanlding */
1760 tmp_flags_old = msginfo->flags.tmp_flags;
1761 msginfo->flags.tmp_flags &= ~tmp_flags;
1763 /* update notification */
1764 if ((perm_flags_old != perm_flags_new) || (tmp_flags_old != msginfo->flags.tmp_flags)) {
1765 msginfo_update.msginfo = msginfo;
1766 msginfo_update.flags = MSGINFO_UPDATE_FLAGS;
1767 hooks_invoke(MSGINFO_UPDATE_HOOKLIST, &msginfo_update);
1768 folder_item_update(msginfo->folder, F_ITEM_UPDATE_MSGCNT);
1772 void procmsg_msginfo_change_flags(MsgInfo *msginfo,
1773 MsgPermFlags add_perm_flags, MsgTmpFlags add_tmp_flags,
1774 MsgPermFlags rem_perm_flags, MsgTmpFlags rem_tmp_flags)
1777 MsgInfoUpdate msginfo_update;
1778 MsgPermFlags perm_flags_new, perm_flags_old;
1779 MsgTmpFlags tmp_flags_old;
1781 g_return_if_fail(msginfo != NULL);
1782 item = msginfo->folder;
1783 g_return_if_fail(item != NULL);
1785 debug_print("Changing flags for message %d in folder %s\n", msginfo->msgnum, item->path);
1787 /* Perm Flags handling */
1788 perm_flags_old = msginfo->flags.perm_flags;
1789 perm_flags_new = (msginfo->flags.perm_flags & ~rem_perm_flags) | add_perm_flags;
1790 if ((add_perm_flags & MSG_IGNORE_THREAD) || (perm_flags_old & MSG_IGNORE_THREAD)) {
1791 perm_flags_new &= ~(MSG_NEW | MSG_UNREAD);
1794 if (perm_flags_old != perm_flags_new) {
1795 folder_item_change_msg_flags(msginfo->folder, msginfo, perm_flags_new);
1797 update_folder_msg_counts(item, msginfo, perm_flags_old);
1801 /* Tmp flags handling */
1802 tmp_flags_old = msginfo->flags.tmp_flags;
1803 msginfo->flags.tmp_flags &= ~rem_tmp_flags;
1804 msginfo->flags.tmp_flags |= add_tmp_flags;
1806 /* update notification */
1807 if ((perm_flags_old != perm_flags_new) || (tmp_flags_old != msginfo->flags.tmp_flags)) {
1808 msginfo_update.msginfo = msginfo;
1809 msginfo_update.flags = MSGINFO_UPDATE_FLAGS;
1810 hooks_invoke(MSGINFO_UPDATE_HOOKLIST, &msginfo_update);
1811 folder_item_update(msginfo->folder, F_ITEM_UPDATE_MSGCNT);
1816 *\brief check for flags (e.g. mark) in prior msgs of current thread
1818 *\param info Current message
1819 *\param perm_flags Flags to be checked
1820 *\param parentmsgs Hash of prior msgs to avoid loops
1822 *\return gboolean TRUE if perm_flags are found
1824 gboolean procmsg_msg_has_flagged_parent_real(MsgInfo *info,
1825 MsgPermFlags perm_flags, GHashTable *parentmsgs)
1829 g_return_val_if_fail(info != NULL, FALSE);
1831 if (info != NULL && info->folder != NULL && info->inreplyto != NULL) {
1832 tmp = folder_item_get_msginfo_by_msgid(info->folder,
1834 if (tmp && (tmp->flags.perm_flags & perm_flags)) {
1835 procmsg_msginfo_free(tmp);
1837 } else if (tmp != NULL) {
1840 if (g_hash_table_lookup(parentmsgs, info)) {
1841 debug_print("loop detected: %s%c%d\n",
1842 folder_item_get_path(info->folder),
1843 G_DIR_SEPARATOR, info->msgnum);
1846 g_hash_table_insert(parentmsgs, info, "1");
1847 result = procmsg_msg_has_flagged_parent_real(
1848 tmp, perm_flags, parentmsgs);
1850 procmsg_msginfo_free(tmp);
1860 *\brief Callback for cleaning up hash of parentmsgs
1862 gboolean parentmsgs_hash_remove(gpointer key,
1870 *\brief Set up list of parentmsgs
1871 * See procmsg_msg_has_flagged_parent_real()
1873 gboolean procmsg_msg_has_flagged_parent(MsgInfo *info, MsgPermFlags perm_flags)
1876 GHashTable *parentmsgs = g_hash_table_new(NULL, NULL);
1878 result = procmsg_msg_has_flagged_parent_real(info, perm_flags, parentmsgs);
1879 g_hash_table_foreach_remove(parentmsgs, parentmsgs_hash_remove, NULL);
1880 g_hash_table_destroy(parentmsgs);
1885 *\brief Check if msgs prior in thread are marked
1886 * See procmsg_msg_has_flagged_parent_real()
1888 gboolean procmsg_msg_has_marked_parent(MsgInfo *info)
1890 return procmsg_msg_has_flagged_parent(info, MSG_MARKED);
1894 GSList *procmsg_find_children_func(MsgInfo *info,
1895 GSList *children, GSList *all)
1899 g_return_val_if_fail(info!=NULL, children);
1900 if (info->msgid == NULL)
1903 for (cur = all; cur != NULL; cur = g_slist_next(cur)) {
1904 MsgInfo *tmp = (MsgInfo *)cur->data;
1905 if (tmp->inreplyto && !strcmp(tmp->inreplyto, info->msgid)) {
1906 /* Check if message is already in the list */
1907 if ((children == NULL) ||
1908 (g_slist_index(children, tmp) == -1)) {
1909 children = g_slist_prepend(children,
1910 procmsg_msginfo_new_ref(tmp));
1911 children = procmsg_find_children_func(tmp,
1920 GSList *procmsg_find_children (MsgInfo *info)
1925 g_return_val_if_fail(info!=NULL, NULL);
1926 all = folder_item_get_msg_list(info->folder);
1927 children = procmsg_find_children_func(info, NULL, all);
1928 if (children != NULL) {
1929 for (cur = all; cur != NULL; cur = g_slist_next(cur)) {
1930 /* this will not free the used pointers
1931 created with procmsg_msginfo_new_ref */
1932 procmsg_msginfo_free((MsgInfo *)cur->data);
1940 void procmsg_update_unread_children(MsgInfo *info, gboolean newly_marked)
1942 GSList *children = procmsg_find_children(info);
1944 for (cur = children; cur != NULL; cur = g_slist_next(cur)) {
1945 MsgInfo *tmp = (MsgInfo *)cur->data;
1946 if(MSG_IS_UNREAD(tmp->flags) && !MSG_IS_IGNORE_THREAD(tmp->flags)) {
1948 info->folder->unreadmarked_msgs++;
1950 info->folder->unreadmarked_msgs--;
1951 folder_item_update(info->folder, F_ITEM_UPDATE_MSGCNT);
1953 procmsg_msginfo_free(tmp);
1955 g_slist_free(children);
1959 * Set the destination folder for a copy or move operation
1961 * \param msginfo The message which's destination folder is changed
1962 * \param to_folder The destination folder for the operation
1964 void procmsg_msginfo_set_to_folder(MsgInfo *msginfo, FolderItem *to_folder)
1966 if(msginfo->to_folder != NULL) {
1967 msginfo->to_folder->op_count--;
1968 folder_item_update(msginfo->to_folder, F_ITEM_UPDATE_MSGCNT);
1970 msginfo->to_folder = to_folder;
1971 if(to_folder != NULL) {
1972 to_folder->op_count++;
1973 folder_item_update(msginfo->to_folder, F_ITEM_UPDATE_MSGCNT);
1978 * Apply filtering actions to the msginfo
1980 * \param msginfo The MsgInfo describing the message that should be filtered
1981 * \return TRUE if the message was moved and MsgInfo is now invalid,
1984 gboolean procmsg_msginfo_filter(MsgInfo *msginfo)
1986 MailFilteringData mail_filtering_data;
1988 mail_filtering_data.msginfo = msginfo;
1989 if (hooks_invoke(MAIL_FILTERING_HOOKLIST, &mail_filtering_data)) {
1993 /* filter if enabled in prefs or move to inbox if not */
1994 if((filtering_rules != NULL) &&
1995 filter_message_by_msginfo(filtering_rules, msginfo)) {
2002 MsgInfo *procmsg_msginfo_new_from_mimeinfo(MsgInfo *src_msginfo, MimeInfo *mimeinfo)
2004 MsgInfo *tmp_msginfo = NULL;
2005 MsgFlags flags = {0, 0};
2006 gchar *tmpfile = get_tmp_file();
2007 FILE *fp = g_fopen(tmpfile, "wb");
2009 if (!mimeinfo || mimeinfo->type != MIMETYPE_MESSAGE ||
2010 g_ascii_strcasecmp(mimeinfo->subtype, "rfc822")) {
2011 g_warning("procmsg_msginfo_new_from_mimeinfo(): unsuitable mimeinfo");
2018 if (fp && procmime_write_mimeinfo(mimeinfo, fp) >= 0) {
2021 tmp_msginfo = procheader_parse_file(
2028 if (tmp_msginfo != NULL) {
2030 tmp_msginfo->folder = src_msginfo->folder;
2031 tmp_msginfo->plaintext_file = g_strdup(tmpfile);
2033 g_warning("procmsg_msginfo_new_from_mimeinfo(): Can't generate new msginfo");
2041 static GSList *spam_learners = NULL;
2043 void procmsg_register_spam_learner (int (*learn_func)(MsgInfo *info, GSList *list, gboolean spam))
2045 if (!g_slist_find(spam_learners, learn_func))
2046 spam_learners = g_slist_append(spam_learners, learn_func);
2047 if (mainwindow_get_mainwindow()) {
2048 main_window_set_menu_sensitive(mainwindow_get_mainwindow());
2049 summary_set_menu_sensitive(
2050 mainwindow_get_mainwindow()->summaryview);
2051 toolbar_main_set_sensitive(mainwindow_get_mainwindow());
2055 void procmsg_unregister_spam_learner (int (*learn_func)(MsgInfo *info, GSList *list, gboolean spam))
2057 spam_learners = g_slist_remove(spam_learners, learn_func);
2058 if (mainwindow_get_mainwindow()) {
2059 main_window_set_menu_sensitive(mainwindow_get_mainwindow());
2060 summary_set_menu_sensitive(
2061 mainwindow_get_mainwindow()->summaryview);
2062 toolbar_main_set_sensitive(mainwindow_get_mainwindow());
2066 gboolean procmsg_spam_can_learn(void)
2068 return g_slist_length(spam_learners) > 0;
2071 int procmsg_spam_learner_learn (MsgInfo *info, GSList *list, gboolean spam)
2073 GSList *cur = spam_learners;
2075 for (; cur; cur = cur->next) {
2076 int ((*func)(MsgInfo *info, GSList *list, gboolean spam)) = cur->data;
2077 ret |= func(info, list, spam);
2082 static gchar *spam_folder_item = NULL;
2083 void procmsg_spam_set_folder (const char *item_identifier)
2085 g_free(spam_folder_item);
2086 if (item_identifier)
2087 spam_folder_item = g_strdup(item_identifier);
2089 spam_folder_item = NULL;
2092 FolderItem *procmsg_spam_get_folder (void)
2094 FolderItem *item = spam_folder_item ? folder_find_item_from_identifier(spam_folder_item) : NULL;
2095 return item ? item : folder_get_default_trash();