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"
51 static gint procmsg_send_message_queue_full(const gchar *file, gboolean keep_session, gchar **errstr);
59 Q_MAIL_ACCOUNT_ID = 4,
60 Q_NEWS_ACCOUNT_ID = 5,
61 Q_SAVE_COPY_FOLDER = 6,
62 Q_REPLY_MESSAGE_ID = 7,
70 GHashTable *procmsg_msg_hash_table_create(GSList *mlist)
72 GHashTable *msg_table;
74 if (mlist == NULL) return NULL;
76 msg_table = g_hash_table_new(NULL, g_direct_equal);
77 procmsg_msg_hash_table_append(msg_table, mlist);
82 void procmsg_msg_hash_table_append(GHashTable *msg_table, GSList *mlist)
87 if (msg_table == NULL || mlist == NULL) return;
89 for (cur = mlist; cur != NULL; cur = cur->next) {
90 msginfo = (MsgInfo *)cur->data;
92 g_hash_table_insert(msg_table,
93 GUINT_TO_POINTER(msginfo->msgnum),
98 GHashTable *procmsg_to_folder_hash_table_create(GSList *mlist)
100 GHashTable *msg_table;
104 if (mlist == NULL) return NULL;
106 msg_table = g_hash_table_new(NULL, g_direct_equal);
108 for (cur = mlist; cur != NULL; cur = cur->next) {
109 msginfo = (MsgInfo *)cur->data;
110 g_hash_table_insert(msg_table, msginfo->to_folder, msginfo);
116 gint procmsg_get_last_num_in_msg_list(GSList *mlist)
122 for (cur = mlist; cur != NULL; cur = cur->next) {
123 msginfo = (MsgInfo *)cur->data;
124 if (msginfo && msginfo->msgnum > last)
125 last = msginfo->msgnum;
131 void procmsg_msg_list_free(GSList *mlist)
136 for (cur = mlist; cur != NULL; cur = cur->next) {
137 msginfo = (MsgInfo *)cur->data;
138 procmsg_msginfo_free(msginfo);
152 /* CLAWS subject threading:
154 in the first round it inserts subject lines in a
155 relation (subject <-> node)
157 the second round finishes the threads by attaching
158 matching subject lines to the one found in the
159 relation. will use the oldest node with the same
160 subject that is not more then thread_by_subject_max_age
161 days old (see subject_relation_lookup)
164 static void subject_relation_insert(GRelation *relation, GNode *node)
169 g_return_if_fail(relation != NULL);
170 g_return_if_fail(node != NULL);
171 msginfo = (MsgInfo *) node->data;
172 g_return_if_fail(msginfo != NULL);
174 subject = msginfo->subject;
177 subject += subject_get_prefix_length(subject);
179 g_relation_insert(relation, subject, node);
182 static GNode *subject_relation_lookup(GRelation *relation, MsgInfo *msginfo)
189 g_return_val_if_fail(relation != NULL, NULL);
191 subject = msginfo->subject;
194 prefix_length = subject_get_prefix_length(subject);
195 if (prefix_length <= 0)
197 subject += prefix_length;
199 tuples = g_relation_select(relation, subject, 0);
203 if (tuples->len > 0) {
205 GNode *relation_node;
206 MsgInfo *relation_msginfo = NULL, *best_msginfo = NULL;
209 /* check all nodes with the same subject to find the best parent */
210 for (i = 0; i < tuples->len; i++) {
211 relation_node = (GNode *) g_tuples_index(tuples, i, 1);
212 relation_msginfo = (MsgInfo *) relation_node->data;
215 /* best node should be the oldest in the found nodes */
216 /* parent node must not be older then msginfo */
217 if ((relation_msginfo->date_t < msginfo->date_t) &&
218 ((best_msginfo == NULL) ||
219 (best_msginfo->date_t > relation_msginfo->date_t)))
222 /* parent node must not be more then thread_by_subject_max_age
223 days older then msginfo */
224 if (abs(difftime(msginfo->date_t, relation_msginfo->date_t)) >
225 prefs_common.thread_by_subject_max_age * 3600 * 24)
228 /* can add new tests for all matching
229 nodes found by subject */
232 node = relation_node;
233 best_msginfo = relation_msginfo;
238 g_tuples_destroy(tuples);
242 /* return the reversed thread tree */
243 GNode *procmsg_get_thread_tree(GSList *mlist)
245 GNode *root, *parent, *node, *next;
246 GHashTable *msgid_table;
247 GRelation *subject_relation;
251 START_TIMING("procmsg_get_thread_tree");
252 root = g_node_new(NULL);
253 msgid_table = g_hash_table_new(g_str_hash, g_str_equal);
254 subject_relation = g_relation_new(2);
255 g_relation_index(subject_relation, 0, g_str_hash, g_str_equal);
257 for (; mlist != NULL; mlist = mlist->next) {
258 msginfo = (MsgInfo *)mlist->data;
261 if (msginfo->inreplyto) {
262 parent = g_hash_table_lookup(msgid_table, msginfo->inreplyto);
263 if (parent == NULL) {
267 node = g_node_insert_data_before
268 (parent, parent == root ? parent->children : NULL,
270 if ((msgid = msginfo->msgid) && g_hash_table_lookup(msgid_table, msgid) == NULL)
271 g_hash_table_insert(msgid_table, (gchar *)msgid, node);
273 /* CLAWS: add subject to relation (without prefix) */
274 if (prefs_common.thread_by_subject) {
275 subject_relation_insert(subject_relation, node);
279 /* complete the unfinished threads */
280 for (node = root->children; node != NULL; ) {
282 msginfo = (MsgInfo *)node->data;
285 if (msginfo->inreplyto)
286 parent = g_hash_table_lookup(msgid_table, msginfo->inreplyto);
288 /* try looking for the indirect parent */
289 if (!parent && msginfo->references) {
290 for (reflist = msginfo->references;
291 reflist != NULL; reflist = reflist->next)
292 if ((parent = g_hash_table_lookup
293 (msgid_table, reflist->data)) != NULL)
297 /* node should not be the parent, and node should not
298 be an ancestor of parent (circular reference) */
299 if (parent && parent != node &&
300 !g_node_is_ancestor(node, parent)) {
303 (parent, parent->children, node);
309 if (prefs_common.thread_by_subject) {
310 START_TIMING("procmsg_get_thread_tree(1)");
311 for (node = root->children; node && node != NULL;) {
313 msginfo = (MsgInfo *) node->data;
315 parent = subject_relation_lookup(subject_relation, msginfo);
317 /* the node may already be threaded by IN-REPLY-TO, so go up
319 find the parent node */
320 if (parent != NULL) {
321 if (g_node_is_ancestor(node, parent))
329 g_node_append(parent, node);
337 g_relation_destroy(subject_relation);
338 g_hash_table_destroy(msgid_table);
343 gint procmsg_move_messages(GSList *mlist)
345 GSList *cur, *movelist = NULL;
347 FolderItem *dest = NULL;
349 gboolean finished = TRUE;
350 if (!mlist) return 0;
352 folder_item_update_freeze();
355 for (cur = mlist; cur != NULL; cur = cur->next) {
356 msginfo = (MsgInfo *)cur->data;
357 if (!msginfo->to_folder) {
363 dest = msginfo->to_folder;
364 movelist = g_slist_prepend(movelist, msginfo);
365 } else if (dest == msginfo->to_folder) {
366 movelist = g_slist_prepend(movelist, msginfo);
370 procmsg_msginfo_set_to_folder(msginfo, NULL);
373 movelist = g_slist_reverse(movelist);
374 retval |= folder_item_move_msgs(dest, movelist);
375 g_slist_free(movelist);
378 if (finished == FALSE) {
384 folder_item_update_thaw();
388 void procmsg_copy_messages(GSList *mlist)
390 GSList *cur, *copylist = NULL;
392 FolderItem *dest = NULL;
393 gboolean finished = TRUE;
396 folder_item_update_freeze();
399 for (cur = mlist; cur != NULL; cur = cur->next) {
400 msginfo = (MsgInfo *)cur->data;
401 if (!msginfo->to_folder) {
407 dest = msginfo->to_folder;
408 copylist = g_slist_prepend(copylist, msginfo);
409 } else if (dest == msginfo->to_folder) {
410 copylist = g_slist_prepend(copylist, msginfo);
414 procmsg_msginfo_set_to_folder(msginfo, NULL);
417 copylist = g_slist_reverse(copylist);
418 folder_item_copy_msgs(dest, copylist);
419 g_slist_free(copylist);
422 if (finished == FALSE) {
428 folder_item_update_thaw();
431 gchar *procmsg_get_message_file_path(MsgInfo *msginfo)
435 g_return_val_if_fail(msginfo != NULL, NULL);
437 if (msginfo->plaintext_file)
438 file = g_strdup(msginfo->plaintext_file);
440 file = folder_item_fetch_msg(msginfo->folder, msginfo->msgnum);
446 gchar *procmsg_get_message_file(MsgInfo *msginfo)
448 gchar *filename = NULL;
450 g_return_val_if_fail(msginfo != NULL, NULL);
452 filename = folder_item_fetch_msg(msginfo->folder, msginfo->msgnum);
454 debug_print("can't fetch message %d\n", msginfo->msgnum);
459 gchar *procmsg_get_message_file_full(MsgInfo *msginfo, gboolean headers, gboolean body)
461 gchar *filename = NULL;
463 g_return_val_if_fail(msginfo != NULL, NULL);
465 filename = folder_item_fetch_msg_full(msginfo->folder, msginfo->msgnum,
468 debug_print("can't fetch message %d\n", msginfo->msgnum);
473 GSList *procmsg_get_message_file_list(GSList *mlist)
475 GSList *file_list = NULL;
477 MsgFileInfo *fileinfo;
480 while (mlist != NULL) {
481 msginfo = (MsgInfo *)mlist->data;
482 file = procmsg_get_message_file(msginfo);
484 procmsg_message_file_list_free(file_list);
487 fileinfo = g_new(MsgFileInfo, 1);
488 fileinfo->msginfo = procmsg_msginfo_new_ref(msginfo);
489 fileinfo->file = file;
490 fileinfo->flags = g_new(MsgFlags, 1);
491 *fileinfo->flags = msginfo->flags;
492 file_list = g_slist_prepend(file_list, fileinfo);
496 file_list = g_slist_reverse(file_list);
501 void procmsg_message_file_list_free(MsgInfoList *file_list)
504 MsgFileInfo *fileinfo;
506 for (cur = file_list; cur != NULL; cur = cur->next) {
507 fileinfo = (MsgFileInfo *)cur->data;
508 procmsg_msginfo_free(fileinfo->msginfo);
509 g_free(fileinfo->file);
510 g_free(fileinfo->flags);
514 g_slist_free(file_list);
517 FILE *procmsg_open_message(MsgInfo *msginfo)
522 g_return_val_if_fail(msginfo != NULL, NULL);
524 file = procmsg_get_message_file_path(msginfo);
525 g_return_val_if_fail(file != NULL, NULL);
527 if (!is_file_exist(file)) {
529 file = procmsg_get_message_file(msginfo);
534 if ((fp = g_fopen(file, "rb")) == NULL) {
535 FILE_OP_ERROR(file, "fopen");
542 if (MSG_IS_QUEUED(msginfo->flags) || MSG_IS_DRAFT(msginfo->flags)) {
545 while (fgets(buf, sizeof(buf), fp) != NULL) {
547 if (!strncmp(buf, "X-Sylpheed-End-Special-Headers: 1",
548 strlen("X-Sylpheed-End-Special-Headers:")))
551 if (buf[0] == '\r' || buf[0] == '\n') break;
552 /* from other mailers */
553 if (!strncmp(buf, "Date: ", 6)
554 || !strncmp(buf, "To: ", 4)
555 || !strncmp(buf, "From: ", 6)
556 || !strncmp(buf, "Subject: ", 9)) {
566 gboolean procmsg_msg_exist(MsgInfo *msginfo)
571 if (!msginfo) return FALSE;
573 path = folder_item_get_path(msginfo->folder);
575 ret = !folder_item_is_msg_changed(msginfo->folder, msginfo);
581 void procmsg_get_filter_keyword(MsgInfo *msginfo, gchar **header, gchar **key,
582 PrefsFilterType type)
584 static HeaderEntry hentry[] = {{"X-BeenThere:", NULL, TRUE},
585 {"X-ML-Name:", NULL, TRUE},
586 {"X-List:", NULL, TRUE},
587 {"X-Mailing-list:", NULL, TRUE},
588 {"List-Id:", NULL, TRUE},
589 {"X-Sequence:", NULL, TRUE},
590 {"Sender:", NULL, TRUE},
591 {"List-Post:", NULL, TRUE},
592 {NULL, NULL, FALSE}};
598 H_X_MAILING_LIST = 3,
607 g_return_if_fail(msginfo != NULL);
608 g_return_if_fail(header != NULL);
609 g_return_if_fail(key != NULL);
618 if ((fp = procmsg_open_message(msginfo)) == NULL)
620 procheader_get_header_fields(fp, hentry);
623 #define SET_FILTER_KEY(hstr, idx) \
625 *header = g_strdup(hstr); \
626 *key = hentry[idx].body; \
627 hentry[idx].body = NULL; \
630 if (hentry[H_X_BEENTHERE].body != NULL) {
631 SET_FILTER_KEY("header \"X-BeenThere\"", H_X_BEENTHERE);
632 } else if (hentry[H_X_ML_NAME].body != NULL) {
633 SET_FILTER_KEY("header \"X-ML-Name\"", H_X_ML_NAME);
634 } else if (hentry[H_X_LIST].body != NULL) {
635 SET_FILTER_KEY("header \"X-List\"", H_X_LIST);
636 } else if (hentry[H_X_MAILING_LIST].body != NULL) {
637 SET_FILTER_KEY("header \"X-Mailing-List\"", H_X_MAILING_LIST);
638 } else if (hentry[H_LIST_ID].body != NULL) {
639 SET_FILTER_KEY("header \"List-Id\"", H_LIST_ID);
640 extract_list_id_str(*key);
641 } else if (hentry[H_X_SEQUENCE].body != NULL) {
644 SET_FILTER_KEY("X-Sequence", H_X_SEQUENCE);
647 while (*p != '\0' && !g_ascii_isspace(*p)) p++;
648 while (g_ascii_isspace(*p)) p++;
649 if (g_ascii_isdigit(*p)) {
655 } else if (hentry[H_SENDER].body != NULL) {
656 SET_FILTER_KEY("header \"Sender\"", H_SENDER);
657 } else if (hentry[H_LIST_POST].body != NULL) {
658 SET_FILTER_KEY("header \"List-Post\"", H_LIST_POST);
659 } else if (msginfo->to) {
660 *header = g_strdup("to");
661 *key = g_strdup(msginfo->to);
662 } else if (msginfo->subject) {
663 *header = g_strdup("subject");
664 *key = g_strdup(msginfo->subject);
667 #undef SET_FILTER_KEY
669 g_free(hentry[H_X_BEENTHERE].body);
670 hentry[H_X_BEENTHERE].body = NULL;
671 g_free(hentry[H_X_ML_NAME].body);
672 hentry[H_X_ML_NAME].body = NULL;
673 g_free(hentry[H_X_LIST].body);
674 hentry[H_X_LIST].body = NULL;
675 g_free(hentry[H_X_MAILING_LIST].body);
676 hentry[H_X_MAILING_LIST].body = NULL;
677 g_free(hentry[H_LIST_ID].body);
678 hentry[H_LIST_ID].body = NULL;
679 g_free(hentry[H_SENDER].body);
680 hentry[H_SENDER].body = NULL;
681 g_free(hentry[H_LIST_POST].body);
682 hentry[H_LIST_POST].body = NULL;
686 *header = g_strdup("from");
687 *key = g_strdup(msginfo->from);
690 *header = g_strdup("to");
691 *key = g_strdup(msginfo->to);
693 case FILTER_BY_SUBJECT:
694 *header = g_strdup("subject");
695 *key = g_strdup(msginfo->subject);
702 void procmsg_empty_trash(FolderItem *trash)
707 (trash->stype != F_TRASH &&
708 !folder_has_parent_of_type(trash, F_TRASH)))
711 if (trash && trash->total_msgs > 0) {
712 GSList *mlist = folder_item_get_msg_list(trash);
714 for (cur = mlist ; cur != NULL ; cur = cur->next) {
715 MsgInfo * msginfo = (MsgInfo *) cur->data;
716 if (MSG_IS_LOCKED(msginfo->flags))
718 if (msginfo->total_size != 0 &&
719 msginfo->size != (off_t)msginfo->total_size)
720 partial_mark_for_delete(msginfo);
722 procmsg_msginfo_free(msginfo);
725 folder_item_remove_all_msg(trash);
728 if (!trash->node || !trash->node->children)
731 node = trash->node->children;
732 while (node != NULL) {
734 procmsg_empty_trash(FOLDER_ITEM(node->data));
739 void procmsg_empty_all_trash(void)
744 for (cur = folder_get_list(); cur != NULL; cur = cur->next) {
745 Folder *folder = FOLDER(cur->data);
746 trash = folder->trash;
747 procmsg_empty_trash(trash);
748 if (folder->account && folder->account->set_trash_folder &&
749 folder_find_item_from_identifier(folder->account->trash_folder))
751 folder_find_item_from_identifier(folder->account->trash_folder));
755 static PrefsAccount *procmsg_get_account_from_file(const gchar *file)
757 PrefsAccount *mailac = NULL;
761 static HeaderEntry qentry[] = {{"S:", NULL, FALSE},
762 {"SSV:", NULL, FALSE},
764 {"NG:", NULL, FALSE},
765 {"MAID:", NULL, FALSE},
766 {"NAID:", NULL, FALSE},
767 {"SCF:", NULL, FALSE},
768 {"RMID:", NULL, FALSE},
769 {"FMID:", NULL, FALSE},
770 {"X-Sylpheed-Privacy-System:", NULL, FALSE},
771 {"X-Sylpheed-Encrypt:", NULL, FALSE},
772 {"X-Sylpheed-Encrypt-Data:", NULL, FALSE},
773 {NULL, NULL, FALSE}};
775 g_return_val_if_fail(file != NULL, NULL);
777 if ((fp = g_fopen(file, "rb")) == NULL) {
778 FILE_OP_ERROR(file, "fopen");
782 while ((hnum = procheader_get_one_field(buf, sizeof(buf), fp, qentry))
784 gchar *p = buf + strlen(qentry[hnum].name);
786 if (hnum == Q_MAIL_ACCOUNT_ID) {
787 mailac = account_find_from_id(atoi(p));
795 static GSList *procmsg_list_sort_by_account(FolderItem *queue, GSList *list)
797 GSList *result = NULL;
799 PrefsAccount *last_account = NULL;
802 gboolean nothing_to_sort = TRUE;
807 orig = g_slist_copy(list);
809 msg = (MsgInfo *)orig->data;
811 for (cur = orig; cur; cur = cur->next)
812 debug_print("sort before %s\n", ((MsgInfo *)cur->data)->from);
817 nothing_to_sort = TRUE;
821 PrefsAccount *ac = NULL;
822 msg = (MsgInfo *)cur->data;
823 file = folder_item_fetch_msg(queue, msg->msgnum);
824 ac = procmsg_get_account_from_file(file);
827 if (last_account == NULL || (ac != NULL && ac == last_account)) {
828 result = g_slist_append(result, msg);
829 orig = g_slist_remove(orig, msg);
831 nothing_to_sort = FALSE;
837 if (orig || g_slist_length(orig)) {
838 if (!last_account && nothing_to_sort) {
839 /* can't find an account for the rest of the list */
842 result = g_slist_append(result, cur->data);
853 for (cur = result; cur; cur = cur->next)
854 debug_print("sort after %s\n", ((MsgInfo *)cur->data)->from);
861 static gboolean procmsg_is_last_for_account(FolderItem *queue, MsgInfo *msginfo, GSList *elem)
863 gchar *file = folder_item_fetch_msg(queue, msginfo->msgnum);
864 PrefsAccount *ac = procmsg_get_account_from_file(file);
867 for (cur = elem; cur; cur = cur->next) {
868 MsgInfo *cur_msginfo = (MsgInfo *)cur->data;
869 file = folder_item_fetch_msg(queue, cur_msginfo->msgnum);
871 if (cur_msginfo != msginfo && !MSG_IS_LOCKED(cur_msginfo->flags)) {
872 if (procmsg_get_account_from_file(file) == ac) {
883 static gboolean send_queue_lock = FALSE;
885 *\brief Send messages in queue
887 *\param queue Queue folder to process
888 *\param save_msgs Unused
890 *\return Number of messages sent, negative if an error occurred
891 * positive if no error occurred
893 gint procmsg_send_queue(FolderItem *queue, gboolean save_msgs, gchar **errstr)
895 gint sent = 0, err = 0;
897 GSList *sorted_list = NULL;
900 if (send_queue_lock) {
901 log_error(_("Already trying to send\n"));
903 if (*errstr) g_free(*errstr);
904 *errstr = g_strdup_printf(_("Already trying to send."));
906 toolbar_main_set_sensitive(mainwindow_get_mainwindow());
909 send_queue_lock = TRUE;
912 queue = folder_get_default_queue();
915 send_queue_lock = FALSE;
920 toolbar_main_set_sensitive(mainwindow_get_mainwindow());
922 folder_item_scan(queue);
923 list = folder_item_get_msg_list(queue);
925 /* sort the list per sender account; this helps reusing the same SMTP server */
926 sorted_list = procmsg_list_sort_by_account(queue, list);
928 for (elem = sorted_list; elem != NULL; elem = elem->next) {
932 msginfo = (MsgInfo *)(elem->data);
933 if (!MSG_IS_LOCKED(msginfo->flags)) {
934 file = folder_item_fetch_msg(queue, msginfo->msgnum);
936 if (procmsg_send_message_queue_full(file,
937 !procmsg_is_last_for_account(queue, msginfo, elem),
939 g_warning("Sending queued message %d failed.\n",
944 folder_item_remove_msg(queue, msginfo->msgnum);
949 /* FIXME: supposedly if only one message is locked, and queue
950 * is being flushed, the following free says something like
951 * "freeing msg ## in folder (nil)". */
952 procmsg_msginfo_free(msginfo);
955 g_slist_free(sorted_list);
956 folder_item_scan(queue);
958 if (queue->node && queue->node->children) {
959 node = queue->node->children;
960 while (node != NULL) {
963 res = procmsg_send_queue(FOLDER_ITEM(node->data), save_msgs, errstr);
971 send_queue_lock = FALSE;
973 toolbar_main_set_sensitive(mainwindow_get_mainwindow());
975 return (err != 0 ? -err : sent);
978 gboolean procmsg_is_sending(void)
980 return send_queue_lock;
984 *\brief Determine if a queue folder is empty
986 *\param queue Queue folder to process
988 *\return TRUE if the queue folder is empty, otherwise return FALSE
990 gboolean procmsg_queue_is_empty(FolderItem *queue)
993 gboolean res = FALSE;
995 queue = folder_get_default_queue();
996 g_return_val_if_fail(queue != NULL, TRUE);
998 folder_item_scan(queue);
999 list = folder_item_get_msg_list(queue);
1000 res = (list == NULL);
1001 procmsg_msg_list_free(list);
1005 if (queue->node && queue->node->children) {
1006 node = queue->node->children;
1007 while (node != NULL) {
1009 if (!procmsg_queue_is_empty(FOLDER_ITEM(node->data)))
1018 gint procmsg_remove_special_headers(const gchar *in, const gchar *out)
1021 gchar buf[BUFFSIZE];
1023 if ((fp = g_fopen(in, "rb")) == NULL) {
1024 FILE_OP_ERROR(in, "fopen");
1027 if ((outfp = g_fopen(out, "wb")) == NULL) {
1028 FILE_OP_ERROR(out, "fopen");
1032 while (fgets(buf, sizeof(buf), fp) != NULL) {
1034 if (!strncmp(buf, "X-Sylpheed-End-Special-Headers: 1",
1035 strlen("X-Sylpheed-End-Special-Headers:")))
1038 if (buf[0] == '\r' || buf[0] == '\n') break;
1039 /* from other mailers */
1040 if (!strncmp(buf, "Date: ", 6)
1041 || !strncmp(buf, "To: ", 4)
1042 || !strncmp(buf, "From: ", 6)
1043 || !strncmp(buf, "Subject: ", 9)) {
1048 while (fgets(buf, sizeof(buf), fp) != NULL)
1055 gint procmsg_save_to_outbox(FolderItem *outbox, const gchar *file,
1059 MsgInfo *msginfo, *tmp_msginfo;
1060 MsgFlags flag = {0, 0};
1062 debug_print("saving sent message...\n");
1065 outbox = folder_get_default_outbox();
1066 g_return_val_if_fail(outbox != NULL, -1);
1068 /* remove queueing headers */
1070 gchar tmp[MAXPATHLEN + 1];
1072 g_snprintf(tmp, sizeof(tmp), "%s%ctmpmsg.out.%08x",
1073 get_rc_dir(), G_DIR_SEPARATOR, (guint) rand());
1075 if (procmsg_remove_special_headers(file, tmp) !=0)
1078 folder_item_scan(outbox);
1079 if ((num = folder_item_add_msg(outbox, tmp, &flag, TRUE)) < 0) {
1080 g_warning("can't save message\n");
1085 folder_item_scan(outbox);
1086 if ((num = folder_item_add_msg
1087 (outbox, file, &flag, FALSE)) < 0) {
1088 g_warning("can't save message\n");
1092 msginfo = folder_item_get_msginfo(outbox, num); /* refcnt++ */
1093 tmp_msginfo = procmsg_msginfo_get_full_info(msginfo); /* refcnt++ */
1094 if (msginfo != NULL) {
1095 procmsg_msginfo_unset_flags(msginfo, ~0, 0);
1096 procmsg_msginfo_free(msginfo); /* refcnt-- */
1097 /* tmp_msginfo == msginfo */
1098 if (tmp_msginfo && (msginfo->dispositionnotificationto ||
1099 msginfo->returnreceiptto)) {
1100 procmsg_msginfo_set_flags(msginfo, MSG_RETRCPT_SENT, 0);
1102 procmsg_msginfo_free(tmp_msginfo); /* refcnt-- */
1108 void procmsg_print_message(MsgInfo *msginfo, const gchar *cmdline)
1110 static const gchar *def_cmd = "lpr %s";
1111 static guint id = 0;
1117 g_return_if_fail(msginfo);
1119 if (procmime_msginfo_is_encrypted(msginfo))
1120 tmpfp = procmime_get_first_encrypted_text_content(msginfo);
1122 tmpfp = procmime_get_first_text_content(msginfo);
1123 if (tmpfp == NULL) {
1124 g_warning("Can't get text part\n");
1128 prtmp = g_strdup_printf("%s%cprinttmp.%08x",
1129 get_mime_tmp_dir(), G_DIR_SEPARATOR, id++);
1131 if ((prfp = g_fopen(prtmp, "wb")) == NULL) {
1132 FILE_OP_ERROR(prtmp, "fopen");
1138 if (msginfo->date) fprintf(prfp, "Date: %s\n", msginfo->date);
1139 if (msginfo->from) fprintf(prfp, "From: %s\n", msginfo->from);
1140 if (msginfo->to) fprintf(prfp, "To: %s\n", msginfo->to);
1141 if (msginfo->cc) fprintf(prfp, "Cc: %s\n", msginfo->cc);
1142 if (msginfo->newsgroups)
1143 fprintf(prfp, "Newsgroups: %s\n", msginfo->newsgroups);
1144 if (msginfo->subject) fprintf(prfp, "Subject: %s\n", msginfo->subject);
1147 while (fgets(buf, sizeof(buf), tmpfp) != NULL)
1153 if (cmdline && (p = strchr(cmdline, '%')) && *(p + 1) == 's' &&
1154 !strchr(p + 2, '%'))
1155 g_snprintf(buf, sizeof(buf) - 1, cmdline, prtmp);
1158 g_warning("Print command line is invalid: '%s'\n",
1160 g_snprintf(buf, sizeof(buf) - 1, def_cmd, prtmp);
1166 if (buf[strlen(buf) - 1] != '&') strcat(buf, "&");
1170 MsgInfo *procmsg_msginfo_new_ref(MsgInfo *msginfo)
1177 MsgInfo *procmsg_msginfo_new(void)
1179 MsgInfo *newmsginfo;
1181 newmsginfo = g_new0(MsgInfo, 1);
1182 newmsginfo->refcnt = 1;
1187 MsgInfo *procmsg_msginfo_copy(MsgInfo *msginfo)
1189 MsgInfo *newmsginfo;
1192 if (msginfo == NULL) return NULL;
1194 newmsginfo = g_new0(MsgInfo, 1);
1196 newmsginfo->refcnt = 1;
1198 #define MEMBCOPY(mmb) newmsginfo->mmb = msginfo->mmb
1199 #define MEMBDUP(mmb) newmsginfo->mmb = msginfo->mmb ? \
1200 g_strdup(msginfo->mmb) : NULL
1215 MEMBDUP(newsgroups);
1222 MEMBCOPY(to_folder);
1226 MEMBDUP(dispositionnotificationto);
1227 MEMBDUP(returnreceiptto);
1229 refs = msginfo->references;
1230 for (refs = msginfo->references; refs != NULL; refs = refs->next) {
1231 newmsginfo->references = g_slist_prepend
1232 (newmsginfo->references, g_strdup(refs->data));
1234 newmsginfo->references = g_slist_reverse(newmsginfo->references);
1237 MEMBDUP(plaintext_file);
1242 MsgInfo *procmsg_msginfo_get_full_info(MsgInfo *msginfo)
1244 MsgInfo *full_msginfo;
1247 if (msginfo == NULL) return NULL;
1249 file = procmsg_get_message_file_path(msginfo);
1250 if (!file || !is_file_exist(file)) {
1252 file = procmsg_get_message_file(msginfo);
1254 if (!file || !is_file_exist(file)) {
1255 g_warning("procmsg_msginfo_get_full_info(): can't get message file.\n");
1259 full_msginfo = procheader_parse_file(file, msginfo->flags, TRUE, FALSE);
1261 if (!full_msginfo) return NULL;
1263 /* CLAWS: make sure we add the missing members; see:
1264 * procheader.c::procheader_get_headernames() */
1265 if (!msginfo->list_post)
1266 msginfo->list_post = g_strdup(full_msginfo->list_post);
1267 if (!msginfo->list_subscribe)
1268 msginfo->list_subscribe = g_strdup(full_msginfo->list_subscribe);
1269 if (!msginfo->list_unsubscribe)
1270 msginfo->list_unsubscribe = g_strdup(full_msginfo->list_unsubscribe);
1271 if (!msginfo->list_help)
1272 msginfo->list_help = g_strdup(full_msginfo->list_help);
1273 if (!msginfo->list_archive)
1274 msginfo->list_archive= g_strdup(full_msginfo->list_archive);
1275 if (!msginfo->list_owner)
1276 msginfo->list_owner = g_strdup(full_msginfo->list_owner);
1277 if (!msginfo->xface)
1278 msginfo->xface = g_strdup(full_msginfo->xface);
1280 msginfo->face = g_strdup(full_msginfo->face);
1281 if (!msginfo->dispositionnotificationto)
1282 msginfo->dispositionnotificationto =
1283 g_strdup(full_msginfo->dispositionnotificationto);
1284 if (!msginfo->returnreceiptto)
1285 msginfo->returnreceiptto = g_strdup
1286 (full_msginfo->returnreceiptto);
1287 if (!msginfo->partial_recv && full_msginfo->partial_recv)
1288 msginfo->partial_recv = g_strdup
1289 (full_msginfo->partial_recv);
1290 msginfo->total_size = full_msginfo->total_size;
1291 if (!msginfo->account_server && full_msginfo->account_server)
1292 msginfo->account_server = g_strdup
1293 (full_msginfo->account_server);
1294 if (!msginfo->account_login && full_msginfo->account_login)
1295 msginfo->account_login = g_strdup
1296 (full_msginfo->account_login);
1297 msginfo->planned_download = full_msginfo->planned_download;
1298 procmsg_msginfo_free(full_msginfo);
1300 return procmsg_msginfo_new_ref(msginfo);
1303 void procmsg_msginfo_free(MsgInfo *msginfo)
1305 if (msginfo == NULL) return;
1308 if (msginfo->refcnt > 0)
1311 if (msginfo->to_folder) {
1312 msginfo->to_folder->op_count--;
1313 folder_item_update(msginfo->to_folder, F_ITEM_UPDATE_MSGCNT);
1316 g_free(msginfo->fromspace);
1317 g_free(msginfo->returnreceiptto);
1318 g_free(msginfo->dispositionnotificationto);
1319 g_free(msginfo->xface);
1320 g_free(msginfo->face);
1322 g_free(msginfo->fromname);
1324 g_free(msginfo->date);
1325 g_free(msginfo->from);
1326 g_free(msginfo->to);
1327 g_free(msginfo->cc);
1328 g_free(msginfo->newsgroups);
1329 g_free(msginfo->subject);
1330 g_free(msginfo->msgid);
1331 g_free(msginfo->inreplyto);
1332 g_free(msginfo->xref);
1334 g_free(msginfo->list_post);
1335 g_free(msginfo->list_subscribe);
1336 g_free(msginfo->list_unsubscribe);
1337 g_free(msginfo->list_help);
1338 g_free(msginfo->list_archive);
1339 g_free(msginfo->list_owner);
1341 g_free(msginfo->partial_recv);
1342 g_free(msginfo->account_server);
1343 g_free(msginfo->account_login);
1345 slist_free_strings(msginfo->references);
1346 g_slist_free(msginfo->references);
1348 g_free(msginfo->plaintext_file);
1353 guint procmsg_msginfo_memusage(MsgInfo *msginfo)
1358 memusage += sizeof(MsgInfo);
1359 if (msginfo->fromname)
1360 memusage += strlen(msginfo->fromname);
1362 memusage += strlen(msginfo->date);
1364 memusage += strlen(msginfo->from);
1366 memusage += strlen(msginfo->to);
1368 memusage += strlen(msginfo->cc);
1369 if (msginfo->newsgroups)
1370 memusage += strlen(msginfo->newsgroups);
1371 if (msginfo->subject)
1372 memusage += strlen(msginfo->subject);
1374 memusage += strlen(msginfo->msgid);
1375 if (msginfo->inreplyto)
1376 memusage += strlen(msginfo->inreplyto);
1378 memusage += strlen(msginfo->xface);
1380 memusage += strlen(msginfo->face);
1381 if (msginfo->dispositionnotificationto)
1382 memusage += strlen(msginfo->dispositionnotificationto);
1383 if (msginfo->returnreceiptto)
1384 memusage += strlen(msginfo->returnreceiptto);
1385 for (refs = msginfo->references; refs; refs=refs->next) {
1386 gchar *r = (gchar *)refs->data;
1387 memusage += r?strlen(r):0;
1389 if (msginfo->fromspace)
1390 memusage += strlen(msginfo->fromspace);
1395 gint procmsg_cmp_msgnum_for_sort(gconstpointer a, gconstpointer b)
1397 const MsgInfo *msginfo1 = a;
1398 const MsgInfo *msginfo2 = b;
1405 return msginfo1->msgnum - msginfo2->msgnum;
1408 static gint procmsg_send_message_queue_full(const gchar *file, gboolean keep_session, gchar **errstr)
1410 static HeaderEntry qentry[] = {{"S:", NULL, FALSE},
1411 {"SSV:", NULL, FALSE},
1412 {"R:", NULL, FALSE},
1413 {"NG:", NULL, FALSE},
1414 {"MAID:", NULL, FALSE},
1415 {"NAID:", NULL, FALSE},
1416 {"SCF:", NULL, FALSE},
1417 {"RMID:", NULL, FALSE},
1418 {"FMID:", NULL, FALSE},
1419 {"X-Sylpheed-Privacy-System:", NULL, FALSE},
1420 {"X-Sylpheed-Encrypt:", NULL, FALSE},
1421 {"X-Sylpheed-Encrypt-Data:", NULL, FALSE},
1422 {"X-Sylpheed-End-Special-Headers:", NULL, FALSE},
1423 {NULL, NULL, FALSE}};
1426 gint mailval = 0, newsval = 0;
1428 gchar *smtpserver = NULL;
1429 GSList *to_list = NULL;
1430 GSList *newsgroup_list = NULL;
1431 gchar *savecopyfolder = NULL;
1432 gchar *replymessageid = NULL;
1433 gchar *fwdmessageid = NULL;
1434 gchar *privacy_system = NULL;
1435 gboolean encrypt = FALSE;
1436 gchar *encrypt_data = NULL;
1437 gchar buf[BUFFSIZE];
1439 PrefsAccount *mailac = NULL, *newsac = NULL;
1440 gboolean save_clear_text = TRUE;
1441 gchar *tmp_enc_file = NULL;
1445 g_return_val_if_fail(file != NULL, -1);
1447 if ((fp = g_fopen(file, "rb")) == NULL) {
1448 FILE_OP_ERROR(file, "fopen");
1450 if (*errstr) g_free(*errstr);
1451 *errstr = g_strdup_printf(_("Couldn't open file %s."), file);
1456 while ((hnum = procheader_get_one_field(buf, sizeof(buf), fp, qentry))
1458 gchar *p = buf + strlen(qentry[hnum].name);
1466 if (smtpserver == NULL)
1467 smtpserver = g_strdup(p);
1470 to_list = address_list_append(to_list, p);
1473 newsgroup_list = newsgroup_list_append(newsgroup_list, p);
1475 case Q_MAIL_ACCOUNT_ID:
1476 mailac = account_find_from_id(atoi(p));
1478 case Q_NEWS_ACCOUNT_ID:
1479 newsac = account_find_from_id(atoi(p));
1481 case Q_SAVE_COPY_FOLDER:
1482 if (savecopyfolder == NULL)
1483 savecopyfolder = g_strdup(p);
1485 case Q_REPLY_MESSAGE_ID:
1486 if (replymessageid == NULL)
1487 replymessageid = g_strdup(p);
1489 case Q_FWD_MESSAGE_ID:
1490 if (fwdmessageid == NULL)
1491 fwdmessageid = g_strdup(p);
1493 case Q_PRIVACY_SYSTEM:
1494 if (privacy_system == NULL)
1495 privacy_system = g_strdup(p);
1501 case Q_ENCRYPT_DATA:
1502 if (encrypt_data == NULL)
1503 encrypt_data = g_strdup(p);
1505 case Q_SYLPHEED_HDRS:
1506 /* end of special headers reached */
1507 goto send_mail; /* can't "break;break;" */
1511 filepos = ftell(fp);
1516 if (mailac && mailac->save_encrypted_as_clear_text
1517 && !mailac->encrypt_to_self)
1518 save_clear_text = TRUE;
1520 save_clear_text = FALSE;
1525 mimeinfo = procmime_scan_queue_file(file);
1526 if (!privacy_encrypt(privacy_system, mimeinfo, encrypt_data)
1527 || (fp = my_tmpfile()) == NULL
1528 || procmime_write_mimeinfo(mimeinfo, fp) < 0) {
1531 procmime_mimeinfo_free_all(mimeinfo);
1534 slist_free_strings(to_list);
1535 g_slist_free(to_list);
1536 slist_free_strings(newsgroup_list);
1537 g_slist_free(newsgroup_list);
1538 g_free(savecopyfolder);
1539 g_free(replymessageid);
1540 g_free(fwdmessageid);
1541 g_free(privacy_system);
1542 g_free(encrypt_data);
1544 if (*errstr) g_free(*errstr);
1545 *errstr = g_strdup_printf(_("Couldn't encrypt the email: %s"),
1546 privacy_get_error());
1552 if (!save_clear_text) {
1553 gchar *content = NULL;
1554 FILE *tmpfp = get_tmpfile_in_dir(get_mime_tmp_dir(), &tmp_enc_file);
1558 content = file_read_stream_to_str(fp);
1561 str_write_to_file(content, tmp_enc_file);
1564 g_warning("couldn't get tempfile\n");
1568 procmime_mimeinfo_free_all(mimeinfo);
1574 debug_print("Sending message by mail\n");
1577 if (*errstr) g_free(*errstr);
1578 *errstr = g_strdup_printf(_("Queued message header is broken."));
1581 } else if (mailac && mailac->use_mail_command &&
1582 mailac->mail_command && (* mailac->mail_command)) {
1583 mailval = send_message_local(mailac->mail_command, fp);
1587 mailac = account_find_from_smtp_server(from, smtpserver);
1589 g_warning("Account not found. "
1590 "Using current account...\n");
1591 mailac = cur_account;
1596 mailval = send_message_smtp_full(mailac, to_list, fp, keep_session);
1597 if (mailval == -1 && errstr) {
1598 if (*errstr) g_free(*errstr);
1599 *errstr = g_strdup_printf(_("An error happened during SMTP session."));
1602 PrefsAccount tmp_ac;
1604 g_warning("Account not found.\n");
1606 memset(&tmp_ac, 0, sizeof(PrefsAccount));
1607 tmp_ac.address = from;
1608 tmp_ac.smtp_server = smtpserver;
1609 tmp_ac.smtpport = SMTP_PORT;
1610 mailval = send_message_smtp(&tmp_ac, to_list, fp);
1611 if (mailval == -1 && errstr) {
1612 if (*errstr) g_free(*errstr);
1613 *errstr = g_strdup_printf(_("No specific account has been found to "
1614 "send, and an error happened during SMTP session."));
1618 } else if (!to_list && !newsgroup_list) {
1620 if (*errstr) g_free(*errstr);
1621 *errstr = g_strdup(_("Couldn't determine sending informations. "
1622 "Maybe the email hasn't been generated by Sylpheed-Claws."));
1627 fseek(fp, filepos, SEEK_SET);
1628 if (newsgroup_list && (mailval == 0)) {
1633 /* write to temporary file */
1634 tmp = g_strdup_printf("%s%ctmp%d", g_get_tmp_dir(),
1635 G_DIR_SEPARATOR, (gint)file);
1636 if ((tmpfp = g_fopen(tmp, "wb")) == NULL) {
1637 FILE_OP_ERROR(tmp, "fopen");
1639 alertpanel_error(_("Could not create temporary file for news sending."));
1641 if (change_file_mode_rw(tmpfp, tmp) < 0) {
1642 FILE_OP_ERROR(tmp, "chmod");
1643 g_warning("can't change file mode\n");
1646 while ((newsval == 0) && fgets(buf, sizeof(buf), fp) != NULL) {
1647 if (fputs(buf, tmpfp) == EOF) {
1648 FILE_OP_ERROR(tmp, "fputs");
1651 if (*errstr) g_free(*errstr);
1652 *errstr = g_strdup_printf(_("Error when writing temporary file for news sending."));
1659 debug_print("Sending message by news\n");
1661 folder = FOLDER(newsac->folder);
1663 newsval = news_post(folder, tmp);
1664 if (newsval < 0 && errstr) {
1665 if (*errstr) g_free(*errstr);
1666 *errstr = g_strdup_printf(_("Error occurred while posting the message to %s."),
1667 newsac->nntp_server);
1677 /* save message to outbox */
1678 if (mailval == 0 && newsval == 0 && savecopyfolder) {
1681 debug_print("saving sent message...\n");
1683 outbox = folder_find_item_from_identifier(savecopyfolder);
1685 outbox = folder_get_default_outbox();
1687 if (save_clear_text || tmp_enc_file == NULL) {
1688 procmsg_save_to_outbox(outbox, file, TRUE);
1690 procmsg_save_to_outbox(outbox, tmp_enc_file, FALSE);
1694 if (tmp_enc_file != NULL) {
1695 g_unlink(tmp_enc_file);
1697 tmp_enc_file = NULL;
1700 if (replymessageid != NULL || fwdmessageid != NULL) {
1704 if (replymessageid != NULL)
1705 tokens = g_strsplit(replymessageid, "\t", 0);
1707 tokens = g_strsplit(fwdmessageid, "\t", 0);
1708 item = folder_find_item_from_identifier(tokens[0]);
1710 /* check if queued message has valid folder and message id */
1711 if (item != NULL && tokens[2] != NULL) {
1714 msginfo = folder_item_get_msginfo(item, atoi(tokens[1]));
1716 /* check if referring message exists and has a message id */
1717 if ((msginfo != NULL) &&
1718 (msginfo->msgid != NULL) &&
1719 (strcmp(msginfo->msgid, tokens[2]) != 0)) {
1720 procmsg_msginfo_free(msginfo);
1724 if (msginfo == NULL) {
1725 msginfo = folder_item_get_msginfo_by_msgid(item, tokens[2]);
1728 if (msginfo != NULL) {
1729 if (replymessageid != NULL) {
1730 procmsg_msginfo_unset_flags(msginfo, MSG_FORWARDED, 0);
1731 procmsg_msginfo_set_flags(msginfo, MSG_REPLIED, 0);
1733 procmsg_msginfo_unset_flags(msginfo, MSG_REPLIED, 0);
1734 procmsg_msginfo_set_flags(msginfo, MSG_FORWARDED, 0);
1736 procmsg_msginfo_free(msginfo);
1744 slist_free_strings(to_list);
1745 g_slist_free(to_list);
1746 slist_free_strings(newsgroup_list);
1747 g_slist_free(newsgroup_list);
1748 g_free(savecopyfolder);
1749 g_free(replymessageid);
1750 g_free(fwdmessageid);
1751 g_free(privacy_system);
1752 g_free(encrypt_data);
1754 return (newsval != 0 ? newsval : mailval);
1757 gint procmsg_send_message_queue(const gchar *file, gchar **errstr)
1759 gint result = procmsg_send_message_queue_full(file, FALSE, errstr);
1760 toolbar_main_set_sensitive(mainwindow_get_mainwindow());
1764 static void update_folder_msg_counts(FolderItem *item, MsgInfo *msginfo, MsgPermFlags old_flags)
1766 MsgPermFlags new_flags = msginfo->flags.perm_flags;
1769 if (!(old_flags & MSG_NEW) && (new_flags & MSG_NEW)) {
1773 if ((old_flags & MSG_NEW) && !(new_flags & MSG_NEW)) {
1778 if (!(old_flags & MSG_UNREAD) && (new_flags & MSG_UNREAD)) {
1779 item->unread_msgs++;
1780 if (procmsg_msg_has_marked_parent(msginfo))
1781 item->unreadmarked_msgs++;
1784 if ((old_flags & MSG_UNREAD) && !(new_flags & MSG_UNREAD)) {
1785 item->unread_msgs--;
1786 if (procmsg_msg_has_marked_parent(msginfo))
1787 item->unreadmarked_msgs--;
1791 if (!(old_flags & MSG_MARKED) && (new_flags & MSG_MARKED)) {
1792 procmsg_update_unread_children(msginfo, TRUE);
1793 item->marked_msgs++;
1796 if ((old_flags & MSG_MARKED) && !(new_flags & MSG_MARKED)) {
1797 procmsg_update_unread_children(msginfo, FALSE);
1798 item->marked_msgs--;
1802 void procmsg_msginfo_set_flags(MsgInfo *msginfo, MsgPermFlags perm_flags, MsgTmpFlags tmp_flags)
1805 MsgInfoUpdate msginfo_update;
1806 MsgPermFlags perm_flags_new, perm_flags_old;
1807 MsgTmpFlags tmp_flags_old;
1809 g_return_if_fail(msginfo != NULL);
1810 item = msginfo->folder;
1811 g_return_if_fail(item != NULL);
1813 debug_print("Setting flags for message %d in folder %s\n", msginfo->msgnum, item->path);
1815 /* Perm Flags handling */
1816 perm_flags_old = msginfo->flags.perm_flags;
1817 perm_flags_new = msginfo->flags.perm_flags | perm_flags;
1818 if ((perm_flags & MSG_IGNORE_THREAD) || (perm_flags_old & MSG_IGNORE_THREAD)) {
1819 perm_flags_new &= ~(MSG_NEW | MSG_UNREAD);
1822 if (perm_flags_old != perm_flags_new) {
1823 folder_item_change_msg_flags(msginfo->folder, msginfo, perm_flags_new);
1825 update_folder_msg_counts(item, msginfo, perm_flags_old);
1829 /* Tmp flags handling */
1830 tmp_flags_old = msginfo->flags.tmp_flags;
1831 msginfo->flags.tmp_flags |= tmp_flags;
1833 /* update notification */
1834 if ((perm_flags_old != perm_flags_new) || (tmp_flags_old != msginfo->flags.tmp_flags)) {
1835 msginfo_update.msginfo = msginfo;
1836 msginfo_update.flags = MSGINFO_UPDATE_FLAGS;
1837 hooks_invoke(MSGINFO_UPDATE_HOOKLIST, &msginfo_update);
1838 folder_item_update(msginfo->folder, F_ITEM_UPDATE_MSGCNT);
1842 void procmsg_msginfo_unset_flags(MsgInfo *msginfo, MsgPermFlags perm_flags, MsgTmpFlags tmp_flags)
1845 MsgInfoUpdate msginfo_update;
1846 MsgPermFlags perm_flags_new, perm_flags_old;
1847 MsgTmpFlags tmp_flags_old;
1849 g_return_if_fail(msginfo != NULL);
1850 item = msginfo->folder;
1851 g_return_if_fail(item != NULL);
1853 debug_print("Unsetting flags for message %d in folder %s\n", msginfo->msgnum, item->path);
1855 /* Perm Flags handling */
1856 perm_flags_old = msginfo->flags.perm_flags;
1857 perm_flags_new = msginfo->flags.perm_flags & ~perm_flags;
1859 if (perm_flags_old != perm_flags_new) {
1860 folder_item_change_msg_flags(msginfo->folder, msginfo, perm_flags_new);
1862 update_folder_msg_counts(item, msginfo, perm_flags_old);
1865 /* Tmp flags hanlding */
1866 tmp_flags_old = msginfo->flags.tmp_flags;
1867 msginfo->flags.tmp_flags &= ~tmp_flags;
1869 /* update notification */
1870 if ((perm_flags_old != perm_flags_new) || (tmp_flags_old != msginfo->flags.tmp_flags)) {
1871 msginfo_update.msginfo = msginfo;
1872 msginfo_update.flags = MSGINFO_UPDATE_FLAGS;
1873 hooks_invoke(MSGINFO_UPDATE_HOOKLIST, &msginfo_update);
1874 folder_item_update(msginfo->folder, F_ITEM_UPDATE_MSGCNT);
1878 void procmsg_msginfo_change_flags(MsgInfo *msginfo,
1879 MsgPermFlags add_perm_flags, MsgTmpFlags add_tmp_flags,
1880 MsgPermFlags rem_perm_flags, MsgTmpFlags rem_tmp_flags)
1883 MsgInfoUpdate msginfo_update;
1884 MsgPermFlags perm_flags_new, perm_flags_old;
1885 MsgTmpFlags tmp_flags_old;
1887 g_return_if_fail(msginfo != NULL);
1888 item = msginfo->folder;
1889 g_return_if_fail(item != NULL);
1891 debug_print("Changing flags for message %d in folder %s\n", msginfo->msgnum, item->path);
1893 /* Perm Flags handling */
1894 perm_flags_old = msginfo->flags.perm_flags;
1895 perm_flags_new = (msginfo->flags.perm_flags & ~rem_perm_flags) | add_perm_flags;
1896 if ((add_perm_flags & MSG_IGNORE_THREAD) || (perm_flags_old & MSG_IGNORE_THREAD)) {
1897 perm_flags_new &= ~(MSG_NEW | MSG_UNREAD);
1900 if (perm_flags_old != perm_flags_new) {
1901 folder_item_change_msg_flags(msginfo->folder, msginfo, perm_flags_new);
1903 update_folder_msg_counts(item, msginfo, perm_flags_old);
1907 /* Tmp flags handling */
1908 tmp_flags_old = msginfo->flags.tmp_flags;
1909 msginfo->flags.tmp_flags &= ~rem_tmp_flags;
1910 msginfo->flags.tmp_flags |= add_tmp_flags;
1912 /* update notification */
1913 if ((perm_flags_old != perm_flags_new) || (tmp_flags_old != msginfo->flags.tmp_flags)) {
1914 msginfo_update.msginfo = msginfo;
1915 msginfo_update.flags = MSGINFO_UPDATE_FLAGS;
1916 hooks_invoke(MSGINFO_UPDATE_HOOKLIST, &msginfo_update);
1917 folder_item_update(msginfo->folder, F_ITEM_UPDATE_MSGCNT);
1922 *\brief check for flags (e.g. mark) in prior msgs of current thread
1924 *\param info Current message
1925 *\param perm_flags Flags to be checked
1926 *\param parentmsgs Hash of prior msgs to avoid loops
1928 *\return gboolean TRUE if perm_flags are found
1930 gboolean procmsg_msg_has_flagged_parent_real(MsgInfo *info,
1931 MsgPermFlags perm_flags, GHashTable *parentmsgs)
1935 g_return_val_if_fail(info != NULL, FALSE);
1937 if (info != NULL && info->folder != NULL && info->inreplyto != NULL) {
1938 tmp = folder_item_get_msginfo_by_msgid(info->folder,
1940 if (tmp && (tmp->flags.perm_flags & perm_flags)) {
1941 procmsg_msginfo_free(tmp);
1943 } else if (tmp != NULL) {
1946 if (g_hash_table_lookup(parentmsgs, info)) {
1947 debug_print("loop detected: %d\n",
1951 g_hash_table_insert(parentmsgs, info, "1");
1952 result = procmsg_msg_has_flagged_parent_real(
1953 tmp, perm_flags, parentmsgs);
1955 procmsg_msginfo_free(tmp);
1965 *\brief Callback for cleaning up hash of parentmsgs
1967 gboolean parentmsgs_hash_remove(gpointer key,
1975 *\brief Set up list of parentmsgs
1976 * See procmsg_msg_has_flagged_parent_real()
1978 gboolean procmsg_msg_has_flagged_parent(MsgInfo *info, MsgPermFlags perm_flags)
1981 GHashTable *parentmsgs = g_hash_table_new(NULL, NULL);
1983 result = procmsg_msg_has_flagged_parent_real(info, perm_flags, parentmsgs);
1984 g_hash_table_foreach_remove(parentmsgs, parentmsgs_hash_remove, NULL);
1985 g_hash_table_destroy(parentmsgs);
1990 *\brief Check if msgs prior in thread are marked
1991 * See procmsg_msg_has_flagged_parent_real()
1993 gboolean procmsg_msg_has_marked_parent(MsgInfo *info)
1995 return procmsg_msg_has_flagged_parent(info, MSG_MARKED);
1999 GSList *procmsg_find_children_func(MsgInfo *info,
2000 GSList *children, GSList *all)
2004 g_return_val_if_fail(info!=NULL, children);
2005 if (info->msgid == NULL)
2008 for (cur = all; cur != NULL; cur = g_slist_next(cur)) {
2009 MsgInfo *tmp = (MsgInfo *)cur->data;
2010 if (tmp->inreplyto && !strcmp(tmp->inreplyto, info->msgid)) {
2011 /* Check if message is already in the list */
2012 if ((children == NULL) ||
2013 (g_slist_index(children, tmp) == -1)) {
2014 children = g_slist_prepend(children,
2015 procmsg_msginfo_new_ref(tmp));
2016 children = procmsg_find_children_func(tmp,
2025 GSList *procmsg_find_children (MsgInfo *info)
2030 g_return_val_if_fail(info!=NULL, NULL);
2031 all = folder_item_get_msg_list(info->folder);
2032 children = procmsg_find_children_func(info, NULL, all);
2033 if (children != NULL) {
2034 for (cur = all; cur != NULL; cur = g_slist_next(cur)) {
2035 /* this will not free the used pointers
2036 created with procmsg_msginfo_new_ref */
2037 procmsg_msginfo_free((MsgInfo *)cur->data);
2045 void procmsg_update_unread_children(MsgInfo *info, gboolean newly_marked)
2047 GSList *children = procmsg_find_children(info);
2049 for (cur = children; cur != NULL; cur = g_slist_next(cur)) {
2050 MsgInfo *tmp = (MsgInfo *)cur->data;
2051 if(MSG_IS_UNREAD(tmp->flags) && !MSG_IS_IGNORE_THREAD(tmp->flags)) {
2053 info->folder->unreadmarked_msgs++;
2055 info->folder->unreadmarked_msgs--;
2056 folder_item_update(info->folder, F_ITEM_UPDATE_MSGCNT);
2058 procmsg_msginfo_free(tmp);
2060 g_slist_free(children);
2064 * Set the destination folder for a copy or move operation
2066 * \param msginfo The message which's destination folder is changed
2067 * \param to_folder The destination folder for the operation
2069 void procmsg_msginfo_set_to_folder(MsgInfo *msginfo, FolderItem *to_folder)
2071 if(msginfo->to_folder != NULL) {
2072 msginfo->to_folder->op_count--;
2073 folder_item_update(msginfo->to_folder, F_ITEM_UPDATE_MSGCNT);
2075 msginfo->to_folder = to_folder;
2076 if(to_folder != NULL) {
2077 to_folder->op_count++;
2078 folder_item_update(msginfo->to_folder, F_ITEM_UPDATE_MSGCNT);
2083 * Apply filtering actions to the msginfo
2085 * \param msginfo The MsgInfo describing the message that should be filtered
2086 * \return TRUE if the message was moved and MsgInfo is now invalid,
2089 gboolean procmsg_msginfo_filter(MsgInfo *msginfo, PrefsAccount* ac_prefs)
2091 MailFilteringData mail_filtering_data;
2093 mail_filtering_data.msginfo = msginfo;
2094 if (hooks_invoke(MAIL_FILTERING_HOOKLIST, &mail_filtering_data)) {
2098 /* filter if enabled in prefs or move to inbox if not */
2099 if((filtering_rules != NULL) &&
2100 filter_message_by_msginfo(filtering_rules, msginfo, ac_prefs)) {
2107 MsgInfo *procmsg_msginfo_new_from_mimeinfo(MsgInfo *src_msginfo, MimeInfo *mimeinfo)
2109 MsgInfo *tmp_msginfo = NULL;
2110 MsgFlags flags = {0, 0};
2111 gchar *tmpfile = get_tmp_file();
2112 FILE *fp = g_fopen(tmpfile, "wb");
2114 if (!mimeinfo || mimeinfo->type != MIMETYPE_MESSAGE ||
2115 g_ascii_strcasecmp(mimeinfo->subtype, "rfc822")) {
2116 g_warning("procmsg_msginfo_new_from_mimeinfo(): unsuitable mimeinfo");
2123 if (fp && procmime_write_mimeinfo(mimeinfo, fp) >= 0) {
2126 tmp_msginfo = procheader_parse_file(
2133 if (tmp_msginfo != NULL) {
2135 tmp_msginfo->folder = src_msginfo->folder;
2136 tmp_msginfo->plaintext_file = g_strdup(tmpfile);
2138 g_warning("procmsg_msginfo_new_from_mimeinfo(): Can't generate new msginfo");
2146 static GSList *spam_learners = NULL;
2148 void procmsg_register_spam_learner (int (*learn_func)(MsgInfo *info, GSList *list, gboolean spam))
2150 if (!g_slist_find(spam_learners, learn_func))
2151 spam_learners = g_slist_append(spam_learners, learn_func);
2152 if (mainwindow_get_mainwindow()) {
2153 main_window_set_menu_sensitive(mainwindow_get_mainwindow());
2154 summary_set_menu_sensitive(
2155 mainwindow_get_mainwindow()->summaryview);
2156 toolbar_main_set_sensitive(mainwindow_get_mainwindow());
2160 void procmsg_unregister_spam_learner (int (*learn_func)(MsgInfo *info, GSList *list, gboolean spam))
2162 spam_learners = g_slist_remove(spam_learners, learn_func);
2163 if (mainwindow_get_mainwindow()) {
2164 main_window_set_menu_sensitive(mainwindow_get_mainwindow());
2165 summary_set_menu_sensitive(
2166 mainwindow_get_mainwindow()->summaryview);
2167 toolbar_main_set_sensitive(mainwindow_get_mainwindow());
2171 gboolean procmsg_spam_can_learn(void)
2173 return g_slist_length(spam_learners) > 0;
2176 int procmsg_spam_learner_learn (MsgInfo *info, GSList *list, gboolean spam)
2178 GSList *cur = spam_learners;
2180 for (; cur; cur = cur->next) {
2181 int ((*func)(MsgInfo *info, GSList *list, gboolean spam)) = cur->data;
2182 ret |= func(info, list, spam);
2187 static gchar *spam_folder_item = NULL;
2188 void procmsg_spam_set_folder (const char *item_identifier)
2190 g_free(spam_folder_item);
2191 if (item_identifier)
2192 spam_folder_item = g_strdup(item_identifier);
2194 spam_folder_item = NULL;
2197 FolderItem *procmsg_spam_get_folder (void)
2199 FolderItem *item = spam_folder_item ? folder_find_item_from_identifier(spam_folder_item) : NULL;
2200 return item ? item : folder_get_default_trash();
2203 static void item_has_queued_mails(FolderItem *item, gpointer data)
2205 gboolean *result = (gboolean *)data;
2206 if (*result == TRUE)
2208 if (folder_has_parent_of_type(item, F_QUEUE) && item->total_msgs > 0)
2212 gboolean procmsg_have_queued_mails_fast (void)
2214 gboolean result = FALSE;
2215 folder_func_to_all_folders(item_has_queued_mails, &result);