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);
1852 if (perm_flags_old != perm_flags_new) {
1853 folder_item_change_msg_flags(msginfo->folder, msginfo, perm_flags_new);
1855 update_folder_msg_counts(item, msginfo, perm_flags_old);
1859 /* Tmp flags handling */
1860 tmp_flags_old = msginfo->flags.tmp_flags;
1861 msginfo->flags.tmp_flags |= tmp_flags;
1863 /* update notification */
1864 if ((perm_flags_old != perm_flags_new) || (tmp_flags_old != msginfo->flags.tmp_flags)) {
1865 msginfo_update.msginfo = msginfo;
1866 msginfo_update.flags = MSGINFO_UPDATE_FLAGS;
1867 hooks_invoke(MSGINFO_UPDATE_HOOKLIST, &msginfo_update);
1868 folder_item_update(msginfo->folder, F_ITEM_UPDATE_MSGCNT);
1872 void procmsg_msginfo_unset_flags(MsgInfo *msginfo, MsgPermFlags perm_flags, MsgTmpFlags tmp_flags)
1875 MsgInfoUpdate msginfo_update;
1876 MsgPermFlags perm_flags_new, perm_flags_old;
1877 MsgTmpFlags tmp_flags_old;
1879 g_return_if_fail(msginfo != NULL);
1880 item = msginfo->folder;
1881 g_return_if_fail(item != NULL);
1883 debug_print("Unsetting flags for message %d in folder %s\n", msginfo->msgnum, item->path);
1885 /* Perm Flags handling */
1886 perm_flags_old = msginfo->flags.perm_flags;
1887 perm_flags_new = msginfo->flags.perm_flags & ~perm_flags;
1889 if (perm_flags_old != perm_flags_new) {
1890 folder_item_change_msg_flags(msginfo->folder, msginfo, perm_flags_new);
1892 update_folder_msg_counts(item, msginfo, perm_flags_old);
1895 /* Tmp flags hanlding */
1896 tmp_flags_old = msginfo->flags.tmp_flags;
1897 msginfo->flags.tmp_flags &= ~tmp_flags;
1899 /* update notification */
1900 if ((perm_flags_old != perm_flags_new) || (tmp_flags_old != msginfo->flags.tmp_flags)) {
1901 msginfo_update.msginfo = msginfo;
1902 msginfo_update.flags = MSGINFO_UPDATE_FLAGS;
1903 hooks_invoke(MSGINFO_UPDATE_HOOKLIST, &msginfo_update);
1904 folder_item_update(msginfo->folder, F_ITEM_UPDATE_MSGCNT);
1908 void procmsg_msginfo_change_flags(MsgInfo *msginfo,
1909 MsgPermFlags add_perm_flags, MsgTmpFlags add_tmp_flags,
1910 MsgPermFlags rem_perm_flags, MsgTmpFlags rem_tmp_flags)
1913 MsgInfoUpdate msginfo_update;
1914 MsgPermFlags perm_flags_new, perm_flags_old;
1915 MsgTmpFlags tmp_flags_old;
1917 g_return_if_fail(msginfo != NULL);
1918 item = msginfo->folder;
1919 g_return_if_fail(item != NULL);
1921 debug_print("Changing flags for message %d in folder %s\n", msginfo->msgnum, item->path);
1923 /* Perm Flags handling */
1924 perm_flags_old = msginfo->flags.perm_flags;
1925 perm_flags_new = (msginfo->flags.perm_flags & ~rem_perm_flags) | add_perm_flags;
1926 if ((add_perm_flags & MSG_IGNORE_THREAD) || (perm_flags_old & MSG_IGNORE_THREAD)) {
1927 perm_flags_new &= ~(MSG_NEW | MSG_UNREAD);
1930 if (perm_flags_old != perm_flags_new) {
1931 folder_item_change_msg_flags(msginfo->folder, msginfo, perm_flags_new);
1933 update_folder_msg_counts(item, msginfo, perm_flags_old);
1937 /* Tmp flags handling */
1938 tmp_flags_old = msginfo->flags.tmp_flags;
1939 msginfo->flags.tmp_flags &= ~rem_tmp_flags;
1940 msginfo->flags.tmp_flags |= add_tmp_flags;
1942 /* update notification */
1943 if ((perm_flags_old != perm_flags_new) || (tmp_flags_old != msginfo->flags.tmp_flags)) {
1944 msginfo_update.msginfo = msginfo;
1945 msginfo_update.flags = MSGINFO_UPDATE_FLAGS;
1946 hooks_invoke(MSGINFO_UPDATE_HOOKLIST, &msginfo_update);
1947 folder_item_update(msginfo->folder, F_ITEM_UPDATE_MSGCNT);
1952 *\brief check for flags (e.g. mark) in prior msgs of current thread
1954 *\param info Current message
1955 *\param perm_flags Flags to be checked
1956 *\param parentmsgs Hash of prior msgs to avoid loops
1958 *\return gboolean TRUE if perm_flags are found
1960 static gboolean procmsg_msg_has_flagged_parent_real(MsgInfo *info,
1961 MsgPermFlags perm_flags, GHashTable *parentmsgs)
1965 g_return_val_if_fail(info != NULL, FALSE);
1967 if (info != NULL && info->folder != NULL && info->inreplyto != NULL) {
1968 tmp = folder_item_get_msginfo_by_msgid(info->folder,
1970 if (tmp && (tmp->flags.perm_flags & perm_flags)) {
1971 procmsg_msginfo_free(tmp);
1973 } else if (tmp != NULL) {
1976 if (g_hash_table_lookup(parentmsgs, info)) {
1977 debug_print("loop detected: %d\n",
1981 g_hash_table_insert(parentmsgs, info, "1");
1982 result = procmsg_msg_has_flagged_parent_real(
1983 tmp, perm_flags, parentmsgs);
1985 procmsg_msginfo_free(tmp);
1995 *\brief Callback for cleaning up hash of parentmsgs
1997 static gboolean parentmsgs_hash_remove(gpointer key,
2005 *\brief Set up list of parentmsgs
2006 * See procmsg_msg_has_flagged_parent_real()
2008 gboolean procmsg_msg_has_flagged_parent(MsgInfo *info, MsgPermFlags perm_flags)
2011 static GHashTable *parentmsgs = NULL;
2013 if (parentmsgs == NULL)
2014 parentmsgs = g_hash_table_new(NULL, NULL);
2016 result = procmsg_msg_has_flagged_parent_real(info, perm_flags, parentmsgs);
2017 g_hash_table_foreach_remove(parentmsgs, parentmsgs_hash_remove, NULL);
2023 *\brief Check if msgs prior in thread are marked
2024 * See procmsg_msg_has_flagged_parent_real()
2026 gboolean procmsg_msg_has_marked_parent(MsgInfo *info)
2028 return procmsg_msg_has_flagged_parent(info, MSG_MARKED);
2032 static GSList *procmsg_find_children_func(MsgInfo *info,
2033 GSList *children, GSList *all)
2037 g_return_val_if_fail(info!=NULL, children);
2038 if (info->msgid == NULL)
2041 for (cur = all; cur != NULL; cur = g_slist_next(cur)) {
2042 MsgInfo *tmp = (MsgInfo *)cur->data;
2043 if (tmp->inreplyto && !strcmp(tmp->inreplyto, info->msgid)) {
2044 /* Check if message is already in the list */
2045 if ((children == NULL) ||
2046 (g_slist_index(children, tmp) == -1)) {
2047 children = g_slist_prepend(children,
2048 procmsg_msginfo_new_ref(tmp));
2049 children = procmsg_find_children_func(tmp,
2058 static GSList *procmsg_find_children (MsgInfo *info)
2063 g_return_val_if_fail(info!=NULL, NULL);
2064 all = folder_item_get_msg_list(info->folder);
2065 children = procmsg_find_children_func(info, NULL, all);
2066 if (children != NULL) {
2067 for (cur = all; cur != NULL; cur = g_slist_next(cur)) {
2068 /* this will not free the used pointers
2069 created with procmsg_msginfo_new_ref */
2070 procmsg_msginfo_free((MsgInfo *)cur->data);
2078 static void procmsg_update_unread_children(MsgInfo *info, gboolean newly_marked)
2080 GSList *children = procmsg_find_children(info);
2082 for (cur = children; cur != NULL; cur = g_slist_next(cur)) {
2083 MsgInfo *tmp = (MsgInfo *)cur->data;
2084 if(MSG_IS_UNREAD(tmp->flags) && !MSG_IS_IGNORE_THREAD(tmp->flags)) {
2086 info->folder->unreadmarked_msgs++;
2088 info->folder->unreadmarked_msgs--;
2089 folder_item_update(info->folder, F_ITEM_UPDATE_MSGCNT);
2091 procmsg_msginfo_free(tmp);
2093 g_slist_free(children);
2097 * Set the destination folder for a copy or move operation
2099 * \param msginfo The message which's destination folder is changed
2100 * \param to_folder The destination folder for the operation
2102 void procmsg_msginfo_set_to_folder(MsgInfo *msginfo, FolderItem *to_folder)
2104 if(msginfo->to_folder != NULL) {
2105 msginfo->to_folder->op_count--;
2106 folder_item_update(msginfo->to_folder, F_ITEM_UPDATE_MSGCNT);
2108 msginfo->to_folder = to_folder;
2109 if(to_folder != NULL) {
2110 to_folder->op_count++;
2111 folder_item_update(msginfo->to_folder, F_ITEM_UPDATE_MSGCNT);
2116 * Apply filtering actions to the msginfo
2118 * \param msginfo The MsgInfo describing the message that should be filtered
2119 * \return TRUE if the message was moved and MsgInfo is now invalid,
2122 static gboolean procmsg_msginfo_filter(MsgInfo *msginfo, PrefsAccount* ac_prefs)
2124 MailFilteringData mail_filtering_data;
2126 mail_filtering_data.msginfo = msginfo;
2127 mail_filtering_data.msglist = NULL;
2128 mail_filtering_data.filtered = NULL;
2129 mail_filtering_data.unfiltered = NULL;
2130 mail_filtering_data.account = ac_prefs;
2132 if (!ac_prefs || ac_prefs->filterhook_on_recv)
2133 if (hooks_invoke(MAIL_FILTERING_HOOKLIST, &mail_filtering_data))
2136 /* filter if enabled in prefs or move to inbox if not */
2137 if((filtering_rules != NULL) &&
2138 filter_message_by_msginfo(filtering_rules, msginfo, ac_prefs,
2139 FILTERING_INCORPORATION, NULL)) {
2146 void procmsg_msglist_filter(GSList *list, PrefsAccount *ac,
2147 GSList **filtered, GSList **unfiltered,
2150 GSList *cur, *to_do = NULL;
2151 gint total = 0, curnum = 0;
2152 MailFilteringData mail_filtering_data;
2154 g_return_if_fail(filtered != NULL);
2155 g_return_if_fail(unfiltered != NULL);
2163 total = g_slist_length(list);
2167 *unfiltered = g_slist_copy(list);
2171 statusbar_print_all(_("Filtering messages...\n"));
2173 mail_filtering_data.msginfo = NULL;
2174 mail_filtering_data.msglist = list;
2175 mail_filtering_data.filtered = NULL;
2176 mail_filtering_data.unfiltered = NULL;
2177 mail_filtering_data.account = ac;
2179 if (!ac || ac->filterhook_on_recv)
2180 hooks_invoke(MAIL_LISTFILTERING_HOOKLIST, &mail_filtering_data);
2182 if (mail_filtering_data.filtered == NULL &&
2183 mail_filtering_data.unfiltered == NULL) {
2184 /* nothing happened */
2185 debug_print(MAIL_LISTFILTERING_HOOKLIST " did nothing. filtering whole list normally.\n");
2188 if (mail_filtering_data.filtered != NULL) {
2189 /* keep track of what's been filtered by the hooks */
2190 debug_print(MAIL_LISTFILTERING_HOOKLIST " filtered some stuff. total %d filtered %d unfilt %d.\n",
2191 g_slist_length(list),
2192 g_slist_length(mail_filtering_data.filtered),
2193 g_slist_length(mail_filtering_data.unfiltered));
2195 *filtered = g_slist_copy(mail_filtering_data.filtered);
2197 if (mail_filtering_data.unfiltered != NULL) {
2198 /* what the hooks didn't handle will go in filtered or
2199 * unfiltered in the next loop */
2200 debug_print(MAIL_LISTFILTERING_HOOKLIST " left unfiltered stuff. total %d filtered %d unfilt %d.\n",
2201 g_slist_length(list),
2202 g_slist_length(mail_filtering_data.filtered),
2203 g_slist_length(mail_filtering_data.unfiltered));
2204 to_do = mail_filtering_data.unfiltered;
2207 for (cur = to_do; cur; cur = cur->next) {
2208 MsgInfo *info = (MsgInfo *)cur->data;
2209 if (procmsg_msginfo_filter(info, ac))
2210 *filtered = g_slist_prepend(*filtered, info);
2212 *unfiltered = g_slist_prepend(*unfiltered, info);
2213 statusbar_progress_all(curnum++, total, prefs_common.statusbar_update_step);
2216 g_slist_free(mail_filtering_data.filtered);
2217 g_slist_free(mail_filtering_data.unfiltered);
2219 *filtered = g_slist_reverse(*filtered);
2220 *unfiltered = g_slist_reverse(*unfiltered);
2222 statusbar_progress_all(0,0,0);
2223 statusbar_pop_all();
2226 MsgInfo *procmsg_msginfo_new_from_mimeinfo(MsgInfo *src_msginfo, MimeInfo *mimeinfo)
2228 MsgInfo *tmp_msginfo = NULL;
2229 MsgFlags flags = {0, 0};
2230 gchar *tmpfile = get_tmp_file();
2231 FILE *fp = g_fopen(tmpfile, "wb");
2233 if (!mimeinfo || mimeinfo->type != MIMETYPE_MESSAGE ||
2234 g_ascii_strcasecmp(mimeinfo->subtype, "rfc822")) {
2235 g_warning("procmsg_msginfo_new_from_mimeinfo(): unsuitable mimeinfo");
2242 if (fp && procmime_write_mimeinfo(mimeinfo, fp) >= 0) {
2245 tmp_msginfo = procheader_parse_file(
2252 if (tmp_msginfo != NULL) {
2254 tmp_msginfo->folder = src_msginfo->folder;
2255 tmp_msginfo->plaintext_file = g_strdup(tmpfile);
2257 g_warning("procmsg_msginfo_new_from_mimeinfo(): Can't generate new msginfo");
2265 static GSList *spam_learners = NULL;
2267 void procmsg_register_spam_learner (int (*learn_func)(MsgInfo *info, GSList *list, gboolean spam))
2269 if (!g_slist_find(spam_learners, learn_func))
2270 spam_learners = g_slist_append(spam_learners, learn_func);
2271 if (mainwindow_get_mainwindow()) {
2272 main_window_set_menu_sensitive(mainwindow_get_mainwindow());
2273 summary_set_menu_sensitive(
2274 mainwindow_get_mainwindow()->summaryview);
2275 toolbar_main_set_sensitive(mainwindow_get_mainwindow());
2279 void procmsg_unregister_spam_learner (int (*learn_func)(MsgInfo *info, GSList *list, gboolean spam))
2281 spam_learners = g_slist_remove(spam_learners, learn_func);
2282 if (mainwindow_get_mainwindow()) {
2283 main_window_set_menu_sensitive(mainwindow_get_mainwindow());
2284 summary_set_menu_sensitive(
2285 mainwindow_get_mainwindow()->summaryview);
2286 toolbar_main_set_sensitive(mainwindow_get_mainwindow());
2290 gboolean procmsg_spam_can_learn(void)
2292 return g_slist_length(spam_learners) > 0;
2295 int procmsg_spam_learner_learn (MsgInfo *info, GSList *list, gboolean spam)
2297 GSList *cur = spam_learners;
2299 for (; cur; cur = cur->next) {
2300 int ((*func)(MsgInfo *info, GSList *list, gboolean spam)) = cur->data;
2301 ret |= func(info, list, spam);
2306 static gchar *spam_folder_item = NULL;
2307 static FolderItem * (*procmsg_spam_get_folder_func)(MsgInfo *msginfo) = NULL;
2308 void procmsg_spam_set_folder (const char *item_identifier, FolderItem *(*spam_get_folder_func)(MsgInfo *info))
2310 g_free(spam_folder_item);
2311 if (item_identifier)
2312 spam_folder_item = g_strdup(item_identifier);
2314 spam_folder_item = NULL;
2315 if (spam_get_folder_func != NULL)
2316 procmsg_spam_get_folder_func = spam_get_folder_func;
2318 procmsg_spam_get_folder_func = NULL;
2321 FolderItem *procmsg_spam_get_folder (MsgInfo *msginfo)
2323 FolderItem *item = NULL;
2325 if (procmsg_spam_get_folder_func)
2326 item = procmsg_spam_get_folder_func(msginfo);
2327 if (item == NULL && spam_folder_item)
2328 item = folder_find_item_from_identifier(spam_folder_item);
2330 item = folder_get_default_trash();
2334 static void item_has_queued_mails(FolderItem *item, gpointer data)
2336 gboolean *result = (gboolean *)data;
2337 if (*result == TRUE)
2339 if (folder_has_parent_of_type(item, F_QUEUE) && item->total_msgs > 0)
2343 gboolean procmsg_have_queued_mails_fast (void)
2345 gboolean result = FALSE;
2346 folder_func_to_all_folders(item_has_queued_mails, &result);
2350 static void item_has_trashed_mails(FolderItem *item, gpointer data)
2352 gboolean *result = (gboolean *)data;
2353 if (*result == TRUE)
2355 if (folder_has_parent_of_type(item, F_TRASH) && item->total_msgs > 0)
2359 gboolean procmsg_have_trashed_mails_fast (void)
2361 gboolean result = FALSE;
2362 folder_func_to_all_folders(item_has_trashed_mails, &result);
2366 gchar *procmsg_msginfo_get_tags_str(MsgInfo *msginfo)
2374 if (msginfo->tags == NULL)
2376 for (cur = msginfo->tags; cur; cur = cur->next) {
2377 const gchar *tag = tags_get_tag(GPOINTER_TO_INT(cur->data));
2381 tags = g_strdup(tag);
2383 int olen = strlen(tags);
2384 int nlen = olen + strlen(tag) + 2 /* strlen(", ") */;
2385 tags = g_realloc(tags, nlen+1);
2388 strcpy(tags+olen, ", ");
2389 strcpy(tags+olen+2, tag);
2396 void procmsg_msginfo_update_tags(MsgInfo *msginfo, gboolean set, gint id)
2399 msginfo->tags = g_slist_remove(
2401 GINT_TO_POINTER(id));
2403 if (!g_slist_find(msginfo->tags, GINT_TO_POINTER(id)))
2404 msginfo->tags = g_slist_append(
2406 GINT_TO_POINTER(id));
2410 void procmsg_msginfo_clear_tags(MsgInfo *msginfo)
2412 g_slist_free(msginfo->tags);
2413 msginfo->tags = NULL;