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, gchar **errstr);
58 Q_MAIL_ACCOUNT_ID = 4,
59 Q_NEWS_ACCOUNT_ID = 5,
60 Q_SAVE_COPY_FOLDER = 6,
61 Q_REPLY_MESSAGE_ID = 7,
69 GHashTable *procmsg_msg_hash_table_create(GSList *mlist)
71 GHashTable *msg_table;
73 if (mlist == NULL) return NULL;
75 msg_table = g_hash_table_new(NULL, g_direct_equal);
76 procmsg_msg_hash_table_append(msg_table, mlist);
81 void procmsg_msg_hash_table_append(GHashTable *msg_table, GSList *mlist)
86 if (msg_table == NULL || mlist == NULL) return;
88 for (cur = mlist; cur != NULL; cur = cur->next) {
89 msginfo = (MsgInfo *)cur->data;
91 g_hash_table_insert(msg_table,
92 GUINT_TO_POINTER(msginfo->msgnum),
97 GHashTable *procmsg_to_folder_hash_table_create(GSList *mlist)
99 GHashTable *msg_table;
103 if (mlist == NULL) return NULL;
105 msg_table = g_hash_table_new(NULL, g_direct_equal);
107 for (cur = mlist; cur != NULL; cur = cur->next) {
108 msginfo = (MsgInfo *)cur->data;
109 g_hash_table_insert(msg_table, msginfo->to_folder, msginfo);
115 gint procmsg_get_last_num_in_msg_list(GSList *mlist)
121 for (cur = mlist; cur != NULL; cur = cur->next) {
122 msginfo = (MsgInfo *)cur->data;
123 if (msginfo && msginfo->msgnum > last)
124 last = msginfo->msgnum;
130 void procmsg_msg_list_free(GSList *mlist)
135 for (cur = mlist; cur != NULL; cur = cur->next) {
136 msginfo = (MsgInfo *)cur->data;
137 procmsg_msginfo_free(msginfo);
151 /* CLAWS subject threading:
153 in the first round it inserts subject lines in a
154 relation (subject <-> node)
156 the second round finishes the threads by attaching
157 matching subject lines to the one found in the
158 relation. will use the oldest node with the same
159 subject that is not more then thread_by_subject_max_age
160 days old (see subject_relation_lookup)
163 static void subject_relation_insert(GRelation *relation, GNode *node)
168 g_return_if_fail(relation != NULL);
169 g_return_if_fail(node != NULL);
170 msginfo = (MsgInfo *) node->data;
171 g_return_if_fail(msginfo != NULL);
173 subject = msginfo->subject;
176 subject += subject_get_prefix_length(subject);
178 g_relation_insert(relation, subject, node);
181 static GNode *subject_relation_lookup(GRelation *relation, MsgInfo *msginfo)
188 g_return_val_if_fail(relation != NULL, NULL);
190 subject = msginfo->subject;
193 prefix_length = subject_get_prefix_length(subject);
194 if (prefix_length <= 0)
196 subject += prefix_length;
198 tuples = g_relation_select(relation, subject, 0);
202 if (tuples->len > 0) {
204 GNode *relation_node;
205 MsgInfo *relation_msginfo = NULL, *best_msginfo = NULL;
208 /* check all nodes with the same subject to find the best parent */
209 for (i = 0; i < tuples->len; i++) {
210 relation_node = (GNode *) g_tuples_index(tuples, i, 1);
211 relation_msginfo = (MsgInfo *) relation_node->data;
214 /* best node should be the oldest in the found nodes */
215 /* parent node must not be older then msginfo */
216 if ((relation_msginfo->date_t < msginfo->date_t) &&
217 ((best_msginfo == NULL) ||
218 (best_msginfo->date_t > relation_msginfo->date_t)))
221 /* parent node must not be more then thread_by_subject_max_age
222 days older then msginfo */
223 if (abs(difftime(msginfo->date_t, relation_msginfo->date_t)) >
224 prefs_common.thread_by_subject_max_age * 3600 * 24)
227 /* can add new tests for all matching
228 nodes found by subject */
231 node = relation_node;
232 best_msginfo = relation_msginfo;
237 g_tuples_destroy(tuples);
241 /* return the reversed thread tree */
242 GNode *procmsg_get_thread_tree(GSList *mlist)
244 GNode *root, *parent, *node, *next;
245 GHashTable *msgid_table;
246 GRelation *subject_relation;
250 START_TIMING("procmsg_get_thread_tree");
251 root = g_node_new(NULL);
252 msgid_table = g_hash_table_new(g_str_hash, g_str_equal);
253 subject_relation = g_relation_new(2);
254 g_relation_index(subject_relation, 0, g_str_hash, g_str_equal);
256 for (; mlist != NULL; mlist = mlist->next) {
257 msginfo = (MsgInfo *)mlist->data;
260 if (msginfo->inreplyto) {
261 parent = g_hash_table_lookup(msgid_table, msginfo->inreplyto);
262 if (parent == NULL) {
266 node = g_node_insert_data_before
267 (parent, parent == root ? parent->children : NULL,
269 if ((msgid = msginfo->msgid) && g_hash_table_lookup(msgid_table, msgid) == NULL)
270 g_hash_table_insert(msgid_table, (gchar *)msgid, node);
272 /* CLAWS: add subject to relation (without prefix) */
273 if (prefs_common.thread_by_subject) {
274 subject_relation_insert(subject_relation, node);
278 /* complete the unfinished threads */
279 for (node = root->children; node != NULL; ) {
281 msginfo = (MsgInfo *)node->data;
284 if (msginfo->inreplyto)
285 parent = g_hash_table_lookup(msgid_table, msginfo->inreplyto);
287 /* try looking for the indirect parent */
288 if (!parent && msginfo->references) {
289 for (reflist = msginfo->references;
290 reflist != NULL; reflist = reflist->next)
291 if ((parent = g_hash_table_lookup
292 (msgid_table, reflist->data)) != NULL)
296 /* node should not be the parent, and node should not
297 be an ancestor of parent (circular reference) */
298 if (parent && parent != node &&
299 !g_node_is_ancestor(node, parent)) {
302 (parent, parent->children, node);
308 if (prefs_common.thread_by_subject) {
309 START_TIMING("procmsg_get_thread_tree(1)");
310 for (node = root->children; node && node != NULL;) {
312 msginfo = (MsgInfo *) node->data;
314 parent = subject_relation_lookup(subject_relation, msginfo);
316 /* the node may already be threaded by IN-REPLY-TO, so go up
318 find the parent node */
319 if (parent != NULL) {
320 if (g_node_is_ancestor(node, parent))
328 g_node_append(parent, node);
336 g_relation_destroy(subject_relation);
337 g_hash_table_destroy(msgid_table);
342 gint procmsg_move_messages(GSList *mlist)
344 GSList *cur, *movelist = NULL;
346 FolderItem *dest = NULL;
348 gboolean finished = TRUE;
349 if (!mlist) return 0;
351 folder_item_update_freeze();
354 for (cur = mlist; cur != NULL; cur = cur->next) {
355 msginfo = (MsgInfo *)cur->data;
356 if (!msginfo->to_folder) {
362 dest = msginfo->to_folder;
363 movelist = g_slist_prepend(movelist, msginfo);
364 } else if (dest == msginfo->to_folder) {
365 movelist = g_slist_prepend(movelist, msginfo);
369 procmsg_msginfo_set_to_folder(msginfo, NULL);
372 movelist = g_slist_reverse(movelist);
373 retval |= folder_item_move_msgs(dest, movelist);
374 g_slist_free(movelist);
377 if (finished == FALSE) {
383 folder_item_update_thaw();
387 void procmsg_copy_messages(GSList *mlist)
389 GSList *cur, *copylist = NULL;
391 FolderItem *dest = NULL;
392 gboolean finished = TRUE;
395 folder_item_update_freeze();
398 for (cur = mlist; cur != NULL; cur = cur->next) {
399 msginfo = (MsgInfo *)cur->data;
400 if (!msginfo->to_folder) {
406 dest = msginfo->to_folder;
407 copylist = g_slist_prepend(copylist, msginfo);
408 } else if (dest == msginfo->to_folder) {
409 copylist = g_slist_prepend(copylist, msginfo);
413 procmsg_msginfo_set_to_folder(msginfo, NULL);
416 copylist = g_slist_reverse(copylist);
417 folder_item_copy_msgs(dest, copylist);
418 g_slist_free(copylist);
421 if (finished == FALSE) {
427 folder_item_update_thaw();
430 gchar *procmsg_get_message_file_path(MsgInfo *msginfo)
434 g_return_val_if_fail(msginfo != NULL, NULL);
436 if (msginfo->plaintext_file)
437 file = g_strdup(msginfo->plaintext_file);
439 file = folder_item_fetch_msg(msginfo->folder, msginfo->msgnum);
445 gchar *procmsg_get_message_file(MsgInfo *msginfo)
447 gchar *filename = NULL;
449 g_return_val_if_fail(msginfo != NULL, NULL);
451 filename = folder_item_fetch_msg(msginfo->folder, msginfo->msgnum);
453 debug_print("can't fetch message %d\n", msginfo->msgnum);
458 gchar *procmsg_get_message_file_full(MsgInfo *msginfo, gboolean headers, gboolean body)
460 gchar *filename = NULL;
462 g_return_val_if_fail(msginfo != NULL, NULL);
464 filename = folder_item_fetch_msg_full(msginfo->folder, msginfo->msgnum,
467 debug_print("can't fetch message %d\n", msginfo->msgnum);
472 GSList *procmsg_get_message_file_list(GSList *mlist)
474 GSList *file_list = NULL;
476 MsgFileInfo *fileinfo;
479 while (mlist != NULL) {
480 msginfo = (MsgInfo *)mlist->data;
481 file = procmsg_get_message_file(msginfo);
483 procmsg_message_file_list_free(file_list);
486 fileinfo = g_new(MsgFileInfo, 1);
487 fileinfo->msginfo = procmsg_msginfo_new_ref(msginfo);
488 fileinfo->file = file;
489 fileinfo->flags = g_new(MsgFlags, 1);
490 *fileinfo->flags = msginfo->flags;
491 file_list = g_slist_prepend(file_list, fileinfo);
495 file_list = g_slist_reverse(file_list);
500 void procmsg_message_file_list_free(MsgInfoList *file_list)
503 MsgFileInfo *fileinfo;
505 for (cur = file_list; cur != NULL; cur = cur->next) {
506 fileinfo = (MsgFileInfo *)cur->data;
507 procmsg_msginfo_free(fileinfo->msginfo);
508 g_free(fileinfo->file);
509 g_free(fileinfo->flags);
513 g_slist_free(file_list);
516 FILE *procmsg_open_message(MsgInfo *msginfo)
521 g_return_val_if_fail(msginfo != NULL, NULL);
523 file = procmsg_get_message_file_path(msginfo);
524 g_return_val_if_fail(file != NULL, NULL);
526 if (!is_file_exist(file)) {
528 file = procmsg_get_message_file(msginfo);
533 if ((fp = g_fopen(file, "rb")) == NULL) {
534 FILE_OP_ERROR(file, "fopen");
541 if (MSG_IS_QUEUED(msginfo->flags) || MSG_IS_DRAFT(msginfo->flags)) {
544 while (fgets(buf, sizeof(buf), fp) != NULL) {
546 if (!strncmp(buf, "X-Sylpheed-End-Special-Headers: 1",
547 strlen("X-Sylpheed-End-Special-Headers:")))
550 if (buf[0] == '\r' || buf[0] == '\n') break;
551 /* from other mailers */
552 if (!strncmp(buf, "Date: ", 6)
553 || !strncmp(buf, "To: ", 4)
554 || !strncmp(buf, "From: ", 6)
555 || !strncmp(buf, "Subject: ", 9)) {
565 gboolean procmsg_msg_exist(MsgInfo *msginfo)
570 if (!msginfo) return FALSE;
572 path = folder_item_get_path(msginfo->folder);
574 ret = !folder_item_is_msg_changed(msginfo->folder, msginfo);
580 void procmsg_get_filter_keyword(MsgInfo *msginfo, gchar **header, gchar **key,
581 PrefsFilterType type)
583 static HeaderEntry hentry[] = {{"X-BeenThere:", NULL, TRUE},
584 {"X-ML-Name:", NULL, TRUE},
585 {"X-List:", NULL, TRUE},
586 {"X-Mailing-list:", NULL, TRUE},
587 {"List-Id:", NULL, TRUE},
588 {"X-Sequence:", NULL, TRUE},
589 {"Sender:", NULL, TRUE},
590 {"List-Post:", NULL, TRUE},
591 {NULL, NULL, FALSE}};
597 H_X_MAILING_LIST = 3,
606 g_return_if_fail(msginfo != NULL);
607 g_return_if_fail(header != NULL);
608 g_return_if_fail(key != NULL);
617 if ((fp = procmsg_open_message(msginfo)) == NULL)
619 procheader_get_header_fields(fp, hentry);
622 #define SET_FILTER_KEY(hstr, idx) \
624 *header = g_strdup(hstr); \
625 *key = hentry[idx].body; \
626 hentry[idx].body = NULL; \
629 if (hentry[H_X_BEENTHERE].body != NULL) {
630 SET_FILTER_KEY("header \"X-BeenThere\"", H_X_BEENTHERE);
631 } else if (hentry[H_X_ML_NAME].body != NULL) {
632 SET_FILTER_KEY("header \"X-ML-Name\"", H_X_ML_NAME);
633 } else if (hentry[H_X_LIST].body != NULL) {
634 SET_FILTER_KEY("header \"X-List\"", H_X_LIST);
635 } else if (hentry[H_X_MAILING_LIST].body != NULL) {
636 SET_FILTER_KEY("header \"X-Mailing-List\"", H_X_MAILING_LIST);
637 } else if (hentry[H_LIST_ID].body != NULL) {
638 SET_FILTER_KEY("header \"List-Id\"", H_LIST_ID);
639 extract_list_id_str(*key);
640 } else if (hentry[H_X_SEQUENCE].body != NULL) {
643 SET_FILTER_KEY("X-Sequence", H_X_SEQUENCE);
646 while (*p != '\0' && !g_ascii_isspace(*p)) p++;
647 while (g_ascii_isspace(*p)) p++;
648 if (g_ascii_isdigit(*p)) {
654 } else if (hentry[H_SENDER].body != NULL) {
655 SET_FILTER_KEY("header \"Sender\"", H_SENDER);
656 } else if (hentry[H_LIST_POST].body != NULL) {
657 SET_FILTER_KEY("header \"List-Post\"", H_LIST_POST);
658 } else if (msginfo->to) {
659 *header = g_strdup("to");
660 *key = g_strdup(msginfo->to);
661 } else if (msginfo->subject) {
662 *header = g_strdup("subject");
663 *key = g_strdup(msginfo->subject);
666 #undef SET_FILTER_KEY
668 g_free(hentry[H_X_BEENTHERE].body);
669 hentry[H_X_BEENTHERE].body = NULL;
670 g_free(hentry[H_X_ML_NAME].body);
671 hentry[H_X_ML_NAME].body = NULL;
672 g_free(hentry[H_X_LIST].body);
673 hentry[H_X_LIST].body = NULL;
674 g_free(hentry[H_X_MAILING_LIST].body);
675 hentry[H_X_MAILING_LIST].body = NULL;
676 g_free(hentry[H_LIST_ID].body);
677 hentry[H_LIST_ID].body = NULL;
678 g_free(hentry[H_SENDER].body);
679 hentry[H_SENDER].body = NULL;
680 g_free(hentry[H_LIST_POST].body);
681 hentry[H_LIST_POST].body = NULL;
685 *header = g_strdup("from");
686 *key = g_strdup(msginfo->from);
689 *header = g_strdup("to");
690 *key = g_strdup(msginfo->to);
692 case FILTER_BY_SUBJECT:
693 *header = g_strdup("subject");
694 *key = g_strdup(msginfo->subject);
701 void procmsg_empty_trash(FolderItem *trash)
706 (trash->stype != F_TRASH &&
707 !folder_has_parent_of_type(trash, F_TRASH)))
710 if (trash && trash->total_msgs > 0) {
711 GSList *mlist = folder_item_get_msg_list(trash);
713 for (cur = mlist ; cur != NULL ; cur = cur->next) {
714 MsgInfo * msginfo = (MsgInfo *) cur->data;
715 if (MSG_IS_LOCKED(msginfo->flags))
717 if (msginfo->total_size != 0 &&
718 msginfo->size != (off_t)msginfo->total_size)
719 partial_mark_for_delete(msginfo);
721 procmsg_msginfo_free(msginfo);
724 folder_item_remove_all_msg(trash);
727 if (!trash->node || !trash->node->children)
730 node = trash->node->children;
731 while (node != NULL) {
733 procmsg_empty_trash(FOLDER_ITEM(node->data));
738 void procmsg_empty_all_trash(void)
743 for (cur = folder_get_list(); cur != NULL; cur = cur->next) {
744 Folder *folder = FOLDER(cur->data);
745 trash = folder->trash;
746 procmsg_empty_trash(trash);
747 if (folder->account && folder->account->set_trash_folder &&
748 folder_find_item_from_identifier(folder->account->trash_folder))
750 folder_find_item_from_identifier(folder->account->trash_folder));
754 static PrefsAccount *procmsg_get_account_from_file(const gchar *file)
756 PrefsAccount *mailac = NULL;
760 static HeaderEntry qentry[] = {{"S:", NULL, FALSE},
761 {"SSV:", NULL, FALSE},
763 {"NG:", NULL, FALSE},
764 {"MAID:", NULL, FALSE},
765 {"NAID:", NULL, FALSE},
766 {"SCF:", NULL, FALSE},
767 {"RMID:", NULL, FALSE},
768 {"FMID:", NULL, FALSE},
769 {"X-Sylpheed-Privacy-System:", NULL, FALSE},
770 {"X-Sylpheed-Encrypt:", NULL, FALSE},
771 {"X-Sylpheed-Encrypt-Data:", NULL, FALSE},
772 {NULL, NULL, FALSE}};
774 g_return_val_if_fail(file != NULL, NULL);
776 if ((fp = g_fopen(file, "rb")) == NULL) {
777 FILE_OP_ERROR(file, "fopen");
781 while ((hnum = procheader_get_one_field(buf, sizeof(buf), fp, qentry))
783 gchar *p = buf + strlen(qentry[hnum].name);
785 if (hnum == Q_MAIL_ACCOUNT_ID) {
786 mailac = account_find_from_id(atoi(p));
794 static GSList *procmsg_list_sort_by_account(FolderItem *queue, GSList *list)
796 GSList *result = NULL;
798 PrefsAccount *last_account = NULL;
801 gboolean nothing_to_sort = TRUE;
806 orig = g_slist_copy(list);
808 msg = (MsgInfo *)orig->data;
810 for (cur = orig; cur; cur = cur->next)
811 debug_print("sort before %s\n", ((MsgInfo *)cur->data)->from);
816 nothing_to_sort = TRUE;
820 PrefsAccount *ac = NULL;
821 msg = (MsgInfo *)cur->data;
822 file = folder_item_fetch_msg(queue, msg->msgnum);
823 ac = procmsg_get_account_from_file(file);
826 if (last_account == NULL || (ac != NULL && ac == last_account)) {
827 result = g_slist_append(result, msg);
828 orig = g_slist_remove(orig, msg);
830 nothing_to_sort = FALSE;
836 if (orig || g_slist_length(orig)) {
837 if (!last_account && nothing_to_sort) {
838 /* can't find an account for the rest of the list */
841 result = g_slist_append(result, cur->data);
852 for (cur = result; cur; cur = cur->next)
853 debug_print("sort after %s\n", ((MsgInfo *)cur->data)->from);
860 static gboolean procmsg_is_last_for_account(FolderItem *queue, MsgInfo *msginfo, GSList *elem)
862 gchar *file = folder_item_fetch_msg(queue, msginfo->msgnum);
863 PrefsAccount *ac = procmsg_get_account_from_file(file);
866 for (cur = elem; cur; cur = cur->next) {
867 MsgInfo *cur_msginfo = (MsgInfo *)cur->data;
868 file = folder_item_fetch_msg(queue, cur_msginfo->msgnum);
870 if (cur_msginfo != msginfo && !MSG_IS_LOCKED(cur_msginfo->flags)) {
871 if (procmsg_get_account_from_file(file) == ac) {
883 *\brief Send messages in queue
885 *\param queue Queue folder to process
886 *\param save_msgs Unused
888 *\return Number of messages sent, negative if an error occurred
889 * positive if no error occurred
891 gint procmsg_send_queue(FolderItem *queue, gboolean save_msgs, gchar **errstr)
893 gint sent = 0, err = 0;
895 GSList *sorted_list = NULL;
897 static gboolean send_queue_lock = FALSE;
899 if (send_queue_lock) {
900 log_error(_("Already trying to send\n"));
902 if (*errstr) g_free(*errstr);
903 *errstr = g_strdup_printf(_("Already trying to send."));
907 send_queue_lock = TRUE;
909 queue = folder_get_default_queue();
910 g_return_val_if_fail(queue != NULL, -1);
912 folder_item_scan(queue);
913 list = folder_item_get_msg_list(queue);
915 /* sort the list per sender account; this helps reusing the same SMTP server */
916 sorted_list = procmsg_list_sort_by_account(queue, list);
918 for (elem = sorted_list; elem != NULL; elem = elem->next) {
922 msginfo = (MsgInfo *)(elem->data);
923 if (!MSG_IS_LOCKED(msginfo->flags)) {
924 file = folder_item_fetch_msg(queue, msginfo->msgnum);
926 if (procmsg_send_message_queue_full(file,
927 !procmsg_is_last_for_account(queue, msginfo, elem),
929 g_warning("Sending queued message %d failed.\n",
934 folder_item_remove_msg(queue, msginfo->msgnum);
939 /* FIXME: supposedly if only one message is locked, and queue
940 * is being flushed, the following free says something like
941 * "freeing msg ## in folder (nil)". */
942 procmsg_msginfo_free(msginfo);
945 g_slist_free(sorted_list);
946 folder_item_scan(queue);
948 if (queue->node && queue->node->children) {
949 node = queue->node->children;
950 while (node != NULL) {
953 res = procmsg_send_queue(FOLDER_ITEM(node->data), save_msgs, errstr);
961 send_queue_lock = FALSE;
962 return (err != 0 ? -err : sent);
966 *\brief Determine if a queue folder is empty
968 *\param queue Queue folder to process
970 *\return TRUE if the queue folder is empty, otherwise return FALSE
972 gboolean procmsg_queue_is_empty(FolderItem *queue)
975 gboolean res = FALSE;
977 queue = folder_get_default_queue();
978 g_return_val_if_fail(queue != NULL, TRUE);
980 folder_item_scan(queue);
981 list = folder_item_get_msg_list(queue);
982 res = (list == NULL);
983 procmsg_msg_list_free(list);
987 if (queue->node && queue->node->children) {
988 node = queue->node->children;
989 while (node != NULL) {
991 if (!procmsg_queue_is_empty(FOLDER_ITEM(node->data)))
1000 gint procmsg_remove_special_headers(const gchar *in, const gchar *out)
1003 gchar buf[BUFFSIZE];
1005 if ((fp = g_fopen(in, "rb")) == NULL) {
1006 FILE_OP_ERROR(in, "fopen");
1009 if ((outfp = g_fopen(out, "wb")) == NULL) {
1010 FILE_OP_ERROR(out, "fopen");
1014 while (fgets(buf, sizeof(buf), fp) != NULL) {
1016 if (!strncmp(buf, "X-Sylpheed-End-Special-Headers: 1",
1017 strlen("X-Sylpheed-End-Special-Headers:")))
1020 if (buf[0] == '\r' || buf[0] == '\n') break;
1021 /* from other mailers */
1022 if (!strncmp(buf, "Date: ", 6)
1023 || !strncmp(buf, "To: ", 4)
1024 || !strncmp(buf, "From: ", 6)
1025 || !strncmp(buf, "Subject: ", 9)) {
1030 while (fgets(buf, sizeof(buf), fp) != NULL)
1037 gint procmsg_save_to_outbox(FolderItem *outbox, const gchar *file,
1041 MsgInfo *msginfo, *tmp_msginfo;
1042 MsgFlags flag = {0, 0};
1044 debug_print("saving sent message...\n");
1047 outbox = folder_get_default_outbox();
1048 g_return_val_if_fail(outbox != NULL, -1);
1050 /* remove queueing headers */
1052 gchar tmp[MAXPATHLEN + 1];
1054 g_snprintf(tmp, sizeof(tmp), "%s%ctmpmsg.out.%08x",
1055 get_rc_dir(), G_DIR_SEPARATOR, (guint) rand());
1057 if (procmsg_remove_special_headers(file, tmp) !=0)
1060 folder_item_scan(outbox);
1061 if ((num = folder_item_add_msg(outbox, tmp, &flag, TRUE)) < 0) {
1062 g_warning("can't save message\n");
1067 folder_item_scan(outbox);
1068 if ((num = folder_item_add_msg
1069 (outbox, file, &flag, FALSE)) < 0) {
1070 g_warning("can't save message\n");
1075 msginfo = folder_item_get_msginfo(outbox, num); /* refcnt++ */
1076 tmp_msginfo = procmsg_msginfo_get_full_info(msginfo); /* refcnt++ */
1077 if (msginfo != NULL) {
1078 procmsg_msginfo_unset_flags(msginfo, ~0, 0);
1079 procmsg_msginfo_free(msginfo); /* refcnt-- */
1080 /* tmp_msginfo == msginfo */
1081 if (tmp_msginfo && (msginfo->dispositionnotificationto ||
1082 msginfo->returnreceiptto)) {
1083 procmsg_msginfo_set_flags(msginfo, MSG_RETRCPT_SENT, 0);
1084 procmsg_msginfo_free(msginfo); /* refcnt-- */
1091 void procmsg_print_message(MsgInfo *msginfo, const gchar *cmdline)
1093 static const gchar *def_cmd = "lpr %s";
1094 static guint id = 0;
1100 g_return_if_fail(msginfo);
1102 if (procmime_msginfo_is_encrypted(msginfo))
1103 tmpfp = procmime_get_first_encrypted_text_content(msginfo);
1105 tmpfp = procmime_get_first_text_content(msginfo);
1106 if (tmpfp == NULL) {
1107 g_warning("Can't get text part\n");
1111 prtmp = g_strdup_printf("%s%cprinttmp.%08x",
1112 get_mime_tmp_dir(), G_DIR_SEPARATOR, id++);
1114 if ((prfp = g_fopen(prtmp, "wb")) == NULL) {
1115 FILE_OP_ERROR(prtmp, "fopen");
1121 if (msginfo->date) fprintf(prfp, "Date: %s\n", msginfo->date);
1122 if (msginfo->from) fprintf(prfp, "From: %s\n", msginfo->from);
1123 if (msginfo->to) fprintf(prfp, "To: %s\n", msginfo->to);
1124 if (msginfo->cc) fprintf(prfp, "Cc: %s\n", msginfo->cc);
1125 if (msginfo->newsgroups)
1126 fprintf(prfp, "Newsgroups: %s\n", msginfo->newsgroups);
1127 if (msginfo->subject) fprintf(prfp, "Subject: %s\n", msginfo->subject);
1130 while (fgets(buf, sizeof(buf), tmpfp) != NULL)
1136 if (cmdline && (p = strchr(cmdline, '%')) && *(p + 1) == 's' &&
1137 !strchr(p + 2, '%'))
1138 g_snprintf(buf, sizeof(buf) - 1, cmdline, prtmp);
1141 g_warning("Print command line is invalid: '%s'\n",
1143 g_snprintf(buf, sizeof(buf) - 1, def_cmd, prtmp);
1149 if (buf[strlen(buf) - 1] != '&') strcat(buf, "&");
1153 MsgInfo *procmsg_msginfo_new_ref(MsgInfo *msginfo)
1160 MsgInfo *procmsg_msginfo_new(void)
1162 MsgInfo *newmsginfo;
1164 newmsginfo = g_new0(MsgInfo, 1);
1165 newmsginfo->refcnt = 1;
1170 MsgInfo *procmsg_msginfo_copy(MsgInfo *msginfo)
1172 MsgInfo *newmsginfo;
1175 if (msginfo == NULL) return NULL;
1177 newmsginfo = g_new0(MsgInfo, 1);
1179 newmsginfo->refcnt = 1;
1181 #define MEMBCOPY(mmb) newmsginfo->mmb = msginfo->mmb
1182 #define MEMBDUP(mmb) newmsginfo->mmb = msginfo->mmb ? \
1183 g_strdup(msginfo->mmb) : NULL
1198 MEMBDUP(newsgroups);
1205 MEMBCOPY(to_folder);
1209 MEMBDUP(dispositionnotificationto);
1210 MEMBDUP(returnreceiptto);
1212 refs = msginfo->references;
1213 for (refs = msginfo->references; refs != NULL; refs = refs->next) {
1214 newmsginfo->references = g_slist_prepend
1215 (newmsginfo->references, g_strdup(refs->data));
1217 newmsginfo->references = g_slist_reverse(newmsginfo->references);
1220 MEMBDUP(plaintext_file);
1225 MsgInfo *procmsg_msginfo_get_full_info(MsgInfo *msginfo)
1227 MsgInfo *full_msginfo;
1230 if (msginfo == NULL) return NULL;
1232 file = procmsg_get_message_file_path(msginfo);
1233 if (!file || !is_file_exist(file)) {
1235 file = procmsg_get_message_file(msginfo);
1237 if (!file || !is_file_exist(file)) {
1238 g_warning("procmsg_msginfo_get_full_info(): can't get message file.\n");
1242 full_msginfo = procheader_parse_file(file, msginfo->flags, TRUE, FALSE);
1244 if (!full_msginfo) return NULL;
1246 /* CLAWS: make sure we add the missing members; see:
1247 * procheader.c::procheader_get_headernames() */
1248 if (!msginfo->list_post)
1249 msginfo->list_post = g_strdup(full_msginfo->list_post);
1250 if (!msginfo->list_subscribe)
1251 msginfo->list_subscribe = g_strdup(full_msginfo->list_subscribe);
1252 if (!msginfo->list_unsubscribe)
1253 msginfo->list_unsubscribe = g_strdup(full_msginfo->list_unsubscribe);
1254 if (!msginfo->list_help)
1255 msginfo->list_help = g_strdup(full_msginfo->list_help);
1256 if (!msginfo->list_archive)
1257 msginfo->list_archive= g_strdup(full_msginfo->list_archive);
1258 if (!msginfo->list_owner)
1259 msginfo->list_owner = g_strdup(full_msginfo->list_owner);
1260 if (!msginfo->xface)
1261 msginfo->xface = g_strdup(full_msginfo->xface);
1263 msginfo->face = g_strdup(full_msginfo->face);
1264 if (!msginfo->dispositionnotificationto)
1265 msginfo->dispositionnotificationto =
1266 g_strdup(full_msginfo->dispositionnotificationto);
1267 if (!msginfo->returnreceiptto)
1268 msginfo->returnreceiptto = g_strdup
1269 (full_msginfo->returnreceiptto);
1270 if (!msginfo->partial_recv && full_msginfo->partial_recv)
1271 msginfo->partial_recv = g_strdup
1272 (full_msginfo->partial_recv);
1273 msginfo->total_size = full_msginfo->total_size;
1274 if (!msginfo->account_server && full_msginfo->account_server)
1275 msginfo->account_server = g_strdup
1276 (full_msginfo->account_server);
1277 if (!msginfo->account_login && full_msginfo->account_login)
1278 msginfo->account_login = g_strdup
1279 (full_msginfo->account_login);
1280 msginfo->planned_download = full_msginfo->planned_download;
1281 procmsg_msginfo_free(full_msginfo);
1283 return procmsg_msginfo_new_ref(msginfo);
1286 void procmsg_msginfo_free(MsgInfo *msginfo)
1288 if (msginfo == NULL) return;
1291 if (msginfo->refcnt > 0)
1294 if (msginfo->to_folder) {
1295 msginfo->to_folder->op_count--;
1296 folder_item_update(msginfo->to_folder, F_ITEM_UPDATE_MSGCNT);
1299 g_free(msginfo->fromspace);
1300 g_free(msginfo->returnreceiptto);
1301 g_free(msginfo->dispositionnotificationto);
1302 g_free(msginfo->xface);
1303 g_free(msginfo->face);
1305 g_free(msginfo->fromname);
1307 g_free(msginfo->date);
1308 g_free(msginfo->from);
1309 g_free(msginfo->to);
1310 g_free(msginfo->cc);
1311 g_free(msginfo->newsgroups);
1312 g_free(msginfo->subject);
1313 g_free(msginfo->msgid);
1314 g_free(msginfo->inreplyto);
1315 g_free(msginfo->xref);
1317 g_free(msginfo->list_post);
1318 g_free(msginfo->list_subscribe);
1319 g_free(msginfo->list_unsubscribe);
1320 g_free(msginfo->list_help);
1321 g_free(msginfo->list_archive);
1322 g_free(msginfo->list_owner);
1324 g_free(msginfo->partial_recv);
1325 g_free(msginfo->account_server);
1326 g_free(msginfo->account_login);
1328 slist_free_strings(msginfo->references);
1329 g_slist_free(msginfo->references);
1331 g_free(msginfo->plaintext_file);
1336 guint procmsg_msginfo_memusage(MsgInfo *msginfo)
1341 memusage += sizeof(MsgInfo);
1342 if (msginfo->fromname)
1343 memusage += strlen(msginfo->fromname);
1345 memusage += strlen(msginfo->date);
1347 memusage += strlen(msginfo->from);
1349 memusage += strlen(msginfo->to);
1351 memusage += strlen(msginfo->cc);
1352 if (msginfo->newsgroups)
1353 memusage += strlen(msginfo->newsgroups);
1354 if (msginfo->subject)
1355 memusage += strlen(msginfo->subject);
1357 memusage += strlen(msginfo->msgid);
1358 if (msginfo->inreplyto)
1359 memusage += strlen(msginfo->inreplyto);
1361 memusage += strlen(msginfo->xface);
1363 memusage += strlen(msginfo->face);
1364 if (msginfo->dispositionnotificationto)
1365 memusage += strlen(msginfo->dispositionnotificationto);
1366 if (msginfo->returnreceiptto)
1367 memusage += strlen(msginfo->returnreceiptto);
1368 for (refs = msginfo->references; refs; refs=refs->next) {
1369 gchar *r = (gchar *)refs->data;
1370 memusage += r?strlen(r):0;
1372 if (msginfo->fromspace)
1373 memusage += strlen(msginfo->fromspace);
1378 gint procmsg_cmp_msgnum_for_sort(gconstpointer a, gconstpointer b)
1380 const MsgInfo *msginfo1 = a;
1381 const MsgInfo *msginfo2 = b;
1388 return msginfo1->msgnum - msginfo2->msgnum;
1391 static gint procmsg_send_message_queue_full(const gchar *file, gboolean keep_session, gchar **errstr)
1393 static HeaderEntry qentry[] = {{"S:", NULL, FALSE},
1394 {"SSV:", NULL, FALSE},
1395 {"R:", NULL, FALSE},
1396 {"NG:", NULL, FALSE},
1397 {"MAID:", NULL, FALSE},
1398 {"NAID:", NULL, FALSE},
1399 {"SCF:", NULL, FALSE},
1400 {"RMID:", NULL, FALSE},
1401 {"FMID:", NULL, FALSE},
1402 {"X-Sylpheed-Privacy-System:", NULL, FALSE},
1403 {"X-Sylpheed-Encrypt:", NULL, FALSE},
1404 {"X-Sylpheed-Encrypt-Data:", NULL, FALSE},
1405 {"X-Sylpheed-End-Special-Headers:", NULL, FALSE},
1406 {NULL, NULL, FALSE}};
1409 gint mailval = 0, newsval = 0;
1411 gchar *smtpserver = NULL;
1412 GSList *to_list = NULL;
1413 GSList *newsgroup_list = NULL;
1414 gchar *savecopyfolder = NULL;
1415 gchar *replymessageid = NULL;
1416 gchar *fwdmessageid = NULL;
1417 gchar *privacy_system = NULL;
1418 gboolean encrypt = FALSE;
1419 gchar *encrypt_data = NULL;
1420 gchar buf[BUFFSIZE];
1422 PrefsAccount *mailac = NULL, *newsac = NULL;
1423 gboolean save_clear_text = TRUE;
1424 gchar *tmp_enc_file = NULL;
1428 g_return_val_if_fail(file != NULL, -1);
1430 if ((fp = g_fopen(file, "rb")) == NULL) {
1431 FILE_OP_ERROR(file, "fopen");
1433 if (*errstr) g_free(*errstr);
1434 *errstr = g_strdup_printf(_("Couldn't open file %s."), file);
1439 while ((hnum = procheader_get_one_field(buf, sizeof(buf), fp, qentry))
1441 gchar *p = buf + strlen(qentry[hnum].name);
1449 if (smtpserver == NULL)
1450 smtpserver = g_strdup(p);
1453 to_list = address_list_append(to_list, p);
1456 newsgroup_list = newsgroup_list_append(newsgroup_list, p);
1458 case Q_MAIL_ACCOUNT_ID:
1459 mailac = account_find_from_id(atoi(p));
1461 case Q_NEWS_ACCOUNT_ID:
1462 newsac = account_find_from_id(atoi(p));
1464 case Q_SAVE_COPY_FOLDER:
1465 if (savecopyfolder == NULL)
1466 savecopyfolder = g_strdup(p);
1468 case Q_REPLY_MESSAGE_ID:
1469 if (replymessageid == NULL)
1470 replymessageid = g_strdup(p);
1472 case Q_FWD_MESSAGE_ID:
1473 if (fwdmessageid == NULL)
1474 fwdmessageid = g_strdup(p);
1476 case Q_PRIVACY_SYSTEM:
1477 if (privacy_system == NULL)
1478 privacy_system = g_strdup(p);
1484 case Q_ENCRYPT_DATA:
1485 if (encrypt_data == NULL)
1486 encrypt_data = g_strdup(p);
1488 case Q_SYLPHEED_HDRS:
1489 /* end of special headers reached */
1490 goto send_mail; /* can't "break;break;" */
1494 filepos = ftell(fp);
1499 if (mailac && mailac->save_encrypted_as_clear_text
1500 && !mailac->encrypt_to_self)
1501 save_clear_text = TRUE;
1503 save_clear_text = FALSE;
1508 mimeinfo = procmime_scan_queue_file(file);
1509 if (!privacy_encrypt(privacy_system, mimeinfo, encrypt_data)
1510 || (fp = my_tmpfile()) == NULL
1511 || procmime_write_mimeinfo(mimeinfo, fp) < 0) {
1514 procmime_mimeinfo_free_all(mimeinfo);
1517 slist_free_strings(to_list);
1518 g_slist_free(to_list);
1519 slist_free_strings(newsgroup_list);
1520 g_slist_free(newsgroup_list);
1521 g_free(savecopyfolder);
1522 g_free(replymessageid);
1523 g_free(fwdmessageid);
1524 g_free(privacy_system);
1525 g_free(encrypt_data);
1527 if (*errstr) g_free(*errstr);
1528 *errstr = g_strdup_printf(_("Couldn't encrypt the email: %s"),
1529 privacy_get_error());
1535 if (!save_clear_text) {
1536 gchar *content = NULL;
1537 FILE *tmpfp = get_tmpfile_in_dir(get_mime_tmp_dir(), &tmp_enc_file);
1541 content = file_read_stream_to_str(fp);
1544 str_write_to_file(content, tmp_enc_file);
1547 g_warning("couldn't get tempfile\n");
1551 procmime_mimeinfo_free_all(mimeinfo);
1557 debug_print("Sending message by mail\n");
1560 if (*errstr) g_free(*errstr);
1561 *errstr = g_strdup_printf(_("Queued message header is broken."));
1564 } else if (mailac && mailac->use_mail_command &&
1565 mailac->mail_command && (* mailac->mail_command)) {
1566 mailval = send_message_local(mailac->mail_command, fp);
1570 mailac = account_find_from_smtp_server(from, smtpserver);
1572 g_warning("Account not found. "
1573 "Using current account...\n");
1574 mailac = cur_account;
1579 mailval = send_message_smtp_full(mailac, to_list, fp, keep_session);
1580 if (mailval == -1 && errstr) {
1581 if (*errstr) g_free(*errstr);
1582 *errstr = g_strdup_printf(_("An error happened during SMTP session."));
1585 PrefsAccount tmp_ac;
1587 g_warning("Account not found.\n");
1589 memset(&tmp_ac, 0, sizeof(PrefsAccount));
1590 tmp_ac.address = from;
1591 tmp_ac.smtp_server = smtpserver;
1592 tmp_ac.smtpport = SMTP_PORT;
1593 mailval = send_message_smtp(&tmp_ac, to_list, fp);
1594 if (mailval == -1 && errstr) {
1595 if (*errstr) g_free(*errstr);
1596 *errstr = g_strdup_printf(_("No specific account has been found to "
1597 "send, and an error happened during SMTP session."));
1601 } else if (!to_list && !newsgroup_list) {
1603 if (*errstr) g_free(*errstr);
1604 *errstr = g_strdup(_("Couldn't determine sending informations. "
1605 "Maybe the email hasn't been generated by Sylpheed-Claws."));
1610 fseek(fp, filepos, SEEK_SET);
1611 if (newsgroup_list && (mailval == 0)) {
1616 /* write to temporary file */
1617 tmp = g_strdup_printf("%s%ctmp%d", g_get_tmp_dir(),
1618 G_DIR_SEPARATOR, (gint)file);
1619 if ((tmpfp = g_fopen(tmp, "wb")) == NULL) {
1620 FILE_OP_ERROR(tmp, "fopen");
1622 alertpanel_error(_("Could not create temporary file for news sending."));
1624 if (change_file_mode_rw(tmpfp, tmp) < 0) {
1625 FILE_OP_ERROR(tmp, "chmod");
1626 g_warning("can't change file mode\n");
1629 while ((newsval == 0) && fgets(buf, sizeof(buf), fp) != NULL) {
1630 if (fputs(buf, tmpfp) == EOF) {
1631 FILE_OP_ERROR(tmp, "fputs");
1634 if (*errstr) g_free(*errstr);
1635 *errstr = g_strdup_printf(_("Error when writing temporary file for news sending."));
1642 debug_print("Sending message by news\n");
1644 folder = FOLDER(newsac->folder);
1646 newsval = news_post(folder, tmp);
1647 if (newsval < 0 && errstr) {
1648 if (*errstr) g_free(*errstr);
1649 *errstr = g_strdup_printf(_("Error occurred while posting the message to %s."),
1650 newsac->nntp_server);
1660 /* save message to outbox */
1661 if (mailval == 0 && newsval == 0 && savecopyfolder) {
1664 debug_print("saving sent message...\n");
1666 outbox = folder_find_item_from_identifier(savecopyfolder);
1668 outbox = folder_get_default_outbox();
1670 if (save_clear_text || tmp_enc_file == NULL) {
1671 procmsg_save_to_outbox(outbox, file, TRUE);
1673 procmsg_save_to_outbox(outbox, tmp_enc_file, FALSE);
1677 if (tmp_enc_file != NULL) {
1678 g_unlink(tmp_enc_file);
1680 tmp_enc_file = NULL;
1683 if (replymessageid != NULL || fwdmessageid != NULL) {
1687 if (replymessageid != NULL)
1688 tokens = g_strsplit(replymessageid, "\t", 0);
1690 tokens = g_strsplit(fwdmessageid, "\t", 0);
1691 item = folder_find_item_from_identifier(tokens[0]);
1693 /* check if queued message has valid folder and message id */
1694 if (item != NULL && tokens[2] != NULL) {
1697 msginfo = folder_item_get_msginfo(item, atoi(tokens[1]));
1699 /* check if referring message exists and has a message id */
1700 if ((msginfo != NULL) &&
1701 (msginfo->msgid != NULL) &&
1702 (strcmp(msginfo->msgid, tokens[2]) != 0)) {
1703 procmsg_msginfo_free(msginfo);
1707 if (msginfo == NULL) {
1708 msginfo = folder_item_get_msginfo_by_msgid(item, tokens[2]);
1711 if (msginfo != NULL) {
1712 if (replymessageid != NULL) {
1713 procmsg_msginfo_unset_flags(msginfo, MSG_FORWARDED, 0);
1714 procmsg_msginfo_set_flags(msginfo, MSG_REPLIED, 0);
1716 procmsg_msginfo_unset_flags(msginfo, MSG_REPLIED, 0);
1717 procmsg_msginfo_set_flags(msginfo, MSG_FORWARDED, 0);
1719 procmsg_msginfo_free(msginfo);
1727 slist_free_strings(to_list);
1728 g_slist_free(to_list);
1729 slist_free_strings(newsgroup_list);
1730 g_slist_free(newsgroup_list);
1731 g_free(savecopyfolder);
1732 g_free(replymessageid);
1733 g_free(fwdmessageid);
1734 g_free(privacy_system);
1735 g_free(encrypt_data);
1737 return (newsval != 0 ? newsval : mailval);
1740 gint procmsg_send_message_queue(const gchar *file, gchar **errstr)
1742 return procmsg_send_message_queue_full(file, FALSE, errstr);
1745 static void update_folder_msg_counts(FolderItem *item, MsgInfo *msginfo, MsgPermFlags old_flags)
1747 MsgPermFlags new_flags = msginfo->flags.perm_flags;
1750 if (!(old_flags & MSG_NEW) && (new_flags & MSG_NEW)) {
1754 if ((old_flags & MSG_NEW) && !(new_flags & MSG_NEW)) {
1759 if (!(old_flags & MSG_UNREAD) && (new_flags & MSG_UNREAD)) {
1760 item->unread_msgs++;
1761 if (procmsg_msg_has_marked_parent(msginfo))
1762 item->unreadmarked_msgs++;
1765 if ((old_flags & MSG_UNREAD) && !(new_flags & MSG_UNREAD)) {
1766 item->unread_msgs--;
1767 if (procmsg_msg_has_marked_parent(msginfo))
1768 item->unreadmarked_msgs--;
1772 if (!(old_flags & MSG_MARKED) && (new_flags & MSG_MARKED)) {
1773 procmsg_update_unread_children(msginfo, TRUE);
1774 item->marked_msgs++;
1777 if ((old_flags & MSG_MARKED) && !(new_flags & MSG_MARKED)) {
1778 procmsg_update_unread_children(msginfo, FALSE);
1779 item->marked_msgs--;
1783 void procmsg_msginfo_set_flags(MsgInfo *msginfo, MsgPermFlags perm_flags, MsgTmpFlags tmp_flags)
1786 MsgInfoUpdate msginfo_update;
1787 MsgPermFlags perm_flags_new, perm_flags_old;
1788 MsgTmpFlags tmp_flags_old;
1790 g_return_if_fail(msginfo != NULL);
1791 item = msginfo->folder;
1792 g_return_if_fail(item != NULL);
1794 debug_print("Setting flags for message %d in folder %s\n", msginfo->msgnum, item->path);
1796 /* Perm Flags handling */
1797 perm_flags_old = msginfo->flags.perm_flags;
1798 perm_flags_new = msginfo->flags.perm_flags | perm_flags;
1799 if ((perm_flags & MSG_IGNORE_THREAD) || (perm_flags_old & MSG_IGNORE_THREAD)) {
1800 perm_flags_new &= ~(MSG_NEW | MSG_UNREAD);
1803 if (perm_flags_old != perm_flags_new) {
1804 folder_item_change_msg_flags(msginfo->folder, msginfo, perm_flags_new);
1806 update_folder_msg_counts(item, msginfo, perm_flags_old);
1810 /* Tmp flags handling */
1811 tmp_flags_old = msginfo->flags.tmp_flags;
1812 msginfo->flags.tmp_flags |= tmp_flags;
1814 /* update notification */
1815 if ((perm_flags_old != perm_flags_new) || (tmp_flags_old != msginfo->flags.tmp_flags)) {
1816 msginfo_update.msginfo = msginfo;
1817 msginfo_update.flags = MSGINFO_UPDATE_FLAGS;
1818 hooks_invoke(MSGINFO_UPDATE_HOOKLIST, &msginfo_update);
1819 folder_item_update(msginfo->folder, F_ITEM_UPDATE_MSGCNT);
1823 void procmsg_msginfo_unset_flags(MsgInfo *msginfo, MsgPermFlags perm_flags, MsgTmpFlags tmp_flags)
1826 MsgInfoUpdate msginfo_update;
1827 MsgPermFlags perm_flags_new, perm_flags_old;
1828 MsgTmpFlags tmp_flags_old;
1830 g_return_if_fail(msginfo != NULL);
1831 item = msginfo->folder;
1832 g_return_if_fail(item != NULL);
1834 debug_print("Unsetting flags for message %d in folder %s\n", msginfo->msgnum, item->path);
1836 /* Perm Flags handling */
1837 perm_flags_old = msginfo->flags.perm_flags;
1838 perm_flags_new = msginfo->flags.perm_flags & ~perm_flags;
1840 if (perm_flags_old != perm_flags_new) {
1841 folder_item_change_msg_flags(msginfo->folder, msginfo, perm_flags_new);
1843 update_folder_msg_counts(item, msginfo, perm_flags_old);
1846 /* Tmp flags hanlding */
1847 tmp_flags_old = msginfo->flags.tmp_flags;
1848 msginfo->flags.tmp_flags &= ~tmp_flags;
1850 /* update notification */
1851 if ((perm_flags_old != perm_flags_new) || (tmp_flags_old != msginfo->flags.tmp_flags)) {
1852 msginfo_update.msginfo = msginfo;
1853 msginfo_update.flags = MSGINFO_UPDATE_FLAGS;
1854 hooks_invoke(MSGINFO_UPDATE_HOOKLIST, &msginfo_update);
1855 folder_item_update(msginfo->folder, F_ITEM_UPDATE_MSGCNT);
1859 void procmsg_msginfo_change_flags(MsgInfo *msginfo,
1860 MsgPermFlags add_perm_flags, MsgTmpFlags add_tmp_flags,
1861 MsgPermFlags rem_perm_flags, MsgTmpFlags rem_tmp_flags)
1864 MsgInfoUpdate msginfo_update;
1865 MsgPermFlags perm_flags_new, perm_flags_old;
1866 MsgTmpFlags tmp_flags_old;
1868 g_return_if_fail(msginfo != NULL);
1869 item = msginfo->folder;
1870 g_return_if_fail(item != NULL);
1872 debug_print("Changing flags for message %d in folder %s\n", msginfo->msgnum, item->path);
1874 /* Perm Flags handling */
1875 perm_flags_old = msginfo->flags.perm_flags;
1876 perm_flags_new = (msginfo->flags.perm_flags & ~rem_perm_flags) | add_perm_flags;
1877 if ((add_perm_flags & MSG_IGNORE_THREAD) || (perm_flags_old & MSG_IGNORE_THREAD)) {
1878 perm_flags_new &= ~(MSG_NEW | MSG_UNREAD);
1881 if (perm_flags_old != perm_flags_new) {
1882 folder_item_change_msg_flags(msginfo->folder, msginfo, perm_flags_new);
1884 update_folder_msg_counts(item, msginfo, perm_flags_old);
1888 /* Tmp flags handling */
1889 tmp_flags_old = msginfo->flags.tmp_flags;
1890 msginfo->flags.tmp_flags &= ~rem_tmp_flags;
1891 msginfo->flags.tmp_flags |= add_tmp_flags;
1893 /* update notification */
1894 if ((perm_flags_old != perm_flags_new) || (tmp_flags_old != msginfo->flags.tmp_flags)) {
1895 msginfo_update.msginfo = msginfo;
1896 msginfo_update.flags = MSGINFO_UPDATE_FLAGS;
1897 hooks_invoke(MSGINFO_UPDATE_HOOKLIST, &msginfo_update);
1898 folder_item_update(msginfo->folder, F_ITEM_UPDATE_MSGCNT);
1903 *\brief check for flags (e.g. mark) in prior msgs of current thread
1905 *\param info Current message
1906 *\param perm_flags Flags to be checked
1907 *\param parentmsgs Hash of prior msgs to avoid loops
1909 *\return gboolean TRUE if perm_flags are found
1911 gboolean procmsg_msg_has_flagged_parent_real(MsgInfo *info,
1912 MsgPermFlags perm_flags, GHashTable *parentmsgs)
1916 g_return_val_if_fail(info != NULL, FALSE);
1918 if (info != NULL && info->folder != NULL && info->inreplyto != NULL) {
1919 tmp = folder_item_get_msginfo_by_msgid(info->folder,
1921 if (tmp && (tmp->flags.perm_flags & perm_flags)) {
1922 procmsg_msginfo_free(tmp);
1924 } else if (tmp != NULL) {
1927 if (g_hash_table_lookup(parentmsgs, info)) {
1928 debug_print("loop detected: %d\n",
1932 g_hash_table_insert(parentmsgs, info, "1");
1933 result = procmsg_msg_has_flagged_parent_real(
1934 tmp, perm_flags, parentmsgs);
1936 procmsg_msginfo_free(tmp);
1946 *\brief Callback for cleaning up hash of parentmsgs
1948 gboolean parentmsgs_hash_remove(gpointer key,
1956 *\brief Set up list of parentmsgs
1957 * See procmsg_msg_has_flagged_parent_real()
1959 gboolean procmsg_msg_has_flagged_parent(MsgInfo *info, MsgPermFlags perm_flags)
1962 GHashTable *parentmsgs = g_hash_table_new(NULL, NULL);
1964 result = procmsg_msg_has_flagged_parent_real(info, perm_flags, parentmsgs);
1965 g_hash_table_foreach_remove(parentmsgs, parentmsgs_hash_remove, NULL);
1966 g_hash_table_destroy(parentmsgs);
1971 *\brief Check if msgs prior in thread are marked
1972 * See procmsg_msg_has_flagged_parent_real()
1974 gboolean procmsg_msg_has_marked_parent(MsgInfo *info)
1976 return procmsg_msg_has_flagged_parent(info, MSG_MARKED);
1980 GSList *procmsg_find_children_func(MsgInfo *info,
1981 GSList *children, GSList *all)
1985 g_return_val_if_fail(info!=NULL, children);
1986 if (info->msgid == NULL)
1989 for (cur = all; cur != NULL; cur = g_slist_next(cur)) {
1990 MsgInfo *tmp = (MsgInfo *)cur->data;
1991 if (tmp->inreplyto && !strcmp(tmp->inreplyto, info->msgid)) {
1992 /* Check if message is already in the list */
1993 if ((children == NULL) ||
1994 (g_slist_index(children, tmp) == -1)) {
1995 children = g_slist_prepend(children,
1996 procmsg_msginfo_new_ref(tmp));
1997 children = procmsg_find_children_func(tmp,
2006 GSList *procmsg_find_children (MsgInfo *info)
2011 g_return_val_if_fail(info!=NULL, NULL);
2012 all = folder_item_get_msg_list(info->folder);
2013 children = procmsg_find_children_func(info, NULL, all);
2014 if (children != NULL) {
2015 for (cur = all; cur != NULL; cur = g_slist_next(cur)) {
2016 /* this will not free the used pointers
2017 created with procmsg_msginfo_new_ref */
2018 procmsg_msginfo_free((MsgInfo *)cur->data);
2026 void procmsg_update_unread_children(MsgInfo *info, gboolean newly_marked)
2028 GSList *children = procmsg_find_children(info);
2030 for (cur = children; cur != NULL; cur = g_slist_next(cur)) {
2031 MsgInfo *tmp = (MsgInfo *)cur->data;
2032 if(MSG_IS_UNREAD(tmp->flags) && !MSG_IS_IGNORE_THREAD(tmp->flags)) {
2034 info->folder->unreadmarked_msgs++;
2036 info->folder->unreadmarked_msgs--;
2037 folder_item_update(info->folder, F_ITEM_UPDATE_MSGCNT);
2039 procmsg_msginfo_free(tmp);
2041 g_slist_free(children);
2045 * Set the destination folder for a copy or move operation
2047 * \param msginfo The message which's destination folder is changed
2048 * \param to_folder The destination folder for the operation
2050 void procmsg_msginfo_set_to_folder(MsgInfo *msginfo, FolderItem *to_folder)
2052 if(msginfo->to_folder != NULL) {
2053 msginfo->to_folder->op_count--;
2054 folder_item_update(msginfo->to_folder, F_ITEM_UPDATE_MSGCNT);
2056 msginfo->to_folder = to_folder;
2057 if(to_folder != NULL) {
2058 to_folder->op_count++;
2059 folder_item_update(msginfo->to_folder, F_ITEM_UPDATE_MSGCNT);
2064 * Apply filtering actions to the msginfo
2066 * \param msginfo The MsgInfo describing the message that should be filtered
2067 * \return TRUE if the message was moved and MsgInfo is now invalid,
2070 gboolean procmsg_msginfo_filter(MsgInfo *msginfo, PrefsAccount* ac_prefs)
2072 MailFilteringData mail_filtering_data;
2074 mail_filtering_data.msginfo = msginfo;
2075 if (hooks_invoke(MAIL_FILTERING_HOOKLIST, &mail_filtering_data)) {
2079 /* filter if enabled in prefs or move to inbox if not */
2080 if((filtering_rules != NULL) &&
2081 filter_message_by_msginfo(filtering_rules, msginfo, ac_prefs)) {
2088 MsgInfo *procmsg_msginfo_new_from_mimeinfo(MsgInfo *src_msginfo, MimeInfo *mimeinfo)
2090 MsgInfo *tmp_msginfo = NULL;
2091 MsgFlags flags = {0, 0};
2092 gchar *tmpfile = get_tmp_file();
2093 FILE *fp = g_fopen(tmpfile, "wb");
2095 if (!mimeinfo || mimeinfo->type != MIMETYPE_MESSAGE ||
2096 g_ascii_strcasecmp(mimeinfo->subtype, "rfc822")) {
2097 g_warning("procmsg_msginfo_new_from_mimeinfo(): unsuitable mimeinfo");
2104 if (fp && procmime_write_mimeinfo(mimeinfo, fp) >= 0) {
2107 tmp_msginfo = procheader_parse_file(
2114 if (tmp_msginfo != NULL) {
2116 tmp_msginfo->folder = src_msginfo->folder;
2117 tmp_msginfo->plaintext_file = g_strdup(tmpfile);
2119 g_warning("procmsg_msginfo_new_from_mimeinfo(): Can't generate new msginfo");
2127 static GSList *spam_learners = NULL;
2129 void procmsg_register_spam_learner (int (*learn_func)(MsgInfo *info, GSList *list, gboolean spam))
2131 if (!g_slist_find(spam_learners, learn_func))
2132 spam_learners = g_slist_append(spam_learners, learn_func);
2133 if (mainwindow_get_mainwindow()) {
2134 main_window_set_menu_sensitive(mainwindow_get_mainwindow());
2135 summary_set_menu_sensitive(
2136 mainwindow_get_mainwindow()->summaryview);
2137 toolbar_main_set_sensitive(mainwindow_get_mainwindow());
2141 void procmsg_unregister_spam_learner (int (*learn_func)(MsgInfo *info, GSList *list, gboolean spam))
2143 spam_learners = g_slist_remove(spam_learners, learn_func);
2144 if (mainwindow_get_mainwindow()) {
2145 main_window_set_menu_sensitive(mainwindow_get_mainwindow());
2146 summary_set_menu_sensitive(
2147 mainwindow_get_mainwindow()->summaryview);
2148 toolbar_main_set_sensitive(mainwindow_get_mainwindow());
2152 gboolean procmsg_spam_can_learn(void)
2154 return g_slist_length(spam_learners) > 0;
2157 int procmsg_spam_learner_learn (MsgInfo *info, GSList *list, gboolean spam)
2159 GSList *cur = spam_learners;
2161 for (; cur; cur = cur->next) {
2162 int ((*func)(MsgInfo *info, GSList *list, gboolean spam)) = cur->data;
2163 ret |= func(info, list, spam);
2168 static gchar *spam_folder_item = NULL;
2169 void procmsg_spam_set_folder (const char *item_identifier)
2171 g_free(spam_folder_item);
2172 if (item_identifier)
2173 spam_folder_item = g_strdup(item_identifier);
2175 spam_folder_item = NULL;
2178 FolderItem *procmsg_spam_get_folder (void)
2180 FolderItem *item = spam_folder_item ? folder_find_item_from_identifier(spam_folder_item) : NULL;
2181 return item ? item : folder_get_default_trash();