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 gchar *procmsg_add_special_headers(const gchar *in, FolderItem *item)
987 gchar *out = get_tmp_file();
989 PrefsAccount *account = NULL;
993 fp = fopen(out, "wb");
999 if (item && item->prefs && item->prefs->enable_default_account)
1000 account = account_find_from_id(item->prefs->default_account);
1002 if (!account) account = cur_account;
1010 fprintf(fp, "X-Sylpheed-Account-Id:%d\n", account->account_id);
1011 fprintf(fp, "S:%s\n", account->address);
1012 if (item && item->prefs && item->prefs->save_copy_to_folder) {
1013 gchar *folderidentifier;
1015 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(compose->savemsg_checkbtn), prefs_common.savemsg);
1016 folderidentifier = folder_item_get_identifier(item);
1017 fprintf(fp, "SCF:%s\n", folderidentifier);
1018 g_free(folderidentifier);
1019 } else if (account_get_special_folder(account, F_OUTBOX)) {
1020 gchar *folderidentifier = folder_item_get_identifier(account_get_special_folder
1021 (compose->account, F_OUTBOX));
1022 fprintf(fp, "SCF:%s\n", folderidentifier);
1023 g_free(folderidentifier);
1031 gint procmsg_save_to_outbox(FolderItem *outbox, const gchar *file,
1035 MsgInfo *msginfo, *tmp_msginfo;
1036 MsgFlags flag = {0, 0};
1038 debug_print("saving sent message...\n");
1041 outbox = folder_get_default_outbox();
1042 g_return_val_if_fail(outbox != NULL, -1);
1044 /* remove queueing headers */
1046 gchar tmp[MAXPATHLEN + 1];
1048 g_snprintf(tmp, sizeof(tmp), "%s%ctmpmsg.out.%08x",
1049 get_rc_dir(), G_DIR_SEPARATOR, (guint) rand());
1051 if (procmsg_remove_special_headers(file, tmp) !=0)
1054 folder_item_scan(outbox);
1055 if ((num = folder_item_add_msg(outbox, tmp, &flag, TRUE)) < 0) {
1056 g_warning("can't save message\n");
1061 folder_item_scan(outbox);
1062 if ((num = folder_item_add_msg
1063 (outbox, file, &flag, FALSE)) < 0) {
1064 g_warning("can't save message\n");
1069 msginfo = folder_item_get_msginfo(outbox, num); /* refcnt++ */
1070 tmp_msginfo = procmsg_msginfo_get_full_info(msginfo); /* refcnt++ */
1071 if (msginfo != NULL) {
1072 procmsg_msginfo_unset_flags(msginfo, ~0, 0);
1073 procmsg_msginfo_free(msginfo); /* refcnt-- */
1074 /* tmp_msginfo == msginfo */
1075 if (tmp_msginfo && (msginfo->dispositionnotificationto ||
1076 msginfo->returnreceiptto)) {
1077 procmsg_msginfo_set_flags(msginfo, MSG_RETRCPT_SENT, 0);
1078 procmsg_msginfo_free(msginfo); /* refcnt-- */
1085 void procmsg_print_message(MsgInfo *msginfo, const gchar *cmdline)
1087 static const gchar *def_cmd = "lpr %s";
1088 static guint id = 0;
1094 g_return_if_fail(msginfo);
1096 if (procmime_msginfo_is_encrypted(msginfo))
1097 tmpfp = procmime_get_first_encrypted_text_content(msginfo);
1099 tmpfp = procmime_get_first_text_content(msginfo);
1100 if (tmpfp == NULL) {
1101 g_warning("Can't get text part\n");
1105 prtmp = g_strdup_printf("%s%cprinttmp.%08x",
1106 get_mime_tmp_dir(), G_DIR_SEPARATOR, id++);
1108 if ((prfp = g_fopen(prtmp, "wb")) == NULL) {
1109 FILE_OP_ERROR(prtmp, "fopen");
1115 if (msginfo->date) fprintf(prfp, "Date: %s\n", msginfo->date);
1116 if (msginfo->from) fprintf(prfp, "From: %s\n", msginfo->from);
1117 if (msginfo->to) fprintf(prfp, "To: %s\n", msginfo->to);
1118 if (msginfo->cc) fprintf(prfp, "Cc: %s\n", msginfo->cc);
1119 if (msginfo->newsgroups)
1120 fprintf(prfp, "Newsgroups: %s\n", msginfo->newsgroups);
1121 if (msginfo->subject) fprintf(prfp, "Subject: %s\n", msginfo->subject);
1124 while (fgets(buf, sizeof(buf), tmpfp) != NULL)
1130 if (cmdline && (p = strchr(cmdline, '%')) && *(p + 1) == 's' &&
1131 !strchr(p + 2, '%'))
1132 g_snprintf(buf, sizeof(buf) - 1, cmdline, prtmp);
1135 g_warning("Print command line is invalid: '%s'\n",
1137 g_snprintf(buf, sizeof(buf) - 1, def_cmd, prtmp);
1143 if (buf[strlen(buf) - 1] != '&') strcat(buf, "&");
1147 MsgInfo *procmsg_msginfo_new_ref(MsgInfo *msginfo)
1154 MsgInfo *procmsg_msginfo_new(void)
1156 MsgInfo *newmsginfo;
1158 newmsginfo = g_new0(MsgInfo, 1);
1159 newmsginfo->refcnt = 1;
1164 MsgInfo *procmsg_msginfo_copy(MsgInfo *msginfo)
1166 MsgInfo *newmsginfo;
1169 if (msginfo == NULL) return NULL;
1171 newmsginfo = g_new0(MsgInfo, 1);
1173 newmsginfo->refcnt = 1;
1175 #define MEMBCOPY(mmb) newmsginfo->mmb = msginfo->mmb
1176 #define MEMBDUP(mmb) newmsginfo->mmb = msginfo->mmb ? \
1177 g_strdup(msginfo->mmb) : NULL
1192 MEMBDUP(newsgroups);
1199 MEMBCOPY(to_folder);
1203 MEMBDUP(dispositionnotificationto);
1204 MEMBDUP(returnreceiptto);
1206 refs = msginfo->references;
1207 for (refs = msginfo->references; refs != NULL; refs = refs->next) {
1208 newmsginfo->references = g_slist_prepend
1209 (newmsginfo->references, g_strdup(refs->data));
1211 newmsginfo->references = g_slist_reverse(newmsginfo->references);
1214 MEMBDUP(plaintext_file);
1219 MsgInfo *procmsg_msginfo_get_full_info(MsgInfo *msginfo)
1221 MsgInfo *full_msginfo;
1224 if (msginfo == NULL) return NULL;
1226 file = procmsg_get_message_file_path(msginfo);
1227 if (!file || !is_file_exist(file)) {
1229 file = procmsg_get_message_file(msginfo);
1231 if (!file || !is_file_exist(file)) {
1232 g_warning("procmsg_msginfo_get_full_info(): can't get message file.\n");
1236 full_msginfo = procheader_parse_file(file, msginfo->flags, TRUE, FALSE);
1238 if (!full_msginfo) return NULL;
1240 /* CLAWS: make sure we add the missing members; see:
1241 * procheader.c::procheader_get_headernames() */
1242 if (!msginfo->xface)
1243 msginfo->xface = g_strdup(full_msginfo->xface);
1245 msginfo->face = g_strdup(full_msginfo->face);
1246 if (!msginfo->dispositionnotificationto)
1247 msginfo->dispositionnotificationto =
1248 g_strdup(full_msginfo->dispositionnotificationto);
1249 if (!msginfo->returnreceiptto)
1250 msginfo->returnreceiptto = g_strdup
1251 (full_msginfo->returnreceiptto);
1252 if (!msginfo->partial_recv && full_msginfo->partial_recv)
1253 msginfo->partial_recv = g_strdup
1254 (full_msginfo->partial_recv);
1255 msginfo->total_size = full_msginfo->total_size;
1256 if (!msginfo->account_server && full_msginfo->account_server)
1257 msginfo->account_server = g_strdup
1258 (full_msginfo->account_server);
1259 if (!msginfo->account_login && full_msginfo->account_login)
1260 msginfo->account_login = g_strdup
1261 (full_msginfo->account_login);
1262 msginfo->planned_download = full_msginfo->planned_download;
1263 procmsg_msginfo_free(full_msginfo);
1265 return procmsg_msginfo_new_ref(msginfo);
1268 void procmsg_msginfo_free(MsgInfo *msginfo)
1270 if (msginfo == NULL) return;
1273 if (msginfo->refcnt > 0)
1276 if (msginfo->to_folder) {
1277 msginfo->to_folder->op_count--;
1278 folder_item_update(msginfo->to_folder, F_ITEM_UPDATE_MSGCNT);
1281 g_free(msginfo->fromspace);
1282 g_free(msginfo->returnreceiptto);
1283 g_free(msginfo->dispositionnotificationto);
1284 g_free(msginfo->xface);
1285 g_free(msginfo->face);
1287 g_free(msginfo->fromname);
1289 g_free(msginfo->date);
1290 g_free(msginfo->from);
1291 g_free(msginfo->to);
1292 g_free(msginfo->cc);
1293 g_free(msginfo->newsgroups);
1294 g_free(msginfo->subject);
1295 g_free(msginfo->msgid);
1296 g_free(msginfo->inreplyto);
1297 g_free(msginfo->xref);
1299 g_free(msginfo->partial_recv);
1300 g_free(msginfo->account_server);
1301 g_free(msginfo->account_login);
1303 slist_free_strings(msginfo->references);
1304 g_slist_free(msginfo->references);
1306 g_free(msginfo->plaintext_file);
1311 guint procmsg_msginfo_memusage(MsgInfo *msginfo)
1316 memusage += sizeof(MsgInfo);
1317 if (msginfo->fromname)
1318 memusage += strlen(msginfo->fromname);
1320 memusage += strlen(msginfo->date);
1322 memusage += strlen(msginfo->from);
1324 memusage += strlen(msginfo->to);
1326 memusage += strlen(msginfo->cc);
1327 if (msginfo->newsgroups)
1328 memusage += strlen(msginfo->newsgroups);
1329 if (msginfo->subject)
1330 memusage += strlen(msginfo->subject);
1332 memusage += strlen(msginfo->msgid);
1333 if (msginfo->inreplyto)
1334 memusage += strlen(msginfo->inreplyto);
1336 memusage += strlen(msginfo->xface);
1338 memusage += strlen(msginfo->face);
1339 if (msginfo->dispositionnotificationto)
1340 memusage += strlen(msginfo->dispositionnotificationto);
1341 if (msginfo->returnreceiptto)
1342 memusage += strlen(msginfo->returnreceiptto);
1343 for (refs = msginfo->references; refs; refs=refs->next) {
1344 gchar *r = (gchar *)refs->data;
1345 memusage += r?strlen(r):0;
1347 if (msginfo->fromspace)
1348 memusage += strlen(msginfo->fromspace);
1353 gint procmsg_cmp_msgnum_for_sort(gconstpointer a, gconstpointer b)
1355 const MsgInfo *msginfo1 = a;
1356 const MsgInfo *msginfo2 = b;
1363 return msginfo1->msgnum - msginfo2->msgnum;
1366 static gint procmsg_send_message_queue_full(const gchar *file, gboolean keep_session)
1368 static HeaderEntry qentry[] = {{"S:", NULL, FALSE},
1369 {"SSV:", NULL, FALSE},
1370 {"R:", NULL, FALSE},
1371 {"NG:", NULL, FALSE},
1372 {"MAID:", NULL, FALSE},
1373 {"NAID:", NULL, FALSE},
1374 {"SCF:", NULL, FALSE},
1375 {"RMID:", NULL, FALSE},
1376 {"FMID:", NULL, FALSE},
1377 {"X-Sylpheed-Privacy-System:", NULL, FALSE},
1378 {"X-Sylpheed-Encrypt:", NULL, FALSE},
1379 {"X-Sylpheed-Encrypt-Data:", NULL, FALSE},
1380 {NULL, NULL, FALSE}};
1383 gint mailval = 0, newsval = 0;
1385 gchar *smtpserver = NULL;
1386 GSList *to_list = NULL;
1387 GSList *newsgroup_list = NULL;
1388 gchar *savecopyfolder = NULL;
1389 gchar *replymessageid = NULL;
1390 gchar *fwdmessageid = NULL;
1391 gchar *privacy_system = NULL;
1392 gboolean encrypt = FALSE;
1393 gchar *encrypt_data = NULL;
1394 gchar buf[BUFFSIZE];
1396 PrefsAccount *mailac = NULL, *newsac = NULL;
1397 gboolean save_clear_text = TRUE;
1398 gchar *tmp_enc_file = NULL;
1402 g_return_val_if_fail(file != NULL, -1);
1404 if ((fp = g_fopen(file, "rb")) == NULL) {
1405 FILE_OP_ERROR(file, "fopen");
1409 while ((hnum = procheader_get_one_field(buf, sizeof(buf), fp, qentry))
1411 gchar *p = buf + strlen(qentry[hnum].name);
1419 if (smtpserver == NULL)
1420 smtpserver = g_strdup(p);
1423 to_list = address_list_append(to_list, p);
1426 newsgroup_list = newsgroup_list_append(newsgroup_list, p);
1428 case Q_MAIL_ACCOUNT_ID:
1429 mailac = account_find_from_id(atoi(p));
1431 case Q_NEWS_ACCOUNT_ID:
1432 newsac = account_find_from_id(atoi(p));
1434 case Q_SAVE_COPY_FOLDER:
1435 if (savecopyfolder == NULL)
1436 savecopyfolder = g_strdup(p);
1438 case Q_REPLY_MESSAGE_ID:
1439 if (replymessageid == NULL)
1440 replymessageid = g_strdup(p);
1442 case Q_FWD_MESSAGE_ID:
1443 if (fwdmessageid == NULL)
1444 fwdmessageid = g_strdup(p);
1446 case Q_PRIVACY_SYSTEM:
1447 if (privacy_system == NULL)
1448 privacy_system = g_strdup(p);
1454 case Q_ENCRYPT_DATA:
1455 if (encrypt_data == NULL)
1456 encrypt_data = g_strdup(p);
1460 filepos = ftell(fp);
1465 save_clear_text = (mailac != NULL && mailac->save_encrypted_as_clear_text);
1470 mimeinfo = procmime_scan_queue_file(file);
1471 if (!privacy_encrypt(privacy_system, mimeinfo, encrypt_data)
1472 || (fp = my_tmpfile()) == NULL
1473 || procmime_write_mimeinfo(mimeinfo, fp) < 0) {
1476 procmime_mimeinfo_free_all(mimeinfo);
1479 slist_free_strings(to_list);
1480 g_slist_free(to_list);
1481 slist_free_strings(newsgroup_list);
1482 g_slist_free(newsgroup_list);
1483 g_free(savecopyfolder);
1484 g_free(replymessageid);
1485 g_free(fwdmessageid);
1486 g_free(privacy_system);
1487 g_free(encrypt_data);
1492 if (!save_clear_text) {
1493 gchar *content = NULL;
1494 FILE *tmpfp = get_tmpfile_in_dir(get_mime_tmp_dir(), &tmp_enc_file);
1498 content = file_read_stream_to_str(fp);
1501 str_write_to_file(content, tmp_enc_file);
1504 g_warning("couldn't get tempfile\n");
1508 procmime_mimeinfo_free_all(mimeinfo);
1514 debug_print("Sending message by mail\n");
1516 g_warning("Queued message header is broken.\n");
1518 } else if (mailac && mailac->use_mail_command &&
1519 mailac->mail_command && (* mailac->mail_command)) {
1520 mailval = send_message_local(mailac->mail_command, fp);
1524 mailac = account_find_from_smtp_server(from, smtpserver);
1526 g_warning("Account not found. "
1527 "Using current account...\n");
1528 mailac = cur_account;
1533 mailval = send_message_smtp_full(mailac, to_list, fp, keep_session);
1535 PrefsAccount tmp_ac;
1537 g_warning("Account not found.\n");
1539 memset(&tmp_ac, 0, sizeof(PrefsAccount));
1540 tmp_ac.address = from;
1541 tmp_ac.smtp_server = smtpserver;
1542 tmp_ac.smtpport = SMTP_PORT;
1543 mailval = send_message_smtp(&tmp_ac, to_list, fp);
1548 fseek(fp, filepos, SEEK_SET);
1549 if (newsgroup_list && (mailval == 0)) {
1554 /* write to temporary file */
1555 tmp = g_strdup_printf("%s%ctmp%d", g_get_tmp_dir(),
1556 G_DIR_SEPARATOR, (gint)file);
1557 if ((tmpfp = g_fopen(tmp, "wb")) == NULL) {
1558 FILE_OP_ERROR(tmp, "fopen");
1560 alertpanel_error(_("Could not create temporary file for news sending."));
1562 if (change_file_mode_rw(tmpfp, tmp) < 0) {
1563 FILE_OP_ERROR(tmp, "chmod");
1564 g_warning("can't change file mode\n");
1567 while ((newsval == 0) && fgets(buf, sizeof(buf), fp) != NULL) {
1568 if (fputs(buf, tmpfp) == EOF) {
1569 FILE_OP_ERROR(tmp, "fputs");
1571 alertpanel_error(_("Error when writing temporary file for news sending."));
1577 debug_print("Sending message by news\n");
1579 folder = FOLDER(newsac->folder);
1581 newsval = news_post(folder, tmp);
1583 alertpanel_error(_("Error occurred while posting the message to %s ."),
1584 newsac->nntp_server);
1594 /* save message to outbox */
1595 if (mailval == 0 && newsval == 0 && savecopyfolder) {
1598 debug_print("saving sent message...\n");
1600 outbox = folder_find_item_from_identifier(savecopyfolder);
1602 outbox = folder_get_default_outbox();
1604 if (save_clear_text || tmp_enc_file == NULL) {
1605 procmsg_save_to_outbox(outbox, file, TRUE);
1607 procmsg_save_to_outbox(outbox, tmp_enc_file, FALSE);
1611 if (tmp_enc_file != NULL) {
1612 g_unlink(tmp_enc_file);
1614 tmp_enc_file = NULL;
1617 if (replymessageid != NULL || fwdmessageid != NULL) {
1621 if (replymessageid != NULL)
1622 tokens = g_strsplit(replymessageid, "\x7f", 0);
1624 tokens = g_strsplit(fwdmessageid, "\x7f", 0);
1625 item = folder_find_item_from_identifier(tokens[0]);
1627 /* check if queued message has valid folder and message id */
1628 if (item != NULL && tokens[2] != NULL) {
1631 msginfo = folder_item_get_msginfo(item, atoi(tokens[1]));
1633 /* check if referring message exists and has a message id */
1634 if ((msginfo != NULL) &&
1635 (msginfo->msgid != NULL) &&
1636 (strcmp(msginfo->msgid, tokens[2]) != 0)) {
1637 procmsg_msginfo_free(msginfo);
1641 if (msginfo == NULL) {
1642 msginfo = folder_item_get_msginfo_by_msgid(item, tokens[2]);
1645 if (msginfo != NULL) {
1646 if (replymessageid != NULL) {
1647 procmsg_msginfo_unset_flags(msginfo, MSG_FORWARDED, 0);
1648 procmsg_msginfo_set_flags(msginfo, MSG_REPLIED, 0);
1650 procmsg_msginfo_unset_flags(msginfo, MSG_REPLIED, 0);
1651 procmsg_msginfo_set_flags(msginfo, MSG_FORWARDED, 0);
1653 procmsg_msginfo_free(msginfo);
1661 slist_free_strings(to_list);
1662 g_slist_free(to_list);
1663 slist_free_strings(newsgroup_list);
1664 g_slist_free(newsgroup_list);
1665 g_free(savecopyfolder);
1666 g_free(replymessageid);
1667 g_free(fwdmessageid);
1668 g_free(privacy_system);
1669 g_free(encrypt_data);
1671 return (newsval != 0 ? newsval : mailval);
1674 gint procmsg_send_message_queue(const gchar *file)
1676 return procmsg_send_message_queue_full(file, FALSE);
1679 static void update_folder_msg_counts(FolderItem *item, MsgInfo *msginfo, MsgPermFlags old_flags)
1681 MsgPermFlags new_flags = msginfo->flags.perm_flags;
1684 if (!(old_flags & MSG_NEW) && (new_flags & MSG_NEW)) {
1688 if ((old_flags & MSG_NEW) && !(new_flags & MSG_NEW)) {
1693 if (!(old_flags & MSG_UNREAD) && (new_flags & MSG_UNREAD)) {
1694 item->unread_msgs++;
1695 if (procmsg_msg_has_marked_parent(msginfo))
1696 item->unreadmarked_msgs++;
1699 if ((old_flags & MSG_UNREAD) && !(new_flags & MSG_UNREAD)) {
1700 item->unread_msgs--;
1701 if (procmsg_msg_has_marked_parent(msginfo))
1702 item->unreadmarked_msgs--;
1706 if (!(old_flags & MSG_MARKED) && (new_flags & MSG_MARKED)) {
1707 procmsg_update_unread_children(msginfo, TRUE);
1708 item->marked_msgs++;
1711 if ((old_flags & MSG_MARKED) && !(new_flags & MSG_MARKED)) {
1712 procmsg_update_unread_children(msginfo, FALSE);
1713 item->marked_msgs--;
1717 void procmsg_msginfo_set_flags(MsgInfo *msginfo, MsgPermFlags perm_flags, MsgTmpFlags tmp_flags)
1720 MsgInfoUpdate msginfo_update;
1721 MsgPermFlags perm_flags_new, perm_flags_old;
1722 MsgTmpFlags tmp_flags_old;
1724 g_return_if_fail(msginfo != NULL);
1725 item = msginfo->folder;
1726 g_return_if_fail(item != NULL);
1728 debug_print("Setting flags for message %d in folder %s\n", msginfo->msgnum, item->path);
1730 /* Perm Flags handling */
1731 perm_flags_old = msginfo->flags.perm_flags;
1732 perm_flags_new = msginfo->flags.perm_flags | perm_flags;
1733 if ((perm_flags & MSG_IGNORE_THREAD) || (perm_flags_old & MSG_IGNORE_THREAD)) {
1734 perm_flags_new &= ~(MSG_NEW | MSG_UNREAD);
1737 if (perm_flags_old != perm_flags_new) {
1738 folder_item_change_msg_flags(msginfo->folder, msginfo, perm_flags_new);
1740 update_folder_msg_counts(item, msginfo, perm_flags_old);
1744 /* Tmp flags handling */
1745 tmp_flags_old = msginfo->flags.tmp_flags;
1746 msginfo->flags.tmp_flags |= tmp_flags;
1748 /* update notification */
1749 if ((perm_flags_old != perm_flags_new) || (tmp_flags_old != msginfo->flags.tmp_flags)) {
1750 msginfo_update.msginfo = msginfo;
1751 msginfo_update.flags = MSGINFO_UPDATE_FLAGS;
1752 hooks_invoke(MSGINFO_UPDATE_HOOKLIST, &msginfo_update);
1753 folder_item_update(msginfo->folder, F_ITEM_UPDATE_MSGCNT);
1757 void procmsg_msginfo_unset_flags(MsgInfo *msginfo, MsgPermFlags perm_flags, MsgTmpFlags tmp_flags)
1760 MsgInfoUpdate msginfo_update;
1761 MsgPermFlags perm_flags_new, perm_flags_old;
1762 MsgTmpFlags tmp_flags_old;
1764 g_return_if_fail(msginfo != NULL);
1765 item = msginfo->folder;
1766 g_return_if_fail(item != NULL);
1768 debug_print("Unsetting flags for message %d in folder %s\n", msginfo->msgnum, item->path);
1770 /* Perm Flags handling */
1771 perm_flags_old = msginfo->flags.perm_flags;
1772 perm_flags_new = msginfo->flags.perm_flags & ~perm_flags;
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);
1779 msginfo_update.msginfo = msginfo;
1780 msginfo_update.flags = MSGINFO_UPDATE_FLAGS;
1781 hooks_invoke(MSGINFO_UPDATE_HOOKLIST, &msginfo_update);
1782 folder_item_update(msginfo->folder, F_ITEM_UPDATE_MSGCNT);
1785 /* Tmp flags hanlding */
1786 tmp_flags_old = msginfo->flags.tmp_flags;
1787 msginfo->flags.tmp_flags &= ~tmp_flags;
1789 /* update notification */
1790 if ((perm_flags_old != perm_flags_new) || (tmp_flags_old != msginfo->flags.tmp_flags)) {
1791 msginfo_update.msginfo = msginfo;
1792 msginfo_update.flags = MSGINFO_UPDATE_FLAGS;
1793 hooks_invoke(MSGINFO_UPDATE_HOOKLIST, &msginfo_update);
1794 folder_item_update(msginfo->folder, F_ITEM_UPDATE_MSGCNT);
1798 void procmsg_msginfo_change_flags(MsgInfo *msginfo,
1799 MsgPermFlags add_perm_flags, MsgTmpFlags add_tmp_flags,
1800 MsgPermFlags rem_perm_flags, MsgTmpFlags rem_tmp_flags)
1803 MsgInfoUpdate msginfo_update;
1804 MsgPermFlags perm_flags_new, perm_flags_old;
1805 MsgTmpFlags tmp_flags_old;
1807 g_return_if_fail(msginfo != NULL);
1808 item = msginfo->folder;
1809 g_return_if_fail(item != NULL);
1811 debug_print("Changing flags for message %d in folder %s\n", msginfo->msgnum, item->path);
1813 /* Perm Flags handling */
1814 perm_flags_old = msginfo->flags.perm_flags;
1815 perm_flags_new = (msginfo->flags.perm_flags & ~rem_perm_flags) | add_perm_flags;
1816 if ((add_perm_flags & MSG_IGNORE_THREAD) || (perm_flags_old & MSG_IGNORE_THREAD)) {
1817 perm_flags_new &= ~(MSG_NEW | MSG_UNREAD);
1820 if (perm_flags_old != perm_flags_new) {
1821 folder_item_change_msg_flags(msginfo->folder, msginfo, perm_flags_new);
1823 update_folder_msg_counts(item, msginfo, perm_flags_old);
1827 /* Tmp flags handling */
1828 tmp_flags_old = msginfo->flags.tmp_flags;
1829 msginfo->flags.tmp_flags &= ~rem_tmp_flags;
1830 msginfo->flags.tmp_flags |= add_tmp_flags;
1832 /* update notification */
1833 if ((perm_flags_old != perm_flags_new) || (tmp_flags_old != msginfo->flags.tmp_flags)) {
1834 msginfo_update.msginfo = msginfo;
1835 msginfo_update.flags = MSGINFO_UPDATE_FLAGS;
1836 hooks_invoke(MSGINFO_UPDATE_HOOKLIST, &msginfo_update);
1837 folder_item_update(msginfo->folder, F_ITEM_UPDATE_MSGCNT);
1842 *\brief check for flags (e.g. mark) in prior msgs of current thread
1844 *\param info Current message
1845 *\param perm_flags Flags to be checked
1846 *\param parentmsgs Hash of prior msgs to avoid loops
1848 *\return gboolean TRUE if perm_flags are found
1850 gboolean procmsg_msg_has_flagged_parent_real(MsgInfo *info,
1851 MsgPermFlags perm_flags, GHashTable *parentmsgs)
1855 g_return_val_if_fail(info != NULL, FALSE);
1857 if (info != NULL && info->folder != NULL && info->inreplyto != NULL) {
1858 tmp = folder_item_get_msginfo_by_msgid(info->folder,
1860 if (tmp && (tmp->flags.perm_flags & perm_flags)) {
1861 procmsg_msginfo_free(tmp);
1863 } else if (tmp != NULL) {
1866 if (g_hash_table_lookup(parentmsgs, info)) {
1867 debug_print("loop detected: %s%c%d\n",
1868 folder_item_get_path(info->folder),
1869 G_DIR_SEPARATOR, info->msgnum);
1872 g_hash_table_insert(parentmsgs, info, "1");
1873 result = procmsg_msg_has_flagged_parent_real(
1874 tmp, perm_flags, parentmsgs);
1876 procmsg_msginfo_free(tmp);
1886 *\brief Callback for cleaning up hash of parentmsgs
1888 gboolean parentmsgs_hash_remove(gpointer key,
1896 *\brief Set up list of parentmsgs
1897 * See procmsg_msg_has_flagged_parent_real()
1899 gboolean procmsg_msg_has_flagged_parent(MsgInfo *info, MsgPermFlags perm_flags)
1902 GHashTable *parentmsgs = g_hash_table_new(NULL, NULL);
1904 result = procmsg_msg_has_flagged_parent_real(info, perm_flags, parentmsgs);
1905 g_hash_table_foreach_remove(parentmsgs, parentmsgs_hash_remove, NULL);
1906 g_hash_table_destroy(parentmsgs);
1911 *\brief Check if msgs prior in thread are marked
1912 * See procmsg_msg_has_flagged_parent_real()
1914 gboolean procmsg_msg_has_marked_parent(MsgInfo *info)
1916 return procmsg_msg_has_flagged_parent(info, MSG_MARKED);
1920 GSList *procmsg_find_children_func(MsgInfo *info,
1921 GSList *children, GSList *all)
1925 g_return_val_if_fail(info!=NULL, children);
1926 if (info->msgid == NULL)
1929 for (cur = all; cur != NULL; cur = g_slist_next(cur)) {
1930 MsgInfo *tmp = (MsgInfo *)cur->data;
1931 if (tmp->inreplyto && !strcmp(tmp->inreplyto, info->msgid)) {
1932 /* Check if message is already in the list */
1933 if ((children == NULL) ||
1934 (g_slist_index(children, tmp) == -1)) {
1935 children = g_slist_prepend(children,
1936 procmsg_msginfo_new_ref(tmp));
1937 children = procmsg_find_children_func(tmp,
1946 GSList *procmsg_find_children (MsgInfo *info)
1951 g_return_val_if_fail(info!=NULL, NULL);
1952 all = folder_item_get_msg_list(info->folder);
1953 children = procmsg_find_children_func(info, NULL, all);
1954 if (children != NULL) {
1955 for (cur = all; cur != NULL; cur = g_slist_next(cur)) {
1956 /* this will not free the used pointers
1957 created with procmsg_msginfo_new_ref */
1958 procmsg_msginfo_free((MsgInfo *)cur->data);
1966 void procmsg_update_unread_children(MsgInfo *info, gboolean newly_marked)
1968 GSList *children = procmsg_find_children(info);
1970 for (cur = children; cur != NULL; cur = g_slist_next(cur)) {
1971 MsgInfo *tmp = (MsgInfo *)cur->data;
1972 if(MSG_IS_UNREAD(tmp->flags) && !MSG_IS_IGNORE_THREAD(tmp->flags)) {
1974 info->folder->unreadmarked_msgs++;
1976 info->folder->unreadmarked_msgs--;
1977 folder_item_update(info->folder, F_ITEM_UPDATE_MSGCNT);
1979 procmsg_msginfo_free(tmp);
1981 g_slist_free(children);
1985 * Set the destination folder for a copy or move operation
1987 * \param msginfo The message which's destination folder is changed
1988 * \param to_folder The destination folder for the operation
1990 void procmsg_msginfo_set_to_folder(MsgInfo *msginfo, FolderItem *to_folder)
1992 if(msginfo->to_folder != NULL) {
1993 msginfo->to_folder->op_count--;
1994 folder_item_update(msginfo->to_folder, F_ITEM_UPDATE_MSGCNT);
1996 msginfo->to_folder = to_folder;
1997 if(to_folder != NULL) {
1998 to_folder->op_count++;
1999 folder_item_update(msginfo->to_folder, F_ITEM_UPDATE_MSGCNT);
2004 * Apply filtering actions to the msginfo
2006 * \param msginfo The MsgInfo describing the message that should be filtered
2007 * \return TRUE if the message was moved and MsgInfo is now invalid,
2010 gboolean procmsg_msginfo_filter(MsgInfo *msginfo)
2012 MailFilteringData mail_filtering_data;
2014 mail_filtering_data.msginfo = msginfo;
2015 if (hooks_invoke(MAIL_FILTERING_HOOKLIST, &mail_filtering_data)) {
2016 hooks_invoke(MAIL_POSTFILTERING_HOOKLIST, msginfo);
2020 /* filter if enabled in prefs or move to inbox if not */
2021 if((filtering_rules != NULL) &&
2022 filter_message_by_msginfo(filtering_rules, msginfo)) {
2023 hooks_invoke(MAIL_POSTFILTERING_HOOKLIST, msginfo);
2027 hooks_invoke(MAIL_POSTFILTERING_HOOKLIST, msginfo);
2031 MsgInfo *procmsg_msginfo_new_from_mimeinfo(MsgInfo *src_msginfo, MimeInfo *mimeinfo)
2033 MsgInfo *tmp_msginfo = NULL;
2034 MsgFlags flags = {0, 0};
2035 gchar *tmpfile = get_tmp_file();
2036 FILE *fp = g_fopen(tmpfile, "wb");
2038 if (!mimeinfo || mimeinfo->type != MIMETYPE_MESSAGE ||
2039 g_ascii_strcasecmp(mimeinfo->subtype, "rfc822")) {
2040 g_warning("procmsg_msginfo_new_from_mimeinfo(): unsuitable mimeinfo");
2047 if (fp && procmime_write_mimeinfo(mimeinfo, fp) >= 0) {
2050 tmp_msginfo = procheader_parse_file(
2057 if (tmp_msginfo != NULL) {
2059 tmp_msginfo->folder = src_msginfo->folder;
2060 tmp_msginfo->plaintext_file = g_strdup(tmpfile);
2062 g_warning("procmsg_msginfo_new_from_mimeinfo(): Can't generate new msginfo");