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 {"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);
1156 MEMBDUP(dispositionnotificationto);
1157 MEMBDUP(returnreceiptto);
1159 refs = msginfo->references;
1160 for (refs = msginfo->references; refs != NULL; refs = refs->next) {
1161 newmsginfo->references = g_slist_prepend
1162 (newmsginfo->references, g_strdup(refs->data));
1164 newmsginfo->references = g_slist_reverse(newmsginfo->references);
1167 MEMBCOPY(threadscore);
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);
1198 if (!msginfo->dispositionnotificationto)
1199 msginfo->dispositionnotificationto =
1200 g_strdup(full_msginfo->dispositionnotificationto);
1201 if (!msginfo->returnreceiptto)
1202 msginfo->returnreceiptto = g_strdup
1203 (full_msginfo->returnreceiptto);
1204 if (!msginfo->partial_recv && full_msginfo->partial_recv)
1205 msginfo->partial_recv = g_strdup
1206 (full_msginfo->partial_recv);
1207 msginfo->total_size = full_msginfo->total_size;
1208 if (!msginfo->account_server && full_msginfo->account_server)
1209 msginfo->account_server = g_strdup
1210 (full_msginfo->account_server);
1211 if (!msginfo->account_login && full_msginfo->account_login)
1212 msginfo->account_login = g_strdup
1213 (full_msginfo->account_login);
1214 msginfo->planned_download = full_msginfo->planned_download;
1215 procmsg_msginfo_free(full_msginfo);
1217 return procmsg_msginfo_new_ref(msginfo);
1220 void procmsg_msginfo_free(MsgInfo *msginfo)
1222 if (msginfo == NULL) return;
1225 if (msginfo->refcnt > 0)
1228 if (msginfo->to_folder) {
1229 msginfo->to_folder->op_count--;
1230 folder_item_update(msginfo->to_folder, F_ITEM_UPDATE_MSGCNT);
1233 g_free(msginfo->fromspace);
1234 g_free(msginfo->returnreceiptto);
1235 g_free(msginfo->dispositionnotificationto);
1236 g_free(msginfo->xface);
1238 g_free(msginfo->fromname);
1240 g_free(msginfo->date);
1241 g_free(msginfo->from);
1242 g_free(msginfo->to);
1243 g_free(msginfo->cc);
1244 g_free(msginfo->newsgroups);
1245 g_free(msginfo->subject);
1246 g_free(msginfo->msgid);
1247 g_free(msginfo->inreplyto);
1248 g_free(msginfo->xref);
1250 g_free(msginfo->partial_recv);
1251 g_free(msginfo->account_server);
1252 g_free(msginfo->account_login);
1254 slist_free_strings(msginfo->references);
1255 g_slist_free(msginfo->references);
1257 g_free(msginfo->plaintext_file);
1262 guint procmsg_msginfo_memusage(MsgInfo *msginfo)
1267 memusage += sizeof(MsgInfo);
1268 if (msginfo->fromname)
1269 memusage += strlen(msginfo->fromname);
1271 memusage += strlen(msginfo->date);
1273 memusage += strlen(msginfo->from);
1275 memusage += strlen(msginfo->to);
1277 memusage += strlen(msginfo->cc);
1278 if (msginfo->newsgroups)
1279 memusage += strlen(msginfo->newsgroups);
1280 if (msginfo->subject)
1281 memusage += strlen(msginfo->subject);
1283 memusage += strlen(msginfo->msgid);
1284 if (msginfo->inreplyto)
1285 memusage += strlen(msginfo->inreplyto);
1287 memusage += strlen(msginfo->xface);
1288 if (msginfo->dispositionnotificationto)
1289 memusage += strlen(msginfo->dispositionnotificationto);
1290 if (msginfo->returnreceiptto)
1291 memusage += strlen(msginfo->returnreceiptto);
1292 for (refs = msginfo->references; refs; refs=refs->next) {
1293 gchar *r = (gchar *)refs->data;
1294 memusage += r?strlen(r):0;
1296 if (msginfo->fromspace)
1297 memusage += strlen(msginfo->fromspace);
1302 gint procmsg_cmp_msgnum_for_sort(gconstpointer a, gconstpointer b)
1304 const MsgInfo *msginfo1 = a;
1305 const MsgInfo *msginfo2 = b;
1312 return msginfo1->msgnum - msginfo2->msgnum;
1315 static gint procmsg_send_message_queue_full(const gchar *file, gboolean keep_session)
1317 static HeaderEntry qentry[] = {{"S:", NULL, FALSE},
1318 {"SSV:", NULL, FALSE},
1319 {"R:", NULL, FALSE},
1320 {"NG:", NULL, FALSE},
1321 {"MAID:", NULL, FALSE},
1322 {"NAID:", NULL, FALSE},
1323 {"SCF:", NULL, FALSE},
1324 {"RMID:", NULL, FALSE},
1325 {"FMID:", NULL, FALSE},
1326 {"X-Sylpheed-Privacy-System:", NULL, FALSE},
1327 {"X-Sylpheed-Encrypt:", NULL, FALSE},
1328 {"X-Sylpheed-Encrypt-Data:", NULL, FALSE},
1329 {NULL, NULL, FALSE}};
1332 gint mailval = 0, newsval = 0;
1334 gchar *smtpserver = NULL;
1335 GSList *to_list = NULL;
1336 GSList *newsgroup_list = NULL;
1337 gchar *savecopyfolder = NULL;
1338 gchar *replymessageid = NULL;
1339 gchar *fwdmessageid = NULL;
1340 gchar *privacy_system = NULL;
1341 gboolean encrypt = FALSE;
1342 gchar *encrypt_data = NULL;
1343 gchar buf[BUFFSIZE];
1345 PrefsAccount *mailac = NULL, *newsac = NULL;
1346 gboolean save_clear_text = TRUE;
1347 gchar *tmp_enc_file = NULL;
1351 g_return_val_if_fail(file != NULL, -1);
1353 if ((fp = g_fopen(file, "rb")) == NULL) {
1354 FILE_OP_ERROR(file, "fopen");
1358 while ((hnum = procheader_get_one_field(buf, sizeof(buf), fp, qentry))
1360 gchar *p = buf + strlen(qentry[hnum].name);
1368 if (smtpserver == NULL)
1369 smtpserver = g_strdup(p);
1372 to_list = address_list_append(to_list, p);
1375 newsgroup_list = newsgroup_list_append(newsgroup_list, p);
1377 case Q_MAIL_ACCOUNT_ID:
1378 mailac = account_find_from_id(atoi(p));
1380 case Q_NEWS_ACCOUNT_ID:
1381 newsac = account_find_from_id(atoi(p));
1383 case Q_SAVE_COPY_FOLDER:
1384 if (savecopyfolder == NULL)
1385 savecopyfolder = g_strdup(p);
1387 case Q_REPLY_MESSAGE_ID:
1388 if (replymessageid == NULL)
1389 replymessageid = g_strdup(p);
1391 case Q_FWD_MESSAGE_ID:
1392 if (fwdmessageid == NULL)
1393 fwdmessageid = g_strdup(p);
1395 case Q_PRIVACY_SYSTEM:
1396 if (privacy_system == NULL)
1397 privacy_system = g_strdup(p);
1403 case Q_ENCRYPT_DATA:
1404 if (encrypt_data == NULL)
1405 encrypt_data = g_strdup(p);
1409 filepos = ftell(fp);
1414 save_clear_text = (mailac != NULL && mailac->save_encrypted_as_clear_text);
1419 mimeinfo = procmime_scan_queue_file(file);
1420 if (!privacy_encrypt(privacy_system, mimeinfo, encrypt_data)
1421 || (fp = my_tmpfile()) == NULL
1422 || procmime_write_mimeinfo(mimeinfo, fp) < 0) {
1425 procmime_mimeinfo_free_all(mimeinfo);
1428 slist_free_strings(to_list);
1429 g_slist_free(to_list);
1430 slist_free_strings(newsgroup_list);
1431 g_slist_free(newsgroup_list);
1432 g_free(savecopyfolder);
1433 g_free(replymessageid);
1434 g_free(fwdmessageid);
1435 g_free(privacy_system);
1436 g_free(encrypt_data);
1441 if (!save_clear_text) {
1442 gchar *content = NULL;
1443 FILE *tmpfp = get_tmpfile_in_dir(get_mime_tmp_dir(), &tmp_enc_file);
1447 content = file_read_stream_to_str(fp);
1450 str_write_to_file(content, tmp_enc_file);
1453 g_warning("couldn't get tempfile\n");
1457 procmime_mimeinfo_free_all(mimeinfo);
1463 debug_print("Sending message by mail\n");
1465 g_warning("Queued message header is broken.\n");
1467 } else if (mailac && mailac->use_mail_command &&
1468 mailac->mail_command && (* mailac->mail_command)) {
1469 mailval = send_message_local(mailac->mail_command, fp);
1473 mailac = account_find_from_smtp_server(from, smtpserver);
1475 g_warning("Account not found. "
1476 "Using current account...\n");
1477 mailac = cur_account;
1482 mailval = send_message_smtp_full(mailac, to_list, fp, keep_session);
1484 PrefsAccount tmp_ac;
1486 g_warning("Account not found.\n");
1488 memset(&tmp_ac, 0, sizeof(PrefsAccount));
1489 tmp_ac.address = from;
1490 tmp_ac.smtp_server = smtpserver;
1491 tmp_ac.smtpport = SMTP_PORT;
1492 mailval = send_message_smtp(&tmp_ac, to_list, fp);
1497 fseek(fp, filepos, SEEK_SET);
1498 if (newsgroup_list && (mailval == 0)) {
1503 /* write to temporary file */
1504 tmp = g_strdup_printf("%s%ctmp%d", g_get_tmp_dir(),
1505 G_DIR_SEPARATOR, (gint)file);
1506 if ((tmpfp = g_fopen(tmp, "wb")) == NULL) {
1507 FILE_OP_ERROR(tmp, "fopen");
1509 alertpanel_error(_("Could not create temporary file for news sending."));
1511 if (change_file_mode_rw(tmpfp, tmp) < 0) {
1512 FILE_OP_ERROR(tmp, "chmod");
1513 g_warning("can't change file mode\n");
1516 while ((newsval == 0) && fgets(buf, sizeof(buf), fp) != NULL) {
1517 if (fputs(buf, tmpfp) == EOF) {
1518 FILE_OP_ERROR(tmp, "fputs");
1520 alertpanel_error(_("Error when writing temporary file for news sending."));
1526 debug_print("Sending message by news\n");
1528 folder = FOLDER(newsac->folder);
1530 newsval = news_post(folder, tmp);
1532 alertpanel_error(_("Error occurred while posting the message to %s ."),
1533 newsac->nntp_server);
1543 /* save message to outbox */
1544 if (mailval == 0 && newsval == 0 && savecopyfolder) {
1547 debug_print("saving sent message...\n");
1549 outbox = folder_find_item_from_identifier(savecopyfolder);
1551 outbox = folder_get_default_outbox();
1553 if (save_clear_text || tmp_enc_file == NULL) {
1554 procmsg_save_to_outbox(outbox, file, TRUE);
1556 procmsg_save_to_outbox(outbox, tmp_enc_file, FALSE);
1560 if (tmp_enc_file != NULL) {
1561 g_unlink(tmp_enc_file);
1563 tmp_enc_file = NULL;
1566 if (replymessageid != NULL || fwdmessageid != NULL) {
1570 if (replymessageid != NULL)
1571 tokens = g_strsplit(replymessageid, "\x7f", 0);
1573 tokens = g_strsplit(fwdmessageid, "\x7f", 0);
1574 item = folder_find_item_from_identifier(tokens[0]);
1576 /* check if queued message has valid folder and message id */
1577 if (item != NULL && tokens[2] != NULL) {
1580 msginfo = folder_item_get_msginfo(item, atoi(tokens[1]));
1582 /* check if referring message exists and has a message id */
1583 if ((msginfo != NULL) &&
1584 (msginfo->msgid != NULL) &&
1585 (strcmp(msginfo->msgid, tokens[2]) != 0)) {
1586 procmsg_msginfo_free(msginfo);
1590 if (msginfo == NULL) {
1591 msginfo = folder_item_get_msginfo_by_msgid(item, tokens[2]);
1594 if (msginfo != NULL) {
1595 if (replymessageid != NULL) {
1596 procmsg_msginfo_unset_flags(msginfo, MSG_FORWARDED, 0);
1597 procmsg_msginfo_set_flags(msginfo, MSG_REPLIED, 0);
1599 procmsg_msginfo_unset_flags(msginfo, MSG_REPLIED, 0);
1600 procmsg_msginfo_set_flags(msginfo, MSG_FORWARDED, 0);
1602 procmsg_msginfo_free(msginfo);
1610 slist_free_strings(to_list);
1611 g_slist_free(to_list);
1612 slist_free_strings(newsgroup_list);
1613 g_slist_free(newsgroup_list);
1614 g_free(savecopyfolder);
1615 g_free(replymessageid);
1616 g_free(fwdmessageid);
1617 g_free(privacy_system);
1618 g_free(encrypt_data);
1620 return (newsval != 0 ? newsval : mailval);
1623 gint procmsg_send_message_queue(const gchar *file)
1625 return procmsg_send_message_queue_full(file, FALSE);
1628 static void update_folder_msg_counts(FolderItem *item, MsgInfo *msginfo, MsgPermFlags old_flags)
1630 MsgPermFlags new_flags = msginfo->flags.perm_flags;
1633 if (!(old_flags & MSG_NEW) && (new_flags & MSG_NEW)) {
1637 if ((old_flags & MSG_NEW) && !(new_flags & MSG_NEW)) {
1642 if (!(old_flags & MSG_UNREAD) && (new_flags & MSG_UNREAD)) {
1643 item->unread_msgs++;
1644 if (procmsg_msg_has_marked_parent(msginfo))
1645 item->unreadmarked_msgs++;
1648 if ((old_flags & MSG_UNREAD) && !(new_flags & MSG_UNREAD)) {
1649 item->unread_msgs--;
1650 if (procmsg_msg_has_marked_parent(msginfo))
1651 item->unreadmarked_msgs--;
1655 if (!(old_flags & MSG_MARKED) && (new_flags & MSG_MARKED)) {
1656 procmsg_update_unread_children(msginfo, TRUE);
1657 item->marked_msgs++;
1660 if ((old_flags & MSG_MARKED) && !(new_flags & MSG_MARKED)) {
1661 procmsg_update_unread_children(msginfo, FALSE);
1662 item->marked_msgs--;
1666 void procmsg_msginfo_set_flags(MsgInfo *msginfo, MsgPermFlags perm_flags, MsgTmpFlags tmp_flags)
1669 MsgInfoUpdate msginfo_update;
1670 MsgPermFlags perm_flags_new, perm_flags_old;
1671 MsgTmpFlags tmp_flags_old;
1673 g_return_if_fail(msginfo != NULL);
1674 item = msginfo->folder;
1675 g_return_if_fail(item != NULL);
1677 debug_print("Setting flags for message %d in folder %s\n", msginfo->msgnum, item->path);
1679 /* Perm Flags handling */
1680 perm_flags_old = msginfo->flags.perm_flags;
1681 perm_flags_new = msginfo->flags.perm_flags | perm_flags;
1682 if ((perm_flags & MSG_IGNORE_THREAD) || (perm_flags_old & MSG_IGNORE_THREAD)) {
1683 perm_flags_new &= ~(MSG_NEW | MSG_UNREAD);
1686 if (perm_flags_old != perm_flags_new) {
1687 folder_item_change_msg_flags(msginfo->folder, msginfo, perm_flags_new);
1689 update_folder_msg_counts(item, msginfo, perm_flags_old);
1693 /* Tmp flags handling */
1694 tmp_flags_old = msginfo->flags.tmp_flags;
1695 msginfo->flags.tmp_flags |= tmp_flags;
1697 /* update notification */
1698 if ((perm_flags_old != perm_flags_new) || (tmp_flags_old != msginfo->flags.tmp_flags)) {
1699 msginfo_update.msginfo = msginfo;
1700 msginfo_update.flags = MSGINFO_UPDATE_FLAGS;
1701 hooks_invoke(MSGINFO_UPDATE_HOOKLIST, &msginfo_update);
1702 folder_item_update(msginfo->folder, F_ITEM_UPDATE_MSGCNT);
1706 void procmsg_msginfo_unset_flags(MsgInfo *msginfo, MsgPermFlags perm_flags, MsgTmpFlags tmp_flags)
1709 MsgInfoUpdate msginfo_update;
1710 MsgPermFlags perm_flags_new, perm_flags_old;
1711 MsgTmpFlags tmp_flags_old;
1713 g_return_if_fail(msginfo != NULL);
1714 item = msginfo->folder;
1715 g_return_if_fail(item != NULL);
1717 debug_print("Unsetting flags for message %d in folder %s\n", msginfo->msgnum, item->path);
1719 /* Perm Flags handling */
1720 perm_flags_old = msginfo->flags.perm_flags;
1721 perm_flags_new = msginfo->flags.perm_flags & ~perm_flags;
1723 if (perm_flags_old != perm_flags_new) {
1724 folder_item_change_msg_flags(msginfo->folder, msginfo, perm_flags_new);
1726 update_folder_msg_counts(item, msginfo, perm_flags_old);
1728 msginfo_update.msginfo = msginfo;
1729 msginfo_update.flags = MSGINFO_UPDATE_FLAGS;
1730 hooks_invoke(MSGINFO_UPDATE_HOOKLIST, &msginfo_update);
1731 folder_item_update(msginfo->folder, F_ITEM_UPDATE_MSGCNT);
1734 /* Tmp flags hanlding */
1735 tmp_flags_old = msginfo->flags.tmp_flags;
1736 msginfo->flags.tmp_flags &= ~tmp_flags;
1738 /* update notification */
1739 if ((perm_flags_old != perm_flags_new) || (tmp_flags_old != msginfo->flags.tmp_flags)) {
1740 msginfo_update.msginfo = msginfo;
1741 msginfo_update.flags = MSGINFO_UPDATE_FLAGS;
1742 hooks_invoke(MSGINFO_UPDATE_HOOKLIST, &msginfo_update);
1743 folder_item_update(msginfo->folder, F_ITEM_UPDATE_MSGCNT);
1747 void procmsg_msginfo_change_flags(MsgInfo *msginfo,
1748 MsgPermFlags add_perm_flags, MsgTmpFlags add_tmp_flags,
1749 MsgPermFlags rem_perm_flags, MsgTmpFlags rem_tmp_flags)
1752 MsgInfoUpdate msginfo_update;
1753 MsgPermFlags perm_flags_new, perm_flags_old;
1754 MsgTmpFlags tmp_flags_old;
1756 g_return_if_fail(msginfo != NULL);
1757 item = msginfo->folder;
1758 g_return_if_fail(item != NULL);
1760 debug_print("Changing flags for message %d in folder %s\n", msginfo->msgnum, item->path);
1762 /* Perm Flags handling */
1763 perm_flags_old = msginfo->flags.perm_flags;
1764 perm_flags_new = (msginfo->flags.perm_flags & ~rem_perm_flags) | add_perm_flags;
1765 if ((add_perm_flags & MSG_IGNORE_THREAD) || (perm_flags_old & MSG_IGNORE_THREAD)) {
1766 perm_flags_new &= ~(MSG_NEW | MSG_UNREAD);
1769 if (perm_flags_old != perm_flags_new) {
1770 folder_item_change_msg_flags(msginfo->folder, msginfo, perm_flags_new);
1772 update_folder_msg_counts(item, msginfo, perm_flags_old);
1776 /* Tmp flags handling */
1777 tmp_flags_old = msginfo->flags.tmp_flags;
1778 msginfo->flags.tmp_flags &= ~rem_tmp_flags;
1779 msginfo->flags.tmp_flags |= add_tmp_flags;
1781 /* update notification */
1782 if ((perm_flags_old != perm_flags_new) || (tmp_flags_old != msginfo->flags.tmp_flags)) {
1783 msginfo_update.msginfo = msginfo;
1784 msginfo_update.flags = MSGINFO_UPDATE_FLAGS;
1785 hooks_invoke(MSGINFO_UPDATE_HOOKLIST, &msginfo_update);
1786 folder_item_update(msginfo->folder, F_ITEM_UPDATE_MSGCNT);
1791 *\brief check for flags (e.g. mark) in prior msgs of current thread
1793 *\param info Current message
1794 *\param perm_flags Flags to be checked
1795 *\param parentmsgs Hash of prior msgs to avoid loops
1797 *\return gboolean TRUE if perm_flags are found
1799 gboolean procmsg_msg_has_flagged_parent_real(MsgInfo *info,
1800 MsgPermFlags perm_flags, GHashTable *parentmsgs)
1804 g_return_val_if_fail(info != NULL, FALSE);
1806 if (info != NULL && info->folder != NULL && info->inreplyto != NULL) {
1807 tmp = folder_item_get_msginfo_by_msgid(info->folder,
1809 if (tmp && (tmp->flags.perm_flags & perm_flags)) {
1810 procmsg_msginfo_free(tmp);
1812 } else if (tmp != NULL) {
1815 if (g_hash_table_lookup(parentmsgs, info)) {
1816 debug_print("loop detected: %s%c%d\n",
1817 folder_item_get_path(info->folder),
1818 G_DIR_SEPARATOR, info->msgnum);
1821 g_hash_table_insert(parentmsgs, info, "1");
1822 result = procmsg_msg_has_flagged_parent_real(
1823 tmp, perm_flags, parentmsgs);
1825 procmsg_msginfo_free(tmp);
1835 *\brief Callback for cleaning up hash of parentmsgs
1837 gboolean parentmsgs_hash_remove(gpointer key,
1845 *\brief Set up list of parentmsgs
1846 * See procmsg_msg_has_flagged_parent_real()
1848 gboolean procmsg_msg_has_flagged_parent(MsgInfo *info, MsgPermFlags perm_flags)
1851 GHashTable *parentmsgs = g_hash_table_new(NULL, NULL);
1853 result = procmsg_msg_has_flagged_parent_real(info, perm_flags, parentmsgs);
1854 g_hash_table_foreach_remove(parentmsgs, parentmsgs_hash_remove, NULL);
1855 g_hash_table_destroy(parentmsgs);
1860 *\brief Check if msgs prior in thread are marked
1861 * See procmsg_msg_has_flagged_parent_real()
1863 gboolean procmsg_msg_has_marked_parent(MsgInfo *info)
1865 return procmsg_msg_has_flagged_parent(info, MSG_MARKED);
1869 GSList *procmsg_find_children_func(MsgInfo *info,
1870 GSList *children, GSList *all)
1874 g_return_val_if_fail(info!=NULL, children);
1875 if (info->msgid == NULL)
1878 for (cur = all; cur != NULL; cur = g_slist_next(cur)) {
1879 MsgInfo *tmp = (MsgInfo *)cur->data;
1880 if (tmp->inreplyto && !strcmp(tmp->inreplyto, info->msgid)) {
1881 /* Check if message is already in the list */
1882 if ((children == NULL) ||
1883 (g_slist_index(children, tmp) == -1)) {
1884 children = g_slist_prepend(children,
1885 procmsg_msginfo_new_ref(tmp));
1886 children = procmsg_find_children_func(tmp,
1895 GSList *procmsg_find_children (MsgInfo *info)
1900 g_return_val_if_fail(info!=NULL, NULL);
1901 all = folder_item_get_msg_list(info->folder);
1902 children = procmsg_find_children_func(info, NULL, all);
1903 if (children != NULL) {
1904 for (cur = all; cur != NULL; cur = g_slist_next(cur)) {
1905 /* this will not free the used pointers
1906 created with procmsg_msginfo_new_ref */
1907 procmsg_msginfo_free((MsgInfo *)cur->data);
1915 void procmsg_update_unread_children(MsgInfo *info, gboolean newly_marked)
1917 GSList *children = procmsg_find_children(info);
1919 for (cur = children; cur != NULL; cur = g_slist_next(cur)) {
1920 MsgInfo *tmp = (MsgInfo *)cur->data;
1921 if(MSG_IS_UNREAD(tmp->flags) && !MSG_IS_IGNORE_THREAD(tmp->flags)) {
1923 info->folder->unreadmarked_msgs++;
1925 info->folder->unreadmarked_msgs--;
1926 folder_item_update(info->folder, F_ITEM_UPDATE_MSGCNT);
1928 procmsg_msginfo_free(tmp);
1930 g_slist_free(children);
1934 * Set the destination folder for a copy or move operation
1936 * \param msginfo The message which's destination folder is changed
1937 * \param to_folder The destination folder for the operation
1939 void procmsg_msginfo_set_to_folder(MsgInfo *msginfo, FolderItem *to_folder)
1941 if(msginfo->to_folder != NULL) {
1942 msginfo->to_folder->op_count--;
1943 folder_item_update(msginfo->to_folder, F_ITEM_UPDATE_MSGCNT);
1945 msginfo->to_folder = to_folder;
1946 if(to_folder != NULL) {
1947 to_folder->op_count++;
1948 folder_item_update(msginfo->to_folder, F_ITEM_UPDATE_MSGCNT);
1953 * Apply filtering actions to the msginfo
1955 * \param msginfo The MsgInfo describing the message that should be filtered
1956 * \return TRUE if the message was moved and MsgInfo is now invalid,
1959 gboolean procmsg_msginfo_filter(MsgInfo *msginfo)
1961 MailFilteringData mail_filtering_data;
1963 mail_filtering_data.msginfo = msginfo;
1964 if (hooks_invoke(MAIL_FILTERING_HOOKLIST, &mail_filtering_data))
1967 /* filter if enabled in prefs or move to inbox if not */
1968 if((filtering_rules != NULL) &&
1969 filter_message_by_msginfo(filtering_rules, msginfo))
1972 hooks_invoke(MAIL_POSTFILTERING_HOOKLIST, msginfo);
1977 MsgInfo *procmsg_msginfo_new_from_mimeinfo(MsgInfo *src_msginfo, MimeInfo *mimeinfo)
1979 MsgInfo *tmp_msginfo = NULL;
1980 MsgFlags flags = {0, 0};
1981 gchar *tmpfile = get_tmp_file();
1982 FILE *fp = g_fopen(tmpfile, "wb");
1984 if (!mimeinfo || mimeinfo->type != MIMETYPE_MESSAGE ||
1985 g_ascii_strcasecmp(mimeinfo->subtype, "rfc822")) {
1986 g_warning("procmsg_msginfo_new_from_mimeinfo(): unsuitable mimeinfo");
1993 if (fp && procmime_write_mimeinfo(mimeinfo, fp) >= 0) {
1996 tmp_msginfo = procheader_parse_file(
2003 if (tmp_msginfo != NULL) {
2004 tmp_msginfo->folder = src_msginfo->folder;
2005 tmp_msginfo->plaintext_file = g_strdup(tmpfile);
2007 g_warning("procmsg_msginfo_new_from_mimeinfo(): Can't generate new msginfo");