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 3 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, see <http://www.gnu.org/licenses/>.
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"
52 static gint procmsg_send_message_queue_full(const gchar *file, gboolean keep_session, gchar **errstr,
53 FolderItem *queue, gint msgnum, gboolean *queued_removed);
54 static void procmsg_update_unread_children (MsgInfo *info,
55 gboolean newly_marked);
62 Q_MAIL_ACCOUNT_ID = 4,
63 Q_NEWS_ACCOUNT_ID = 5,
64 Q_SAVE_COPY_FOLDER = 6,
65 Q_REPLY_MESSAGE_ID = 7,
71 Q_PRIVACY_SYSTEM_OLD = 13,
73 Q_ENCRYPT_DATA_OLD = 15,
74 Q_CLAWS_HDRS_OLD = 16,
77 void procmsg_msg_list_free(GSList *mlist)
82 for (cur = mlist; cur != NULL; cur = cur->next) {
83 msginfo = (MsgInfo *)cur->data;
84 procmsg_msginfo_free(msginfo);
98 /* CLAWS subject threading:
100 in the first round it inserts subject lines in a
101 relation (subject <-> node)
103 the second round finishes the threads by attaching
104 matching subject lines to the one found in the
105 relation. will use the oldest node with the same
106 subject that is not more then thread_by_subject_max_age
107 days old (see subject_relation_lookup)
110 static void subject_relation_insert(GRelation *relation, GNode *node)
115 g_return_if_fail(relation != NULL);
116 g_return_if_fail(node != NULL);
117 msginfo = (MsgInfo *) node->data;
118 g_return_if_fail(msginfo != NULL);
120 subject = msginfo->subject;
123 subject += subject_get_prefix_length(subject);
125 g_relation_insert(relation, subject, node);
128 static GNode *subject_relation_lookup(GRelation *relation, MsgInfo *msginfo)
135 g_return_val_if_fail(relation != NULL, NULL);
137 subject = msginfo->subject;
140 prefix_length = subject_get_prefix_length(subject);
141 if (prefix_length <= 0)
143 subject += prefix_length;
145 tuples = g_relation_select(relation, subject, 0);
149 if (tuples->len > 0) {
151 GNode *relation_node;
152 MsgInfo *relation_msginfo = NULL, *best_msginfo = NULL;
155 /* check all nodes with the same subject to find the best parent */
156 for (i = 0; i < tuples->len; i++) {
157 relation_node = (GNode *) g_tuples_index(tuples, i, 1);
158 relation_msginfo = (MsgInfo *) relation_node->data;
161 /* best node should be the oldest in the found nodes */
162 /* parent node must not be older then msginfo */
163 if ((relation_msginfo->date_t < msginfo->date_t) &&
164 ((best_msginfo == NULL) ||
165 (best_msginfo->date_t > relation_msginfo->date_t)))
168 /* parent node must not be more then thread_by_subject_max_age
169 days older then msginfo */
170 if (abs(difftime(msginfo->date_t, relation_msginfo->date_t)) >
171 prefs_common.thread_by_subject_max_age * 3600 * 24)
174 /* can add new tests for all matching
175 nodes found by subject */
178 node = relation_node;
179 best_msginfo = relation_msginfo;
184 g_tuples_destroy(tuples);
188 /* return the reversed thread tree */
189 GNode *procmsg_get_thread_tree(GSList *mlist)
191 GNode *root, *parent, *node, *next;
192 GHashTable *msgid_table;
193 GRelation *subject_relation = NULL;
198 root = g_node_new(NULL);
199 msgid_table = g_hash_table_new(g_str_hash, g_str_equal);
201 if (prefs_common.thread_by_subject) {
202 subject_relation = g_relation_new(2);
203 g_relation_index(subject_relation, 0, g_str_hash, g_str_equal);
206 for (; mlist != NULL; mlist = mlist->next) {
207 msginfo = (MsgInfo *)mlist->data;
210 if (msginfo->inreplyto) {
211 parent = g_hash_table_lookup(msgid_table, msginfo->inreplyto);
212 if (parent == NULL) {
216 node = g_node_insert_data_before
217 (parent, parent == root ? parent->children : NULL,
219 if ((msgid = msginfo->msgid) && g_hash_table_lookup(msgid_table, msgid) == NULL)
220 g_hash_table_insert(msgid_table, (gchar *)msgid, node);
222 /* CLAWS: add subject to relation (without prefix) */
223 if (prefs_common.thread_by_subject) {
224 subject_relation_insert(subject_relation, node);
228 /* complete the unfinished threads */
229 for (node = root->children; node != NULL; ) {
231 msginfo = (MsgInfo *)node->data;
234 if (msginfo->inreplyto)
235 parent = g_hash_table_lookup(msgid_table, msginfo->inreplyto);
237 /* try looking for the indirect parent */
238 if (!parent && msginfo->references) {
239 for (reflist = msginfo->references;
240 reflist != NULL; reflist = reflist->next)
241 if ((parent = g_hash_table_lookup
242 (msgid_table, reflist->data)) != NULL)
246 /* node should not be the parent, and node should not
247 be an ancestor of parent (circular reference) */
248 if (parent && parent != node &&
249 !g_node_is_ancestor(node, parent)) {
252 (parent, parent->children, node);
258 if (prefs_common.thread_by_subject) {
259 START_TIMING("thread by subject");
260 for (node = root->children; node && node != NULL;) {
262 msginfo = (MsgInfo *) node->data;
264 parent = subject_relation_lookup(subject_relation, msginfo);
266 /* the node may already be threaded by IN-REPLY-TO, so go up
268 find the parent node */
269 if (parent != NULL) {
270 if (g_node_is_ancestor(node, parent))
278 g_node_append(parent, node);
286 if (prefs_common.thread_by_subject)
287 g_relation_destroy(subject_relation);
289 g_hash_table_destroy(msgid_table);
294 gint procmsg_move_messages(GSList *mlist)
296 GSList *cur, *movelist = NULL;
298 FolderItem *dest = NULL;
300 gboolean finished = TRUE;
301 if (!mlist) return 0;
303 folder_item_update_freeze();
306 for (cur = mlist; cur != NULL; cur = cur->next) {
307 msginfo = (MsgInfo *)cur->data;
308 if (!msginfo->to_folder) {
314 dest = msginfo->to_folder;
315 movelist = g_slist_prepend(movelist, msginfo);
316 } else if (dest == msginfo->to_folder) {
317 movelist = g_slist_prepend(movelist, msginfo);
321 procmsg_msginfo_set_to_folder(msginfo, NULL);
324 movelist = g_slist_reverse(movelist);
325 retval |= folder_item_move_msgs(dest, movelist);
326 g_slist_free(movelist);
329 if (finished == FALSE) {
335 folder_item_update_thaw();
339 void procmsg_copy_messages(GSList *mlist)
341 GSList *cur, *copylist = NULL;
343 FolderItem *dest = NULL;
344 gboolean finished = TRUE;
347 folder_item_update_freeze();
350 for (cur = mlist; cur != NULL; cur = cur->next) {
351 msginfo = (MsgInfo *)cur->data;
352 if (!msginfo->to_folder) {
358 dest = msginfo->to_folder;
359 copylist = g_slist_prepend(copylist, msginfo);
360 } else if (dest == msginfo->to_folder) {
361 copylist = g_slist_prepend(copylist, msginfo);
365 procmsg_msginfo_set_to_folder(msginfo, NULL);
368 copylist = g_slist_reverse(copylist);
369 folder_item_copy_msgs(dest, copylist);
370 g_slist_free(copylist);
373 if (finished == FALSE) {
379 folder_item_update_thaw();
382 gchar *procmsg_get_message_file_path(MsgInfo *msginfo)
386 g_return_val_if_fail(msginfo != NULL, NULL);
388 if (msginfo->plaintext_file)
389 file = g_strdup(msginfo->plaintext_file);
391 file = folder_item_fetch_msg(msginfo->folder, msginfo->msgnum);
397 gchar *procmsg_get_message_file(MsgInfo *msginfo)
399 gchar *filename = NULL;
401 g_return_val_if_fail(msginfo != NULL, NULL);
403 filename = folder_item_fetch_msg(msginfo->folder, msginfo->msgnum);
405 debug_print("can't fetch message %d\n", msginfo->msgnum);
410 gchar *procmsg_get_message_file_full(MsgInfo *msginfo, gboolean headers, gboolean body)
412 gchar *filename = NULL;
414 g_return_val_if_fail(msginfo != NULL, NULL);
416 filename = folder_item_fetch_msg_full(msginfo->folder, msginfo->msgnum,
419 debug_print("can't fetch message %d\n", msginfo->msgnum);
424 GSList *procmsg_get_message_file_list(GSList *mlist)
426 GSList *file_list = NULL;
428 MsgFileInfo *fileinfo;
431 while (mlist != NULL) {
432 msginfo = (MsgInfo *)mlist->data;
433 file = procmsg_get_message_file(msginfo);
435 procmsg_message_file_list_free(file_list);
438 fileinfo = g_new(MsgFileInfo, 1);
439 fileinfo->msginfo = procmsg_msginfo_new_ref(msginfo);
440 fileinfo->file = file;
441 fileinfo->flags = g_new(MsgFlags, 1);
442 *fileinfo->flags = msginfo->flags;
443 file_list = g_slist_prepend(file_list, fileinfo);
447 file_list = g_slist_reverse(file_list);
452 void procmsg_message_file_list_free(MsgInfoList *file_list)
455 MsgFileInfo *fileinfo;
457 for (cur = file_list; cur != NULL; cur = cur->next) {
458 fileinfo = (MsgFileInfo *)cur->data;
459 procmsg_msginfo_free(fileinfo->msginfo);
460 g_free(fileinfo->file);
461 g_free(fileinfo->flags);
465 g_slist_free(file_list);
468 FILE *procmsg_open_message(MsgInfo *msginfo)
473 g_return_val_if_fail(msginfo != NULL, NULL);
475 file = procmsg_get_message_file_path(msginfo);
476 g_return_val_if_fail(file != NULL, NULL);
478 if (!is_file_exist(file)) {
480 file = procmsg_get_message_file(msginfo);
485 if ((fp = g_fopen(file, "rb")) == NULL) {
486 FILE_OP_ERROR(file, "fopen");
493 if (MSG_IS_QUEUED(msginfo->flags) || MSG_IS_DRAFT(msginfo->flags)) {
496 while (fgets(buf, sizeof(buf), fp) != NULL) {
498 if ((!strncmp(buf, "X-Claws-End-Special-Headers: 1",
499 strlen("X-Claws-End-Special-Headers:"))) ||
500 (!strncmp(buf, "X-Sylpheed-End-Special-Headers: 1",
501 strlen("X-Sylpheed-End-Special-Headers:"))))
504 if (buf[0] == '\r' || buf[0] == '\n') break;
505 /* from other mailers */
506 if (!strncmp(buf, "Date: ", 6)
507 || !strncmp(buf, "To: ", 4)
508 || !strncmp(buf, "From: ", 6)
509 || !strncmp(buf, "Subject: ", 9)) {
519 gboolean procmsg_msg_exist(MsgInfo *msginfo)
524 if (!msginfo) return FALSE;
526 path = folder_item_get_path(msginfo->folder);
528 ret = !folder_item_is_msg_changed(msginfo->folder, msginfo);
534 void procmsg_get_filter_keyword(MsgInfo *msginfo, gchar **header, gchar **key,
535 PrefsFilterType type)
537 static HeaderEntry hentry[] = {{"X-BeenThere:", NULL, TRUE},
538 {"X-ML-Name:", NULL, TRUE},
539 {"X-List:", NULL, TRUE},
540 {"X-Mailing-list:", NULL, TRUE},
541 {"List-Id:", NULL, TRUE},
542 {"X-Sequence:", NULL, TRUE},
543 {"Sender:", NULL, TRUE},
544 {"List-Post:", NULL, TRUE},
545 {NULL, NULL, FALSE}};
551 H_X_MAILING_LIST = 3,
560 g_return_if_fail(msginfo != NULL);
561 g_return_if_fail(header != NULL);
562 g_return_if_fail(key != NULL);
571 if ((fp = procmsg_open_message(msginfo)) == NULL)
573 procheader_get_header_fields(fp, hentry);
576 #define SET_FILTER_KEY(hstr, idx) \
578 *header = g_strdup(hstr); \
579 *key = hentry[idx].body; \
580 hentry[idx].body = NULL; \
583 if (hentry[H_X_BEENTHERE].body != NULL) {
584 SET_FILTER_KEY("header \"X-BeenThere\"", H_X_BEENTHERE);
585 } else if (hentry[H_X_ML_NAME].body != NULL) {
586 SET_FILTER_KEY("header \"X-ML-Name\"", H_X_ML_NAME);
587 } else if (hentry[H_X_LIST].body != NULL) {
588 SET_FILTER_KEY("header \"X-List\"", H_X_LIST);
589 } else if (hentry[H_X_MAILING_LIST].body != NULL) {
590 SET_FILTER_KEY("header \"X-Mailing-List\"", H_X_MAILING_LIST);
591 } else if (hentry[H_LIST_ID].body != NULL) {
592 SET_FILTER_KEY("header \"List-Id\"", H_LIST_ID);
593 extract_list_id_str(*key);
594 } else if (hentry[H_X_SEQUENCE].body != NULL) {
597 SET_FILTER_KEY("X-Sequence", H_X_SEQUENCE);
600 while (*p != '\0' && !g_ascii_isspace(*p)) p++;
601 while (g_ascii_isspace(*p)) p++;
602 if (g_ascii_isdigit(*p)) {
608 } else if (hentry[H_SENDER].body != NULL) {
609 SET_FILTER_KEY("header \"Sender\"", H_SENDER);
610 } else if (hentry[H_LIST_POST].body != NULL) {
611 SET_FILTER_KEY("header \"List-Post\"", H_LIST_POST);
612 } else if (msginfo->to) {
613 *header = g_strdup("to");
614 *key = g_strdup(msginfo->to);
615 } else if (msginfo->subject) {
616 *header = g_strdup("subject");
617 *key = g_strdup(msginfo->subject);
620 #undef SET_FILTER_KEY
622 g_free(hentry[H_X_BEENTHERE].body);
623 hentry[H_X_BEENTHERE].body = NULL;
624 g_free(hentry[H_X_ML_NAME].body);
625 hentry[H_X_ML_NAME].body = NULL;
626 g_free(hentry[H_X_LIST].body);
627 hentry[H_X_LIST].body = NULL;
628 g_free(hentry[H_X_MAILING_LIST].body);
629 hentry[H_X_MAILING_LIST].body = NULL;
630 g_free(hentry[H_LIST_ID].body);
631 hentry[H_LIST_ID].body = NULL;
632 g_free(hentry[H_SENDER].body);
633 hentry[H_SENDER].body = NULL;
634 g_free(hentry[H_LIST_POST].body);
635 hentry[H_LIST_POST].body = NULL;
639 *header = g_strdup("from");
640 *key = g_strdup(msginfo->from);
643 *header = g_strdup("to");
644 *key = g_strdup(msginfo->to);
646 case FILTER_BY_SUBJECT:
647 *header = g_strdup("subject");
648 *key = g_strdup(msginfo->subject);
655 static void procmsg_empty_trash(FolderItem *trash)
660 (trash->stype != F_TRASH &&
661 !folder_has_parent_of_type(trash, F_TRASH)))
664 if (trash && trash->total_msgs > 0) {
665 GSList *mlist = folder_item_get_msg_list(trash);
667 for (cur = mlist ; cur != NULL ; cur = cur->next) {
668 MsgInfo * msginfo = (MsgInfo *) cur->data;
669 if (MSG_IS_LOCKED(msginfo->flags)) {
670 procmsg_msginfo_free(msginfo);
673 if (msginfo->total_size != 0 &&
674 msginfo->size != (off_t)msginfo->total_size)
675 partial_mark_for_delete(msginfo);
677 procmsg_msginfo_free(msginfo);
680 folder_item_remove_all_msg(trash);
683 if (!trash->node || !trash->node->children)
686 node = trash->node->children;
687 while (node != NULL) {
689 procmsg_empty_trash(FOLDER_ITEM(node->data));
694 void procmsg_empty_all_trash(void)
699 for (cur = folder_get_list(); cur != NULL; cur = cur->next) {
700 Folder *folder = FOLDER(cur->data);
701 trash = folder->trash;
702 procmsg_empty_trash(trash);
703 if (folder->account && folder->account->set_trash_folder &&
704 folder_find_item_from_identifier(folder->account->trash_folder))
706 folder_find_item_from_identifier(folder->account->trash_folder));
710 static PrefsAccount *procmsg_get_account_from_file(const gchar *file)
712 PrefsAccount *mailac = NULL;
716 static HeaderEntry qentry[] = {{"S:", NULL, FALSE},
717 {"SSV:", NULL, FALSE},
719 {"NG:", NULL, FALSE},
720 {"MAID:", NULL, FALSE},
721 {"NAID:", NULL, FALSE},
722 {"SCF:", NULL, FALSE},
723 {"RMID:", NULL, FALSE},
724 {"FMID:", NULL, FALSE},
725 {"X-Claws-Privacy-System:", NULL, FALSE},
726 {"X-Claws-Encrypt:", NULL, FALSE},
727 {"X-Claws-Encrypt-Data:", NULL, FALSE},
728 {"X-Claws-End-Special-Headers", NULL, FALSE},
729 {"X-Sylpheed-Privacy-System:", NULL, FALSE},
730 {"X-Sylpheed-Encrypt:", NULL, FALSE},
731 {"X-Sylpheed-Encrypt-Data:", NULL, FALSE},
732 {NULL, NULL, FALSE}};
734 g_return_val_if_fail(file != NULL, NULL);
736 if ((fp = g_fopen(file, "rb")) == NULL) {
737 FILE_OP_ERROR(file, "fopen");
741 while ((hnum = procheader_get_one_field(buf, sizeof(buf), fp, qentry))
743 gchar *p = buf + strlen(qentry[hnum].name);
745 if (hnum == Q_MAIL_ACCOUNT_ID) {
746 mailac = account_find_from_id(atoi(p));
754 static GSList *procmsg_list_sort_by_account(FolderItem *queue, GSList *list)
756 GSList *result = NULL;
758 PrefsAccount *last_account = NULL;
761 gboolean nothing_to_sort = TRUE;
766 orig = g_slist_copy(list);
768 msg = (MsgInfo *)orig->data;
770 for (cur = orig; cur; cur = cur->next)
771 debug_print("sort before %s\n", ((MsgInfo *)cur->data)->from);
776 nothing_to_sort = TRUE;
780 PrefsAccount *ac = NULL;
781 msg = (MsgInfo *)cur->data;
782 file = folder_item_fetch_msg(queue, msg->msgnum);
783 ac = procmsg_get_account_from_file(file);
786 if (last_account == NULL || (ac != NULL && ac == last_account)) {
787 result = g_slist_append(result, msg);
788 orig = g_slist_remove(orig, msg);
790 nothing_to_sort = FALSE;
796 if (orig || g_slist_length(orig)) {
797 if (!last_account && nothing_to_sort) {
798 /* can't find an account for the rest of the list */
801 result = g_slist_append(result, cur->data);
812 for (cur = result; cur; cur = cur->next)
813 debug_print("sort after %s\n", ((MsgInfo *)cur->data)->from);
820 static gboolean procmsg_is_last_for_account(FolderItem *queue, MsgInfo *msginfo, GSList *elem)
822 gchar *file = folder_item_fetch_msg(queue, msginfo->msgnum);
823 PrefsAccount *ac = procmsg_get_account_from_file(file);
826 for (cur = elem; cur; cur = cur->next) {
827 MsgInfo *cur_msginfo = (MsgInfo *)cur->data;
828 file = folder_item_fetch_msg(queue, cur_msginfo->msgnum);
830 if (cur_msginfo != msginfo && !MSG_IS_LOCKED(cur_msginfo->flags)) {
831 if (procmsg_get_account_from_file(file) == ac) {
842 static gboolean send_queue_lock = FALSE;
844 *\brief Send messages in queue
846 *\param queue Queue folder to process
847 *\param save_msgs Unused
849 *\return Number of messages sent, negative if an error occurred
850 * positive if no error occurred
852 gint procmsg_send_queue(FolderItem *queue, gboolean save_msgs, gchar **errstr)
854 gint sent = 0, err = 0;
856 GSList *sorted_list = NULL;
859 if (send_queue_lock) {
860 /* Avoid having to translate two similar strings */
861 log_warning(LOG_PROTOCOL, "%s\n", _("Already trying to send."));
863 if (*errstr) g_free(*errstr);
864 *errstr = g_strdup_printf(_("Already trying to send."));
866 toolbar_main_set_sensitive(mainwindow_get_mainwindow());
869 send_queue_lock = TRUE;
872 queue = folder_get_default_queue();
875 send_queue_lock = FALSE;
880 toolbar_main_set_sensitive(mainwindow_get_mainwindow());
882 folder_item_scan(queue);
883 list = folder_item_get_msg_list(queue);
885 /* sort the list per sender account; this helps reusing the same SMTP server */
886 sorted_list = procmsg_list_sort_by_account(queue, list);
888 for (elem = sorted_list; elem != NULL; elem = elem->next) {
892 msginfo = (MsgInfo *)(elem->data);
893 if (!MSG_IS_LOCKED(msginfo->flags)) {
894 file = folder_item_fetch_msg(queue, msginfo->msgnum);
896 gboolean queued_removed = FALSE;
897 if (procmsg_send_message_queue_full(file,
898 !procmsg_is_last_for_account(queue, msginfo, elem),
899 errstr, queue, msginfo->msgnum, &queued_removed) < 0) {
900 g_warning("Sending queued message %d failed.\n",
906 folder_item_remove_msg(queue, msginfo->msgnum);
911 /* FIXME: supposedly if only one message is locked, and queue
912 * is being flushed, the following free says something like
913 * "freeing msg ## in folder (nil)". */
914 procmsg_msginfo_free(msginfo);
917 g_slist_free(sorted_list);
918 folder_item_scan(queue);
920 if (queue->node && queue->node->children) {
921 node = queue->node->children;
922 while (node != NULL) {
925 send_queue_lock = FALSE;
926 res = procmsg_send_queue(FOLDER_ITEM(node->data), save_msgs, errstr);
927 send_queue_lock = TRUE;
935 send_queue_lock = FALSE;
937 toolbar_main_set_sensitive(mainwindow_get_mainwindow());
939 return (err != 0 ? -err : sent);
942 gboolean procmsg_is_sending(void)
944 return send_queue_lock;
948 *\brief Determine if a queue folder is empty
950 *\param queue Queue folder to process
952 *\return TRUE if the queue folder is empty, otherwise return FALSE
954 gboolean procmsg_queue_is_empty(FolderItem *queue)
957 gboolean res = FALSE;
959 queue = folder_get_default_queue();
960 g_return_val_if_fail(queue != NULL, TRUE);
962 folder_item_scan(queue);
963 list = folder_item_get_msg_list(queue);
964 res = (list == NULL);
965 procmsg_msg_list_free(list);
969 if (queue->node && queue->node->children) {
970 node = queue->node->children;
971 while (node != NULL) {
973 if (!procmsg_queue_is_empty(FOLDER_ITEM(node->data)))
982 gint procmsg_remove_special_headers(const gchar *in, const gchar *out)
987 if ((fp = g_fopen(in, "rb")) == NULL) {
988 FILE_OP_ERROR(in, "fopen");
991 if ((outfp = g_fopen(out, "wb")) == NULL) {
992 FILE_OP_ERROR(out, "fopen");
996 while (fgets(buf, sizeof(buf), fp) != NULL) {
998 if ((!strncmp(buf, "X-Claws-End-Special-Headers: 1",
999 strlen("X-Claws-End-Special-Headers:"))) ||
1000 (!strncmp(buf, "X-Sylpheed-End-Special-Headers: 1",
1001 strlen("X-Sylpheed-End-Special-Headers:"))))
1004 if (buf[0] == '\r' || buf[0] == '\n') break;
1005 /* from other mailers */
1006 if (!strncmp(buf, "Date: ", 6)
1007 || !strncmp(buf, "To: ", 4)
1008 || !strncmp(buf, "From: ", 6)
1009 || !strncmp(buf, "Subject: ", 9)) {
1014 while (fgets(buf, sizeof(buf), fp) != NULL)
1021 static gint procmsg_save_to_outbox(FolderItem *outbox, const gchar *file,
1025 MsgInfo *msginfo, *tmp_msginfo;
1026 MsgFlags flag = {0, 0};
1028 debug_print("saving sent message...\n");
1031 outbox = folder_get_default_outbox();
1032 g_return_val_if_fail(outbox != NULL, -1);
1034 /* remove queueing headers */
1036 gchar tmp[MAXPATHLEN + 1];
1038 g_snprintf(tmp, sizeof(tmp), "%s%ctmpmsg.out.%08x",
1039 get_rc_dir(), G_DIR_SEPARATOR, (guint) rand());
1041 if (procmsg_remove_special_headers(file, tmp) !=0)
1044 folder_item_scan(outbox);
1045 if ((num = folder_item_add_msg(outbox, tmp, &flag, TRUE)) < 0) {
1046 g_warning("can't save message\n");
1051 folder_item_scan(outbox);
1052 if ((num = folder_item_add_msg
1053 (outbox, file, &flag, FALSE)) < 0) {
1054 g_warning("can't save message\n");
1058 msginfo = folder_item_get_msginfo(outbox, num); /* refcnt++ */
1059 tmp_msginfo = procmsg_msginfo_get_full_info(msginfo); /* refcnt++ */
1060 if (msginfo != NULL) {
1061 procmsg_msginfo_unset_flags(msginfo, ~0, 0);
1062 procmsg_msginfo_free(msginfo); /* refcnt-- */
1063 /* tmp_msginfo == msginfo */
1064 if (tmp_msginfo && msginfo->extradata &&
1065 (msginfo->extradata->dispositionnotificationto ||
1066 msginfo->extradata->returnreceiptto)) {
1067 procmsg_msginfo_set_flags(msginfo, MSG_RETRCPT_SENT, 0);
1069 procmsg_msginfo_free(tmp_msginfo); /* refcnt-- */
1075 void procmsg_print_message(MsgInfo *msginfo, const gchar *cmdline)
1077 static const gchar *def_cmd = "lpr %s";
1078 static guint id = 0;
1084 g_return_if_fail(msginfo);
1086 if (procmime_msginfo_is_encrypted(msginfo))
1087 tmpfp = procmime_get_first_encrypted_text_content(msginfo);
1089 tmpfp = procmime_get_first_text_content(msginfo);
1090 if (tmpfp == NULL) {
1091 g_warning("Can't get text part\n");
1095 prtmp = g_strdup_printf("%s%cprinttmp.%08x",
1096 get_mime_tmp_dir(), G_DIR_SEPARATOR, id++);
1098 if ((prfp = g_fopen(prtmp, "wb")) == NULL) {
1099 FILE_OP_ERROR(prtmp, "fopen");
1105 if (msginfo->date) fprintf(prfp, "Date: %s\n", msginfo->date);
1106 if (msginfo->from) fprintf(prfp, "From: %s\n", msginfo->from);
1107 if (msginfo->to) fprintf(prfp, "To: %s\n", msginfo->to);
1108 if (msginfo->cc) fprintf(prfp, "Cc: %s\n", msginfo->cc);
1109 if (msginfo->newsgroups)
1110 fprintf(prfp, "Newsgroups: %s\n", msginfo->newsgroups);
1111 if (msginfo->subject) fprintf(prfp, "Subject: %s\n", msginfo->subject);
1114 while (fgets(buf, sizeof(buf), tmpfp) != NULL)
1120 if (cmdline && (p = strchr(cmdline, '%')) && *(p + 1) == 's' &&
1121 !strchr(p + 2, '%'))
1122 g_snprintf(buf, sizeof(buf) - 1, cmdline, prtmp);
1125 g_warning("Print command line is invalid: '%s'\n",
1127 g_snprintf(buf, sizeof(buf) - 1, def_cmd, prtmp);
1133 if (buf[strlen(buf) - 1] != '&') strcat(buf, "&");
1137 MsgInfo *procmsg_msginfo_new_ref(MsgInfo *msginfo)
1144 MsgInfo *procmsg_msginfo_new(void)
1146 MsgInfo *newmsginfo;
1148 newmsginfo = g_new0(MsgInfo, 1);
1149 newmsginfo->refcnt = 1;
1154 MsgInfo *procmsg_msginfo_copy(MsgInfo *msginfo)
1156 MsgInfo *newmsginfo;
1159 if (msginfo == NULL) return NULL;
1161 newmsginfo = g_new0(MsgInfo, 1);
1163 newmsginfo->refcnt = 1;
1165 #define MEMBCOPY(mmb) newmsginfo->mmb = msginfo->mmb
1166 #define MEMBDUP(mmb) newmsginfo->mmb = msginfo->mmb ? \
1167 g_strdup(msginfo->mmb) : NULL
1182 MEMBDUP(newsgroups);
1189 MEMBCOPY(to_folder);
1191 if (msginfo->extradata) {
1192 newmsginfo->extradata = g_new0(MsgInfoExtraData, 1);
1193 MEMBDUP(extradata->face);
1194 MEMBDUP(extradata->xface);
1195 MEMBDUP(extradata->dispositionnotificationto);
1196 MEMBDUP(extradata->returnreceiptto);
1197 MEMBDUP(extradata->partial_recv);
1198 MEMBDUP(extradata->account_server);
1199 MEMBDUP(extradata->account_login);
1200 MEMBDUP(extradata->list_post);
1201 MEMBDUP(extradata->list_subscribe);
1202 MEMBDUP(extradata->list_unsubscribe);
1203 MEMBDUP(extradata->list_help);
1204 MEMBDUP(extradata->list_archive);
1205 MEMBDUP(extradata->list_owner);
1208 refs = msginfo->references;
1209 for (refs = msginfo->references; refs != NULL; refs = refs->next) {
1210 newmsginfo->references = g_slist_prepend
1211 (newmsginfo->references, g_strdup(refs->data));
1213 newmsginfo->references = g_slist_reverse(newmsginfo->references);
1216 MEMBDUP(plaintext_file);
1221 MsgInfo *procmsg_msginfo_get_full_info(MsgInfo *msginfo)
1223 MsgInfo *full_msginfo;
1226 if (msginfo == NULL) return NULL;
1228 file = procmsg_get_message_file_path(msginfo);
1229 if (!file || !is_file_exist(file)) {
1231 file = procmsg_get_message_file(msginfo);
1233 if (!file || !is_file_exist(file)) {
1234 g_warning("procmsg_msginfo_get_full_info(): can't get message file.\n");
1238 full_msginfo = procheader_parse_file(file, msginfo->flags, TRUE, FALSE);
1240 if (!full_msginfo) return NULL;
1242 msginfo->total_size = full_msginfo->total_size;
1243 msginfo->planned_download = full_msginfo->planned_download;
1245 if (full_msginfo->extradata) {
1246 if (!msginfo->extradata)
1247 msginfo->extradata = g_new0(MsgInfoExtraData, 1);
1248 if (!msginfo->extradata->list_post)
1249 msginfo->extradata->list_post = g_strdup(full_msginfo->extradata->list_post);
1250 if (!msginfo->extradata->list_subscribe)
1251 msginfo->extradata->list_subscribe = g_strdup(full_msginfo->extradata->list_subscribe);
1252 if (!msginfo->extradata->list_unsubscribe)
1253 msginfo->extradata->list_unsubscribe = g_strdup(full_msginfo->extradata->list_unsubscribe);
1254 if (!msginfo->extradata->list_help)
1255 msginfo->extradata->list_help = g_strdup(full_msginfo->extradata->list_help);
1256 if (!msginfo->extradata->list_archive)
1257 msginfo->extradata->list_archive= g_strdup(full_msginfo->extradata->list_archive);
1258 if (!msginfo->extradata->list_owner)
1259 msginfo->extradata->list_owner = g_strdup(full_msginfo->extradata->list_owner);
1260 if (!msginfo->extradata->xface)
1261 msginfo->extradata->xface = g_strdup(full_msginfo->extradata->xface);
1262 if (!msginfo->extradata->face)
1263 msginfo->extradata->face = g_strdup(full_msginfo->extradata->face);
1264 if (!msginfo->extradata->dispositionnotificationto)
1265 msginfo->extradata->dispositionnotificationto =
1266 g_strdup(full_msginfo->extradata->dispositionnotificationto);
1267 if (!msginfo->extradata->returnreceiptto)
1268 msginfo->extradata->returnreceiptto = g_strdup
1269 (full_msginfo->extradata->returnreceiptto);
1270 if (!msginfo->extradata->partial_recv && full_msginfo->extradata->partial_recv)
1271 msginfo->extradata->partial_recv = g_strdup
1272 (full_msginfo->extradata->partial_recv);
1273 if (!msginfo->extradata->account_server && full_msginfo->extradata->account_server)
1274 msginfo->extradata->account_server = g_strdup
1275 (full_msginfo->extradata->account_server);
1276 if (!msginfo->extradata->account_login && full_msginfo->extradata->account_login)
1277 msginfo->extradata->account_login = g_strdup
1278 (full_msginfo->extradata->account_login);
1280 procmsg_msginfo_free(full_msginfo);
1282 return procmsg_msginfo_new_ref(msginfo);
1285 void procmsg_msginfo_free(MsgInfo *msginfo)
1287 if (msginfo == NULL) return;
1290 if (msginfo->refcnt > 0)
1293 if (msginfo->to_folder) {
1294 msginfo->to_folder->op_count--;
1295 folder_item_update(msginfo->to_folder, F_ITEM_UPDATE_MSGCNT);
1298 g_free(msginfo->fromspace);
1300 g_free(msginfo->fromname);
1302 g_free(msginfo->date);
1303 g_free(msginfo->from);
1304 g_free(msginfo->to);
1305 g_free(msginfo->cc);
1306 g_free(msginfo->newsgroups);
1307 g_free(msginfo->subject);
1308 g_free(msginfo->msgid);
1309 g_free(msginfo->inreplyto);
1310 g_free(msginfo->xref);
1312 if (msginfo->extradata) {
1313 g_free(msginfo->extradata->returnreceiptto);
1314 g_free(msginfo->extradata->dispositionnotificationto);
1315 g_free(msginfo->extradata->xface);
1316 g_free(msginfo->extradata->face);
1317 g_free(msginfo->extradata->list_post);
1318 g_free(msginfo->extradata->list_subscribe);
1319 g_free(msginfo->extradata->list_unsubscribe);
1320 g_free(msginfo->extradata->list_help);
1321 g_free(msginfo->extradata->list_archive);
1322 g_free(msginfo->extradata->list_owner);
1323 g_free(msginfo->extradata->partial_recv);
1324 g_free(msginfo->extradata->account_server);
1325 g_free(msginfo->extradata->account_login);
1326 g_free(msginfo->extradata);
1328 slist_free_strings(msginfo->references);
1329 g_slist_free(msginfo->references);
1330 g_slist_free(msginfo->tags);
1332 g_free(msginfo->plaintext_file);
1337 guint procmsg_msginfo_memusage(MsgInfo *msginfo)
1342 memusage += sizeof(MsgInfo);
1343 if (msginfo->fromname)
1344 memusage += strlen(msginfo->fromname);
1346 memusage += strlen(msginfo->date);
1348 memusage += strlen(msginfo->from);
1350 memusage += strlen(msginfo->to);
1352 memusage += strlen(msginfo->cc);
1353 if (msginfo->newsgroups)
1354 memusage += strlen(msginfo->newsgroups);
1355 if (msginfo->subject)
1356 memusage += strlen(msginfo->subject);
1358 memusage += strlen(msginfo->msgid);
1359 if (msginfo->inreplyto)
1360 memusage += strlen(msginfo->inreplyto);
1362 for (tmp = msginfo->references; tmp; tmp=tmp->next) {
1363 gchar *r = (gchar *)tmp->data;
1364 memusage += r?strlen(r):0 + sizeof(GSList);
1366 if (msginfo->fromspace)
1367 memusage += strlen(msginfo->fromspace);
1369 for (tmp = msginfo->tags; tmp; tmp=tmp->next) {
1370 memusage += sizeof(GSList);
1372 if (msginfo->extradata) {
1373 memusage += sizeof(MsgInfoExtraData);
1374 if (msginfo->extradata->xface)
1375 memusage += strlen(msginfo->extradata->xface);
1376 if (msginfo->extradata->face)
1377 memusage += strlen(msginfo->extradata->face);
1378 if (msginfo->extradata->dispositionnotificationto)
1379 memusage += strlen(msginfo->extradata->dispositionnotificationto);
1380 if (msginfo->extradata->returnreceiptto)
1381 memusage += strlen(msginfo->extradata->returnreceiptto);
1383 if (msginfo->extradata->partial_recv)
1384 memusage += strlen(msginfo->extradata->partial_recv);
1385 if (msginfo->extradata->account_server)
1386 memusage += strlen(msginfo->extradata->account_server);
1387 if (msginfo->extradata->account_login)
1388 memusage += strlen(msginfo->extradata->account_login);
1390 if (msginfo->extradata->list_post)
1391 memusage += strlen(msginfo->extradata->list_post);
1392 if (msginfo->extradata->list_subscribe)
1393 memusage += strlen(msginfo->extradata->list_subscribe);
1394 if (msginfo->extradata->list_unsubscribe)
1395 memusage += strlen(msginfo->extradata->list_unsubscribe);
1396 if (msginfo->extradata->list_help)
1397 memusage += strlen(msginfo->extradata->list_help);
1398 if (msginfo->extradata->list_archive)
1399 memusage += strlen(msginfo->extradata->list_archive);
1400 if (msginfo->extradata->list_owner)
1401 memusage += strlen(msginfo->extradata->list_owner);
1406 static gint procmsg_send_message_queue_full(const gchar *file, gboolean keep_session, gchar **errstr,
1407 FolderItem *queue, gint msgnum, gboolean *queued_removed)
1409 static HeaderEntry qentry[] = {{"S:", NULL, FALSE},
1410 {"SSV:", NULL, FALSE},
1411 {"R:", NULL, FALSE},
1412 {"NG:", NULL, FALSE},
1413 {"MAID:", NULL, FALSE},
1414 {"NAID:", NULL, FALSE},
1415 {"SCF:", NULL, FALSE},
1416 {"RMID:", NULL, FALSE},
1417 {"FMID:", NULL, FALSE},
1418 {"X-Claws-Privacy-System:", NULL, FALSE},
1419 {"X-Claws-Encrypt:", NULL, FALSE},
1420 {"X-Claws-Encrypt-Data:", NULL, FALSE},
1421 {"X-Claws-End-Special-Headers:", NULL, FALSE},
1422 {"X-Sylpheed-Privacy-System:", NULL, FALSE},
1423 {"X-Sylpheed-Encrypt:", NULL, FALSE},
1424 {"X-Sylpheed-Encrypt-Data:", NULL, FALSE},
1425 {"X-Sylpheed-End-Special-Headers:", NULL, FALSE},
1426 {NULL, NULL, FALSE}};
1429 gint mailval = 0, newsval = 0;
1431 gchar *smtpserver = NULL;
1432 GSList *to_list = NULL;
1433 GSList *newsgroup_list = NULL;
1434 gchar *savecopyfolder = NULL;
1435 gchar *replymessageid = NULL;
1436 gchar *fwdmessageid = NULL;
1437 gchar *privacy_system = NULL;
1438 gboolean encrypt = FALSE;
1439 gchar *encrypt_data = NULL;
1440 gchar buf[BUFFSIZE];
1442 PrefsAccount *mailac = NULL, *newsac = NULL;
1443 gboolean save_clear_text = TRUE;
1444 gchar *tmp_enc_file = NULL;
1448 g_return_val_if_fail(file != NULL, -1);
1450 if ((fp = g_fopen(file, "rb")) == NULL) {
1451 FILE_OP_ERROR(file, "fopen");
1453 if (*errstr) g_free(*errstr);
1454 *errstr = g_strdup_printf(_("Couldn't open file %s."), file);
1459 while ((hnum = procheader_get_one_field(buf, sizeof(buf), fp, qentry))
1461 gchar *p = buf + strlen(qentry[hnum].name);
1469 if (smtpserver == NULL)
1470 smtpserver = g_strdup(p);
1473 to_list = address_list_append(to_list, p);
1476 newsgroup_list = newsgroup_list_append(newsgroup_list, p);
1478 case Q_MAIL_ACCOUNT_ID:
1479 mailac = account_find_from_id(atoi(p));
1481 case Q_NEWS_ACCOUNT_ID:
1482 newsac = account_find_from_id(atoi(p));
1484 case Q_SAVE_COPY_FOLDER:
1485 if (savecopyfolder == NULL)
1486 savecopyfolder = g_strdup(p);
1488 case Q_REPLY_MESSAGE_ID:
1489 if (replymessageid == NULL)
1490 replymessageid = g_strdup(p);
1492 case Q_FWD_MESSAGE_ID:
1493 if (fwdmessageid == NULL)
1494 fwdmessageid = g_strdup(p);
1496 case Q_PRIVACY_SYSTEM:
1497 case Q_PRIVACY_SYSTEM_OLD:
1498 if (privacy_system == NULL)
1499 privacy_system = g_strdup(p);
1506 case Q_ENCRYPT_DATA:
1507 case Q_ENCRYPT_DATA_OLD:
1508 if (encrypt_data == NULL)
1509 encrypt_data = g_strdup(p);
1512 case Q_CLAWS_HDRS_OLD:
1513 /* end of special headers reached */
1514 goto send_mail; /* can't "break;break;" */
1518 filepos = ftell(fp);
1523 if (mailac && mailac->save_encrypted_as_clear_text
1524 && !mailac->encrypt_to_self)
1525 save_clear_text = TRUE;
1527 save_clear_text = FALSE;
1532 mimeinfo = procmime_scan_queue_file(file);
1533 if (!privacy_encrypt(privacy_system, mimeinfo, encrypt_data)
1534 || (fp = my_tmpfile()) == NULL
1535 || procmime_write_mimeinfo(mimeinfo, fp) < 0) {
1538 procmime_mimeinfo_free_all(mimeinfo);
1541 slist_free_strings(to_list);
1542 g_slist_free(to_list);
1543 slist_free_strings(newsgroup_list);
1544 g_slist_free(newsgroup_list);
1545 g_free(savecopyfolder);
1546 g_free(replymessageid);
1547 g_free(fwdmessageid);
1548 g_free(privacy_system);
1549 g_free(encrypt_data);
1551 if (*errstr) g_free(*errstr);
1552 *errstr = g_strdup_printf(_("Couldn't encrypt the email: %s"),
1553 privacy_get_error());
1559 if (!save_clear_text) {
1560 gchar *content = NULL;
1561 FILE *tmpfp = get_tmpfile_in_dir(get_mime_tmp_dir(), &tmp_enc_file);
1565 content = file_read_stream_to_str(fp);
1568 str_write_to_file(content, tmp_enc_file);
1571 g_warning("couldn't get tempfile\n");
1575 procmime_mimeinfo_free_all(mimeinfo);
1581 debug_print("Sending message by mail\n");
1584 if (*errstr) g_free(*errstr);
1585 *errstr = g_strdup_printf(_("Queued message header is broken."));
1588 } else if (mailac && mailac->use_mail_command &&
1589 mailac->mail_command && (* mailac->mail_command)) {
1590 mailval = send_message_local(mailac->mail_command, fp);
1594 mailac = account_find_from_smtp_server(from, smtpserver);
1596 g_warning("Account not found. "
1597 "Using current account...\n");
1598 mailac = cur_account;
1603 mailval = send_message_smtp_full(mailac, to_list, fp, keep_session);
1604 if (mailval == -1 && errstr) {
1605 if (*errstr) g_free(*errstr);
1606 *errstr = g_strdup_printf(_("An error happened during SMTP session."));
1609 PrefsAccount tmp_ac;
1611 g_warning("Account not found.\n");
1613 memset(&tmp_ac, 0, sizeof(PrefsAccount));
1614 tmp_ac.address = from;
1615 tmp_ac.smtp_server = smtpserver;
1616 tmp_ac.smtpport = SMTP_PORT;
1617 mailval = send_message_smtp(&tmp_ac, to_list, fp);
1618 if (mailval == -1 && errstr) {
1619 if (*errstr) g_free(*errstr);
1620 *errstr = g_strdup_printf(_("No specific account has been found to "
1621 "send, and an error happened during SMTP session."));
1625 } else if (!to_list && !newsgroup_list) {
1627 if (*errstr) g_free(*errstr);
1628 *errstr = g_strdup(_("Couldn't determine sending informations. "
1629 "Maybe the email hasn't been generated by Claws Mail."));
1634 fseek(fp, filepos, SEEK_SET);
1635 if (newsgroup_list && (mailval == 0)) {
1640 /* write to temporary file */
1641 tmp = g_strdup_printf("%s%ctmp%p", g_get_tmp_dir(),
1642 G_DIR_SEPARATOR, file);
1643 if ((tmpfp = g_fopen(tmp, "wb")) == NULL) {
1644 FILE_OP_ERROR(tmp, "fopen");
1646 alertpanel_error(_("Couldn't create temporary file for news sending."));
1648 if (change_file_mode_rw(tmpfp, tmp) < 0) {
1649 FILE_OP_ERROR(tmp, "chmod");
1650 g_warning("can't change file mode\n");
1653 while ((newsval == 0) && fgets(buf, sizeof(buf), fp) != NULL) {
1654 if (fputs(buf, tmpfp) == EOF) {
1655 FILE_OP_ERROR(tmp, "fputs");
1658 if (*errstr) g_free(*errstr);
1659 *errstr = g_strdup_printf(_("Error when writing temporary file for news sending."));
1666 debug_print("Sending message by news\n");
1668 folder = FOLDER(newsac->folder);
1670 newsval = news_post(folder, tmp);
1671 if (newsval < 0 && errstr) {
1672 if (*errstr) g_free(*errstr);
1673 *errstr = g_strdup_printf(_("Error occurred while posting the message to %s."),
1674 newsac->nntp_server);
1684 /* save message to outbox */
1685 if (mailval == 0 && newsval == 0 && savecopyfolder) {
1688 debug_print("saving sent message...\n");
1690 outbox = folder_find_item_from_identifier(savecopyfolder);
1692 outbox = folder_get_default_outbox();
1694 if (save_clear_text || tmp_enc_file == NULL) {
1695 gboolean saved = FALSE;
1696 *queued_removed = FALSE;
1697 if (queue && msgnum > 0) {
1698 MsgInfo *queued_mail = folder_item_get_msginfo(queue, msgnum);
1699 if (folder_item_move_msg(outbox, queued_mail) >= 0) {
1700 debug_print("moved queued mail %d to sent folder\n", msgnum);
1702 *queued_removed = TRUE;
1703 } else if (folder_item_copy_msg(outbox, queued_mail) >= 0) {
1704 debug_print("copied queued mail %d to sent folder\n", msgnum);
1707 procmsg_msginfo_free(queued_mail);
1710 debug_print("resaving clear text queued mail to sent folder\n");
1711 procmsg_save_to_outbox(outbox, file, TRUE);
1714 debug_print("saving encrpyted queued mail to sent folder\n");
1715 procmsg_save_to_outbox(outbox, tmp_enc_file, FALSE);
1719 if (tmp_enc_file != NULL) {
1720 g_unlink(tmp_enc_file);
1722 tmp_enc_file = NULL;
1725 if (replymessageid != NULL || fwdmessageid != NULL) {
1729 if (replymessageid != NULL)
1730 tokens = g_strsplit(replymessageid, "\t", 0);
1732 tokens = g_strsplit(fwdmessageid, "\t", 0);
1733 item = folder_find_item_from_identifier(tokens[0]);
1735 /* check if queued message has valid folder and message id */
1736 if (item != NULL && tokens[2] != NULL) {
1739 msginfo = folder_item_get_msginfo(item, atoi(tokens[1]));
1741 /* check if referring message exists and has a message id */
1742 if ((msginfo != NULL) &&
1743 (msginfo->msgid != NULL) &&
1744 (strcmp(msginfo->msgid, tokens[2]) != 0)) {
1745 procmsg_msginfo_free(msginfo);
1749 if (msginfo == NULL) {
1750 msginfo = folder_item_get_msginfo_by_msgid(item, tokens[2]);
1753 if (msginfo != NULL) {
1754 MsgPermFlags to_unset = 0;
1756 if (prefs_common.mark_as_read_on_new_window)
1757 to_unset = (MSG_NEW|MSG_UNREAD);
1759 if (replymessageid != NULL) {
1760 procmsg_msginfo_unset_flags(msginfo, to_unset|MSG_FORWARDED, 0);
1761 procmsg_msginfo_set_flags(msginfo, MSG_REPLIED, 0);
1763 procmsg_msginfo_unset_flags(msginfo, MSG_REPLIED, 0);
1764 procmsg_msginfo_set_flags(msginfo, MSG_FORWARDED, 0);
1766 procmsg_msginfo_free(msginfo);
1774 slist_free_strings(to_list);
1775 g_slist_free(to_list);
1776 slist_free_strings(newsgroup_list);
1777 g_slist_free(newsgroup_list);
1778 g_free(savecopyfolder);
1779 g_free(replymessageid);
1780 g_free(fwdmessageid);
1781 g_free(privacy_system);
1782 g_free(encrypt_data);
1784 return (newsval != 0 ? newsval : mailval);
1787 gint procmsg_send_message_queue(const gchar *file, gchar **errstr, FolderItem *queue, gint msgnum, gboolean *queued_removed)
1789 gint result = procmsg_send_message_queue_full(file, FALSE, errstr, queue, msgnum, queued_removed);
1790 toolbar_main_set_sensitive(mainwindow_get_mainwindow());
1794 static void update_folder_msg_counts(FolderItem *item, MsgInfo *msginfo, MsgPermFlags old_flags)
1796 MsgPermFlags new_flags = msginfo->flags.perm_flags;
1799 if (!(old_flags & MSG_NEW) && (new_flags & MSG_NEW)) {
1803 if ((old_flags & MSG_NEW) && !(new_flags & MSG_NEW)) {
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++;
1814 if ((old_flags & MSG_UNREAD) && !(new_flags & MSG_UNREAD)) {
1815 item->unread_msgs--;
1816 if (procmsg_msg_has_marked_parent(msginfo))
1817 item->unreadmarked_msgs--;
1821 if (!(old_flags & MSG_MARKED) && (new_flags & MSG_MARKED)) {
1822 procmsg_update_unread_children(msginfo, TRUE);
1823 item->marked_msgs++;
1826 if ((old_flags & MSG_MARKED) && !(new_flags & MSG_MARKED)) {
1827 procmsg_update_unread_children(msginfo, FALSE);
1828 item->marked_msgs--;
1832 void procmsg_msginfo_set_flags(MsgInfo *msginfo, MsgPermFlags perm_flags, MsgTmpFlags tmp_flags)
1835 MsgInfoUpdate msginfo_update;
1836 MsgPermFlags perm_flags_new, perm_flags_old;
1837 MsgTmpFlags tmp_flags_old;
1839 g_return_if_fail(msginfo != NULL);
1840 item = msginfo->folder;
1841 g_return_if_fail(item != NULL);
1843 debug_print("Setting flags for message %d in folder %s\n", msginfo->msgnum, item->path);
1845 /* Perm Flags handling */
1846 perm_flags_old = msginfo->flags.perm_flags;
1847 perm_flags_new = msginfo->flags.perm_flags | perm_flags;
1848 if ((perm_flags & MSG_IGNORE_THREAD) || (perm_flags_old & MSG_IGNORE_THREAD)) {
1849 perm_flags_new &= ~(MSG_NEW | MSG_UNREAD);
1851 if ((perm_flags & MSG_WATCH_THREAD) || (perm_flags_old & MSG_WATCH_THREAD)) {
1852 perm_flags_new &= ~(MSG_IGNORE_THREAD);
1855 if (perm_flags_old != perm_flags_new) {
1856 folder_item_change_msg_flags(msginfo->folder, msginfo, perm_flags_new);
1858 update_folder_msg_counts(item, msginfo, perm_flags_old);
1862 /* Tmp flags handling */
1863 tmp_flags_old = msginfo->flags.tmp_flags;
1864 msginfo->flags.tmp_flags |= tmp_flags;
1866 /* update notification */
1867 if ((perm_flags_old != perm_flags_new) || (tmp_flags_old != msginfo->flags.tmp_flags)) {
1868 msginfo_update.msginfo = msginfo;
1869 msginfo_update.flags = MSGINFO_UPDATE_FLAGS;
1870 hooks_invoke(MSGINFO_UPDATE_HOOKLIST, &msginfo_update);
1871 folder_item_update(msginfo->folder, F_ITEM_UPDATE_MSGCNT);
1875 void procmsg_msginfo_unset_flags(MsgInfo *msginfo, MsgPermFlags perm_flags, MsgTmpFlags tmp_flags)
1878 MsgInfoUpdate msginfo_update;
1879 MsgPermFlags perm_flags_new, perm_flags_old;
1880 MsgTmpFlags tmp_flags_old;
1882 g_return_if_fail(msginfo != NULL);
1883 item = msginfo->folder;
1884 g_return_if_fail(item != NULL);
1886 debug_print("Unsetting flags for message %d in folder %s\n", msginfo->msgnum, item->path);
1888 /* Perm Flags handling */
1889 perm_flags_old = msginfo->flags.perm_flags;
1890 perm_flags_new = msginfo->flags.perm_flags & ~perm_flags;
1892 if (perm_flags_old != perm_flags_new) {
1893 folder_item_change_msg_flags(msginfo->folder, msginfo, perm_flags_new);
1895 update_folder_msg_counts(item, msginfo, perm_flags_old);
1898 /* Tmp flags hanlding */
1899 tmp_flags_old = msginfo->flags.tmp_flags;
1900 msginfo->flags.tmp_flags &= ~tmp_flags;
1902 /* update notification */
1903 if ((perm_flags_old != perm_flags_new) || (tmp_flags_old != msginfo->flags.tmp_flags)) {
1904 msginfo_update.msginfo = msginfo;
1905 msginfo_update.flags = MSGINFO_UPDATE_FLAGS;
1906 hooks_invoke(MSGINFO_UPDATE_HOOKLIST, &msginfo_update);
1907 folder_item_update(msginfo->folder, F_ITEM_UPDATE_MSGCNT);
1911 void procmsg_msginfo_change_flags(MsgInfo *msginfo,
1912 MsgPermFlags add_perm_flags, MsgTmpFlags add_tmp_flags,
1913 MsgPermFlags rem_perm_flags, MsgTmpFlags rem_tmp_flags)
1916 MsgInfoUpdate msginfo_update;
1917 MsgPermFlags perm_flags_new, perm_flags_old;
1918 MsgTmpFlags tmp_flags_old;
1920 g_return_if_fail(msginfo != NULL);
1921 item = msginfo->folder;
1922 g_return_if_fail(item != NULL);
1924 debug_print("Changing flags for message %d in folder %s\n", msginfo->msgnum, item->path);
1926 /* Perm Flags handling */
1927 perm_flags_old = msginfo->flags.perm_flags;
1928 perm_flags_new = (msginfo->flags.perm_flags & ~rem_perm_flags) | add_perm_flags;
1929 if ((add_perm_flags & MSG_IGNORE_THREAD) || (perm_flags_old & MSG_IGNORE_THREAD)) {
1930 perm_flags_new &= ~(MSG_NEW | MSG_UNREAD);
1932 if ((add_perm_flags & MSG_WATCH_THREAD) || (perm_flags_old & MSG_WATCH_THREAD)) {
1933 perm_flags_new &= ~(MSG_IGNORE_THREAD);
1936 if (perm_flags_old != perm_flags_new) {
1937 folder_item_change_msg_flags(msginfo->folder, msginfo, perm_flags_new);
1939 update_folder_msg_counts(item, msginfo, perm_flags_old);
1943 /* Tmp flags handling */
1944 tmp_flags_old = msginfo->flags.tmp_flags;
1945 msginfo->flags.tmp_flags &= ~rem_tmp_flags;
1946 msginfo->flags.tmp_flags |= add_tmp_flags;
1948 /* update notification */
1949 if ((perm_flags_old != perm_flags_new) || (tmp_flags_old != msginfo->flags.tmp_flags)) {
1950 msginfo_update.msginfo = msginfo;
1951 msginfo_update.flags = MSGINFO_UPDATE_FLAGS;
1952 hooks_invoke(MSGINFO_UPDATE_HOOKLIST, &msginfo_update);
1953 folder_item_update(msginfo->folder, F_ITEM_UPDATE_MSGCNT);
1958 *\brief check for flags (e.g. mark) in prior msgs of current thread
1960 *\param info Current message
1961 *\param perm_flags Flags to be checked
1962 *\param parentmsgs Hash of prior msgs to avoid loops
1964 *\return gboolean TRUE if perm_flags are found
1966 static gboolean procmsg_msg_has_flagged_parent_real(MsgInfo *info,
1967 MsgPermFlags perm_flags, GHashTable *parentmsgs)
1971 g_return_val_if_fail(info != NULL, FALSE);
1973 if (info != NULL && info->folder != NULL && info->inreplyto != NULL) {
1974 tmp = folder_item_get_msginfo_by_msgid(info->folder,
1976 if (tmp && (tmp->flags.perm_flags & perm_flags)) {
1977 procmsg_msginfo_free(tmp);
1979 } else if (tmp != NULL) {
1982 if (g_hash_table_lookup(parentmsgs, info)) {
1983 debug_print("loop detected: %d\n",
1987 g_hash_table_insert(parentmsgs, info, "1");
1988 result = procmsg_msg_has_flagged_parent_real(
1989 tmp, perm_flags, parentmsgs);
1991 procmsg_msginfo_free(tmp);
2001 *\brief Callback for cleaning up hash of parentmsgs
2003 static gboolean parentmsgs_hash_remove(gpointer key,
2011 *\brief Set up list of parentmsgs
2012 * See procmsg_msg_has_flagged_parent_real()
2014 gboolean procmsg_msg_has_flagged_parent(MsgInfo *info, MsgPermFlags perm_flags)
2017 static GHashTable *parentmsgs = NULL;
2019 if (parentmsgs == NULL)
2020 parentmsgs = g_hash_table_new(NULL, NULL);
2022 result = procmsg_msg_has_flagged_parent_real(info, perm_flags, parentmsgs);
2023 g_hash_table_foreach_remove(parentmsgs, parentmsgs_hash_remove, NULL);
2029 *\brief Check if msgs prior in thread are marked
2030 * See procmsg_msg_has_flagged_parent_real()
2032 gboolean procmsg_msg_has_marked_parent(MsgInfo *info)
2034 return procmsg_msg_has_flagged_parent(info, MSG_MARKED);
2038 static GSList *procmsg_find_children_func(MsgInfo *info,
2039 GSList *children, GSList *all)
2043 g_return_val_if_fail(info!=NULL, children);
2044 if (info->msgid == NULL)
2047 for (cur = all; cur != NULL; cur = g_slist_next(cur)) {
2048 MsgInfo *tmp = (MsgInfo *)cur->data;
2049 if (tmp->inreplyto && !strcmp(tmp->inreplyto, info->msgid)) {
2050 /* Check if message is already in the list */
2051 if ((children == NULL) ||
2052 (g_slist_index(children, tmp) == -1)) {
2053 children = g_slist_prepend(children,
2054 procmsg_msginfo_new_ref(tmp));
2055 children = procmsg_find_children_func(tmp,
2064 static GSList *procmsg_find_children (MsgInfo *info)
2069 g_return_val_if_fail(info!=NULL, NULL);
2070 all = folder_item_get_msg_list(info->folder);
2071 children = procmsg_find_children_func(info, NULL, all);
2072 if (children != NULL) {
2073 for (cur = all; cur != NULL; cur = g_slist_next(cur)) {
2074 /* this will not free the used pointers
2075 created with procmsg_msginfo_new_ref */
2076 procmsg_msginfo_free((MsgInfo *)cur->data);
2084 static void procmsg_update_unread_children(MsgInfo *info, gboolean newly_marked)
2086 GSList *children = procmsg_find_children(info);
2088 for (cur = children; cur != NULL; cur = g_slist_next(cur)) {
2089 MsgInfo *tmp = (MsgInfo *)cur->data;
2090 if(MSG_IS_UNREAD(tmp->flags) && !MSG_IS_IGNORE_THREAD(tmp->flags)) {
2092 info->folder->unreadmarked_msgs++;
2094 info->folder->unreadmarked_msgs--;
2095 folder_item_update(info->folder, F_ITEM_UPDATE_MSGCNT);
2097 procmsg_msginfo_free(tmp);
2099 g_slist_free(children);
2103 * Set the destination folder for a copy or move operation
2105 * \param msginfo The message which's destination folder is changed
2106 * \param to_folder The destination folder for the operation
2108 void procmsg_msginfo_set_to_folder(MsgInfo *msginfo, FolderItem *to_folder)
2110 if(msginfo->to_folder != NULL) {
2111 msginfo->to_folder->op_count--;
2112 folder_item_update(msginfo->to_folder, F_ITEM_UPDATE_MSGCNT);
2114 msginfo->to_folder = to_folder;
2115 if(to_folder != NULL) {
2116 to_folder->op_count++;
2117 folder_item_update(msginfo->to_folder, F_ITEM_UPDATE_MSGCNT);
2122 * Apply filtering actions to the msginfo
2124 * \param msginfo The MsgInfo describing the message that should be filtered
2125 * \return TRUE if the message was moved and MsgInfo is now invalid,
2128 static gboolean procmsg_msginfo_filter(MsgInfo *msginfo, PrefsAccount* ac_prefs)
2130 MailFilteringData mail_filtering_data;
2132 mail_filtering_data.msginfo = msginfo;
2133 mail_filtering_data.msglist = NULL;
2134 mail_filtering_data.filtered = NULL;
2135 mail_filtering_data.unfiltered = NULL;
2136 mail_filtering_data.account = ac_prefs;
2138 if (!ac_prefs || ac_prefs->filterhook_on_recv)
2139 if (hooks_invoke(MAIL_FILTERING_HOOKLIST, &mail_filtering_data))
2142 /* filter if enabled in prefs or move to inbox if not */
2143 if((filtering_rules != NULL) &&
2144 filter_message_by_msginfo(filtering_rules, msginfo, ac_prefs,
2145 FILTERING_INCORPORATION, NULL)) {
2152 void procmsg_msglist_filter(GSList *list, PrefsAccount *ac,
2153 GSList **filtered, GSList **unfiltered,
2156 GSList *cur, *to_do = NULL;
2157 gint total = 0, curnum = 0;
2158 MailFilteringData mail_filtering_data;
2160 g_return_if_fail(filtered != NULL);
2161 g_return_if_fail(unfiltered != NULL);
2169 total = g_slist_length(list);
2173 *unfiltered = g_slist_copy(list);
2177 statusbar_print_all(_("Filtering messages...\n"));
2179 mail_filtering_data.msginfo = NULL;
2180 mail_filtering_data.msglist = list;
2181 mail_filtering_data.filtered = NULL;
2182 mail_filtering_data.unfiltered = NULL;
2183 mail_filtering_data.account = ac;
2185 if (!ac || ac->filterhook_on_recv)
2186 hooks_invoke(MAIL_LISTFILTERING_HOOKLIST, &mail_filtering_data);
2188 if (mail_filtering_data.filtered == NULL &&
2189 mail_filtering_data.unfiltered == NULL) {
2190 /* nothing happened */
2191 debug_print(MAIL_LISTFILTERING_HOOKLIST " did nothing. filtering whole list normally.\n");
2194 if (mail_filtering_data.filtered != NULL) {
2195 /* keep track of what's been filtered by the hooks */
2196 debug_print(MAIL_LISTFILTERING_HOOKLIST " filtered some stuff. total %d filtered %d unfilt %d.\n",
2197 g_slist_length(list),
2198 g_slist_length(mail_filtering_data.filtered),
2199 g_slist_length(mail_filtering_data.unfiltered));
2201 *filtered = g_slist_copy(mail_filtering_data.filtered);
2203 if (mail_filtering_data.unfiltered != NULL) {
2204 /* what the hooks didn't handle will go in filtered or
2205 * unfiltered in the next loop */
2206 debug_print(MAIL_LISTFILTERING_HOOKLIST " left unfiltered stuff. total %d filtered %d unfilt %d.\n",
2207 g_slist_length(list),
2208 g_slist_length(mail_filtering_data.filtered),
2209 g_slist_length(mail_filtering_data.unfiltered));
2210 to_do = mail_filtering_data.unfiltered;
2213 for (cur = to_do; cur; cur = cur->next) {
2214 MsgInfo *info = (MsgInfo *)cur->data;
2215 if (procmsg_msginfo_filter(info, ac))
2216 *filtered = g_slist_prepend(*filtered, info);
2218 *unfiltered = g_slist_prepend(*unfiltered, info);
2219 statusbar_progress_all(curnum++, total, prefs_common.statusbar_update_step);
2222 g_slist_free(mail_filtering_data.filtered);
2223 g_slist_free(mail_filtering_data.unfiltered);
2225 *filtered = g_slist_reverse(*filtered);
2226 *unfiltered = g_slist_reverse(*unfiltered);
2228 statusbar_progress_all(0,0,0);
2229 statusbar_pop_all();
2232 MsgInfo *procmsg_msginfo_new_from_mimeinfo(MsgInfo *src_msginfo, MimeInfo *mimeinfo)
2234 MsgInfo *tmp_msginfo = NULL;
2235 MsgFlags flags = {0, 0};
2236 gchar *tmpfile = get_tmp_file();
2237 FILE *fp = g_fopen(tmpfile, "wb");
2239 if (!mimeinfo || mimeinfo->type != MIMETYPE_MESSAGE ||
2240 g_ascii_strcasecmp(mimeinfo->subtype, "rfc822")) {
2241 g_warning("procmsg_msginfo_new_from_mimeinfo(): unsuitable mimeinfo");
2248 if (fp && procmime_write_mimeinfo(mimeinfo, fp) >= 0) {
2251 tmp_msginfo = procheader_parse_file(
2258 if (tmp_msginfo != NULL) {
2260 tmp_msginfo->folder = src_msginfo->folder;
2261 tmp_msginfo->plaintext_file = g_strdup(tmpfile);
2263 g_warning("procmsg_msginfo_new_from_mimeinfo(): Can't generate new msginfo");
2271 static GSList *spam_learners = NULL;
2273 void procmsg_register_spam_learner (int (*learn_func)(MsgInfo *info, GSList *list, gboolean spam))
2275 if (!g_slist_find(spam_learners, learn_func))
2276 spam_learners = g_slist_append(spam_learners, learn_func);
2277 if (mainwindow_get_mainwindow()) {
2278 main_window_set_menu_sensitive(mainwindow_get_mainwindow());
2279 summary_set_menu_sensitive(
2280 mainwindow_get_mainwindow()->summaryview);
2281 toolbar_main_set_sensitive(mainwindow_get_mainwindow());
2285 void procmsg_unregister_spam_learner (int (*learn_func)(MsgInfo *info, GSList *list, gboolean spam))
2287 spam_learners = g_slist_remove(spam_learners, learn_func);
2288 if (mainwindow_get_mainwindow()) {
2289 main_window_set_menu_sensitive(mainwindow_get_mainwindow());
2290 summary_set_menu_sensitive(
2291 mainwindow_get_mainwindow()->summaryview);
2292 toolbar_main_set_sensitive(mainwindow_get_mainwindow());
2296 gboolean procmsg_spam_can_learn(void)
2298 return g_slist_length(spam_learners) > 0;
2301 int procmsg_spam_learner_learn (MsgInfo *info, GSList *list, gboolean spam)
2303 GSList *cur = spam_learners;
2305 for (; cur; cur = cur->next) {
2306 int ((*func)(MsgInfo *info, GSList *list, gboolean spam)) = cur->data;
2307 ret |= func(info, list, spam);
2312 static gchar *spam_folder_item = NULL;
2313 static FolderItem * (*procmsg_spam_get_folder_func)(MsgInfo *msginfo) = NULL;
2314 void procmsg_spam_set_folder (const char *item_identifier, FolderItem *(*spam_get_folder_func)(MsgInfo *info))
2316 g_free(spam_folder_item);
2317 if (item_identifier)
2318 spam_folder_item = g_strdup(item_identifier);
2320 spam_folder_item = NULL;
2321 if (spam_get_folder_func != NULL)
2322 procmsg_spam_get_folder_func = spam_get_folder_func;
2324 procmsg_spam_get_folder_func = NULL;
2327 FolderItem *procmsg_spam_get_folder (MsgInfo *msginfo)
2329 FolderItem *item = NULL;
2331 if (procmsg_spam_get_folder_func)
2332 item = procmsg_spam_get_folder_func(msginfo);
2333 if (item == NULL && spam_folder_item)
2334 item = folder_find_item_from_identifier(spam_folder_item);
2336 item = folder_get_default_trash();
2340 static void item_has_queued_mails(FolderItem *item, gpointer data)
2342 gboolean *result = (gboolean *)data;
2343 if (*result == TRUE)
2345 if (folder_has_parent_of_type(item, F_QUEUE) && item->total_msgs > 0)
2349 gboolean procmsg_have_queued_mails_fast (void)
2351 gboolean result = FALSE;
2352 folder_func_to_all_folders(item_has_queued_mails, &result);
2356 static void item_has_trashed_mails(FolderItem *item, gpointer data)
2358 gboolean *result = (gboolean *)data;
2359 if (*result == TRUE)
2361 if (folder_has_parent_of_type(item, F_TRASH) && item->total_msgs > 0)
2365 gboolean procmsg_have_trashed_mails_fast (void)
2367 gboolean result = FALSE;
2368 folder_func_to_all_folders(item_has_trashed_mails, &result);
2372 gchar *procmsg_msginfo_get_tags_str(MsgInfo *msginfo)
2380 if (msginfo->tags == NULL)
2382 for (cur = msginfo->tags; cur; cur = cur->next) {
2383 const gchar *tag = tags_get_tag(GPOINTER_TO_INT(cur->data));
2387 tags = g_strdup(tag);
2389 int olen = strlen(tags);
2390 int nlen = olen + strlen(tag) + 2 /* strlen(", ") */;
2391 tags = g_realloc(tags, nlen+1);
2394 strcpy(tags+olen, ", ");
2395 strcpy(tags+olen+2, tag);
2402 void procmsg_msginfo_update_tags(MsgInfo *msginfo, gboolean set, gint id)
2405 msginfo->tags = g_slist_remove(
2407 GINT_TO_POINTER(id));
2409 if (!g_slist_find(msginfo->tags, GINT_TO_POINTER(id)))
2410 msginfo->tags = g_slist_append(
2412 GINT_TO_POINTER(id));
2416 void procmsg_msginfo_clear_tags(MsgInfo *msginfo)
2418 g_slist_free(msginfo->tags);
2419 msginfo->tags = NULL;