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"
46 static gint procmsg_send_message_queue_full(const gchar *file, gboolean keep_session);
54 Q_MAIL_ACCOUNT_ID = 4,
55 Q_NEWS_ACCOUNT_ID = 5,
56 Q_SAVE_COPY_FOLDER = 6,
57 Q_REPLY_MESSAGE_ID = 7,
64 GHashTable *procmsg_msg_hash_table_create(GSList *mlist)
66 GHashTable *msg_table;
68 if (mlist == NULL) return NULL;
70 msg_table = g_hash_table_new(NULL, g_direct_equal);
71 procmsg_msg_hash_table_append(msg_table, mlist);
76 void procmsg_msg_hash_table_append(GHashTable *msg_table, GSList *mlist)
81 if (msg_table == NULL || mlist == NULL) return;
83 for (cur = mlist; cur != NULL; cur = cur->next) {
84 msginfo = (MsgInfo *)cur->data;
86 g_hash_table_insert(msg_table,
87 GUINT_TO_POINTER(msginfo->msgnum),
92 GHashTable *procmsg_to_folder_hash_table_create(GSList *mlist)
94 GHashTable *msg_table;
98 if (mlist == NULL) return NULL;
100 msg_table = g_hash_table_new(NULL, g_direct_equal);
102 for (cur = mlist; cur != NULL; cur = cur->next) {
103 msginfo = (MsgInfo *)cur->data;
104 g_hash_table_insert(msg_table, msginfo->to_folder, msginfo);
110 gint procmsg_get_last_num_in_msg_list(GSList *mlist)
116 for (cur = mlist; cur != NULL; cur = cur->next) {
117 msginfo = (MsgInfo *)cur->data;
118 if (msginfo && msginfo->msgnum > last)
119 last = msginfo->msgnum;
125 void procmsg_msg_list_free(GSList *mlist)
130 for (cur = mlist; cur != NULL; cur = cur->next) {
131 msginfo = (MsgInfo *)cur->data;
132 procmsg_msginfo_free(msginfo);
146 /* CLAWS subject threading:
148 in the first round it inserts subject lines in a
149 relation (subject <-> node)
151 the second round finishes the threads by attaching
152 matching subject lines to the one found in the
153 relation. will use the oldest node with the same
154 subject that is not more then thread_by_subject_max_age
155 days old (see subject_relation_lookup)
158 static void subject_relation_insert(GRelation *relation, GNode *node)
163 g_return_if_fail(relation != NULL);
164 g_return_if_fail(node != NULL);
165 msginfo = (MsgInfo *) node->data;
166 g_return_if_fail(msginfo != NULL);
168 subject = msginfo->subject;
171 subject += subject_get_prefix_length(subject);
173 g_relation_insert(relation, subject, node);
176 static GNode *subject_relation_lookup(GRelation *relation, MsgInfo *msginfo)
183 g_return_val_if_fail(relation != NULL, NULL);
185 subject = msginfo->subject;
188 prefix_length = subject_get_prefix_length(subject);
189 if (prefix_length <= 0)
191 subject += prefix_length;
193 tuples = g_relation_select(relation, subject, 0);
197 if (tuples->len > 0) {
199 GNode *relation_node;
200 MsgInfo *relation_msginfo = NULL, *best_msginfo = NULL;
203 /* check all nodes with the same subject to find the best parent */
204 for (i = 0; i < tuples->len; i++) {
205 relation_node = (GNode *) g_tuples_index(tuples, i, 1);
206 relation_msginfo = (MsgInfo *) relation_node->data;
209 /* best node should be the oldest in the found nodes */
210 /* parent node must not be older then msginfo */
211 if ((relation_msginfo->date_t < msginfo->date_t) &&
212 ((best_msginfo == NULL) ||
213 (best_msginfo->date_t > relation_msginfo->date_t)))
216 /* parent node must not be more then thread_by_subject_max_age
217 days older then msginfo */
218 if (abs(difftime(msginfo->date_t, relation_msginfo->date_t)) >
219 prefs_common.thread_by_subject_max_age * 3600 * 24)
222 /* can add new tests for all matching
223 nodes found by subject */
226 node = relation_node;
227 best_msginfo = relation_msginfo;
232 g_tuples_destroy(tuples);
236 /* return the reversed thread tree */
237 GNode *procmsg_get_thread_tree(GSList *mlist)
239 GNode *root, *parent, *node, *next;
240 GHashTable *msgid_table;
241 GRelation *subject_relation;
246 root = g_node_new(NULL);
247 msgid_table = g_hash_table_new(g_str_hash, g_str_equal);
248 subject_relation = g_relation_new(2);
249 g_relation_index(subject_relation, 0, g_str_hash, g_str_equal);
251 for (; mlist != NULL; mlist = mlist->next) {
252 msginfo = (MsgInfo *)mlist->data;
255 if (msginfo->inreplyto) {
256 parent = g_hash_table_lookup(msgid_table, msginfo->inreplyto);
257 if (parent == NULL) {
261 node = g_node_insert_data_before
262 (parent, parent == root ? parent->children : NULL,
264 if ((msgid = msginfo->msgid) && g_hash_table_lookup(msgid_table, msgid) == NULL)
265 g_hash_table_insert(msgid_table, (gchar *)msgid, node);
267 /* CLAWS: add subject to relation (without prefix) */
268 if (prefs_common.thread_by_subject) {
269 subject_relation_insert(subject_relation, node);
273 /* complete the unfinished threads */
274 for (node = root->children; node != NULL; ) {
276 msginfo = (MsgInfo *)node->data;
279 if (msginfo->inreplyto)
280 parent = g_hash_table_lookup(msgid_table, msginfo->inreplyto);
282 /* try looking for the indirect parent */
283 if (!parent && msginfo->references) {
284 for (reflist = msginfo->references;
285 reflist != NULL; reflist = reflist->next)
286 if ((parent = g_hash_table_lookup
287 (msgid_table, reflist->data)) != NULL)
291 /* node should not be the parent, and node should not
292 be an ancestor of parent (circular reference) */
293 if (parent && parent != node &&
294 !g_node_is_ancestor(node, parent)) {
297 (parent, parent->children, node);
303 if (prefs_common.thread_by_subject) {
304 for (node = root->children; node && node != NULL;) {
306 msginfo = (MsgInfo *) node->data;
308 parent = subject_relation_lookup(subject_relation, msginfo);
310 /* the node may already be threaded by IN-REPLY-TO, so go up
312 find the parent node */
313 if (parent != NULL) {
314 if (g_node_is_ancestor(node, parent))
322 g_node_append(parent, node);
329 g_relation_destroy(subject_relation);
330 g_hash_table_destroy(msgid_table);
335 gint procmsg_move_messages(GSList *mlist)
337 GSList *cur, *movelist = NULL;
339 FolderItem *dest = NULL;
342 if (!mlist) return 0;
344 folder_item_update_freeze();
346 for (cur = mlist; cur != NULL; cur = cur->next) {
347 msginfo = (MsgInfo *)cur->data;
349 dest = msginfo->to_folder;
350 movelist = g_slist_append(movelist, msginfo);
351 } else if (dest == msginfo->to_folder) {
352 movelist = g_slist_append(movelist, msginfo);
354 folder_item_move_msgs(dest, movelist);
355 g_slist_free(movelist);
357 dest = msginfo->to_folder;
358 movelist = g_slist_append(movelist, msginfo);
360 procmsg_msginfo_set_to_folder(msginfo, NULL);
364 retval = folder_item_move_msgs(dest, movelist);
365 g_slist_free(movelist);
368 folder_item_update_thaw();
372 void procmsg_copy_messages(GSList *mlist)
374 GSList *cur, *copylist = NULL;
376 FolderItem *dest = NULL;
380 folder_item_update_freeze();
382 for (cur = mlist; cur != NULL; cur = cur->next) {
383 msginfo = (MsgInfo *)cur->data;
385 dest = msginfo->to_folder;
386 copylist = g_slist_append(copylist, msginfo);
387 } else if (dest == msginfo->to_folder) {
388 copylist = g_slist_append(copylist, msginfo);
390 folder_item_copy_msgs(dest, copylist);
391 g_slist_free(copylist);
393 dest = msginfo->to_folder;
394 copylist = g_slist_append(copylist, msginfo);
396 procmsg_msginfo_set_to_folder(msginfo, NULL);
400 folder_item_copy_msgs(dest, copylist);
401 g_slist_free(copylist);
404 folder_item_update_thaw();
407 gchar *procmsg_get_message_file_path(MsgInfo *msginfo)
411 g_return_val_if_fail(msginfo != NULL, NULL);
413 if (msginfo->plaintext_file)
414 file = g_strdup(msginfo->plaintext_file);
416 file = folder_item_fetch_msg(msginfo->folder, msginfo->msgnum);
422 gchar *procmsg_get_message_file(MsgInfo *msginfo)
424 gchar *filename = NULL;
426 g_return_val_if_fail(msginfo != NULL, NULL);
428 filename = folder_item_fetch_msg(msginfo->folder, msginfo->msgnum);
430 debug_print("can't fetch message %d\n", msginfo->msgnum);
435 gchar *procmsg_get_message_file_full(MsgInfo *msginfo, gboolean headers, gboolean body)
437 gchar *filename = NULL;
439 g_return_val_if_fail(msginfo != NULL, NULL);
441 filename = folder_item_fetch_msg_full(msginfo->folder, msginfo->msgnum,
444 debug_print("can't fetch message %d\n", msginfo->msgnum);
449 GSList *procmsg_get_message_file_list(GSList *mlist)
451 GSList *file_list = NULL;
453 MsgFileInfo *fileinfo;
456 while (mlist != NULL) {
457 msginfo = (MsgInfo *)mlist->data;
458 file = procmsg_get_message_file(msginfo);
460 procmsg_message_file_list_free(file_list);
463 fileinfo = g_new(MsgFileInfo, 1);
464 fileinfo->msginfo = procmsg_msginfo_new_ref(msginfo);
465 fileinfo->file = file;
466 fileinfo->flags = g_new(MsgFlags, 1);
467 *fileinfo->flags = msginfo->flags;
468 file_list = g_slist_prepend(file_list, fileinfo);
472 file_list = g_slist_reverse(file_list);
477 void procmsg_message_file_list_free(MsgInfoList *file_list)
480 MsgFileInfo *fileinfo;
482 for (cur = file_list; cur != NULL; cur = cur->next) {
483 fileinfo = (MsgFileInfo *)cur->data;
484 procmsg_msginfo_free(fileinfo->msginfo);
485 g_free(fileinfo->file);
486 g_free(fileinfo->flags);
490 g_slist_free(file_list);
493 FILE *procmsg_open_message(MsgInfo *msginfo)
498 g_return_val_if_fail(msginfo != NULL, NULL);
500 file = procmsg_get_message_file_path(msginfo);
501 g_return_val_if_fail(file != NULL, NULL);
503 if (!is_file_exist(file)) {
505 file = procmsg_get_message_file(msginfo);
510 if ((fp = g_fopen(file, "rb")) == NULL) {
511 FILE_OP_ERROR(file, "fopen");
518 if (MSG_IS_QUEUED(msginfo->flags) || MSG_IS_DRAFT(msginfo->flags)) {
521 while (fgets(buf, sizeof(buf), fp) != NULL)
522 if (buf[0] == '\r' || buf[0] == '\n') break;
528 gboolean procmsg_msg_exist(MsgInfo *msginfo)
533 if (!msginfo) return FALSE;
535 path = folder_item_get_path(msginfo->folder);
537 ret = !folder_item_is_msg_changed(msginfo->folder, msginfo);
543 void procmsg_get_filter_keyword(MsgInfo *msginfo, gchar **header, gchar **key,
544 PrefsFilterType type)
546 static HeaderEntry hentry[] = {{"X-BeenThere:", NULL, TRUE},
547 {"X-ML-Name:", NULL, TRUE},
548 {"X-List:", NULL, TRUE},
549 {"X-Mailing-list:", NULL, TRUE},
550 {"List-Id:", NULL, TRUE},
551 {"X-Sequence:", NULL, TRUE},
552 {"Sender:", NULL, TRUE},
553 {"List-Post:", NULL, TRUE},
554 {NULL, NULL, FALSE}};
560 H_X_MAILING_LIST = 3,
569 g_return_if_fail(msginfo != NULL);
570 g_return_if_fail(header != NULL);
571 g_return_if_fail(key != NULL);
580 if ((fp = procmsg_open_message(msginfo)) == NULL)
582 procheader_get_header_fields(fp, hentry);
585 #define SET_FILTER_KEY(hstr, idx) \
587 *header = g_strdup(hstr); \
588 *key = hentry[idx].body; \
589 hentry[idx].body = NULL; \
592 if (hentry[H_X_BEENTHERE].body != NULL) {
593 SET_FILTER_KEY("header \"X-BeenThere\"", H_X_BEENTHERE);
594 } else if (hentry[H_X_ML_NAME].body != NULL) {
595 SET_FILTER_KEY("header \"X-ML-Name\"", H_X_ML_NAME);
596 } else if (hentry[H_X_LIST].body != NULL) {
597 SET_FILTER_KEY("header \"X-List\"", H_X_LIST);
598 } else if (hentry[H_X_MAILING_LIST].body != NULL) {
599 SET_FILTER_KEY("header \"X-Mailing-List\"", H_X_MAILING_LIST);
600 } else if (hentry[H_LIST_ID].body != NULL) {
601 SET_FILTER_KEY("header \"List-Id\"", H_LIST_ID);
602 extract_list_id_str(*key);
603 } else if (hentry[H_X_SEQUENCE].body != NULL) {
606 SET_FILTER_KEY("X-Sequence", H_X_SEQUENCE);
609 while (*p != '\0' && !g_ascii_isspace(*p)) p++;
610 while (g_ascii_isspace(*p)) p++;
611 if (g_ascii_isdigit(*p)) {
617 } else if (hentry[H_SENDER].body != NULL) {
618 SET_FILTER_KEY("header \"Sender\"", H_SENDER);
619 } else if (hentry[H_LIST_POST].body != NULL) {
620 SET_FILTER_KEY("header \"Sender\"", H_LIST_POST);
621 } else if (msginfo->to) {
622 *header = g_strdup("to");
623 *key = g_strdup(msginfo->to);
624 } else if (msginfo->subject) {
625 *header = g_strdup("subject");
626 *key = g_strdup(msginfo->subject);
629 #undef SET_FILTER_KEY
631 g_free(hentry[H_X_BEENTHERE].body);
632 hentry[H_X_BEENTHERE].body = NULL;
633 g_free(hentry[H_X_ML_NAME].body);
634 hentry[H_X_ML_NAME].body = NULL;
635 g_free(hentry[H_X_LIST].body);
636 hentry[H_X_LIST].body = NULL;
637 g_free(hentry[H_X_MAILING_LIST].body);
638 hentry[H_X_MAILING_LIST].body = NULL;
639 g_free(hentry[H_LIST_ID].body);
640 hentry[H_LIST_ID].body = NULL;
641 g_free(hentry[H_SENDER].body);
642 hentry[H_SENDER].body = NULL;
643 g_free(hentry[H_LIST_POST].body);
644 hentry[H_LIST_POST].body = NULL;
648 *header = g_strdup("from");
649 *key = g_strdup(msginfo->from);
652 *header = g_strdup("to");
653 *key = g_strdup(msginfo->to);
655 case FILTER_BY_SUBJECT:
656 *header = g_strdup("subject");
657 *key = g_strdup(msginfo->subject);
664 void procmsg_empty_trash(FolderItem *trash)
669 (trash->stype != F_TRASH &&
670 !folder_has_parent_of_type(trash, F_TRASH)))
673 if (trash && trash->total_msgs > 0) {
674 GSList *mlist = folder_item_get_msg_list(trash);
676 for (cur = mlist ; cur != NULL ; cur = cur->next) {
677 MsgInfo * msginfo = (MsgInfo *) cur->data;
678 if (MSG_IS_LOCKED(msginfo->flags))
680 if (msginfo->total_size != 0 &&
681 msginfo->size != (off_t)msginfo->total_size)
682 partial_mark_for_delete(msginfo);
684 procmsg_msginfo_free(msginfo);
687 folder_item_remove_all_msg(trash);
690 if (!trash->node || !trash->node->children)
693 node = trash->node->children;
694 while (node != NULL) {
696 procmsg_empty_trash(FOLDER_ITEM(node->data));
701 void procmsg_empty_all_trash(void)
706 for (cur = folder_get_list(); cur != NULL; cur = cur->next) {
707 Folder *folder = FOLDER(cur->data);
708 trash = folder->trash;
709 procmsg_empty_trash(trash);
710 if (folder->account && folder->account->set_trash_folder &&
711 folder_find_item_from_identifier(folder->account->trash_folder))
713 folder_find_item_from_identifier(folder->account->trash_folder));
717 static PrefsAccount *procmsg_get_account_from_file(const gchar *file)
719 PrefsAccount *mailac = NULL;
723 static HeaderEntry qentry[] = {{"S:", NULL, FALSE},
724 {"SSV:", NULL, FALSE},
726 {"NG:", NULL, FALSE},
727 {"MAID:", NULL, FALSE},
728 {"NAID:", NULL, FALSE},
729 {"SCF:", NULL, FALSE},
730 {"RMID:", NULL, FALSE},
731 {"FMID:", NULL, FALSE},
732 {"X-Sylpheed-Privacy-System:", NULL, FALSE},
733 {"X-Sylpheed-Encrypt:", NULL, FALSE},
734 {"X-Sylpheed-Encrypt-Data:", NULL, FALSE},
735 {NULL, NULL, FALSE}};
737 g_return_val_if_fail(file != NULL, NULL);
739 if ((fp = g_fopen(file, "rb")) == NULL) {
740 FILE_OP_ERROR(file, "fopen");
744 while ((hnum = procheader_get_one_field(buf, sizeof(buf), fp, qentry))
746 gchar *p = buf + strlen(qentry[hnum].name);
748 if (hnum == Q_MAIL_ACCOUNT_ID) {
749 mailac = account_find_from_id(atoi(p));
757 static GSList *procmsg_list_sort_by_account(FolderItem *queue, GSList *list)
759 GSList *result = NULL;
761 PrefsAccount *last_account = NULL;
764 gboolean nothing_to_sort = TRUE;
769 orig = g_slist_copy(list);
771 msg = (MsgInfo *)orig->data;
773 for (cur = orig; cur; cur = cur->next)
774 debug_print("sort before %s\n", ((MsgInfo *)cur->data)->from);
779 nothing_to_sort = TRUE;
783 PrefsAccount *ac = NULL;
784 msg = (MsgInfo *)cur->data;
785 file = folder_item_fetch_msg(queue, msg->msgnum);
786 ac = procmsg_get_account_from_file(file);
789 if (last_account == NULL || (ac != NULL && ac == last_account)) {
790 result = g_slist_append(result, msg);
791 orig = g_slist_remove(orig, msg);
793 nothing_to_sort = FALSE;
799 if (orig || g_slist_length(orig)) {
800 if (!last_account && nothing_to_sort) {
801 /* can't find an account for the rest of the list */
804 result = g_slist_append(result, cur->data);
815 for (cur = result; cur; cur = cur->next)
816 debug_print("sort after %s\n", ((MsgInfo *)cur->data)->from);
823 static gboolean procmsg_is_last_for_account(FolderItem *queue, MsgInfo *msginfo, GSList *elem)
825 gchar *file = folder_item_fetch_msg(queue, msginfo->msgnum);
826 PrefsAccount *ac = procmsg_get_account_from_file(file);
829 for (cur = elem; cur; cur = cur->next) {
830 MsgInfo *cur_msginfo = (MsgInfo *)cur->data;
831 file = folder_item_fetch_msg(queue, cur_msginfo->msgnum);
833 if (cur_msginfo != msginfo && !MSG_IS_LOCKED(cur_msginfo->flags)) {
834 if (procmsg_get_account_from_file(file) == ac) {
846 *\brief Send messages in queue
848 *\param queue Queue folder to process
849 *\param save_msgs Unused
851 *\return Number of messages sent, negative if an error occurred
852 * positive if no error occurred
854 gint procmsg_send_queue(FolderItem *queue, gboolean save_msgs)
856 gint sent = 0, err = 0;
858 GSList *sorted_list = NULL;
862 queue = folder_get_default_queue();
863 g_return_val_if_fail(queue != NULL, -1);
865 folder_item_scan(queue);
866 list = folder_item_get_msg_list(queue);
868 /* sort the list per sender account; this helps reusing the same SMTP server */
869 sorted_list = procmsg_list_sort_by_account(queue, list);
871 for (elem = sorted_list; elem != NULL; elem = elem->next) {
875 msginfo = (MsgInfo *)(elem->data);
876 if (!MSG_IS_LOCKED(msginfo->flags)) {
877 file = folder_item_fetch_msg(queue, msginfo->msgnum);
879 if (procmsg_send_message_queue_full(file,
880 !procmsg_is_last_for_account(queue, msginfo, elem)) < 0) {
881 g_warning("Sending queued message %d failed.\n",
886 * We save in procmsg_send_message_queue because
887 * we need the destination folder from the queue
891 procmsg_save_to_outbox
892 (queue->folder->outbox,
896 folder_item_remove_msg(queue, msginfo->msgnum);
901 /* FIXME: supposedly if only one message is locked, and queue
902 * is being flushed, the following free says something like
903 * "freeing msg ## in folder (nil)". */
904 procmsg_msginfo_free(msginfo);
907 g_slist_free(sorted_list);
908 folder_item_scan(queue);
910 if (queue->node && queue->node->children) {
911 node = queue->node->children;
912 while (node != NULL) {
915 res = procmsg_send_queue(FOLDER_ITEM(node->data), save_msgs);
924 return (err != 0 ? -err : sent);
928 *\brief Determine if a queue folder is empty
930 *\param queue Queue folder to process
932 *\return TRUE if the queue folder is empty, otherwise return FALSE
934 gboolean procmsg_queue_is_empty(FolderItem *queue)
937 gboolean res = FALSE;
939 queue = folder_get_default_queue();
940 g_return_val_if_fail(queue != NULL, TRUE);
942 folder_item_scan(queue);
943 list = folder_item_get_msg_list(queue);
944 res = (list == NULL);
945 procmsg_msg_list_free(list);
949 if (queue->node && queue->node->children) {
950 node = queue->node->children;
951 while (node != NULL) {
953 if (!procmsg_queue_is_empty(FOLDER_ITEM(node->data)))
962 gint procmsg_remove_special_headers(const gchar *in, const gchar *out)
967 if ((fp = g_fopen(in, "rb")) == NULL) {
968 FILE_OP_ERROR(in, "fopen");
971 if ((outfp = g_fopen(out, "wb")) == NULL) {
972 FILE_OP_ERROR(out, "fopen");
976 while (fgets(buf, sizeof(buf), fp) != NULL)
977 if (buf[0] == '\r' || buf[0] == '\n') break;
978 while (fgets(buf, sizeof(buf), fp) != NULL)
985 gint procmsg_save_to_outbox(FolderItem *outbox, const gchar *file,
989 MsgInfo *msginfo, *tmp_msginfo;
990 MsgFlags flag = {0, 0};
992 debug_print("saving sent message...\n");
995 outbox = folder_get_default_outbox();
996 g_return_val_if_fail(outbox != NULL, -1);
998 /* remove queueing headers */
1000 gchar tmp[MAXPATHLEN + 1];
1002 g_snprintf(tmp, sizeof(tmp), "%s%ctmpmsg.out.%08x",
1003 get_rc_dir(), G_DIR_SEPARATOR, (guint) rand());
1005 if (procmsg_remove_special_headers(file, tmp) !=0)
1008 folder_item_scan(outbox);
1009 if ((num = folder_item_add_msg(outbox, tmp, &flag, TRUE)) < 0) {
1010 g_warning("can't save message\n");
1015 folder_item_scan(outbox);
1016 if ((num = folder_item_add_msg
1017 (outbox, file, &flag, FALSE)) < 0) {
1018 g_warning("can't save message\n");
1023 msginfo = folder_item_get_msginfo(outbox, num); /* refcnt++ */
1024 tmp_msginfo = procmsg_msginfo_get_full_info(msginfo); /* refcnt++ */
1025 if (msginfo != NULL) {
1026 procmsg_msginfo_unset_flags(msginfo, ~0, 0);
1027 procmsg_msginfo_free(msginfo); /* refcnt-- */
1028 /* tmp_msginfo == msginfo */
1029 if (tmp_msginfo && (msginfo->dispositionnotificationto ||
1030 msginfo->returnreceiptto)) {
1031 procmsg_msginfo_set_flags(msginfo, MSG_RETRCPT_SENT, 0);
1032 procmsg_msginfo_free(msginfo); /* refcnt-- */
1039 void procmsg_print_message(MsgInfo *msginfo, const gchar *cmdline)
1041 static const gchar *def_cmd = "lpr %s";
1042 static guint id = 0;
1048 g_return_if_fail(msginfo);
1050 if (procmime_msginfo_is_encrypted(msginfo))
1051 tmpfp = procmime_get_first_encrypted_text_content(msginfo);
1053 tmpfp = procmime_get_first_text_content(msginfo);
1054 if (tmpfp == NULL) {
1055 g_warning("Can't get text part\n");
1059 prtmp = g_strdup_printf("%s%cprinttmp.%08x",
1060 get_mime_tmp_dir(), G_DIR_SEPARATOR, id++);
1062 if ((prfp = g_fopen(prtmp, "wb")) == NULL) {
1063 FILE_OP_ERROR(prtmp, "fopen");
1069 if (msginfo->date) fprintf(prfp, "Date: %s\n", msginfo->date);
1070 if (msginfo->from) fprintf(prfp, "From: %s\n", msginfo->from);
1071 if (msginfo->to) fprintf(prfp, "To: %s\n", msginfo->to);
1072 if (msginfo->cc) fprintf(prfp, "Cc: %s\n", msginfo->cc);
1073 if (msginfo->newsgroups)
1074 fprintf(prfp, "Newsgroups: %s\n", msginfo->newsgroups);
1075 if (msginfo->subject) fprintf(prfp, "Subject: %s\n", msginfo->subject);
1078 while (fgets(buf, sizeof(buf), tmpfp) != NULL)
1084 if (cmdline && (p = strchr(cmdline, '%')) && *(p + 1) == 's' &&
1085 !strchr(p + 2, '%'))
1086 g_snprintf(buf, sizeof(buf) - 1, cmdline, prtmp);
1089 g_warning("Print command line is invalid: '%s'\n",
1091 g_snprintf(buf, sizeof(buf) - 1, def_cmd, prtmp);
1097 if (buf[strlen(buf) - 1] != '&') strcat(buf, "&");
1101 MsgInfo *procmsg_msginfo_new_ref(MsgInfo *msginfo)
1108 MsgInfo *procmsg_msginfo_new(void)
1110 MsgInfo *newmsginfo;
1112 newmsginfo = g_new0(MsgInfo, 1);
1113 newmsginfo->refcnt = 1;
1118 MsgInfo *procmsg_msginfo_copy(MsgInfo *msginfo)
1120 MsgInfo *newmsginfo;
1123 if (msginfo == NULL) return NULL;
1125 newmsginfo = g_new0(MsgInfo, 1);
1127 newmsginfo->refcnt = 1;
1129 #define MEMBCOPY(mmb) newmsginfo->mmb = msginfo->mmb
1130 #define MEMBDUP(mmb) newmsginfo->mmb = msginfo->mmb ? \
1131 g_strdup(msginfo->mmb) : NULL
1146 MEMBDUP(newsgroups);
1153 MEMBCOPY(to_folder);
1157 MEMBDUP(dispositionnotificationto);
1158 MEMBDUP(returnreceiptto);
1160 refs = msginfo->references;
1161 for (refs = msginfo->references; refs != NULL; refs = refs->next) {
1162 newmsginfo->references = g_slist_prepend
1163 (newmsginfo->references, g_strdup(refs->data));
1165 newmsginfo->references = g_slist_reverse(newmsginfo->references);
1168 MEMBDUP(plaintext_file);
1173 MsgInfo *procmsg_msginfo_get_full_info(MsgInfo *msginfo)
1175 MsgInfo *full_msginfo;
1178 if (msginfo == NULL) return NULL;
1180 file = procmsg_get_message_file_path(msginfo);
1181 if (!file || !is_file_exist(file)) {
1183 file = procmsg_get_message_file(msginfo);
1185 if (!file || !is_file_exist(file)) {
1186 g_warning("procmsg_msginfo_get_full_info(): can't get message file.\n");
1190 full_msginfo = procheader_parse_file(file, msginfo->flags, TRUE, FALSE);
1192 if (!full_msginfo) return NULL;
1194 /* CLAWS: make sure we add the missing members; see:
1195 * procheader.c::procheader_get_headernames() */
1196 if (!msginfo->xface)
1197 msginfo->xface = g_strdup(full_msginfo->xface);
1199 msginfo->face = g_strdup(full_msginfo->face);
1200 if (!msginfo->dispositionnotificationto)
1201 msginfo->dispositionnotificationto =
1202 g_strdup(full_msginfo->dispositionnotificationto);
1203 if (!msginfo->returnreceiptto)
1204 msginfo->returnreceiptto = g_strdup
1205 (full_msginfo->returnreceiptto);
1206 if (!msginfo->partial_recv && full_msginfo->partial_recv)
1207 msginfo->partial_recv = g_strdup
1208 (full_msginfo->partial_recv);
1209 msginfo->total_size = full_msginfo->total_size;
1210 if (!msginfo->account_server && full_msginfo->account_server)
1211 msginfo->account_server = g_strdup
1212 (full_msginfo->account_server);
1213 if (!msginfo->account_login && full_msginfo->account_login)
1214 msginfo->account_login = g_strdup
1215 (full_msginfo->account_login);
1216 msginfo->planned_download = full_msginfo->planned_download;
1217 procmsg_msginfo_free(full_msginfo);
1219 return procmsg_msginfo_new_ref(msginfo);
1222 void procmsg_msginfo_free(MsgInfo *msginfo)
1224 if (msginfo == NULL) return;
1227 if (msginfo->refcnt > 0)
1230 if (msginfo->to_folder) {
1231 msginfo->to_folder->op_count--;
1232 folder_item_update(msginfo->to_folder, F_ITEM_UPDATE_MSGCNT);
1235 g_free(msginfo->fromspace);
1236 g_free(msginfo->returnreceiptto);
1237 g_free(msginfo->dispositionnotificationto);
1238 g_free(msginfo->xface);
1239 g_free(msginfo->face);
1241 g_free(msginfo->fromname);
1243 g_free(msginfo->date);
1244 g_free(msginfo->from);
1245 g_free(msginfo->to);
1246 g_free(msginfo->cc);
1247 g_free(msginfo->newsgroups);
1248 g_free(msginfo->subject);
1249 g_free(msginfo->msgid);
1250 g_free(msginfo->inreplyto);
1251 g_free(msginfo->xref);
1253 g_free(msginfo->partial_recv);
1254 g_free(msginfo->account_server);
1255 g_free(msginfo->account_login);
1257 slist_free_strings(msginfo->references);
1258 g_slist_free(msginfo->references);
1260 g_free(msginfo->plaintext_file);
1265 guint procmsg_msginfo_memusage(MsgInfo *msginfo)
1270 memusage += sizeof(MsgInfo);
1271 if (msginfo->fromname)
1272 memusage += strlen(msginfo->fromname);
1274 memusage += strlen(msginfo->date);
1276 memusage += strlen(msginfo->from);
1278 memusage += strlen(msginfo->to);
1280 memusage += strlen(msginfo->cc);
1281 if (msginfo->newsgroups)
1282 memusage += strlen(msginfo->newsgroups);
1283 if (msginfo->subject)
1284 memusage += strlen(msginfo->subject);
1286 memusage += strlen(msginfo->msgid);
1287 if (msginfo->inreplyto)
1288 memusage += strlen(msginfo->inreplyto);
1290 memusage += strlen(msginfo->xface);
1292 memusage += strlen(msginfo->face);
1293 if (msginfo->dispositionnotificationto)
1294 memusage += strlen(msginfo->dispositionnotificationto);
1295 if (msginfo->returnreceiptto)
1296 memusage += strlen(msginfo->returnreceiptto);
1297 for (refs = msginfo->references; refs; refs=refs->next) {
1298 gchar *r = (gchar *)refs->data;
1299 memusage += r?strlen(r):0;
1301 if (msginfo->fromspace)
1302 memusage += strlen(msginfo->fromspace);
1307 gint procmsg_cmp_msgnum_for_sort(gconstpointer a, gconstpointer b)
1309 const MsgInfo *msginfo1 = a;
1310 const MsgInfo *msginfo2 = b;
1317 return msginfo1->msgnum - msginfo2->msgnum;
1320 static gint procmsg_send_message_queue_full(const gchar *file, gboolean keep_session)
1322 static HeaderEntry qentry[] = {{"S:", NULL, FALSE},
1323 {"SSV:", NULL, FALSE},
1324 {"R:", NULL, FALSE},
1325 {"NG:", NULL, FALSE},
1326 {"MAID:", NULL, FALSE},
1327 {"NAID:", NULL, FALSE},
1328 {"SCF:", NULL, FALSE},
1329 {"RMID:", NULL, FALSE},
1330 {"FMID:", NULL, FALSE},
1331 {"X-Sylpheed-Privacy-System:", NULL, FALSE},
1332 {"X-Sylpheed-Encrypt:", NULL, FALSE},
1333 {"X-Sylpheed-Encrypt-Data:", NULL, FALSE},
1334 {NULL, NULL, FALSE}};
1337 gint mailval = 0, newsval = 0;
1339 gchar *smtpserver = NULL;
1340 GSList *to_list = NULL;
1341 GSList *newsgroup_list = NULL;
1342 gchar *savecopyfolder = NULL;
1343 gchar *replymessageid = NULL;
1344 gchar *fwdmessageid = NULL;
1345 gchar *privacy_system = NULL;
1346 gboolean encrypt = FALSE;
1347 gchar *encrypt_data = NULL;
1348 gchar buf[BUFFSIZE];
1350 PrefsAccount *mailac = NULL, *newsac = NULL;
1351 gboolean save_clear_text = TRUE;
1352 gchar *tmp_enc_file = NULL;
1356 g_return_val_if_fail(file != NULL, -1);
1358 if ((fp = g_fopen(file, "rb")) == NULL) {
1359 FILE_OP_ERROR(file, "fopen");
1363 while ((hnum = procheader_get_one_field(buf, sizeof(buf), fp, qentry))
1365 gchar *p = buf + strlen(qentry[hnum].name);
1373 if (smtpserver == NULL)
1374 smtpserver = g_strdup(p);
1377 to_list = address_list_append(to_list, p);
1380 newsgroup_list = newsgroup_list_append(newsgroup_list, p);
1382 case Q_MAIL_ACCOUNT_ID:
1383 mailac = account_find_from_id(atoi(p));
1385 case Q_NEWS_ACCOUNT_ID:
1386 newsac = account_find_from_id(atoi(p));
1388 case Q_SAVE_COPY_FOLDER:
1389 if (savecopyfolder == NULL)
1390 savecopyfolder = g_strdup(p);
1392 case Q_REPLY_MESSAGE_ID:
1393 if (replymessageid == NULL)
1394 replymessageid = g_strdup(p);
1396 case Q_FWD_MESSAGE_ID:
1397 if (fwdmessageid == NULL)
1398 fwdmessageid = g_strdup(p);
1400 case Q_PRIVACY_SYSTEM:
1401 if (privacy_system == NULL)
1402 privacy_system = g_strdup(p);
1408 case Q_ENCRYPT_DATA:
1409 if (encrypt_data == NULL)
1410 encrypt_data = g_strdup(p);
1414 filepos = ftell(fp);
1419 save_clear_text = (mailac != NULL && mailac->save_encrypted_as_clear_text);
1424 mimeinfo = procmime_scan_queue_file(file);
1425 if (!privacy_encrypt(privacy_system, mimeinfo, encrypt_data)
1426 || (fp = my_tmpfile()) == NULL
1427 || procmime_write_mimeinfo(mimeinfo, fp) < 0) {
1430 procmime_mimeinfo_free_all(mimeinfo);
1433 slist_free_strings(to_list);
1434 g_slist_free(to_list);
1435 slist_free_strings(newsgroup_list);
1436 g_slist_free(newsgroup_list);
1437 g_free(savecopyfolder);
1438 g_free(replymessageid);
1439 g_free(fwdmessageid);
1440 g_free(privacy_system);
1441 g_free(encrypt_data);
1446 if (!save_clear_text) {
1447 gchar *content = NULL;
1448 FILE *tmpfp = get_tmpfile_in_dir(get_mime_tmp_dir(), &tmp_enc_file);
1452 content = file_read_stream_to_str(fp);
1455 str_write_to_file(content, tmp_enc_file);
1458 g_warning("couldn't get tempfile\n");
1462 procmime_mimeinfo_free_all(mimeinfo);
1468 debug_print("Sending message by mail\n");
1470 g_warning("Queued message header is broken.\n");
1472 } else if (mailac && mailac->use_mail_command &&
1473 mailac->mail_command && (* mailac->mail_command)) {
1474 mailval = send_message_local(mailac->mail_command, fp);
1478 mailac = account_find_from_smtp_server(from, smtpserver);
1480 g_warning("Account not found. "
1481 "Using current account...\n");
1482 mailac = cur_account;
1487 mailval = send_message_smtp_full(mailac, to_list, fp, keep_session);
1489 PrefsAccount tmp_ac;
1491 g_warning("Account not found.\n");
1493 memset(&tmp_ac, 0, sizeof(PrefsAccount));
1494 tmp_ac.address = from;
1495 tmp_ac.smtp_server = smtpserver;
1496 tmp_ac.smtpport = SMTP_PORT;
1497 mailval = send_message_smtp(&tmp_ac, to_list, fp);
1502 fseek(fp, filepos, SEEK_SET);
1503 if (newsgroup_list && (mailval == 0)) {
1508 /* write to temporary file */
1509 tmp = g_strdup_printf("%s%ctmp%d", g_get_tmp_dir(),
1510 G_DIR_SEPARATOR, (gint)file);
1511 if ((tmpfp = g_fopen(tmp, "wb")) == NULL) {
1512 FILE_OP_ERROR(tmp, "fopen");
1514 alertpanel_error(_("Could not create temporary file for news sending."));
1516 if (change_file_mode_rw(tmpfp, tmp) < 0) {
1517 FILE_OP_ERROR(tmp, "chmod");
1518 g_warning("can't change file mode\n");
1521 while ((newsval == 0) && fgets(buf, sizeof(buf), fp) != NULL) {
1522 if (fputs(buf, tmpfp) == EOF) {
1523 FILE_OP_ERROR(tmp, "fputs");
1525 alertpanel_error(_("Error when writing temporary file for news sending."));
1531 debug_print("Sending message by news\n");
1533 folder = FOLDER(newsac->folder);
1535 newsval = news_post(folder, tmp);
1537 alertpanel_error(_("Error occurred while posting the message to %s ."),
1538 newsac->nntp_server);
1548 /* save message to outbox */
1549 if (mailval == 0 && newsval == 0 && savecopyfolder) {
1552 debug_print("saving sent message...\n");
1554 outbox = folder_find_item_from_identifier(savecopyfolder);
1556 outbox = folder_get_default_outbox();
1558 if (save_clear_text || tmp_enc_file == NULL) {
1559 procmsg_save_to_outbox(outbox, file, TRUE);
1561 procmsg_save_to_outbox(outbox, tmp_enc_file, FALSE);
1565 if (tmp_enc_file != NULL) {
1566 g_unlink(tmp_enc_file);
1568 tmp_enc_file = NULL;
1571 if (replymessageid != NULL || fwdmessageid != NULL) {
1575 if (replymessageid != NULL)
1576 tokens = g_strsplit(replymessageid, "\x7f", 0);
1578 tokens = g_strsplit(fwdmessageid, "\x7f", 0);
1579 item = folder_find_item_from_identifier(tokens[0]);
1581 /* check if queued message has valid folder and message id */
1582 if (item != NULL && tokens[2] != NULL) {
1585 msginfo = folder_item_get_msginfo(item, atoi(tokens[1]));
1587 /* check if referring message exists and has a message id */
1588 if ((msginfo != NULL) &&
1589 (msginfo->msgid != NULL) &&
1590 (strcmp(msginfo->msgid, tokens[2]) != 0)) {
1591 procmsg_msginfo_free(msginfo);
1595 if (msginfo == NULL) {
1596 msginfo = folder_item_get_msginfo_by_msgid(item, tokens[2]);
1599 if (msginfo != NULL) {
1600 if (replymessageid != NULL) {
1601 procmsg_msginfo_unset_flags(msginfo, MSG_FORWARDED, 0);
1602 procmsg_msginfo_set_flags(msginfo, MSG_REPLIED, 0);
1604 procmsg_msginfo_unset_flags(msginfo, MSG_REPLIED, 0);
1605 procmsg_msginfo_set_flags(msginfo, MSG_FORWARDED, 0);
1607 procmsg_msginfo_free(msginfo);
1615 slist_free_strings(to_list);
1616 g_slist_free(to_list);
1617 slist_free_strings(newsgroup_list);
1618 g_slist_free(newsgroup_list);
1619 g_free(savecopyfolder);
1620 g_free(replymessageid);
1621 g_free(fwdmessageid);
1622 g_free(privacy_system);
1623 g_free(encrypt_data);
1625 return (newsval != 0 ? newsval : mailval);
1628 gint procmsg_send_message_queue(const gchar *file)
1630 return procmsg_send_message_queue_full(file, FALSE);
1633 static void update_folder_msg_counts(FolderItem *item, MsgInfo *msginfo, MsgPermFlags old_flags)
1635 MsgPermFlags new_flags = msginfo->flags.perm_flags;
1638 if (!(old_flags & MSG_NEW) && (new_flags & MSG_NEW)) {
1642 if ((old_flags & MSG_NEW) && !(new_flags & MSG_NEW)) {
1647 if (!(old_flags & MSG_UNREAD) && (new_flags & MSG_UNREAD)) {
1648 item->unread_msgs++;
1649 if (procmsg_msg_has_marked_parent(msginfo))
1650 item->unreadmarked_msgs++;
1653 if ((old_flags & MSG_UNREAD) && !(new_flags & MSG_UNREAD)) {
1654 item->unread_msgs--;
1655 if (procmsg_msg_has_marked_parent(msginfo))
1656 item->unreadmarked_msgs--;
1660 if (!(old_flags & MSG_MARKED) && (new_flags & MSG_MARKED)) {
1661 procmsg_update_unread_children(msginfo, TRUE);
1662 item->marked_msgs++;
1665 if ((old_flags & MSG_MARKED) && !(new_flags & MSG_MARKED)) {
1666 procmsg_update_unread_children(msginfo, FALSE);
1667 item->marked_msgs--;
1671 void procmsg_msginfo_set_flags(MsgInfo *msginfo, MsgPermFlags perm_flags, MsgTmpFlags tmp_flags)
1674 MsgInfoUpdate msginfo_update;
1675 MsgPermFlags perm_flags_new, perm_flags_old;
1676 MsgTmpFlags tmp_flags_old;
1678 g_return_if_fail(msginfo != NULL);
1679 item = msginfo->folder;
1680 g_return_if_fail(item != NULL);
1682 debug_print("Setting flags for message %d in folder %s\n", msginfo->msgnum, item->path);
1684 /* Perm Flags handling */
1685 perm_flags_old = msginfo->flags.perm_flags;
1686 perm_flags_new = msginfo->flags.perm_flags | perm_flags;
1687 if ((perm_flags & MSG_IGNORE_THREAD) || (perm_flags_old & MSG_IGNORE_THREAD)) {
1688 perm_flags_new &= ~(MSG_NEW | MSG_UNREAD);
1691 if (perm_flags_old != perm_flags_new) {
1692 folder_item_change_msg_flags(msginfo->folder, msginfo, perm_flags_new);
1694 update_folder_msg_counts(item, msginfo, perm_flags_old);
1698 /* Tmp flags handling */
1699 tmp_flags_old = msginfo->flags.tmp_flags;
1700 msginfo->flags.tmp_flags |= tmp_flags;
1702 /* update notification */
1703 if ((perm_flags_old != perm_flags_new) || (tmp_flags_old != msginfo->flags.tmp_flags)) {
1704 msginfo_update.msginfo = msginfo;
1705 msginfo_update.flags = MSGINFO_UPDATE_FLAGS;
1706 hooks_invoke(MSGINFO_UPDATE_HOOKLIST, &msginfo_update);
1707 folder_item_update(msginfo->folder, F_ITEM_UPDATE_MSGCNT);
1711 void procmsg_msginfo_unset_flags(MsgInfo *msginfo, MsgPermFlags perm_flags, MsgTmpFlags tmp_flags)
1714 MsgInfoUpdate msginfo_update;
1715 MsgPermFlags perm_flags_new, perm_flags_old;
1716 MsgTmpFlags tmp_flags_old;
1718 g_return_if_fail(msginfo != NULL);
1719 item = msginfo->folder;
1720 g_return_if_fail(item != NULL);
1722 debug_print("Unsetting flags for message %d in folder %s\n", msginfo->msgnum, item->path);
1724 /* Perm Flags handling */
1725 perm_flags_old = msginfo->flags.perm_flags;
1726 perm_flags_new = msginfo->flags.perm_flags & ~perm_flags;
1728 if (perm_flags_old != perm_flags_new) {
1729 folder_item_change_msg_flags(msginfo->folder, msginfo, perm_flags_new);
1731 update_folder_msg_counts(item, msginfo, perm_flags_old);
1733 msginfo_update.msginfo = msginfo;
1734 msginfo_update.flags = MSGINFO_UPDATE_FLAGS;
1735 hooks_invoke(MSGINFO_UPDATE_HOOKLIST, &msginfo_update);
1736 folder_item_update(msginfo->folder, F_ITEM_UPDATE_MSGCNT);
1739 /* Tmp flags hanlding */
1740 tmp_flags_old = msginfo->flags.tmp_flags;
1741 msginfo->flags.tmp_flags &= ~tmp_flags;
1743 /* update notification */
1744 if ((perm_flags_old != perm_flags_new) || (tmp_flags_old != msginfo->flags.tmp_flags)) {
1745 msginfo_update.msginfo = msginfo;
1746 msginfo_update.flags = MSGINFO_UPDATE_FLAGS;
1747 hooks_invoke(MSGINFO_UPDATE_HOOKLIST, &msginfo_update);
1748 folder_item_update(msginfo->folder, F_ITEM_UPDATE_MSGCNT);
1752 void procmsg_msginfo_change_flags(MsgInfo *msginfo,
1753 MsgPermFlags add_perm_flags, MsgTmpFlags add_tmp_flags,
1754 MsgPermFlags rem_perm_flags, MsgTmpFlags rem_tmp_flags)
1757 MsgInfoUpdate msginfo_update;
1758 MsgPermFlags perm_flags_new, perm_flags_old;
1759 MsgTmpFlags tmp_flags_old;
1761 g_return_if_fail(msginfo != NULL);
1762 item = msginfo->folder;
1763 g_return_if_fail(item != NULL);
1765 debug_print("Changing flags for message %d in folder %s\n", msginfo->msgnum, item->path);
1767 /* Perm Flags handling */
1768 perm_flags_old = msginfo->flags.perm_flags;
1769 perm_flags_new = (msginfo->flags.perm_flags & ~rem_perm_flags) | add_perm_flags;
1770 if ((add_perm_flags & MSG_IGNORE_THREAD) || (perm_flags_old & MSG_IGNORE_THREAD)) {
1771 perm_flags_new &= ~(MSG_NEW | MSG_UNREAD);
1774 if (perm_flags_old != perm_flags_new) {
1775 folder_item_change_msg_flags(msginfo->folder, msginfo, perm_flags_new);
1777 update_folder_msg_counts(item, msginfo, perm_flags_old);
1781 /* Tmp flags handling */
1782 tmp_flags_old = msginfo->flags.tmp_flags;
1783 msginfo->flags.tmp_flags &= ~rem_tmp_flags;
1784 msginfo->flags.tmp_flags |= add_tmp_flags;
1786 /* update notification */
1787 if ((perm_flags_old != perm_flags_new) || (tmp_flags_old != msginfo->flags.tmp_flags)) {
1788 msginfo_update.msginfo = msginfo;
1789 msginfo_update.flags = MSGINFO_UPDATE_FLAGS;
1790 hooks_invoke(MSGINFO_UPDATE_HOOKLIST, &msginfo_update);
1791 folder_item_update(msginfo->folder, F_ITEM_UPDATE_MSGCNT);
1796 *\brief check for flags (e.g. mark) in prior msgs of current thread
1798 *\param info Current message
1799 *\param perm_flags Flags to be checked
1800 *\param parentmsgs Hash of prior msgs to avoid loops
1802 *\return gboolean TRUE if perm_flags are found
1804 gboolean procmsg_msg_has_flagged_parent_real(MsgInfo *info,
1805 MsgPermFlags perm_flags, GHashTable *parentmsgs)
1809 g_return_val_if_fail(info != NULL, FALSE);
1811 if (info != NULL && info->folder != NULL && info->inreplyto != NULL) {
1812 tmp = folder_item_get_msginfo_by_msgid(info->folder,
1814 if (tmp && (tmp->flags.perm_flags & perm_flags)) {
1815 procmsg_msginfo_free(tmp);
1817 } else if (tmp != NULL) {
1820 if (g_hash_table_lookup(parentmsgs, info)) {
1821 debug_print("loop detected: %s%c%d\n",
1822 folder_item_get_path(info->folder),
1823 G_DIR_SEPARATOR, info->msgnum);
1826 g_hash_table_insert(parentmsgs, info, "1");
1827 result = procmsg_msg_has_flagged_parent_real(
1828 tmp, perm_flags, parentmsgs);
1830 procmsg_msginfo_free(tmp);
1840 *\brief Callback for cleaning up hash of parentmsgs
1842 gboolean parentmsgs_hash_remove(gpointer key,
1850 *\brief Set up list of parentmsgs
1851 * See procmsg_msg_has_flagged_parent_real()
1853 gboolean procmsg_msg_has_flagged_parent(MsgInfo *info, MsgPermFlags perm_flags)
1856 GHashTable *parentmsgs = g_hash_table_new(NULL, NULL);
1858 result = procmsg_msg_has_flagged_parent_real(info, perm_flags, parentmsgs);
1859 g_hash_table_foreach_remove(parentmsgs, parentmsgs_hash_remove, NULL);
1860 g_hash_table_destroy(parentmsgs);
1865 *\brief Check if msgs prior in thread are marked
1866 * See procmsg_msg_has_flagged_parent_real()
1868 gboolean procmsg_msg_has_marked_parent(MsgInfo *info)
1870 return procmsg_msg_has_flagged_parent(info, MSG_MARKED);
1874 GSList *procmsg_find_children_func(MsgInfo *info,
1875 GSList *children, GSList *all)
1879 g_return_val_if_fail(info!=NULL, children);
1880 if (info->msgid == NULL)
1883 for (cur = all; cur != NULL; cur = g_slist_next(cur)) {
1884 MsgInfo *tmp = (MsgInfo *)cur->data;
1885 if (tmp->inreplyto && !strcmp(tmp->inreplyto, info->msgid)) {
1886 /* Check if message is already in the list */
1887 if ((children == NULL) ||
1888 (g_slist_index(children, tmp) == -1)) {
1889 children = g_slist_prepend(children,
1890 procmsg_msginfo_new_ref(tmp));
1891 children = procmsg_find_children_func(tmp,
1900 GSList *procmsg_find_children (MsgInfo *info)
1905 g_return_val_if_fail(info!=NULL, NULL);
1906 all = folder_item_get_msg_list(info->folder);
1907 children = procmsg_find_children_func(info, NULL, all);
1908 if (children != NULL) {
1909 for (cur = all; cur != NULL; cur = g_slist_next(cur)) {
1910 /* this will not free the used pointers
1911 created with procmsg_msginfo_new_ref */
1912 procmsg_msginfo_free((MsgInfo *)cur->data);
1920 void procmsg_update_unread_children(MsgInfo *info, gboolean newly_marked)
1922 GSList *children = procmsg_find_children(info);
1924 for (cur = children; cur != NULL; cur = g_slist_next(cur)) {
1925 MsgInfo *tmp = (MsgInfo *)cur->data;
1926 if(MSG_IS_UNREAD(tmp->flags) && !MSG_IS_IGNORE_THREAD(tmp->flags)) {
1928 info->folder->unreadmarked_msgs++;
1930 info->folder->unreadmarked_msgs--;
1931 folder_item_update(info->folder, F_ITEM_UPDATE_MSGCNT);
1933 procmsg_msginfo_free(tmp);
1935 g_slist_free(children);
1939 * Set the destination folder for a copy or move operation
1941 * \param msginfo The message which's destination folder is changed
1942 * \param to_folder The destination folder for the operation
1944 void procmsg_msginfo_set_to_folder(MsgInfo *msginfo, FolderItem *to_folder)
1946 if(msginfo->to_folder != NULL) {
1947 msginfo->to_folder->op_count--;
1948 folder_item_update(msginfo->to_folder, F_ITEM_UPDATE_MSGCNT);
1950 msginfo->to_folder = to_folder;
1951 if(to_folder != NULL) {
1952 to_folder->op_count++;
1953 folder_item_update(msginfo->to_folder, F_ITEM_UPDATE_MSGCNT);
1958 * Apply filtering actions to the msginfo
1960 * \param msginfo The MsgInfo describing the message that should be filtered
1961 * \return TRUE if the message was moved and MsgInfo is now invalid,
1964 gboolean procmsg_msginfo_filter(MsgInfo *msginfo)
1966 MailFilteringData mail_filtering_data;
1968 mail_filtering_data.msginfo = msginfo;
1969 if (hooks_invoke(MAIL_FILTERING_HOOKLIST, &mail_filtering_data)) {
1970 hooks_invoke(MAIL_POSTFILTERING_HOOKLIST, msginfo);
1974 /* filter if enabled in prefs or move to inbox if not */
1975 if((filtering_rules != NULL) &&
1976 filter_message_by_msginfo(filtering_rules, msginfo)) {
1977 hooks_invoke(MAIL_POSTFILTERING_HOOKLIST, msginfo);
1981 hooks_invoke(MAIL_POSTFILTERING_HOOKLIST, msginfo);
1985 MsgInfo *procmsg_msginfo_new_from_mimeinfo(MsgInfo *src_msginfo, MimeInfo *mimeinfo)
1987 MsgInfo *tmp_msginfo = NULL;
1988 MsgFlags flags = {0, 0};
1989 gchar *tmpfile = get_tmp_file();
1990 FILE *fp = g_fopen(tmpfile, "wb");
1992 if (!mimeinfo || mimeinfo->type != MIMETYPE_MESSAGE ||
1993 g_ascii_strcasecmp(mimeinfo->subtype, "rfc822")) {
1994 g_warning("procmsg_msginfo_new_from_mimeinfo(): unsuitable mimeinfo");
2001 if (fp && procmime_write_mimeinfo(mimeinfo, fp) >= 0) {
2004 tmp_msginfo = procheader_parse_file(
2011 if (tmp_msginfo != NULL) {
2013 tmp_msginfo->folder = src_msginfo->folder;
2014 tmp_msginfo->plaintext_file = g_strdup(tmpfile);
2016 g_warning("procmsg_msginfo_new_from_mimeinfo(): Can't generate new msginfo");