2 * Sylpheed -- a GTK+ based, lightweight, and fast e-mail client
3 * Copyright (C) 1999-2007 Hiroyuki Yamamoto and the Claws Mail 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,
52 FolderItem *queue, gint msgnum, gboolean *queued_removed);
53 static void procmsg_update_unread_children (MsgInfo *info,
54 gboolean newly_marked);
61 Q_MAIL_ACCOUNT_ID = 4,
62 Q_NEWS_ACCOUNT_ID = 5,
63 Q_SAVE_COPY_FOLDER = 6,
64 Q_REPLY_MESSAGE_ID = 7,
70 Q_PRIVACY_SYSTEM_OLD = 13,
72 Q_ENCRYPT_DATA_OLD = 15,
73 Q_CLAWS_HDRS_OLD = 16,
76 void procmsg_msg_list_free(GSList *mlist)
81 for (cur = mlist; cur != NULL; cur = cur->next) {
82 msginfo = (MsgInfo *)cur->data;
83 procmsg_msginfo_free(msginfo);
97 /* CLAWS subject threading:
99 in the first round it inserts subject lines in a
100 relation (subject <-> node)
102 the second round finishes the threads by attaching
103 matching subject lines to the one found in the
104 relation. will use the oldest node with the same
105 subject that is not more then thread_by_subject_max_age
106 days old (see subject_relation_lookup)
109 static void subject_relation_insert(GRelation *relation, GNode *node)
114 g_return_if_fail(relation != NULL);
115 g_return_if_fail(node != NULL);
116 msginfo = (MsgInfo *) node->data;
117 g_return_if_fail(msginfo != NULL);
119 subject = msginfo->subject;
122 subject += subject_get_prefix_length(subject);
124 g_relation_insert(relation, subject, node);
127 static GNode *subject_relation_lookup(GRelation *relation, MsgInfo *msginfo)
134 g_return_val_if_fail(relation != NULL, NULL);
136 subject = msginfo->subject;
139 prefix_length = subject_get_prefix_length(subject);
140 if (prefix_length <= 0)
142 subject += prefix_length;
144 tuples = g_relation_select(relation, subject, 0);
148 if (tuples->len > 0) {
150 GNode *relation_node;
151 MsgInfo *relation_msginfo = NULL, *best_msginfo = NULL;
154 /* check all nodes with the same subject to find the best parent */
155 for (i = 0; i < tuples->len; i++) {
156 relation_node = (GNode *) g_tuples_index(tuples, i, 1);
157 relation_msginfo = (MsgInfo *) relation_node->data;
160 /* best node should be the oldest in the found nodes */
161 /* parent node must not be older then msginfo */
162 if ((relation_msginfo->date_t < msginfo->date_t) &&
163 ((best_msginfo == NULL) ||
164 (best_msginfo->date_t > relation_msginfo->date_t)))
167 /* parent node must not be more then thread_by_subject_max_age
168 days older then msginfo */
169 if (abs(difftime(msginfo->date_t, relation_msginfo->date_t)) >
170 prefs_common.thread_by_subject_max_age * 3600 * 24)
173 /* can add new tests for all matching
174 nodes found by subject */
177 node = relation_node;
178 best_msginfo = relation_msginfo;
183 g_tuples_destroy(tuples);
187 /* return the reversed thread tree */
188 GNode *procmsg_get_thread_tree(GSList *mlist)
190 GNode *root, *parent, *node, *next;
191 GHashTable *msgid_table;
192 GRelation *subject_relation = NULL;
197 root = g_node_new(NULL);
198 msgid_table = g_hash_table_new(g_str_hash, g_str_equal);
200 if (prefs_common.thread_by_subject) {
201 subject_relation = g_relation_new(2);
202 g_relation_index(subject_relation, 0, g_str_hash, g_str_equal);
205 for (; mlist != NULL; mlist = mlist->next) {
206 msginfo = (MsgInfo *)mlist->data;
209 if (msginfo->inreplyto) {
210 parent = g_hash_table_lookup(msgid_table, msginfo->inreplyto);
211 if (parent == NULL) {
215 node = g_node_insert_data_before
216 (parent, parent == root ? parent->children : NULL,
218 if ((msgid = msginfo->msgid) && g_hash_table_lookup(msgid_table, msgid) == NULL)
219 g_hash_table_insert(msgid_table, (gchar *)msgid, node);
221 /* CLAWS: add subject to relation (without prefix) */
222 if (prefs_common.thread_by_subject) {
223 subject_relation_insert(subject_relation, node);
227 /* complete the unfinished threads */
228 for (node = root->children; node != NULL; ) {
230 msginfo = (MsgInfo *)node->data;
233 if (msginfo->inreplyto)
234 parent = g_hash_table_lookup(msgid_table, msginfo->inreplyto);
236 /* try looking for the indirect parent */
237 if (!parent && msginfo->references) {
238 for (reflist = msginfo->references;
239 reflist != NULL; reflist = reflist->next)
240 if ((parent = g_hash_table_lookup
241 (msgid_table, reflist->data)) != NULL)
245 /* node should not be the parent, and node should not
246 be an ancestor of parent (circular reference) */
247 if (parent && parent != node &&
248 !g_node_is_ancestor(node, parent)) {
251 (parent, parent->children, node);
257 if (prefs_common.thread_by_subject) {
258 START_TIMING("thread by subject");
259 for (node = root->children; node && node != NULL;) {
261 msginfo = (MsgInfo *) node->data;
263 parent = subject_relation_lookup(subject_relation, msginfo);
265 /* the node may already be threaded by IN-REPLY-TO, so go up
267 find the parent node */
268 if (parent != NULL) {
269 if (g_node_is_ancestor(node, parent))
277 g_node_append(parent, node);
285 if (prefs_common.thread_by_subject)
286 g_relation_destroy(subject_relation);
288 g_hash_table_destroy(msgid_table);
293 gint procmsg_move_messages(GSList *mlist)
295 GSList *cur, *movelist = NULL;
297 FolderItem *dest = NULL;
299 gboolean finished = TRUE;
300 if (!mlist) return 0;
302 folder_item_update_freeze();
305 for (cur = mlist; cur != NULL; cur = cur->next) {
306 msginfo = (MsgInfo *)cur->data;
307 if (!msginfo->to_folder) {
313 dest = msginfo->to_folder;
314 movelist = g_slist_prepend(movelist, msginfo);
315 } else if (dest == msginfo->to_folder) {
316 movelist = g_slist_prepend(movelist, msginfo);
320 procmsg_msginfo_set_to_folder(msginfo, NULL);
323 movelist = g_slist_reverse(movelist);
324 retval |= folder_item_move_msgs(dest, movelist);
325 g_slist_free(movelist);
328 if (finished == FALSE) {
334 folder_item_update_thaw();
338 void procmsg_copy_messages(GSList *mlist)
340 GSList *cur, *copylist = NULL;
342 FolderItem *dest = NULL;
343 gboolean finished = TRUE;
346 folder_item_update_freeze();
349 for (cur = mlist; cur != NULL; cur = cur->next) {
350 msginfo = (MsgInfo *)cur->data;
351 if (!msginfo->to_folder) {
357 dest = msginfo->to_folder;
358 copylist = g_slist_prepend(copylist, msginfo);
359 } else if (dest == msginfo->to_folder) {
360 copylist = g_slist_prepend(copylist, msginfo);
364 procmsg_msginfo_set_to_folder(msginfo, NULL);
367 copylist = g_slist_reverse(copylist);
368 folder_item_copy_msgs(dest, copylist);
369 g_slist_free(copylist);
372 if (finished == FALSE) {
378 folder_item_update_thaw();
381 gchar *procmsg_get_message_file_path(MsgInfo *msginfo)
385 g_return_val_if_fail(msginfo != NULL, NULL);
387 if (msginfo->plaintext_file)
388 file = g_strdup(msginfo->plaintext_file);
390 file = folder_item_fetch_msg(msginfo->folder, msginfo->msgnum);
396 gchar *procmsg_get_message_file(MsgInfo *msginfo)
398 gchar *filename = NULL;
400 g_return_val_if_fail(msginfo != NULL, NULL);
402 filename = folder_item_fetch_msg(msginfo->folder, msginfo->msgnum);
404 debug_print("can't fetch message %d\n", msginfo->msgnum);
409 gchar *procmsg_get_message_file_full(MsgInfo *msginfo, gboolean headers, gboolean body)
411 gchar *filename = NULL;
413 g_return_val_if_fail(msginfo != NULL, NULL);
415 filename = folder_item_fetch_msg_full(msginfo->folder, msginfo->msgnum,
418 debug_print("can't fetch message %d\n", msginfo->msgnum);
423 GSList *procmsg_get_message_file_list(GSList *mlist)
425 GSList *file_list = NULL;
427 MsgFileInfo *fileinfo;
430 while (mlist != NULL) {
431 msginfo = (MsgInfo *)mlist->data;
432 file = procmsg_get_message_file(msginfo);
434 procmsg_message_file_list_free(file_list);
437 fileinfo = g_new(MsgFileInfo, 1);
438 fileinfo->msginfo = procmsg_msginfo_new_ref(msginfo);
439 fileinfo->file = file;
440 fileinfo->flags = g_new(MsgFlags, 1);
441 *fileinfo->flags = msginfo->flags;
442 file_list = g_slist_prepend(file_list, fileinfo);
446 file_list = g_slist_reverse(file_list);
451 void procmsg_message_file_list_free(MsgInfoList *file_list)
454 MsgFileInfo *fileinfo;
456 for (cur = file_list; cur != NULL; cur = cur->next) {
457 fileinfo = (MsgFileInfo *)cur->data;
458 procmsg_msginfo_free(fileinfo->msginfo);
459 g_free(fileinfo->file);
460 g_free(fileinfo->flags);
464 g_slist_free(file_list);
467 FILE *procmsg_open_message(MsgInfo *msginfo)
472 g_return_val_if_fail(msginfo != NULL, NULL);
474 file = procmsg_get_message_file_path(msginfo);
475 g_return_val_if_fail(file != NULL, NULL);
477 if (!is_file_exist(file)) {
479 file = procmsg_get_message_file(msginfo);
484 if ((fp = g_fopen(file, "rb")) == NULL) {
485 FILE_OP_ERROR(file, "fopen");
492 if (MSG_IS_QUEUED(msginfo->flags) || MSG_IS_DRAFT(msginfo->flags)) {
495 while (fgets(buf, sizeof(buf), fp) != NULL) {
497 if ((!strncmp(buf, "X-Claws-End-Special-Headers: 1",
498 strlen("X-Claws-End-Special-Headers:"))) ||
499 (!strncmp(buf, "X-Sylpheed-End-Special-Headers: 1",
500 strlen("X-Sylpheed-End-Special-Headers:"))))
503 if (buf[0] == '\r' || buf[0] == '\n') break;
504 /* from other mailers */
505 if (!strncmp(buf, "Date: ", 6)
506 || !strncmp(buf, "To: ", 4)
507 || !strncmp(buf, "From: ", 6)
508 || !strncmp(buf, "Subject: ", 9)) {
518 gboolean procmsg_msg_exist(MsgInfo *msginfo)
523 if (!msginfo) return FALSE;
525 path = folder_item_get_path(msginfo->folder);
527 ret = !folder_item_is_msg_changed(msginfo->folder, msginfo);
533 void procmsg_get_filter_keyword(MsgInfo *msginfo, gchar **header, gchar **key,
534 PrefsFilterType type)
536 static HeaderEntry hentry[] = {{"X-BeenThere:", NULL, TRUE},
537 {"X-ML-Name:", NULL, TRUE},
538 {"X-List:", NULL, TRUE},
539 {"X-Mailing-list:", NULL, TRUE},
540 {"List-Id:", NULL, TRUE},
541 {"X-Sequence:", NULL, TRUE},
542 {"Sender:", NULL, TRUE},
543 {"List-Post:", NULL, TRUE},
544 {NULL, NULL, FALSE}};
550 H_X_MAILING_LIST = 3,
559 g_return_if_fail(msginfo != NULL);
560 g_return_if_fail(header != NULL);
561 g_return_if_fail(key != NULL);
570 if ((fp = procmsg_open_message(msginfo)) == NULL)
572 procheader_get_header_fields(fp, hentry);
575 #define SET_FILTER_KEY(hstr, idx) \
577 *header = g_strdup(hstr); \
578 *key = hentry[idx].body; \
579 hentry[idx].body = NULL; \
582 if (hentry[H_X_BEENTHERE].body != NULL) {
583 SET_FILTER_KEY("header \"X-BeenThere\"", H_X_BEENTHERE);
584 } else if (hentry[H_X_ML_NAME].body != NULL) {
585 SET_FILTER_KEY("header \"X-ML-Name\"", H_X_ML_NAME);
586 } else if (hentry[H_X_LIST].body != NULL) {
587 SET_FILTER_KEY("header \"X-List\"", H_X_LIST);
588 } else if (hentry[H_X_MAILING_LIST].body != NULL) {
589 SET_FILTER_KEY("header \"X-Mailing-List\"", H_X_MAILING_LIST);
590 } else if (hentry[H_LIST_ID].body != NULL) {
591 SET_FILTER_KEY("header \"List-Id\"", H_LIST_ID);
592 extract_list_id_str(*key);
593 } else if (hentry[H_X_SEQUENCE].body != NULL) {
596 SET_FILTER_KEY("X-Sequence", H_X_SEQUENCE);
599 while (*p != '\0' && !g_ascii_isspace(*p)) p++;
600 while (g_ascii_isspace(*p)) p++;
601 if (g_ascii_isdigit(*p)) {
607 } else if (hentry[H_SENDER].body != NULL) {
608 SET_FILTER_KEY("header \"Sender\"", H_SENDER);
609 } else if (hentry[H_LIST_POST].body != NULL) {
610 SET_FILTER_KEY("header \"List-Post\"", H_LIST_POST);
611 } else if (msginfo->to) {
612 *header = g_strdup("to");
613 *key = g_strdup(msginfo->to);
614 } else if (msginfo->subject) {
615 *header = g_strdup("subject");
616 *key = g_strdup(msginfo->subject);
619 #undef SET_FILTER_KEY
621 g_free(hentry[H_X_BEENTHERE].body);
622 hentry[H_X_BEENTHERE].body = NULL;
623 g_free(hentry[H_X_ML_NAME].body);
624 hentry[H_X_ML_NAME].body = NULL;
625 g_free(hentry[H_X_LIST].body);
626 hentry[H_X_LIST].body = NULL;
627 g_free(hentry[H_X_MAILING_LIST].body);
628 hentry[H_X_MAILING_LIST].body = NULL;
629 g_free(hentry[H_LIST_ID].body);
630 hentry[H_LIST_ID].body = NULL;
631 g_free(hentry[H_SENDER].body);
632 hentry[H_SENDER].body = NULL;
633 g_free(hentry[H_LIST_POST].body);
634 hentry[H_LIST_POST].body = NULL;
638 *header = g_strdup("from");
639 *key = g_strdup(msginfo->from);
642 *header = g_strdup("to");
643 *key = g_strdup(msginfo->to);
645 case FILTER_BY_SUBJECT:
646 *header = g_strdup("subject");
647 *key = g_strdup(msginfo->subject);
654 static void procmsg_empty_trash(FolderItem *trash)
659 (trash->stype != F_TRASH &&
660 !folder_has_parent_of_type(trash, F_TRASH)))
663 if (trash && trash->total_msgs > 0) {
664 GSList *mlist = folder_item_get_msg_list(trash);
666 for (cur = mlist ; cur != NULL ; cur = cur->next) {
667 MsgInfo * msginfo = (MsgInfo *) cur->data;
668 if (MSG_IS_LOCKED(msginfo->flags)) {
669 procmsg_msginfo_free(msginfo);
672 if (msginfo->total_size != 0 &&
673 msginfo->size != (off_t)msginfo->total_size)
674 partial_mark_for_delete(msginfo);
676 procmsg_msginfo_free(msginfo);
679 folder_item_remove_all_msg(trash);
682 if (!trash->node || !trash->node->children)
685 node = trash->node->children;
686 while (node != NULL) {
688 procmsg_empty_trash(FOLDER_ITEM(node->data));
693 void procmsg_empty_all_trash(void)
698 for (cur = folder_get_list(); cur != NULL; cur = cur->next) {
699 Folder *folder = FOLDER(cur->data);
700 trash = folder->trash;
701 procmsg_empty_trash(trash);
702 if (folder->account && folder->account->set_trash_folder &&
703 folder_find_item_from_identifier(folder->account->trash_folder))
705 folder_find_item_from_identifier(folder->account->trash_folder));
709 static PrefsAccount *procmsg_get_account_from_file(const gchar *file)
711 PrefsAccount *mailac = NULL;
715 static HeaderEntry qentry[] = {{"S:", NULL, FALSE},
716 {"SSV:", NULL, FALSE},
718 {"NG:", NULL, FALSE},
719 {"MAID:", NULL, FALSE},
720 {"NAID:", NULL, FALSE},
721 {"SCF:", NULL, FALSE},
722 {"RMID:", NULL, FALSE},
723 {"FMID:", NULL, FALSE},
724 {"X-Claws-Privacy-System:", NULL, FALSE},
725 {"X-Claws-Encrypt:", NULL, FALSE},
726 {"X-Claws-Encrypt-Data:", NULL, FALSE},
727 {"X-Claws-End-Special-Headers", NULL, FALSE},
728 {"X-Sylpheed-Privacy-System:", NULL, FALSE},
729 {"X-Sylpheed-Encrypt:", NULL, FALSE},
730 {"X-Sylpheed-Encrypt-Data:", NULL, FALSE},
731 {NULL, NULL, FALSE}};
733 g_return_val_if_fail(file != NULL, NULL);
735 if ((fp = g_fopen(file, "rb")) == NULL) {
736 FILE_OP_ERROR(file, "fopen");
740 while ((hnum = procheader_get_one_field(buf, sizeof(buf), fp, qentry))
742 gchar *p = buf + strlen(qentry[hnum].name);
744 if (hnum == Q_MAIL_ACCOUNT_ID) {
745 mailac = account_find_from_id(atoi(p));
753 static GSList *procmsg_list_sort_by_account(FolderItem *queue, GSList *list)
755 GSList *result = NULL;
757 PrefsAccount *last_account = NULL;
760 gboolean nothing_to_sort = TRUE;
765 orig = g_slist_copy(list);
767 msg = (MsgInfo *)orig->data;
769 for (cur = orig; cur; cur = cur->next)
770 debug_print("sort before %s\n", ((MsgInfo *)cur->data)->from);
775 nothing_to_sort = TRUE;
779 PrefsAccount *ac = NULL;
780 msg = (MsgInfo *)cur->data;
781 file = folder_item_fetch_msg(queue, msg->msgnum);
782 ac = procmsg_get_account_from_file(file);
785 if (last_account == NULL || (ac != NULL && ac == last_account)) {
786 result = g_slist_append(result, msg);
787 orig = g_slist_remove(orig, msg);
789 nothing_to_sort = FALSE;
795 if (orig || g_slist_length(orig)) {
796 if (!last_account && nothing_to_sort) {
797 /* can't find an account for the rest of the list */
800 result = g_slist_append(result, cur->data);
811 for (cur = result; cur; cur = cur->next)
812 debug_print("sort after %s\n", ((MsgInfo *)cur->data)->from);
819 static gboolean procmsg_is_last_for_account(FolderItem *queue, MsgInfo *msginfo, GSList *elem)
821 gchar *file = folder_item_fetch_msg(queue, msginfo->msgnum);
822 PrefsAccount *ac = procmsg_get_account_from_file(file);
825 for (cur = elem; cur; cur = cur->next) {
826 MsgInfo *cur_msginfo = (MsgInfo *)cur->data;
827 file = folder_item_fetch_msg(queue, cur_msginfo->msgnum);
829 if (cur_msginfo != msginfo && !MSG_IS_LOCKED(cur_msginfo->flags)) {
830 if (procmsg_get_account_from_file(file) == ac) {
841 static gboolean send_queue_lock = FALSE;
843 *\brief Send messages in queue
845 *\param queue Queue folder to process
846 *\param save_msgs Unused
848 *\return Number of messages sent, negative if an error occurred
849 * positive if no error occurred
851 gint procmsg_send_queue(FolderItem *queue, gboolean save_msgs, gchar **errstr)
853 gint sent = 0, err = 0;
855 GSList *sorted_list = NULL;
858 if (send_queue_lock) {
859 /* Avoid having to translate two similar strings */
860 log_warning(LOG_PROTOCOL, "%s\n", _("Already trying to send."));
862 if (*errstr) g_free(*errstr);
863 *errstr = g_strdup_printf(_("Already trying to send."));
865 toolbar_main_set_sensitive(mainwindow_get_mainwindow());
868 send_queue_lock = TRUE;
871 queue = folder_get_default_queue();
874 send_queue_lock = FALSE;
879 toolbar_main_set_sensitive(mainwindow_get_mainwindow());
881 folder_item_scan(queue);
882 list = folder_item_get_msg_list(queue);
884 /* sort the list per sender account; this helps reusing the same SMTP server */
885 sorted_list = procmsg_list_sort_by_account(queue, list);
887 for (elem = sorted_list; elem != NULL; elem = elem->next) {
891 msginfo = (MsgInfo *)(elem->data);
892 if (!MSG_IS_LOCKED(msginfo->flags)) {
893 file = folder_item_fetch_msg(queue, msginfo->msgnum);
895 gboolean queued_removed = FALSE;
896 if (procmsg_send_message_queue_full(file,
897 !procmsg_is_last_for_account(queue, msginfo, elem),
898 errstr, queue, msginfo->msgnum, &queued_removed) < 0) {
899 g_warning("Sending queued message %d failed.\n",
905 folder_item_remove_msg(queue, msginfo->msgnum);
910 /* FIXME: supposedly if only one message is locked, and queue
911 * is being flushed, the following free says something like
912 * "freeing msg ## in folder (nil)". */
913 procmsg_msginfo_free(msginfo);
916 g_slist_free(sorted_list);
917 folder_item_scan(queue);
919 if (queue->node && queue->node->children) {
920 node = queue->node->children;
921 while (node != NULL) {
924 send_queue_lock = FALSE;
925 res = procmsg_send_queue(FOLDER_ITEM(node->data), save_msgs, errstr);
926 send_queue_lock = TRUE;
934 send_queue_lock = FALSE;
936 toolbar_main_set_sensitive(mainwindow_get_mainwindow());
938 return (err != 0 ? -err : sent);
941 gboolean procmsg_is_sending(void)
943 return send_queue_lock;
947 *\brief Determine if a queue folder is empty
949 *\param queue Queue folder to process
951 *\return TRUE if the queue folder is empty, otherwise return FALSE
953 gboolean procmsg_queue_is_empty(FolderItem *queue)
956 gboolean res = FALSE;
958 queue = folder_get_default_queue();
959 g_return_val_if_fail(queue != NULL, TRUE);
961 folder_item_scan(queue);
962 list = folder_item_get_msg_list(queue);
963 res = (list == NULL);
964 procmsg_msg_list_free(list);
968 if (queue->node && queue->node->children) {
969 node = queue->node->children;
970 while (node != NULL) {
972 if (!procmsg_queue_is_empty(FOLDER_ITEM(node->data)))
981 gint procmsg_remove_special_headers(const gchar *in, const gchar *out)
986 if ((fp = g_fopen(in, "rb")) == NULL) {
987 FILE_OP_ERROR(in, "fopen");
990 if ((outfp = g_fopen(out, "wb")) == NULL) {
991 FILE_OP_ERROR(out, "fopen");
995 while (fgets(buf, sizeof(buf), fp) != NULL) {
997 if ((!strncmp(buf, "X-Claws-End-Special-Headers: 1",
998 strlen("X-Claws-End-Special-Headers:"))) ||
999 (!strncmp(buf, "X-Sylpheed-End-Special-Headers: 1",
1000 strlen("X-Sylpheed-End-Special-Headers:"))))
1003 if (buf[0] == '\r' || buf[0] == '\n') break;
1004 /* from other mailers */
1005 if (!strncmp(buf, "Date: ", 6)
1006 || !strncmp(buf, "To: ", 4)
1007 || !strncmp(buf, "From: ", 6)
1008 || !strncmp(buf, "Subject: ", 9)) {
1013 while (fgets(buf, sizeof(buf), fp) != NULL)
1020 static gint procmsg_save_to_outbox(FolderItem *outbox, const gchar *file,
1024 MsgInfo *msginfo, *tmp_msginfo;
1025 MsgFlags flag = {0, 0};
1027 debug_print("saving sent message...\n");
1030 outbox = folder_get_default_outbox();
1031 g_return_val_if_fail(outbox != NULL, -1);
1033 /* remove queueing headers */
1035 gchar tmp[MAXPATHLEN + 1];
1037 g_snprintf(tmp, sizeof(tmp), "%s%ctmpmsg.out.%08x",
1038 get_rc_dir(), G_DIR_SEPARATOR, (guint) rand());
1040 if (procmsg_remove_special_headers(file, tmp) !=0)
1043 folder_item_scan(outbox);
1044 if ((num = folder_item_add_msg(outbox, tmp, &flag, TRUE)) < 0) {
1045 g_warning("can't save message\n");
1050 folder_item_scan(outbox);
1051 if ((num = folder_item_add_msg
1052 (outbox, file, &flag, FALSE)) < 0) {
1053 g_warning("can't save message\n");
1057 msginfo = folder_item_get_msginfo(outbox, num); /* refcnt++ */
1058 tmp_msginfo = procmsg_msginfo_get_full_info(msginfo); /* refcnt++ */
1059 if (msginfo != NULL) {
1060 procmsg_msginfo_unset_flags(msginfo, ~0, 0);
1061 procmsg_msginfo_free(msginfo); /* refcnt-- */
1062 /* tmp_msginfo == msginfo */
1063 if (tmp_msginfo && msginfo->extradata &&
1064 (msginfo->extradata->dispositionnotificationto ||
1065 msginfo->extradata->returnreceiptto)) {
1066 procmsg_msginfo_set_flags(msginfo, MSG_RETRCPT_SENT, 0);
1068 procmsg_msginfo_free(tmp_msginfo); /* refcnt-- */
1074 void procmsg_print_message(MsgInfo *msginfo, const gchar *cmdline)
1076 static const gchar *def_cmd = "lpr %s";
1077 static guint id = 0;
1083 g_return_if_fail(msginfo);
1085 if (procmime_msginfo_is_encrypted(msginfo))
1086 tmpfp = procmime_get_first_encrypted_text_content(msginfo);
1088 tmpfp = procmime_get_first_text_content(msginfo);
1089 if (tmpfp == NULL) {
1090 g_warning("Can't get text part\n");
1094 prtmp = g_strdup_printf("%s%cprinttmp.%08x",
1095 get_mime_tmp_dir(), G_DIR_SEPARATOR, id++);
1097 if ((prfp = g_fopen(prtmp, "wb")) == NULL) {
1098 FILE_OP_ERROR(prtmp, "fopen");
1104 if (msginfo->date) fprintf(prfp, "Date: %s\n", msginfo->date);
1105 if (msginfo->from) fprintf(prfp, "From: %s\n", msginfo->from);
1106 if (msginfo->to) fprintf(prfp, "To: %s\n", msginfo->to);
1107 if (msginfo->cc) fprintf(prfp, "Cc: %s\n", msginfo->cc);
1108 if (msginfo->newsgroups)
1109 fprintf(prfp, "Newsgroups: %s\n", msginfo->newsgroups);
1110 if (msginfo->subject) fprintf(prfp, "Subject: %s\n", msginfo->subject);
1113 while (fgets(buf, sizeof(buf), tmpfp) != NULL)
1119 if (cmdline && (p = strchr(cmdline, '%')) && *(p + 1) == 's' &&
1120 !strchr(p + 2, '%'))
1121 g_snprintf(buf, sizeof(buf) - 1, cmdline, prtmp);
1124 g_warning("Print command line is invalid: '%s'\n",
1126 g_snprintf(buf, sizeof(buf) - 1, def_cmd, prtmp);
1132 if (buf[strlen(buf) - 1] != '&') strcat(buf, "&");
1136 MsgInfo *procmsg_msginfo_new_ref(MsgInfo *msginfo)
1143 MsgInfo *procmsg_msginfo_new(void)
1145 MsgInfo *newmsginfo;
1147 newmsginfo = g_new0(MsgInfo, 1);
1148 newmsginfo->refcnt = 1;
1153 MsgInfo *procmsg_msginfo_copy(MsgInfo *msginfo)
1155 MsgInfo *newmsginfo;
1158 if (msginfo == NULL) return NULL;
1160 newmsginfo = g_new0(MsgInfo, 1);
1162 newmsginfo->refcnt = 1;
1164 #define MEMBCOPY(mmb) newmsginfo->mmb = msginfo->mmb
1165 #define MEMBDUP(mmb) newmsginfo->mmb = msginfo->mmb ? \
1166 g_strdup(msginfo->mmb) : NULL
1181 MEMBDUP(newsgroups);
1188 MEMBCOPY(to_folder);
1190 if (msginfo->extradata) {
1191 newmsginfo->extradata = g_new0(MsgInfoExtraData, 1);
1192 MEMBDUP(extradata->face);
1193 MEMBDUP(extradata->xface);
1194 MEMBDUP(extradata->dispositionnotificationto);
1195 MEMBDUP(extradata->returnreceiptto);
1196 MEMBDUP(extradata->partial_recv);
1197 MEMBDUP(extradata->account_server);
1198 MEMBDUP(extradata->account_login);
1199 MEMBDUP(extradata->list_post);
1200 MEMBDUP(extradata->list_subscribe);
1201 MEMBDUP(extradata->list_unsubscribe);
1202 MEMBDUP(extradata->list_help);
1203 MEMBDUP(extradata->list_archive);
1204 MEMBDUP(extradata->list_owner);
1207 refs = msginfo->references;
1208 for (refs = msginfo->references; refs != NULL; refs = refs->next) {
1209 newmsginfo->references = g_slist_prepend
1210 (newmsginfo->references, g_strdup(refs->data));
1212 newmsginfo->references = g_slist_reverse(newmsginfo->references);
1215 MEMBDUP(plaintext_file);
1220 MsgInfo *procmsg_msginfo_get_full_info(MsgInfo *msginfo)
1222 MsgInfo *full_msginfo;
1225 if (msginfo == NULL) return NULL;
1227 file = procmsg_get_message_file_path(msginfo);
1228 if (!file || !is_file_exist(file)) {
1230 file = procmsg_get_message_file(msginfo);
1232 if (!file || !is_file_exist(file)) {
1233 g_warning("procmsg_msginfo_get_full_info(): can't get message file.\n");
1237 full_msginfo = procheader_parse_file(file, msginfo->flags, TRUE, FALSE);
1239 if (!full_msginfo) return NULL;
1241 msginfo->total_size = full_msginfo->total_size;
1242 msginfo->planned_download = full_msginfo->planned_download;
1244 if (full_msginfo->extradata) {
1245 if (!msginfo->extradata)
1246 msginfo->extradata = g_new0(MsgInfoExtraData, 1);
1247 if (!msginfo->extradata->list_post)
1248 msginfo->extradata->list_post = g_strdup(full_msginfo->extradata->list_post);
1249 if (!msginfo->extradata->list_subscribe)
1250 msginfo->extradata->list_subscribe = g_strdup(full_msginfo->extradata->list_subscribe);
1251 if (!msginfo->extradata->list_unsubscribe)
1252 msginfo->extradata->list_unsubscribe = g_strdup(full_msginfo->extradata->list_unsubscribe);
1253 if (!msginfo->extradata->list_help)
1254 msginfo->extradata->list_help = g_strdup(full_msginfo->extradata->list_help);
1255 if (!msginfo->extradata->list_archive)
1256 msginfo->extradata->list_archive= g_strdup(full_msginfo->extradata->list_archive);
1257 if (!msginfo->extradata->list_owner)
1258 msginfo->extradata->list_owner = g_strdup(full_msginfo->extradata->list_owner);
1259 if (!msginfo->extradata->xface)
1260 msginfo->extradata->xface = g_strdup(full_msginfo->extradata->xface);
1261 if (!msginfo->extradata->face)
1262 msginfo->extradata->face = g_strdup(full_msginfo->extradata->face);
1263 if (!msginfo->extradata->dispositionnotificationto)
1264 msginfo->extradata->dispositionnotificationto =
1265 g_strdup(full_msginfo->extradata->dispositionnotificationto);
1266 if (!msginfo->extradata->returnreceiptto)
1267 msginfo->extradata->returnreceiptto = g_strdup
1268 (full_msginfo->extradata->returnreceiptto);
1269 if (!msginfo->extradata->partial_recv && full_msginfo->extradata->partial_recv)
1270 msginfo->extradata->partial_recv = g_strdup
1271 (full_msginfo->extradata->partial_recv);
1272 if (!msginfo->extradata->account_server && full_msginfo->extradata->account_server)
1273 msginfo->extradata->account_server = g_strdup
1274 (full_msginfo->extradata->account_server);
1275 if (!msginfo->extradata->account_login && full_msginfo->extradata->account_login)
1276 msginfo->extradata->account_login = g_strdup
1277 (full_msginfo->extradata->account_login);
1279 procmsg_msginfo_free(full_msginfo);
1281 return procmsg_msginfo_new_ref(msginfo);
1284 void procmsg_msginfo_free(MsgInfo *msginfo)
1286 if (msginfo == NULL) return;
1289 if (msginfo->refcnt > 0)
1292 if (msginfo->to_folder) {
1293 msginfo->to_folder->op_count--;
1294 folder_item_update(msginfo->to_folder, F_ITEM_UPDATE_MSGCNT);
1297 g_free(msginfo->fromspace);
1299 g_free(msginfo->fromname);
1301 g_free(msginfo->date);
1302 g_free(msginfo->from);
1303 g_free(msginfo->to);
1304 g_free(msginfo->cc);
1305 g_free(msginfo->newsgroups);
1306 g_free(msginfo->subject);
1307 g_free(msginfo->msgid);
1308 g_free(msginfo->inreplyto);
1309 g_free(msginfo->xref);
1311 if (msginfo->extradata) {
1312 g_free(msginfo->extradata->returnreceiptto);
1313 g_free(msginfo->extradata->dispositionnotificationto);
1314 g_free(msginfo->extradata->xface);
1315 g_free(msginfo->extradata->face);
1316 g_free(msginfo->extradata->list_post);
1317 g_free(msginfo->extradata->list_subscribe);
1318 g_free(msginfo->extradata->list_unsubscribe);
1319 g_free(msginfo->extradata->list_help);
1320 g_free(msginfo->extradata->list_archive);
1321 g_free(msginfo->extradata->list_owner);
1322 g_free(msginfo->extradata->partial_recv);
1323 g_free(msginfo->extradata->account_server);
1324 g_free(msginfo->extradata->account_login);
1325 g_free(msginfo->extradata);
1327 slist_free_strings(msginfo->references);
1328 g_slist_free(msginfo->references);
1330 g_free(msginfo->plaintext_file);
1335 guint procmsg_msginfo_memusage(MsgInfo *msginfo)
1340 memusage += sizeof(MsgInfo);
1341 if (msginfo->fromname)
1342 memusage += strlen(msginfo->fromname);
1344 memusage += strlen(msginfo->date);
1346 memusage += strlen(msginfo->from);
1348 memusage += strlen(msginfo->to);
1350 memusage += strlen(msginfo->cc);
1351 if (msginfo->newsgroups)
1352 memusage += strlen(msginfo->newsgroups);
1353 if (msginfo->subject)
1354 memusage += strlen(msginfo->subject);
1356 memusage += strlen(msginfo->msgid);
1357 if (msginfo->inreplyto)
1358 memusage += strlen(msginfo->inreplyto);
1359 for (refs = msginfo->references; refs; refs=refs->next) {
1360 gchar *r = (gchar *)refs->data;
1361 memusage += r?strlen(r):0;
1363 if (msginfo->fromspace)
1364 memusage += strlen(msginfo->fromspace);
1366 if (msginfo->extradata) {
1367 memusage += sizeof(MsgInfoExtraData);
1368 if (msginfo->extradata->xface)
1369 memusage += strlen(msginfo->extradata->xface);
1370 if (msginfo->extradata->face)
1371 memusage += strlen(msginfo->extradata->face);
1372 if (msginfo->extradata->dispositionnotificationto)
1373 memusage += strlen(msginfo->extradata->dispositionnotificationto);
1374 if (msginfo->extradata->returnreceiptto)
1375 memusage += strlen(msginfo->extradata->returnreceiptto);
1377 if (msginfo->extradata->partial_recv)
1378 memusage += strlen(msginfo->extradata->partial_recv);
1379 if (msginfo->extradata->account_server)
1380 memusage += strlen(msginfo->extradata->account_server);
1381 if (msginfo->extradata->account_login)
1382 memusage += strlen(msginfo->extradata->account_login);
1384 if (msginfo->extradata->list_post)
1385 memusage += strlen(msginfo->extradata->list_post);
1386 if (msginfo->extradata->list_subscribe)
1387 memusage += strlen(msginfo->extradata->list_subscribe);
1388 if (msginfo->extradata->list_unsubscribe)
1389 memusage += strlen(msginfo->extradata->list_unsubscribe);
1390 if (msginfo->extradata->list_help)
1391 memusage += strlen(msginfo->extradata->list_help);
1392 if (msginfo->extradata->list_archive)
1393 memusage += strlen(msginfo->extradata->list_archive);
1394 if (msginfo->extradata->list_owner)
1395 memusage += strlen(msginfo->extradata->list_owner);
1400 static gint procmsg_send_message_queue_full(const gchar *file, gboolean keep_session, gchar **errstr,
1401 FolderItem *queue, gint msgnum, gboolean *queued_removed)
1403 static HeaderEntry qentry[] = {{"S:", NULL, FALSE},
1404 {"SSV:", NULL, FALSE},
1405 {"R:", NULL, FALSE},
1406 {"NG:", NULL, FALSE},
1407 {"MAID:", NULL, FALSE},
1408 {"NAID:", NULL, FALSE},
1409 {"SCF:", NULL, FALSE},
1410 {"RMID:", NULL, FALSE},
1411 {"FMID:", NULL, FALSE},
1412 {"X-Claws-Privacy-System:", NULL, FALSE},
1413 {"X-Claws-Encrypt:", NULL, FALSE},
1414 {"X-Claws-Encrypt-Data:", NULL, FALSE},
1415 {"X-Claws-End-Special-Headers:", NULL, FALSE},
1416 {"X-Sylpheed-Privacy-System:", NULL, FALSE},
1417 {"X-Sylpheed-Encrypt:", NULL, FALSE},
1418 {"X-Sylpheed-Encrypt-Data:", NULL, FALSE},
1419 {"X-Sylpheed-End-Special-Headers:", NULL, FALSE},
1420 {NULL, NULL, FALSE}};
1423 gint mailval = 0, newsval = 0;
1425 gchar *smtpserver = NULL;
1426 GSList *to_list = NULL;
1427 GSList *newsgroup_list = NULL;
1428 gchar *savecopyfolder = NULL;
1429 gchar *replymessageid = NULL;
1430 gchar *fwdmessageid = NULL;
1431 gchar *privacy_system = NULL;
1432 gboolean encrypt = FALSE;
1433 gchar *encrypt_data = NULL;
1434 gchar buf[BUFFSIZE];
1436 PrefsAccount *mailac = NULL, *newsac = NULL;
1437 gboolean save_clear_text = TRUE;
1438 gchar *tmp_enc_file = NULL;
1442 g_return_val_if_fail(file != NULL, -1);
1444 if ((fp = g_fopen(file, "rb")) == NULL) {
1445 FILE_OP_ERROR(file, "fopen");
1447 if (*errstr) g_free(*errstr);
1448 *errstr = g_strdup_printf(_("Couldn't open file %s."), file);
1453 while ((hnum = procheader_get_one_field(buf, sizeof(buf), fp, qentry))
1455 gchar *p = buf + strlen(qentry[hnum].name);
1463 if (smtpserver == NULL)
1464 smtpserver = g_strdup(p);
1467 to_list = address_list_append(to_list, p);
1470 newsgroup_list = newsgroup_list_append(newsgroup_list, p);
1472 case Q_MAIL_ACCOUNT_ID:
1473 mailac = account_find_from_id(atoi(p));
1475 case Q_NEWS_ACCOUNT_ID:
1476 newsac = account_find_from_id(atoi(p));
1478 case Q_SAVE_COPY_FOLDER:
1479 if (savecopyfolder == NULL)
1480 savecopyfolder = g_strdup(p);
1482 case Q_REPLY_MESSAGE_ID:
1483 if (replymessageid == NULL)
1484 replymessageid = g_strdup(p);
1486 case Q_FWD_MESSAGE_ID:
1487 if (fwdmessageid == NULL)
1488 fwdmessageid = g_strdup(p);
1490 case Q_PRIVACY_SYSTEM:
1491 case Q_PRIVACY_SYSTEM_OLD:
1492 if (privacy_system == NULL)
1493 privacy_system = g_strdup(p);
1500 case Q_ENCRYPT_DATA:
1501 case Q_ENCRYPT_DATA_OLD:
1502 if (encrypt_data == NULL)
1503 encrypt_data = g_strdup(p);
1506 case Q_CLAWS_HDRS_OLD:
1507 /* end of special headers reached */
1508 goto send_mail; /* can't "break;break;" */
1512 filepos = ftell(fp);
1517 if (mailac && mailac->save_encrypted_as_clear_text
1518 && !mailac->encrypt_to_self)
1519 save_clear_text = TRUE;
1521 save_clear_text = FALSE;
1526 mimeinfo = procmime_scan_queue_file(file);
1527 if (!privacy_encrypt(privacy_system, mimeinfo, encrypt_data)
1528 || (fp = my_tmpfile()) == NULL
1529 || procmime_write_mimeinfo(mimeinfo, fp) < 0) {
1532 procmime_mimeinfo_free_all(mimeinfo);
1535 slist_free_strings(to_list);
1536 g_slist_free(to_list);
1537 slist_free_strings(newsgroup_list);
1538 g_slist_free(newsgroup_list);
1539 g_free(savecopyfolder);
1540 g_free(replymessageid);
1541 g_free(fwdmessageid);
1542 g_free(privacy_system);
1543 g_free(encrypt_data);
1545 if (*errstr) g_free(*errstr);
1546 *errstr = g_strdup_printf(_("Couldn't encrypt the email: %s"),
1547 privacy_get_error());
1553 if (!save_clear_text) {
1554 gchar *content = NULL;
1555 FILE *tmpfp = get_tmpfile_in_dir(get_mime_tmp_dir(), &tmp_enc_file);
1559 content = file_read_stream_to_str(fp);
1562 str_write_to_file(content, tmp_enc_file);
1565 g_warning("couldn't get tempfile\n");
1569 procmime_mimeinfo_free_all(mimeinfo);
1575 debug_print("Sending message by mail\n");
1578 if (*errstr) g_free(*errstr);
1579 *errstr = g_strdup_printf(_("Queued message header is broken."));
1582 } else if (mailac && mailac->use_mail_command &&
1583 mailac->mail_command && (* mailac->mail_command)) {
1584 mailval = send_message_local(mailac->mail_command, fp);
1588 mailac = account_find_from_smtp_server(from, smtpserver);
1590 g_warning("Account not found. "
1591 "Using current account...\n");
1592 mailac = cur_account;
1597 mailval = send_message_smtp_full(mailac, to_list, fp, keep_session);
1598 if (mailval == -1 && errstr) {
1599 if (*errstr) g_free(*errstr);
1600 *errstr = g_strdup_printf(_("An error happened during SMTP session."));
1603 PrefsAccount tmp_ac;
1605 g_warning("Account not found.\n");
1607 memset(&tmp_ac, 0, sizeof(PrefsAccount));
1608 tmp_ac.address = from;
1609 tmp_ac.smtp_server = smtpserver;
1610 tmp_ac.smtpport = SMTP_PORT;
1611 mailval = send_message_smtp(&tmp_ac, to_list, fp);
1612 if (mailval == -1 && errstr) {
1613 if (*errstr) g_free(*errstr);
1614 *errstr = g_strdup_printf(_("No specific account has been found to "
1615 "send, and an error happened during SMTP session."));
1619 } else if (!to_list && !newsgroup_list) {
1621 if (*errstr) g_free(*errstr);
1622 *errstr = g_strdup(_("Couldn't determine sending informations. "
1623 "Maybe the email hasn't been generated by Claws Mail."));
1628 fseek(fp, filepos, SEEK_SET);
1629 if (newsgroup_list && (mailval == 0)) {
1634 /* write to temporary file */
1635 tmp = g_strdup_printf("%s%ctmp%p", g_get_tmp_dir(),
1636 G_DIR_SEPARATOR, file);
1637 if ((tmpfp = g_fopen(tmp, "wb")) == NULL) {
1638 FILE_OP_ERROR(tmp, "fopen");
1640 alertpanel_error(_("Couldn't create temporary file for news sending."));
1642 if (change_file_mode_rw(tmpfp, tmp) < 0) {
1643 FILE_OP_ERROR(tmp, "chmod");
1644 g_warning("can't change file mode\n");
1647 while ((newsval == 0) && fgets(buf, sizeof(buf), fp) != NULL) {
1648 if (fputs(buf, tmpfp) == EOF) {
1649 FILE_OP_ERROR(tmp, "fputs");
1652 if (*errstr) g_free(*errstr);
1653 *errstr = g_strdup_printf(_("Error when writing temporary file for news sending."));
1660 debug_print("Sending message by news\n");
1662 folder = FOLDER(newsac->folder);
1664 newsval = news_post(folder, tmp);
1665 if (newsval < 0 && errstr) {
1666 if (*errstr) g_free(*errstr);
1667 *errstr = g_strdup_printf(_("Error occurred while posting the message to %s."),
1668 newsac->nntp_server);
1678 /* save message to outbox */
1679 if (mailval == 0 && newsval == 0 && savecopyfolder) {
1682 debug_print("saving sent message...\n");
1684 outbox = folder_find_item_from_identifier(savecopyfolder);
1686 outbox = folder_get_default_outbox();
1688 if (save_clear_text || tmp_enc_file == NULL) {
1689 gboolean saved = FALSE;
1690 *queued_removed = FALSE;
1691 if (queue && msgnum > 0) {
1692 MsgInfo *queued_mail = folder_item_get_msginfo(queue, msgnum);
1693 if (folder_item_move_msg(outbox, queued_mail) >= 0) {
1694 debug_print("moved queued mail %d to sent folder\n", msgnum);
1696 *queued_removed = TRUE;
1697 } else if (folder_item_copy_msg(outbox, queued_mail) >= 0) {
1698 debug_print("copied queued mail %d to sent folder\n", msgnum);
1701 procmsg_msginfo_free(queued_mail);
1704 debug_print("resaving clear text queued mail to sent folder\n");
1705 procmsg_save_to_outbox(outbox, file, TRUE);
1708 debug_print("saving encrpyted queued mail to sent folder\n");
1709 procmsg_save_to_outbox(outbox, tmp_enc_file, FALSE);
1713 if (tmp_enc_file != NULL) {
1714 g_unlink(tmp_enc_file);
1716 tmp_enc_file = NULL;
1719 if (replymessageid != NULL || fwdmessageid != NULL) {
1723 if (replymessageid != NULL)
1724 tokens = g_strsplit(replymessageid, "\t", 0);
1726 tokens = g_strsplit(fwdmessageid, "\t", 0);
1727 item = folder_find_item_from_identifier(tokens[0]);
1729 /* check if queued message has valid folder and message id */
1730 if (item != NULL && tokens[2] != NULL) {
1733 msginfo = folder_item_get_msginfo(item, atoi(tokens[1]));
1735 /* check if referring message exists and has a message id */
1736 if ((msginfo != NULL) &&
1737 (msginfo->msgid != NULL) &&
1738 (strcmp(msginfo->msgid, tokens[2]) != 0)) {
1739 procmsg_msginfo_free(msginfo);
1743 if (msginfo == NULL) {
1744 msginfo = folder_item_get_msginfo_by_msgid(item, tokens[2]);
1747 if (msginfo != NULL) {
1748 if (replymessageid != NULL) {
1749 procmsg_msginfo_unset_flags(msginfo, MSG_FORWARDED, 0);
1750 procmsg_msginfo_set_flags(msginfo, MSG_REPLIED, 0);
1752 procmsg_msginfo_unset_flags(msginfo, MSG_REPLIED, 0);
1753 procmsg_msginfo_set_flags(msginfo, MSG_FORWARDED, 0);
1755 procmsg_msginfo_free(msginfo);
1763 slist_free_strings(to_list);
1764 g_slist_free(to_list);
1765 slist_free_strings(newsgroup_list);
1766 g_slist_free(newsgroup_list);
1767 g_free(savecopyfolder);
1768 g_free(replymessageid);
1769 g_free(fwdmessageid);
1770 g_free(privacy_system);
1771 g_free(encrypt_data);
1773 return (newsval != 0 ? newsval : mailval);
1776 gint procmsg_send_message_queue(const gchar *file, gchar **errstr, FolderItem *queue, gint msgnum, gboolean *queued_removed)
1778 gint result = procmsg_send_message_queue_full(file, FALSE, errstr, queue, msgnum, queued_removed);
1779 toolbar_main_set_sensitive(mainwindow_get_mainwindow());
1783 static void update_folder_msg_counts(FolderItem *item, MsgInfo *msginfo, MsgPermFlags old_flags)
1785 MsgPermFlags new_flags = msginfo->flags.perm_flags;
1788 if (!(old_flags & MSG_NEW) && (new_flags & MSG_NEW)) {
1792 if ((old_flags & MSG_NEW) && !(new_flags & MSG_NEW)) {
1797 if (!(old_flags & MSG_UNREAD) && (new_flags & MSG_UNREAD)) {
1798 item->unread_msgs++;
1799 if (procmsg_msg_has_marked_parent(msginfo))
1800 item->unreadmarked_msgs++;
1803 if ((old_flags & MSG_UNREAD) && !(new_flags & MSG_UNREAD)) {
1804 item->unread_msgs--;
1805 if (procmsg_msg_has_marked_parent(msginfo))
1806 item->unreadmarked_msgs--;
1810 if (!(old_flags & MSG_MARKED) && (new_flags & MSG_MARKED)) {
1811 procmsg_update_unread_children(msginfo, TRUE);
1812 item->marked_msgs++;
1815 if ((old_flags & MSG_MARKED) && !(new_flags & MSG_MARKED)) {
1816 procmsg_update_unread_children(msginfo, FALSE);
1817 item->marked_msgs--;
1821 void procmsg_msginfo_set_flags(MsgInfo *msginfo, MsgPermFlags perm_flags, MsgTmpFlags tmp_flags)
1824 MsgInfoUpdate msginfo_update;
1825 MsgPermFlags perm_flags_new, perm_flags_old;
1826 MsgTmpFlags tmp_flags_old;
1828 g_return_if_fail(msginfo != NULL);
1829 item = msginfo->folder;
1830 g_return_if_fail(item != NULL);
1832 debug_print("Setting flags for message %d in folder %s\n", msginfo->msgnum, item->path);
1834 /* Perm Flags handling */
1835 perm_flags_old = msginfo->flags.perm_flags;
1836 perm_flags_new = msginfo->flags.perm_flags | perm_flags;
1837 if ((perm_flags & MSG_IGNORE_THREAD) || (perm_flags_old & MSG_IGNORE_THREAD)) {
1838 perm_flags_new &= ~(MSG_NEW | MSG_UNREAD);
1841 if (perm_flags_old != perm_flags_new) {
1842 folder_item_change_msg_flags(msginfo->folder, msginfo, perm_flags_new);
1844 update_folder_msg_counts(item, msginfo, perm_flags_old);
1848 /* Tmp flags handling */
1849 tmp_flags_old = msginfo->flags.tmp_flags;
1850 msginfo->flags.tmp_flags |= tmp_flags;
1852 /* update notification */
1853 if ((perm_flags_old != perm_flags_new) || (tmp_flags_old != msginfo->flags.tmp_flags)) {
1854 msginfo_update.msginfo = msginfo;
1855 msginfo_update.flags = MSGINFO_UPDATE_FLAGS;
1856 hooks_invoke(MSGINFO_UPDATE_HOOKLIST, &msginfo_update);
1857 folder_item_update(msginfo->folder, F_ITEM_UPDATE_MSGCNT);
1861 void procmsg_msginfo_unset_flags(MsgInfo *msginfo, MsgPermFlags perm_flags, MsgTmpFlags 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("Unsetting 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 & ~perm_flags;
1878 if (perm_flags_old != perm_flags_new) {
1879 folder_item_change_msg_flags(msginfo->folder, msginfo, perm_flags_new);
1881 update_folder_msg_counts(item, msginfo, perm_flags_old);
1884 /* Tmp flags hanlding */
1885 tmp_flags_old = msginfo->flags.tmp_flags;
1886 msginfo->flags.tmp_flags &= ~tmp_flags;
1888 /* update notification */
1889 if ((perm_flags_old != perm_flags_new) || (tmp_flags_old != msginfo->flags.tmp_flags)) {
1890 msginfo_update.msginfo = msginfo;
1891 msginfo_update.flags = MSGINFO_UPDATE_FLAGS;
1892 hooks_invoke(MSGINFO_UPDATE_HOOKLIST, &msginfo_update);
1893 folder_item_update(msginfo->folder, F_ITEM_UPDATE_MSGCNT);
1897 void procmsg_msginfo_change_flags(MsgInfo *msginfo,
1898 MsgPermFlags add_perm_flags, MsgTmpFlags add_tmp_flags,
1899 MsgPermFlags rem_perm_flags, MsgTmpFlags rem_tmp_flags)
1902 MsgInfoUpdate msginfo_update;
1903 MsgPermFlags perm_flags_new, perm_flags_old;
1904 MsgTmpFlags tmp_flags_old;
1906 g_return_if_fail(msginfo != NULL);
1907 item = msginfo->folder;
1908 g_return_if_fail(item != NULL);
1910 debug_print("Changing flags for message %d in folder %s\n", msginfo->msgnum, item->path);
1912 /* Perm Flags handling */
1913 perm_flags_old = msginfo->flags.perm_flags;
1914 perm_flags_new = (msginfo->flags.perm_flags & ~rem_perm_flags) | add_perm_flags;
1915 if ((add_perm_flags & MSG_IGNORE_THREAD) || (perm_flags_old & MSG_IGNORE_THREAD)) {
1916 perm_flags_new &= ~(MSG_NEW | MSG_UNREAD);
1919 if (perm_flags_old != perm_flags_new) {
1920 folder_item_change_msg_flags(msginfo->folder, msginfo, perm_flags_new);
1922 update_folder_msg_counts(item, msginfo, perm_flags_old);
1926 /* Tmp flags handling */
1927 tmp_flags_old = msginfo->flags.tmp_flags;
1928 msginfo->flags.tmp_flags &= ~rem_tmp_flags;
1929 msginfo->flags.tmp_flags |= add_tmp_flags;
1931 /* update notification */
1932 if ((perm_flags_old != perm_flags_new) || (tmp_flags_old != msginfo->flags.tmp_flags)) {
1933 msginfo_update.msginfo = msginfo;
1934 msginfo_update.flags = MSGINFO_UPDATE_FLAGS;
1935 hooks_invoke(MSGINFO_UPDATE_HOOKLIST, &msginfo_update);
1936 folder_item_update(msginfo->folder, F_ITEM_UPDATE_MSGCNT);
1941 *\brief check for flags (e.g. mark) in prior msgs of current thread
1943 *\param info Current message
1944 *\param perm_flags Flags to be checked
1945 *\param parentmsgs Hash of prior msgs to avoid loops
1947 *\return gboolean TRUE if perm_flags are found
1949 static gboolean procmsg_msg_has_flagged_parent_real(MsgInfo *info,
1950 MsgPermFlags perm_flags, GHashTable *parentmsgs)
1954 g_return_val_if_fail(info != NULL, FALSE);
1956 if (info != NULL && info->folder != NULL && info->inreplyto != NULL) {
1957 tmp = folder_item_get_msginfo_by_msgid(info->folder,
1959 if (tmp && (tmp->flags.perm_flags & perm_flags)) {
1960 procmsg_msginfo_free(tmp);
1962 } else if (tmp != NULL) {
1965 if (g_hash_table_lookup(parentmsgs, info)) {
1966 debug_print("loop detected: %d\n",
1970 g_hash_table_insert(parentmsgs, info, "1");
1971 result = procmsg_msg_has_flagged_parent_real(
1972 tmp, perm_flags, parentmsgs);
1974 procmsg_msginfo_free(tmp);
1984 *\brief Callback for cleaning up hash of parentmsgs
1986 static gboolean parentmsgs_hash_remove(gpointer key,
1994 *\brief Set up list of parentmsgs
1995 * See procmsg_msg_has_flagged_parent_real()
1997 gboolean procmsg_msg_has_flagged_parent(MsgInfo *info, MsgPermFlags perm_flags)
2000 static GHashTable *parentmsgs = NULL;
2002 if (parentmsgs == NULL)
2003 parentmsgs = g_hash_table_new(NULL, NULL);
2005 result = procmsg_msg_has_flagged_parent_real(info, perm_flags, parentmsgs);
2006 g_hash_table_foreach_remove(parentmsgs, parentmsgs_hash_remove, NULL);
2012 *\brief Check if msgs prior in thread are marked
2013 * See procmsg_msg_has_flagged_parent_real()
2015 gboolean procmsg_msg_has_marked_parent(MsgInfo *info)
2017 return procmsg_msg_has_flagged_parent(info, MSG_MARKED);
2021 static GSList *procmsg_find_children_func(MsgInfo *info,
2022 GSList *children, GSList *all)
2026 g_return_val_if_fail(info!=NULL, children);
2027 if (info->msgid == NULL)
2030 for (cur = all; cur != NULL; cur = g_slist_next(cur)) {
2031 MsgInfo *tmp = (MsgInfo *)cur->data;
2032 if (tmp->inreplyto && !strcmp(tmp->inreplyto, info->msgid)) {
2033 /* Check if message is already in the list */
2034 if ((children == NULL) ||
2035 (g_slist_index(children, tmp) == -1)) {
2036 children = g_slist_prepend(children,
2037 procmsg_msginfo_new_ref(tmp));
2038 children = procmsg_find_children_func(tmp,
2047 static GSList *procmsg_find_children (MsgInfo *info)
2052 g_return_val_if_fail(info!=NULL, NULL);
2053 all = folder_item_get_msg_list(info->folder);
2054 children = procmsg_find_children_func(info, NULL, all);
2055 if (children != NULL) {
2056 for (cur = all; cur != NULL; cur = g_slist_next(cur)) {
2057 /* this will not free the used pointers
2058 created with procmsg_msginfo_new_ref */
2059 procmsg_msginfo_free((MsgInfo *)cur->data);
2067 static void procmsg_update_unread_children(MsgInfo *info, gboolean newly_marked)
2069 GSList *children = procmsg_find_children(info);
2071 for (cur = children; cur != NULL; cur = g_slist_next(cur)) {
2072 MsgInfo *tmp = (MsgInfo *)cur->data;
2073 if(MSG_IS_UNREAD(tmp->flags) && !MSG_IS_IGNORE_THREAD(tmp->flags)) {
2075 info->folder->unreadmarked_msgs++;
2077 info->folder->unreadmarked_msgs--;
2078 folder_item_update(info->folder, F_ITEM_UPDATE_MSGCNT);
2080 procmsg_msginfo_free(tmp);
2082 g_slist_free(children);
2086 * Set the destination folder for a copy or move operation
2088 * \param msginfo The message which's destination folder is changed
2089 * \param to_folder The destination folder for the operation
2091 void procmsg_msginfo_set_to_folder(MsgInfo *msginfo, FolderItem *to_folder)
2093 if(msginfo->to_folder != NULL) {
2094 msginfo->to_folder->op_count--;
2095 folder_item_update(msginfo->to_folder, F_ITEM_UPDATE_MSGCNT);
2097 msginfo->to_folder = to_folder;
2098 if(to_folder != NULL) {
2099 to_folder->op_count++;
2100 folder_item_update(msginfo->to_folder, F_ITEM_UPDATE_MSGCNT);
2105 * Apply filtering actions to the msginfo
2107 * \param msginfo The MsgInfo describing the message that should be filtered
2108 * \return TRUE if the message was moved and MsgInfo is now invalid,
2111 static gboolean procmsg_msginfo_filter(MsgInfo *msginfo, PrefsAccount* ac_prefs)
2113 MailFilteringData mail_filtering_data;
2115 mail_filtering_data.msginfo = msginfo;
2116 mail_filtering_data.msglist = NULL;
2117 mail_filtering_data.filtered = NULL;
2118 mail_filtering_data.unfiltered = NULL;
2119 mail_filtering_data.account = ac_prefs;
2121 if (!ac_prefs || ac_prefs->filterhook_on_recv)
2122 if (hooks_invoke(MAIL_FILTERING_HOOKLIST, &mail_filtering_data))
2125 /* filter if enabled in prefs or move to inbox if not */
2126 if((filtering_rules != NULL) &&
2127 filter_message_by_msginfo(filtering_rules, msginfo, ac_prefs,
2128 FILTERING_INCORPORATION, NULL)) {
2135 void procmsg_msglist_filter(GSList *list, PrefsAccount *ac,
2136 GSList **filtered, GSList **unfiltered,
2139 GSList *cur, *to_do = NULL;
2140 gint total = 0, curnum = 0;
2141 MailFilteringData mail_filtering_data;
2143 g_return_if_fail(filtered != NULL);
2144 g_return_if_fail(unfiltered != NULL);
2152 total = g_slist_length(list);
2156 *unfiltered = g_slist_copy(list);
2160 statusbar_print_all(_("Filtering messages...\n"));
2162 mail_filtering_data.msginfo = NULL;
2163 mail_filtering_data.msglist = list;
2164 mail_filtering_data.filtered = NULL;
2165 mail_filtering_data.unfiltered = NULL;
2166 mail_filtering_data.account = ac;
2168 if (!ac || ac->filterhook_on_recv)
2169 hooks_invoke(MAIL_LISTFILTERING_HOOKLIST, &mail_filtering_data);
2171 if (mail_filtering_data.filtered == NULL &&
2172 mail_filtering_data.unfiltered == NULL) {
2173 /* nothing happened */
2174 debug_print(MAIL_LISTFILTERING_HOOKLIST " did nothing. filtering whole list normally.\n");
2177 if (mail_filtering_data.filtered != NULL) {
2178 /* keep track of what's been filtered by the hooks */
2179 debug_print(MAIL_LISTFILTERING_HOOKLIST " filtered some stuff. total %d filtered %d unfilt %d.\n",
2180 g_slist_length(list),
2181 g_slist_length(mail_filtering_data.filtered),
2182 g_slist_length(mail_filtering_data.unfiltered));
2184 *filtered = g_slist_copy(mail_filtering_data.filtered);
2186 if (mail_filtering_data.unfiltered != NULL) {
2187 /* what the hooks didn't handle will go in filtered or
2188 * unfiltered in the next loop */
2189 debug_print(MAIL_LISTFILTERING_HOOKLIST " left unfiltered stuff. total %d filtered %d unfilt %d.\n",
2190 g_slist_length(list),
2191 g_slist_length(mail_filtering_data.filtered),
2192 g_slist_length(mail_filtering_data.unfiltered));
2193 to_do = mail_filtering_data.unfiltered;
2196 for (cur = to_do; cur; cur = cur->next) {
2197 MsgInfo *info = (MsgInfo *)cur->data;
2198 if (procmsg_msginfo_filter(info, ac))
2199 *filtered = g_slist_prepend(*filtered, info);
2201 *unfiltered = g_slist_prepend(*unfiltered, info);
2202 statusbar_progress_all(curnum++, total, prefs_common.statusbar_update_step);
2205 g_slist_free(mail_filtering_data.filtered);
2206 g_slist_free(mail_filtering_data.unfiltered);
2208 *filtered = g_slist_reverse(*filtered);
2209 *unfiltered = g_slist_reverse(*unfiltered);
2211 statusbar_progress_all(0,0,0);
2212 statusbar_pop_all();
2215 MsgInfo *procmsg_msginfo_new_from_mimeinfo(MsgInfo *src_msginfo, MimeInfo *mimeinfo)
2217 MsgInfo *tmp_msginfo = NULL;
2218 MsgFlags flags = {0, 0};
2219 gchar *tmpfile = get_tmp_file();
2220 FILE *fp = g_fopen(tmpfile, "wb");
2222 if (!mimeinfo || mimeinfo->type != MIMETYPE_MESSAGE ||
2223 g_ascii_strcasecmp(mimeinfo->subtype, "rfc822")) {
2224 g_warning("procmsg_msginfo_new_from_mimeinfo(): unsuitable mimeinfo");
2231 if (fp && procmime_write_mimeinfo(mimeinfo, fp) >= 0) {
2234 tmp_msginfo = procheader_parse_file(
2241 if (tmp_msginfo != NULL) {
2243 tmp_msginfo->folder = src_msginfo->folder;
2244 tmp_msginfo->plaintext_file = g_strdup(tmpfile);
2246 g_warning("procmsg_msginfo_new_from_mimeinfo(): Can't generate new msginfo");
2254 static GSList *spam_learners = NULL;
2256 void procmsg_register_spam_learner (int (*learn_func)(MsgInfo *info, GSList *list, gboolean spam))
2258 if (!g_slist_find(spam_learners, learn_func))
2259 spam_learners = g_slist_append(spam_learners, learn_func);
2260 if (mainwindow_get_mainwindow()) {
2261 main_window_set_menu_sensitive(mainwindow_get_mainwindow());
2262 summary_set_menu_sensitive(
2263 mainwindow_get_mainwindow()->summaryview);
2264 toolbar_main_set_sensitive(mainwindow_get_mainwindow());
2268 void procmsg_unregister_spam_learner (int (*learn_func)(MsgInfo *info, GSList *list, gboolean spam))
2270 spam_learners = g_slist_remove(spam_learners, learn_func);
2271 if (mainwindow_get_mainwindow()) {
2272 main_window_set_menu_sensitive(mainwindow_get_mainwindow());
2273 summary_set_menu_sensitive(
2274 mainwindow_get_mainwindow()->summaryview);
2275 toolbar_main_set_sensitive(mainwindow_get_mainwindow());
2279 gboolean procmsg_spam_can_learn(void)
2281 return g_slist_length(spam_learners) > 0;
2284 int procmsg_spam_learner_learn (MsgInfo *info, GSList *list, gboolean spam)
2286 GSList *cur = spam_learners;
2288 for (; cur; cur = cur->next) {
2289 int ((*func)(MsgInfo *info, GSList *list, gboolean spam)) = cur->data;
2290 ret |= func(info, list, spam);
2295 static gchar *spam_folder_item = NULL;
2296 static FolderItem * (*procmsg_spam_get_folder_func)(MsgInfo *msginfo) = NULL;
2297 void procmsg_spam_set_folder (const char *item_identifier, FolderItem *(*spam_get_folder_func)(MsgInfo *info))
2299 g_free(spam_folder_item);
2300 if (item_identifier)
2301 spam_folder_item = g_strdup(item_identifier);
2303 spam_folder_item = NULL;
2304 if (spam_get_folder_func != NULL)
2305 procmsg_spam_get_folder_func = spam_get_folder_func;
2307 procmsg_spam_get_folder_func = NULL;
2310 FolderItem *procmsg_spam_get_folder (MsgInfo *msginfo)
2312 FolderItem *item = NULL;
2314 if (procmsg_spam_get_folder_func)
2315 item = procmsg_spam_get_folder_func(msginfo);
2316 if (item == NULL && spam_folder_item)
2317 item = folder_find_item_from_identifier(spam_folder_item);
2319 item = folder_get_default_trash();
2323 static void item_has_queued_mails(FolderItem *item, gpointer data)
2325 gboolean *result = (gboolean *)data;
2326 if (*result == TRUE)
2328 if (folder_has_parent_of_type(item, F_QUEUE) && item->total_msgs > 0)
2332 gboolean procmsg_have_queued_mails_fast (void)
2334 gboolean result = FALSE;
2335 folder_func_to_all_folders(item_has_queued_mails, &result);
2339 static void item_has_trashed_mails(FolderItem *item, gpointer data)
2341 gboolean *result = (gboolean *)data;
2342 if (*result == TRUE)
2344 if (folder_has_parent_of_type(item, F_TRASH) && item->total_msgs > 0)
2348 gboolean procmsg_have_trashed_mails_fast (void)
2350 gboolean result = FALSE;
2351 folder_func_to_all_folders(item_has_trashed_mails, &result);