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))
670 if (msginfo->total_size != 0 &&
671 msginfo->size != (off_t)msginfo->total_size)
672 partial_mark_for_delete(msginfo);
674 procmsg_msginfo_free(msginfo);
677 folder_item_remove_all_msg(trash);
680 if (!trash->node || !trash->node->children)
683 node = trash->node->children;
684 while (node != NULL) {
686 procmsg_empty_trash(FOLDER_ITEM(node->data));
691 void procmsg_empty_all_trash(void)
696 for (cur = folder_get_list(); cur != NULL; cur = cur->next) {
697 Folder *folder = FOLDER(cur->data);
698 trash = folder->trash;
699 procmsg_empty_trash(trash);
700 if (folder->account && folder->account->set_trash_folder &&
701 folder_find_item_from_identifier(folder->account->trash_folder))
703 folder_find_item_from_identifier(folder->account->trash_folder));
707 static PrefsAccount *procmsg_get_account_from_file(const gchar *file)
709 PrefsAccount *mailac = NULL;
713 static HeaderEntry qentry[] = {{"S:", NULL, FALSE},
714 {"SSV:", NULL, FALSE},
716 {"NG:", NULL, FALSE},
717 {"MAID:", NULL, FALSE},
718 {"NAID:", NULL, FALSE},
719 {"SCF:", NULL, FALSE},
720 {"RMID:", NULL, FALSE},
721 {"FMID:", NULL, FALSE},
722 {"X-Claws-Privacy-System:", NULL, FALSE},
723 {"X-Claws-Encrypt:", NULL, FALSE},
724 {"X-Claws-Encrypt-Data:", NULL, FALSE},
725 {"X-Claws-End-Special-Headers", NULL, FALSE},
726 {"X-Sylpheed-Privacy-System:", NULL, FALSE},
727 {"X-Sylpheed-Encrypt:", NULL, FALSE},
728 {"X-Sylpheed-Encrypt-Data:", NULL, FALSE},
729 {NULL, NULL, FALSE}};
731 g_return_val_if_fail(file != NULL, NULL);
733 if ((fp = g_fopen(file, "rb")) == NULL) {
734 FILE_OP_ERROR(file, "fopen");
738 while ((hnum = procheader_get_one_field(buf, sizeof(buf), fp, qentry))
740 gchar *p = buf + strlen(qentry[hnum].name);
742 if (hnum == Q_MAIL_ACCOUNT_ID) {
743 mailac = account_find_from_id(atoi(p));
751 static GSList *procmsg_list_sort_by_account(FolderItem *queue, GSList *list)
753 GSList *result = NULL;
755 PrefsAccount *last_account = NULL;
758 gboolean nothing_to_sort = TRUE;
763 orig = g_slist_copy(list);
765 msg = (MsgInfo *)orig->data;
767 for (cur = orig; cur; cur = cur->next)
768 debug_print("sort before %s\n", ((MsgInfo *)cur->data)->from);
773 nothing_to_sort = TRUE;
777 PrefsAccount *ac = NULL;
778 msg = (MsgInfo *)cur->data;
779 file = folder_item_fetch_msg(queue, msg->msgnum);
780 ac = procmsg_get_account_from_file(file);
783 if (last_account == NULL || (ac != NULL && ac == last_account)) {
784 result = g_slist_append(result, msg);
785 orig = g_slist_remove(orig, msg);
787 nothing_to_sort = FALSE;
793 if (orig || g_slist_length(orig)) {
794 if (!last_account && nothing_to_sort) {
795 /* can't find an account for the rest of the list */
798 result = g_slist_append(result, cur->data);
809 for (cur = result; cur; cur = cur->next)
810 debug_print("sort after %s\n", ((MsgInfo *)cur->data)->from);
817 static gboolean procmsg_is_last_for_account(FolderItem *queue, MsgInfo *msginfo, GSList *elem)
819 gchar *file = folder_item_fetch_msg(queue, msginfo->msgnum);
820 PrefsAccount *ac = procmsg_get_account_from_file(file);
823 for (cur = elem; cur; cur = cur->next) {
824 MsgInfo *cur_msginfo = (MsgInfo *)cur->data;
825 file = folder_item_fetch_msg(queue, cur_msginfo->msgnum);
827 if (cur_msginfo != msginfo && !MSG_IS_LOCKED(cur_msginfo->flags)) {
828 if (procmsg_get_account_from_file(file) == ac) {
839 static gboolean send_queue_lock = FALSE;
841 *\brief Send messages in queue
843 *\param queue Queue folder to process
844 *\param save_msgs Unused
846 *\return Number of messages sent, negative if an error occurred
847 * positive if no error occurred
849 gint procmsg_send_queue(FolderItem *queue, gboolean save_msgs, gchar **errstr)
851 gint sent = 0, err = 0;
853 GSList *sorted_list = NULL;
856 if (send_queue_lock) {
857 /* Avoid having to translate two similar strings */
858 log_warning("%s\n", _("Already trying to send."));
860 if (*errstr) g_free(*errstr);
861 *errstr = g_strdup_printf(_("Already trying to send."));
863 toolbar_main_set_sensitive(mainwindow_get_mainwindow());
866 send_queue_lock = TRUE;
869 queue = folder_get_default_queue();
872 send_queue_lock = FALSE;
877 toolbar_main_set_sensitive(mainwindow_get_mainwindow());
879 folder_item_scan(queue);
880 list = folder_item_get_msg_list(queue);
882 /* sort the list per sender account; this helps reusing the same SMTP server */
883 sorted_list = procmsg_list_sort_by_account(queue, list);
885 for (elem = sorted_list; elem != NULL; elem = elem->next) {
889 msginfo = (MsgInfo *)(elem->data);
890 if (!MSG_IS_LOCKED(msginfo->flags)) {
891 file = folder_item_fetch_msg(queue, msginfo->msgnum);
893 gboolean queued_removed = FALSE;
894 if (procmsg_send_message_queue_full(file,
895 !procmsg_is_last_for_account(queue, msginfo, elem),
896 errstr, queue, msginfo->msgnum, &queued_removed) < 0) {
897 g_warning("Sending queued message %d failed.\n",
903 folder_item_remove_msg(queue, msginfo->msgnum);
908 /* FIXME: supposedly if only one message is locked, and queue
909 * is being flushed, the following free says something like
910 * "freeing msg ## in folder (nil)". */
911 procmsg_msginfo_free(msginfo);
914 g_slist_free(sorted_list);
915 folder_item_scan(queue);
917 if (queue->node && queue->node->children) {
918 node = queue->node->children;
919 while (node != NULL) {
922 res = procmsg_send_queue(FOLDER_ITEM(node->data), save_msgs, errstr);
930 send_queue_lock = FALSE;
932 toolbar_main_set_sensitive(mainwindow_get_mainwindow());
934 return (err != 0 ? -err : sent);
937 gboolean procmsg_is_sending(void)
939 return send_queue_lock;
943 *\brief Determine if a queue folder is empty
945 *\param queue Queue folder to process
947 *\return TRUE if the queue folder is empty, otherwise return FALSE
949 gboolean procmsg_queue_is_empty(FolderItem *queue)
952 gboolean res = FALSE;
954 queue = folder_get_default_queue();
955 g_return_val_if_fail(queue != NULL, TRUE);
957 folder_item_scan(queue);
958 list = folder_item_get_msg_list(queue);
959 res = (list == NULL);
960 procmsg_msg_list_free(list);
964 if (queue->node && queue->node->children) {
965 node = queue->node->children;
966 while (node != NULL) {
968 if (!procmsg_queue_is_empty(FOLDER_ITEM(node->data)))
977 gint procmsg_remove_special_headers(const gchar *in, const gchar *out)
982 if ((fp = g_fopen(in, "rb")) == NULL) {
983 FILE_OP_ERROR(in, "fopen");
986 if ((outfp = g_fopen(out, "wb")) == NULL) {
987 FILE_OP_ERROR(out, "fopen");
991 while (fgets(buf, sizeof(buf), fp) != NULL) {
993 if ((!strncmp(buf, "X-Claws-End-Special-Headers: 1",
994 strlen("X-Claws-End-Special-Headers:"))) ||
995 (!strncmp(buf, "X-Sylpheed-End-Special-Headers: 1",
996 strlen("X-Sylpheed-End-Special-Headers:"))))
999 if (buf[0] == '\r' || buf[0] == '\n') break;
1000 /* from other mailers */
1001 if (!strncmp(buf, "Date: ", 6)
1002 || !strncmp(buf, "To: ", 4)
1003 || !strncmp(buf, "From: ", 6)
1004 || !strncmp(buf, "Subject: ", 9)) {
1009 while (fgets(buf, sizeof(buf), fp) != NULL)
1016 static gint procmsg_save_to_outbox(FolderItem *outbox, const gchar *file,
1020 MsgInfo *msginfo, *tmp_msginfo;
1021 MsgFlags flag = {0, 0};
1023 debug_print("saving sent message...\n");
1026 outbox = folder_get_default_outbox();
1027 g_return_val_if_fail(outbox != NULL, -1);
1029 /* remove queueing headers */
1031 gchar tmp[MAXPATHLEN + 1];
1033 g_snprintf(tmp, sizeof(tmp), "%s%ctmpmsg.out.%08x",
1034 get_rc_dir(), G_DIR_SEPARATOR, (guint) rand());
1036 if (procmsg_remove_special_headers(file, tmp) !=0)
1039 folder_item_scan(outbox);
1040 if ((num = folder_item_add_msg(outbox, tmp, &flag, TRUE)) < 0) {
1041 g_warning("can't save message\n");
1046 folder_item_scan(outbox);
1047 if ((num = folder_item_add_msg
1048 (outbox, file, &flag, FALSE)) < 0) {
1049 g_warning("can't save message\n");
1053 msginfo = folder_item_get_msginfo(outbox, num); /* refcnt++ */
1054 tmp_msginfo = procmsg_msginfo_get_full_info(msginfo); /* refcnt++ */
1055 if (msginfo != NULL) {
1056 procmsg_msginfo_unset_flags(msginfo, ~0, 0);
1057 procmsg_msginfo_free(msginfo); /* refcnt-- */
1058 /* tmp_msginfo == msginfo */
1059 if (tmp_msginfo && msginfo->extradata &&
1060 (msginfo->extradata->dispositionnotificationto ||
1061 msginfo->extradata->returnreceiptto)) {
1062 procmsg_msginfo_set_flags(msginfo, MSG_RETRCPT_SENT, 0);
1064 procmsg_msginfo_free(tmp_msginfo); /* refcnt-- */
1070 void procmsg_print_message(MsgInfo *msginfo, const gchar *cmdline)
1072 static const gchar *def_cmd = "lpr %s";
1073 static guint id = 0;
1079 g_return_if_fail(msginfo);
1081 if (procmime_msginfo_is_encrypted(msginfo))
1082 tmpfp = procmime_get_first_encrypted_text_content(msginfo);
1084 tmpfp = procmime_get_first_text_content(msginfo);
1085 if (tmpfp == NULL) {
1086 g_warning("Can't get text part\n");
1090 prtmp = g_strdup_printf("%s%cprinttmp.%08x",
1091 get_mime_tmp_dir(), G_DIR_SEPARATOR, id++);
1093 if ((prfp = g_fopen(prtmp, "wb")) == NULL) {
1094 FILE_OP_ERROR(prtmp, "fopen");
1100 if (msginfo->date) fprintf(prfp, "Date: %s\n", msginfo->date);
1101 if (msginfo->from) fprintf(prfp, "From: %s\n", msginfo->from);
1102 if (msginfo->to) fprintf(prfp, "To: %s\n", msginfo->to);
1103 if (msginfo->cc) fprintf(prfp, "Cc: %s\n", msginfo->cc);
1104 if (msginfo->newsgroups)
1105 fprintf(prfp, "Newsgroups: %s\n", msginfo->newsgroups);
1106 if (msginfo->subject) fprintf(prfp, "Subject: %s\n", msginfo->subject);
1109 while (fgets(buf, sizeof(buf), tmpfp) != NULL)
1115 if (cmdline && (p = strchr(cmdline, '%')) && *(p + 1) == 's' &&
1116 !strchr(p + 2, '%'))
1117 g_snprintf(buf, sizeof(buf) - 1, cmdline, prtmp);
1120 g_warning("Print command line is invalid: '%s'\n",
1122 g_snprintf(buf, sizeof(buf) - 1, def_cmd, prtmp);
1128 if (buf[strlen(buf) - 1] != '&') strcat(buf, "&");
1132 MsgInfo *procmsg_msginfo_new_ref(MsgInfo *msginfo)
1139 MsgInfo *procmsg_msginfo_new(void)
1141 MsgInfo *newmsginfo;
1143 newmsginfo = g_new0(MsgInfo, 1);
1144 newmsginfo->refcnt = 1;
1149 MsgInfo *procmsg_msginfo_copy(MsgInfo *msginfo)
1151 MsgInfo *newmsginfo;
1154 if (msginfo == NULL) return NULL;
1156 newmsginfo = g_new0(MsgInfo, 1);
1158 newmsginfo->refcnt = 1;
1160 #define MEMBCOPY(mmb) newmsginfo->mmb = msginfo->mmb
1161 #define MEMBDUP(mmb) newmsginfo->mmb = msginfo->mmb ? \
1162 g_strdup(msginfo->mmb) : NULL
1177 MEMBDUP(newsgroups);
1184 MEMBCOPY(to_folder);
1186 if (msginfo->extradata) {
1187 newmsginfo->extradata = g_new0(MsgInfoExtraData, 1);
1188 MEMBDUP(extradata->face);
1189 MEMBDUP(extradata->xface);
1190 MEMBDUP(extradata->dispositionnotificationto);
1191 MEMBDUP(extradata->returnreceiptto);
1192 MEMBDUP(extradata->partial_recv);
1193 MEMBDUP(extradata->account_server);
1194 MEMBDUP(extradata->account_login);
1195 MEMBDUP(extradata->list_post);
1196 MEMBDUP(extradata->list_subscribe);
1197 MEMBDUP(extradata->list_unsubscribe);
1198 MEMBDUP(extradata->list_help);
1199 MEMBDUP(extradata->list_archive);
1200 MEMBDUP(extradata->list_owner);
1203 refs = msginfo->references;
1204 for (refs = msginfo->references; refs != NULL; refs = refs->next) {
1205 newmsginfo->references = g_slist_prepend
1206 (newmsginfo->references, g_strdup(refs->data));
1208 newmsginfo->references = g_slist_reverse(newmsginfo->references);
1211 MEMBDUP(plaintext_file);
1216 MsgInfo *procmsg_msginfo_get_full_info(MsgInfo *msginfo)
1218 MsgInfo *full_msginfo;
1221 if (msginfo == NULL) return NULL;
1223 file = procmsg_get_message_file_path(msginfo);
1224 if (!file || !is_file_exist(file)) {
1226 file = procmsg_get_message_file(msginfo);
1228 if (!file || !is_file_exist(file)) {
1229 g_warning("procmsg_msginfo_get_full_info(): can't get message file.\n");
1233 full_msginfo = procheader_parse_file(file, msginfo->flags, TRUE, FALSE);
1235 if (!full_msginfo) return NULL;
1237 msginfo->total_size = full_msginfo->total_size;
1238 msginfo->planned_download = full_msginfo->planned_download;
1240 if (full_msginfo->extradata) {
1241 if (!msginfo->extradata)
1242 msginfo->extradata = g_new0(MsgInfoExtraData, 1);
1243 if (!msginfo->extradata->list_post)
1244 msginfo->extradata->list_post = g_strdup(full_msginfo->extradata->list_post);
1245 if (!msginfo->extradata->list_subscribe)
1246 msginfo->extradata->list_subscribe = g_strdup(full_msginfo->extradata->list_subscribe);
1247 if (!msginfo->extradata->list_unsubscribe)
1248 msginfo->extradata->list_unsubscribe = g_strdup(full_msginfo->extradata->list_unsubscribe);
1249 if (!msginfo->extradata->list_help)
1250 msginfo->extradata->list_help = g_strdup(full_msginfo->extradata->list_help);
1251 if (!msginfo->extradata->list_archive)
1252 msginfo->extradata->list_archive= g_strdup(full_msginfo->extradata->list_archive);
1253 if (!msginfo->extradata->list_owner)
1254 msginfo->extradata->list_owner = g_strdup(full_msginfo->extradata->list_owner);
1255 if (!msginfo->extradata->xface)
1256 msginfo->extradata->xface = g_strdup(full_msginfo->extradata->xface);
1257 if (!msginfo->extradata->face)
1258 msginfo->extradata->face = g_strdup(full_msginfo->extradata->face);
1259 if (!msginfo->extradata->dispositionnotificationto)
1260 msginfo->extradata->dispositionnotificationto =
1261 g_strdup(full_msginfo->extradata->dispositionnotificationto);
1262 if (!msginfo->extradata->returnreceiptto)
1263 msginfo->extradata->returnreceiptto = g_strdup
1264 (full_msginfo->extradata->returnreceiptto);
1265 if (!msginfo->extradata->partial_recv && full_msginfo->extradata->partial_recv)
1266 msginfo->extradata->partial_recv = g_strdup
1267 (full_msginfo->extradata->partial_recv);
1268 if (!msginfo->extradata->account_server && full_msginfo->extradata->account_server)
1269 msginfo->extradata->account_server = g_strdup
1270 (full_msginfo->extradata->account_server);
1271 if (!msginfo->extradata->account_login && full_msginfo->extradata->account_login)
1272 msginfo->extradata->account_login = g_strdup
1273 (full_msginfo->extradata->account_login);
1275 procmsg_msginfo_free(full_msginfo);
1277 return procmsg_msginfo_new_ref(msginfo);
1280 void procmsg_msginfo_free(MsgInfo *msginfo)
1282 if (msginfo == NULL) return;
1285 if (msginfo->refcnt > 0)
1288 if (msginfo->to_folder) {
1289 msginfo->to_folder->op_count--;
1290 folder_item_update(msginfo->to_folder, F_ITEM_UPDATE_MSGCNT);
1293 g_free(msginfo->fromspace);
1295 g_free(msginfo->fromname);
1297 g_free(msginfo->date);
1298 g_free(msginfo->from);
1299 g_free(msginfo->to);
1300 g_free(msginfo->cc);
1301 g_free(msginfo->newsgroups);
1302 g_free(msginfo->subject);
1303 g_free(msginfo->msgid);
1304 g_free(msginfo->inreplyto);
1305 g_free(msginfo->xref);
1307 if (msginfo->extradata) {
1308 g_free(msginfo->extradata->returnreceiptto);
1309 g_free(msginfo->extradata->dispositionnotificationto);
1310 g_free(msginfo->extradata->xface);
1311 g_free(msginfo->extradata->face);
1312 g_free(msginfo->extradata->list_post);
1313 g_free(msginfo->extradata->list_subscribe);
1314 g_free(msginfo->extradata->list_unsubscribe);
1315 g_free(msginfo->extradata->list_help);
1316 g_free(msginfo->extradata->list_archive);
1317 g_free(msginfo->extradata->list_owner);
1318 g_free(msginfo->extradata->partial_recv);
1319 g_free(msginfo->extradata->account_server);
1320 g_free(msginfo->extradata->account_login);
1321 g_free(msginfo->extradata);
1323 slist_free_strings(msginfo->references);
1324 g_slist_free(msginfo->references);
1326 g_free(msginfo->plaintext_file);
1331 guint procmsg_msginfo_memusage(MsgInfo *msginfo)
1336 memusage += sizeof(MsgInfo);
1337 if (msginfo->fromname)
1338 memusage += strlen(msginfo->fromname);
1340 memusage += strlen(msginfo->date);
1342 memusage += strlen(msginfo->from);
1344 memusage += strlen(msginfo->to);
1346 memusage += strlen(msginfo->cc);
1347 if (msginfo->newsgroups)
1348 memusage += strlen(msginfo->newsgroups);
1349 if (msginfo->subject)
1350 memusage += strlen(msginfo->subject);
1352 memusage += strlen(msginfo->msgid);
1353 if (msginfo->inreplyto)
1354 memusage += strlen(msginfo->inreplyto);
1355 for (refs = msginfo->references; refs; refs=refs->next) {
1356 gchar *r = (gchar *)refs->data;
1357 memusage += r?strlen(r):0;
1359 if (msginfo->fromspace)
1360 memusage += strlen(msginfo->fromspace);
1362 if (msginfo->extradata) {
1363 memusage += sizeof(MsgInfoExtraData);
1364 if (msginfo->extradata->xface)
1365 memusage += strlen(msginfo->extradata->xface);
1366 if (msginfo->extradata->face)
1367 memusage += strlen(msginfo->extradata->face);
1368 if (msginfo->extradata->dispositionnotificationto)
1369 memusage += strlen(msginfo->extradata->dispositionnotificationto);
1370 if (msginfo->extradata->returnreceiptto)
1371 memusage += strlen(msginfo->extradata->returnreceiptto);
1373 if (msginfo->extradata->partial_recv)
1374 memusage += strlen(msginfo->extradata->partial_recv);
1375 if (msginfo->extradata->account_server)
1376 memusage += strlen(msginfo->extradata->account_server);
1377 if (msginfo->extradata->account_login)
1378 memusage += strlen(msginfo->extradata->account_login);
1380 if (msginfo->extradata->list_post)
1381 memusage += strlen(msginfo->extradata->list_post);
1382 if (msginfo->extradata->list_subscribe)
1383 memusage += strlen(msginfo->extradata->list_subscribe);
1384 if (msginfo->extradata->list_unsubscribe)
1385 memusage += strlen(msginfo->extradata->list_unsubscribe);
1386 if (msginfo->extradata->list_help)
1387 memusage += strlen(msginfo->extradata->list_help);
1388 if (msginfo->extradata->list_archive)
1389 memusage += strlen(msginfo->extradata->list_archive);
1390 if (msginfo->extradata->list_owner)
1391 memusage += strlen(msginfo->extradata->list_owner);
1396 static gint procmsg_send_message_queue_full(const gchar *file, gboolean keep_session, gchar **errstr,
1397 FolderItem *queue, gint msgnum, gboolean *queued_removed)
1399 static HeaderEntry qentry[] = {{"S:", NULL, FALSE},
1400 {"SSV:", NULL, FALSE},
1401 {"R:", NULL, FALSE},
1402 {"NG:", NULL, FALSE},
1403 {"MAID:", NULL, FALSE},
1404 {"NAID:", NULL, FALSE},
1405 {"SCF:", NULL, FALSE},
1406 {"RMID:", NULL, FALSE},
1407 {"FMID:", NULL, FALSE},
1408 {"X-Claws-Privacy-System:", NULL, FALSE},
1409 {"X-Claws-Encrypt:", NULL, FALSE},
1410 {"X-Claws-Encrypt-Data:", NULL, FALSE},
1411 {"X-Claws-End-Special-Headers:", NULL, FALSE},
1412 {"X-Sylpheed-Privacy-System:", NULL, FALSE},
1413 {"X-Sylpheed-Encrypt:", NULL, FALSE},
1414 {"X-Sylpheed-Encrypt-Data:", NULL, FALSE},
1415 {"X-Sylpheed-End-Special-Headers:", NULL, FALSE},
1416 {NULL, NULL, FALSE}};
1419 gint mailval = 0, newsval = 0;
1421 gchar *smtpserver = NULL;
1422 GSList *to_list = NULL;
1423 GSList *newsgroup_list = NULL;
1424 gchar *savecopyfolder = NULL;
1425 gchar *replymessageid = NULL;
1426 gchar *fwdmessageid = NULL;
1427 gchar *privacy_system = NULL;
1428 gboolean encrypt = FALSE;
1429 gchar *encrypt_data = NULL;
1430 gchar buf[BUFFSIZE];
1432 PrefsAccount *mailac = NULL, *newsac = NULL;
1433 gboolean save_clear_text = TRUE;
1434 gchar *tmp_enc_file = NULL;
1438 g_return_val_if_fail(file != NULL, -1);
1440 if ((fp = g_fopen(file, "rb")) == NULL) {
1441 FILE_OP_ERROR(file, "fopen");
1443 if (*errstr) g_free(*errstr);
1444 *errstr = g_strdup_printf(_("Couldn't open file %s."), file);
1449 while ((hnum = procheader_get_one_field(buf, sizeof(buf), fp, qentry))
1451 gchar *p = buf + strlen(qentry[hnum].name);
1459 if (smtpserver == NULL)
1460 smtpserver = g_strdup(p);
1463 to_list = address_list_append(to_list, p);
1466 newsgroup_list = newsgroup_list_append(newsgroup_list, p);
1468 case Q_MAIL_ACCOUNT_ID:
1469 mailac = account_find_from_id(atoi(p));
1471 case Q_NEWS_ACCOUNT_ID:
1472 newsac = account_find_from_id(atoi(p));
1474 case Q_SAVE_COPY_FOLDER:
1475 if (savecopyfolder == NULL)
1476 savecopyfolder = g_strdup(p);
1478 case Q_REPLY_MESSAGE_ID:
1479 if (replymessageid == NULL)
1480 replymessageid = g_strdup(p);
1482 case Q_FWD_MESSAGE_ID:
1483 if (fwdmessageid == NULL)
1484 fwdmessageid = g_strdup(p);
1486 case Q_PRIVACY_SYSTEM:
1487 case Q_PRIVACY_SYSTEM_OLD:
1488 if (privacy_system == NULL)
1489 privacy_system = g_strdup(p);
1496 case Q_ENCRYPT_DATA:
1497 case Q_ENCRYPT_DATA_OLD:
1498 if (encrypt_data == NULL)
1499 encrypt_data = g_strdup(p);
1502 case Q_CLAWS_HDRS_OLD:
1503 /* end of special headers reached */
1504 goto send_mail; /* can't "break;break;" */
1508 filepos = ftell(fp);
1513 if (mailac && mailac->save_encrypted_as_clear_text
1514 && !mailac->encrypt_to_self)
1515 save_clear_text = TRUE;
1517 save_clear_text = FALSE;
1522 mimeinfo = procmime_scan_queue_file(file);
1523 if (!privacy_encrypt(privacy_system, mimeinfo, encrypt_data)
1524 || (fp = my_tmpfile()) == NULL
1525 || procmime_write_mimeinfo(mimeinfo, fp) < 0) {
1528 procmime_mimeinfo_free_all(mimeinfo);
1531 slist_free_strings(to_list);
1532 g_slist_free(to_list);
1533 slist_free_strings(newsgroup_list);
1534 g_slist_free(newsgroup_list);
1535 g_free(savecopyfolder);
1536 g_free(replymessageid);
1537 g_free(fwdmessageid);
1538 g_free(privacy_system);
1539 g_free(encrypt_data);
1541 if (*errstr) g_free(*errstr);
1542 *errstr = g_strdup_printf(_("Couldn't encrypt the email: %s"),
1543 privacy_get_error());
1549 if (!save_clear_text) {
1550 gchar *content = NULL;
1551 FILE *tmpfp = get_tmpfile_in_dir(get_mime_tmp_dir(), &tmp_enc_file);
1555 content = file_read_stream_to_str(fp);
1558 str_write_to_file(content, tmp_enc_file);
1561 g_warning("couldn't get tempfile\n");
1565 procmime_mimeinfo_free_all(mimeinfo);
1571 debug_print("Sending message by mail\n");
1574 if (*errstr) g_free(*errstr);
1575 *errstr = g_strdup_printf(_("Queued message header is broken."));
1578 } else if (mailac && mailac->use_mail_command &&
1579 mailac->mail_command && (* mailac->mail_command)) {
1580 mailval = send_message_local(mailac->mail_command, fp);
1584 mailac = account_find_from_smtp_server(from, smtpserver);
1586 g_warning("Account not found. "
1587 "Using current account...\n");
1588 mailac = cur_account;
1593 mailval = send_message_smtp_full(mailac, to_list, fp, keep_session);
1594 if (mailval == -1 && errstr) {
1595 if (*errstr) g_free(*errstr);
1596 *errstr = g_strdup_printf(_("An error happened during SMTP session."));
1599 PrefsAccount tmp_ac;
1601 g_warning("Account not found.\n");
1603 memset(&tmp_ac, 0, sizeof(PrefsAccount));
1604 tmp_ac.address = from;
1605 tmp_ac.smtp_server = smtpserver;
1606 tmp_ac.smtpport = SMTP_PORT;
1607 mailval = send_message_smtp(&tmp_ac, to_list, fp);
1608 if (mailval == -1 && errstr) {
1609 if (*errstr) g_free(*errstr);
1610 *errstr = g_strdup_printf(_("No specific account has been found to "
1611 "send, and an error happened during SMTP session."));
1615 } else if (!to_list && !newsgroup_list) {
1617 if (*errstr) g_free(*errstr);
1618 *errstr = g_strdup(_("Couldn't determine sending informations. "
1619 "Maybe the email hasn't been generated by Claws Mail."));
1624 fseek(fp, filepos, SEEK_SET);
1625 if (newsgroup_list && (mailval == 0)) {
1630 /* write to temporary file */
1631 tmp = g_strdup_printf("%s%ctmp%p", g_get_tmp_dir(),
1632 G_DIR_SEPARATOR, file);
1633 if ((tmpfp = g_fopen(tmp, "wb")) == NULL) {
1634 FILE_OP_ERROR(tmp, "fopen");
1636 alertpanel_error(_("Couldn't create temporary file for news sending."));
1638 if (change_file_mode_rw(tmpfp, tmp) < 0) {
1639 FILE_OP_ERROR(tmp, "chmod");
1640 g_warning("can't change file mode\n");
1643 while ((newsval == 0) && fgets(buf, sizeof(buf), fp) != NULL) {
1644 if (fputs(buf, tmpfp) == EOF) {
1645 FILE_OP_ERROR(tmp, "fputs");
1648 if (*errstr) g_free(*errstr);
1649 *errstr = g_strdup_printf(_("Error when writing temporary file for news sending."));
1656 debug_print("Sending message by news\n");
1658 folder = FOLDER(newsac->folder);
1660 newsval = news_post(folder, tmp);
1661 if (newsval < 0 && errstr) {
1662 if (*errstr) g_free(*errstr);
1663 *errstr = g_strdup_printf(_("Error occurred while posting the message to %s."),
1664 newsac->nntp_server);
1674 /* save message to outbox */
1675 if (mailval == 0 && newsval == 0 && savecopyfolder) {
1678 debug_print("saving sent message...\n");
1680 outbox = folder_find_item_from_identifier(savecopyfolder);
1682 outbox = folder_get_default_outbox();
1684 if (save_clear_text || tmp_enc_file == NULL) {
1685 gboolean saved = FALSE;
1686 *queued_removed = FALSE;
1687 if (queue && msgnum > 0) {
1688 MsgInfo *queued_mail = folder_item_get_msginfo(queue, msgnum);
1689 if (folder_item_move_msg(outbox, queued_mail) >= 0) {
1690 debug_print("moved queued mail %d to sent folder\n", msgnum);
1692 *queued_removed = TRUE;
1693 } else if (folder_item_copy_msg(outbox, queued_mail) >= 0) {
1694 debug_print("copied queued mail %d to sent folder\n", msgnum);
1697 procmsg_msginfo_free(queued_mail);
1700 debug_print("resaving clear text queued mail to sent folder\n");
1701 procmsg_save_to_outbox(outbox, file, TRUE);
1704 debug_print("saving encrpyted queued mail to sent folder\n");
1705 procmsg_save_to_outbox(outbox, tmp_enc_file, FALSE);
1709 if (tmp_enc_file != NULL) {
1710 g_unlink(tmp_enc_file);
1712 tmp_enc_file = NULL;
1715 if (replymessageid != NULL || fwdmessageid != NULL) {
1719 if (replymessageid != NULL)
1720 tokens = g_strsplit(replymessageid, "\t", 0);
1722 tokens = g_strsplit(fwdmessageid, "\t", 0);
1723 item = folder_find_item_from_identifier(tokens[0]);
1725 /* check if queued message has valid folder and message id */
1726 if (item != NULL && tokens[2] != NULL) {
1729 msginfo = folder_item_get_msginfo(item, atoi(tokens[1]));
1731 /* check if referring message exists and has a message id */
1732 if ((msginfo != NULL) &&
1733 (msginfo->msgid != NULL) &&
1734 (strcmp(msginfo->msgid, tokens[2]) != 0)) {
1735 procmsg_msginfo_free(msginfo);
1739 if (msginfo == NULL) {
1740 msginfo = folder_item_get_msginfo_by_msgid(item, tokens[2]);
1743 if (msginfo != NULL) {
1744 if (replymessageid != NULL) {
1745 procmsg_msginfo_unset_flags(msginfo, MSG_FORWARDED, 0);
1746 procmsg_msginfo_set_flags(msginfo, MSG_REPLIED, 0);
1748 procmsg_msginfo_unset_flags(msginfo, MSG_REPLIED, 0);
1749 procmsg_msginfo_set_flags(msginfo, MSG_FORWARDED, 0);
1751 procmsg_msginfo_free(msginfo);
1759 slist_free_strings(to_list);
1760 g_slist_free(to_list);
1761 slist_free_strings(newsgroup_list);
1762 g_slist_free(newsgroup_list);
1763 g_free(savecopyfolder);
1764 g_free(replymessageid);
1765 g_free(fwdmessageid);
1766 g_free(privacy_system);
1767 g_free(encrypt_data);
1769 return (newsval != 0 ? newsval : mailval);
1772 gint procmsg_send_message_queue(const gchar *file, gchar **errstr, FolderItem *queue, gint msgnum, gboolean *queued_removed)
1774 gint result = procmsg_send_message_queue_full(file, FALSE, errstr, queue, msgnum, queued_removed);
1775 toolbar_main_set_sensitive(mainwindow_get_mainwindow());
1779 static void update_folder_msg_counts(FolderItem *item, MsgInfo *msginfo, MsgPermFlags old_flags)
1781 MsgPermFlags new_flags = msginfo->flags.perm_flags;
1784 if (!(old_flags & MSG_NEW) && (new_flags & MSG_NEW)) {
1788 if ((old_flags & MSG_NEW) && !(new_flags & MSG_NEW)) {
1793 if (!(old_flags & MSG_UNREAD) && (new_flags & MSG_UNREAD)) {
1794 item->unread_msgs++;
1795 if (procmsg_msg_has_marked_parent(msginfo))
1796 item->unreadmarked_msgs++;
1799 if ((old_flags & MSG_UNREAD) && !(new_flags & MSG_UNREAD)) {
1800 item->unread_msgs--;
1801 if (procmsg_msg_has_marked_parent(msginfo))
1802 item->unreadmarked_msgs--;
1806 if (!(old_flags & MSG_MARKED) && (new_flags & MSG_MARKED)) {
1807 procmsg_update_unread_children(msginfo, TRUE);
1808 item->marked_msgs++;
1811 if ((old_flags & MSG_MARKED) && !(new_flags & MSG_MARKED)) {
1812 procmsg_update_unread_children(msginfo, FALSE);
1813 item->marked_msgs--;
1817 void procmsg_msginfo_set_flags(MsgInfo *msginfo, MsgPermFlags perm_flags, MsgTmpFlags tmp_flags)
1820 MsgInfoUpdate msginfo_update;
1821 MsgPermFlags perm_flags_new, perm_flags_old;
1822 MsgTmpFlags tmp_flags_old;
1824 g_return_if_fail(msginfo != NULL);
1825 item = msginfo->folder;
1826 g_return_if_fail(item != NULL);
1828 debug_print("Setting flags for message %d in folder %s\n", msginfo->msgnum, item->path);
1830 /* Perm Flags handling */
1831 perm_flags_old = msginfo->flags.perm_flags;
1832 perm_flags_new = msginfo->flags.perm_flags | perm_flags;
1833 if ((perm_flags & MSG_IGNORE_THREAD) || (perm_flags_old & MSG_IGNORE_THREAD)) {
1834 perm_flags_new &= ~(MSG_NEW | MSG_UNREAD);
1837 if (perm_flags_old != perm_flags_new) {
1838 folder_item_change_msg_flags(msginfo->folder, msginfo, perm_flags_new);
1840 update_folder_msg_counts(item, msginfo, perm_flags_old);
1844 /* Tmp flags handling */
1845 tmp_flags_old = msginfo->flags.tmp_flags;
1846 msginfo->flags.tmp_flags |= tmp_flags;
1848 /* update notification */
1849 if ((perm_flags_old != perm_flags_new) || (tmp_flags_old != msginfo->flags.tmp_flags)) {
1850 msginfo_update.msginfo = msginfo;
1851 msginfo_update.flags = MSGINFO_UPDATE_FLAGS;
1852 hooks_invoke(MSGINFO_UPDATE_HOOKLIST, &msginfo_update);
1853 folder_item_update(msginfo->folder, F_ITEM_UPDATE_MSGCNT);
1857 void procmsg_msginfo_unset_flags(MsgInfo *msginfo, MsgPermFlags perm_flags, MsgTmpFlags tmp_flags)
1860 MsgInfoUpdate msginfo_update;
1861 MsgPermFlags perm_flags_new, perm_flags_old;
1862 MsgTmpFlags tmp_flags_old;
1864 g_return_if_fail(msginfo != NULL);
1865 item = msginfo->folder;
1866 g_return_if_fail(item != NULL);
1868 debug_print("Unsetting flags for message %d in folder %s\n", msginfo->msgnum, item->path);
1870 /* Perm Flags handling */
1871 perm_flags_old = msginfo->flags.perm_flags;
1872 perm_flags_new = msginfo->flags.perm_flags & ~perm_flags;
1874 if (perm_flags_old != perm_flags_new) {
1875 folder_item_change_msg_flags(msginfo->folder, msginfo, perm_flags_new);
1877 update_folder_msg_counts(item, msginfo, perm_flags_old);
1880 /* Tmp flags hanlding */
1881 tmp_flags_old = msginfo->flags.tmp_flags;
1882 msginfo->flags.tmp_flags &= ~tmp_flags;
1884 /* update notification */
1885 if ((perm_flags_old != perm_flags_new) || (tmp_flags_old != msginfo->flags.tmp_flags)) {
1886 msginfo_update.msginfo = msginfo;
1887 msginfo_update.flags = MSGINFO_UPDATE_FLAGS;
1888 hooks_invoke(MSGINFO_UPDATE_HOOKLIST, &msginfo_update);
1889 folder_item_update(msginfo->folder, F_ITEM_UPDATE_MSGCNT);
1893 void procmsg_msginfo_change_flags(MsgInfo *msginfo,
1894 MsgPermFlags add_perm_flags, MsgTmpFlags add_tmp_flags,
1895 MsgPermFlags rem_perm_flags, MsgTmpFlags rem_tmp_flags)
1898 MsgInfoUpdate msginfo_update;
1899 MsgPermFlags perm_flags_new, perm_flags_old;
1900 MsgTmpFlags tmp_flags_old;
1902 g_return_if_fail(msginfo != NULL);
1903 item = msginfo->folder;
1904 g_return_if_fail(item != NULL);
1906 debug_print("Changing flags for message %d in folder %s\n", msginfo->msgnum, item->path);
1908 /* Perm Flags handling */
1909 perm_flags_old = msginfo->flags.perm_flags;
1910 perm_flags_new = (msginfo->flags.perm_flags & ~rem_perm_flags) | add_perm_flags;
1911 if ((add_perm_flags & MSG_IGNORE_THREAD) || (perm_flags_old & MSG_IGNORE_THREAD)) {
1912 perm_flags_new &= ~(MSG_NEW | MSG_UNREAD);
1915 if (perm_flags_old != perm_flags_new) {
1916 folder_item_change_msg_flags(msginfo->folder, msginfo, perm_flags_new);
1918 update_folder_msg_counts(item, msginfo, perm_flags_old);
1922 /* Tmp flags handling */
1923 tmp_flags_old = msginfo->flags.tmp_flags;
1924 msginfo->flags.tmp_flags &= ~rem_tmp_flags;
1925 msginfo->flags.tmp_flags |= add_tmp_flags;
1927 /* update notification */
1928 if ((perm_flags_old != perm_flags_new) || (tmp_flags_old != msginfo->flags.tmp_flags)) {
1929 msginfo_update.msginfo = msginfo;
1930 msginfo_update.flags = MSGINFO_UPDATE_FLAGS;
1931 hooks_invoke(MSGINFO_UPDATE_HOOKLIST, &msginfo_update);
1932 folder_item_update(msginfo->folder, F_ITEM_UPDATE_MSGCNT);
1937 *\brief check for flags (e.g. mark) in prior msgs of current thread
1939 *\param info Current message
1940 *\param perm_flags Flags to be checked
1941 *\param parentmsgs Hash of prior msgs to avoid loops
1943 *\return gboolean TRUE if perm_flags are found
1945 static gboolean procmsg_msg_has_flagged_parent_real(MsgInfo *info,
1946 MsgPermFlags perm_flags, GHashTable *parentmsgs)
1950 g_return_val_if_fail(info != NULL, FALSE);
1952 if (info != NULL && info->folder != NULL && info->inreplyto != NULL) {
1953 tmp = folder_item_get_msginfo_by_msgid(info->folder,
1955 if (tmp && (tmp->flags.perm_flags & perm_flags)) {
1956 procmsg_msginfo_free(tmp);
1958 } else if (tmp != NULL) {
1961 if (g_hash_table_lookup(parentmsgs, info)) {
1962 debug_print("loop detected: %d\n",
1966 g_hash_table_insert(parentmsgs, info, "1");
1967 result = procmsg_msg_has_flagged_parent_real(
1968 tmp, perm_flags, parentmsgs);
1970 procmsg_msginfo_free(tmp);
1980 *\brief Callback for cleaning up hash of parentmsgs
1982 static gboolean parentmsgs_hash_remove(gpointer key,
1990 *\brief Set up list of parentmsgs
1991 * See procmsg_msg_has_flagged_parent_real()
1993 gboolean procmsg_msg_has_flagged_parent(MsgInfo *info, MsgPermFlags perm_flags)
1996 static GHashTable *parentmsgs = NULL;
1998 if (parentmsgs == NULL)
1999 parentmsgs = g_hash_table_new(NULL, NULL);
2001 result = procmsg_msg_has_flagged_parent_real(info, perm_flags, parentmsgs);
2002 g_hash_table_foreach_remove(parentmsgs, parentmsgs_hash_remove, NULL);
2008 *\brief Check if msgs prior in thread are marked
2009 * See procmsg_msg_has_flagged_parent_real()
2011 gboolean procmsg_msg_has_marked_parent(MsgInfo *info)
2013 return procmsg_msg_has_flagged_parent(info, MSG_MARKED);
2017 static GSList *procmsg_find_children_func(MsgInfo *info,
2018 GSList *children, GSList *all)
2022 g_return_val_if_fail(info!=NULL, children);
2023 if (info->msgid == NULL)
2026 for (cur = all; cur != NULL; cur = g_slist_next(cur)) {
2027 MsgInfo *tmp = (MsgInfo *)cur->data;
2028 if (tmp->inreplyto && !strcmp(tmp->inreplyto, info->msgid)) {
2029 /* Check if message is already in the list */
2030 if ((children == NULL) ||
2031 (g_slist_index(children, tmp) == -1)) {
2032 children = g_slist_prepend(children,
2033 procmsg_msginfo_new_ref(tmp));
2034 children = procmsg_find_children_func(tmp,
2043 static GSList *procmsg_find_children (MsgInfo *info)
2048 g_return_val_if_fail(info!=NULL, NULL);
2049 all = folder_item_get_msg_list(info->folder);
2050 children = procmsg_find_children_func(info, NULL, all);
2051 if (children != NULL) {
2052 for (cur = all; cur != NULL; cur = g_slist_next(cur)) {
2053 /* this will not free the used pointers
2054 created with procmsg_msginfo_new_ref */
2055 procmsg_msginfo_free((MsgInfo *)cur->data);
2063 static void procmsg_update_unread_children(MsgInfo *info, gboolean newly_marked)
2065 GSList *children = procmsg_find_children(info);
2067 for (cur = children; cur != NULL; cur = g_slist_next(cur)) {
2068 MsgInfo *tmp = (MsgInfo *)cur->data;
2069 if(MSG_IS_UNREAD(tmp->flags) && !MSG_IS_IGNORE_THREAD(tmp->flags)) {
2071 info->folder->unreadmarked_msgs++;
2073 info->folder->unreadmarked_msgs--;
2074 folder_item_update(info->folder, F_ITEM_UPDATE_MSGCNT);
2076 procmsg_msginfo_free(tmp);
2078 g_slist_free(children);
2082 * Set the destination folder for a copy or move operation
2084 * \param msginfo The message which's destination folder is changed
2085 * \param to_folder The destination folder for the operation
2087 void procmsg_msginfo_set_to_folder(MsgInfo *msginfo, FolderItem *to_folder)
2089 if(msginfo->to_folder != NULL) {
2090 msginfo->to_folder->op_count--;
2091 folder_item_update(msginfo->to_folder, F_ITEM_UPDATE_MSGCNT);
2093 msginfo->to_folder = to_folder;
2094 if(to_folder != NULL) {
2095 to_folder->op_count++;
2096 folder_item_update(msginfo->to_folder, F_ITEM_UPDATE_MSGCNT);
2101 * Apply filtering actions to the msginfo
2103 * \param msginfo The MsgInfo describing the message that should be filtered
2104 * \return TRUE if the message was moved and MsgInfo is now invalid,
2107 static gboolean procmsg_msginfo_filter(MsgInfo *msginfo, PrefsAccount* ac_prefs)
2109 MailFilteringData mail_filtering_data;
2111 mail_filtering_data.msginfo = msginfo;
2112 mail_filtering_data.msglist = NULL;
2113 mail_filtering_data.filtered = NULL;
2114 mail_filtering_data.unfiltered = NULL;
2115 if (hooks_invoke(MAIL_FILTERING_HOOKLIST, &mail_filtering_data)) {
2119 /* filter if enabled in prefs or move to inbox if not */
2120 if((filtering_rules != NULL) &&
2121 filter_message_by_msginfo(filtering_rules, msginfo, ac_prefs)) {
2128 void procmsg_msglist_filter(GSList *list, PrefsAccount *ac,
2129 GSList **filtered, GSList **unfiltered,
2132 GSList *cur, *to_do = NULL;
2133 gint total = 0, curnum = 0;
2134 MailFilteringData mail_filtering_data;
2136 g_return_if_fail(filtered != NULL);
2137 g_return_if_fail(unfiltered != NULL);
2145 total = g_slist_length(list);
2149 *unfiltered = g_slist_copy(list);
2153 statusbar_print_all(_("Filtering messages...\n"));
2155 mail_filtering_data.msginfo = NULL;
2156 mail_filtering_data.msglist = list;
2157 mail_filtering_data.filtered = NULL;
2158 mail_filtering_data.unfiltered = NULL;
2160 hooks_invoke(MAIL_LISTFILTERING_HOOKLIST, &mail_filtering_data);
2162 if (mail_filtering_data.filtered == NULL &&
2163 mail_filtering_data.unfiltered == NULL) {
2164 /* nothing happened */
2165 debug_print(MAIL_LISTFILTERING_HOOKLIST " did nothing. filtering whole list normally.\n");
2168 if (mail_filtering_data.filtered != NULL) {
2169 /* keep track of what's been filtered by the hooks */
2170 debug_print(MAIL_LISTFILTERING_HOOKLIST " filtered some stuff. total %d filtered %d unfilt %d.\n",
2171 g_slist_length(list),
2172 g_slist_length(mail_filtering_data.filtered),
2173 g_slist_length(mail_filtering_data.unfiltered));
2175 *filtered = g_slist_copy(mail_filtering_data.filtered);
2177 if (mail_filtering_data.unfiltered != NULL) {
2178 /* what the hooks didn't handle will go in filtered or
2179 * unfiltered in the next loop */
2180 debug_print(MAIL_LISTFILTERING_HOOKLIST " left unfiltered stuff. total %d filtered %d unfilt %d.\n",
2181 g_slist_length(list),
2182 g_slist_length(mail_filtering_data.filtered),
2183 g_slist_length(mail_filtering_data.unfiltered));
2184 to_do = mail_filtering_data.unfiltered;
2187 for (cur = to_do; cur; cur = cur->next) {
2188 MsgInfo *info = (MsgInfo *)cur->data;
2189 if (procmsg_msginfo_filter(info, ac))
2190 *filtered = g_slist_prepend(*filtered, info);
2192 *unfiltered = g_slist_prepend(*unfiltered, info);
2193 statusbar_progress_all(curnum++, total, prefs_common.statusbar_update_step);
2196 g_slist_free(mail_filtering_data.filtered);
2197 g_slist_free(mail_filtering_data.unfiltered);
2199 *filtered = g_slist_reverse(*filtered);
2200 *unfiltered = g_slist_reverse(*unfiltered);
2202 statusbar_progress_all(0,0,0);
2203 statusbar_pop_all();
2206 MsgInfo *procmsg_msginfo_new_from_mimeinfo(MsgInfo *src_msginfo, MimeInfo *mimeinfo)
2208 MsgInfo *tmp_msginfo = NULL;
2209 MsgFlags flags = {0, 0};
2210 gchar *tmpfile = get_tmp_file();
2211 FILE *fp = g_fopen(tmpfile, "wb");
2213 if (!mimeinfo || mimeinfo->type != MIMETYPE_MESSAGE ||
2214 g_ascii_strcasecmp(mimeinfo->subtype, "rfc822")) {
2215 g_warning("procmsg_msginfo_new_from_mimeinfo(): unsuitable mimeinfo");
2222 if (fp && procmime_write_mimeinfo(mimeinfo, fp) >= 0) {
2225 tmp_msginfo = procheader_parse_file(
2232 if (tmp_msginfo != NULL) {
2234 tmp_msginfo->folder = src_msginfo->folder;
2235 tmp_msginfo->plaintext_file = g_strdup(tmpfile);
2237 g_warning("procmsg_msginfo_new_from_mimeinfo(): Can't generate new msginfo");
2245 static GSList *spam_learners = NULL;
2247 void procmsg_register_spam_learner (int (*learn_func)(MsgInfo *info, GSList *list, gboolean spam))
2249 if (!g_slist_find(spam_learners, learn_func))
2250 spam_learners = g_slist_append(spam_learners, learn_func);
2251 if (mainwindow_get_mainwindow()) {
2252 main_window_set_menu_sensitive(mainwindow_get_mainwindow());
2253 summary_set_menu_sensitive(
2254 mainwindow_get_mainwindow()->summaryview);
2255 toolbar_main_set_sensitive(mainwindow_get_mainwindow());
2259 void procmsg_unregister_spam_learner (int (*learn_func)(MsgInfo *info, GSList *list, gboolean spam))
2261 spam_learners = g_slist_remove(spam_learners, learn_func);
2262 if (mainwindow_get_mainwindow()) {
2263 main_window_set_menu_sensitive(mainwindow_get_mainwindow());
2264 summary_set_menu_sensitive(
2265 mainwindow_get_mainwindow()->summaryview);
2266 toolbar_main_set_sensitive(mainwindow_get_mainwindow());
2270 gboolean procmsg_spam_can_learn(void)
2272 return g_slist_length(spam_learners) > 0;
2275 int procmsg_spam_learner_learn (MsgInfo *info, GSList *list, gboolean spam)
2277 GSList *cur = spam_learners;
2279 for (; cur; cur = cur->next) {
2280 int ((*func)(MsgInfo *info, GSList *list, gboolean spam)) = cur->data;
2281 ret |= func(info, list, spam);
2286 static gchar *spam_folder_item = NULL;
2287 void procmsg_spam_set_folder (const char *item_identifier)
2289 g_free(spam_folder_item);
2290 if (item_identifier)
2291 spam_folder_item = g_strdup(item_identifier);
2293 spam_folder_item = NULL;
2296 FolderItem *procmsg_spam_get_folder (void)
2298 FolderItem *item = spam_folder_item ? folder_find_item_from_identifier(spam_folder_item) : NULL;
2299 return item ? item : folder_get_default_trash();
2302 static void item_has_queued_mails(FolderItem *item, gpointer data)
2304 gboolean *result = (gboolean *)data;
2305 if (*result == TRUE)
2307 if (folder_has_parent_of_type(item, F_QUEUE) && item->total_msgs > 0)
2311 gboolean procmsg_have_queued_mails_fast (void)
2313 gboolean result = FALSE;
2314 folder_func_to_all_folders(item_has_queued_mails, &result);