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_from_file(MsgInfo *msginfo, const gchar *file)
1223 MsgInfo *full_msginfo;
1225 if (msginfo == NULL) return NULL;
1227 if (!file || !is_file_exist(file)) {
1228 g_warning("procmsg_msginfo_get_full_info_from_file(): can't get message file.\n");
1232 full_msginfo = procheader_parse_file(file, msginfo->flags, TRUE, FALSE);
1233 if (!full_msginfo) return NULL;
1235 msginfo->total_size = full_msginfo->total_size;
1236 msginfo->planned_download = full_msginfo->planned_download;
1238 if (full_msginfo->extradata) {
1239 if (!msginfo->extradata)
1240 msginfo->extradata = g_new0(MsgInfoExtraData, 1);
1241 if (!msginfo->extradata->list_post)
1242 msginfo->extradata->list_post = g_strdup(full_msginfo->extradata->list_post);
1243 if (!msginfo->extradata->list_subscribe)
1244 msginfo->extradata->list_subscribe = g_strdup(full_msginfo->extradata->list_subscribe);
1245 if (!msginfo->extradata->list_unsubscribe)
1246 msginfo->extradata->list_unsubscribe = g_strdup(full_msginfo->extradata->list_unsubscribe);
1247 if (!msginfo->extradata->list_help)
1248 msginfo->extradata->list_help = g_strdup(full_msginfo->extradata->list_help);
1249 if (!msginfo->extradata->list_archive)
1250 msginfo->extradata->list_archive= g_strdup(full_msginfo->extradata->list_archive);
1251 if (!msginfo->extradata->list_owner)
1252 msginfo->extradata->list_owner = g_strdup(full_msginfo->extradata->list_owner);
1253 if (!msginfo->extradata->xface)
1254 msginfo->extradata->xface = g_strdup(full_msginfo->extradata->xface);
1255 if (!msginfo->extradata->face)
1256 msginfo->extradata->face = g_strdup(full_msginfo->extradata->face);
1257 if (!msginfo->extradata->dispositionnotificationto)
1258 msginfo->extradata->dispositionnotificationto =
1259 g_strdup(full_msginfo->extradata->dispositionnotificationto);
1260 if (!msginfo->extradata->returnreceiptto)
1261 msginfo->extradata->returnreceiptto = g_strdup
1262 (full_msginfo->extradata->returnreceiptto);
1263 if (!msginfo->extradata->partial_recv && full_msginfo->extradata->partial_recv)
1264 msginfo->extradata->partial_recv = g_strdup
1265 (full_msginfo->extradata->partial_recv);
1266 if (!msginfo->extradata->account_server && full_msginfo->extradata->account_server)
1267 msginfo->extradata->account_server = g_strdup
1268 (full_msginfo->extradata->account_server);
1269 if (!msginfo->extradata->account_login && full_msginfo->extradata->account_login)
1270 msginfo->extradata->account_login = g_strdup
1271 (full_msginfo->extradata->account_login);
1273 procmsg_msginfo_free(full_msginfo);
1275 return procmsg_msginfo_new_ref(msginfo);
1278 MsgInfo *procmsg_msginfo_get_full_info(MsgInfo *msginfo)
1280 MsgInfo *full_msginfo;
1283 if (msginfo == NULL) return NULL;
1285 file = procmsg_get_message_file_path(msginfo);
1286 if (!file || !is_file_exist(file)) {
1288 file = procmsg_get_message_file(msginfo);
1290 if (!file || !is_file_exist(file)) {
1291 g_warning("procmsg_msginfo_get_full_info(): can't get message file.\n");
1295 full_msginfo = procmsg_msginfo_get_full_info_from_file(msginfo, file);
1297 return full_msginfo;
1300 void procmsg_msginfo_free(MsgInfo *msginfo)
1302 if (msginfo == NULL) return;
1305 if (msginfo->refcnt > 0)
1308 if (msginfo->to_folder) {
1309 msginfo->to_folder->op_count--;
1310 folder_item_update(msginfo->to_folder, F_ITEM_UPDATE_MSGCNT);
1313 g_free(msginfo->fromspace);
1315 g_free(msginfo->fromname);
1317 g_free(msginfo->date);
1318 g_free(msginfo->from);
1319 g_free(msginfo->to);
1320 g_free(msginfo->cc);
1321 g_free(msginfo->newsgroups);
1322 g_free(msginfo->subject);
1323 g_free(msginfo->msgid);
1324 g_free(msginfo->inreplyto);
1325 g_free(msginfo->xref);
1327 if (msginfo->extradata) {
1328 g_free(msginfo->extradata->returnreceiptto);
1329 g_free(msginfo->extradata->dispositionnotificationto);
1330 g_free(msginfo->extradata->xface);
1331 g_free(msginfo->extradata->face);
1332 g_free(msginfo->extradata->list_post);
1333 g_free(msginfo->extradata->list_subscribe);
1334 g_free(msginfo->extradata->list_unsubscribe);
1335 g_free(msginfo->extradata->list_help);
1336 g_free(msginfo->extradata->list_archive);
1337 g_free(msginfo->extradata->list_owner);
1338 g_free(msginfo->extradata->partial_recv);
1339 g_free(msginfo->extradata->account_server);
1340 g_free(msginfo->extradata->account_login);
1341 g_free(msginfo->extradata);
1343 slist_free_strings(msginfo->references);
1344 g_slist_free(msginfo->references);
1345 g_slist_free(msginfo->tags);
1347 g_free(msginfo->plaintext_file);
1352 guint procmsg_msginfo_memusage(MsgInfo *msginfo)
1357 memusage += sizeof(MsgInfo);
1358 if (msginfo->fromname)
1359 memusage += strlen(msginfo->fromname);
1361 memusage += strlen(msginfo->date);
1363 memusage += strlen(msginfo->from);
1365 memusage += strlen(msginfo->to);
1367 memusage += strlen(msginfo->cc);
1368 if (msginfo->newsgroups)
1369 memusage += strlen(msginfo->newsgroups);
1370 if (msginfo->subject)
1371 memusage += strlen(msginfo->subject);
1373 memusage += strlen(msginfo->msgid);
1374 if (msginfo->inreplyto)
1375 memusage += strlen(msginfo->inreplyto);
1377 for (tmp = msginfo->references; tmp; tmp=tmp->next) {
1378 gchar *r = (gchar *)tmp->data;
1379 memusage += r?strlen(r):0 + sizeof(GSList);
1381 if (msginfo->fromspace)
1382 memusage += strlen(msginfo->fromspace);
1384 for (tmp = msginfo->tags; tmp; tmp=tmp->next) {
1385 memusage += sizeof(GSList);
1387 if (msginfo->extradata) {
1388 memusage += sizeof(MsgInfoExtraData);
1389 if (msginfo->extradata->xface)
1390 memusage += strlen(msginfo->extradata->xface);
1391 if (msginfo->extradata->face)
1392 memusage += strlen(msginfo->extradata->face);
1393 if (msginfo->extradata->dispositionnotificationto)
1394 memusage += strlen(msginfo->extradata->dispositionnotificationto);
1395 if (msginfo->extradata->returnreceiptto)
1396 memusage += strlen(msginfo->extradata->returnreceiptto);
1398 if (msginfo->extradata->partial_recv)
1399 memusage += strlen(msginfo->extradata->partial_recv);
1400 if (msginfo->extradata->account_server)
1401 memusage += strlen(msginfo->extradata->account_server);
1402 if (msginfo->extradata->account_login)
1403 memusage += strlen(msginfo->extradata->account_login);
1405 if (msginfo->extradata->list_post)
1406 memusage += strlen(msginfo->extradata->list_post);
1407 if (msginfo->extradata->list_subscribe)
1408 memusage += strlen(msginfo->extradata->list_subscribe);
1409 if (msginfo->extradata->list_unsubscribe)
1410 memusage += strlen(msginfo->extradata->list_unsubscribe);
1411 if (msginfo->extradata->list_help)
1412 memusage += strlen(msginfo->extradata->list_help);
1413 if (msginfo->extradata->list_archive)
1414 memusage += strlen(msginfo->extradata->list_archive);
1415 if (msginfo->extradata->list_owner)
1416 memusage += strlen(msginfo->extradata->list_owner);
1421 static gint procmsg_send_message_queue_full(const gchar *file, gboolean keep_session, gchar **errstr,
1422 FolderItem *queue, gint msgnum, gboolean *queued_removed)
1424 static HeaderEntry qentry[] = {{"S:", NULL, FALSE},
1425 {"SSV:", NULL, FALSE},
1426 {"R:", NULL, FALSE},
1427 {"NG:", NULL, FALSE},
1428 {"MAID:", NULL, FALSE},
1429 {"NAID:", NULL, FALSE},
1430 {"SCF:", NULL, FALSE},
1431 {"RMID:", NULL, FALSE},
1432 {"FMID:", NULL, FALSE},
1433 {"X-Claws-Privacy-System:", NULL, FALSE},
1434 {"X-Claws-Encrypt:", NULL, FALSE},
1435 {"X-Claws-Encrypt-Data:", NULL, FALSE},
1436 {"X-Claws-End-Special-Headers:", NULL, FALSE},
1437 {"X-Sylpheed-Privacy-System:", NULL, FALSE},
1438 {"X-Sylpheed-Encrypt:", NULL, FALSE},
1439 {"X-Sylpheed-Encrypt-Data:", NULL, FALSE},
1440 {"X-Sylpheed-End-Special-Headers:", NULL, FALSE},
1441 {NULL, NULL, FALSE}};
1444 gint mailval = 0, newsval = 0;
1446 gchar *smtpserver = NULL;
1447 GSList *to_list = NULL;
1448 GSList *newsgroup_list = NULL;
1449 gchar *savecopyfolder = NULL;
1450 gchar *replymessageid = NULL;
1451 gchar *fwdmessageid = NULL;
1452 gchar *privacy_system = NULL;
1453 gboolean encrypt = FALSE;
1454 gchar *encrypt_data = NULL;
1455 gchar buf[BUFFSIZE];
1457 PrefsAccount *mailac = NULL, *newsac = NULL;
1458 gboolean save_clear_text = TRUE;
1459 gchar *tmp_enc_file = NULL;
1463 g_return_val_if_fail(file != NULL, -1);
1465 if ((fp = g_fopen(file, "rb")) == NULL) {
1466 FILE_OP_ERROR(file, "fopen");
1468 if (*errstr) g_free(*errstr);
1469 *errstr = g_strdup_printf(_("Couldn't open file %s."), file);
1474 while ((hnum = procheader_get_one_field(buf, sizeof(buf), fp, qentry))
1476 gchar *p = buf + strlen(qentry[hnum].name);
1484 if (smtpserver == NULL)
1485 smtpserver = g_strdup(p);
1488 to_list = address_list_append(to_list, p);
1491 newsgroup_list = newsgroup_list_append(newsgroup_list, p);
1493 case Q_MAIL_ACCOUNT_ID:
1494 mailac = account_find_from_id(atoi(p));
1496 case Q_NEWS_ACCOUNT_ID:
1497 newsac = account_find_from_id(atoi(p));
1499 case Q_SAVE_COPY_FOLDER:
1500 if (savecopyfolder == NULL)
1501 savecopyfolder = g_strdup(p);
1503 case Q_REPLY_MESSAGE_ID:
1504 if (replymessageid == NULL)
1505 replymessageid = g_strdup(p);
1507 case Q_FWD_MESSAGE_ID:
1508 if (fwdmessageid == NULL)
1509 fwdmessageid = g_strdup(p);
1511 case Q_PRIVACY_SYSTEM:
1512 case Q_PRIVACY_SYSTEM_OLD:
1513 if (privacy_system == NULL)
1514 privacy_system = g_strdup(p);
1521 case Q_ENCRYPT_DATA:
1522 case Q_ENCRYPT_DATA_OLD:
1523 if (encrypt_data == NULL)
1524 encrypt_data = g_strdup(p);
1527 case Q_CLAWS_HDRS_OLD:
1528 /* end of special headers reached */
1529 goto send_mail; /* can't "break;break;" */
1533 filepos = ftell(fp);
1538 if (mailac && mailac->save_encrypted_as_clear_text
1539 && !mailac->encrypt_to_self)
1540 save_clear_text = TRUE;
1542 save_clear_text = FALSE;
1547 mimeinfo = procmime_scan_queue_file(file);
1548 if (!privacy_encrypt(privacy_system, mimeinfo, encrypt_data)
1549 || (fp = my_tmpfile()) == NULL
1550 || procmime_write_mimeinfo(mimeinfo, fp) < 0) {
1553 procmime_mimeinfo_free_all(mimeinfo);
1556 slist_free_strings(to_list);
1557 g_slist_free(to_list);
1558 slist_free_strings(newsgroup_list);
1559 g_slist_free(newsgroup_list);
1560 g_free(savecopyfolder);
1561 g_free(replymessageid);
1562 g_free(fwdmessageid);
1563 g_free(privacy_system);
1564 g_free(encrypt_data);
1566 if (*errstr) g_free(*errstr);
1567 *errstr = g_strdup_printf(_("Couldn't encrypt the email: %s"),
1568 privacy_get_error());
1574 if (!save_clear_text) {
1575 gchar *content = NULL;
1576 FILE *tmpfp = get_tmpfile_in_dir(get_mime_tmp_dir(), &tmp_enc_file);
1580 content = file_read_stream_to_str(fp);
1583 str_write_to_file(content, tmp_enc_file);
1586 g_warning("couldn't get tempfile\n");
1590 procmime_mimeinfo_free_all(mimeinfo);
1596 debug_print("Sending message by mail\n");
1599 if (*errstr) g_free(*errstr);
1600 *errstr = g_strdup_printf(_("Queued message header is broken."));
1603 } else if (mailac && mailac->use_mail_command &&
1604 mailac->mail_command && (* mailac->mail_command)) {
1605 mailval = send_message_local(mailac->mail_command, fp);
1609 mailac = account_find_from_smtp_server(from, smtpserver);
1611 g_warning("Account not found. "
1612 "Using current account...\n");
1613 mailac = cur_account;
1618 mailval = send_message_smtp_full(mailac, to_list, fp, keep_session);
1619 if (mailval == -1 && errstr) {
1620 if (*errstr) g_free(*errstr);
1621 *errstr = g_strdup_printf(_("An error happened during SMTP session."));
1624 PrefsAccount tmp_ac;
1626 g_warning("Account not found.\n");
1628 memset(&tmp_ac, 0, sizeof(PrefsAccount));
1629 tmp_ac.address = from;
1630 tmp_ac.smtp_server = smtpserver;
1631 tmp_ac.smtpport = SMTP_PORT;
1632 mailval = send_message_smtp(&tmp_ac, to_list, fp);
1633 if (mailval == -1 && errstr) {
1634 if (*errstr) g_free(*errstr);
1635 *errstr = g_strdup_printf(_("No specific account has been found to "
1636 "send, and an error happened during SMTP session."));
1640 } else if (!to_list && !newsgroup_list) {
1642 if (*errstr) g_free(*errstr);
1643 *errstr = g_strdup(_("Couldn't determine sending informations. "
1644 "Maybe the email hasn't been generated by Claws Mail."));
1649 fseek(fp, filepos, SEEK_SET);
1650 if (newsgroup_list && (mailval == 0)) {
1655 /* write to temporary file */
1656 tmp = g_strdup_printf("%s%ctmp%p", g_get_tmp_dir(),
1657 G_DIR_SEPARATOR, file);
1658 if ((tmpfp = g_fopen(tmp, "wb")) == NULL) {
1659 FILE_OP_ERROR(tmp, "fopen");
1661 alertpanel_error(_("Couldn't create temporary file for news sending."));
1663 if (change_file_mode_rw(tmpfp, tmp) < 0) {
1664 FILE_OP_ERROR(tmp, "chmod");
1665 g_warning("can't change file mode\n");
1668 while ((newsval == 0) && fgets(buf, sizeof(buf), fp) != NULL) {
1669 if (fputs(buf, tmpfp) == EOF) {
1670 FILE_OP_ERROR(tmp, "fputs");
1673 if (*errstr) g_free(*errstr);
1674 *errstr = g_strdup_printf(_("Error when writing temporary file for news sending."));
1681 debug_print("Sending message by news\n");
1683 folder = FOLDER(newsac->folder);
1685 newsval = news_post(folder, tmp);
1686 if (newsval < 0 && errstr) {
1687 if (*errstr) g_free(*errstr);
1688 *errstr = g_strdup_printf(_("Error occurred while posting the message to %s."),
1689 newsac->nntp_server);
1699 /* save message to outbox */
1700 if (mailval == 0 && newsval == 0 && savecopyfolder) {
1703 debug_print("saving sent message...\n");
1705 outbox = folder_find_item_from_identifier(savecopyfolder);
1707 outbox = folder_get_default_outbox();
1709 if (save_clear_text || tmp_enc_file == NULL) {
1710 gboolean saved = FALSE;
1711 *queued_removed = FALSE;
1712 if (queue && msgnum > 0) {
1713 MsgInfo *queued_mail = folder_item_get_msginfo(queue, msgnum);
1714 if (folder_item_move_msg(outbox, queued_mail) >= 0) {
1715 debug_print("moved queued mail %d to sent folder\n", msgnum);
1717 *queued_removed = TRUE;
1718 } else if (folder_item_copy_msg(outbox, queued_mail) >= 0) {
1719 debug_print("copied queued mail %d to sent folder\n", msgnum);
1722 procmsg_msginfo_free(queued_mail);
1725 debug_print("resaving clear text queued mail to sent folder\n");
1726 procmsg_save_to_outbox(outbox, file, TRUE);
1729 debug_print("saving encrpyted queued mail to sent folder\n");
1730 procmsg_save_to_outbox(outbox, tmp_enc_file, FALSE);
1734 if (tmp_enc_file != NULL) {
1735 g_unlink(tmp_enc_file);
1737 tmp_enc_file = NULL;
1740 if (replymessageid != NULL || fwdmessageid != NULL) {
1744 if (replymessageid != NULL)
1745 tokens = g_strsplit(replymessageid, "\t", 0);
1747 tokens = g_strsplit(fwdmessageid, "\t", 0);
1748 item = folder_find_item_from_identifier(tokens[0]);
1750 /* check if queued message has valid folder and message id */
1751 if (item != NULL && tokens[2] != NULL) {
1754 msginfo = folder_item_get_msginfo(item, atoi(tokens[1]));
1756 /* check if referring message exists and has a message id */
1757 if ((msginfo != NULL) &&
1758 (msginfo->msgid != NULL) &&
1759 (strcmp(msginfo->msgid, tokens[2]) != 0)) {
1760 procmsg_msginfo_free(msginfo);
1764 if (msginfo == NULL) {
1765 msginfo = folder_item_get_msginfo_by_msgid(item, tokens[2]);
1768 if (msginfo != NULL) {
1769 MsgPermFlags to_unset = 0;
1771 if (prefs_common.mark_as_read_on_new_window)
1772 to_unset = (MSG_NEW|MSG_UNREAD);
1774 if (replymessageid != NULL) {
1775 procmsg_msginfo_unset_flags(msginfo, to_unset|MSG_FORWARDED, 0);
1776 procmsg_msginfo_set_flags(msginfo, MSG_REPLIED, 0);
1778 procmsg_msginfo_unset_flags(msginfo, MSG_REPLIED, 0);
1779 procmsg_msginfo_set_flags(msginfo, MSG_FORWARDED, 0);
1781 procmsg_msginfo_free(msginfo);
1789 slist_free_strings(to_list);
1790 g_slist_free(to_list);
1791 slist_free_strings(newsgroup_list);
1792 g_slist_free(newsgroup_list);
1793 g_free(savecopyfolder);
1794 g_free(replymessageid);
1795 g_free(fwdmessageid);
1796 g_free(privacy_system);
1797 g_free(encrypt_data);
1799 return (newsval != 0 ? newsval : mailval);
1802 gint procmsg_send_message_queue(const gchar *file, gchar **errstr, FolderItem *queue, gint msgnum, gboolean *queued_removed)
1804 gint result = procmsg_send_message_queue_full(file, FALSE, errstr, queue, msgnum, queued_removed);
1805 toolbar_main_set_sensitive(mainwindow_get_mainwindow());
1809 static void update_folder_msg_counts(FolderItem *item, MsgInfo *msginfo, MsgPermFlags old_flags)
1811 MsgPermFlags new_flags = msginfo->flags.perm_flags;
1814 if (!(old_flags & MSG_NEW) && (new_flags & MSG_NEW)) {
1818 if ((old_flags & MSG_NEW) && !(new_flags & MSG_NEW)) {
1823 if (!(old_flags & MSG_UNREAD) && (new_flags & MSG_UNREAD)) {
1824 item->unread_msgs++;
1825 if (procmsg_msg_has_marked_parent(msginfo))
1826 item->unreadmarked_msgs++;
1829 if ((old_flags & MSG_UNREAD) && !(new_flags & MSG_UNREAD)) {
1830 item->unread_msgs--;
1831 if (procmsg_msg_has_marked_parent(msginfo))
1832 item->unreadmarked_msgs--;
1836 if (!(old_flags & MSG_MARKED) && (new_flags & MSG_MARKED)) {
1837 procmsg_update_unread_children(msginfo, TRUE);
1838 item->marked_msgs++;
1841 if ((old_flags & MSG_MARKED) && !(new_flags & MSG_MARKED)) {
1842 procmsg_update_unread_children(msginfo, FALSE);
1843 item->marked_msgs--;
1847 void procmsg_msginfo_set_flags(MsgInfo *msginfo, MsgPermFlags perm_flags, MsgTmpFlags tmp_flags)
1850 MsgInfoUpdate msginfo_update;
1851 MsgPermFlags perm_flags_new, perm_flags_old;
1852 MsgTmpFlags tmp_flags_old;
1854 g_return_if_fail(msginfo != NULL);
1855 item = msginfo->folder;
1856 g_return_if_fail(item != NULL);
1858 debug_print("Setting flags for message %d in folder %s\n", msginfo->msgnum, item->path);
1860 /* Perm Flags handling */
1861 perm_flags_old = msginfo->flags.perm_flags;
1862 perm_flags_new = msginfo->flags.perm_flags | perm_flags;
1863 if ((perm_flags & MSG_IGNORE_THREAD) || (perm_flags_old & MSG_IGNORE_THREAD)) {
1864 perm_flags_new &= ~(MSG_NEW | MSG_UNREAD);
1866 if ((perm_flags & MSG_WATCH_THREAD) || (perm_flags_old & MSG_WATCH_THREAD)) {
1867 perm_flags_new &= ~(MSG_IGNORE_THREAD);
1870 if (perm_flags_old != perm_flags_new) {
1871 folder_item_change_msg_flags(msginfo->folder, msginfo, perm_flags_new);
1873 update_folder_msg_counts(item, msginfo, perm_flags_old);
1877 /* Tmp flags handling */
1878 tmp_flags_old = msginfo->flags.tmp_flags;
1879 msginfo->flags.tmp_flags |= tmp_flags;
1881 /* update notification */
1882 if ((perm_flags_old != perm_flags_new) || (tmp_flags_old != msginfo->flags.tmp_flags)) {
1883 msginfo_update.msginfo = msginfo;
1884 msginfo_update.flags = MSGINFO_UPDATE_FLAGS;
1885 hooks_invoke(MSGINFO_UPDATE_HOOKLIST, &msginfo_update);
1886 folder_item_update(msginfo->folder, F_ITEM_UPDATE_MSGCNT);
1890 void procmsg_msginfo_unset_flags(MsgInfo *msginfo, MsgPermFlags perm_flags, MsgTmpFlags tmp_flags)
1893 MsgInfoUpdate msginfo_update;
1894 MsgPermFlags perm_flags_new, perm_flags_old;
1895 MsgTmpFlags tmp_flags_old;
1897 g_return_if_fail(msginfo != NULL);
1898 item = msginfo->folder;
1899 g_return_if_fail(item != NULL);
1901 debug_print("Unsetting flags for message %d in folder %s\n", msginfo->msgnum, item->path);
1903 /* Perm Flags handling */
1904 perm_flags_old = msginfo->flags.perm_flags;
1905 perm_flags_new = msginfo->flags.perm_flags & ~perm_flags;
1907 if (perm_flags_old != perm_flags_new) {
1908 folder_item_change_msg_flags(msginfo->folder, msginfo, perm_flags_new);
1910 update_folder_msg_counts(item, msginfo, perm_flags_old);
1913 /* Tmp flags hanlding */
1914 tmp_flags_old = msginfo->flags.tmp_flags;
1915 msginfo->flags.tmp_flags &= ~tmp_flags;
1917 /* update notification */
1918 if ((perm_flags_old != perm_flags_new) || (tmp_flags_old != msginfo->flags.tmp_flags)) {
1919 msginfo_update.msginfo = msginfo;
1920 msginfo_update.flags = MSGINFO_UPDATE_FLAGS;
1921 hooks_invoke(MSGINFO_UPDATE_HOOKLIST, &msginfo_update);
1922 folder_item_update(msginfo->folder, F_ITEM_UPDATE_MSGCNT);
1926 void procmsg_msginfo_change_flags(MsgInfo *msginfo,
1927 MsgPermFlags add_perm_flags, MsgTmpFlags add_tmp_flags,
1928 MsgPermFlags rem_perm_flags, MsgTmpFlags rem_tmp_flags)
1931 MsgInfoUpdate msginfo_update;
1932 MsgPermFlags perm_flags_new, perm_flags_old;
1933 MsgTmpFlags tmp_flags_old;
1935 g_return_if_fail(msginfo != NULL);
1936 item = msginfo->folder;
1937 g_return_if_fail(item != NULL);
1939 debug_print("Changing flags for message %d in folder %s\n", msginfo->msgnum, item->path);
1941 /* Perm Flags handling */
1942 perm_flags_old = msginfo->flags.perm_flags;
1943 perm_flags_new = (msginfo->flags.perm_flags & ~rem_perm_flags) | add_perm_flags;
1944 if ((add_perm_flags & MSG_IGNORE_THREAD) || (perm_flags_old & MSG_IGNORE_THREAD)) {
1945 perm_flags_new &= ~(MSG_NEW | MSG_UNREAD);
1947 if ((add_perm_flags & MSG_WATCH_THREAD) || (perm_flags_old & MSG_WATCH_THREAD)) {
1948 perm_flags_new &= ~(MSG_IGNORE_THREAD);
1951 if (perm_flags_old != perm_flags_new) {
1952 folder_item_change_msg_flags(msginfo->folder, msginfo, perm_flags_new);
1954 update_folder_msg_counts(item, msginfo, perm_flags_old);
1958 /* Tmp flags handling */
1959 tmp_flags_old = msginfo->flags.tmp_flags;
1960 msginfo->flags.tmp_flags &= ~rem_tmp_flags;
1961 msginfo->flags.tmp_flags |= add_tmp_flags;
1963 /* update notification */
1964 if ((perm_flags_old != perm_flags_new) || (tmp_flags_old != msginfo->flags.tmp_flags)) {
1965 msginfo_update.msginfo = msginfo;
1966 msginfo_update.flags = MSGINFO_UPDATE_FLAGS;
1967 hooks_invoke(MSGINFO_UPDATE_HOOKLIST, &msginfo_update);
1968 folder_item_update(msginfo->folder, F_ITEM_UPDATE_MSGCNT);
1973 *\brief check for flags (e.g. mark) in prior msgs of current thread
1975 *\param info Current message
1976 *\param perm_flags Flags to be checked
1977 *\param parentmsgs Hash of prior msgs to avoid loops
1979 *\return gboolean TRUE if perm_flags are found
1981 static gboolean procmsg_msg_has_flagged_parent_real(MsgInfo *info,
1982 MsgPermFlags perm_flags, GHashTable *parentmsgs)
1986 g_return_val_if_fail(info != NULL, FALSE);
1988 if (info != NULL && info->folder != NULL && info->inreplyto != NULL) {
1989 tmp = folder_item_get_msginfo_by_msgid(info->folder,
1991 if (tmp && (tmp->flags.perm_flags & perm_flags)) {
1992 procmsg_msginfo_free(tmp);
1994 } else if (tmp != NULL) {
1997 if (g_hash_table_lookup(parentmsgs, info)) {
1998 debug_print("loop detected: %d\n",
2002 g_hash_table_insert(parentmsgs, info, "1");
2003 result = procmsg_msg_has_flagged_parent_real(
2004 tmp, perm_flags, parentmsgs);
2006 procmsg_msginfo_free(tmp);
2016 *\brief Callback for cleaning up hash of parentmsgs
2018 static gboolean parentmsgs_hash_remove(gpointer key,
2026 *\brief Set up list of parentmsgs
2027 * See procmsg_msg_has_flagged_parent_real()
2029 gboolean procmsg_msg_has_flagged_parent(MsgInfo *info, MsgPermFlags perm_flags)
2032 static GHashTable *parentmsgs = NULL;
2034 if (parentmsgs == NULL)
2035 parentmsgs = g_hash_table_new(NULL, NULL);
2037 result = procmsg_msg_has_flagged_parent_real(info, perm_flags, parentmsgs);
2038 g_hash_table_foreach_remove(parentmsgs, parentmsgs_hash_remove, NULL);
2044 *\brief Check if msgs prior in thread are marked
2045 * See procmsg_msg_has_flagged_parent_real()
2047 gboolean procmsg_msg_has_marked_parent(MsgInfo *info)
2049 return procmsg_msg_has_flagged_parent(info, MSG_MARKED);
2053 static GSList *procmsg_find_children_func(MsgInfo *info,
2054 GSList *children, GSList *all)
2058 g_return_val_if_fail(info!=NULL, children);
2059 if (info->msgid == NULL)
2062 for (cur = all; cur != NULL; cur = g_slist_next(cur)) {
2063 MsgInfo *tmp = (MsgInfo *)cur->data;
2064 if (tmp->inreplyto && !strcmp(tmp->inreplyto, info->msgid)) {
2065 /* Check if message is already in the list */
2066 if ((children == NULL) ||
2067 (g_slist_index(children, tmp) == -1)) {
2068 children = g_slist_prepend(children,
2069 procmsg_msginfo_new_ref(tmp));
2070 children = procmsg_find_children_func(tmp,
2079 static GSList *procmsg_find_children (MsgInfo *info)
2084 g_return_val_if_fail(info!=NULL, NULL);
2085 all = folder_item_get_msg_list(info->folder);
2086 children = procmsg_find_children_func(info, NULL, all);
2087 if (children != NULL) {
2088 for (cur = all; cur != NULL; cur = g_slist_next(cur)) {
2089 /* this will not free the used pointers
2090 created with procmsg_msginfo_new_ref */
2091 procmsg_msginfo_free((MsgInfo *)cur->data);
2099 static void procmsg_update_unread_children(MsgInfo *info, gboolean newly_marked)
2101 GSList *children = procmsg_find_children(info);
2103 for (cur = children; cur != NULL; cur = g_slist_next(cur)) {
2104 MsgInfo *tmp = (MsgInfo *)cur->data;
2105 if(MSG_IS_UNREAD(tmp->flags) && !MSG_IS_IGNORE_THREAD(tmp->flags)) {
2107 info->folder->unreadmarked_msgs++;
2109 info->folder->unreadmarked_msgs--;
2110 folder_item_update(info->folder, F_ITEM_UPDATE_MSGCNT);
2112 procmsg_msginfo_free(tmp);
2114 g_slist_free(children);
2118 * Set the destination folder for a copy or move operation
2120 * \param msginfo The message which's destination folder is changed
2121 * \param to_folder The destination folder for the operation
2123 void procmsg_msginfo_set_to_folder(MsgInfo *msginfo, FolderItem *to_folder)
2125 if(msginfo->to_folder != NULL) {
2126 msginfo->to_folder->op_count--;
2127 folder_item_update(msginfo->to_folder, F_ITEM_UPDATE_MSGCNT);
2129 msginfo->to_folder = to_folder;
2130 if(to_folder != NULL) {
2131 to_folder->op_count++;
2132 folder_item_update(msginfo->to_folder, F_ITEM_UPDATE_MSGCNT);
2137 * Apply filtering actions to the msginfo
2139 * \param msginfo The MsgInfo describing the message that should be filtered
2140 * \return TRUE if the message was moved and MsgInfo is now invalid,
2143 static gboolean procmsg_msginfo_filter(MsgInfo *msginfo, PrefsAccount* ac_prefs)
2145 MailFilteringData mail_filtering_data;
2147 mail_filtering_data.msginfo = msginfo;
2148 mail_filtering_data.msglist = NULL;
2149 mail_filtering_data.filtered = NULL;
2150 mail_filtering_data.unfiltered = NULL;
2151 mail_filtering_data.account = ac_prefs;
2153 if (!ac_prefs || ac_prefs->filterhook_on_recv)
2154 if (hooks_invoke(MAIL_FILTERING_HOOKLIST, &mail_filtering_data))
2157 /* filter if enabled in prefs or move to inbox if not */
2158 if((filtering_rules != NULL) &&
2159 filter_message_by_msginfo(filtering_rules, msginfo, ac_prefs,
2160 FILTERING_INCORPORATION, NULL)) {
2167 void procmsg_msglist_filter(GSList *list, PrefsAccount *ac,
2168 GSList **filtered, GSList **unfiltered,
2171 GSList *cur, *to_do = NULL;
2172 gint total = 0, curnum = 0;
2173 MailFilteringData mail_filtering_data;
2175 g_return_if_fail(filtered != NULL);
2176 g_return_if_fail(unfiltered != NULL);
2184 total = g_slist_length(list);
2188 *unfiltered = g_slist_copy(list);
2192 statusbar_print_all(_("Filtering messages...\n"));
2194 mail_filtering_data.msginfo = NULL;
2195 mail_filtering_data.msglist = list;
2196 mail_filtering_data.filtered = NULL;
2197 mail_filtering_data.unfiltered = NULL;
2198 mail_filtering_data.account = ac;
2200 if (!ac || ac->filterhook_on_recv)
2201 hooks_invoke(MAIL_LISTFILTERING_HOOKLIST, &mail_filtering_data);
2203 if (mail_filtering_data.filtered == NULL &&
2204 mail_filtering_data.unfiltered == NULL) {
2205 /* nothing happened */
2206 debug_print(MAIL_LISTFILTERING_HOOKLIST " did nothing. filtering whole list normally.\n");
2209 if (mail_filtering_data.filtered != NULL) {
2210 /* keep track of what's been filtered by the hooks */
2211 debug_print(MAIL_LISTFILTERING_HOOKLIST " filtered some stuff. total %d filtered %d unfilt %d.\n",
2212 g_slist_length(list),
2213 g_slist_length(mail_filtering_data.filtered),
2214 g_slist_length(mail_filtering_data.unfiltered));
2216 *filtered = g_slist_copy(mail_filtering_data.filtered);
2218 if (mail_filtering_data.unfiltered != NULL) {
2219 /* what the hooks didn't handle will go in filtered or
2220 * unfiltered in the next loop */
2221 debug_print(MAIL_LISTFILTERING_HOOKLIST " left unfiltered stuff. total %d filtered %d unfilt %d.\n",
2222 g_slist_length(list),
2223 g_slist_length(mail_filtering_data.filtered),
2224 g_slist_length(mail_filtering_data.unfiltered));
2225 to_do = mail_filtering_data.unfiltered;
2228 for (cur = to_do; cur; cur = cur->next) {
2229 MsgInfo *info = (MsgInfo *)cur->data;
2230 if (procmsg_msginfo_filter(info, ac))
2231 *filtered = g_slist_prepend(*filtered, info);
2233 *unfiltered = g_slist_prepend(*unfiltered, info);
2234 statusbar_progress_all(curnum++, total, prefs_common.statusbar_update_step);
2237 g_slist_free(mail_filtering_data.filtered);
2238 g_slist_free(mail_filtering_data.unfiltered);
2240 *filtered = g_slist_reverse(*filtered);
2241 *unfiltered = g_slist_reverse(*unfiltered);
2243 statusbar_progress_all(0,0,0);
2244 statusbar_pop_all();
2247 MsgInfo *procmsg_msginfo_new_from_mimeinfo(MsgInfo *src_msginfo, MimeInfo *mimeinfo)
2249 MsgInfo *tmp_msginfo = NULL;
2250 MsgFlags flags = {0, 0};
2251 gchar *tmpfile = get_tmp_file();
2252 FILE *fp = g_fopen(tmpfile, "wb");
2254 if (!mimeinfo || mimeinfo->type != MIMETYPE_MESSAGE ||
2255 g_ascii_strcasecmp(mimeinfo->subtype, "rfc822")) {
2256 g_warning("procmsg_msginfo_new_from_mimeinfo(): unsuitable mimeinfo");
2263 if (fp && procmime_write_mimeinfo(mimeinfo, fp) >= 0) {
2266 tmp_msginfo = procheader_parse_file(
2273 if (tmp_msginfo != NULL) {
2275 tmp_msginfo->folder = src_msginfo->folder;
2276 tmp_msginfo->plaintext_file = g_strdup(tmpfile);
2278 g_warning("procmsg_msginfo_new_from_mimeinfo(): Can't generate new msginfo");
2286 static GSList *spam_learners = NULL;
2288 void procmsg_register_spam_learner (int (*learn_func)(MsgInfo *info, GSList *list, gboolean spam))
2290 if (!g_slist_find(spam_learners, learn_func))
2291 spam_learners = g_slist_append(spam_learners, learn_func);
2292 if (mainwindow_get_mainwindow()) {
2293 main_window_set_menu_sensitive(mainwindow_get_mainwindow());
2294 summary_set_menu_sensitive(
2295 mainwindow_get_mainwindow()->summaryview);
2296 toolbar_main_set_sensitive(mainwindow_get_mainwindow());
2300 void procmsg_unregister_spam_learner (int (*learn_func)(MsgInfo *info, GSList *list, gboolean spam))
2302 spam_learners = g_slist_remove(spam_learners, learn_func);
2303 if (mainwindow_get_mainwindow()) {
2304 main_window_set_menu_sensitive(mainwindow_get_mainwindow());
2305 summary_set_menu_sensitive(
2306 mainwindow_get_mainwindow()->summaryview);
2307 toolbar_main_set_sensitive(mainwindow_get_mainwindow());
2311 gboolean procmsg_spam_can_learn(void)
2313 return g_slist_length(spam_learners) > 0;
2316 int procmsg_spam_learner_learn (MsgInfo *info, GSList *list, gboolean spam)
2318 GSList *cur = spam_learners;
2320 for (; cur; cur = cur->next) {
2321 int ((*func)(MsgInfo *info, GSList *list, gboolean spam)) = cur->data;
2322 ret |= func(info, list, spam);
2327 static gchar *spam_folder_item = NULL;
2328 static FolderItem * (*procmsg_spam_get_folder_func)(MsgInfo *msginfo) = NULL;
2329 void procmsg_spam_set_folder (const char *item_identifier, FolderItem *(*spam_get_folder_func)(MsgInfo *info))
2331 g_free(spam_folder_item);
2332 if (item_identifier)
2333 spam_folder_item = g_strdup(item_identifier);
2335 spam_folder_item = NULL;
2336 if (spam_get_folder_func != NULL)
2337 procmsg_spam_get_folder_func = spam_get_folder_func;
2339 procmsg_spam_get_folder_func = NULL;
2342 FolderItem *procmsg_spam_get_folder (MsgInfo *msginfo)
2344 FolderItem *item = NULL;
2346 if (procmsg_spam_get_folder_func)
2347 item = procmsg_spam_get_folder_func(msginfo);
2348 if (item == NULL && spam_folder_item)
2349 item = folder_find_item_from_identifier(spam_folder_item);
2351 item = folder_get_default_trash();
2355 static void item_has_queued_mails(FolderItem *item, gpointer data)
2357 gboolean *result = (gboolean *)data;
2358 if (*result == TRUE)
2360 if (folder_has_parent_of_type(item, F_QUEUE) && item->total_msgs > 0)
2364 gboolean procmsg_have_queued_mails_fast (void)
2366 gboolean result = FALSE;
2367 folder_func_to_all_folders(item_has_queued_mails, &result);
2371 static void item_has_trashed_mails(FolderItem *item, gpointer data)
2373 gboolean *result = (gboolean *)data;
2374 if (*result == TRUE)
2376 if (folder_has_parent_of_type(item, F_TRASH) && item->total_msgs > 0)
2380 gboolean procmsg_have_trashed_mails_fast (void)
2382 gboolean result = FALSE;
2383 folder_func_to_all_folders(item_has_trashed_mails, &result);
2387 gchar *procmsg_msginfo_get_tags_str(MsgInfo *msginfo)
2395 if (msginfo->tags == NULL)
2397 for (cur = msginfo->tags; cur; cur = cur->next) {
2398 const gchar *tag = tags_get_tag(GPOINTER_TO_INT(cur->data));
2402 tags = g_strdup(tag);
2404 int olen = strlen(tags);
2405 int nlen = olen + strlen(tag) + 2 /* strlen(", ") */;
2406 tags = g_realloc(tags, nlen+1);
2409 strcpy(tags+olen, ", ");
2410 strcpy(tags+olen+2, tag);
2417 void procmsg_msginfo_update_tags(MsgInfo *msginfo, gboolean set, gint id)
2420 msginfo->tags = g_slist_remove(
2422 GINT_TO_POINTER(id));
2424 if (!g_slist_find(msginfo->tags, GINT_TO_POINTER(id)))
2425 msginfo->tags = g_slist_append(
2427 GINT_TO_POINTER(id));
2431 void procmsg_msginfo_clear_tags(MsgInfo *msginfo)
2433 g_slist_free(msginfo->tags);
2434 msginfo->tags = NULL;