2 * Sylpheed -- a GTK+ based, lightweight, and fast e-mail client
3 * Copyright (C) 1999-2006 Hiroyuki Yamamoto and the Sylpheed-Claws team
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation; either version 2 of the License, or
8 * (at your option) any later version.
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
15 * You should have received a copy of the GNU General Public License
16 * along with this program; if not, write to the Free Software
17 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
23 #include <glib/gi18n.h>
31 #include "procheader.h"
32 #include "send_message.h"
34 #include "statusbar.h"
35 #include "prefs_filtering.h"
36 #include "filtering.h"
38 #include "prefs_common.h"
40 #include "alertpanel.h"
44 #include "partial_download.h"
45 #include "mainwindow.h"
46 #include "summaryview.h"
50 static gint procmsg_send_message_queue_full(const gchar *file, gboolean keep_session, gchar **errstr);
58 Q_MAIL_ACCOUNT_ID = 4,
59 Q_NEWS_ACCOUNT_ID = 5,
60 Q_SAVE_COPY_FOLDER = 6,
61 Q_REPLY_MESSAGE_ID = 7,
69 GHashTable *procmsg_msg_hash_table_create(GSList *mlist)
71 GHashTable *msg_table;
73 if (mlist == NULL) return NULL;
75 msg_table = g_hash_table_new(NULL, g_direct_equal);
76 procmsg_msg_hash_table_append(msg_table, mlist);
81 void procmsg_msg_hash_table_append(GHashTable *msg_table, GSList *mlist)
86 if (msg_table == NULL || mlist == NULL) return;
88 for (cur = mlist; cur != NULL; cur = cur->next) {
89 msginfo = (MsgInfo *)cur->data;
91 g_hash_table_insert(msg_table,
92 GUINT_TO_POINTER(msginfo->msgnum),
97 GHashTable *procmsg_to_folder_hash_table_create(GSList *mlist)
99 GHashTable *msg_table;
103 if (mlist == NULL) return NULL;
105 msg_table = g_hash_table_new(NULL, g_direct_equal);
107 for (cur = mlist; cur != NULL; cur = cur->next) {
108 msginfo = (MsgInfo *)cur->data;
109 g_hash_table_insert(msg_table, msginfo->to_folder, msginfo);
115 gint procmsg_get_last_num_in_msg_list(GSList *mlist)
121 for (cur = mlist; cur != NULL; cur = cur->next) {
122 msginfo = (MsgInfo *)cur->data;
123 if (msginfo && msginfo->msgnum > last)
124 last = msginfo->msgnum;
130 void procmsg_msg_list_free(GSList *mlist)
135 for (cur = mlist; cur != NULL; cur = cur->next) {
136 msginfo = (MsgInfo *)cur->data;
137 procmsg_msginfo_free(msginfo);
151 /* CLAWS subject threading:
153 in the first round it inserts subject lines in a
154 relation (subject <-> node)
156 the second round finishes the threads by attaching
157 matching subject lines to the one found in the
158 relation. will use the oldest node with the same
159 subject that is not more then thread_by_subject_max_age
160 days old (see subject_relation_lookup)
163 static void subject_relation_insert(GRelation *relation, GNode *node)
168 g_return_if_fail(relation != NULL);
169 g_return_if_fail(node != NULL);
170 msginfo = (MsgInfo *) node->data;
171 g_return_if_fail(msginfo != NULL);
173 subject = msginfo->subject;
176 subject += subject_get_prefix_length(subject);
178 g_relation_insert(relation, subject, node);
181 static GNode *subject_relation_lookup(GRelation *relation, MsgInfo *msginfo)
188 g_return_val_if_fail(relation != NULL, NULL);
190 subject = msginfo->subject;
193 prefix_length = subject_get_prefix_length(subject);
194 if (prefix_length <= 0)
196 subject += prefix_length;
198 tuples = g_relation_select(relation, subject, 0);
202 if (tuples->len > 0) {
204 GNode *relation_node;
205 MsgInfo *relation_msginfo = NULL, *best_msginfo = NULL;
208 /* check all nodes with the same subject to find the best parent */
209 for (i = 0; i < tuples->len; i++) {
210 relation_node = (GNode *) g_tuples_index(tuples, i, 1);
211 relation_msginfo = (MsgInfo *) relation_node->data;
214 /* best node should be the oldest in the found nodes */
215 /* parent node must not be older then msginfo */
216 if ((relation_msginfo->date_t < msginfo->date_t) &&
217 ((best_msginfo == NULL) ||
218 (best_msginfo->date_t > relation_msginfo->date_t)))
221 /* parent node must not be more then thread_by_subject_max_age
222 days older then msginfo */
223 if (abs(difftime(msginfo->date_t, relation_msginfo->date_t)) >
224 prefs_common.thread_by_subject_max_age * 3600 * 24)
227 /* can add new tests for all matching
228 nodes found by subject */
231 node = relation_node;
232 best_msginfo = relation_msginfo;
237 g_tuples_destroy(tuples);
241 /* return the reversed thread tree */
242 GNode *procmsg_get_thread_tree(GSList *mlist)
244 GNode *root, *parent, *node, *next;
245 GHashTable *msgid_table;
246 GRelation *subject_relation;
250 START_TIMING("procmsg_get_thread_tree");
251 root = g_node_new(NULL);
252 msgid_table = g_hash_table_new(g_str_hash, g_str_equal);
253 subject_relation = g_relation_new(2);
254 g_relation_index(subject_relation, 0, g_str_hash, g_str_equal);
256 for (; mlist != NULL; mlist = mlist->next) {
257 msginfo = (MsgInfo *)mlist->data;
260 if (msginfo->inreplyto) {
261 parent = g_hash_table_lookup(msgid_table, msginfo->inreplyto);
262 if (parent == NULL) {
266 node = g_node_insert_data_before
267 (parent, parent == root ? parent->children : NULL,
269 if ((msgid = msginfo->msgid) && g_hash_table_lookup(msgid_table, msgid) == NULL)
270 g_hash_table_insert(msgid_table, (gchar *)msgid, node);
272 /* CLAWS: add subject to relation (without prefix) */
273 if (prefs_common.thread_by_subject) {
274 subject_relation_insert(subject_relation, node);
278 /* complete the unfinished threads */
279 for (node = root->children; node != NULL; ) {
281 msginfo = (MsgInfo *)node->data;
284 if (msginfo->inreplyto)
285 parent = g_hash_table_lookup(msgid_table, msginfo->inreplyto);
287 /* try looking for the indirect parent */
288 if (!parent && msginfo->references) {
289 for (reflist = msginfo->references;
290 reflist != NULL; reflist = reflist->next)
291 if ((parent = g_hash_table_lookup
292 (msgid_table, reflist->data)) != NULL)
296 /* node should not be the parent, and node should not
297 be an ancestor of parent (circular reference) */
298 if (parent && parent != node &&
299 !g_node_is_ancestor(node, parent)) {
302 (parent, parent->children, node);
308 if (prefs_common.thread_by_subject) {
309 START_TIMING("procmsg_get_thread_tree(1)");
310 for (node = root->children; node && node != NULL;) {
312 msginfo = (MsgInfo *) node->data;
314 parent = subject_relation_lookup(subject_relation, msginfo);
316 /* the node may already be threaded by IN-REPLY-TO, so go up
318 find the parent node */
319 if (parent != NULL) {
320 if (g_node_is_ancestor(node, parent))
328 g_node_append(parent, node);
336 g_relation_destroy(subject_relation);
337 g_hash_table_destroy(msgid_table);
342 gint procmsg_move_messages(GSList *mlist)
344 GSList *cur, *movelist = NULL;
346 FolderItem *dest = NULL;
348 gboolean finished = TRUE;
349 if (!mlist) return 0;
351 folder_item_update_freeze();
354 for (cur = mlist; cur != NULL; cur = cur->next) {
355 msginfo = (MsgInfo *)cur->data;
356 if (!msginfo->to_folder) {
362 dest = msginfo->to_folder;
363 movelist = g_slist_prepend(movelist, msginfo);
364 } else if (dest == msginfo->to_folder) {
365 movelist = g_slist_prepend(movelist, msginfo);
369 procmsg_msginfo_set_to_folder(msginfo, NULL);
372 movelist = g_slist_reverse(movelist);
373 retval |= folder_item_move_msgs(dest, movelist);
374 g_slist_free(movelist);
377 if (finished == FALSE) {
383 folder_item_update_thaw();
387 void procmsg_copy_messages(GSList *mlist)
389 GSList *cur, *copylist = NULL;
391 FolderItem *dest = NULL;
392 gboolean finished = TRUE;
395 folder_item_update_freeze();
398 for (cur = mlist; cur != NULL; cur = cur->next) {
399 msginfo = (MsgInfo *)cur->data;
400 if (!msginfo->to_folder) {
406 dest = msginfo->to_folder;
407 copylist = g_slist_prepend(copylist, msginfo);
408 } else if (dest == msginfo->to_folder) {
409 copylist = g_slist_prepend(copylist, msginfo);
413 procmsg_msginfo_set_to_folder(msginfo, NULL);
416 copylist = g_slist_reverse(copylist);
417 folder_item_copy_msgs(dest, copylist);
418 g_slist_free(copylist);
421 if (finished == FALSE) {
427 folder_item_update_thaw();
430 gchar *procmsg_get_message_file_path(MsgInfo *msginfo)
434 g_return_val_if_fail(msginfo != NULL, NULL);
436 if (msginfo->plaintext_file)
437 file = g_strdup(msginfo->plaintext_file);
439 file = folder_item_fetch_msg(msginfo->folder, msginfo->msgnum);
445 gchar *procmsg_get_message_file(MsgInfo *msginfo)
447 gchar *filename = NULL;
449 g_return_val_if_fail(msginfo != NULL, NULL);
451 filename = folder_item_fetch_msg(msginfo->folder, msginfo->msgnum);
453 debug_print("can't fetch message %d\n", msginfo->msgnum);
458 gchar *procmsg_get_message_file_full(MsgInfo *msginfo, gboolean headers, gboolean body)
460 gchar *filename = NULL;
462 g_return_val_if_fail(msginfo != NULL, NULL);
464 filename = folder_item_fetch_msg_full(msginfo->folder, msginfo->msgnum,
467 debug_print("can't fetch message %d\n", msginfo->msgnum);
472 GSList *procmsg_get_message_file_list(GSList *mlist)
474 GSList *file_list = NULL;
476 MsgFileInfo *fileinfo;
479 while (mlist != NULL) {
480 msginfo = (MsgInfo *)mlist->data;
481 file = procmsg_get_message_file(msginfo);
483 procmsg_message_file_list_free(file_list);
486 fileinfo = g_new(MsgFileInfo, 1);
487 fileinfo->msginfo = procmsg_msginfo_new_ref(msginfo);
488 fileinfo->file = file;
489 fileinfo->flags = g_new(MsgFlags, 1);
490 *fileinfo->flags = msginfo->flags;
491 file_list = g_slist_prepend(file_list, fileinfo);
495 file_list = g_slist_reverse(file_list);
500 void procmsg_message_file_list_free(MsgInfoList *file_list)
503 MsgFileInfo *fileinfo;
505 for (cur = file_list; cur != NULL; cur = cur->next) {
506 fileinfo = (MsgFileInfo *)cur->data;
507 procmsg_msginfo_free(fileinfo->msginfo);
508 g_free(fileinfo->file);
509 g_free(fileinfo->flags);
513 g_slist_free(file_list);
516 FILE *procmsg_open_message(MsgInfo *msginfo)
521 g_return_val_if_fail(msginfo != NULL, NULL);
523 file = procmsg_get_message_file_path(msginfo);
524 g_return_val_if_fail(file != NULL, NULL);
526 if (!is_file_exist(file)) {
528 file = procmsg_get_message_file(msginfo);
533 if ((fp = g_fopen(file, "rb")) == NULL) {
534 FILE_OP_ERROR(file, "fopen");
541 if (MSG_IS_QUEUED(msginfo->flags) || MSG_IS_DRAFT(msginfo->flags)) {
544 while (fgets(buf, sizeof(buf), fp) != NULL) {
546 if (!strncmp(buf, "X-Sylpheed-End-Special-Headers: 1",
547 strlen("X-Sylpheed-End-Special-Headers:")))
550 if (buf[0] == '\r' || buf[0] == '\n') break;
551 /* from other mailers */
552 if (!strncmp(buf, "Date: ", 6)
553 || !strncmp(buf, "To: ", 4)
554 || !strncmp(buf, "From: ", 6)
555 || !strncmp(buf, "Subject: ", 9)) {
565 gboolean procmsg_msg_exist(MsgInfo *msginfo)
570 if (!msginfo) return FALSE;
572 path = folder_item_get_path(msginfo->folder);
574 ret = !folder_item_is_msg_changed(msginfo->folder, msginfo);
580 void procmsg_get_filter_keyword(MsgInfo *msginfo, gchar **header, gchar **key,
581 PrefsFilterType type)
583 static HeaderEntry hentry[] = {{"X-BeenThere:", NULL, TRUE},
584 {"X-ML-Name:", NULL, TRUE},
585 {"X-List:", NULL, TRUE},
586 {"X-Mailing-list:", NULL, TRUE},
587 {"List-Id:", NULL, TRUE},
588 {"X-Sequence:", NULL, TRUE},
589 {"Sender:", NULL, TRUE},
590 {"List-Post:", NULL, TRUE},
591 {NULL, NULL, FALSE}};
597 H_X_MAILING_LIST = 3,
606 g_return_if_fail(msginfo != NULL);
607 g_return_if_fail(header != NULL);
608 g_return_if_fail(key != NULL);
617 if ((fp = procmsg_open_message(msginfo)) == NULL)
619 procheader_get_header_fields(fp, hentry);
622 #define SET_FILTER_KEY(hstr, idx) \
624 *header = g_strdup(hstr); \
625 *key = hentry[idx].body; \
626 hentry[idx].body = NULL; \
629 if (hentry[H_X_BEENTHERE].body != NULL) {
630 SET_FILTER_KEY("header \"X-BeenThere\"", H_X_BEENTHERE);
631 } else if (hentry[H_X_ML_NAME].body != NULL) {
632 SET_FILTER_KEY("header \"X-ML-Name\"", H_X_ML_NAME);
633 } else if (hentry[H_X_LIST].body != NULL) {
634 SET_FILTER_KEY("header \"X-List\"", H_X_LIST);
635 } else if (hentry[H_X_MAILING_LIST].body != NULL) {
636 SET_FILTER_KEY("header \"X-Mailing-List\"", H_X_MAILING_LIST);
637 } else if (hentry[H_LIST_ID].body != NULL) {
638 SET_FILTER_KEY("header \"List-Id\"", H_LIST_ID);
639 extract_list_id_str(*key);
640 } else if (hentry[H_X_SEQUENCE].body != NULL) {
643 SET_FILTER_KEY("X-Sequence", H_X_SEQUENCE);
646 while (*p != '\0' && !g_ascii_isspace(*p)) p++;
647 while (g_ascii_isspace(*p)) p++;
648 if (g_ascii_isdigit(*p)) {
654 } else if (hentry[H_SENDER].body != NULL) {
655 SET_FILTER_KEY("header \"Sender\"", H_SENDER);
656 } else if (hentry[H_LIST_POST].body != NULL) {
657 SET_FILTER_KEY("header \"List-Post\"", H_LIST_POST);
658 } else if (msginfo->to) {
659 *header = g_strdup("to");
660 *key = g_strdup(msginfo->to);
661 } else if (msginfo->subject) {
662 *header = g_strdup("subject");
663 *key = g_strdup(msginfo->subject);
666 #undef SET_FILTER_KEY
668 g_free(hentry[H_X_BEENTHERE].body);
669 hentry[H_X_BEENTHERE].body = NULL;
670 g_free(hentry[H_X_ML_NAME].body);
671 hentry[H_X_ML_NAME].body = NULL;
672 g_free(hentry[H_X_LIST].body);
673 hentry[H_X_LIST].body = NULL;
674 g_free(hentry[H_X_MAILING_LIST].body);
675 hentry[H_X_MAILING_LIST].body = NULL;
676 g_free(hentry[H_LIST_ID].body);
677 hentry[H_LIST_ID].body = NULL;
678 g_free(hentry[H_SENDER].body);
679 hentry[H_SENDER].body = NULL;
680 g_free(hentry[H_LIST_POST].body);
681 hentry[H_LIST_POST].body = NULL;
685 *header = g_strdup("from");
686 *key = g_strdup(msginfo->from);
689 *header = g_strdup("to");
690 *key = g_strdup(msginfo->to);
692 case FILTER_BY_SUBJECT:
693 *header = g_strdup("subject");
694 *key = g_strdup(msginfo->subject);
701 void procmsg_empty_trash(FolderItem *trash)
706 (trash->stype != F_TRASH &&
707 !folder_has_parent_of_type(trash, F_TRASH)))
710 if (trash && trash->total_msgs > 0) {
711 GSList *mlist = folder_item_get_msg_list(trash);
713 for (cur = mlist ; cur != NULL ; cur = cur->next) {
714 MsgInfo * msginfo = (MsgInfo *) cur->data;
715 if (MSG_IS_LOCKED(msginfo->flags))
717 if (msginfo->total_size != 0 &&
718 msginfo->size != (off_t)msginfo->total_size)
719 partial_mark_for_delete(msginfo);
721 procmsg_msginfo_free(msginfo);
724 folder_item_remove_all_msg(trash);
727 if (!trash->node || !trash->node->children)
730 node = trash->node->children;
731 while (node != NULL) {
733 procmsg_empty_trash(FOLDER_ITEM(node->data));
738 void procmsg_empty_all_trash(void)
743 for (cur = folder_get_list(); cur != NULL; cur = cur->next) {
744 Folder *folder = FOLDER(cur->data);
745 trash = folder->trash;
746 procmsg_empty_trash(trash);
747 if (folder->account && folder->account->set_trash_folder &&
748 folder_find_item_from_identifier(folder->account->trash_folder))
750 folder_find_item_from_identifier(folder->account->trash_folder));
754 static PrefsAccount *procmsg_get_account_from_file(const gchar *file)
756 PrefsAccount *mailac = NULL;
760 static HeaderEntry qentry[] = {{"S:", NULL, FALSE},
761 {"SSV:", NULL, FALSE},
763 {"NG:", NULL, FALSE},
764 {"MAID:", NULL, FALSE},
765 {"NAID:", NULL, FALSE},
766 {"SCF:", NULL, FALSE},
767 {"RMID:", NULL, FALSE},
768 {"FMID:", NULL, FALSE},
769 {"X-Sylpheed-Privacy-System:", NULL, FALSE},
770 {"X-Sylpheed-Encrypt:", NULL, FALSE},
771 {"X-Sylpheed-Encrypt-Data:", NULL, FALSE},
772 {NULL, NULL, FALSE}};
774 g_return_val_if_fail(file != NULL, NULL);
776 if ((fp = g_fopen(file, "rb")) == NULL) {
777 FILE_OP_ERROR(file, "fopen");
781 while ((hnum = procheader_get_one_field(buf, sizeof(buf), fp, qentry))
783 gchar *p = buf + strlen(qentry[hnum].name);
785 if (hnum == Q_MAIL_ACCOUNT_ID) {
786 mailac = account_find_from_id(atoi(p));
794 static GSList *procmsg_list_sort_by_account(FolderItem *queue, GSList *list)
796 GSList *result = NULL;
798 PrefsAccount *last_account = NULL;
801 gboolean nothing_to_sort = TRUE;
806 orig = g_slist_copy(list);
808 msg = (MsgInfo *)orig->data;
810 for (cur = orig; cur; cur = cur->next)
811 debug_print("sort before %s\n", ((MsgInfo *)cur->data)->from);
816 nothing_to_sort = TRUE;
820 PrefsAccount *ac = NULL;
821 msg = (MsgInfo *)cur->data;
822 file = folder_item_fetch_msg(queue, msg->msgnum);
823 ac = procmsg_get_account_from_file(file);
826 if (last_account == NULL || (ac != NULL && ac == last_account)) {
827 result = g_slist_append(result, msg);
828 orig = g_slist_remove(orig, msg);
830 nothing_to_sort = FALSE;
836 if (orig || g_slist_length(orig)) {
837 if (!last_account && nothing_to_sort) {
838 /* can't find an account for the rest of the list */
841 result = g_slist_append(result, cur->data);
852 for (cur = result; cur; cur = cur->next)
853 debug_print("sort after %s\n", ((MsgInfo *)cur->data)->from);
860 static gboolean procmsg_is_last_for_account(FolderItem *queue, MsgInfo *msginfo, GSList *elem)
862 gchar *file = folder_item_fetch_msg(queue, msginfo->msgnum);
863 PrefsAccount *ac = procmsg_get_account_from_file(file);
866 for (cur = elem; cur; cur = cur->next) {
867 MsgInfo *cur_msginfo = (MsgInfo *)cur->data;
868 file = folder_item_fetch_msg(queue, cur_msginfo->msgnum);
870 if (cur_msginfo != msginfo && !MSG_IS_LOCKED(cur_msginfo->flags)) {
871 if (procmsg_get_account_from_file(file) == ac) {
882 static gboolean send_queue_lock = FALSE;
884 *\brief Send messages in queue
886 *\param queue Queue folder to process
887 *\param save_msgs Unused
889 *\return Number of messages sent, negative if an error occurred
890 * positive if no error occurred
892 gint procmsg_send_queue(FolderItem *queue, gboolean save_msgs, gchar **errstr)
894 gint sent = 0, err = 0;
896 GSList *sorted_list = NULL;
899 if (send_queue_lock) {
900 log_error(_("Already trying to send\n"));
902 if (*errstr) g_free(*errstr);
903 *errstr = g_strdup_printf(_("Already trying to send."));
905 toolbar_main_set_sensitive(mainwindow_get_mainwindow());
908 send_queue_lock = TRUE;
910 queue = folder_get_default_queue();
913 send_queue_lock = FALSE;
917 toolbar_main_set_sensitive(mainwindow_get_mainwindow());
919 folder_item_scan(queue);
920 list = folder_item_get_msg_list(queue);
922 /* sort the list per sender account; this helps reusing the same SMTP server */
923 sorted_list = procmsg_list_sort_by_account(queue, list);
925 for (elem = sorted_list; elem != NULL; elem = elem->next) {
929 msginfo = (MsgInfo *)(elem->data);
930 if (!MSG_IS_LOCKED(msginfo->flags)) {
931 file = folder_item_fetch_msg(queue, msginfo->msgnum);
933 if (procmsg_send_message_queue_full(file,
934 !procmsg_is_last_for_account(queue, msginfo, elem),
936 g_warning("Sending queued message %d failed.\n",
941 folder_item_remove_msg(queue, msginfo->msgnum);
946 /* FIXME: supposedly if only one message is locked, and queue
947 * is being flushed, the following free says something like
948 * "freeing msg ## in folder (nil)". */
949 procmsg_msginfo_free(msginfo);
952 g_slist_free(sorted_list);
953 folder_item_scan(queue);
955 if (queue->node && queue->node->children) {
956 node = queue->node->children;
957 while (node != NULL) {
960 res = procmsg_send_queue(FOLDER_ITEM(node->data), save_msgs, errstr);
968 send_queue_lock = FALSE;
969 toolbar_main_set_sensitive(mainwindow_get_mainwindow());
971 return (err != 0 ? -err : sent);
974 gboolean procmsg_is_sending(void)
976 return send_queue_lock;
980 *\brief Determine if a queue folder is empty
982 *\param queue Queue folder to process
984 *\return TRUE if the queue folder is empty, otherwise return FALSE
986 gboolean procmsg_queue_is_empty(FolderItem *queue)
989 gboolean res = FALSE;
991 queue = folder_get_default_queue();
992 g_return_val_if_fail(queue != NULL, TRUE);
994 folder_item_scan(queue);
995 list = folder_item_get_msg_list(queue);
996 res = (list == NULL);
997 procmsg_msg_list_free(list);
1001 if (queue->node && queue->node->children) {
1002 node = queue->node->children;
1003 while (node != NULL) {
1005 if (!procmsg_queue_is_empty(FOLDER_ITEM(node->data)))
1014 gint procmsg_remove_special_headers(const gchar *in, const gchar *out)
1017 gchar buf[BUFFSIZE];
1019 if ((fp = g_fopen(in, "rb")) == NULL) {
1020 FILE_OP_ERROR(in, "fopen");
1023 if ((outfp = g_fopen(out, "wb")) == NULL) {
1024 FILE_OP_ERROR(out, "fopen");
1028 while (fgets(buf, sizeof(buf), fp) != NULL) {
1030 if (!strncmp(buf, "X-Sylpheed-End-Special-Headers: 1",
1031 strlen("X-Sylpheed-End-Special-Headers:")))
1034 if (buf[0] == '\r' || buf[0] == '\n') break;
1035 /* from other mailers */
1036 if (!strncmp(buf, "Date: ", 6)
1037 || !strncmp(buf, "To: ", 4)
1038 || !strncmp(buf, "From: ", 6)
1039 || !strncmp(buf, "Subject: ", 9)) {
1044 while (fgets(buf, sizeof(buf), fp) != NULL)
1051 gint procmsg_save_to_outbox(FolderItem *outbox, const gchar *file,
1055 MsgInfo *msginfo, *tmp_msginfo;
1056 MsgFlags flag = {0, 0};
1058 debug_print("saving sent message...\n");
1061 outbox = folder_get_default_outbox();
1062 g_return_val_if_fail(outbox != NULL, -1);
1064 /* remove queueing headers */
1066 gchar tmp[MAXPATHLEN + 1];
1068 g_snprintf(tmp, sizeof(tmp), "%s%ctmpmsg.out.%08x",
1069 get_rc_dir(), G_DIR_SEPARATOR, (guint) rand());
1071 if (procmsg_remove_special_headers(file, tmp) !=0)
1074 folder_item_scan(outbox);
1075 if ((num = folder_item_add_msg(outbox, tmp, &flag, TRUE)) < 0) {
1076 g_warning("can't save message\n");
1081 folder_item_scan(outbox);
1082 if ((num = folder_item_add_msg
1083 (outbox, file, &flag, FALSE)) < 0) {
1084 g_warning("can't save message\n");
1089 msginfo = folder_item_get_msginfo(outbox, num); /* refcnt++ */
1090 tmp_msginfo = procmsg_msginfo_get_full_info(msginfo); /* refcnt++ */
1091 if (msginfo != NULL) {
1092 procmsg_msginfo_unset_flags(msginfo, ~0, 0);
1093 procmsg_msginfo_free(msginfo); /* refcnt-- */
1094 /* tmp_msginfo == msginfo */
1095 if (tmp_msginfo && (msginfo->dispositionnotificationto ||
1096 msginfo->returnreceiptto)) {
1097 procmsg_msginfo_set_flags(msginfo, MSG_RETRCPT_SENT, 0);
1098 procmsg_msginfo_free(msginfo); /* refcnt-- */
1105 void procmsg_print_message(MsgInfo *msginfo, const gchar *cmdline)
1107 static const gchar *def_cmd = "lpr %s";
1108 static guint id = 0;
1114 g_return_if_fail(msginfo);
1116 if (procmime_msginfo_is_encrypted(msginfo))
1117 tmpfp = procmime_get_first_encrypted_text_content(msginfo);
1119 tmpfp = procmime_get_first_text_content(msginfo);
1120 if (tmpfp == NULL) {
1121 g_warning("Can't get text part\n");
1125 prtmp = g_strdup_printf("%s%cprinttmp.%08x",
1126 get_mime_tmp_dir(), G_DIR_SEPARATOR, id++);
1128 if ((prfp = g_fopen(prtmp, "wb")) == NULL) {
1129 FILE_OP_ERROR(prtmp, "fopen");
1135 if (msginfo->date) fprintf(prfp, "Date: %s\n", msginfo->date);
1136 if (msginfo->from) fprintf(prfp, "From: %s\n", msginfo->from);
1137 if (msginfo->to) fprintf(prfp, "To: %s\n", msginfo->to);
1138 if (msginfo->cc) fprintf(prfp, "Cc: %s\n", msginfo->cc);
1139 if (msginfo->newsgroups)
1140 fprintf(prfp, "Newsgroups: %s\n", msginfo->newsgroups);
1141 if (msginfo->subject) fprintf(prfp, "Subject: %s\n", msginfo->subject);
1144 while (fgets(buf, sizeof(buf), tmpfp) != NULL)
1150 if (cmdline && (p = strchr(cmdline, '%')) && *(p + 1) == 's' &&
1151 !strchr(p + 2, '%'))
1152 g_snprintf(buf, sizeof(buf) - 1, cmdline, prtmp);
1155 g_warning("Print command line is invalid: '%s'\n",
1157 g_snprintf(buf, sizeof(buf) - 1, def_cmd, prtmp);
1163 if (buf[strlen(buf) - 1] != '&') strcat(buf, "&");
1167 MsgInfo *procmsg_msginfo_new_ref(MsgInfo *msginfo)
1174 MsgInfo *procmsg_msginfo_new(void)
1176 MsgInfo *newmsginfo;
1178 newmsginfo = g_new0(MsgInfo, 1);
1179 newmsginfo->refcnt = 1;
1184 MsgInfo *procmsg_msginfo_copy(MsgInfo *msginfo)
1186 MsgInfo *newmsginfo;
1189 if (msginfo == NULL) return NULL;
1191 newmsginfo = g_new0(MsgInfo, 1);
1193 newmsginfo->refcnt = 1;
1195 #define MEMBCOPY(mmb) newmsginfo->mmb = msginfo->mmb
1196 #define MEMBDUP(mmb) newmsginfo->mmb = msginfo->mmb ? \
1197 g_strdup(msginfo->mmb) : NULL
1212 MEMBDUP(newsgroups);
1219 MEMBCOPY(to_folder);
1223 MEMBDUP(dispositionnotificationto);
1224 MEMBDUP(returnreceiptto);
1226 refs = msginfo->references;
1227 for (refs = msginfo->references; refs != NULL; refs = refs->next) {
1228 newmsginfo->references = g_slist_prepend
1229 (newmsginfo->references, g_strdup(refs->data));
1231 newmsginfo->references = g_slist_reverse(newmsginfo->references);
1234 MEMBDUP(plaintext_file);
1239 MsgInfo *procmsg_msginfo_get_full_info(MsgInfo *msginfo)
1241 MsgInfo *full_msginfo;
1244 if (msginfo == NULL) return NULL;
1246 file = procmsg_get_message_file_path(msginfo);
1247 if (!file || !is_file_exist(file)) {
1249 file = procmsg_get_message_file(msginfo);
1251 if (!file || !is_file_exist(file)) {
1252 g_warning("procmsg_msginfo_get_full_info(): can't get message file.\n");
1256 full_msginfo = procheader_parse_file(file, msginfo->flags, TRUE, FALSE);
1258 if (!full_msginfo) return NULL;
1260 /* CLAWS: make sure we add the missing members; see:
1261 * procheader.c::procheader_get_headernames() */
1262 if (!msginfo->list_post)
1263 msginfo->list_post = g_strdup(full_msginfo->list_post);
1264 if (!msginfo->list_subscribe)
1265 msginfo->list_subscribe = g_strdup(full_msginfo->list_subscribe);
1266 if (!msginfo->list_unsubscribe)
1267 msginfo->list_unsubscribe = g_strdup(full_msginfo->list_unsubscribe);
1268 if (!msginfo->list_help)
1269 msginfo->list_help = g_strdup(full_msginfo->list_help);
1270 if (!msginfo->list_archive)
1271 msginfo->list_archive= g_strdup(full_msginfo->list_archive);
1272 if (!msginfo->list_owner)
1273 msginfo->list_owner = g_strdup(full_msginfo->list_owner);
1274 if (!msginfo->xface)
1275 msginfo->xface = g_strdup(full_msginfo->xface);
1277 msginfo->face = g_strdup(full_msginfo->face);
1278 if (!msginfo->dispositionnotificationto)
1279 msginfo->dispositionnotificationto =
1280 g_strdup(full_msginfo->dispositionnotificationto);
1281 if (!msginfo->returnreceiptto)
1282 msginfo->returnreceiptto = g_strdup
1283 (full_msginfo->returnreceiptto);
1284 if (!msginfo->partial_recv && full_msginfo->partial_recv)
1285 msginfo->partial_recv = g_strdup
1286 (full_msginfo->partial_recv);
1287 msginfo->total_size = full_msginfo->total_size;
1288 if (!msginfo->account_server && full_msginfo->account_server)
1289 msginfo->account_server = g_strdup
1290 (full_msginfo->account_server);
1291 if (!msginfo->account_login && full_msginfo->account_login)
1292 msginfo->account_login = g_strdup
1293 (full_msginfo->account_login);
1294 msginfo->planned_download = full_msginfo->planned_download;
1295 procmsg_msginfo_free(full_msginfo);
1297 return procmsg_msginfo_new_ref(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);
1314 g_free(msginfo->returnreceiptto);
1315 g_free(msginfo->dispositionnotificationto);
1316 g_free(msginfo->xface);
1317 g_free(msginfo->face);
1319 g_free(msginfo->fromname);
1321 g_free(msginfo->date);
1322 g_free(msginfo->from);
1323 g_free(msginfo->to);
1324 g_free(msginfo->cc);
1325 g_free(msginfo->newsgroups);
1326 g_free(msginfo->subject);
1327 g_free(msginfo->msgid);
1328 g_free(msginfo->inreplyto);
1329 g_free(msginfo->xref);
1331 g_free(msginfo->list_post);
1332 g_free(msginfo->list_subscribe);
1333 g_free(msginfo->list_unsubscribe);
1334 g_free(msginfo->list_help);
1335 g_free(msginfo->list_archive);
1336 g_free(msginfo->list_owner);
1338 g_free(msginfo->partial_recv);
1339 g_free(msginfo->account_server);
1340 g_free(msginfo->account_login);
1342 slist_free_strings(msginfo->references);
1343 g_slist_free(msginfo->references);
1345 g_free(msginfo->plaintext_file);
1350 guint procmsg_msginfo_memusage(MsgInfo *msginfo)
1355 memusage += sizeof(MsgInfo);
1356 if (msginfo->fromname)
1357 memusage += strlen(msginfo->fromname);
1359 memusage += strlen(msginfo->date);
1361 memusage += strlen(msginfo->from);
1363 memusage += strlen(msginfo->to);
1365 memusage += strlen(msginfo->cc);
1366 if (msginfo->newsgroups)
1367 memusage += strlen(msginfo->newsgroups);
1368 if (msginfo->subject)
1369 memusage += strlen(msginfo->subject);
1371 memusage += strlen(msginfo->msgid);
1372 if (msginfo->inreplyto)
1373 memusage += strlen(msginfo->inreplyto);
1375 memusage += strlen(msginfo->xface);
1377 memusage += strlen(msginfo->face);
1378 if (msginfo->dispositionnotificationto)
1379 memusage += strlen(msginfo->dispositionnotificationto);
1380 if (msginfo->returnreceiptto)
1381 memusage += strlen(msginfo->returnreceiptto);
1382 for (refs = msginfo->references; refs; refs=refs->next) {
1383 gchar *r = (gchar *)refs->data;
1384 memusage += r?strlen(r):0;
1386 if (msginfo->fromspace)
1387 memusage += strlen(msginfo->fromspace);
1392 gint procmsg_cmp_msgnum_for_sort(gconstpointer a, gconstpointer b)
1394 const MsgInfo *msginfo1 = a;
1395 const MsgInfo *msginfo2 = b;
1402 return msginfo1->msgnum - msginfo2->msgnum;
1405 static gint procmsg_send_message_queue_full(const gchar *file, gboolean keep_session, gchar **errstr)
1407 static HeaderEntry qentry[] = {{"S:", NULL, FALSE},
1408 {"SSV:", NULL, FALSE},
1409 {"R:", NULL, FALSE},
1410 {"NG:", NULL, FALSE},
1411 {"MAID:", NULL, FALSE},
1412 {"NAID:", NULL, FALSE},
1413 {"SCF:", NULL, FALSE},
1414 {"RMID:", NULL, FALSE},
1415 {"FMID:", NULL, FALSE},
1416 {"X-Sylpheed-Privacy-System:", NULL, FALSE},
1417 {"X-Sylpheed-Encrypt:", NULL, FALSE},
1418 {"X-Sylpheed-Encrypt-Data:", NULL, FALSE},
1419 {"X-Sylpheed-End-Special-Headers:", NULL, FALSE},
1420 {NULL, NULL, FALSE}};
1423 gint mailval = 0, newsval = 0;
1425 gchar *smtpserver = NULL;
1426 GSList *to_list = NULL;
1427 GSList *newsgroup_list = NULL;
1428 gchar *savecopyfolder = NULL;
1429 gchar *replymessageid = NULL;
1430 gchar *fwdmessageid = NULL;
1431 gchar *privacy_system = NULL;
1432 gboolean encrypt = FALSE;
1433 gchar *encrypt_data = NULL;
1434 gchar buf[BUFFSIZE];
1436 PrefsAccount *mailac = NULL, *newsac = NULL;
1437 gboolean save_clear_text = TRUE;
1438 gchar *tmp_enc_file = NULL;
1442 g_return_val_if_fail(file != NULL, -1);
1444 if ((fp = g_fopen(file, "rb")) == NULL) {
1445 FILE_OP_ERROR(file, "fopen");
1447 if (*errstr) g_free(*errstr);
1448 *errstr = g_strdup_printf(_("Couldn't open file %s."), file);
1453 while ((hnum = procheader_get_one_field(buf, sizeof(buf), fp, qentry))
1455 gchar *p = buf + strlen(qentry[hnum].name);
1463 if (smtpserver == NULL)
1464 smtpserver = g_strdup(p);
1467 to_list = address_list_append(to_list, p);
1470 newsgroup_list = newsgroup_list_append(newsgroup_list, p);
1472 case Q_MAIL_ACCOUNT_ID:
1473 mailac = account_find_from_id(atoi(p));
1475 case Q_NEWS_ACCOUNT_ID:
1476 newsac = account_find_from_id(atoi(p));
1478 case Q_SAVE_COPY_FOLDER:
1479 if (savecopyfolder == NULL)
1480 savecopyfolder = g_strdup(p);
1482 case Q_REPLY_MESSAGE_ID:
1483 if (replymessageid == NULL)
1484 replymessageid = g_strdup(p);
1486 case Q_FWD_MESSAGE_ID:
1487 if (fwdmessageid == NULL)
1488 fwdmessageid = g_strdup(p);
1490 case Q_PRIVACY_SYSTEM:
1491 if (privacy_system == NULL)
1492 privacy_system = g_strdup(p);
1498 case Q_ENCRYPT_DATA:
1499 if (encrypt_data == NULL)
1500 encrypt_data = g_strdup(p);
1502 case Q_SYLPHEED_HDRS:
1503 /* end of special headers reached */
1504 goto send_mail; /* can't "break;break;" */
1508 filepos = ftell(fp);
1513 if (mailac && mailac->save_encrypted_as_clear_text
1514 && !mailac->encrypt_to_self)
1515 save_clear_text = TRUE;
1517 save_clear_text = FALSE;
1522 mimeinfo = procmime_scan_queue_file(file);
1523 if (!privacy_encrypt(privacy_system, mimeinfo, encrypt_data)
1524 || (fp = my_tmpfile()) == NULL
1525 || procmime_write_mimeinfo(mimeinfo, fp) < 0) {
1528 procmime_mimeinfo_free_all(mimeinfo);
1531 slist_free_strings(to_list);
1532 g_slist_free(to_list);
1533 slist_free_strings(newsgroup_list);
1534 g_slist_free(newsgroup_list);
1535 g_free(savecopyfolder);
1536 g_free(replymessageid);
1537 g_free(fwdmessageid);
1538 g_free(privacy_system);
1539 g_free(encrypt_data);
1541 if (*errstr) g_free(*errstr);
1542 *errstr = g_strdup_printf(_("Couldn't encrypt the email: %s"),
1543 privacy_get_error());
1549 if (!save_clear_text) {
1550 gchar *content = NULL;
1551 FILE *tmpfp = get_tmpfile_in_dir(get_mime_tmp_dir(), &tmp_enc_file);
1555 content = file_read_stream_to_str(fp);
1558 str_write_to_file(content, tmp_enc_file);
1561 g_warning("couldn't get tempfile\n");
1565 procmime_mimeinfo_free_all(mimeinfo);
1571 debug_print("Sending message by mail\n");
1574 if (*errstr) g_free(*errstr);
1575 *errstr = g_strdup_printf(_("Queued message header is broken."));
1578 } else if (mailac && mailac->use_mail_command &&
1579 mailac->mail_command && (* mailac->mail_command)) {
1580 mailval = send_message_local(mailac->mail_command, fp);
1584 mailac = account_find_from_smtp_server(from, smtpserver);
1586 g_warning("Account not found. "
1587 "Using current account...\n");
1588 mailac = cur_account;
1593 mailval = send_message_smtp_full(mailac, to_list, fp, keep_session);
1594 if (mailval == -1 && errstr) {
1595 if (*errstr) g_free(*errstr);
1596 *errstr = g_strdup_printf(_("An error happened during SMTP session."));
1599 PrefsAccount tmp_ac;
1601 g_warning("Account not found.\n");
1603 memset(&tmp_ac, 0, sizeof(PrefsAccount));
1604 tmp_ac.address = from;
1605 tmp_ac.smtp_server = smtpserver;
1606 tmp_ac.smtpport = SMTP_PORT;
1607 mailval = send_message_smtp(&tmp_ac, to_list, fp);
1608 if (mailval == -1 && errstr) {
1609 if (*errstr) g_free(*errstr);
1610 *errstr = g_strdup_printf(_("No specific account has been found to "
1611 "send, and an error happened during SMTP session."));
1615 } else if (!to_list && !newsgroup_list) {
1617 if (*errstr) g_free(*errstr);
1618 *errstr = g_strdup(_("Couldn't determine sending informations. "
1619 "Maybe the email hasn't been generated by Sylpheed-Claws."));
1624 fseek(fp, filepos, SEEK_SET);
1625 if (newsgroup_list && (mailval == 0)) {
1630 /* write to temporary file */
1631 tmp = g_strdup_printf("%s%ctmp%d", g_get_tmp_dir(),
1632 G_DIR_SEPARATOR, (gint)file);
1633 if ((tmpfp = g_fopen(tmp, "wb")) == NULL) {
1634 FILE_OP_ERROR(tmp, "fopen");
1636 alertpanel_error(_("Could not create temporary file for news sending."));
1638 if (change_file_mode_rw(tmpfp, tmp) < 0) {
1639 FILE_OP_ERROR(tmp, "chmod");
1640 g_warning("can't change file mode\n");
1643 while ((newsval == 0) && fgets(buf, sizeof(buf), fp) != NULL) {
1644 if (fputs(buf, tmpfp) == EOF) {
1645 FILE_OP_ERROR(tmp, "fputs");
1648 if (*errstr) g_free(*errstr);
1649 *errstr = g_strdup_printf(_("Error when writing temporary file for news sending."));
1656 debug_print("Sending message by news\n");
1658 folder = FOLDER(newsac->folder);
1660 newsval = news_post(folder, tmp);
1661 if (newsval < 0 && errstr) {
1662 if (*errstr) g_free(*errstr);
1663 *errstr = g_strdup_printf(_("Error occurred while posting the message to %s."),
1664 newsac->nntp_server);
1674 /* save message to outbox */
1675 if (mailval == 0 && newsval == 0 && savecopyfolder) {
1678 debug_print("saving sent message...\n");
1680 outbox = folder_find_item_from_identifier(savecopyfolder);
1682 outbox = folder_get_default_outbox();
1684 if (save_clear_text || tmp_enc_file == NULL) {
1685 procmsg_save_to_outbox(outbox, file, TRUE);
1687 procmsg_save_to_outbox(outbox, tmp_enc_file, FALSE);
1691 if (tmp_enc_file != NULL) {
1692 g_unlink(tmp_enc_file);
1694 tmp_enc_file = NULL;
1697 if (replymessageid != NULL || fwdmessageid != NULL) {
1701 if (replymessageid != NULL)
1702 tokens = g_strsplit(replymessageid, "\t", 0);
1704 tokens = g_strsplit(fwdmessageid, "\t", 0);
1705 item = folder_find_item_from_identifier(tokens[0]);
1707 /* check if queued message has valid folder and message id */
1708 if (item != NULL && tokens[2] != NULL) {
1711 msginfo = folder_item_get_msginfo(item, atoi(tokens[1]));
1713 /* check if referring message exists and has a message id */
1714 if ((msginfo != NULL) &&
1715 (msginfo->msgid != NULL) &&
1716 (strcmp(msginfo->msgid, tokens[2]) != 0)) {
1717 procmsg_msginfo_free(msginfo);
1721 if (msginfo == NULL) {
1722 msginfo = folder_item_get_msginfo_by_msgid(item, tokens[2]);
1725 if (msginfo != NULL) {
1726 if (replymessageid != NULL) {
1727 procmsg_msginfo_unset_flags(msginfo, MSG_FORWARDED, 0);
1728 procmsg_msginfo_set_flags(msginfo, MSG_REPLIED, 0);
1730 procmsg_msginfo_unset_flags(msginfo, MSG_REPLIED, 0);
1731 procmsg_msginfo_set_flags(msginfo, MSG_FORWARDED, 0);
1733 procmsg_msginfo_free(msginfo);
1741 slist_free_strings(to_list);
1742 g_slist_free(to_list);
1743 slist_free_strings(newsgroup_list);
1744 g_slist_free(newsgroup_list);
1745 g_free(savecopyfolder);
1746 g_free(replymessageid);
1747 g_free(fwdmessageid);
1748 g_free(privacy_system);
1749 g_free(encrypt_data);
1751 return (newsval != 0 ? newsval : mailval);
1754 gint procmsg_send_message_queue(const gchar *file, gchar **errstr)
1756 gint result = procmsg_send_message_queue_full(file, FALSE, errstr);
1757 toolbar_main_set_sensitive(mainwindow_get_mainwindow());
1761 static void update_folder_msg_counts(FolderItem *item, MsgInfo *msginfo, MsgPermFlags old_flags)
1763 MsgPermFlags new_flags = msginfo->flags.perm_flags;
1766 if (!(old_flags & MSG_NEW) && (new_flags & MSG_NEW)) {
1770 if ((old_flags & MSG_NEW) && !(new_flags & MSG_NEW)) {
1775 if (!(old_flags & MSG_UNREAD) && (new_flags & MSG_UNREAD)) {
1776 item->unread_msgs++;
1777 if (procmsg_msg_has_marked_parent(msginfo))
1778 item->unreadmarked_msgs++;
1781 if ((old_flags & MSG_UNREAD) && !(new_flags & MSG_UNREAD)) {
1782 item->unread_msgs--;
1783 if (procmsg_msg_has_marked_parent(msginfo))
1784 item->unreadmarked_msgs--;
1788 if (!(old_flags & MSG_MARKED) && (new_flags & MSG_MARKED)) {
1789 procmsg_update_unread_children(msginfo, TRUE);
1790 item->marked_msgs++;
1793 if ((old_flags & MSG_MARKED) && !(new_flags & MSG_MARKED)) {
1794 procmsg_update_unread_children(msginfo, FALSE);
1795 item->marked_msgs--;
1799 void procmsg_msginfo_set_flags(MsgInfo *msginfo, MsgPermFlags perm_flags, MsgTmpFlags tmp_flags)
1802 MsgInfoUpdate msginfo_update;
1803 MsgPermFlags perm_flags_new, perm_flags_old;
1804 MsgTmpFlags tmp_flags_old;
1806 g_return_if_fail(msginfo != NULL);
1807 item = msginfo->folder;
1808 g_return_if_fail(item != NULL);
1810 debug_print("Setting flags for message %d in folder %s\n", msginfo->msgnum, item->path);
1812 /* Perm Flags handling */
1813 perm_flags_old = msginfo->flags.perm_flags;
1814 perm_flags_new = msginfo->flags.perm_flags | perm_flags;
1815 if ((perm_flags & MSG_IGNORE_THREAD) || (perm_flags_old & MSG_IGNORE_THREAD)) {
1816 perm_flags_new &= ~(MSG_NEW | MSG_UNREAD);
1819 if (perm_flags_old != perm_flags_new) {
1820 folder_item_change_msg_flags(msginfo->folder, msginfo, perm_flags_new);
1822 update_folder_msg_counts(item, msginfo, perm_flags_old);
1826 /* Tmp flags handling */
1827 tmp_flags_old = msginfo->flags.tmp_flags;
1828 msginfo->flags.tmp_flags |= tmp_flags;
1830 /* update notification */
1831 if ((perm_flags_old != perm_flags_new) || (tmp_flags_old != msginfo->flags.tmp_flags)) {
1832 msginfo_update.msginfo = msginfo;
1833 msginfo_update.flags = MSGINFO_UPDATE_FLAGS;
1834 hooks_invoke(MSGINFO_UPDATE_HOOKLIST, &msginfo_update);
1835 folder_item_update(msginfo->folder, F_ITEM_UPDATE_MSGCNT);
1839 void procmsg_msginfo_unset_flags(MsgInfo *msginfo, MsgPermFlags perm_flags, MsgTmpFlags tmp_flags)
1842 MsgInfoUpdate msginfo_update;
1843 MsgPermFlags perm_flags_new, perm_flags_old;
1844 MsgTmpFlags tmp_flags_old;
1846 g_return_if_fail(msginfo != NULL);
1847 item = msginfo->folder;
1848 g_return_if_fail(item != NULL);
1850 debug_print("Unsetting flags for message %d in folder %s\n", msginfo->msgnum, item->path);
1852 /* Perm Flags handling */
1853 perm_flags_old = msginfo->flags.perm_flags;
1854 perm_flags_new = msginfo->flags.perm_flags & ~perm_flags;
1856 if (perm_flags_old != perm_flags_new) {
1857 folder_item_change_msg_flags(msginfo->folder, msginfo, perm_flags_new);
1859 update_folder_msg_counts(item, msginfo, perm_flags_old);
1862 /* Tmp flags hanlding */
1863 tmp_flags_old = msginfo->flags.tmp_flags;
1864 msginfo->flags.tmp_flags &= ~tmp_flags;
1866 /* update notification */
1867 if ((perm_flags_old != perm_flags_new) || (tmp_flags_old != msginfo->flags.tmp_flags)) {
1868 msginfo_update.msginfo = msginfo;
1869 msginfo_update.flags = MSGINFO_UPDATE_FLAGS;
1870 hooks_invoke(MSGINFO_UPDATE_HOOKLIST, &msginfo_update);
1871 folder_item_update(msginfo->folder, F_ITEM_UPDATE_MSGCNT);
1875 void procmsg_msginfo_change_flags(MsgInfo *msginfo,
1876 MsgPermFlags add_perm_flags, MsgTmpFlags add_tmp_flags,
1877 MsgPermFlags rem_perm_flags, MsgTmpFlags rem_tmp_flags)
1880 MsgInfoUpdate msginfo_update;
1881 MsgPermFlags perm_flags_new, perm_flags_old;
1882 MsgTmpFlags tmp_flags_old;
1884 g_return_if_fail(msginfo != NULL);
1885 item = msginfo->folder;
1886 g_return_if_fail(item != NULL);
1888 debug_print("Changing flags for message %d in folder %s\n", msginfo->msgnum, item->path);
1890 /* Perm Flags handling */
1891 perm_flags_old = msginfo->flags.perm_flags;
1892 perm_flags_new = (msginfo->flags.perm_flags & ~rem_perm_flags) | add_perm_flags;
1893 if ((add_perm_flags & MSG_IGNORE_THREAD) || (perm_flags_old & MSG_IGNORE_THREAD)) {
1894 perm_flags_new &= ~(MSG_NEW | MSG_UNREAD);
1897 if (perm_flags_old != perm_flags_new) {
1898 folder_item_change_msg_flags(msginfo->folder, msginfo, perm_flags_new);
1900 update_folder_msg_counts(item, msginfo, perm_flags_old);
1904 /* Tmp flags handling */
1905 tmp_flags_old = msginfo->flags.tmp_flags;
1906 msginfo->flags.tmp_flags &= ~rem_tmp_flags;
1907 msginfo->flags.tmp_flags |= add_tmp_flags;
1909 /* update notification */
1910 if ((perm_flags_old != perm_flags_new) || (tmp_flags_old != msginfo->flags.tmp_flags)) {
1911 msginfo_update.msginfo = msginfo;
1912 msginfo_update.flags = MSGINFO_UPDATE_FLAGS;
1913 hooks_invoke(MSGINFO_UPDATE_HOOKLIST, &msginfo_update);
1914 folder_item_update(msginfo->folder, F_ITEM_UPDATE_MSGCNT);
1919 *\brief check for flags (e.g. mark) in prior msgs of current thread
1921 *\param info Current message
1922 *\param perm_flags Flags to be checked
1923 *\param parentmsgs Hash of prior msgs to avoid loops
1925 *\return gboolean TRUE if perm_flags are found
1927 gboolean procmsg_msg_has_flagged_parent_real(MsgInfo *info,
1928 MsgPermFlags perm_flags, GHashTable *parentmsgs)
1932 g_return_val_if_fail(info != NULL, FALSE);
1934 if (info != NULL && info->folder != NULL && info->inreplyto != NULL) {
1935 tmp = folder_item_get_msginfo_by_msgid(info->folder,
1937 if (tmp && (tmp->flags.perm_flags & perm_flags)) {
1938 procmsg_msginfo_free(tmp);
1940 } else if (tmp != NULL) {
1943 if (g_hash_table_lookup(parentmsgs, info)) {
1944 debug_print("loop detected: %d\n",
1948 g_hash_table_insert(parentmsgs, info, "1");
1949 result = procmsg_msg_has_flagged_parent_real(
1950 tmp, perm_flags, parentmsgs);
1952 procmsg_msginfo_free(tmp);
1962 *\brief Callback for cleaning up hash of parentmsgs
1964 gboolean parentmsgs_hash_remove(gpointer key,
1972 *\brief Set up list of parentmsgs
1973 * See procmsg_msg_has_flagged_parent_real()
1975 gboolean procmsg_msg_has_flagged_parent(MsgInfo *info, MsgPermFlags perm_flags)
1978 GHashTable *parentmsgs = g_hash_table_new(NULL, NULL);
1980 result = procmsg_msg_has_flagged_parent_real(info, perm_flags, parentmsgs);
1981 g_hash_table_foreach_remove(parentmsgs, parentmsgs_hash_remove, NULL);
1982 g_hash_table_destroy(parentmsgs);
1987 *\brief Check if msgs prior in thread are marked
1988 * See procmsg_msg_has_flagged_parent_real()
1990 gboolean procmsg_msg_has_marked_parent(MsgInfo *info)
1992 return procmsg_msg_has_flagged_parent(info, MSG_MARKED);
1996 GSList *procmsg_find_children_func(MsgInfo *info,
1997 GSList *children, GSList *all)
2001 g_return_val_if_fail(info!=NULL, children);
2002 if (info->msgid == NULL)
2005 for (cur = all; cur != NULL; cur = g_slist_next(cur)) {
2006 MsgInfo *tmp = (MsgInfo *)cur->data;
2007 if (tmp->inreplyto && !strcmp(tmp->inreplyto, info->msgid)) {
2008 /* Check if message is already in the list */
2009 if ((children == NULL) ||
2010 (g_slist_index(children, tmp) == -1)) {
2011 children = g_slist_prepend(children,
2012 procmsg_msginfo_new_ref(tmp));
2013 children = procmsg_find_children_func(tmp,
2022 GSList *procmsg_find_children (MsgInfo *info)
2027 g_return_val_if_fail(info!=NULL, NULL);
2028 all = folder_item_get_msg_list(info->folder);
2029 children = procmsg_find_children_func(info, NULL, all);
2030 if (children != NULL) {
2031 for (cur = all; cur != NULL; cur = g_slist_next(cur)) {
2032 /* this will not free the used pointers
2033 created with procmsg_msginfo_new_ref */
2034 procmsg_msginfo_free((MsgInfo *)cur->data);
2042 void procmsg_update_unread_children(MsgInfo *info, gboolean newly_marked)
2044 GSList *children = procmsg_find_children(info);
2046 for (cur = children; cur != NULL; cur = g_slist_next(cur)) {
2047 MsgInfo *tmp = (MsgInfo *)cur->data;
2048 if(MSG_IS_UNREAD(tmp->flags) && !MSG_IS_IGNORE_THREAD(tmp->flags)) {
2050 info->folder->unreadmarked_msgs++;
2052 info->folder->unreadmarked_msgs--;
2053 folder_item_update(info->folder, F_ITEM_UPDATE_MSGCNT);
2055 procmsg_msginfo_free(tmp);
2057 g_slist_free(children);
2061 * Set the destination folder for a copy or move operation
2063 * \param msginfo The message which's destination folder is changed
2064 * \param to_folder The destination folder for the operation
2066 void procmsg_msginfo_set_to_folder(MsgInfo *msginfo, FolderItem *to_folder)
2068 if(msginfo->to_folder != NULL) {
2069 msginfo->to_folder->op_count--;
2070 folder_item_update(msginfo->to_folder, F_ITEM_UPDATE_MSGCNT);
2072 msginfo->to_folder = to_folder;
2073 if(to_folder != NULL) {
2074 to_folder->op_count++;
2075 folder_item_update(msginfo->to_folder, F_ITEM_UPDATE_MSGCNT);
2080 * Apply filtering actions to the msginfo
2082 * \param msginfo The MsgInfo describing the message that should be filtered
2083 * \return TRUE if the message was moved and MsgInfo is now invalid,
2086 gboolean procmsg_msginfo_filter(MsgInfo *msginfo, PrefsAccount* ac_prefs)
2088 MailFilteringData mail_filtering_data;
2090 mail_filtering_data.msginfo = msginfo;
2091 if (hooks_invoke(MAIL_FILTERING_HOOKLIST, &mail_filtering_data)) {
2095 /* filter if enabled in prefs or move to inbox if not */
2096 if((filtering_rules != NULL) &&
2097 filter_message_by_msginfo(filtering_rules, msginfo, ac_prefs)) {
2104 MsgInfo *procmsg_msginfo_new_from_mimeinfo(MsgInfo *src_msginfo, MimeInfo *mimeinfo)
2106 MsgInfo *tmp_msginfo = NULL;
2107 MsgFlags flags = {0, 0};
2108 gchar *tmpfile = get_tmp_file();
2109 FILE *fp = g_fopen(tmpfile, "wb");
2111 if (!mimeinfo || mimeinfo->type != MIMETYPE_MESSAGE ||
2112 g_ascii_strcasecmp(mimeinfo->subtype, "rfc822")) {
2113 g_warning("procmsg_msginfo_new_from_mimeinfo(): unsuitable mimeinfo");
2120 if (fp && procmime_write_mimeinfo(mimeinfo, fp) >= 0) {
2123 tmp_msginfo = procheader_parse_file(
2130 if (tmp_msginfo != NULL) {
2132 tmp_msginfo->folder = src_msginfo->folder;
2133 tmp_msginfo->plaintext_file = g_strdup(tmpfile);
2135 g_warning("procmsg_msginfo_new_from_mimeinfo(): Can't generate new msginfo");
2143 static GSList *spam_learners = NULL;
2145 void procmsg_register_spam_learner (int (*learn_func)(MsgInfo *info, GSList *list, gboolean spam))
2147 if (!g_slist_find(spam_learners, learn_func))
2148 spam_learners = g_slist_append(spam_learners, learn_func);
2149 if (mainwindow_get_mainwindow()) {
2150 main_window_set_menu_sensitive(mainwindow_get_mainwindow());
2151 summary_set_menu_sensitive(
2152 mainwindow_get_mainwindow()->summaryview);
2153 toolbar_main_set_sensitive(mainwindow_get_mainwindow());
2157 void procmsg_unregister_spam_learner (int (*learn_func)(MsgInfo *info, GSList *list, gboolean spam))
2159 spam_learners = g_slist_remove(spam_learners, learn_func);
2160 if (mainwindow_get_mainwindow()) {
2161 main_window_set_menu_sensitive(mainwindow_get_mainwindow());
2162 summary_set_menu_sensitive(
2163 mainwindow_get_mainwindow()->summaryview);
2164 toolbar_main_set_sensitive(mainwindow_get_mainwindow());
2168 gboolean procmsg_spam_can_learn(void)
2170 return g_slist_length(spam_learners) > 0;
2173 int procmsg_spam_learner_learn (MsgInfo *info, GSList *list, gboolean spam)
2175 GSList *cur = spam_learners;
2177 for (; cur; cur = cur->next) {
2178 int ((*func)(MsgInfo *info, GSList *list, gboolean spam)) = cur->data;
2179 ret |= func(info, list, spam);
2184 static gchar *spam_folder_item = NULL;
2185 void procmsg_spam_set_folder (const char *item_identifier)
2187 g_free(spam_folder_item);
2188 if (item_identifier)
2189 spam_folder_item = g_strdup(item_identifier);
2191 spam_folder_item = NULL;
2194 FolderItem *procmsg_spam_get_folder (void)
2196 FolderItem *item = spam_folder_item ? folder_find_item_from_identifier(spam_folder_item) : NULL;
2197 return item ? item : folder_get_default_trash();
2200 static void item_has_queued_mails(FolderItem *item, gpointer data)
2202 gboolean *result = (gboolean *)data;
2203 if (*result == TRUE)
2205 if (folder_has_parent_of_type(item, F_QUEUE) && item->total_msgs > 0)
2209 gboolean procmsg_have_queued_mails_fast (void)
2211 gboolean result = FALSE;
2212 folder_func_to_all_folders(item_has_queued_mails, &result);