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"
50 static gint procmsg_send_message_queue_full(const gchar *file, gboolean keep_session);
58 Q_MAIL_ACCOUNT_ID = 4,
59 Q_NEWS_ACCOUNT_ID = 5,
60 Q_SAVE_COPY_FOLDER = 6,
61 Q_REPLY_MESSAGE_ID = 7,
68 GHashTable *procmsg_msg_hash_table_create(GSList *mlist)
70 GHashTable *msg_table;
72 if (mlist == NULL) return NULL;
74 msg_table = g_hash_table_new(NULL, g_direct_equal);
75 procmsg_msg_hash_table_append(msg_table, mlist);
80 void procmsg_msg_hash_table_append(GHashTable *msg_table, GSList *mlist)
85 if (msg_table == NULL || mlist == NULL) return;
87 for (cur = mlist; cur != NULL; cur = cur->next) {
88 msginfo = (MsgInfo *)cur->data;
90 g_hash_table_insert(msg_table,
91 GUINT_TO_POINTER(msginfo->msgnum),
96 GHashTable *procmsg_to_folder_hash_table_create(GSList *mlist)
98 GHashTable *msg_table;
102 if (mlist == NULL) return NULL;
104 msg_table = g_hash_table_new(NULL, g_direct_equal);
106 for (cur = mlist; cur != NULL; cur = cur->next) {
107 msginfo = (MsgInfo *)cur->data;
108 g_hash_table_insert(msg_table, msginfo->to_folder, msginfo);
114 gint procmsg_get_last_num_in_msg_list(GSList *mlist)
120 for (cur = mlist; cur != NULL; cur = cur->next) {
121 msginfo = (MsgInfo *)cur->data;
122 if (msginfo && msginfo->msgnum > last)
123 last = msginfo->msgnum;
129 void procmsg_msg_list_free(GSList *mlist)
134 for (cur = mlist; cur != NULL; cur = cur->next) {
135 msginfo = (MsgInfo *)cur->data;
136 procmsg_msginfo_free(msginfo);
150 /* CLAWS subject threading:
152 in the first round it inserts subject lines in a
153 relation (subject <-> node)
155 the second round finishes the threads by attaching
156 matching subject lines to the one found in the
157 relation. will use the oldest node with the same
158 subject that is not more then thread_by_subject_max_age
159 days old (see subject_relation_lookup)
162 static void subject_relation_insert(GRelation *relation, GNode *node)
167 g_return_if_fail(relation != NULL);
168 g_return_if_fail(node != NULL);
169 msginfo = (MsgInfo *) node->data;
170 g_return_if_fail(msginfo != NULL);
172 subject = msginfo->subject;
175 subject += subject_get_prefix_length(subject);
177 g_relation_insert(relation, subject, node);
180 static GNode *subject_relation_lookup(GRelation *relation, MsgInfo *msginfo)
187 g_return_val_if_fail(relation != NULL, NULL);
189 subject = msginfo->subject;
192 prefix_length = subject_get_prefix_length(subject);
193 if (prefix_length <= 0)
195 subject += prefix_length;
197 tuples = g_relation_select(relation, subject, 0);
201 if (tuples->len > 0) {
203 GNode *relation_node;
204 MsgInfo *relation_msginfo = NULL, *best_msginfo = NULL;
207 /* check all nodes with the same subject to find the best parent */
208 for (i = 0; i < tuples->len; i++) {
209 relation_node = (GNode *) g_tuples_index(tuples, i, 1);
210 relation_msginfo = (MsgInfo *) relation_node->data;
213 /* best node should be the oldest in the found nodes */
214 /* parent node must not be older then msginfo */
215 if ((relation_msginfo->date_t < msginfo->date_t) &&
216 ((best_msginfo == NULL) ||
217 (best_msginfo->date_t > relation_msginfo->date_t)))
220 /* parent node must not be more then thread_by_subject_max_age
221 days older then msginfo */
222 if (abs(difftime(msginfo->date_t, relation_msginfo->date_t)) >
223 prefs_common.thread_by_subject_max_age * 3600 * 24)
226 /* can add new tests for all matching
227 nodes found by subject */
230 node = relation_node;
231 best_msginfo = relation_msginfo;
236 g_tuples_destroy(tuples);
240 /* return the reversed thread tree */
241 GNode *procmsg_get_thread_tree(GSList *mlist)
243 GNode *root, *parent, *node, *next;
244 GHashTable *msgid_table;
245 GRelation *subject_relation;
249 START_TIMING("procmsg_get_thread_tree");
250 root = g_node_new(NULL);
251 msgid_table = g_hash_table_new(g_str_hash, g_str_equal);
252 subject_relation = g_relation_new(2);
253 g_relation_index(subject_relation, 0, g_str_hash, g_str_equal);
255 for (; mlist != NULL; mlist = mlist->next) {
256 msginfo = (MsgInfo *)mlist->data;
259 if (msginfo->inreplyto) {
260 parent = g_hash_table_lookup(msgid_table, msginfo->inreplyto);
261 if (parent == NULL) {
265 node = g_node_insert_data_before
266 (parent, parent == root ? parent->children : NULL,
268 if ((msgid = msginfo->msgid) && g_hash_table_lookup(msgid_table, msgid) == NULL)
269 g_hash_table_insert(msgid_table, (gchar *)msgid, node);
271 /* CLAWS: add subject to relation (without prefix) */
272 if (prefs_common.thread_by_subject) {
273 subject_relation_insert(subject_relation, node);
277 /* complete the unfinished threads */
278 for (node = root->children; node != NULL; ) {
280 msginfo = (MsgInfo *)node->data;
283 if (msginfo->inreplyto)
284 parent = g_hash_table_lookup(msgid_table, msginfo->inreplyto);
286 /* try looking for the indirect parent */
287 if (!parent && msginfo->references) {
288 for (reflist = msginfo->references;
289 reflist != NULL; reflist = reflist->next)
290 if ((parent = g_hash_table_lookup
291 (msgid_table, reflist->data)) != NULL)
295 /* node should not be the parent, and node should not
296 be an ancestor of parent (circular reference) */
297 if (parent && parent != node &&
298 !g_node_is_ancestor(node, parent)) {
301 (parent, parent->children, node);
307 if (prefs_common.thread_by_subject) {
308 START_TIMING("procmsg_get_thread_tree(1)");
309 for (node = root->children; node && node != NULL;) {
311 msginfo = (MsgInfo *) node->data;
313 parent = subject_relation_lookup(subject_relation, msginfo);
315 /* the node may already be threaded by IN-REPLY-TO, so go up
317 find the parent node */
318 if (parent != NULL) {
319 if (g_node_is_ancestor(node, parent))
327 g_node_append(parent, node);
335 g_relation_destroy(subject_relation);
336 g_hash_table_destroy(msgid_table);
341 gint procmsg_move_messages(GSList *mlist)
343 GSList *cur, *movelist = NULL;
345 FolderItem *dest = NULL;
347 gboolean finished = TRUE;
348 if (!mlist) return 0;
350 folder_item_update_freeze();
353 for (cur = mlist; cur != NULL; cur = cur->next) {
354 msginfo = (MsgInfo *)cur->data;
355 if (!msginfo->to_folder) {
361 dest = msginfo->to_folder;
362 movelist = g_slist_prepend(movelist, msginfo);
363 } else if (dest == msginfo->to_folder) {
364 movelist = g_slist_prepend(movelist, msginfo);
368 procmsg_msginfo_set_to_folder(msginfo, NULL);
371 movelist = g_slist_reverse(movelist);
372 retval |= folder_item_move_msgs(dest, movelist);
373 g_slist_free(movelist);
376 if (finished == FALSE) {
382 folder_item_update_thaw();
386 void procmsg_copy_messages(GSList *mlist)
388 GSList *cur, *copylist = NULL;
390 FolderItem *dest = NULL;
391 gboolean finished = TRUE;
394 folder_item_update_freeze();
397 for (cur = mlist; cur != NULL; cur = cur->next) {
398 msginfo = (MsgInfo *)cur->data;
399 if (!msginfo->to_folder) {
405 dest = msginfo->to_folder;
406 copylist = g_slist_prepend(copylist, msginfo);
407 } else if (dest == msginfo->to_folder) {
408 copylist = g_slist_prepend(copylist, msginfo);
412 procmsg_msginfo_set_to_folder(msginfo, NULL);
415 copylist = g_slist_reverse(copylist);
416 folder_item_copy_msgs(dest, copylist);
417 g_slist_free(copylist);
420 if (finished == FALSE) {
426 folder_item_update_thaw();
429 gchar *procmsg_get_message_file_path(MsgInfo *msginfo)
433 g_return_val_if_fail(msginfo != NULL, NULL);
435 if (msginfo->plaintext_file)
436 file = g_strdup(msginfo->plaintext_file);
438 file = folder_item_fetch_msg(msginfo->folder, msginfo->msgnum);
444 gchar *procmsg_get_message_file(MsgInfo *msginfo)
446 gchar *filename = NULL;
448 g_return_val_if_fail(msginfo != NULL, NULL);
450 filename = folder_item_fetch_msg(msginfo->folder, msginfo->msgnum);
452 debug_print("can't fetch message %d\n", msginfo->msgnum);
457 gchar *procmsg_get_message_file_full(MsgInfo *msginfo, gboolean headers, gboolean body)
459 gchar *filename = NULL;
461 g_return_val_if_fail(msginfo != NULL, NULL);
463 filename = folder_item_fetch_msg_full(msginfo->folder, msginfo->msgnum,
466 debug_print("can't fetch message %d\n", msginfo->msgnum);
471 GSList *procmsg_get_message_file_list(GSList *mlist)
473 GSList *file_list = NULL;
475 MsgFileInfo *fileinfo;
478 while (mlist != NULL) {
479 msginfo = (MsgInfo *)mlist->data;
480 file = procmsg_get_message_file(msginfo);
482 procmsg_message_file_list_free(file_list);
485 fileinfo = g_new(MsgFileInfo, 1);
486 fileinfo->msginfo = procmsg_msginfo_new_ref(msginfo);
487 fileinfo->file = file;
488 fileinfo->flags = g_new(MsgFlags, 1);
489 *fileinfo->flags = msginfo->flags;
490 file_list = g_slist_prepend(file_list, fileinfo);
494 file_list = g_slist_reverse(file_list);
499 void procmsg_message_file_list_free(MsgInfoList *file_list)
502 MsgFileInfo *fileinfo;
504 for (cur = file_list; cur != NULL; cur = cur->next) {
505 fileinfo = (MsgFileInfo *)cur->data;
506 procmsg_msginfo_free(fileinfo->msginfo);
507 g_free(fileinfo->file);
508 g_free(fileinfo->flags);
512 g_slist_free(file_list);
515 FILE *procmsg_open_message(MsgInfo *msginfo)
520 g_return_val_if_fail(msginfo != NULL, NULL);
522 file = procmsg_get_message_file_path(msginfo);
523 g_return_val_if_fail(file != NULL, NULL);
525 if (!is_file_exist(file)) {
527 file = procmsg_get_message_file(msginfo);
532 if ((fp = g_fopen(file, "rb")) == NULL) {
533 FILE_OP_ERROR(file, "fopen");
540 if (MSG_IS_QUEUED(msginfo->flags) || MSG_IS_DRAFT(msginfo->flags)) {
543 while (fgets(buf, sizeof(buf), fp) != NULL)
544 if (buf[0] == '\r' || buf[0] == '\n') break;
550 gboolean procmsg_msg_exist(MsgInfo *msginfo)
555 if (!msginfo) return FALSE;
557 path = folder_item_get_path(msginfo->folder);
559 ret = !folder_item_is_msg_changed(msginfo->folder, msginfo);
565 void procmsg_get_filter_keyword(MsgInfo *msginfo, gchar **header, gchar **key,
566 PrefsFilterType type)
568 static HeaderEntry hentry[] = {{"X-BeenThere:", NULL, TRUE},
569 {"X-ML-Name:", NULL, TRUE},
570 {"X-List:", NULL, TRUE},
571 {"X-Mailing-list:", NULL, TRUE},
572 {"List-Id:", NULL, TRUE},
573 {"X-Sequence:", NULL, TRUE},
574 {"Sender:", NULL, TRUE},
575 {"List-Post:", NULL, TRUE},
576 {NULL, NULL, FALSE}};
582 H_X_MAILING_LIST = 3,
591 g_return_if_fail(msginfo != NULL);
592 g_return_if_fail(header != NULL);
593 g_return_if_fail(key != NULL);
602 if ((fp = procmsg_open_message(msginfo)) == NULL)
604 procheader_get_header_fields(fp, hentry);
607 #define SET_FILTER_KEY(hstr, idx) \
609 *header = g_strdup(hstr); \
610 *key = hentry[idx].body; \
611 hentry[idx].body = NULL; \
614 if (hentry[H_X_BEENTHERE].body != NULL) {
615 SET_FILTER_KEY("header \"X-BeenThere\"", H_X_BEENTHERE);
616 } else if (hentry[H_X_ML_NAME].body != NULL) {
617 SET_FILTER_KEY("header \"X-ML-Name\"", H_X_ML_NAME);
618 } else if (hentry[H_X_LIST].body != NULL) {
619 SET_FILTER_KEY("header \"X-List\"", H_X_LIST);
620 } else if (hentry[H_X_MAILING_LIST].body != NULL) {
621 SET_FILTER_KEY("header \"X-Mailing-List\"", H_X_MAILING_LIST);
622 } else if (hentry[H_LIST_ID].body != NULL) {
623 SET_FILTER_KEY("header \"List-Id\"", H_LIST_ID);
624 extract_list_id_str(*key);
625 } else if (hentry[H_X_SEQUENCE].body != NULL) {
628 SET_FILTER_KEY("X-Sequence", H_X_SEQUENCE);
631 while (*p != '\0' && !g_ascii_isspace(*p)) p++;
632 while (g_ascii_isspace(*p)) p++;
633 if (g_ascii_isdigit(*p)) {
639 } else if (hentry[H_SENDER].body != NULL) {
640 SET_FILTER_KEY("header \"Sender\"", H_SENDER);
641 } else if (hentry[H_LIST_POST].body != NULL) {
642 SET_FILTER_KEY("header \"List-Post\"", H_LIST_POST);
643 } else if (msginfo->to) {
644 *header = g_strdup("to");
645 *key = g_strdup(msginfo->to);
646 } else if (msginfo->subject) {
647 *header = g_strdup("subject");
648 *key = g_strdup(msginfo->subject);
651 #undef SET_FILTER_KEY
653 g_free(hentry[H_X_BEENTHERE].body);
654 hentry[H_X_BEENTHERE].body = NULL;
655 g_free(hentry[H_X_ML_NAME].body);
656 hentry[H_X_ML_NAME].body = NULL;
657 g_free(hentry[H_X_LIST].body);
658 hentry[H_X_LIST].body = NULL;
659 g_free(hentry[H_X_MAILING_LIST].body);
660 hentry[H_X_MAILING_LIST].body = NULL;
661 g_free(hentry[H_LIST_ID].body);
662 hentry[H_LIST_ID].body = NULL;
663 g_free(hentry[H_SENDER].body);
664 hentry[H_SENDER].body = NULL;
665 g_free(hentry[H_LIST_POST].body);
666 hentry[H_LIST_POST].body = NULL;
670 *header = g_strdup("from");
671 *key = g_strdup(msginfo->from);
674 *header = g_strdup("to");
675 *key = g_strdup(msginfo->to);
677 case FILTER_BY_SUBJECT:
678 *header = g_strdup("subject");
679 *key = g_strdup(msginfo->subject);
686 void procmsg_empty_trash(FolderItem *trash)
691 (trash->stype != F_TRASH &&
692 !folder_has_parent_of_type(trash, F_TRASH)))
695 if (trash && trash->total_msgs > 0) {
696 GSList *mlist = folder_item_get_msg_list(trash);
698 for (cur = mlist ; cur != NULL ; cur = cur->next) {
699 MsgInfo * msginfo = (MsgInfo *) cur->data;
700 if (MSG_IS_LOCKED(msginfo->flags))
702 if (msginfo->total_size != 0 &&
703 msginfo->size != (off_t)msginfo->total_size)
704 partial_mark_for_delete(msginfo);
706 procmsg_msginfo_free(msginfo);
709 folder_item_remove_all_msg(trash);
712 if (!trash->node || !trash->node->children)
715 node = trash->node->children;
716 while (node != NULL) {
718 procmsg_empty_trash(FOLDER_ITEM(node->data));
723 void procmsg_empty_all_trash(void)
728 for (cur = folder_get_list(); cur != NULL; cur = cur->next) {
729 Folder *folder = FOLDER(cur->data);
730 trash = folder->trash;
731 procmsg_empty_trash(trash);
732 if (folder->account && folder->account->set_trash_folder &&
733 folder_find_item_from_identifier(folder->account->trash_folder))
735 folder_find_item_from_identifier(folder->account->trash_folder));
739 static PrefsAccount *procmsg_get_account_from_file(const gchar *file)
741 PrefsAccount *mailac = NULL;
745 static HeaderEntry qentry[] = {{"S:", NULL, FALSE},
746 {"SSV:", NULL, FALSE},
748 {"NG:", NULL, FALSE},
749 {"MAID:", NULL, FALSE},
750 {"NAID:", NULL, FALSE},
751 {"SCF:", NULL, FALSE},
752 {"RMID:", NULL, FALSE},
753 {"FMID:", NULL, FALSE},
754 {"X-Sylpheed-Privacy-System:", NULL, FALSE},
755 {"X-Sylpheed-Encrypt:", NULL, FALSE},
756 {"X-Sylpheed-Encrypt-Data:", NULL, FALSE},
757 {NULL, NULL, FALSE}};
759 g_return_val_if_fail(file != NULL, NULL);
761 if ((fp = g_fopen(file, "rb")) == NULL) {
762 FILE_OP_ERROR(file, "fopen");
766 while ((hnum = procheader_get_one_field(buf, sizeof(buf), fp, qentry))
768 gchar *p = buf + strlen(qentry[hnum].name);
770 if (hnum == Q_MAIL_ACCOUNT_ID) {
771 mailac = account_find_from_id(atoi(p));
779 static GSList *procmsg_list_sort_by_account(FolderItem *queue, GSList *list)
781 GSList *result = NULL;
783 PrefsAccount *last_account = NULL;
786 gboolean nothing_to_sort = TRUE;
791 orig = g_slist_copy(list);
793 msg = (MsgInfo *)orig->data;
795 for (cur = orig; cur; cur = cur->next)
796 debug_print("sort before %s\n", ((MsgInfo *)cur->data)->from);
801 nothing_to_sort = TRUE;
805 PrefsAccount *ac = NULL;
806 msg = (MsgInfo *)cur->data;
807 file = folder_item_fetch_msg(queue, msg->msgnum);
808 ac = procmsg_get_account_from_file(file);
811 if (last_account == NULL || (ac != NULL && ac == last_account)) {
812 result = g_slist_append(result, msg);
813 orig = g_slist_remove(orig, msg);
815 nothing_to_sort = FALSE;
821 if (orig || g_slist_length(orig)) {
822 if (!last_account && nothing_to_sort) {
823 /* can't find an account for the rest of the list */
826 result = g_slist_append(result, cur->data);
837 for (cur = result; cur; cur = cur->next)
838 debug_print("sort after %s\n", ((MsgInfo *)cur->data)->from);
845 static gboolean procmsg_is_last_for_account(FolderItem *queue, MsgInfo *msginfo, GSList *elem)
847 gchar *file = folder_item_fetch_msg(queue, msginfo->msgnum);
848 PrefsAccount *ac = procmsg_get_account_from_file(file);
851 for (cur = elem; cur; cur = cur->next) {
852 MsgInfo *cur_msginfo = (MsgInfo *)cur->data;
853 file = folder_item_fetch_msg(queue, cur_msginfo->msgnum);
855 if (cur_msginfo != msginfo && !MSG_IS_LOCKED(cur_msginfo->flags)) {
856 if (procmsg_get_account_from_file(file) == ac) {
868 *\brief Send messages in queue
870 *\param queue Queue folder to process
871 *\param save_msgs Unused
873 *\return Number of messages sent, negative if an error occurred
874 * positive if no error occurred
876 gint procmsg_send_queue(FolderItem *queue, gboolean save_msgs)
878 gint sent = 0, err = 0;
880 GSList *sorted_list = NULL;
882 static gboolean send_queue_lock = FALSE;
884 if (send_queue_lock) {
885 log_error(_("Already trying to send\n"));
888 send_queue_lock = TRUE;
890 queue = folder_get_default_queue();
891 g_return_val_if_fail(queue != NULL, -1);
893 folder_item_scan(queue);
894 list = folder_item_get_msg_list(queue);
896 /* sort the list per sender account; this helps reusing the same SMTP server */
897 sorted_list = procmsg_list_sort_by_account(queue, list);
899 for (elem = sorted_list; elem != NULL; elem = elem->next) {
903 msginfo = (MsgInfo *)(elem->data);
904 if (!MSG_IS_LOCKED(msginfo->flags)) {
905 file = folder_item_fetch_msg(queue, msginfo->msgnum);
907 if (procmsg_send_message_queue_full(file,
908 !procmsg_is_last_for_account(queue, msginfo, elem)) < 0) {
909 g_warning("Sending queued message %d failed.\n",
914 folder_item_remove_msg(queue, msginfo->msgnum);
919 /* FIXME: supposedly if only one message is locked, and queue
920 * is being flushed, the following free says something like
921 * "freeing msg ## in folder (nil)". */
922 procmsg_msginfo_free(msginfo);
925 g_slist_free(sorted_list);
926 folder_item_scan(queue);
928 if (queue->node && queue->node->children) {
929 node = queue->node->children;
930 while (node != NULL) {
933 res = procmsg_send_queue(FOLDER_ITEM(node->data), save_msgs);
941 send_queue_lock = FALSE;
942 return (err != 0 ? -err : sent);
946 *\brief Determine if a queue folder is empty
948 *\param queue Queue folder to process
950 *\return TRUE if the queue folder is empty, otherwise return FALSE
952 gboolean procmsg_queue_is_empty(FolderItem *queue)
955 gboolean res = FALSE;
957 queue = folder_get_default_queue();
958 g_return_val_if_fail(queue != NULL, TRUE);
960 folder_item_scan(queue);
961 list = folder_item_get_msg_list(queue);
962 res = (list == NULL);
963 procmsg_msg_list_free(list);
967 if (queue->node && queue->node->children) {
968 node = queue->node->children;
969 while (node != NULL) {
971 if (!procmsg_queue_is_empty(FOLDER_ITEM(node->data)))
980 gint procmsg_remove_special_headers(const gchar *in, const gchar *out)
985 if ((fp = g_fopen(in, "rb")) == NULL) {
986 FILE_OP_ERROR(in, "fopen");
989 if ((outfp = g_fopen(out, "wb")) == NULL) {
990 FILE_OP_ERROR(out, "fopen");
994 while (fgets(buf, sizeof(buf), fp) != NULL)
995 if (buf[0] == '\r' || buf[0] == '\n') break;
996 while (fgets(buf, sizeof(buf), fp) != NULL)
1003 gint procmsg_save_to_outbox(FolderItem *outbox, const gchar *file,
1007 MsgInfo *msginfo, *tmp_msginfo;
1008 MsgFlags flag = {0, 0};
1010 debug_print("saving sent message...\n");
1013 outbox = folder_get_default_outbox();
1014 g_return_val_if_fail(outbox != NULL, -1);
1016 /* remove queueing headers */
1018 gchar tmp[MAXPATHLEN + 1];
1020 g_snprintf(tmp, sizeof(tmp), "%s%ctmpmsg.out.%08x",
1021 get_rc_dir(), G_DIR_SEPARATOR, (guint) rand());
1023 if (procmsg_remove_special_headers(file, tmp) !=0)
1026 folder_item_scan(outbox);
1027 if ((num = folder_item_add_msg(outbox, tmp, &flag, TRUE)) < 0) {
1028 g_warning("can't save message\n");
1033 folder_item_scan(outbox);
1034 if ((num = folder_item_add_msg
1035 (outbox, file, &flag, FALSE)) < 0) {
1036 g_warning("can't save message\n");
1041 msginfo = folder_item_get_msginfo(outbox, num); /* refcnt++ */
1042 tmp_msginfo = procmsg_msginfo_get_full_info(msginfo); /* refcnt++ */
1043 if (msginfo != NULL) {
1044 procmsg_msginfo_unset_flags(msginfo, ~0, 0);
1045 procmsg_msginfo_free(msginfo); /* refcnt-- */
1046 /* tmp_msginfo == msginfo */
1047 if (tmp_msginfo && (msginfo->dispositionnotificationto ||
1048 msginfo->returnreceiptto)) {
1049 procmsg_msginfo_set_flags(msginfo, MSG_RETRCPT_SENT, 0);
1050 procmsg_msginfo_free(msginfo); /* refcnt-- */
1057 void procmsg_print_message(MsgInfo *msginfo, const gchar *cmdline)
1059 static const gchar *def_cmd = "lpr %s";
1060 static guint id = 0;
1066 g_return_if_fail(msginfo);
1068 if (procmime_msginfo_is_encrypted(msginfo))
1069 tmpfp = procmime_get_first_encrypted_text_content(msginfo);
1071 tmpfp = procmime_get_first_text_content(msginfo);
1072 if (tmpfp == NULL) {
1073 g_warning("Can't get text part\n");
1077 prtmp = g_strdup_printf("%s%cprinttmp.%08x",
1078 get_mime_tmp_dir(), G_DIR_SEPARATOR, id++);
1080 if ((prfp = g_fopen(prtmp, "wb")) == NULL) {
1081 FILE_OP_ERROR(prtmp, "fopen");
1087 if (msginfo->date) fprintf(prfp, "Date: %s\n", msginfo->date);
1088 if (msginfo->from) fprintf(prfp, "From: %s\n", msginfo->from);
1089 if (msginfo->to) fprintf(prfp, "To: %s\n", msginfo->to);
1090 if (msginfo->cc) fprintf(prfp, "Cc: %s\n", msginfo->cc);
1091 if (msginfo->newsgroups)
1092 fprintf(prfp, "Newsgroups: %s\n", msginfo->newsgroups);
1093 if (msginfo->subject) fprintf(prfp, "Subject: %s\n", msginfo->subject);
1096 while (fgets(buf, sizeof(buf), tmpfp) != NULL)
1102 if (cmdline && (p = strchr(cmdline, '%')) && *(p + 1) == 's' &&
1103 !strchr(p + 2, '%'))
1104 g_snprintf(buf, sizeof(buf) - 1, cmdline, prtmp);
1107 g_warning("Print command line is invalid: '%s'\n",
1109 g_snprintf(buf, sizeof(buf) - 1, def_cmd, prtmp);
1115 if (buf[strlen(buf) - 1] != '&') strcat(buf, "&");
1119 MsgInfo *procmsg_msginfo_new_ref(MsgInfo *msginfo)
1126 MsgInfo *procmsg_msginfo_new(void)
1128 MsgInfo *newmsginfo;
1130 newmsginfo = g_new0(MsgInfo, 1);
1131 newmsginfo->refcnt = 1;
1136 MsgInfo *procmsg_msginfo_copy(MsgInfo *msginfo)
1138 MsgInfo *newmsginfo;
1141 if (msginfo == NULL) return NULL;
1143 newmsginfo = g_new0(MsgInfo, 1);
1145 newmsginfo->refcnt = 1;
1147 #define MEMBCOPY(mmb) newmsginfo->mmb = msginfo->mmb
1148 #define MEMBDUP(mmb) newmsginfo->mmb = msginfo->mmb ? \
1149 g_strdup(msginfo->mmb) : NULL
1164 MEMBDUP(newsgroups);
1171 MEMBCOPY(to_folder);
1175 MEMBDUP(dispositionnotificationto);
1176 MEMBDUP(returnreceiptto);
1178 refs = msginfo->references;
1179 for (refs = msginfo->references; refs != NULL; refs = refs->next) {
1180 newmsginfo->references = g_slist_prepend
1181 (newmsginfo->references, g_strdup(refs->data));
1183 newmsginfo->references = g_slist_reverse(newmsginfo->references);
1186 MEMBDUP(plaintext_file);
1191 MsgInfo *procmsg_msginfo_get_full_info(MsgInfo *msginfo)
1193 MsgInfo *full_msginfo;
1196 if (msginfo == NULL) return NULL;
1198 file = procmsg_get_message_file_path(msginfo);
1199 if (!file || !is_file_exist(file)) {
1201 file = procmsg_get_message_file(msginfo);
1203 if (!file || !is_file_exist(file)) {
1204 g_warning("procmsg_msginfo_get_full_info(): can't get message file.\n");
1208 full_msginfo = procheader_parse_file(file, msginfo->flags, TRUE, FALSE);
1210 if (!full_msginfo) return NULL;
1212 /* CLAWS: make sure we add the missing members; see:
1213 * procheader.c::procheader_get_headernames() */
1214 if (!msginfo->list_post)
1215 msginfo->list_post = g_strdup(full_msginfo->list_post);
1216 if (!msginfo->list_subscribe)
1217 msginfo->list_subscribe = g_strdup(full_msginfo->list_subscribe);
1218 if (!msginfo->list_unsubscribe)
1219 msginfo->list_unsubscribe = g_strdup(full_msginfo->list_unsubscribe);
1220 if (!msginfo->list_help)
1221 msginfo->list_help = g_strdup(full_msginfo->list_help);
1222 if (!msginfo->list_archive)
1223 msginfo->list_archive= g_strdup(full_msginfo->list_archive);
1224 if (!msginfo->list_owner)
1225 msginfo->list_owner = g_strdup(full_msginfo->list_owner);
1226 if (!msginfo->xface)
1227 msginfo->xface = g_strdup(full_msginfo->xface);
1229 msginfo->face = g_strdup(full_msginfo->face);
1230 if (!msginfo->dispositionnotificationto)
1231 msginfo->dispositionnotificationto =
1232 g_strdup(full_msginfo->dispositionnotificationto);
1233 if (!msginfo->returnreceiptto)
1234 msginfo->returnreceiptto = g_strdup
1235 (full_msginfo->returnreceiptto);
1236 if (!msginfo->partial_recv && full_msginfo->partial_recv)
1237 msginfo->partial_recv = g_strdup
1238 (full_msginfo->partial_recv);
1239 msginfo->total_size = full_msginfo->total_size;
1240 if (!msginfo->account_server && full_msginfo->account_server)
1241 msginfo->account_server = g_strdup
1242 (full_msginfo->account_server);
1243 if (!msginfo->account_login && full_msginfo->account_login)
1244 msginfo->account_login = g_strdup
1245 (full_msginfo->account_login);
1246 msginfo->planned_download = full_msginfo->planned_download;
1247 procmsg_msginfo_free(full_msginfo);
1249 return procmsg_msginfo_new_ref(msginfo);
1252 void procmsg_msginfo_free(MsgInfo *msginfo)
1254 if (msginfo == NULL) return;
1257 if (msginfo->refcnt > 0)
1260 if (msginfo->to_folder) {
1261 msginfo->to_folder->op_count--;
1262 folder_item_update(msginfo->to_folder, F_ITEM_UPDATE_MSGCNT);
1265 g_free(msginfo->fromspace);
1266 g_free(msginfo->returnreceiptto);
1267 g_free(msginfo->dispositionnotificationto);
1268 g_free(msginfo->xface);
1269 g_free(msginfo->face);
1271 g_free(msginfo->fromname);
1273 g_free(msginfo->date);
1274 g_free(msginfo->from);
1275 g_free(msginfo->to);
1276 g_free(msginfo->cc);
1277 g_free(msginfo->newsgroups);
1278 g_free(msginfo->subject);
1279 g_free(msginfo->msgid);
1280 g_free(msginfo->inreplyto);
1281 g_free(msginfo->xref);
1283 g_free(msginfo->list_post);
1284 g_free(msginfo->list_subscribe);
1285 g_free(msginfo->list_unsubscribe);
1286 g_free(msginfo->list_help);
1287 g_free(msginfo->list_archive);
1288 g_free(msginfo->list_owner);
1290 g_free(msginfo->partial_recv);
1291 g_free(msginfo->account_server);
1292 g_free(msginfo->account_login);
1294 slist_free_strings(msginfo->references);
1295 g_slist_free(msginfo->references);
1297 g_free(msginfo->plaintext_file);
1302 guint procmsg_msginfo_memusage(MsgInfo *msginfo)
1307 memusage += sizeof(MsgInfo);
1308 if (msginfo->fromname)
1309 memusage += strlen(msginfo->fromname);
1311 memusage += strlen(msginfo->date);
1313 memusage += strlen(msginfo->from);
1315 memusage += strlen(msginfo->to);
1317 memusage += strlen(msginfo->cc);
1318 if (msginfo->newsgroups)
1319 memusage += strlen(msginfo->newsgroups);
1320 if (msginfo->subject)
1321 memusage += strlen(msginfo->subject);
1323 memusage += strlen(msginfo->msgid);
1324 if (msginfo->inreplyto)
1325 memusage += strlen(msginfo->inreplyto);
1327 memusage += strlen(msginfo->xface);
1329 memusage += strlen(msginfo->face);
1330 if (msginfo->dispositionnotificationto)
1331 memusage += strlen(msginfo->dispositionnotificationto);
1332 if (msginfo->returnreceiptto)
1333 memusage += strlen(msginfo->returnreceiptto);
1334 for (refs = msginfo->references; refs; refs=refs->next) {
1335 gchar *r = (gchar *)refs->data;
1336 memusage += r?strlen(r):0;
1338 if (msginfo->fromspace)
1339 memusage += strlen(msginfo->fromspace);
1344 gint procmsg_cmp_msgnum_for_sort(gconstpointer a, gconstpointer b)
1346 const MsgInfo *msginfo1 = a;
1347 const MsgInfo *msginfo2 = b;
1354 return msginfo1->msgnum - msginfo2->msgnum;
1357 static gint procmsg_send_message_queue_full(const gchar *file, gboolean keep_session)
1359 static HeaderEntry qentry[] = {{"S:", NULL, FALSE},
1360 {"SSV:", NULL, FALSE},
1361 {"R:", NULL, FALSE},
1362 {"NG:", NULL, FALSE},
1363 {"MAID:", NULL, FALSE},
1364 {"NAID:", NULL, FALSE},
1365 {"SCF:", NULL, FALSE},
1366 {"RMID:", NULL, FALSE},
1367 {"FMID:", NULL, FALSE},
1368 {"X-Sylpheed-Privacy-System:", NULL, FALSE},
1369 {"X-Sylpheed-Encrypt:", NULL, FALSE},
1370 {"X-Sylpheed-Encrypt-Data:", NULL, FALSE},
1371 {NULL, NULL, FALSE}};
1374 gint mailval = 0, newsval = 0;
1376 gchar *smtpserver = NULL;
1377 GSList *to_list = NULL;
1378 GSList *newsgroup_list = NULL;
1379 gchar *savecopyfolder = NULL;
1380 gchar *replymessageid = NULL;
1381 gchar *fwdmessageid = NULL;
1382 gchar *privacy_system = NULL;
1383 gboolean encrypt = FALSE;
1384 gchar *encrypt_data = NULL;
1385 gchar buf[BUFFSIZE];
1387 PrefsAccount *mailac = NULL, *newsac = NULL;
1388 gboolean save_clear_text = TRUE;
1389 gchar *tmp_enc_file = NULL;
1393 g_return_val_if_fail(file != NULL, -1);
1395 if ((fp = g_fopen(file, "rb")) == NULL) {
1396 FILE_OP_ERROR(file, "fopen");
1400 while ((hnum = procheader_get_one_field(buf, sizeof(buf), fp, qentry))
1402 gchar *p = buf + strlen(qentry[hnum].name);
1410 if (smtpserver == NULL)
1411 smtpserver = g_strdup(p);
1414 to_list = address_list_append(to_list, p);
1417 newsgroup_list = newsgroup_list_append(newsgroup_list, p);
1419 case Q_MAIL_ACCOUNT_ID:
1420 mailac = account_find_from_id(atoi(p));
1422 case Q_NEWS_ACCOUNT_ID:
1423 newsac = account_find_from_id(atoi(p));
1425 case Q_SAVE_COPY_FOLDER:
1426 if (savecopyfolder == NULL)
1427 savecopyfolder = g_strdup(p);
1429 case Q_REPLY_MESSAGE_ID:
1430 if (replymessageid == NULL)
1431 replymessageid = g_strdup(p);
1433 case Q_FWD_MESSAGE_ID:
1434 if (fwdmessageid == NULL)
1435 fwdmessageid = g_strdup(p);
1437 case Q_PRIVACY_SYSTEM:
1438 if (privacy_system == NULL)
1439 privacy_system = g_strdup(p);
1445 case Q_ENCRYPT_DATA:
1446 if (encrypt_data == NULL)
1447 encrypt_data = g_strdup(p);
1451 filepos = ftell(fp);
1456 if (mailac && mailac->save_encrypted_as_clear_text
1457 && !mailac->encrypt_to_self)
1458 save_clear_text = TRUE;
1460 save_clear_text = FALSE;
1465 mimeinfo = procmime_scan_queue_file(file);
1466 if (!privacy_encrypt(privacy_system, mimeinfo, encrypt_data)
1467 || (fp = my_tmpfile()) == NULL
1468 || procmime_write_mimeinfo(mimeinfo, fp) < 0) {
1471 procmime_mimeinfo_free_all(mimeinfo);
1474 slist_free_strings(to_list);
1475 g_slist_free(to_list);
1476 slist_free_strings(newsgroup_list);
1477 g_slist_free(newsgroup_list);
1478 g_free(savecopyfolder);
1479 g_free(replymessageid);
1480 g_free(fwdmessageid);
1481 g_free(privacy_system);
1482 g_free(encrypt_data);
1487 if (!save_clear_text) {
1488 gchar *content = NULL;
1489 FILE *tmpfp = get_tmpfile_in_dir(get_mime_tmp_dir(), &tmp_enc_file);
1493 content = file_read_stream_to_str(fp);
1496 str_write_to_file(content, tmp_enc_file);
1499 g_warning("couldn't get tempfile\n");
1503 procmime_mimeinfo_free_all(mimeinfo);
1509 debug_print("Sending message by mail\n");
1511 g_warning("Queued message header is broken.\n");
1513 } else if (mailac && mailac->use_mail_command &&
1514 mailac->mail_command && (* mailac->mail_command)) {
1515 mailval = send_message_local(mailac->mail_command, fp);
1519 mailac = account_find_from_smtp_server(from, smtpserver);
1521 g_warning("Account not found. "
1522 "Using current account...\n");
1523 mailac = cur_account;
1528 mailval = send_message_smtp_full(mailac, to_list, fp, keep_session);
1530 PrefsAccount tmp_ac;
1532 g_warning("Account not found.\n");
1534 memset(&tmp_ac, 0, sizeof(PrefsAccount));
1535 tmp_ac.address = from;
1536 tmp_ac.smtp_server = smtpserver;
1537 tmp_ac.smtpport = SMTP_PORT;
1538 mailval = send_message_smtp(&tmp_ac, to_list, fp);
1541 } else if (!to_list && !newsgroup_list)
1545 fseek(fp, filepos, SEEK_SET);
1546 if (newsgroup_list && (mailval == 0)) {
1551 /* write to temporary file */
1552 tmp = g_strdup_printf("%s%ctmp%d", g_get_tmp_dir(),
1553 G_DIR_SEPARATOR, (gint)file);
1554 if ((tmpfp = g_fopen(tmp, "wb")) == NULL) {
1555 FILE_OP_ERROR(tmp, "fopen");
1557 alertpanel_error(_("Could not create temporary file for news sending."));
1559 if (change_file_mode_rw(tmpfp, tmp) < 0) {
1560 FILE_OP_ERROR(tmp, "chmod");
1561 g_warning("can't change file mode\n");
1564 while ((newsval == 0) && fgets(buf, sizeof(buf), fp) != NULL) {
1565 if (fputs(buf, tmpfp) == EOF) {
1566 FILE_OP_ERROR(tmp, "fputs");
1568 alertpanel_error(_("Error when writing temporary file for news sending."));
1574 debug_print("Sending message by news\n");
1576 folder = FOLDER(newsac->folder);
1578 newsval = news_post(folder, tmp);
1580 alertpanel_error(_("Error occurred while posting the message to %s ."),
1581 newsac->nntp_server);
1591 /* save message to outbox */
1592 if (mailval == 0 && newsval == 0 && savecopyfolder) {
1595 debug_print("saving sent message...\n");
1597 outbox = folder_find_item_from_identifier(savecopyfolder);
1599 outbox = folder_get_default_outbox();
1601 if (save_clear_text || tmp_enc_file == NULL) {
1602 procmsg_save_to_outbox(outbox, file, TRUE);
1604 procmsg_save_to_outbox(outbox, tmp_enc_file, FALSE);
1608 if (tmp_enc_file != NULL) {
1609 g_unlink(tmp_enc_file);
1611 tmp_enc_file = NULL;
1614 if (replymessageid != NULL || fwdmessageid != NULL) {
1618 if (replymessageid != NULL)
1619 tokens = g_strsplit(replymessageid, "\t", 0);
1621 tokens = g_strsplit(fwdmessageid, "\t", 0);
1622 item = folder_find_item_from_identifier(tokens[0]);
1624 /* check if queued message has valid folder and message id */
1625 if (item != NULL && tokens[2] != NULL) {
1628 msginfo = folder_item_get_msginfo(item, atoi(tokens[1]));
1630 /* check if referring message exists and has a message id */
1631 if ((msginfo != NULL) &&
1632 (msginfo->msgid != NULL) &&
1633 (strcmp(msginfo->msgid, tokens[2]) != 0)) {
1634 procmsg_msginfo_free(msginfo);
1638 if (msginfo == NULL) {
1639 msginfo = folder_item_get_msginfo_by_msgid(item, tokens[2]);
1642 if (msginfo != NULL) {
1643 if (replymessageid != NULL) {
1644 procmsg_msginfo_unset_flags(msginfo, MSG_FORWARDED, 0);
1645 procmsg_msginfo_set_flags(msginfo, MSG_REPLIED, 0);
1647 procmsg_msginfo_unset_flags(msginfo, MSG_REPLIED, 0);
1648 procmsg_msginfo_set_flags(msginfo, MSG_FORWARDED, 0);
1650 procmsg_msginfo_free(msginfo);
1658 slist_free_strings(to_list);
1659 g_slist_free(to_list);
1660 slist_free_strings(newsgroup_list);
1661 g_slist_free(newsgroup_list);
1662 g_free(savecopyfolder);
1663 g_free(replymessageid);
1664 g_free(fwdmessageid);
1665 g_free(privacy_system);
1666 g_free(encrypt_data);
1668 return (newsval != 0 ? newsval : mailval);
1671 gint procmsg_send_message_queue(const gchar *file)
1673 return procmsg_send_message_queue_full(file, FALSE);
1676 static void update_folder_msg_counts(FolderItem *item, MsgInfo *msginfo, MsgPermFlags old_flags)
1678 MsgPermFlags new_flags = msginfo->flags.perm_flags;
1681 if (!(old_flags & MSG_NEW) && (new_flags & MSG_NEW)) {
1685 if ((old_flags & MSG_NEW) && !(new_flags & MSG_NEW)) {
1690 if (!(old_flags & MSG_UNREAD) && (new_flags & MSG_UNREAD)) {
1691 item->unread_msgs++;
1692 if (procmsg_msg_has_marked_parent(msginfo))
1693 item->unreadmarked_msgs++;
1696 if ((old_flags & MSG_UNREAD) && !(new_flags & MSG_UNREAD)) {
1697 item->unread_msgs--;
1698 if (procmsg_msg_has_marked_parent(msginfo))
1699 item->unreadmarked_msgs--;
1703 if (!(old_flags & MSG_MARKED) && (new_flags & MSG_MARKED)) {
1704 procmsg_update_unread_children(msginfo, TRUE);
1705 item->marked_msgs++;
1708 if ((old_flags & MSG_MARKED) && !(new_flags & MSG_MARKED)) {
1709 procmsg_update_unread_children(msginfo, FALSE);
1710 item->marked_msgs--;
1714 void procmsg_msginfo_set_flags(MsgInfo *msginfo, MsgPermFlags perm_flags, MsgTmpFlags tmp_flags)
1717 MsgInfoUpdate msginfo_update;
1718 MsgPermFlags perm_flags_new, perm_flags_old;
1719 MsgTmpFlags tmp_flags_old;
1721 g_return_if_fail(msginfo != NULL);
1722 item = msginfo->folder;
1723 g_return_if_fail(item != NULL);
1725 debug_print("Setting flags for message %d in folder %s\n", msginfo->msgnum, item->path);
1727 /* Perm Flags handling */
1728 perm_flags_old = msginfo->flags.perm_flags;
1729 perm_flags_new = msginfo->flags.perm_flags | perm_flags;
1730 if ((perm_flags & MSG_IGNORE_THREAD) || (perm_flags_old & MSG_IGNORE_THREAD)) {
1731 perm_flags_new &= ~(MSG_NEW | MSG_UNREAD);
1734 if (perm_flags_old != perm_flags_new) {
1735 folder_item_change_msg_flags(msginfo->folder, msginfo, perm_flags_new);
1737 update_folder_msg_counts(item, msginfo, perm_flags_old);
1741 /* Tmp flags handling */
1742 tmp_flags_old = msginfo->flags.tmp_flags;
1743 msginfo->flags.tmp_flags |= tmp_flags;
1745 /* update notification */
1746 if ((perm_flags_old != perm_flags_new) || (tmp_flags_old != msginfo->flags.tmp_flags)) {
1747 msginfo_update.msginfo = msginfo;
1748 msginfo_update.flags = MSGINFO_UPDATE_FLAGS;
1749 hooks_invoke(MSGINFO_UPDATE_HOOKLIST, &msginfo_update);
1750 folder_item_update(msginfo->folder, F_ITEM_UPDATE_MSGCNT);
1754 void procmsg_msginfo_unset_flags(MsgInfo *msginfo, MsgPermFlags perm_flags, MsgTmpFlags tmp_flags)
1757 MsgInfoUpdate msginfo_update;
1758 MsgPermFlags perm_flags_new, perm_flags_old;
1759 MsgTmpFlags tmp_flags_old;
1761 g_return_if_fail(msginfo != NULL);
1762 item = msginfo->folder;
1763 g_return_if_fail(item != NULL);
1765 debug_print("Unsetting flags for message %d in folder %s\n", msginfo->msgnum, item->path);
1767 /* Perm Flags handling */
1768 perm_flags_old = msginfo->flags.perm_flags;
1769 perm_flags_new = msginfo->flags.perm_flags & ~perm_flags;
1771 if (perm_flags_old != perm_flags_new) {
1772 folder_item_change_msg_flags(msginfo->folder, msginfo, perm_flags_new);
1774 update_folder_msg_counts(item, msginfo, perm_flags_old);
1777 /* Tmp flags hanlding */
1778 tmp_flags_old = msginfo->flags.tmp_flags;
1779 msginfo->flags.tmp_flags &= ~tmp_flags;
1781 /* update notification */
1782 if ((perm_flags_old != perm_flags_new) || (tmp_flags_old != msginfo->flags.tmp_flags)) {
1783 msginfo_update.msginfo = msginfo;
1784 msginfo_update.flags = MSGINFO_UPDATE_FLAGS;
1785 hooks_invoke(MSGINFO_UPDATE_HOOKLIST, &msginfo_update);
1786 folder_item_update(msginfo->folder, F_ITEM_UPDATE_MSGCNT);
1790 void procmsg_msginfo_change_flags(MsgInfo *msginfo,
1791 MsgPermFlags add_perm_flags, MsgTmpFlags add_tmp_flags,
1792 MsgPermFlags rem_perm_flags, MsgTmpFlags rem_tmp_flags)
1795 MsgInfoUpdate msginfo_update;
1796 MsgPermFlags perm_flags_new, perm_flags_old;
1797 MsgTmpFlags tmp_flags_old;
1799 g_return_if_fail(msginfo != NULL);
1800 item = msginfo->folder;
1801 g_return_if_fail(item != NULL);
1803 debug_print("Changing flags for message %d in folder %s\n", msginfo->msgnum, item->path);
1805 /* Perm Flags handling */
1806 perm_flags_old = msginfo->flags.perm_flags;
1807 perm_flags_new = (msginfo->flags.perm_flags & ~rem_perm_flags) | add_perm_flags;
1808 if ((add_perm_flags & MSG_IGNORE_THREAD) || (perm_flags_old & MSG_IGNORE_THREAD)) {
1809 perm_flags_new &= ~(MSG_NEW | MSG_UNREAD);
1812 if (perm_flags_old != perm_flags_new) {
1813 folder_item_change_msg_flags(msginfo->folder, msginfo, perm_flags_new);
1815 update_folder_msg_counts(item, msginfo, perm_flags_old);
1819 /* Tmp flags handling */
1820 tmp_flags_old = msginfo->flags.tmp_flags;
1821 msginfo->flags.tmp_flags &= ~rem_tmp_flags;
1822 msginfo->flags.tmp_flags |= add_tmp_flags;
1824 /* update notification */
1825 if ((perm_flags_old != perm_flags_new) || (tmp_flags_old != msginfo->flags.tmp_flags)) {
1826 msginfo_update.msginfo = msginfo;
1827 msginfo_update.flags = MSGINFO_UPDATE_FLAGS;
1828 hooks_invoke(MSGINFO_UPDATE_HOOKLIST, &msginfo_update);
1829 folder_item_update(msginfo->folder, F_ITEM_UPDATE_MSGCNT);
1834 *\brief check for flags (e.g. mark) in prior msgs of current thread
1836 *\param info Current message
1837 *\param perm_flags Flags to be checked
1838 *\param parentmsgs Hash of prior msgs to avoid loops
1840 *\return gboolean TRUE if perm_flags are found
1842 gboolean procmsg_msg_has_flagged_parent_real(MsgInfo *info,
1843 MsgPermFlags perm_flags, GHashTable *parentmsgs)
1847 g_return_val_if_fail(info != NULL, FALSE);
1849 if (info != NULL && info->folder != NULL && info->inreplyto != NULL) {
1850 tmp = folder_item_get_msginfo_by_msgid(info->folder,
1852 if (tmp && (tmp->flags.perm_flags & perm_flags)) {
1853 procmsg_msginfo_free(tmp);
1855 } else if (tmp != NULL) {
1858 if (g_hash_table_lookup(parentmsgs, info)) {
1859 debug_print("loop detected: %d\n",
1863 g_hash_table_insert(parentmsgs, info, "1");
1864 result = procmsg_msg_has_flagged_parent_real(
1865 tmp, perm_flags, parentmsgs);
1867 procmsg_msginfo_free(tmp);
1877 *\brief Callback for cleaning up hash of parentmsgs
1879 gboolean parentmsgs_hash_remove(gpointer key,
1887 *\brief Set up list of parentmsgs
1888 * See procmsg_msg_has_flagged_parent_real()
1890 gboolean procmsg_msg_has_flagged_parent(MsgInfo *info, MsgPermFlags perm_flags)
1893 GHashTable *parentmsgs = g_hash_table_new(NULL, NULL);
1895 result = procmsg_msg_has_flagged_parent_real(info, perm_flags, parentmsgs);
1896 g_hash_table_foreach_remove(parentmsgs, parentmsgs_hash_remove, NULL);
1897 g_hash_table_destroy(parentmsgs);
1902 *\brief Check if msgs prior in thread are marked
1903 * See procmsg_msg_has_flagged_parent_real()
1905 gboolean procmsg_msg_has_marked_parent(MsgInfo *info)
1907 return procmsg_msg_has_flagged_parent(info, MSG_MARKED);
1911 GSList *procmsg_find_children_func(MsgInfo *info,
1912 GSList *children, GSList *all)
1916 g_return_val_if_fail(info!=NULL, children);
1917 if (info->msgid == NULL)
1920 for (cur = all; cur != NULL; cur = g_slist_next(cur)) {
1921 MsgInfo *tmp = (MsgInfo *)cur->data;
1922 if (tmp->inreplyto && !strcmp(tmp->inreplyto, info->msgid)) {
1923 /* Check if message is already in the list */
1924 if ((children == NULL) ||
1925 (g_slist_index(children, tmp) == -1)) {
1926 children = g_slist_prepend(children,
1927 procmsg_msginfo_new_ref(tmp));
1928 children = procmsg_find_children_func(tmp,
1937 GSList *procmsg_find_children (MsgInfo *info)
1942 g_return_val_if_fail(info!=NULL, NULL);
1943 all = folder_item_get_msg_list(info->folder);
1944 children = procmsg_find_children_func(info, NULL, all);
1945 if (children != NULL) {
1946 for (cur = all; cur != NULL; cur = g_slist_next(cur)) {
1947 /* this will not free the used pointers
1948 created with procmsg_msginfo_new_ref */
1949 procmsg_msginfo_free((MsgInfo *)cur->data);
1957 void procmsg_update_unread_children(MsgInfo *info, gboolean newly_marked)
1959 GSList *children = procmsg_find_children(info);
1961 for (cur = children; cur != NULL; cur = g_slist_next(cur)) {
1962 MsgInfo *tmp = (MsgInfo *)cur->data;
1963 if(MSG_IS_UNREAD(tmp->flags) && !MSG_IS_IGNORE_THREAD(tmp->flags)) {
1965 info->folder->unreadmarked_msgs++;
1967 info->folder->unreadmarked_msgs--;
1968 folder_item_update(info->folder, F_ITEM_UPDATE_MSGCNT);
1970 procmsg_msginfo_free(tmp);
1972 g_slist_free(children);
1976 * Set the destination folder for a copy or move operation
1978 * \param msginfo The message which's destination folder is changed
1979 * \param to_folder The destination folder for the operation
1981 void procmsg_msginfo_set_to_folder(MsgInfo *msginfo, FolderItem *to_folder)
1983 if(msginfo->to_folder != NULL) {
1984 msginfo->to_folder->op_count--;
1985 folder_item_update(msginfo->to_folder, F_ITEM_UPDATE_MSGCNT);
1987 msginfo->to_folder = to_folder;
1988 if(to_folder != NULL) {
1989 to_folder->op_count++;
1990 folder_item_update(msginfo->to_folder, F_ITEM_UPDATE_MSGCNT);
1995 * Apply filtering actions to the msginfo
1997 * \param msginfo The MsgInfo describing the message that should be filtered
1998 * \return TRUE if the message was moved and MsgInfo is now invalid,
2001 gboolean procmsg_msginfo_filter(MsgInfo *msginfo)
2003 MailFilteringData mail_filtering_data;
2005 mail_filtering_data.msginfo = msginfo;
2006 if (hooks_invoke(MAIL_FILTERING_HOOKLIST, &mail_filtering_data)) {
2010 /* filter if enabled in prefs or move to inbox if not */
2011 if((filtering_rules != NULL) &&
2012 filter_message_by_msginfo(filtering_rules, msginfo)) {
2019 MsgInfo *procmsg_msginfo_new_from_mimeinfo(MsgInfo *src_msginfo, MimeInfo *mimeinfo)
2021 MsgInfo *tmp_msginfo = NULL;
2022 MsgFlags flags = {0, 0};
2023 gchar *tmpfile = get_tmp_file();
2024 FILE *fp = g_fopen(tmpfile, "wb");
2026 if (!mimeinfo || mimeinfo->type != MIMETYPE_MESSAGE ||
2027 g_ascii_strcasecmp(mimeinfo->subtype, "rfc822")) {
2028 g_warning("procmsg_msginfo_new_from_mimeinfo(): unsuitable mimeinfo");
2035 if (fp && procmime_write_mimeinfo(mimeinfo, fp) >= 0) {
2038 tmp_msginfo = procheader_parse_file(
2045 if (tmp_msginfo != NULL) {
2047 tmp_msginfo->folder = src_msginfo->folder;
2048 tmp_msginfo->plaintext_file = g_strdup(tmpfile);
2050 g_warning("procmsg_msginfo_new_from_mimeinfo(): Can't generate new msginfo");
2058 static GSList *spam_learners = NULL;
2060 void procmsg_register_spam_learner (int (*learn_func)(MsgInfo *info, GSList *list, gboolean spam))
2062 if (!g_slist_find(spam_learners, learn_func))
2063 spam_learners = g_slist_append(spam_learners, learn_func);
2064 if (mainwindow_get_mainwindow()) {
2065 main_window_set_menu_sensitive(mainwindow_get_mainwindow());
2066 summary_set_menu_sensitive(
2067 mainwindow_get_mainwindow()->summaryview);
2068 toolbar_main_set_sensitive(mainwindow_get_mainwindow());
2072 void procmsg_unregister_spam_learner (int (*learn_func)(MsgInfo *info, GSList *list, gboolean spam))
2074 spam_learners = g_slist_remove(spam_learners, learn_func);
2075 if (mainwindow_get_mainwindow()) {
2076 main_window_set_menu_sensitive(mainwindow_get_mainwindow());
2077 summary_set_menu_sensitive(
2078 mainwindow_get_mainwindow()->summaryview);
2079 toolbar_main_set_sensitive(mainwindow_get_mainwindow());
2083 gboolean procmsg_spam_can_learn(void)
2085 return g_slist_length(spam_learners) > 0;
2088 int procmsg_spam_learner_learn (MsgInfo *info, GSList *list, gboolean spam)
2090 GSList *cur = spam_learners;
2092 for (; cur; cur = cur->next) {
2093 int ((*func)(MsgInfo *info, GSList *list, gboolean spam)) = cur->data;
2094 ret |= func(info, list, spam);
2099 static gchar *spam_folder_item = NULL;
2100 void procmsg_spam_set_folder (const char *item_identifier)
2102 g_free(spam_folder_item);
2103 if (item_identifier)
2104 spam_folder_item = g_strdup(item_identifier);
2106 spam_folder_item = NULL;
2109 FolderItem *procmsg_spam_get_folder (void)
2111 FolderItem *item = spam_folder_item ? folder_find_item_from_identifier(spam_folder_item) : NULL;
2112 return item ? item : folder_get_default_trash();