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"
51 static gint procmsg_send_message_queue_full(const gchar *file, gboolean keep_session, gchar **errstr);
59 Q_MAIL_ACCOUNT_ID = 4,
60 Q_NEWS_ACCOUNT_ID = 5,
61 Q_SAVE_COPY_FOLDER = 6,
62 Q_REPLY_MESSAGE_ID = 7,
70 GHashTable *procmsg_msg_hash_table_create(GSList *mlist)
72 GHashTable *msg_table;
74 if (mlist == NULL) return NULL;
76 msg_table = g_hash_table_new(NULL, g_direct_equal);
77 procmsg_msg_hash_table_append(msg_table, mlist);
82 void procmsg_msg_hash_table_append(GHashTable *msg_table, GSList *mlist)
87 if (msg_table == NULL || mlist == NULL) return;
89 for (cur = mlist; cur != NULL; cur = cur->next) {
90 msginfo = (MsgInfo *)cur->data;
92 g_hash_table_insert(msg_table,
93 GUINT_TO_POINTER(msginfo->msgnum),
98 GHashTable *procmsg_to_folder_hash_table_create(GSList *mlist)
100 GHashTable *msg_table;
104 if (mlist == NULL) return NULL;
106 msg_table = g_hash_table_new(NULL, g_direct_equal);
108 for (cur = mlist; cur != NULL; cur = cur->next) {
109 msginfo = (MsgInfo *)cur->data;
110 g_hash_table_insert(msg_table, msginfo->to_folder, msginfo);
116 gint procmsg_get_last_num_in_msg_list(GSList *mlist)
122 for (cur = mlist; cur != NULL; cur = cur->next) {
123 msginfo = (MsgInfo *)cur->data;
124 if (msginfo && msginfo->msgnum > last)
125 last = msginfo->msgnum;
131 void procmsg_msg_list_free(GSList *mlist)
136 for (cur = mlist; cur != NULL; cur = cur->next) {
137 msginfo = (MsgInfo *)cur->data;
138 procmsg_msginfo_free(msginfo);
152 /* CLAWS subject threading:
154 in the first round it inserts subject lines in a
155 relation (subject <-> node)
157 the second round finishes the threads by attaching
158 matching subject lines to the one found in the
159 relation. will use the oldest node with the same
160 subject that is not more then thread_by_subject_max_age
161 days old (see subject_relation_lookup)
164 static void subject_relation_insert(GRelation *relation, GNode *node)
169 g_return_if_fail(relation != NULL);
170 g_return_if_fail(node != NULL);
171 msginfo = (MsgInfo *) node->data;
172 g_return_if_fail(msginfo != NULL);
174 subject = msginfo->subject;
177 subject += subject_get_prefix_length(subject);
179 g_relation_insert(relation, subject, node);
182 static GNode *subject_relation_lookup(GRelation *relation, MsgInfo *msginfo)
189 g_return_val_if_fail(relation != NULL, NULL);
191 subject = msginfo->subject;
194 prefix_length = subject_get_prefix_length(subject);
195 if (prefix_length <= 0)
197 subject += prefix_length;
199 tuples = g_relation_select(relation, subject, 0);
203 if (tuples->len > 0) {
205 GNode *relation_node;
206 MsgInfo *relation_msginfo = NULL, *best_msginfo = NULL;
209 /* check all nodes with the same subject to find the best parent */
210 for (i = 0; i < tuples->len; i++) {
211 relation_node = (GNode *) g_tuples_index(tuples, i, 1);
212 relation_msginfo = (MsgInfo *) relation_node->data;
215 /* best node should be the oldest in the found nodes */
216 /* parent node must not be older then msginfo */
217 if ((relation_msginfo->date_t < msginfo->date_t) &&
218 ((best_msginfo == NULL) ||
219 (best_msginfo->date_t > relation_msginfo->date_t)))
222 /* parent node must not be more then thread_by_subject_max_age
223 days older then msginfo */
224 if (abs(difftime(msginfo->date_t, relation_msginfo->date_t)) >
225 prefs_common.thread_by_subject_max_age * 3600 * 24)
228 /* can add new tests for all matching
229 nodes found by subject */
232 node = relation_node;
233 best_msginfo = relation_msginfo;
238 g_tuples_destroy(tuples);
242 /* return the reversed thread tree */
243 GNode *procmsg_get_thread_tree(GSList *mlist)
245 GNode *root, *parent, *node, *next;
246 GHashTable *msgid_table;
247 GRelation *subject_relation;
251 START_TIMING("procmsg_get_thread_tree");
252 root = g_node_new(NULL);
253 msgid_table = g_hash_table_new(g_str_hash, g_str_equal);
255 if (prefs_common.thread_by_subject) {
256 subject_relation = g_relation_new(2);
257 g_relation_index(subject_relation, 0, g_str_hash, g_str_equal);
260 for (; mlist != NULL; mlist = mlist->next) {
261 msginfo = (MsgInfo *)mlist->data;
264 if (msginfo->inreplyto) {
265 parent = g_hash_table_lookup(msgid_table, msginfo->inreplyto);
266 if (parent == NULL) {
270 node = g_node_insert_data_before
271 (parent, parent == root ? parent->children : NULL,
273 if ((msgid = msginfo->msgid) && g_hash_table_lookup(msgid_table, msgid) == NULL)
274 g_hash_table_insert(msgid_table, (gchar *)msgid, node);
276 /* CLAWS: add subject to relation (without prefix) */
277 if (prefs_common.thread_by_subject) {
278 subject_relation_insert(subject_relation, node);
282 /* complete the unfinished threads */
283 for (node = root->children; node != NULL; ) {
285 msginfo = (MsgInfo *)node->data;
288 if (msginfo->inreplyto)
289 parent = g_hash_table_lookup(msgid_table, msginfo->inreplyto);
291 /* try looking for the indirect parent */
292 if (!parent && msginfo->references) {
293 for (reflist = msginfo->references;
294 reflist != NULL; reflist = reflist->next)
295 if ((parent = g_hash_table_lookup
296 (msgid_table, reflist->data)) != NULL)
300 /* node should not be the parent, and node should not
301 be an ancestor of parent (circular reference) */
302 if (parent && parent != node &&
303 !g_node_is_ancestor(node, parent)) {
306 (parent, parent->children, node);
312 if (prefs_common.thread_by_subject) {
313 START_TIMING("procmsg_get_thread_tree(1)");
314 for (node = root->children; node && node != NULL;) {
316 msginfo = (MsgInfo *) node->data;
318 parent = subject_relation_lookup(subject_relation, msginfo);
320 /* the node may already be threaded by IN-REPLY-TO, so go up
322 find the parent node */
323 if (parent != NULL) {
324 if (g_node_is_ancestor(node, parent))
332 g_node_append(parent, node);
340 if (prefs_common.thread_by_subject)
341 g_relation_destroy(subject_relation);
343 g_hash_table_destroy(msgid_table);
348 gint procmsg_move_messages(GSList *mlist)
350 GSList *cur, *movelist = NULL;
352 FolderItem *dest = NULL;
354 gboolean finished = TRUE;
355 if (!mlist) return 0;
357 folder_item_update_freeze();
360 for (cur = mlist; cur != NULL; cur = cur->next) {
361 msginfo = (MsgInfo *)cur->data;
362 if (!msginfo->to_folder) {
368 dest = msginfo->to_folder;
369 movelist = g_slist_prepend(movelist, msginfo);
370 } else if (dest == msginfo->to_folder) {
371 movelist = g_slist_prepend(movelist, msginfo);
375 procmsg_msginfo_set_to_folder(msginfo, NULL);
378 movelist = g_slist_reverse(movelist);
379 retval |= folder_item_move_msgs(dest, movelist);
380 g_slist_free(movelist);
383 if (finished == FALSE) {
389 folder_item_update_thaw();
393 void procmsg_copy_messages(GSList *mlist)
395 GSList *cur, *copylist = NULL;
397 FolderItem *dest = NULL;
398 gboolean finished = TRUE;
401 folder_item_update_freeze();
404 for (cur = mlist; cur != NULL; cur = cur->next) {
405 msginfo = (MsgInfo *)cur->data;
406 if (!msginfo->to_folder) {
412 dest = msginfo->to_folder;
413 copylist = g_slist_prepend(copylist, msginfo);
414 } else if (dest == msginfo->to_folder) {
415 copylist = g_slist_prepend(copylist, msginfo);
419 procmsg_msginfo_set_to_folder(msginfo, NULL);
422 copylist = g_slist_reverse(copylist);
423 folder_item_copy_msgs(dest, copylist);
424 g_slist_free(copylist);
427 if (finished == FALSE) {
433 folder_item_update_thaw();
436 gchar *procmsg_get_message_file_path(MsgInfo *msginfo)
440 g_return_val_if_fail(msginfo != NULL, NULL);
442 if (msginfo->plaintext_file)
443 file = g_strdup(msginfo->plaintext_file);
445 file = folder_item_fetch_msg(msginfo->folder, msginfo->msgnum);
451 gchar *procmsg_get_message_file(MsgInfo *msginfo)
453 gchar *filename = NULL;
455 g_return_val_if_fail(msginfo != NULL, NULL);
457 filename = folder_item_fetch_msg(msginfo->folder, msginfo->msgnum);
459 debug_print("can't fetch message %d\n", msginfo->msgnum);
464 gchar *procmsg_get_message_file_full(MsgInfo *msginfo, gboolean headers, gboolean body)
466 gchar *filename = NULL;
468 g_return_val_if_fail(msginfo != NULL, NULL);
470 filename = folder_item_fetch_msg_full(msginfo->folder, msginfo->msgnum,
473 debug_print("can't fetch message %d\n", msginfo->msgnum);
478 GSList *procmsg_get_message_file_list(GSList *mlist)
480 GSList *file_list = NULL;
482 MsgFileInfo *fileinfo;
485 while (mlist != NULL) {
486 msginfo = (MsgInfo *)mlist->data;
487 file = procmsg_get_message_file(msginfo);
489 procmsg_message_file_list_free(file_list);
492 fileinfo = g_new(MsgFileInfo, 1);
493 fileinfo->msginfo = procmsg_msginfo_new_ref(msginfo);
494 fileinfo->file = file;
495 fileinfo->flags = g_new(MsgFlags, 1);
496 *fileinfo->flags = msginfo->flags;
497 file_list = g_slist_prepend(file_list, fileinfo);
501 file_list = g_slist_reverse(file_list);
506 void procmsg_message_file_list_free(MsgInfoList *file_list)
509 MsgFileInfo *fileinfo;
511 for (cur = file_list; cur != NULL; cur = cur->next) {
512 fileinfo = (MsgFileInfo *)cur->data;
513 procmsg_msginfo_free(fileinfo->msginfo);
514 g_free(fileinfo->file);
515 g_free(fileinfo->flags);
519 g_slist_free(file_list);
522 FILE *procmsg_open_message(MsgInfo *msginfo)
527 g_return_val_if_fail(msginfo != NULL, NULL);
529 file = procmsg_get_message_file_path(msginfo);
530 g_return_val_if_fail(file != NULL, NULL);
532 if (!is_file_exist(file)) {
534 file = procmsg_get_message_file(msginfo);
539 if ((fp = g_fopen(file, "rb")) == NULL) {
540 FILE_OP_ERROR(file, "fopen");
547 if (MSG_IS_QUEUED(msginfo->flags) || MSG_IS_DRAFT(msginfo->flags)) {
550 while (fgets(buf, sizeof(buf), fp) != NULL) {
552 if (!strncmp(buf, "X-Sylpheed-End-Special-Headers: 1",
553 strlen("X-Sylpheed-End-Special-Headers:")))
556 if (buf[0] == '\r' || buf[0] == '\n') break;
557 /* from other mailers */
558 if (!strncmp(buf, "Date: ", 6)
559 || !strncmp(buf, "To: ", 4)
560 || !strncmp(buf, "From: ", 6)
561 || !strncmp(buf, "Subject: ", 9)) {
571 gboolean procmsg_msg_exist(MsgInfo *msginfo)
576 if (!msginfo) return FALSE;
578 path = folder_item_get_path(msginfo->folder);
580 ret = !folder_item_is_msg_changed(msginfo->folder, msginfo);
586 void procmsg_get_filter_keyword(MsgInfo *msginfo, gchar **header, gchar **key,
587 PrefsFilterType type)
589 static HeaderEntry hentry[] = {{"X-BeenThere:", NULL, TRUE},
590 {"X-ML-Name:", NULL, TRUE},
591 {"X-List:", NULL, TRUE},
592 {"X-Mailing-list:", NULL, TRUE},
593 {"List-Id:", NULL, TRUE},
594 {"X-Sequence:", NULL, TRUE},
595 {"Sender:", NULL, TRUE},
596 {"List-Post:", NULL, TRUE},
597 {NULL, NULL, FALSE}};
603 H_X_MAILING_LIST = 3,
612 g_return_if_fail(msginfo != NULL);
613 g_return_if_fail(header != NULL);
614 g_return_if_fail(key != NULL);
623 if ((fp = procmsg_open_message(msginfo)) == NULL)
625 procheader_get_header_fields(fp, hentry);
628 #define SET_FILTER_KEY(hstr, idx) \
630 *header = g_strdup(hstr); \
631 *key = hentry[idx].body; \
632 hentry[idx].body = NULL; \
635 if (hentry[H_X_BEENTHERE].body != NULL) {
636 SET_FILTER_KEY("header \"X-BeenThere\"", H_X_BEENTHERE);
637 } else if (hentry[H_X_ML_NAME].body != NULL) {
638 SET_FILTER_KEY("header \"X-ML-Name\"", H_X_ML_NAME);
639 } else if (hentry[H_X_LIST].body != NULL) {
640 SET_FILTER_KEY("header \"X-List\"", H_X_LIST);
641 } else if (hentry[H_X_MAILING_LIST].body != NULL) {
642 SET_FILTER_KEY("header \"X-Mailing-List\"", H_X_MAILING_LIST);
643 } else if (hentry[H_LIST_ID].body != NULL) {
644 SET_FILTER_KEY("header \"List-Id\"", H_LIST_ID);
645 extract_list_id_str(*key);
646 } else if (hentry[H_X_SEQUENCE].body != NULL) {
649 SET_FILTER_KEY("X-Sequence", H_X_SEQUENCE);
652 while (*p != '\0' && !g_ascii_isspace(*p)) p++;
653 while (g_ascii_isspace(*p)) p++;
654 if (g_ascii_isdigit(*p)) {
660 } else if (hentry[H_SENDER].body != NULL) {
661 SET_FILTER_KEY("header \"Sender\"", H_SENDER);
662 } else if (hentry[H_LIST_POST].body != NULL) {
663 SET_FILTER_KEY("header \"List-Post\"", H_LIST_POST);
664 } else if (msginfo->to) {
665 *header = g_strdup("to");
666 *key = g_strdup(msginfo->to);
667 } else if (msginfo->subject) {
668 *header = g_strdup("subject");
669 *key = g_strdup(msginfo->subject);
672 #undef SET_FILTER_KEY
674 g_free(hentry[H_X_BEENTHERE].body);
675 hentry[H_X_BEENTHERE].body = NULL;
676 g_free(hentry[H_X_ML_NAME].body);
677 hentry[H_X_ML_NAME].body = NULL;
678 g_free(hentry[H_X_LIST].body);
679 hentry[H_X_LIST].body = NULL;
680 g_free(hentry[H_X_MAILING_LIST].body);
681 hentry[H_X_MAILING_LIST].body = NULL;
682 g_free(hentry[H_LIST_ID].body);
683 hentry[H_LIST_ID].body = NULL;
684 g_free(hentry[H_SENDER].body);
685 hentry[H_SENDER].body = NULL;
686 g_free(hentry[H_LIST_POST].body);
687 hentry[H_LIST_POST].body = NULL;
691 *header = g_strdup("from");
692 *key = g_strdup(msginfo->from);
695 *header = g_strdup("to");
696 *key = g_strdup(msginfo->to);
698 case FILTER_BY_SUBJECT:
699 *header = g_strdup("subject");
700 *key = g_strdup(msginfo->subject);
707 void procmsg_empty_trash(FolderItem *trash)
712 (trash->stype != F_TRASH &&
713 !folder_has_parent_of_type(trash, F_TRASH)))
716 if (trash && trash->total_msgs > 0) {
717 GSList *mlist = folder_item_get_msg_list(trash);
719 for (cur = mlist ; cur != NULL ; cur = cur->next) {
720 MsgInfo * msginfo = (MsgInfo *) cur->data;
721 if (MSG_IS_LOCKED(msginfo->flags))
723 if (msginfo->total_size != 0 &&
724 msginfo->size != (off_t)msginfo->total_size)
725 partial_mark_for_delete(msginfo);
727 procmsg_msginfo_free(msginfo);
730 folder_item_remove_all_msg(trash);
733 if (!trash->node || !trash->node->children)
736 node = trash->node->children;
737 while (node != NULL) {
739 procmsg_empty_trash(FOLDER_ITEM(node->data));
744 void procmsg_empty_all_trash(void)
749 for (cur = folder_get_list(); cur != NULL; cur = cur->next) {
750 Folder *folder = FOLDER(cur->data);
751 trash = folder->trash;
752 procmsg_empty_trash(trash);
753 if (folder->account && folder->account->set_trash_folder &&
754 folder_find_item_from_identifier(folder->account->trash_folder))
756 folder_find_item_from_identifier(folder->account->trash_folder));
760 static PrefsAccount *procmsg_get_account_from_file(const gchar *file)
762 PrefsAccount *mailac = NULL;
766 static HeaderEntry qentry[] = {{"S:", NULL, FALSE},
767 {"SSV:", NULL, FALSE},
769 {"NG:", NULL, FALSE},
770 {"MAID:", NULL, FALSE},
771 {"NAID:", NULL, FALSE},
772 {"SCF:", NULL, FALSE},
773 {"RMID:", NULL, FALSE},
774 {"FMID:", NULL, FALSE},
775 {"X-Sylpheed-Privacy-System:", NULL, FALSE},
776 {"X-Sylpheed-Encrypt:", NULL, FALSE},
777 {"X-Sylpheed-Encrypt-Data:", NULL, FALSE},
778 {NULL, NULL, FALSE}};
780 g_return_val_if_fail(file != NULL, NULL);
782 if ((fp = g_fopen(file, "rb")) == NULL) {
783 FILE_OP_ERROR(file, "fopen");
787 while ((hnum = procheader_get_one_field(buf, sizeof(buf), fp, qentry))
789 gchar *p = buf + strlen(qentry[hnum].name);
791 if (hnum == Q_MAIL_ACCOUNT_ID) {
792 mailac = account_find_from_id(atoi(p));
800 static GSList *procmsg_list_sort_by_account(FolderItem *queue, GSList *list)
802 GSList *result = NULL;
804 PrefsAccount *last_account = NULL;
807 gboolean nothing_to_sort = TRUE;
812 orig = g_slist_copy(list);
814 msg = (MsgInfo *)orig->data;
816 for (cur = orig; cur; cur = cur->next)
817 debug_print("sort before %s\n", ((MsgInfo *)cur->data)->from);
822 nothing_to_sort = TRUE;
826 PrefsAccount *ac = NULL;
827 msg = (MsgInfo *)cur->data;
828 file = folder_item_fetch_msg(queue, msg->msgnum);
829 ac = procmsg_get_account_from_file(file);
832 if (last_account == NULL || (ac != NULL && ac == last_account)) {
833 result = g_slist_append(result, msg);
834 orig = g_slist_remove(orig, msg);
836 nothing_to_sort = FALSE;
842 if (orig || g_slist_length(orig)) {
843 if (!last_account && nothing_to_sort) {
844 /* can't find an account for the rest of the list */
847 result = g_slist_append(result, cur->data);
858 for (cur = result; cur; cur = cur->next)
859 debug_print("sort after %s\n", ((MsgInfo *)cur->data)->from);
866 static gboolean procmsg_is_last_for_account(FolderItem *queue, MsgInfo *msginfo, GSList *elem)
868 gchar *file = folder_item_fetch_msg(queue, msginfo->msgnum);
869 PrefsAccount *ac = procmsg_get_account_from_file(file);
872 for (cur = elem; cur; cur = cur->next) {
873 MsgInfo *cur_msginfo = (MsgInfo *)cur->data;
874 file = folder_item_fetch_msg(queue, cur_msginfo->msgnum);
876 if (cur_msginfo != msginfo && !MSG_IS_LOCKED(cur_msginfo->flags)) {
877 if (procmsg_get_account_from_file(file) == ac) {
888 static gboolean send_queue_lock = FALSE;
890 *\brief Send messages in queue
892 *\param queue Queue folder to process
893 *\param save_msgs Unused
895 *\return Number of messages sent, negative if an error occurred
896 * positive if no error occurred
898 gint procmsg_send_queue(FolderItem *queue, gboolean save_msgs, gchar **errstr)
900 gint sent = 0, err = 0;
902 GSList *sorted_list = NULL;
905 if (send_queue_lock) {
906 log_error(_("Already trying to send\n"));
908 if (*errstr) g_free(*errstr);
909 *errstr = g_strdup_printf(_("Already trying to send."));
911 toolbar_main_set_sensitive(mainwindow_get_mainwindow());
914 send_queue_lock = TRUE;
917 queue = folder_get_default_queue();
920 send_queue_lock = FALSE;
925 toolbar_main_set_sensitive(mainwindow_get_mainwindow());
927 folder_item_scan(queue);
928 list = folder_item_get_msg_list(queue);
930 /* sort the list per sender account; this helps reusing the same SMTP server */
931 sorted_list = procmsg_list_sort_by_account(queue, list);
933 for (elem = sorted_list; elem != NULL; elem = elem->next) {
937 msginfo = (MsgInfo *)(elem->data);
938 if (!MSG_IS_LOCKED(msginfo->flags)) {
939 file = folder_item_fetch_msg(queue, msginfo->msgnum);
941 if (procmsg_send_message_queue_full(file,
942 !procmsg_is_last_for_account(queue, msginfo, elem),
944 g_warning("Sending queued message %d failed.\n",
949 folder_item_remove_msg(queue, msginfo->msgnum);
954 /* FIXME: supposedly if only one message is locked, and queue
955 * is being flushed, the following free says something like
956 * "freeing msg ## in folder (nil)". */
957 procmsg_msginfo_free(msginfo);
960 g_slist_free(sorted_list);
961 folder_item_scan(queue);
963 if (queue->node && queue->node->children) {
964 node = queue->node->children;
965 while (node != NULL) {
968 res = procmsg_send_queue(FOLDER_ITEM(node->data), save_msgs, errstr);
976 send_queue_lock = FALSE;
978 toolbar_main_set_sensitive(mainwindow_get_mainwindow());
980 return (err != 0 ? -err : sent);
983 gboolean procmsg_is_sending(void)
985 return send_queue_lock;
989 *\brief Determine if a queue folder is empty
991 *\param queue Queue folder to process
993 *\return TRUE if the queue folder is empty, otherwise return FALSE
995 gboolean procmsg_queue_is_empty(FolderItem *queue)
998 gboolean res = FALSE;
1000 queue = folder_get_default_queue();
1001 g_return_val_if_fail(queue != NULL, TRUE);
1003 folder_item_scan(queue);
1004 list = folder_item_get_msg_list(queue);
1005 res = (list == NULL);
1006 procmsg_msg_list_free(list);
1010 if (queue->node && queue->node->children) {
1011 node = queue->node->children;
1012 while (node != NULL) {
1014 if (!procmsg_queue_is_empty(FOLDER_ITEM(node->data)))
1023 gint procmsg_remove_special_headers(const gchar *in, const gchar *out)
1026 gchar buf[BUFFSIZE];
1028 if ((fp = g_fopen(in, "rb")) == NULL) {
1029 FILE_OP_ERROR(in, "fopen");
1032 if ((outfp = g_fopen(out, "wb")) == NULL) {
1033 FILE_OP_ERROR(out, "fopen");
1037 while (fgets(buf, sizeof(buf), fp) != NULL) {
1039 if (!strncmp(buf, "X-Sylpheed-End-Special-Headers: 1",
1040 strlen("X-Sylpheed-End-Special-Headers:")))
1043 if (buf[0] == '\r' || buf[0] == '\n') break;
1044 /* from other mailers */
1045 if (!strncmp(buf, "Date: ", 6)
1046 || !strncmp(buf, "To: ", 4)
1047 || !strncmp(buf, "From: ", 6)
1048 || !strncmp(buf, "Subject: ", 9)) {
1053 while (fgets(buf, sizeof(buf), fp) != NULL)
1060 gint procmsg_save_to_outbox(FolderItem *outbox, const gchar *file,
1064 MsgInfo *msginfo, *tmp_msginfo;
1065 MsgFlags flag = {0, 0};
1067 debug_print("saving sent message...\n");
1070 outbox = folder_get_default_outbox();
1071 g_return_val_if_fail(outbox != NULL, -1);
1073 /* remove queueing headers */
1075 gchar tmp[MAXPATHLEN + 1];
1077 g_snprintf(tmp, sizeof(tmp), "%s%ctmpmsg.out.%08x",
1078 get_rc_dir(), G_DIR_SEPARATOR, (guint) rand());
1080 if (procmsg_remove_special_headers(file, tmp) !=0)
1083 folder_item_scan(outbox);
1084 if ((num = folder_item_add_msg(outbox, tmp, &flag, TRUE)) < 0) {
1085 g_warning("can't save message\n");
1090 folder_item_scan(outbox);
1091 if ((num = folder_item_add_msg
1092 (outbox, file, &flag, FALSE)) < 0) {
1093 g_warning("can't save message\n");
1097 msginfo = folder_item_get_msginfo(outbox, num); /* refcnt++ */
1098 tmp_msginfo = procmsg_msginfo_get_full_info(msginfo); /* refcnt++ */
1099 if (msginfo != NULL) {
1100 procmsg_msginfo_unset_flags(msginfo, ~0, 0);
1101 procmsg_msginfo_free(msginfo); /* refcnt-- */
1102 /* tmp_msginfo == msginfo */
1103 if (tmp_msginfo && (msginfo->dispositionnotificationto ||
1104 msginfo->returnreceiptto)) {
1105 procmsg_msginfo_set_flags(msginfo, MSG_RETRCPT_SENT, 0);
1107 procmsg_msginfo_free(tmp_msginfo); /* refcnt-- */
1113 void procmsg_print_message(MsgInfo *msginfo, const gchar *cmdline)
1115 static const gchar *def_cmd = "lpr %s";
1116 static guint id = 0;
1122 g_return_if_fail(msginfo);
1124 if (procmime_msginfo_is_encrypted(msginfo))
1125 tmpfp = procmime_get_first_encrypted_text_content(msginfo);
1127 tmpfp = procmime_get_first_text_content(msginfo);
1128 if (tmpfp == NULL) {
1129 g_warning("Can't get text part\n");
1133 prtmp = g_strdup_printf("%s%cprinttmp.%08x",
1134 get_mime_tmp_dir(), G_DIR_SEPARATOR, id++);
1136 if ((prfp = g_fopen(prtmp, "wb")) == NULL) {
1137 FILE_OP_ERROR(prtmp, "fopen");
1143 if (msginfo->date) fprintf(prfp, "Date: %s\n", msginfo->date);
1144 if (msginfo->from) fprintf(prfp, "From: %s\n", msginfo->from);
1145 if (msginfo->to) fprintf(prfp, "To: %s\n", msginfo->to);
1146 if (msginfo->cc) fprintf(prfp, "Cc: %s\n", msginfo->cc);
1147 if (msginfo->newsgroups)
1148 fprintf(prfp, "Newsgroups: %s\n", msginfo->newsgroups);
1149 if (msginfo->subject) fprintf(prfp, "Subject: %s\n", msginfo->subject);
1152 while (fgets(buf, sizeof(buf), tmpfp) != NULL)
1158 if (cmdline && (p = strchr(cmdline, '%')) && *(p + 1) == 's' &&
1159 !strchr(p + 2, '%'))
1160 g_snprintf(buf, sizeof(buf) - 1, cmdline, prtmp);
1163 g_warning("Print command line is invalid: '%s'\n",
1165 g_snprintf(buf, sizeof(buf) - 1, def_cmd, prtmp);
1171 if (buf[strlen(buf) - 1] != '&') strcat(buf, "&");
1175 MsgInfo *procmsg_msginfo_new_ref(MsgInfo *msginfo)
1182 MsgInfo *procmsg_msginfo_new(void)
1184 MsgInfo *newmsginfo;
1186 newmsginfo = g_new0(MsgInfo, 1);
1187 newmsginfo->refcnt = 1;
1192 MsgInfo *procmsg_msginfo_copy(MsgInfo *msginfo)
1194 MsgInfo *newmsginfo;
1197 if (msginfo == NULL) return NULL;
1199 newmsginfo = g_new0(MsgInfo, 1);
1201 newmsginfo->refcnt = 1;
1203 #define MEMBCOPY(mmb) newmsginfo->mmb = msginfo->mmb
1204 #define MEMBDUP(mmb) newmsginfo->mmb = msginfo->mmb ? \
1205 g_strdup(msginfo->mmb) : NULL
1220 MEMBDUP(newsgroups);
1227 MEMBCOPY(to_folder);
1231 MEMBDUP(dispositionnotificationto);
1232 MEMBDUP(returnreceiptto);
1234 refs = msginfo->references;
1235 for (refs = msginfo->references; refs != NULL; refs = refs->next) {
1236 newmsginfo->references = g_slist_prepend
1237 (newmsginfo->references, g_strdup(refs->data));
1239 newmsginfo->references = g_slist_reverse(newmsginfo->references);
1242 MEMBDUP(plaintext_file);
1247 MsgInfo *procmsg_msginfo_get_full_info(MsgInfo *msginfo)
1249 MsgInfo *full_msginfo;
1252 if (msginfo == NULL) return NULL;
1254 file = procmsg_get_message_file_path(msginfo);
1255 if (!file || !is_file_exist(file)) {
1257 file = procmsg_get_message_file(msginfo);
1259 if (!file || !is_file_exist(file)) {
1260 g_warning("procmsg_msginfo_get_full_info(): can't get message file.\n");
1264 full_msginfo = procheader_parse_file(file, msginfo->flags, TRUE, FALSE);
1266 if (!full_msginfo) return NULL;
1268 /* CLAWS: make sure we add the missing members; see:
1269 * procheader.c::procheader_get_headernames() */
1270 if (!msginfo->list_post)
1271 msginfo->list_post = g_strdup(full_msginfo->list_post);
1272 if (!msginfo->list_subscribe)
1273 msginfo->list_subscribe = g_strdup(full_msginfo->list_subscribe);
1274 if (!msginfo->list_unsubscribe)
1275 msginfo->list_unsubscribe = g_strdup(full_msginfo->list_unsubscribe);
1276 if (!msginfo->list_help)
1277 msginfo->list_help = g_strdup(full_msginfo->list_help);
1278 if (!msginfo->list_archive)
1279 msginfo->list_archive= g_strdup(full_msginfo->list_archive);
1280 if (!msginfo->list_owner)
1281 msginfo->list_owner = g_strdup(full_msginfo->list_owner);
1282 if (!msginfo->xface)
1283 msginfo->xface = g_strdup(full_msginfo->xface);
1285 msginfo->face = g_strdup(full_msginfo->face);
1286 if (!msginfo->dispositionnotificationto)
1287 msginfo->dispositionnotificationto =
1288 g_strdup(full_msginfo->dispositionnotificationto);
1289 if (!msginfo->returnreceiptto)
1290 msginfo->returnreceiptto = g_strdup
1291 (full_msginfo->returnreceiptto);
1292 if (!msginfo->partial_recv && full_msginfo->partial_recv)
1293 msginfo->partial_recv = g_strdup
1294 (full_msginfo->partial_recv);
1295 msginfo->total_size = full_msginfo->total_size;
1296 if (!msginfo->account_server && full_msginfo->account_server)
1297 msginfo->account_server = g_strdup
1298 (full_msginfo->account_server);
1299 if (!msginfo->account_login && full_msginfo->account_login)
1300 msginfo->account_login = g_strdup
1301 (full_msginfo->account_login);
1302 msginfo->planned_download = full_msginfo->planned_download;
1303 procmsg_msginfo_free(full_msginfo);
1305 return procmsg_msginfo_new_ref(msginfo);
1308 void procmsg_msginfo_free(MsgInfo *msginfo)
1310 if (msginfo == NULL) return;
1313 if (msginfo->refcnt > 0)
1316 if (msginfo->to_folder) {
1317 msginfo->to_folder->op_count--;
1318 folder_item_update(msginfo->to_folder, F_ITEM_UPDATE_MSGCNT);
1321 g_free(msginfo->fromspace);
1322 g_free(msginfo->returnreceiptto);
1323 g_free(msginfo->dispositionnotificationto);
1324 g_free(msginfo->xface);
1325 g_free(msginfo->face);
1327 g_free(msginfo->fromname);
1329 g_free(msginfo->date);
1330 g_free(msginfo->from);
1331 g_free(msginfo->to);
1332 g_free(msginfo->cc);
1333 g_free(msginfo->newsgroups);
1334 g_free(msginfo->subject);
1335 g_free(msginfo->msgid);
1336 g_free(msginfo->inreplyto);
1337 g_free(msginfo->xref);
1339 g_free(msginfo->list_post);
1340 g_free(msginfo->list_subscribe);
1341 g_free(msginfo->list_unsubscribe);
1342 g_free(msginfo->list_help);
1343 g_free(msginfo->list_archive);
1344 g_free(msginfo->list_owner);
1346 g_free(msginfo->partial_recv);
1347 g_free(msginfo->account_server);
1348 g_free(msginfo->account_login);
1350 slist_free_strings(msginfo->references);
1351 g_slist_free(msginfo->references);
1353 g_free(msginfo->plaintext_file);
1358 guint procmsg_msginfo_memusage(MsgInfo *msginfo)
1363 memusage += sizeof(MsgInfo);
1364 if (msginfo->fromname)
1365 memusage += strlen(msginfo->fromname);
1367 memusage += strlen(msginfo->date);
1369 memusage += strlen(msginfo->from);
1371 memusage += strlen(msginfo->to);
1373 memusage += strlen(msginfo->cc);
1374 if (msginfo->newsgroups)
1375 memusage += strlen(msginfo->newsgroups);
1376 if (msginfo->subject)
1377 memusage += strlen(msginfo->subject);
1379 memusage += strlen(msginfo->msgid);
1380 if (msginfo->inreplyto)
1381 memusage += strlen(msginfo->inreplyto);
1383 memusage += strlen(msginfo->xface);
1385 memusage += strlen(msginfo->face);
1386 if (msginfo->dispositionnotificationto)
1387 memusage += strlen(msginfo->dispositionnotificationto);
1388 if (msginfo->returnreceiptto)
1389 memusage += strlen(msginfo->returnreceiptto);
1390 for (refs = msginfo->references; refs; refs=refs->next) {
1391 gchar *r = (gchar *)refs->data;
1392 memusage += r?strlen(r):0;
1394 if (msginfo->fromspace)
1395 memusage += strlen(msginfo->fromspace);
1400 gint procmsg_cmp_msgnum_for_sort(gconstpointer a, gconstpointer b)
1402 const MsgInfo *msginfo1 = a;
1403 const MsgInfo *msginfo2 = b;
1410 return msginfo1->msgnum - msginfo2->msgnum;
1413 static gint procmsg_send_message_queue_full(const gchar *file, gboolean keep_session, gchar **errstr)
1415 static HeaderEntry qentry[] = {{"S:", NULL, FALSE},
1416 {"SSV:", NULL, FALSE},
1417 {"R:", NULL, FALSE},
1418 {"NG:", NULL, FALSE},
1419 {"MAID:", NULL, FALSE},
1420 {"NAID:", NULL, FALSE},
1421 {"SCF:", NULL, FALSE},
1422 {"RMID:", NULL, FALSE},
1423 {"FMID:", NULL, FALSE},
1424 {"X-Sylpheed-Privacy-System:", NULL, FALSE},
1425 {"X-Sylpheed-Encrypt:", NULL, FALSE},
1426 {"X-Sylpheed-Encrypt-Data:", NULL, FALSE},
1427 {"X-Sylpheed-End-Special-Headers:", NULL, FALSE},
1428 {NULL, NULL, FALSE}};
1431 gint mailval = 0, newsval = 0;
1433 gchar *smtpserver = NULL;
1434 GSList *to_list = NULL;
1435 GSList *newsgroup_list = NULL;
1436 gchar *savecopyfolder = NULL;
1437 gchar *replymessageid = NULL;
1438 gchar *fwdmessageid = NULL;
1439 gchar *privacy_system = NULL;
1440 gboolean encrypt = FALSE;
1441 gchar *encrypt_data = NULL;
1442 gchar buf[BUFFSIZE];
1444 PrefsAccount *mailac = NULL, *newsac = NULL;
1445 gboolean save_clear_text = TRUE;
1446 gchar *tmp_enc_file = NULL;
1450 g_return_val_if_fail(file != NULL, -1);
1452 if ((fp = g_fopen(file, "rb")) == NULL) {
1453 FILE_OP_ERROR(file, "fopen");
1455 if (*errstr) g_free(*errstr);
1456 *errstr = g_strdup_printf(_("Couldn't open file %s."), file);
1461 while ((hnum = procheader_get_one_field(buf, sizeof(buf), fp, qentry))
1463 gchar *p = buf + strlen(qentry[hnum].name);
1471 if (smtpserver == NULL)
1472 smtpserver = g_strdup(p);
1475 to_list = address_list_append(to_list, p);
1478 newsgroup_list = newsgroup_list_append(newsgroup_list, p);
1480 case Q_MAIL_ACCOUNT_ID:
1481 mailac = account_find_from_id(atoi(p));
1483 case Q_NEWS_ACCOUNT_ID:
1484 newsac = account_find_from_id(atoi(p));
1486 case Q_SAVE_COPY_FOLDER:
1487 if (savecopyfolder == NULL)
1488 savecopyfolder = g_strdup(p);
1490 case Q_REPLY_MESSAGE_ID:
1491 if (replymessageid == NULL)
1492 replymessageid = g_strdup(p);
1494 case Q_FWD_MESSAGE_ID:
1495 if (fwdmessageid == NULL)
1496 fwdmessageid = g_strdup(p);
1498 case Q_PRIVACY_SYSTEM:
1499 if (privacy_system == NULL)
1500 privacy_system = g_strdup(p);
1506 case Q_ENCRYPT_DATA:
1507 if (encrypt_data == NULL)
1508 encrypt_data = g_strdup(p);
1510 case Q_SYLPHEED_HDRS:
1511 /* end of special headers reached */
1512 goto send_mail; /* can't "break;break;" */
1516 filepos = ftell(fp);
1521 if (mailac && mailac->save_encrypted_as_clear_text
1522 && !mailac->encrypt_to_self)
1523 save_clear_text = TRUE;
1525 save_clear_text = FALSE;
1530 mimeinfo = procmime_scan_queue_file(file);
1531 if (!privacy_encrypt(privacy_system, mimeinfo, encrypt_data)
1532 || (fp = my_tmpfile()) == NULL
1533 || procmime_write_mimeinfo(mimeinfo, fp) < 0) {
1536 procmime_mimeinfo_free_all(mimeinfo);
1539 slist_free_strings(to_list);
1540 g_slist_free(to_list);
1541 slist_free_strings(newsgroup_list);
1542 g_slist_free(newsgroup_list);
1543 g_free(savecopyfolder);
1544 g_free(replymessageid);
1545 g_free(fwdmessageid);
1546 g_free(privacy_system);
1547 g_free(encrypt_data);
1549 if (*errstr) g_free(*errstr);
1550 *errstr = g_strdup_printf(_("Couldn't encrypt the email: %s"),
1551 privacy_get_error());
1557 if (!save_clear_text) {
1558 gchar *content = NULL;
1559 FILE *tmpfp = get_tmpfile_in_dir(get_mime_tmp_dir(), &tmp_enc_file);
1563 content = file_read_stream_to_str(fp);
1566 str_write_to_file(content, tmp_enc_file);
1569 g_warning("couldn't get tempfile\n");
1573 procmime_mimeinfo_free_all(mimeinfo);
1579 debug_print("Sending message by mail\n");
1582 if (*errstr) g_free(*errstr);
1583 *errstr = g_strdup_printf(_("Queued message header is broken."));
1586 } else if (mailac && mailac->use_mail_command &&
1587 mailac->mail_command && (* mailac->mail_command)) {
1588 mailval = send_message_local(mailac->mail_command, fp);
1592 mailac = account_find_from_smtp_server(from, smtpserver);
1594 g_warning("Account not found. "
1595 "Using current account...\n");
1596 mailac = cur_account;
1601 mailval = send_message_smtp_full(mailac, to_list, fp, keep_session);
1602 if (mailval == -1 && errstr) {
1603 if (*errstr) g_free(*errstr);
1604 *errstr = g_strdup_printf(_("An error happened during SMTP session."));
1607 PrefsAccount tmp_ac;
1609 g_warning("Account not found.\n");
1611 memset(&tmp_ac, 0, sizeof(PrefsAccount));
1612 tmp_ac.address = from;
1613 tmp_ac.smtp_server = smtpserver;
1614 tmp_ac.smtpport = SMTP_PORT;
1615 mailval = send_message_smtp(&tmp_ac, to_list, fp);
1616 if (mailval == -1 && errstr) {
1617 if (*errstr) g_free(*errstr);
1618 *errstr = g_strdup_printf(_("No specific account has been found to "
1619 "send, and an error happened during SMTP session."));
1623 } else if (!to_list && !newsgroup_list) {
1625 if (*errstr) g_free(*errstr);
1626 *errstr = g_strdup(_("Couldn't determine sending informations. "
1627 "Maybe the email hasn't been generated by Sylpheed-Claws."));
1632 fseek(fp, filepos, SEEK_SET);
1633 if (newsgroup_list && (mailval == 0)) {
1638 /* write to temporary file */
1639 tmp = g_strdup_printf("%s%ctmp%d", g_get_tmp_dir(),
1640 G_DIR_SEPARATOR, (gint)file);
1641 if ((tmpfp = g_fopen(tmp, "wb")) == NULL) {
1642 FILE_OP_ERROR(tmp, "fopen");
1644 alertpanel_error(_("Could not create temporary file for news sending."));
1646 if (change_file_mode_rw(tmpfp, tmp) < 0) {
1647 FILE_OP_ERROR(tmp, "chmod");
1648 g_warning("can't change file mode\n");
1651 while ((newsval == 0) && fgets(buf, sizeof(buf), fp) != NULL) {
1652 if (fputs(buf, tmpfp) == EOF) {
1653 FILE_OP_ERROR(tmp, "fputs");
1656 if (*errstr) g_free(*errstr);
1657 *errstr = g_strdup_printf(_("Error when writing temporary file for news sending."));
1664 debug_print("Sending message by news\n");
1666 folder = FOLDER(newsac->folder);
1668 newsval = news_post(folder, tmp);
1669 if (newsval < 0 && errstr) {
1670 if (*errstr) g_free(*errstr);
1671 *errstr = g_strdup_printf(_("Error occurred while posting the message to %s."),
1672 newsac->nntp_server);
1682 /* save message to outbox */
1683 if (mailval == 0 && newsval == 0 && savecopyfolder) {
1686 debug_print("saving sent message...\n");
1688 outbox = folder_find_item_from_identifier(savecopyfolder);
1690 outbox = folder_get_default_outbox();
1692 if (save_clear_text || tmp_enc_file == NULL) {
1693 procmsg_save_to_outbox(outbox, file, TRUE);
1695 procmsg_save_to_outbox(outbox, tmp_enc_file, FALSE);
1699 if (tmp_enc_file != NULL) {
1700 g_unlink(tmp_enc_file);
1702 tmp_enc_file = NULL;
1705 if (replymessageid != NULL || fwdmessageid != NULL) {
1709 if (replymessageid != NULL)
1710 tokens = g_strsplit(replymessageid, "\t", 0);
1712 tokens = g_strsplit(fwdmessageid, "\t", 0);
1713 item = folder_find_item_from_identifier(tokens[0]);
1715 /* check if queued message has valid folder and message id */
1716 if (item != NULL && tokens[2] != NULL) {
1719 msginfo = folder_item_get_msginfo(item, atoi(tokens[1]));
1721 /* check if referring message exists and has a message id */
1722 if ((msginfo != NULL) &&
1723 (msginfo->msgid != NULL) &&
1724 (strcmp(msginfo->msgid, tokens[2]) != 0)) {
1725 procmsg_msginfo_free(msginfo);
1729 if (msginfo == NULL) {
1730 msginfo = folder_item_get_msginfo_by_msgid(item, tokens[2]);
1733 if (msginfo != NULL) {
1734 if (replymessageid != NULL) {
1735 procmsg_msginfo_unset_flags(msginfo, MSG_FORWARDED, 0);
1736 procmsg_msginfo_set_flags(msginfo, MSG_REPLIED, 0);
1738 procmsg_msginfo_unset_flags(msginfo, MSG_REPLIED, 0);
1739 procmsg_msginfo_set_flags(msginfo, MSG_FORWARDED, 0);
1741 procmsg_msginfo_free(msginfo);
1749 slist_free_strings(to_list);
1750 g_slist_free(to_list);
1751 slist_free_strings(newsgroup_list);
1752 g_slist_free(newsgroup_list);
1753 g_free(savecopyfolder);
1754 g_free(replymessageid);
1755 g_free(fwdmessageid);
1756 g_free(privacy_system);
1757 g_free(encrypt_data);
1759 return (newsval != 0 ? newsval : mailval);
1762 gint procmsg_send_message_queue(const gchar *file, gchar **errstr)
1764 gint result = procmsg_send_message_queue_full(file, FALSE, errstr);
1765 toolbar_main_set_sensitive(mainwindow_get_mainwindow());
1769 static void update_folder_msg_counts(FolderItem *item, MsgInfo *msginfo, MsgPermFlags old_flags)
1771 MsgPermFlags new_flags = msginfo->flags.perm_flags;
1774 if (!(old_flags & MSG_NEW) && (new_flags & MSG_NEW)) {
1778 if ((old_flags & MSG_NEW) && !(new_flags & MSG_NEW)) {
1783 if (!(old_flags & MSG_UNREAD) && (new_flags & MSG_UNREAD)) {
1784 item->unread_msgs++;
1785 if (procmsg_msg_has_marked_parent(msginfo))
1786 item->unreadmarked_msgs++;
1789 if ((old_flags & MSG_UNREAD) && !(new_flags & MSG_UNREAD)) {
1790 item->unread_msgs--;
1791 if (procmsg_msg_has_marked_parent(msginfo))
1792 item->unreadmarked_msgs--;
1796 if (!(old_flags & MSG_MARKED) && (new_flags & MSG_MARKED)) {
1797 procmsg_update_unread_children(msginfo, TRUE);
1798 item->marked_msgs++;
1801 if ((old_flags & MSG_MARKED) && !(new_flags & MSG_MARKED)) {
1802 procmsg_update_unread_children(msginfo, FALSE);
1803 item->marked_msgs--;
1807 void procmsg_msginfo_set_flags(MsgInfo *msginfo, MsgPermFlags perm_flags, MsgTmpFlags tmp_flags)
1810 MsgInfoUpdate msginfo_update;
1811 MsgPermFlags perm_flags_new, perm_flags_old;
1812 MsgTmpFlags tmp_flags_old;
1814 g_return_if_fail(msginfo != NULL);
1815 item = msginfo->folder;
1816 g_return_if_fail(item != NULL);
1818 debug_print("Setting flags for message %d in folder %s\n", msginfo->msgnum, item->path);
1820 /* Perm Flags handling */
1821 perm_flags_old = msginfo->flags.perm_flags;
1822 perm_flags_new = msginfo->flags.perm_flags | perm_flags;
1823 if ((perm_flags & MSG_IGNORE_THREAD) || (perm_flags_old & MSG_IGNORE_THREAD)) {
1824 perm_flags_new &= ~(MSG_NEW | MSG_UNREAD);
1827 if (perm_flags_old != perm_flags_new) {
1828 folder_item_change_msg_flags(msginfo->folder, msginfo, perm_flags_new);
1830 update_folder_msg_counts(item, msginfo, perm_flags_old);
1834 /* Tmp flags handling */
1835 tmp_flags_old = msginfo->flags.tmp_flags;
1836 msginfo->flags.tmp_flags |= tmp_flags;
1838 /* update notification */
1839 if ((perm_flags_old != perm_flags_new) || (tmp_flags_old != msginfo->flags.tmp_flags)) {
1840 msginfo_update.msginfo = msginfo;
1841 msginfo_update.flags = MSGINFO_UPDATE_FLAGS;
1842 hooks_invoke(MSGINFO_UPDATE_HOOKLIST, &msginfo_update);
1843 folder_item_update(msginfo->folder, F_ITEM_UPDATE_MSGCNT);
1847 void procmsg_msginfo_unset_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("Unsetting 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;
1864 if (perm_flags_old != perm_flags_new) {
1865 folder_item_change_msg_flags(msginfo->folder, msginfo, perm_flags_new);
1867 update_folder_msg_counts(item, msginfo, perm_flags_old);
1870 /* Tmp flags hanlding */
1871 tmp_flags_old = msginfo->flags.tmp_flags;
1872 msginfo->flags.tmp_flags &= ~tmp_flags;
1874 /* update notification */
1875 if ((perm_flags_old != perm_flags_new) || (tmp_flags_old != msginfo->flags.tmp_flags)) {
1876 msginfo_update.msginfo = msginfo;
1877 msginfo_update.flags = MSGINFO_UPDATE_FLAGS;
1878 hooks_invoke(MSGINFO_UPDATE_HOOKLIST, &msginfo_update);
1879 folder_item_update(msginfo->folder, F_ITEM_UPDATE_MSGCNT);
1883 void procmsg_msginfo_change_flags(MsgInfo *msginfo,
1884 MsgPermFlags add_perm_flags, MsgTmpFlags add_tmp_flags,
1885 MsgPermFlags rem_perm_flags, MsgTmpFlags rem_tmp_flags)
1888 MsgInfoUpdate msginfo_update;
1889 MsgPermFlags perm_flags_new, perm_flags_old;
1890 MsgTmpFlags tmp_flags_old;
1892 g_return_if_fail(msginfo != NULL);
1893 item = msginfo->folder;
1894 g_return_if_fail(item != NULL);
1896 debug_print("Changing flags for message %d in folder %s\n", msginfo->msgnum, item->path);
1898 /* Perm Flags handling */
1899 perm_flags_old = msginfo->flags.perm_flags;
1900 perm_flags_new = (msginfo->flags.perm_flags & ~rem_perm_flags) | add_perm_flags;
1901 if ((add_perm_flags & MSG_IGNORE_THREAD) || (perm_flags_old & MSG_IGNORE_THREAD)) {
1902 perm_flags_new &= ~(MSG_NEW | MSG_UNREAD);
1905 if (perm_flags_old != perm_flags_new) {
1906 folder_item_change_msg_flags(msginfo->folder, msginfo, perm_flags_new);
1908 update_folder_msg_counts(item, msginfo, perm_flags_old);
1912 /* Tmp flags handling */
1913 tmp_flags_old = msginfo->flags.tmp_flags;
1914 msginfo->flags.tmp_flags &= ~rem_tmp_flags;
1915 msginfo->flags.tmp_flags |= add_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);
1927 *\brief check for flags (e.g. mark) in prior msgs of current thread
1929 *\param info Current message
1930 *\param perm_flags Flags to be checked
1931 *\param parentmsgs Hash of prior msgs to avoid loops
1933 *\return gboolean TRUE if perm_flags are found
1935 gboolean procmsg_msg_has_flagged_parent_real(MsgInfo *info,
1936 MsgPermFlags perm_flags, GHashTable *parentmsgs)
1940 g_return_val_if_fail(info != NULL, FALSE);
1942 if (info != NULL && info->folder != NULL && info->inreplyto != NULL) {
1943 tmp = folder_item_get_msginfo_by_msgid(info->folder,
1945 if (tmp && (tmp->flags.perm_flags & perm_flags)) {
1946 procmsg_msginfo_free(tmp);
1948 } else if (tmp != NULL) {
1951 if (g_hash_table_lookup(parentmsgs, info)) {
1952 debug_print("loop detected: %d\n",
1956 g_hash_table_insert(parentmsgs, info, "1");
1957 result = procmsg_msg_has_flagged_parent_real(
1958 tmp, perm_flags, parentmsgs);
1960 procmsg_msginfo_free(tmp);
1970 *\brief Callback for cleaning up hash of parentmsgs
1972 gboolean parentmsgs_hash_remove(gpointer key,
1980 *\brief Set up list of parentmsgs
1981 * See procmsg_msg_has_flagged_parent_real()
1983 gboolean procmsg_msg_has_flagged_parent(MsgInfo *info, MsgPermFlags perm_flags)
1986 GHashTable *parentmsgs = g_hash_table_new(NULL, NULL);
1988 result = procmsg_msg_has_flagged_parent_real(info, perm_flags, parentmsgs);
1989 g_hash_table_foreach_remove(parentmsgs, parentmsgs_hash_remove, NULL);
1990 g_hash_table_destroy(parentmsgs);
1995 *\brief Check if msgs prior in thread are marked
1996 * See procmsg_msg_has_flagged_parent_real()
1998 gboolean procmsg_msg_has_marked_parent(MsgInfo *info)
2000 return procmsg_msg_has_flagged_parent(info, MSG_MARKED);
2004 GSList *procmsg_find_children_func(MsgInfo *info,
2005 GSList *children, GSList *all)
2009 g_return_val_if_fail(info!=NULL, children);
2010 if (info->msgid == NULL)
2013 for (cur = all; cur != NULL; cur = g_slist_next(cur)) {
2014 MsgInfo *tmp = (MsgInfo *)cur->data;
2015 if (tmp->inreplyto && !strcmp(tmp->inreplyto, info->msgid)) {
2016 /* Check if message is already in the list */
2017 if ((children == NULL) ||
2018 (g_slist_index(children, tmp) == -1)) {
2019 children = g_slist_prepend(children,
2020 procmsg_msginfo_new_ref(tmp));
2021 children = procmsg_find_children_func(tmp,
2030 GSList *procmsg_find_children (MsgInfo *info)
2035 g_return_val_if_fail(info!=NULL, NULL);
2036 all = folder_item_get_msg_list(info->folder);
2037 children = procmsg_find_children_func(info, NULL, all);
2038 if (children != NULL) {
2039 for (cur = all; cur != NULL; cur = g_slist_next(cur)) {
2040 /* this will not free the used pointers
2041 created with procmsg_msginfo_new_ref */
2042 procmsg_msginfo_free((MsgInfo *)cur->data);
2050 void procmsg_update_unread_children(MsgInfo *info, gboolean newly_marked)
2052 GSList *children = procmsg_find_children(info);
2054 for (cur = children; cur != NULL; cur = g_slist_next(cur)) {
2055 MsgInfo *tmp = (MsgInfo *)cur->data;
2056 if(MSG_IS_UNREAD(tmp->flags) && !MSG_IS_IGNORE_THREAD(tmp->flags)) {
2058 info->folder->unreadmarked_msgs++;
2060 info->folder->unreadmarked_msgs--;
2061 folder_item_update(info->folder, F_ITEM_UPDATE_MSGCNT);
2063 procmsg_msginfo_free(tmp);
2065 g_slist_free(children);
2069 * Set the destination folder for a copy or move operation
2071 * \param msginfo The message which's destination folder is changed
2072 * \param to_folder The destination folder for the operation
2074 void procmsg_msginfo_set_to_folder(MsgInfo *msginfo, FolderItem *to_folder)
2076 if(msginfo->to_folder != NULL) {
2077 msginfo->to_folder->op_count--;
2078 folder_item_update(msginfo->to_folder, F_ITEM_UPDATE_MSGCNT);
2080 msginfo->to_folder = to_folder;
2081 if(to_folder != NULL) {
2082 to_folder->op_count++;
2083 folder_item_update(msginfo->to_folder, F_ITEM_UPDATE_MSGCNT);
2088 * Apply filtering actions to the msginfo
2090 * \param msginfo The MsgInfo describing the message that should be filtered
2091 * \return TRUE if the message was moved and MsgInfo is now invalid,
2094 gboolean procmsg_msginfo_filter(MsgInfo *msginfo, PrefsAccount* ac_prefs)
2096 MailFilteringData mail_filtering_data;
2098 mail_filtering_data.msginfo = msginfo;
2099 if (hooks_invoke(MAIL_FILTERING_HOOKLIST, &mail_filtering_data)) {
2103 /* filter if enabled in prefs or move to inbox if not */
2104 if((filtering_rules != NULL) &&
2105 filter_message_by_msginfo(filtering_rules, msginfo, ac_prefs)) {
2112 MsgInfo *procmsg_msginfo_new_from_mimeinfo(MsgInfo *src_msginfo, MimeInfo *mimeinfo)
2114 MsgInfo *tmp_msginfo = NULL;
2115 MsgFlags flags = {0, 0};
2116 gchar *tmpfile = get_tmp_file();
2117 FILE *fp = g_fopen(tmpfile, "wb");
2119 if (!mimeinfo || mimeinfo->type != MIMETYPE_MESSAGE ||
2120 g_ascii_strcasecmp(mimeinfo->subtype, "rfc822")) {
2121 g_warning("procmsg_msginfo_new_from_mimeinfo(): unsuitable mimeinfo");
2128 if (fp && procmime_write_mimeinfo(mimeinfo, fp) >= 0) {
2131 tmp_msginfo = procheader_parse_file(
2138 if (tmp_msginfo != NULL) {
2140 tmp_msginfo->folder = src_msginfo->folder;
2141 tmp_msginfo->plaintext_file = g_strdup(tmpfile);
2143 g_warning("procmsg_msginfo_new_from_mimeinfo(): Can't generate new msginfo");
2151 static GSList *spam_learners = NULL;
2153 void procmsg_register_spam_learner (int (*learn_func)(MsgInfo *info, GSList *list, gboolean spam))
2155 if (!g_slist_find(spam_learners, learn_func))
2156 spam_learners = g_slist_append(spam_learners, learn_func);
2157 if (mainwindow_get_mainwindow()) {
2158 main_window_set_menu_sensitive(mainwindow_get_mainwindow());
2159 summary_set_menu_sensitive(
2160 mainwindow_get_mainwindow()->summaryview);
2161 toolbar_main_set_sensitive(mainwindow_get_mainwindow());
2165 void procmsg_unregister_spam_learner (int (*learn_func)(MsgInfo *info, GSList *list, gboolean spam))
2167 spam_learners = g_slist_remove(spam_learners, learn_func);
2168 if (mainwindow_get_mainwindow()) {
2169 main_window_set_menu_sensitive(mainwindow_get_mainwindow());
2170 summary_set_menu_sensitive(
2171 mainwindow_get_mainwindow()->summaryview);
2172 toolbar_main_set_sensitive(mainwindow_get_mainwindow());
2176 gboolean procmsg_spam_can_learn(void)
2178 return g_slist_length(spam_learners) > 0;
2181 int procmsg_spam_learner_learn (MsgInfo *info, GSList *list, gboolean spam)
2183 GSList *cur = spam_learners;
2185 for (; cur; cur = cur->next) {
2186 int ((*func)(MsgInfo *info, GSList *list, gboolean spam)) = cur->data;
2187 ret |= func(info, list, spam);
2192 static gchar *spam_folder_item = NULL;
2193 void procmsg_spam_set_folder (const char *item_identifier)
2195 g_free(spam_folder_item);
2196 if (item_identifier)
2197 spam_folder_item = g_strdup(item_identifier);
2199 spam_folder_item = NULL;
2202 FolderItem *procmsg_spam_get_folder (void)
2204 FolderItem *item = spam_folder_item ? folder_find_item_from_identifier(spam_folder_item) : NULL;
2205 return item ? item : folder_get_default_trash();
2208 static void item_has_queued_mails(FolderItem *item, gpointer data)
2210 gboolean *result = (gboolean *)data;
2211 if (*result == TRUE)
2213 if (folder_has_parent_of_type(item, F_QUEUE) && item->total_msgs > 0)
2217 gboolean procmsg_have_queued_mails_fast (void)
2219 gboolean result = FALSE;
2220 folder_func_to_all_folders(item_has_queued_mails, &result);