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 MsgPermFlags to_unset = 0;
1750 if (prefs_common.mark_as_read_on_new_window)
1751 to_unset = (MSG_NEW|MSG_UNREAD);
1753 if (replymessageid != NULL) {
1754 procmsg_msginfo_unset_flags(msginfo, to_unset|MSG_FORWARDED, 0);
1755 procmsg_msginfo_set_flags(msginfo, MSG_REPLIED, 0);
1757 procmsg_msginfo_unset_flags(msginfo, MSG_REPLIED, 0);
1758 procmsg_msginfo_set_flags(msginfo, MSG_FORWARDED, 0);
1760 procmsg_msginfo_free(msginfo);
1768 slist_free_strings(to_list);
1769 g_slist_free(to_list);
1770 slist_free_strings(newsgroup_list);
1771 g_slist_free(newsgroup_list);
1772 g_free(savecopyfolder);
1773 g_free(replymessageid);
1774 g_free(fwdmessageid);
1775 g_free(privacy_system);
1776 g_free(encrypt_data);
1778 return (newsval != 0 ? newsval : mailval);
1781 gint procmsg_send_message_queue(const gchar *file, gchar **errstr, FolderItem *queue, gint msgnum, gboolean *queued_removed)
1783 gint result = procmsg_send_message_queue_full(file, FALSE, errstr, queue, msgnum, queued_removed);
1784 toolbar_main_set_sensitive(mainwindow_get_mainwindow());
1788 static void update_folder_msg_counts(FolderItem *item, MsgInfo *msginfo, MsgPermFlags old_flags)
1790 MsgPermFlags new_flags = msginfo->flags.perm_flags;
1793 if (!(old_flags & MSG_NEW) && (new_flags & MSG_NEW)) {
1797 if ((old_flags & MSG_NEW) && !(new_flags & MSG_NEW)) {
1802 if (!(old_flags & MSG_UNREAD) && (new_flags & MSG_UNREAD)) {
1803 item->unread_msgs++;
1804 if (procmsg_msg_has_marked_parent(msginfo))
1805 item->unreadmarked_msgs++;
1808 if ((old_flags & MSG_UNREAD) && !(new_flags & MSG_UNREAD)) {
1809 item->unread_msgs--;
1810 if (procmsg_msg_has_marked_parent(msginfo))
1811 item->unreadmarked_msgs--;
1815 if (!(old_flags & MSG_MARKED) && (new_flags & MSG_MARKED)) {
1816 procmsg_update_unread_children(msginfo, TRUE);
1817 item->marked_msgs++;
1820 if ((old_flags & MSG_MARKED) && !(new_flags & MSG_MARKED)) {
1821 procmsg_update_unread_children(msginfo, FALSE);
1822 item->marked_msgs--;
1826 void procmsg_msginfo_set_flags(MsgInfo *msginfo, MsgPermFlags perm_flags, MsgTmpFlags tmp_flags)
1829 MsgInfoUpdate msginfo_update;
1830 MsgPermFlags perm_flags_new, perm_flags_old;
1831 MsgTmpFlags tmp_flags_old;
1833 g_return_if_fail(msginfo != NULL);
1834 item = msginfo->folder;
1835 g_return_if_fail(item != NULL);
1837 debug_print("Setting flags for message %d in folder %s\n", msginfo->msgnum, item->path);
1839 /* Perm Flags handling */
1840 perm_flags_old = msginfo->flags.perm_flags;
1841 perm_flags_new = msginfo->flags.perm_flags | perm_flags;
1842 if ((perm_flags & MSG_IGNORE_THREAD) || (perm_flags_old & MSG_IGNORE_THREAD)) {
1843 perm_flags_new &= ~(MSG_NEW | MSG_UNREAD);
1846 if (perm_flags_old != perm_flags_new) {
1847 folder_item_change_msg_flags(msginfo->folder, msginfo, perm_flags_new);
1849 update_folder_msg_counts(item, msginfo, perm_flags_old);
1853 /* Tmp flags handling */
1854 tmp_flags_old = msginfo->flags.tmp_flags;
1855 msginfo->flags.tmp_flags |= tmp_flags;
1857 /* update notification */
1858 if ((perm_flags_old != perm_flags_new) || (tmp_flags_old != msginfo->flags.tmp_flags)) {
1859 msginfo_update.msginfo = msginfo;
1860 msginfo_update.flags = MSGINFO_UPDATE_FLAGS;
1861 hooks_invoke(MSGINFO_UPDATE_HOOKLIST, &msginfo_update);
1862 folder_item_update(msginfo->folder, F_ITEM_UPDATE_MSGCNT);
1866 void procmsg_msginfo_unset_flags(MsgInfo *msginfo, MsgPermFlags perm_flags, MsgTmpFlags tmp_flags)
1869 MsgInfoUpdate msginfo_update;
1870 MsgPermFlags perm_flags_new, perm_flags_old;
1871 MsgTmpFlags tmp_flags_old;
1873 g_return_if_fail(msginfo != NULL);
1874 item = msginfo->folder;
1875 g_return_if_fail(item != NULL);
1877 debug_print("Unsetting flags for message %d in folder %s\n", msginfo->msgnum, item->path);
1879 /* Perm Flags handling */
1880 perm_flags_old = msginfo->flags.perm_flags;
1881 perm_flags_new = msginfo->flags.perm_flags & ~perm_flags;
1883 if (perm_flags_old != perm_flags_new) {
1884 folder_item_change_msg_flags(msginfo->folder, msginfo, perm_flags_new);
1886 update_folder_msg_counts(item, msginfo, perm_flags_old);
1889 /* Tmp flags hanlding */
1890 tmp_flags_old = msginfo->flags.tmp_flags;
1891 msginfo->flags.tmp_flags &= ~tmp_flags;
1893 /* update notification */
1894 if ((perm_flags_old != perm_flags_new) || (tmp_flags_old != msginfo->flags.tmp_flags)) {
1895 msginfo_update.msginfo = msginfo;
1896 msginfo_update.flags = MSGINFO_UPDATE_FLAGS;
1897 hooks_invoke(MSGINFO_UPDATE_HOOKLIST, &msginfo_update);
1898 folder_item_update(msginfo->folder, F_ITEM_UPDATE_MSGCNT);
1902 void procmsg_msginfo_change_flags(MsgInfo *msginfo,
1903 MsgPermFlags add_perm_flags, MsgTmpFlags add_tmp_flags,
1904 MsgPermFlags rem_perm_flags, MsgTmpFlags rem_tmp_flags)
1907 MsgInfoUpdate msginfo_update;
1908 MsgPermFlags perm_flags_new, perm_flags_old;
1909 MsgTmpFlags tmp_flags_old;
1911 g_return_if_fail(msginfo != NULL);
1912 item = msginfo->folder;
1913 g_return_if_fail(item != NULL);
1915 debug_print("Changing flags for message %d in folder %s\n", msginfo->msgnum, item->path);
1917 /* Perm Flags handling */
1918 perm_flags_old = msginfo->flags.perm_flags;
1919 perm_flags_new = (msginfo->flags.perm_flags & ~rem_perm_flags) | add_perm_flags;
1920 if ((add_perm_flags & MSG_IGNORE_THREAD) || (perm_flags_old & MSG_IGNORE_THREAD)) {
1921 perm_flags_new &= ~(MSG_NEW | MSG_UNREAD);
1924 if (perm_flags_old != perm_flags_new) {
1925 folder_item_change_msg_flags(msginfo->folder, msginfo, perm_flags_new);
1927 update_folder_msg_counts(item, msginfo, perm_flags_old);
1931 /* Tmp flags handling */
1932 tmp_flags_old = msginfo->flags.tmp_flags;
1933 msginfo->flags.tmp_flags &= ~rem_tmp_flags;
1934 msginfo->flags.tmp_flags |= add_tmp_flags;
1936 /* update notification */
1937 if ((perm_flags_old != perm_flags_new) || (tmp_flags_old != msginfo->flags.tmp_flags)) {
1938 msginfo_update.msginfo = msginfo;
1939 msginfo_update.flags = MSGINFO_UPDATE_FLAGS;
1940 hooks_invoke(MSGINFO_UPDATE_HOOKLIST, &msginfo_update);
1941 folder_item_update(msginfo->folder, F_ITEM_UPDATE_MSGCNT);
1946 *\brief check for flags (e.g. mark) in prior msgs of current thread
1948 *\param info Current message
1949 *\param perm_flags Flags to be checked
1950 *\param parentmsgs Hash of prior msgs to avoid loops
1952 *\return gboolean TRUE if perm_flags are found
1954 static gboolean procmsg_msg_has_flagged_parent_real(MsgInfo *info,
1955 MsgPermFlags perm_flags, GHashTable *parentmsgs)
1959 g_return_val_if_fail(info != NULL, FALSE);
1961 if (info != NULL && info->folder != NULL && info->inreplyto != NULL) {
1962 tmp = folder_item_get_msginfo_by_msgid(info->folder,
1964 if (tmp && (tmp->flags.perm_flags & perm_flags)) {
1965 procmsg_msginfo_free(tmp);
1967 } else if (tmp != NULL) {
1970 if (g_hash_table_lookup(parentmsgs, info)) {
1971 debug_print("loop detected: %d\n",
1975 g_hash_table_insert(parentmsgs, info, "1");
1976 result = procmsg_msg_has_flagged_parent_real(
1977 tmp, perm_flags, parentmsgs);
1979 procmsg_msginfo_free(tmp);
1989 *\brief Callback for cleaning up hash of parentmsgs
1991 static gboolean parentmsgs_hash_remove(gpointer key,
1999 *\brief Set up list of parentmsgs
2000 * See procmsg_msg_has_flagged_parent_real()
2002 gboolean procmsg_msg_has_flagged_parent(MsgInfo *info, MsgPermFlags perm_flags)
2005 static GHashTable *parentmsgs = NULL;
2007 if (parentmsgs == NULL)
2008 parentmsgs = g_hash_table_new(NULL, NULL);
2010 result = procmsg_msg_has_flagged_parent_real(info, perm_flags, parentmsgs);
2011 g_hash_table_foreach_remove(parentmsgs, parentmsgs_hash_remove, NULL);
2017 *\brief Check if msgs prior in thread are marked
2018 * See procmsg_msg_has_flagged_parent_real()
2020 gboolean procmsg_msg_has_marked_parent(MsgInfo *info)
2022 return procmsg_msg_has_flagged_parent(info, MSG_MARKED);
2026 static GSList *procmsg_find_children_func(MsgInfo *info,
2027 GSList *children, GSList *all)
2031 g_return_val_if_fail(info!=NULL, children);
2032 if (info->msgid == NULL)
2035 for (cur = all; cur != NULL; cur = g_slist_next(cur)) {
2036 MsgInfo *tmp = (MsgInfo *)cur->data;
2037 if (tmp->inreplyto && !strcmp(tmp->inreplyto, info->msgid)) {
2038 /* Check if message is already in the list */
2039 if ((children == NULL) ||
2040 (g_slist_index(children, tmp) == -1)) {
2041 children = g_slist_prepend(children,
2042 procmsg_msginfo_new_ref(tmp));
2043 children = procmsg_find_children_func(tmp,
2052 static GSList *procmsg_find_children (MsgInfo *info)
2057 g_return_val_if_fail(info!=NULL, NULL);
2058 all = folder_item_get_msg_list(info->folder);
2059 children = procmsg_find_children_func(info, NULL, all);
2060 if (children != NULL) {
2061 for (cur = all; cur != NULL; cur = g_slist_next(cur)) {
2062 /* this will not free the used pointers
2063 created with procmsg_msginfo_new_ref */
2064 procmsg_msginfo_free((MsgInfo *)cur->data);
2072 static void procmsg_update_unread_children(MsgInfo *info, gboolean newly_marked)
2074 GSList *children = procmsg_find_children(info);
2076 for (cur = children; cur != NULL; cur = g_slist_next(cur)) {
2077 MsgInfo *tmp = (MsgInfo *)cur->data;
2078 if(MSG_IS_UNREAD(tmp->flags) && !MSG_IS_IGNORE_THREAD(tmp->flags)) {
2080 info->folder->unreadmarked_msgs++;
2082 info->folder->unreadmarked_msgs--;
2083 folder_item_update(info->folder, F_ITEM_UPDATE_MSGCNT);
2085 procmsg_msginfo_free(tmp);
2087 g_slist_free(children);
2091 * Set the destination folder for a copy or move operation
2093 * \param msginfo The message which's destination folder is changed
2094 * \param to_folder The destination folder for the operation
2096 void procmsg_msginfo_set_to_folder(MsgInfo *msginfo, FolderItem *to_folder)
2098 if(msginfo->to_folder != NULL) {
2099 msginfo->to_folder->op_count--;
2100 folder_item_update(msginfo->to_folder, F_ITEM_UPDATE_MSGCNT);
2102 msginfo->to_folder = to_folder;
2103 if(to_folder != NULL) {
2104 to_folder->op_count++;
2105 folder_item_update(msginfo->to_folder, F_ITEM_UPDATE_MSGCNT);
2110 * Apply filtering actions to the msginfo
2112 * \param msginfo The MsgInfo describing the message that should be filtered
2113 * \return TRUE if the message was moved and MsgInfo is now invalid,
2116 static gboolean procmsg_msginfo_filter(MsgInfo *msginfo, PrefsAccount* ac_prefs)
2118 MailFilteringData mail_filtering_data;
2120 mail_filtering_data.msginfo = msginfo;
2121 mail_filtering_data.msglist = NULL;
2122 mail_filtering_data.filtered = NULL;
2123 mail_filtering_data.unfiltered = NULL;
2124 mail_filtering_data.account = ac_prefs;
2126 if (!ac_prefs || ac_prefs->filterhook_on_recv)
2127 if (hooks_invoke(MAIL_FILTERING_HOOKLIST, &mail_filtering_data))
2130 /* filter if enabled in prefs or move to inbox if not */
2131 if((filtering_rules != NULL) &&
2132 filter_message_by_msginfo(filtering_rules, msginfo, ac_prefs,
2133 FILTERING_INCORPORATION, NULL)) {
2140 void procmsg_msglist_filter(GSList *list, PrefsAccount *ac,
2141 GSList **filtered, GSList **unfiltered,
2144 GSList *cur, *to_do = NULL;
2145 gint total = 0, curnum = 0;
2146 MailFilteringData mail_filtering_data;
2148 g_return_if_fail(filtered != NULL);
2149 g_return_if_fail(unfiltered != NULL);
2157 total = g_slist_length(list);
2161 *unfiltered = g_slist_copy(list);
2165 statusbar_print_all(_("Filtering messages...\n"));
2167 mail_filtering_data.msginfo = NULL;
2168 mail_filtering_data.msglist = list;
2169 mail_filtering_data.filtered = NULL;
2170 mail_filtering_data.unfiltered = NULL;
2171 mail_filtering_data.account = ac;
2173 if (!ac || ac->filterhook_on_recv)
2174 hooks_invoke(MAIL_LISTFILTERING_HOOKLIST, &mail_filtering_data);
2176 if (mail_filtering_data.filtered == NULL &&
2177 mail_filtering_data.unfiltered == NULL) {
2178 /* nothing happened */
2179 debug_print(MAIL_LISTFILTERING_HOOKLIST " did nothing. filtering whole list normally.\n");
2182 if (mail_filtering_data.filtered != NULL) {
2183 /* keep track of what's been filtered by the hooks */
2184 debug_print(MAIL_LISTFILTERING_HOOKLIST " filtered some stuff. total %d filtered %d unfilt %d.\n",
2185 g_slist_length(list),
2186 g_slist_length(mail_filtering_data.filtered),
2187 g_slist_length(mail_filtering_data.unfiltered));
2189 *filtered = g_slist_copy(mail_filtering_data.filtered);
2191 if (mail_filtering_data.unfiltered != NULL) {
2192 /* what the hooks didn't handle will go in filtered or
2193 * unfiltered in the next loop */
2194 debug_print(MAIL_LISTFILTERING_HOOKLIST " left unfiltered stuff. total %d filtered %d unfilt %d.\n",
2195 g_slist_length(list),
2196 g_slist_length(mail_filtering_data.filtered),
2197 g_slist_length(mail_filtering_data.unfiltered));
2198 to_do = mail_filtering_data.unfiltered;
2201 for (cur = to_do; cur; cur = cur->next) {
2202 MsgInfo *info = (MsgInfo *)cur->data;
2203 if (procmsg_msginfo_filter(info, ac))
2204 *filtered = g_slist_prepend(*filtered, info);
2206 *unfiltered = g_slist_prepend(*unfiltered, info);
2207 statusbar_progress_all(curnum++, total, prefs_common.statusbar_update_step);
2210 g_slist_free(mail_filtering_data.filtered);
2211 g_slist_free(mail_filtering_data.unfiltered);
2213 *filtered = g_slist_reverse(*filtered);
2214 *unfiltered = g_slist_reverse(*unfiltered);
2216 statusbar_progress_all(0,0,0);
2217 statusbar_pop_all();
2220 MsgInfo *procmsg_msginfo_new_from_mimeinfo(MsgInfo *src_msginfo, MimeInfo *mimeinfo)
2222 MsgInfo *tmp_msginfo = NULL;
2223 MsgFlags flags = {0, 0};
2224 gchar *tmpfile = get_tmp_file();
2225 FILE *fp = g_fopen(tmpfile, "wb");
2227 if (!mimeinfo || mimeinfo->type != MIMETYPE_MESSAGE ||
2228 g_ascii_strcasecmp(mimeinfo->subtype, "rfc822")) {
2229 g_warning("procmsg_msginfo_new_from_mimeinfo(): unsuitable mimeinfo");
2236 if (fp && procmime_write_mimeinfo(mimeinfo, fp) >= 0) {
2239 tmp_msginfo = procheader_parse_file(
2246 if (tmp_msginfo != NULL) {
2248 tmp_msginfo->folder = src_msginfo->folder;
2249 tmp_msginfo->plaintext_file = g_strdup(tmpfile);
2251 g_warning("procmsg_msginfo_new_from_mimeinfo(): Can't generate new msginfo");
2259 static GSList *spam_learners = NULL;
2261 void procmsg_register_spam_learner (int (*learn_func)(MsgInfo *info, GSList *list, gboolean spam))
2263 if (!g_slist_find(spam_learners, learn_func))
2264 spam_learners = g_slist_append(spam_learners, learn_func);
2265 if (mainwindow_get_mainwindow()) {
2266 main_window_set_menu_sensitive(mainwindow_get_mainwindow());
2267 summary_set_menu_sensitive(
2268 mainwindow_get_mainwindow()->summaryview);
2269 toolbar_main_set_sensitive(mainwindow_get_mainwindow());
2273 void procmsg_unregister_spam_learner (int (*learn_func)(MsgInfo *info, GSList *list, gboolean spam))
2275 spam_learners = g_slist_remove(spam_learners, learn_func);
2276 if (mainwindow_get_mainwindow()) {
2277 main_window_set_menu_sensitive(mainwindow_get_mainwindow());
2278 summary_set_menu_sensitive(
2279 mainwindow_get_mainwindow()->summaryview);
2280 toolbar_main_set_sensitive(mainwindow_get_mainwindow());
2284 gboolean procmsg_spam_can_learn(void)
2286 return g_slist_length(spam_learners) > 0;
2289 int procmsg_spam_learner_learn (MsgInfo *info, GSList *list, gboolean spam)
2291 GSList *cur = spam_learners;
2293 for (; cur; cur = cur->next) {
2294 int ((*func)(MsgInfo *info, GSList *list, gboolean spam)) = cur->data;
2295 ret |= func(info, list, spam);
2300 static gchar *spam_folder_item = NULL;
2301 static FolderItem * (*procmsg_spam_get_folder_func)(MsgInfo *msginfo) = NULL;
2302 void procmsg_spam_set_folder (const char *item_identifier, FolderItem *(*spam_get_folder_func)(MsgInfo *info))
2304 g_free(spam_folder_item);
2305 if (item_identifier)
2306 spam_folder_item = g_strdup(item_identifier);
2308 spam_folder_item = NULL;
2309 if (spam_get_folder_func != NULL)
2310 procmsg_spam_get_folder_func = spam_get_folder_func;
2312 procmsg_spam_get_folder_func = NULL;
2315 FolderItem *procmsg_spam_get_folder (MsgInfo *msginfo)
2317 FolderItem *item = NULL;
2319 if (procmsg_spam_get_folder_func)
2320 item = procmsg_spam_get_folder_func(msginfo);
2321 if (item == NULL && spam_folder_item)
2322 item = folder_find_item_from_identifier(spam_folder_item);
2324 item = folder_get_default_trash();
2328 static void item_has_queued_mails(FolderItem *item, gpointer data)
2330 gboolean *result = (gboolean *)data;
2331 if (*result == TRUE)
2333 if (folder_has_parent_of_type(item, F_QUEUE) && item->total_msgs > 0)
2337 gboolean procmsg_have_queued_mails_fast (void)
2339 gboolean result = FALSE;
2340 folder_func_to_all_folders(item_has_queued_mails, &result);
2344 static void item_has_trashed_mails(FolderItem *item, gpointer data)
2346 gboolean *result = (gboolean *)data;
2347 if (*result == TRUE)
2349 if (folder_has_parent_of_type(item, F_TRASH) && item->total_msgs > 0)
2353 gboolean procmsg_have_trashed_mails_fast (void)
2355 gboolean result = FALSE;
2356 folder_func_to_all_folders(item_has_trashed_mails, &result);