2 * Sylpheed -- a GTK+ based, lightweight, and fast e-mail client
3 * Copyright (C) 1999-2005 Hiroyuki Yamamoto
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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, 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 {NULL, NULL, FALSE}};
558 H_X_MAILING_LIST = 3,
565 g_return_if_fail(msginfo != NULL);
566 g_return_if_fail(header != NULL);
567 g_return_if_fail(key != NULL);
576 if ((fp = procmsg_open_message(msginfo)) == NULL)
578 procheader_get_header_fields(fp, hentry);
581 #define SET_FILTER_KEY(hstr, idx) \
583 *header = g_strdup(hstr); \
584 *key = hentry[idx].body; \
585 hentry[idx].body = NULL; \
588 if (hentry[H_X_BEENTHERE].body != NULL) {
589 SET_FILTER_KEY("header \"X-BeenThere\"", H_X_BEENTHERE);
590 } else if (hentry[H_X_ML_NAME].body != NULL) {
591 SET_FILTER_KEY("header \"X-ML-Name\"", H_X_ML_NAME);
592 } else if (hentry[H_X_LIST].body != NULL) {
593 SET_FILTER_KEY("header \"X-List\"", H_X_LIST);
594 } else if (hentry[H_X_MAILING_LIST].body != NULL) {
595 SET_FILTER_KEY("header \"X-Mailing-List\"", H_X_MAILING_LIST);
596 } else if (hentry[H_LIST_ID].body != NULL) {
597 SET_FILTER_KEY("header \"List-Id\"", H_LIST_ID);
598 extract_list_id_str(*key);
599 } else if (hentry[H_X_SEQUENCE].body != NULL) {
602 SET_FILTER_KEY("X-Sequence", H_X_SEQUENCE);
605 while (*p != '\0' && !g_ascii_isspace(*p)) p++;
606 while (g_ascii_isspace(*p)) p++;
607 if (g_ascii_isdigit(*p)) {
613 } else if (msginfo->subject) {
614 *header = g_strdup("subject");
615 *key = g_strdup(msginfo->subject);
618 #undef SET_FILTER_KEY
620 g_free(hentry[H_X_BEENTHERE].body);
621 hentry[H_X_BEENTHERE].body = NULL;
622 g_free(hentry[H_X_ML_NAME].body);
623 hentry[H_X_ML_NAME].body = NULL;
624 g_free(hentry[H_X_LIST].body);
625 hentry[H_X_LIST].body = NULL;
626 g_free(hentry[H_X_MAILING_LIST].body);
627 hentry[H_X_MAILING_LIST].body = NULL;
628 g_free(hentry[H_LIST_ID].body);
629 hentry[H_LIST_ID].body = NULL;
633 *header = g_strdup("from");
634 *key = g_strdup(msginfo->from);
637 *header = g_strdup("to");
638 *key = g_strdup(msginfo->to);
640 case FILTER_BY_SUBJECT:
641 *header = g_strdup("subject");
642 *key = g_strdup(msginfo->subject);
649 void procmsg_empty_trash(FolderItem *trash)
654 (trash->stype != F_TRASH &&
655 !folder_has_parent_of_type(trash, F_TRASH)))
658 if (trash && trash->total_msgs > 0) {
659 GSList *mlist = folder_item_get_msg_list(trash);
661 for (cur = mlist ; cur != NULL ; cur = cur->next) {
662 MsgInfo * msginfo = (MsgInfo *) cur->data;
663 if (MSG_IS_LOCKED(msginfo->flags))
665 if (msginfo->total_size != 0 &&
666 msginfo->size != (off_t)msginfo->total_size)
667 partial_mark_for_delete(msginfo);
669 procmsg_msginfo_free(msginfo);
672 folder_item_remove_all_msg(trash);
675 if (!trash->node || !trash->node->children)
678 node = trash->node->children;
679 while (node != NULL) {
681 procmsg_empty_trash(FOLDER_ITEM(node->data));
686 void procmsg_empty_all_trash(void)
691 for (cur = folder_get_list(); cur != NULL; cur = cur->next) {
692 Folder *folder = FOLDER(cur->data);
693 trash = folder->trash;
694 procmsg_empty_trash(trash);
695 if (folder->account && folder->account->set_trash_folder &&
696 folder_find_item_from_identifier(folder->account->trash_folder))
698 folder_find_item_from_identifier(folder->account->trash_folder));
702 static PrefsAccount *procmsg_get_account_from_file(const gchar *file)
704 PrefsAccount *mailac = NULL;
708 static HeaderEntry qentry[] = {{"S:", NULL, FALSE},
709 {"SSV:", NULL, FALSE},
711 {"NG:", NULL, FALSE},
712 {"MAID:", NULL, FALSE},
713 {"NAID:", NULL, FALSE},
714 {"SCF:", NULL, FALSE},
715 {"RMID:", NULL, FALSE},
716 {"FMID:", NULL, FALSE},
717 {"X-Sylpheed-Privacy-System:", NULL, FALSE},
718 {"X-Sylpheed-Encrypt:", NULL, FALSE},
719 {"X-Sylpheed-Encrypt-Data:", NULL, FALSE},
720 {NULL, NULL, FALSE}};
722 g_return_val_if_fail(file != NULL, NULL);
724 if ((fp = g_fopen(file, "rb")) == NULL) {
725 FILE_OP_ERROR(file, "fopen");
729 while ((hnum = procheader_get_one_field(buf, sizeof(buf), fp, qentry))
731 gchar *p = buf + strlen(qentry[hnum].name);
733 if (hnum == Q_MAIL_ACCOUNT_ID) {
734 mailac = account_find_from_id(atoi(p));
742 static GSList *procmsg_list_sort_by_account(FolderItem *queue, GSList *list)
744 GSList *result = NULL;
746 PrefsAccount *last_account = NULL;
749 gboolean nothing_to_sort = TRUE;
754 orig = g_slist_copy(list);
756 msg = (MsgInfo *)orig->data;
758 for (cur = orig; cur; cur = cur->next)
759 debug_print("sort before %s\n", ((MsgInfo *)cur->data)->from);
764 nothing_to_sort = TRUE;
768 PrefsAccount *ac = NULL;
769 msg = (MsgInfo *)cur->data;
770 file = folder_item_fetch_msg(queue, msg->msgnum);
771 ac = procmsg_get_account_from_file(file);
774 if (last_account == NULL || (ac != NULL && ac == last_account)) {
775 result = g_slist_append(result, msg);
776 orig = g_slist_remove(orig, msg);
778 nothing_to_sort = FALSE;
784 if (orig || g_slist_length(orig)) {
785 if (!last_account && nothing_to_sort) {
786 /* can't find an account for the rest of the list */
789 result = g_slist_append(result, cur->data);
800 for (cur = result; cur; cur = cur->next)
801 debug_print("sort after %s\n", ((MsgInfo *)cur->data)->from);
808 static gboolean procmsg_is_last_for_account(FolderItem *queue, MsgInfo *msginfo, GSList *elem)
810 gchar *file = folder_item_fetch_msg(queue, msginfo->msgnum);
811 PrefsAccount *ac = procmsg_get_account_from_file(file);
814 for (cur = elem; cur; cur = cur->next) {
815 MsgInfo *cur_msginfo = (MsgInfo *)cur->data;
816 file = folder_item_fetch_msg(queue, cur_msginfo->msgnum);
818 if (cur_msginfo != msginfo && !MSG_IS_LOCKED(cur_msginfo->flags)) {
819 if (procmsg_get_account_from_file(file) == ac) {
831 *\brief Send messages in queue
833 *\param queue Queue folder to process
834 *\param save_msgs Unused
836 *\return Number of messages sent, negative if an error occurred
837 * positive if no error occurred
839 gint procmsg_send_queue(FolderItem *queue, gboolean save_msgs)
841 gint sent = 0, err = 0;
843 GSList *sorted_list = NULL;
847 queue = folder_get_default_queue();
848 g_return_val_if_fail(queue != NULL, -1);
850 folder_item_scan(queue);
851 list = folder_item_get_msg_list(queue);
853 /* sort the list per sender account; this helps reusing the same SMTP server */
854 sorted_list = procmsg_list_sort_by_account(queue, list);
856 for (elem = sorted_list; elem != NULL; elem = elem->next) {
860 msginfo = (MsgInfo *)(elem->data);
861 if (!MSG_IS_LOCKED(msginfo->flags)) {
862 file = folder_item_fetch_msg(queue, msginfo->msgnum);
864 if (procmsg_send_message_queue_full(file,
865 !procmsg_is_last_for_account(queue, msginfo, elem)) < 0) {
866 g_warning("Sending queued message %d failed.\n",
871 * We save in procmsg_send_message_queue because
872 * we need the destination folder from the queue
876 procmsg_save_to_outbox
877 (queue->folder->outbox,
881 folder_item_remove_msg(queue, msginfo->msgnum);
886 /* FIXME: supposedly if only one message is locked, and queue
887 * is being flushed, the following free says something like
888 * "freeing msg ## in folder (nil)". */
889 procmsg_msginfo_free(msginfo);
892 g_slist_free(sorted_list);
893 folder_item_scan(queue);
895 if (queue->node && queue->node->children) {
896 node = queue->node->children;
897 while (node != NULL) {
900 res = procmsg_send_queue(FOLDER_ITEM(node->data), save_msgs);
909 return (err != 0 ? -err : sent);
913 *\brief Determine if a queue folder is empty
915 *\param queue Queue folder to process
917 *\return TRUE if the queue folder is empty, otherwise return FALSE
919 gboolean procmsg_queue_is_empty(FolderItem *queue)
922 gboolean res = FALSE;
924 queue = folder_get_default_queue();
925 g_return_val_if_fail(queue != NULL, TRUE);
927 folder_item_scan(queue);
928 list = folder_item_get_msg_list(queue);
929 res = (list == NULL);
930 procmsg_msg_list_free(list);
934 if (queue->node && queue->node->children) {
935 node = queue->node->children;
936 while (node != NULL) {
938 if (!procmsg_queue_is_empty(FOLDER_ITEM(node->data)))
947 gint procmsg_remove_special_headers(const gchar *in, const gchar *out)
952 if ((fp = g_fopen(in, "rb")) == NULL) {
953 FILE_OP_ERROR(in, "fopen");
956 if ((outfp = g_fopen(out, "wb")) == NULL) {
957 FILE_OP_ERROR(out, "fopen");
961 while (fgets(buf, sizeof(buf), fp) != NULL)
962 if (buf[0] == '\r' || buf[0] == '\n') break;
963 while (fgets(buf, sizeof(buf), fp) != NULL)
970 gint procmsg_save_to_outbox(FolderItem *outbox, const gchar *file,
974 MsgInfo *msginfo, *tmp_msginfo;
975 MsgFlags flag = {0, 0};
977 debug_print("saving sent message...\n");
980 outbox = folder_get_default_outbox();
981 g_return_val_if_fail(outbox != NULL, -1);
983 /* remove queueing headers */
985 gchar tmp[MAXPATHLEN + 1];
987 g_snprintf(tmp, sizeof(tmp), "%s%ctmpmsg.out.%08x",
988 get_rc_dir(), G_DIR_SEPARATOR, (guint) rand());
990 if (procmsg_remove_special_headers(file, tmp) !=0)
993 folder_item_scan(outbox);
994 if ((num = folder_item_add_msg(outbox, tmp, &flag, TRUE)) < 0) {
995 g_warning("can't save message\n");
1000 folder_item_scan(outbox);
1001 if ((num = folder_item_add_msg
1002 (outbox, file, &flag, FALSE)) < 0) {
1003 g_warning("can't save message\n");
1008 msginfo = folder_item_get_msginfo(outbox, num); /* refcnt++ */
1009 tmp_msginfo = procmsg_msginfo_get_full_info(msginfo); /* refcnt++ */
1010 if (msginfo != NULL) {
1011 procmsg_msginfo_unset_flags(msginfo, ~0, 0);
1012 procmsg_msginfo_free(msginfo); /* refcnt-- */
1013 /* tmp_msginfo == msginfo */
1014 if (tmp_msginfo && (msginfo->dispositionnotificationto ||
1015 msginfo->returnreceiptto)) {
1016 procmsg_msginfo_set_flags(msginfo, MSG_RETRCPT_SENT, 0);
1017 procmsg_msginfo_free(msginfo); /* refcnt-- */
1024 void procmsg_print_message(MsgInfo *msginfo, const gchar *cmdline)
1026 static const gchar *def_cmd = "lpr %s";
1027 static guint id = 0;
1033 g_return_if_fail(msginfo);
1035 if (procmime_msginfo_is_encrypted(msginfo))
1036 tmpfp = procmime_get_first_encrypted_text_content(msginfo);
1038 tmpfp = procmime_get_first_text_content(msginfo);
1039 if (tmpfp == NULL) {
1040 g_warning("Can't get text part\n");
1044 prtmp = g_strdup_printf("%s%cprinttmp.%08x",
1045 get_mime_tmp_dir(), G_DIR_SEPARATOR, id++);
1047 if ((prfp = g_fopen(prtmp, "wb")) == NULL) {
1048 FILE_OP_ERROR(prtmp, "fopen");
1054 if (msginfo->date) fprintf(prfp, "Date: %s\n", msginfo->date);
1055 if (msginfo->from) fprintf(prfp, "From: %s\n", msginfo->from);
1056 if (msginfo->to) fprintf(prfp, "To: %s\n", msginfo->to);
1057 if (msginfo->cc) fprintf(prfp, "Cc: %s\n", msginfo->cc);
1058 if (msginfo->newsgroups)
1059 fprintf(prfp, "Newsgroups: %s\n", msginfo->newsgroups);
1060 if (msginfo->subject) fprintf(prfp, "Subject: %s\n", msginfo->subject);
1063 while (fgets(buf, sizeof(buf), tmpfp) != NULL)
1069 if (cmdline && (p = strchr(cmdline, '%')) && *(p + 1) == 's' &&
1070 !strchr(p + 2, '%'))
1071 g_snprintf(buf, sizeof(buf) - 1, cmdline, prtmp);
1074 g_warning("Print command line is invalid: '%s'\n",
1076 g_snprintf(buf, sizeof(buf) - 1, def_cmd, prtmp);
1082 if (buf[strlen(buf) - 1] != '&') strcat(buf, "&");
1086 MsgInfo *procmsg_msginfo_new_ref(MsgInfo *msginfo)
1093 MsgInfo *procmsg_msginfo_new(void)
1095 MsgInfo *newmsginfo;
1097 newmsginfo = g_new0(MsgInfo, 1);
1098 newmsginfo->refcnt = 1;
1103 MsgInfo *procmsg_msginfo_copy(MsgInfo *msginfo)
1105 MsgInfo *newmsginfo;
1108 if (msginfo == NULL) return NULL;
1110 newmsginfo = g_new0(MsgInfo, 1);
1112 newmsginfo->refcnt = 1;
1114 #define MEMBCOPY(mmb) newmsginfo->mmb = msginfo->mmb
1115 #define MEMBDUP(mmb) newmsginfo->mmb = msginfo->mmb ? \
1116 g_strdup(msginfo->mmb) : NULL
1131 MEMBDUP(newsgroups);
1138 MEMBCOPY(to_folder);
1141 MEMBDUP(dispositionnotificationto);
1142 MEMBDUP(returnreceiptto);
1144 refs = msginfo->references;
1145 for (refs = msginfo->references; refs != NULL; refs = refs->next) {
1146 newmsginfo->references = g_slist_prepend
1147 (newmsginfo->references, g_strdup(refs->data));
1149 newmsginfo->references = g_slist_reverse(newmsginfo->references);
1152 MEMBCOPY(threadscore);
1153 MEMBDUP(plaintext_file);
1158 MsgInfo *procmsg_msginfo_get_full_info(MsgInfo *msginfo)
1160 MsgInfo *full_msginfo;
1163 if (msginfo == NULL) return NULL;
1165 file = procmsg_get_message_file_path(msginfo);
1166 if (!file || !is_file_exist(file)) {
1168 file = procmsg_get_message_file(msginfo);
1170 if (!file || !is_file_exist(file)) {
1171 g_warning("procmsg_msginfo_get_full_info(): can't get message file.\n");
1175 full_msginfo = procheader_parse_file(file, msginfo->flags, TRUE, FALSE);
1177 if (!full_msginfo) return NULL;
1179 /* CLAWS: make sure we add the missing members; see:
1180 * procheader.c::procheader_get_headernames() */
1181 if (!msginfo->xface)
1182 msginfo->xface = g_strdup(full_msginfo->xface);
1183 if (!msginfo->dispositionnotificationto)
1184 msginfo->dispositionnotificationto =
1185 g_strdup(full_msginfo->dispositionnotificationto);
1186 if (!msginfo->returnreceiptto)
1187 msginfo->returnreceiptto = g_strdup
1188 (full_msginfo->returnreceiptto);
1189 if (!msginfo->partial_recv && full_msginfo->partial_recv)
1190 msginfo->partial_recv = g_strdup
1191 (full_msginfo->partial_recv);
1192 msginfo->total_size = full_msginfo->total_size;
1193 if (!msginfo->account_server && full_msginfo->account_server)
1194 msginfo->account_server = g_strdup
1195 (full_msginfo->account_server);
1196 if (!msginfo->account_login && full_msginfo->account_login)
1197 msginfo->account_login = g_strdup
1198 (full_msginfo->account_login);
1199 msginfo->planned_download = full_msginfo->planned_download;
1200 procmsg_msginfo_free(full_msginfo);
1202 return procmsg_msginfo_new_ref(msginfo);
1205 void procmsg_msginfo_free(MsgInfo *msginfo)
1207 if (msginfo == NULL) return;
1210 if (msginfo->refcnt > 0)
1213 if (msginfo->to_folder) {
1214 msginfo->to_folder->op_count--;
1215 folder_item_update(msginfo->to_folder, F_ITEM_UPDATE_MSGCNT);
1218 g_free(msginfo->fromspace);
1219 g_free(msginfo->returnreceiptto);
1220 g_free(msginfo->dispositionnotificationto);
1221 g_free(msginfo->xface);
1223 g_free(msginfo->fromname);
1225 g_free(msginfo->date);
1226 g_free(msginfo->from);
1227 g_free(msginfo->to);
1228 g_free(msginfo->cc);
1229 g_free(msginfo->newsgroups);
1230 g_free(msginfo->subject);
1231 g_free(msginfo->msgid);
1232 g_free(msginfo->inreplyto);
1233 g_free(msginfo->xref);
1235 g_free(msginfo->partial_recv);
1236 g_free(msginfo->account_server);
1237 g_free(msginfo->account_login);
1239 slist_free_strings(msginfo->references);
1240 g_slist_free(msginfo->references);
1242 g_free(msginfo->plaintext_file);
1247 guint procmsg_msginfo_memusage(MsgInfo *msginfo)
1252 memusage += sizeof(MsgInfo);
1253 if (msginfo->fromname)
1254 memusage += strlen(msginfo->fromname);
1256 memusage += strlen(msginfo->date);
1258 memusage += strlen(msginfo->from);
1260 memusage += strlen(msginfo->to);
1262 memusage += strlen(msginfo->cc);
1263 if (msginfo->newsgroups)
1264 memusage += strlen(msginfo->newsgroups);
1265 if (msginfo->subject)
1266 memusage += strlen(msginfo->subject);
1268 memusage += strlen(msginfo->msgid);
1269 if (msginfo->inreplyto)
1270 memusage += strlen(msginfo->inreplyto);
1272 memusage += strlen(msginfo->xface);
1273 if (msginfo->dispositionnotificationto)
1274 memusage += strlen(msginfo->dispositionnotificationto);
1275 if (msginfo->returnreceiptto)
1276 memusage += strlen(msginfo->returnreceiptto);
1277 for (refs = msginfo->references; refs; refs=refs->next) {
1278 gchar *r = (gchar *)refs->data;
1279 memusage += r?strlen(r):0;
1281 if (msginfo->fromspace)
1282 memusage += strlen(msginfo->fromspace);
1287 gint procmsg_cmp_msgnum_for_sort(gconstpointer a, gconstpointer b)
1289 const MsgInfo *msginfo1 = a;
1290 const MsgInfo *msginfo2 = b;
1297 return msginfo1->msgnum - msginfo2->msgnum;
1300 static gint procmsg_send_message_queue_full(const gchar *file, gboolean keep_session)
1302 static HeaderEntry qentry[] = {{"S:", NULL, FALSE},
1303 {"SSV:", NULL, FALSE},
1304 {"R:", NULL, FALSE},
1305 {"NG:", NULL, FALSE},
1306 {"MAID:", NULL, FALSE},
1307 {"NAID:", NULL, FALSE},
1308 {"SCF:", NULL, FALSE},
1309 {"RMID:", NULL, FALSE},
1310 {"FMID:", NULL, FALSE},
1311 {"X-Sylpheed-Privacy-System:", NULL, FALSE},
1312 {"X-Sylpheed-Encrypt:", NULL, FALSE},
1313 {"X-Sylpheed-Encrypt-Data:", NULL, FALSE},
1314 {NULL, NULL, FALSE}};
1317 gint mailval = 0, newsval = 0;
1319 gchar *smtpserver = NULL;
1320 GSList *to_list = NULL;
1321 GSList *newsgroup_list = NULL;
1322 gchar *savecopyfolder = NULL;
1323 gchar *replymessageid = NULL;
1324 gchar *fwdmessageid = NULL;
1325 gchar *privacy_system = NULL;
1326 gboolean encrypt = FALSE;
1327 gchar *encrypt_data = NULL;
1328 gchar buf[BUFFSIZE];
1330 PrefsAccount *mailac = NULL, *newsac = NULL;
1331 gboolean save_clear_text = TRUE;
1332 gchar *tmp_enc_file = NULL;
1336 g_return_val_if_fail(file != NULL, -1);
1338 if ((fp = g_fopen(file, "rb")) == NULL) {
1339 FILE_OP_ERROR(file, "fopen");
1343 while ((hnum = procheader_get_one_field(buf, sizeof(buf), fp, qentry))
1345 gchar *p = buf + strlen(qentry[hnum].name);
1353 if (smtpserver == NULL)
1354 smtpserver = g_strdup(p);
1357 to_list = address_list_append(to_list, p);
1360 newsgroup_list = newsgroup_list_append(newsgroup_list, p);
1362 case Q_MAIL_ACCOUNT_ID:
1363 mailac = account_find_from_id(atoi(p));
1365 case Q_NEWS_ACCOUNT_ID:
1366 newsac = account_find_from_id(atoi(p));
1368 case Q_SAVE_COPY_FOLDER:
1369 if (savecopyfolder == NULL)
1370 savecopyfolder = g_strdup(p);
1372 case Q_REPLY_MESSAGE_ID:
1373 if (replymessageid == NULL)
1374 replymessageid = g_strdup(p);
1376 case Q_FWD_MESSAGE_ID:
1377 if (fwdmessageid == NULL)
1378 fwdmessageid = g_strdup(p);
1380 case Q_PRIVACY_SYSTEM:
1381 if (privacy_system == NULL)
1382 privacy_system = g_strdup(p);
1388 case Q_ENCRYPT_DATA:
1389 if (encrypt_data == NULL)
1390 encrypt_data = g_strdup(p);
1394 filepos = ftell(fp);
1399 save_clear_text = (mailac != NULL && mailac->save_encrypted_as_clear_text);
1404 mimeinfo = procmime_scan_queue_file(file);
1405 if (!privacy_encrypt(privacy_system, mimeinfo, encrypt_data)
1406 || (fp = my_tmpfile()) == NULL
1407 || procmime_write_mimeinfo(mimeinfo, fp) < 0) {
1410 procmime_mimeinfo_free_all(mimeinfo);
1413 slist_free_strings(to_list);
1414 g_slist_free(to_list);
1415 slist_free_strings(newsgroup_list);
1416 g_slist_free(newsgroup_list);
1417 g_free(savecopyfolder);
1418 g_free(replymessageid);
1419 g_free(fwdmessageid);
1420 g_free(privacy_system);
1421 g_free(encrypt_data);
1426 if (!save_clear_text) {
1427 gchar *content = NULL;
1428 FILE *tmpfp = get_tmpfile_in_dir(get_mime_tmp_dir(), &tmp_enc_file);
1432 content = file_read_stream_to_str(fp);
1435 str_write_to_file(content, tmp_enc_file);
1438 g_warning("couldn't get tempfile\n");
1442 procmime_mimeinfo_free_all(mimeinfo);
1448 debug_print("Sending message by mail\n");
1450 g_warning("Queued message header is broken.\n");
1452 } else if (mailac && mailac->use_mail_command &&
1453 mailac->mail_command && (* mailac->mail_command)) {
1454 mailval = send_message_local(mailac->mail_command, fp);
1458 mailac = account_find_from_smtp_server(from, smtpserver);
1460 g_warning("Account not found. "
1461 "Using current account...\n");
1462 mailac = cur_account;
1467 mailval = send_message_smtp_full(mailac, to_list, fp, keep_session);
1469 PrefsAccount tmp_ac;
1471 g_warning("Account not found.\n");
1473 memset(&tmp_ac, 0, sizeof(PrefsAccount));
1474 tmp_ac.address = from;
1475 tmp_ac.smtp_server = smtpserver;
1476 tmp_ac.smtpport = SMTP_PORT;
1477 mailval = send_message_smtp(&tmp_ac, to_list, fp);
1482 fseek(fp, filepos, SEEK_SET);
1483 if (newsgroup_list && (mailval == 0)) {
1488 /* write to temporary file */
1489 tmp = g_strdup_printf("%s%ctmp%d", g_get_tmp_dir(),
1490 G_DIR_SEPARATOR, (gint)file);
1491 if ((tmpfp = g_fopen(tmp, "wb")) == NULL) {
1492 FILE_OP_ERROR(tmp, "fopen");
1494 alertpanel_error(_("Could not create temporary file for news sending."));
1496 if (change_file_mode_rw(tmpfp, tmp) < 0) {
1497 FILE_OP_ERROR(tmp, "chmod");
1498 g_warning("can't change file mode\n");
1501 while ((newsval == 0) && fgets(buf, sizeof(buf), fp) != NULL) {
1502 if (fputs(buf, tmpfp) == EOF) {
1503 FILE_OP_ERROR(tmp, "fputs");
1505 alertpanel_error(_("Error when writing temporary file for news sending."));
1511 debug_print("Sending message by news\n");
1513 folder = FOLDER(newsac->folder);
1515 newsval = news_post(folder, tmp);
1517 alertpanel_error(_("Error occurred while posting the message to %s ."),
1518 newsac->nntp_server);
1528 /* save message to outbox */
1529 if (mailval == 0 && newsval == 0 && savecopyfolder) {
1532 debug_print("saving sent message...\n");
1534 outbox = folder_find_item_from_identifier(savecopyfolder);
1536 outbox = folder_get_default_outbox();
1538 if (save_clear_text || tmp_enc_file == NULL) {
1539 procmsg_save_to_outbox(outbox, file, TRUE);
1541 procmsg_save_to_outbox(outbox, tmp_enc_file, FALSE);
1545 if (tmp_enc_file != NULL) {
1546 g_unlink(tmp_enc_file);
1548 tmp_enc_file = NULL;
1551 if (replymessageid != NULL || fwdmessageid != NULL) {
1555 if (replymessageid != NULL)
1556 tokens = g_strsplit(replymessageid, "\x7f", 0);
1558 tokens = g_strsplit(fwdmessageid, "\x7f", 0);
1559 item = folder_find_item_from_identifier(tokens[0]);
1561 /* check if queued message has valid folder and message id */
1562 if (item != NULL && tokens[2] != NULL) {
1565 msginfo = folder_item_get_msginfo(item, atoi(tokens[1]));
1567 /* check if referring message exists and has a message id */
1568 if ((msginfo != NULL) &&
1569 (msginfo->msgid != NULL) &&
1570 (strcmp(msginfo->msgid, tokens[2]) != 0)) {
1571 procmsg_msginfo_free(msginfo);
1575 if (msginfo == NULL) {
1576 msginfo = folder_item_get_msginfo_by_msgid(item, tokens[2]);
1579 if (msginfo != NULL) {
1580 if (replymessageid != NULL) {
1581 procmsg_msginfo_unset_flags(msginfo, MSG_FORWARDED, 0);
1582 procmsg_msginfo_set_flags(msginfo, MSG_REPLIED, 0);
1584 procmsg_msginfo_unset_flags(msginfo, MSG_REPLIED, 0);
1585 procmsg_msginfo_set_flags(msginfo, MSG_FORWARDED, 0);
1587 procmsg_msginfo_free(msginfo);
1595 slist_free_strings(to_list);
1596 g_slist_free(to_list);
1597 slist_free_strings(newsgroup_list);
1598 g_slist_free(newsgroup_list);
1599 g_free(savecopyfolder);
1600 g_free(replymessageid);
1601 g_free(fwdmessageid);
1602 g_free(privacy_system);
1603 g_free(encrypt_data);
1605 return (newsval != 0 ? newsval : mailval);
1608 gint procmsg_send_message_queue(const gchar *file)
1610 return procmsg_send_message_queue_full(file, FALSE);
1613 static void update_folder_msg_counts(FolderItem *item, MsgInfo *msginfo, MsgPermFlags old_flags)
1615 MsgPermFlags new_flags = msginfo->flags.perm_flags;
1618 if (!(old_flags & MSG_NEW) && (new_flags & MSG_NEW)) {
1622 if ((old_flags & MSG_NEW) && !(new_flags & MSG_NEW)) {
1627 if (!(old_flags & MSG_UNREAD) && (new_flags & MSG_UNREAD)) {
1628 item->unread_msgs++;
1629 if (procmsg_msg_has_marked_parent(msginfo))
1630 item->unreadmarked_msgs++;
1633 if ((old_flags & MSG_UNREAD) && !(new_flags & MSG_UNREAD)) {
1634 item->unread_msgs--;
1635 if (procmsg_msg_has_marked_parent(msginfo))
1636 item->unreadmarked_msgs--;
1640 if (!(old_flags & MSG_MARKED) && (new_flags & MSG_MARKED)) {
1641 procmsg_update_unread_children(msginfo, TRUE);
1642 item->marked_msgs++;
1645 if ((old_flags & MSG_MARKED) && !(new_flags & MSG_MARKED)) {
1646 procmsg_update_unread_children(msginfo, FALSE);
1647 item->marked_msgs--;
1651 void procmsg_msginfo_set_flags(MsgInfo *msginfo, MsgPermFlags perm_flags, MsgTmpFlags tmp_flags)
1654 MsgInfoUpdate msginfo_update;
1655 MsgPermFlags perm_flags_new, perm_flags_old;
1656 MsgTmpFlags tmp_flags_old;
1658 g_return_if_fail(msginfo != NULL);
1659 item = msginfo->folder;
1660 g_return_if_fail(item != NULL);
1662 debug_print("Setting flags for message %d in folder %s\n", msginfo->msgnum, item->path);
1664 /* Perm Flags handling */
1665 perm_flags_old = msginfo->flags.perm_flags;
1666 perm_flags_new = msginfo->flags.perm_flags | perm_flags;
1667 if ((perm_flags & MSG_IGNORE_THREAD) || (perm_flags_old & MSG_IGNORE_THREAD)) {
1668 perm_flags_new &= ~(MSG_NEW | MSG_UNREAD);
1671 if (perm_flags_old != perm_flags_new) {
1672 folder_item_change_msg_flags(msginfo->folder, msginfo, perm_flags_new);
1674 update_folder_msg_counts(item, msginfo, perm_flags_old);
1678 /* Tmp flags handling */
1679 tmp_flags_old = msginfo->flags.tmp_flags;
1680 msginfo->flags.tmp_flags |= tmp_flags;
1682 /* update notification */
1683 if ((perm_flags_old != perm_flags_new) || (tmp_flags_old != msginfo->flags.tmp_flags)) {
1684 msginfo_update.msginfo = msginfo;
1685 msginfo_update.flags = MSGINFO_UPDATE_FLAGS;
1686 hooks_invoke(MSGINFO_UPDATE_HOOKLIST, &msginfo_update);
1687 folder_item_update(msginfo->folder, F_ITEM_UPDATE_MSGCNT);
1691 void procmsg_msginfo_unset_flags(MsgInfo *msginfo, MsgPermFlags perm_flags, MsgTmpFlags tmp_flags)
1694 MsgInfoUpdate msginfo_update;
1695 MsgPermFlags perm_flags_new, perm_flags_old;
1696 MsgTmpFlags tmp_flags_old;
1698 g_return_if_fail(msginfo != NULL);
1699 item = msginfo->folder;
1700 g_return_if_fail(item != NULL);
1702 debug_print("Unsetting flags for message %d in folder %s\n", msginfo->msgnum, item->path);
1704 /* Perm Flags handling */
1705 perm_flags_old = msginfo->flags.perm_flags;
1706 perm_flags_new = msginfo->flags.perm_flags & ~perm_flags;
1708 if (perm_flags_old != perm_flags_new) {
1709 folder_item_change_msg_flags(msginfo->folder, msginfo, perm_flags_new);
1711 update_folder_msg_counts(item, msginfo, perm_flags_old);
1713 msginfo_update.msginfo = msginfo;
1714 msginfo_update.flags = MSGINFO_UPDATE_FLAGS;
1715 hooks_invoke(MSGINFO_UPDATE_HOOKLIST, &msginfo_update);
1716 folder_item_update(msginfo->folder, F_ITEM_UPDATE_MSGCNT);
1719 /* Tmp flags hanlding */
1720 tmp_flags_old = msginfo->flags.tmp_flags;
1721 msginfo->flags.tmp_flags &= ~tmp_flags;
1723 /* update notification */
1724 if ((perm_flags_old != perm_flags_new) || (tmp_flags_old != msginfo->flags.tmp_flags)) {
1725 msginfo_update.msginfo = msginfo;
1726 msginfo_update.flags = MSGINFO_UPDATE_FLAGS;
1727 hooks_invoke(MSGINFO_UPDATE_HOOKLIST, &msginfo_update);
1728 folder_item_update(msginfo->folder, F_ITEM_UPDATE_MSGCNT);
1732 void procmsg_msginfo_change_flags(MsgInfo *msginfo,
1733 MsgPermFlags add_perm_flags, MsgTmpFlags add_tmp_flags,
1734 MsgPermFlags rem_perm_flags, MsgTmpFlags rem_tmp_flags)
1737 MsgInfoUpdate msginfo_update;
1738 MsgPermFlags perm_flags_new, perm_flags_old;
1739 MsgTmpFlags tmp_flags_old;
1741 g_return_if_fail(msginfo != NULL);
1742 item = msginfo->folder;
1743 g_return_if_fail(item != NULL);
1745 debug_print("Changing flags for message %d in folder %s\n", msginfo->msgnum, item->path);
1747 /* Perm Flags handling */
1748 perm_flags_old = msginfo->flags.perm_flags;
1749 perm_flags_new = (msginfo->flags.perm_flags & ~rem_perm_flags) | add_perm_flags;
1750 if ((add_perm_flags & MSG_IGNORE_THREAD) || (perm_flags_old & MSG_IGNORE_THREAD)) {
1751 perm_flags_new &= ~(MSG_NEW | MSG_UNREAD);
1754 if (perm_flags_old != perm_flags_new) {
1755 folder_item_change_msg_flags(msginfo->folder, msginfo, perm_flags_new);
1757 update_folder_msg_counts(item, msginfo, perm_flags_old);
1761 /* Tmp flags handling */
1762 tmp_flags_old = msginfo->flags.tmp_flags;
1763 msginfo->flags.tmp_flags &= ~rem_tmp_flags;
1764 msginfo->flags.tmp_flags |= add_tmp_flags;
1766 /* update notification */
1767 if ((perm_flags_old != perm_flags_new) || (tmp_flags_old != msginfo->flags.tmp_flags)) {
1768 msginfo_update.msginfo = msginfo;
1769 msginfo_update.flags = MSGINFO_UPDATE_FLAGS;
1770 hooks_invoke(MSGINFO_UPDATE_HOOKLIST, &msginfo_update);
1771 folder_item_update(msginfo->folder, F_ITEM_UPDATE_MSGCNT);
1776 *\brief check for flags (e.g. mark) in prior msgs of current thread
1778 *\param info Current message
1779 *\param perm_flags Flags to be checked
1780 *\param parentmsgs Hash of prior msgs to avoid loops
1782 *\return gboolean TRUE if perm_flags are found
1784 gboolean procmsg_msg_has_flagged_parent_real(MsgInfo *info,
1785 MsgPermFlags perm_flags, GHashTable *parentmsgs)
1789 g_return_val_if_fail(info != NULL, FALSE);
1791 if (info != NULL && info->folder != NULL && info->inreplyto != NULL) {
1792 tmp = folder_item_get_msginfo_by_msgid(info->folder,
1794 if (tmp && (tmp->flags.perm_flags & perm_flags)) {
1795 procmsg_msginfo_free(tmp);
1797 } else if (tmp != NULL) {
1800 if (g_hash_table_lookup(parentmsgs, info)) {
1801 debug_print("loop detected: %s%c%d\n",
1802 folder_item_get_path(info->folder),
1803 G_DIR_SEPARATOR, info->msgnum);
1806 g_hash_table_insert(parentmsgs, info, "1");
1807 result = procmsg_msg_has_flagged_parent_real(
1808 tmp, perm_flags, parentmsgs);
1810 procmsg_msginfo_free(tmp);
1820 *\brief Callback for cleaning up hash of parentmsgs
1822 gboolean parentmsgs_hash_remove(gpointer key,
1830 *\brief Set up list of parentmsgs
1831 * See procmsg_msg_has_flagged_parent_real()
1833 gboolean procmsg_msg_has_flagged_parent(MsgInfo *info, MsgPermFlags perm_flags)
1836 GHashTable *parentmsgs = g_hash_table_new(NULL, NULL);
1838 result = procmsg_msg_has_flagged_parent_real(info, perm_flags, parentmsgs);
1839 g_hash_table_foreach_remove(parentmsgs, parentmsgs_hash_remove, NULL);
1840 g_hash_table_destroy(parentmsgs);
1845 *\brief Check if msgs prior in thread are marked
1846 * See procmsg_msg_has_flagged_parent_real()
1848 gboolean procmsg_msg_has_marked_parent(MsgInfo *info)
1850 return procmsg_msg_has_flagged_parent(info, MSG_MARKED);
1854 GSList *procmsg_find_children_func(MsgInfo *info,
1855 GSList *children, GSList *all)
1859 g_return_val_if_fail(info!=NULL, children);
1860 if (info->msgid == NULL)
1863 for (cur = all; cur != NULL; cur = g_slist_next(cur)) {
1864 MsgInfo *tmp = (MsgInfo *)cur->data;
1865 if (tmp->inreplyto && !strcmp(tmp->inreplyto, info->msgid)) {
1866 /* Check if message is already in the list */
1867 if ((children == NULL) ||
1868 (g_slist_index(children, tmp) == -1)) {
1869 children = g_slist_prepend(children,
1870 procmsg_msginfo_new_ref(tmp));
1871 children = procmsg_find_children_func(tmp,
1880 GSList *procmsg_find_children (MsgInfo *info)
1885 g_return_val_if_fail(info!=NULL, NULL);
1886 all = folder_item_get_msg_list(info->folder);
1887 children = procmsg_find_children_func(info, NULL, all);
1888 if (children != NULL) {
1889 for (cur = all; cur != NULL; cur = g_slist_next(cur)) {
1890 /* this will not free the used pointers
1891 created with procmsg_msginfo_new_ref */
1892 procmsg_msginfo_free((MsgInfo *)cur->data);
1900 void procmsg_update_unread_children(MsgInfo *info, gboolean newly_marked)
1902 GSList *children = procmsg_find_children(info);
1904 for (cur = children; cur != NULL; cur = g_slist_next(cur)) {
1905 MsgInfo *tmp = (MsgInfo *)cur->data;
1906 if(MSG_IS_UNREAD(tmp->flags) && !MSG_IS_IGNORE_THREAD(tmp->flags)) {
1908 info->folder->unreadmarked_msgs++;
1910 info->folder->unreadmarked_msgs--;
1911 folder_item_update(info->folder, F_ITEM_UPDATE_MSGCNT);
1913 procmsg_msginfo_free(tmp);
1915 g_slist_free(children);
1919 * Set the destination folder for a copy or move operation
1921 * \param msginfo The message which's destination folder is changed
1922 * \param to_folder The destination folder for the operation
1924 void procmsg_msginfo_set_to_folder(MsgInfo *msginfo, FolderItem *to_folder)
1926 if(msginfo->to_folder != NULL) {
1927 msginfo->to_folder->op_count--;
1928 folder_item_update(msginfo->to_folder, F_ITEM_UPDATE_MSGCNT);
1930 msginfo->to_folder = to_folder;
1931 if(to_folder != NULL) {
1932 to_folder->op_count++;
1933 folder_item_update(msginfo->to_folder, F_ITEM_UPDATE_MSGCNT);
1938 * Apply filtering actions to the msginfo
1940 * \param msginfo The MsgInfo describing the message that should be filtered
1941 * \return TRUE if the message was moved and MsgInfo is now invalid,
1944 gboolean procmsg_msginfo_filter(MsgInfo *msginfo)
1946 MailFilteringData mail_filtering_data;
1948 mail_filtering_data.msginfo = msginfo;
1949 if (hooks_invoke(MAIL_FILTERING_HOOKLIST, &mail_filtering_data))
1952 /* filter if enabled in prefs or move to inbox if not */
1953 if((filtering_rules != NULL) &&
1954 filter_message_by_msginfo(filtering_rules, msginfo))
1957 hooks_invoke(MAIL_POSTFILTERING_HOOKLIST, msginfo);
1962 MsgInfo *procmsg_msginfo_new_from_mimeinfo(MsgInfo *src_msginfo, MimeInfo *mimeinfo)
1964 MsgInfo *tmp_msginfo = NULL;
1965 MsgFlags flags = {0, 0};
1966 gchar *tmpfile = get_tmp_file();
1967 FILE *fp = g_fopen(tmpfile, "wb");
1969 if (!mimeinfo || mimeinfo->type != MIMETYPE_MESSAGE ||
1970 g_ascii_strcasecmp(mimeinfo->subtype, "rfc822")) {
1971 g_warning("procmsg_msginfo_new_from_mimeinfo(): unsuitable mimeinfo");
1978 if (fp && procmime_write_mimeinfo(mimeinfo, fp) >= 0) {
1981 tmp_msginfo = procheader_parse_file(
1988 if (tmp_msginfo != NULL) {
1989 tmp_msginfo->folder = src_msginfo->folder;
1990 tmp_msginfo->plaintext_file = g_strdup(tmpfile);
1992 g_warning("procmsg_msginfo_new_from_mimeinfo(): Can't generate new msginfo");