2 * Sylpheed -- a GTK+ based, lightweight, and fast e-mail client
3 * Copyright (C) 1999-2006 Hiroyuki Yamamoto and the Sylpheed-Claws team
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation; either version 2 of the License, or
8 * (at your option) any later version.
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
15 * You should have received a copy of the GNU General Public License
16 * along with this program; if not, write to the Free Software
17 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
23 #include <glib/gi18n.h>
31 #include "procheader.h"
32 #include "send_message.h"
34 #include "statusbar.h"
35 #include "prefs_filtering.h"
36 #include "filtering.h"
38 #include "prefs_common.h"
40 #include "alertpanel.h"
44 #include "partial_download.h"
45 #include "mainwindow.h"
46 #include "summaryview.h"
48 static gint procmsg_send_message_queue_full(const gchar *file, gboolean keep_session);
56 Q_MAIL_ACCOUNT_ID = 4,
57 Q_NEWS_ACCOUNT_ID = 5,
58 Q_SAVE_COPY_FOLDER = 6,
59 Q_REPLY_MESSAGE_ID = 7,
66 GHashTable *procmsg_msg_hash_table_create(GSList *mlist)
68 GHashTable *msg_table;
70 if (mlist == NULL) return NULL;
72 msg_table = g_hash_table_new(NULL, g_direct_equal);
73 procmsg_msg_hash_table_append(msg_table, mlist);
78 void procmsg_msg_hash_table_append(GHashTable *msg_table, GSList *mlist)
83 if (msg_table == NULL || mlist == NULL) return;
85 for (cur = mlist; cur != NULL; cur = cur->next) {
86 msginfo = (MsgInfo *)cur->data;
88 g_hash_table_insert(msg_table,
89 GUINT_TO_POINTER(msginfo->msgnum),
94 GHashTable *procmsg_to_folder_hash_table_create(GSList *mlist)
96 GHashTable *msg_table;
100 if (mlist == NULL) return NULL;
102 msg_table = g_hash_table_new(NULL, g_direct_equal);
104 for (cur = mlist; cur != NULL; cur = cur->next) {
105 msginfo = (MsgInfo *)cur->data;
106 g_hash_table_insert(msg_table, msginfo->to_folder, msginfo);
112 gint procmsg_get_last_num_in_msg_list(GSList *mlist)
118 for (cur = mlist; cur != NULL; cur = cur->next) {
119 msginfo = (MsgInfo *)cur->data;
120 if (msginfo && msginfo->msgnum > last)
121 last = msginfo->msgnum;
127 void procmsg_msg_list_free(GSList *mlist)
132 for (cur = mlist; cur != NULL; cur = cur->next) {
133 msginfo = (MsgInfo *)cur->data;
134 procmsg_msginfo_free(msginfo);
148 /* CLAWS subject threading:
150 in the first round it inserts subject lines in a
151 relation (subject <-> node)
153 the second round finishes the threads by attaching
154 matching subject lines to the one found in the
155 relation. will use the oldest node with the same
156 subject that is not more then thread_by_subject_max_age
157 days old (see subject_relation_lookup)
160 static void subject_relation_insert(GRelation *relation, GNode *node)
165 g_return_if_fail(relation != NULL);
166 g_return_if_fail(node != NULL);
167 msginfo = (MsgInfo *) node->data;
168 g_return_if_fail(msginfo != NULL);
170 subject = msginfo->subject;
173 subject += subject_get_prefix_length(subject);
175 g_relation_insert(relation, subject, node);
178 static GNode *subject_relation_lookup(GRelation *relation, MsgInfo *msginfo)
185 g_return_val_if_fail(relation != NULL, NULL);
187 subject = msginfo->subject;
190 prefix_length = subject_get_prefix_length(subject);
191 if (prefix_length <= 0)
193 subject += prefix_length;
195 tuples = g_relation_select(relation, subject, 0);
199 if (tuples->len > 0) {
201 GNode *relation_node;
202 MsgInfo *relation_msginfo = NULL, *best_msginfo = NULL;
205 /* check all nodes with the same subject to find the best parent */
206 for (i = 0; i < tuples->len; i++) {
207 relation_node = (GNode *) g_tuples_index(tuples, i, 1);
208 relation_msginfo = (MsgInfo *) relation_node->data;
211 /* best node should be the oldest in the found nodes */
212 /* parent node must not be older then msginfo */
213 if ((relation_msginfo->date_t < msginfo->date_t) &&
214 ((best_msginfo == NULL) ||
215 (best_msginfo->date_t > relation_msginfo->date_t)))
218 /* parent node must not be more then thread_by_subject_max_age
219 days older then msginfo */
220 if (abs(difftime(msginfo->date_t, relation_msginfo->date_t)) >
221 prefs_common.thread_by_subject_max_age * 3600 * 24)
224 /* can add new tests for all matching
225 nodes found by subject */
228 node = relation_node;
229 best_msginfo = relation_msginfo;
234 g_tuples_destroy(tuples);
238 /* return the reversed thread tree */
239 GNode *procmsg_get_thread_tree(GSList *mlist)
241 GNode *root, *parent, *node, *next;
242 GHashTable *msgid_table;
243 GRelation *subject_relation;
248 root = g_node_new(NULL);
249 msgid_table = g_hash_table_new(g_str_hash, g_str_equal);
250 subject_relation = g_relation_new(2);
251 g_relation_index(subject_relation, 0, g_str_hash, g_str_equal);
253 for (; mlist != NULL; mlist = mlist->next) {
254 msginfo = (MsgInfo *)mlist->data;
257 if (msginfo->inreplyto) {
258 parent = g_hash_table_lookup(msgid_table, msginfo->inreplyto);
259 if (parent == NULL) {
263 node = g_node_insert_data_before
264 (parent, parent == root ? parent->children : NULL,
266 if ((msgid = msginfo->msgid) && g_hash_table_lookup(msgid_table, msgid) == NULL)
267 g_hash_table_insert(msgid_table, (gchar *)msgid, node);
269 /* CLAWS: add subject to relation (without prefix) */
270 if (prefs_common.thread_by_subject) {
271 subject_relation_insert(subject_relation, node);
275 /* complete the unfinished threads */
276 for (node = root->children; node != NULL; ) {
278 msginfo = (MsgInfo *)node->data;
281 if (msginfo->inreplyto)
282 parent = g_hash_table_lookup(msgid_table, msginfo->inreplyto);
284 /* try looking for the indirect parent */
285 if (!parent && msginfo->references) {
286 for (reflist = msginfo->references;
287 reflist != NULL; reflist = reflist->next)
288 if ((parent = g_hash_table_lookup
289 (msgid_table, reflist->data)) != NULL)
293 /* node should not be the parent, and node should not
294 be an ancestor of parent (circular reference) */
295 if (parent && parent != node &&
296 !g_node_is_ancestor(node, parent)) {
299 (parent, parent->children, node);
305 if (prefs_common.thread_by_subject) {
306 for (node = root->children; node && node != NULL;) {
308 msginfo = (MsgInfo *) node->data;
310 parent = subject_relation_lookup(subject_relation, msginfo);
312 /* the node may already be threaded by IN-REPLY-TO, so go up
314 find the parent node */
315 if (parent != NULL) {
316 if (g_node_is_ancestor(node, parent))
324 g_node_append(parent, node);
331 g_relation_destroy(subject_relation);
332 g_hash_table_destroy(msgid_table);
337 gint procmsg_move_messages(GSList *mlist)
339 GSList *cur, *movelist = NULL;
341 FolderItem *dest = NULL;
344 if (!mlist) return 0;
346 folder_item_update_freeze();
348 for (cur = mlist; cur != NULL; cur = cur->next) {
349 msginfo = (MsgInfo *)cur->data;
351 dest = msginfo->to_folder;
352 movelist = g_slist_append(movelist, msginfo);
353 } else if (dest == msginfo->to_folder) {
354 movelist = g_slist_append(movelist, msginfo);
356 folder_item_move_msgs(dest, movelist);
357 g_slist_free(movelist);
359 dest = msginfo->to_folder;
360 movelist = g_slist_append(movelist, msginfo);
362 procmsg_msginfo_set_to_folder(msginfo, NULL);
366 retval = folder_item_move_msgs(dest, movelist);
367 g_slist_free(movelist);
370 folder_item_update_thaw();
374 void procmsg_copy_messages(GSList *mlist)
376 GSList *cur, *copylist = NULL;
378 FolderItem *dest = NULL;
382 folder_item_update_freeze();
384 for (cur = mlist; cur != NULL; cur = cur->next) {
385 msginfo = (MsgInfo *)cur->data;
387 dest = msginfo->to_folder;
388 copylist = g_slist_append(copylist, msginfo);
389 } else if (dest == msginfo->to_folder) {
390 copylist = g_slist_append(copylist, msginfo);
392 folder_item_copy_msgs(dest, copylist);
393 g_slist_free(copylist);
395 dest = msginfo->to_folder;
396 copylist = g_slist_append(copylist, msginfo);
398 procmsg_msginfo_set_to_folder(msginfo, NULL);
402 folder_item_copy_msgs(dest, copylist);
403 g_slist_free(copylist);
406 folder_item_update_thaw();
409 gchar *procmsg_get_message_file_path(MsgInfo *msginfo)
413 g_return_val_if_fail(msginfo != NULL, NULL);
415 if (msginfo->plaintext_file)
416 file = g_strdup(msginfo->plaintext_file);
418 file = folder_item_fetch_msg(msginfo->folder, msginfo->msgnum);
424 gchar *procmsg_get_message_file(MsgInfo *msginfo)
426 gchar *filename = NULL;
428 g_return_val_if_fail(msginfo != NULL, NULL);
430 filename = folder_item_fetch_msg(msginfo->folder, msginfo->msgnum);
432 debug_print("can't fetch message %d\n", msginfo->msgnum);
437 gchar *procmsg_get_message_file_full(MsgInfo *msginfo, gboolean headers, gboolean body)
439 gchar *filename = NULL;
441 g_return_val_if_fail(msginfo != NULL, NULL);
443 filename = folder_item_fetch_msg_full(msginfo->folder, msginfo->msgnum,
446 debug_print("can't fetch message %d\n", msginfo->msgnum);
451 GSList *procmsg_get_message_file_list(GSList *mlist)
453 GSList *file_list = NULL;
455 MsgFileInfo *fileinfo;
458 while (mlist != NULL) {
459 msginfo = (MsgInfo *)mlist->data;
460 file = procmsg_get_message_file(msginfo);
462 procmsg_message_file_list_free(file_list);
465 fileinfo = g_new(MsgFileInfo, 1);
466 fileinfo->msginfo = procmsg_msginfo_new_ref(msginfo);
467 fileinfo->file = file;
468 fileinfo->flags = g_new(MsgFlags, 1);
469 *fileinfo->flags = msginfo->flags;
470 file_list = g_slist_prepend(file_list, fileinfo);
474 file_list = g_slist_reverse(file_list);
479 void procmsg_message_file_list_free(MsgInfoList *file_list)
482 MsgFileInfo *fileinfo;
484 for (cur = file_list; cur != NULL; cur = cur->next) {
485 fileinfo = (MsgFileInfo *)cur->data;
486 procmsg_msginfo_free(fileinfo->msginfo);
487 g_free(fileinfo->file);
488 g_free(fileinfo->flags);
492 g_slist_free(file_list);
495 FILE *procmsg_open_message(MsgInfo *msginfo)
500 g_return_val_if_fail(msginfo != NULL, NULL);
502 file = procmsg_get_message_file_path(msginfo);
503 g_return_val_if_fail(file != NULL, NULL);
505 if (!is_file_exist(file)) {
507 file = procmsg_get_message_file(msginfo);
512 if ((fp = g_fopen(file, "rb")) == NULL) {
513 FILE_OP_ERROR(file, "fopen");
520 if (MSG_IS_QUEUED(msginfo->flags) || MSG_IS_DRAFT(msginfo->flags)) {
523 while (fgets(buf, sizeof(buf), fp) != NULL)
524 if (buf[0] == '\r' || buf[0] == '\n') break;
530 gboolean procmsg_msg_exist(MsgInfo *msginfo)
535 if (!msginfo) return FALSE;
537 path = folder_item_get_path(msginfo->folder);
539 ret = !folder_item_is_msg_changed(msginfo->folder, msginfo);
545 void procmsg_get_filter_keyword(MsgInfo *msginfo, gchar **header, gchar **key,
546 PrefsFilterType type)
548 static HeaderEntry hentry[] = {{"X-BeenThere:", NULL, TRUE},
549 {"X-ML-Name:", NULL, TRUE},
550 {"X-List:", NULL, TRUE},
551 {"X-Mailing-list:", NULL, TRUE},
552 {"List-Id:", NULL, TRUE},
553 {"X-Sequence:", NULL, TRUE},
554 {"Sender:", NULL, TRUE},
555 {"List-Post:", NULL, TRUE},
556 {NULL, NULL, FALSE}};
562 H_X_MAILING_LIST = 3,
571 g_return_if_fail(msginfo != NULL);
572 g_return_if_fail(header != NULL);
573 g_return_if_fail(key != NULL);
582 if ((fp = procmsg_open_message(msginfo)) == NULL)
584 procheader_get_header_fields(fp, hentry);
587 #define SET_FILTER_KEY(hstr, idx) \
589 *header = g_strdup(hstr); \
590 *key = hentry[idx].body; \
591 hentry[idx].body = NULL; \
594 if (hentry[H_X_BEENTHERE].body != NULL) {
595 SET_FILTER_KEY("header \"X-BeenThere\"", H_X_BEENTHERE);
596 } else if (hentry[H_X_ML_NAME].body != NULL) {
597 SET_FILTER_KEY("header \"X-ML-Name\"", H_X_ML_NAME);
598 } else if (hentry[H_X_LIST].body != NULL) {
599 SET_FILTER_KEY("header \"X-List\"", H_X_LIST);
600 } else if (hentry[H_X_MAILING_LIST].body != NULL) {
601 SET_FILTER_KEY("header \"X-Mailing-List\"", H_X_MAILING_LIST);
602 } else if (hentry[H_LIST_ID].body != NULL) {
603 SET_FILTER_KEY("header \"List-Id\"", H_LIST_ID);
604 extract_list_id_str(*key);
605 } else if (hentry[H_X_SEQUENCE].body != NULL) {
608 SET_FILTER_KEY("X-Sequence", H_X_SEQUENCE);
611 while (*p != '\0' && !g_ascii_isspace(*p)) p++;
612 while (g_ascii_isspace(*p)) p++;
613 if (g_ascii_isdigit(*p)) {
619 } else if (hentry[H_SENDER].body != NULL) {
620 SET_FILTER_KEY("header \"Sender\"", H_SENDER);
621 } else if (hentry[H_LIST_POST].body != NULL) {
622 SET_FILTER_KEY("header \"Sender\"", H_LIST_POST);
623 } else if (msginfo->to) {
624 *header = g_strdup("to");
625 *key = g_strdup(msginfo->to);
626 } else if (msginfo->subject) {
627 *header = g_strdup("subject");
628 *key = g_strdup(msginfo->subject);
631 #undef SET_FILTER_KEY
633 g_free(hentry[H_X_BEENTHERE].body);
634 hentry[H_X_BEENTHERE].body = NULL;
635 g_free(hentry[H_X_ML_NAME].body);
636 hentry[H_X_ML_NAME].body = NULL;
637 g_free(hentry[H_X_LIST].body);
638 hentry[H_X_LIST].body = NULL;
639 g_free(hentry[H_X_MAILING_LIST].body);
640 hentry[H_X_MAILING_LIST].body = NULL;
641 g_free(hentry[H_LIST_ID].body);
642 hentry[H_LIST_ID].body = NULL;
643 g_free(hentry[H_SENDER].body);
644 hentry[H_SENDER].body = NULL;
645 g_free(hentry[H_LIST_POST].body);
646 hentry[H_LIST_POST].body = NULL;
650 *header = g_strdup("from");
651 *key = g_strdup(msginfo->from);
654 *header = g_strdup("to");
655 *key = g_strdup(msginfo->to);
657 case FILTER_BY_SUBJECT:
658 *header = g_strdup("subject");
659 *key = g_strdup(msginfo->subject);
666 void procmsg_empty_trash(FolderItem *trash)
671 (trash->stype != F_TRASH &&
672 !folder_has_parent_of_type(trash, F_TRASH)))
675 if (trash && trash->total_msgs > 0) {
676 GSList *mlist = folder_item_get_msg_list(trash);
678 for (cur = mlist ; cur != NULL ; cur = cur->next) {
679 MsgInfo * msginfo = (MsgInfo *) cur->data;
680 if (MSG_IS_LOCKED(msginfo->flags))
682 if (msginfo->total_size != 0 &&
683 msginfo->size != (off_t)msginfo->total_size)
684 partial_mark_for_delete(msginfo);
686 procmsg_msginfo_free(msginfo);
689 folder_item_remove_all_msg(trash);
692 if (!trash->node || !trash->node->children)
695 node = trash->node->children;
696 while (node != NULL) {
698 procmsg_empty_trash(FOLDER_ITEM(node->data));
703 void procmsg_empty_all_trash(void)
708 for (cur = folder_get_list(); cur != NULL; cur = cur->next) {
709 Folder *folder = FOLDER(cur->data);
710 trash = folder->trash;
711 procmsg_empty_trash(trash);
712 if (folder->account && folder->account->set_trash_folder &&
713 folder_find_item_from_identifier(folder->account->trash_folder))
715 folder_find_item_from_identifier(folder->account->trash_folder));
719 static PrefsAccount *procmsg_get_account_from_file(const gchar *file)
721 PrefsAccount *mailac = NULL;
725 static HeaderEntry qentry[] = {{"S:", NULL, FALSE},
726 {"SSV:", NULL, FALSE},
728 {"NG:", NULL, FALSE},
729 {"MAID:", NULL, FALSE},
730 {"NAID:", NULL, FALSE},
731 {"SCF:", NULL, FALSE},
732 {"RMID:", NULL, FALSE},
733 {"FMID:", NULL, FALSE},
734 {"X-Sylpheed-Privacy-System:", NULL, FALSE},
735 {"X-Sylpheed-Encrypt:", NULL, FALSE},
736 {"X-Sylpheed-Encrypt-Data:", NULL, FALSE},
737 {NULL, NULL, FALSE}};
739 g_return_val_if_fail(file != NULL, NULL);
741 if ((fp = g_fopen(file, "rb")) == NULL) {
742 FILE_OP_ERROR(file, "fopen");
746 while ((hnum = procheader_get_one_field(buf, sizeof(buf), fp, qentry))
748 gchar *p = buf + strlen(qentry[hnum].name);
750 if (hnum == Q_MAIL_ACCOUNT_ID) {
751 mailac = account_find_from_id(atoi(p));
759 static GSList *procmsg_list_sort_by_account(FolderItem *queue, GSList *list)
761 GSList *result = NULL;
763 PrefsAccount *last_account = NULL;
766 gboolean nothing_to_sort = TRUE;
771 orig = g_slist_copy(list);
773 msg = (MsgInfo *)orig->data;
775 for (cur = orig; cur; cur = cur->next)
776 debug_print("sort before %s\n", ((MsgInfo *)cur->data)->from);
781 nothing_to_sort = TRUE;
785 PrefsAccount *ac = NULL;
786 msg = (MsgInfo *)cur->data;
787 file = folder_item_fetch_msg(queue, msg->msgnum);
788 ac = procmsg_get_account_from_file(file);
791 if (last_account == NULL || (ac != NULL && ac == last_account)) {
792 result = g_slist_append(result, msg);
793 orig = g_slist_remove(orig, msg);
795 nothing_to_sort = FALSE;
801 if (orig || g_slist_length(orig)) {
802 if (!last_account && nothing_to_sort) {
803 /* can't find an account for the rest of the list */
806 result = g_slist_append(result, cur->data);
817 for (cur = result; cur; cur = cur->next)
818 debug_print("sort after %s\n", ((MsgInfo *)cur->data)->from);
825 static gboolean procmsg_is_last_for_account(FolderItem *queue, MsgInfo *msginfo, GSList *elem)
827 gchar *file = folder_item_fetch_msg(queue, msginfo->msgnum);
828 PrefsAccount *ac = procmsg_get_account_from_file(file);
831 for (cur = elem; cur; cur = cur->next) {
832 MsgInfo *cur_msginfo = (MsgInfo *)cur->data;
833 file = folder_item_fetch_msg(queue, cur_msginfo->msgnum);
835 if (cur_msginfo != msginfo && !MSG_IS_LOCKED(cur_msginfo->flags)) {
836 if (procmsg_get_account_from_file(file) == ac) {
848 *\brief Send messages in queue
850 *\param queue Queue folder to process
851 *\param save_msgs Unused
853 *\return Number of messages sent, negative if an error occurred
854 * positive if no error occurred
856 gint procmsg_send_queue(FolderItem *queue, gboolean save_msgs)
858 gint sent = 0, err = 0;
860 GSList *sorted_list = NULL;
864 queue = folder_get_default_queue();
865 g_return_val_if_fail(queue != NULL, -1);
867 folder_item_scan(queue);
868 list = folder_item_get_msg_list(queue);
870 /* sort the list per sender account; this helps reusing the same SMTP server */
871 sorted_list = procmsg_list_sort_by_account(queue, list);
873 for (elem = sorted_list; elem != NULL; elem = elem->next) {
877 msginfo = (MsgInfo *)(elem->data);
878 if (!MSG_IS_LOCKED(msginfo->flags)) {
879 file = folder_item_fetch_msg(queue, msginfo->msgnum);
881 if (procmsg_send_message_queue_full(file,
882 !procmsg_is_last_for_account(queue, msginfo, elem)) < 0) {
883 g_warning("Sending queued message %d failed.\n",
888 * We save in procmsg_send_message_queue because
889 * we need the destination folder from the queue
893 procmsg_save_to_outbox
894 (queue->folder->outbox,
898 folder_item_remove_msg(queue, msginfo->msgnum);
903 /* FIXME: supposedly if only one message is locked, and queue
904 * is being flushed, the following free says something like
905 * "freeing msg ## in folder (nil)". */
906 procmsg_msginfo_free(msginfo);
909 g_slist_free(sorted_list);
910 folder_item_scan(queue);
912 if (queue->node && queue->node->children) {
913 node = queue->node->children;
914 while (node != NULL) {
917 res = procmsg_send_queue(FOLDER_ITEM(node->data), save_msgs);
926 return (err != 0 ? -err : sent);
930 *\brief Determine if a queue folder is empty
932 *\param queue Queue folder to process
934 *\return TRUE if the queue folder is empty, otherwise return FALSE
936 gboolean procmsg_queue_is_empty(FolderItem *queue)
939 gboolean res = FALSE;
941 queue = folder_get_default_queue();
942 g_return_val_if_fail(queue != NULL, TRUE);
944 folder_item_scan(queue);
945 list = folder_item_get_msg_list(queue);
946 res = (list == NULL);
947 procmsg_msg_list_free(list);
951 if (queue->node && queue->node->children) {
952 node = queue->node->children;
953 while (node != NULL) {
955 if (!procmsg_queue_is_empty(FOLDER_ITEM(node->data)))
964 gint procmsg_remove_special_headers(const gchar *in, const gchar *out)
969 if ((fp = g_fopen(in, "rb")) == NULL) {
970 FILE_OP_ERROR(in, "fopen");
973 if ((outfp = g_fopen(out, "wb")) == NULL) {
974 FILE_OP_ERROR(out, "fopen");
978 while (fgets(buf, sizeof(buf), fp) != NULL)
979 if (buf[0] == '\r' || buf[0] == '\n') break;
980 while (fgets(buf, sizeof(buf), fp) != NULL)
987 gchar *procmsg_add_special_headers(const gchar *in, FolderItem *item)
989 gchar *out = get_tmp_file();
991 PrefsAccount *account = NULL;
995 fp = fopen(out, "wb");
1001 if (item && item->prefs && item->prefs->enable_default_account)
1002 account = account_find_from_id(item->prefs->default_account);
1004 if (!account) account = cur_account;
1012 fprintf(fp, "X-Sylpheed-Account-Id:%d\n", account->account_id);
1013 fprintf(fp, "S:%s\n", account->address);
1014 if (item && item->prefs && item->prefs->save_copy_to_folder) {
1015 gchar *folderidentifier;
1017 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(compose->savemsg_checkbtn), prefs_common.savemsg);
1018 folderidentifier = folder_item_get_identifier(item);
1019 fprintf(fp, "SCF:%s\n", folderidentifier);
1020 g_free(folderidentifier);
1021 } else if (account_get_special_folder(account, F_OUTBOX)) {
1022 gchar *folderidentifier = folder_item_get_identifier(account_get_special_folder
1023 (compose->account, F_OUTBOX));
1024 fprintf(fp, "SCF:%s\n", folderidentifier);
1025 g_free(folderidentifier);
1033 gint procmsg_save_to_outbox(FolderItem *outbox, const gchar *file,
1037 MsgInfo *msginfo, *tmp_msginfo;
1038 MsgFlags flag = {0, 0};
1040 debug_print("saving sent message...\n");
1043 outbox = folder_get_default_outbox();
1044 g_return_val_if_fail(outbox != NULL, -1);
1046 /* remove queueing headers */
1048 gchar tmp[MAXPATHLEN + 1];
1050 g_snprintf(tmp, sizeof(tmp), "%s%ctmpmsg.out.%08x",
1051 get_rc_dir(), G_DIR_SEPARATOR, (guint) rand());
1053 if (procmsg_remove_special_headers(file, tmp) !=0)
1056 folder_item_scan(outbox);
1057 if ((num = folder_item_add_msg(outbox, tmp, &flag, TRUE)) < 0) {
1058 g_warning("can't save message\n");
1063 folder_item_scan(outbox);
1064 if ((num = folder_item_add_msg
1065 (outbox, file, &flag, FALSE)) < 0) {
1066 g_warning("can't save message\n");
1071 msginfo = folder_item_get_msginfo(outbox, num); /* refcnt++ */
1072 tmp_msginfo = procmsg_msginfo_get_full_info(msginfo); /* refcnt++ */
1073 if (msginfo != NULL) {
1074 procmsg_msginfo_unset_flags(msginfo, ~0, 0);
1075 procmsg_msginfo_free(msginfo); /* refcnt-- */
1076 /* tmp_msginfo == msginfo */
1077 if (tmp_msginfo && (msginfo->dispositionnotificationto ||
1078 msginfo->returnreceiptto)) {
1079 procmsg_msginfo_set_flags(msginfo, MSG_RETRCPT_SENT, 0);
1080 procmsg_msginfo_free(msginfo); /* refcnt-- */
1087 void procmsg_print_message(MsgInfo *msginfo, const gchar *cmdline)
1089 static const gchar *def_cmd = "lpr %s";
1090 static guint id = 0;
1096 g_return_if_fail(msginfo);
1098 if (procmime_msginfo_is_encrypted(msginfo))
1099 tmpfp = procmime_get_first_encrypted_text_content(msginfo);
1101 tmpfp = procmime_get_first_text_content(msginfo);
1102 if (tmpfp == NULL) {
1103 g_warning("Can't get text part\n");
1107 prtmp = g_strdup_printf("%s%cprinttmp.%08x",
1108 get_mime_tmp_dir(), G_DIR_SEPARATOR, id++);
1110 if ((prfp = g_fopen(prtmp, "wb")) == NULL) {
1111 FILE_OP_ERROR(prtmp, "fopen");
1117 if (msginfo->date) fprintf(prfp, "Date: %s\n", msginfo->date);
1118 if (msginfo->from) fprintf(prfp, "From: %s\n", msginfo->from);
1119 if (msginfo->to) fprintf(prfp, "To: %s\n", msginfo->to);
1120 if (msginfo->cc) fprintf(prfp, "Cc: %s\n", msginfo->cc);
1121 if (msginfo->newsgroups)
1122 fprintf(prfp, "Newsgroups: %s\n", msginfo->newsgroups);
1123 if (msginfo->subject) fprintf(prfp, "Subject: %s\n", msginfo->subject);
1126 while (fgets(buf, sizeof(buf), tmpfp) != NULL)
1132 if (cmdline && (p = strchr(cmdline, '%')) && *(p + 1) == 's' &&
1133 !strchr(p + 2, '%'))
1134 g_snprintf(buf, sizeof(buf) - 1, cmdline, prtmp);
1137 g_warning("Print command line is invalid: '%s'\n",
1139 g_snprintf(buf, sizeof(buf) - 1, def_cmd, prtmp);
1145 if (buf[strlen(buf) - 1] != '&') strcat(buf, "&");
1149 MsgInfo *procmsg_msginfo_new_ref(MsgInfo *msginfo)
1156 MsgInfo *procmsg_msginfo_new(void)
1158 MsgInfo *newmsginfo;
1160 newmsginfo = g_new0(MsgInfo, 1);
1161 newmsginfo->refcnt = 1;
1166 MsgInfo *procmsg_msginfo_copy(MsgInfo *msginfo)
1168 MsgInfo *newmsginfo;
1171 if (msginfo == NULL) return NULL;
1173 newmsginfo = g_new0(MsgInfo, 1);
1175 newmsginfo->refcnt = 1;
1177 #define MEMBCOPY(mmb) newmsginfo->mmb = msginfo->mmb
1178 #define MEMBDUP(mmb) newmsginfo->mmb = msginfo->mmb ? \
1179 g_strdup(msginfo->mmb) : NULL
1194 MEMBDUP(newsgroups);
1201 MEMBCOPY(to_folder);
1205 MEMBDUP(dispositionnotificationto);
1206 MEMBDUP(returnreceiptto);
1208 refs = msginfo->references;
1209 for (refs = msginfo->references; refs != NULL; refs = refs->next) {
1210 newmsginfo->references = g_slist_prepend
1211 (newmsginfo->references, g_strdup(refs->data));
1213 newmsginfo->references = g_slist_reverse(newmsginfo->references);
1216 MEMBDUP(plaintext_file);
1221 MsgInfo *procmsg_msginfo_get_full_info(MsgInfo *msginfo)
1223 MsgInfo *full_msginfo;
1226 if (msginfo == NULL) return NULL;
1228 file = procmsg_get_message_file_path(msginfo);
1229 if (!file || !is_file_exist(file)) {
1231 file = procmsg_get_message_file(msginfo);
1233 if (!file || !is_file_exist(file)) {
1234 g_warning("procmsg_msginfo_get_full_info(): can't get message file.\n");
1238 full_msginfo = procheader_parse_file(file, msginfo->flags, TRUE, FALSE);
1240 if (!full_msginfo) return NULL;
1242 /* CLAWS: make sure we add the missing members; see:
1243 * procheader.c::procheader_get_headernames() */
1244 if (!msginfo->xface)
1245 msginfo->xface = g_strdup(full_msginfo->xface);
1247 msginfo->face = g_strdup(full_msginfo->face);
1248 if (!msginfo->dispositionnotificationto)
1249 msginfo->dispositionnotificationto =
1250 g_strdup(full_msginfo->dispositionnotificationto);
1251 if (!msginfo->returnreceiptto)
1252 msginfo->returnreceiptto = g_strdup
1253 (full_msginfo->returnreceiptto);
1254 if (!msginfo->partial_recv && full_msginfo->partial_recv)
1255 msginfo->partial_recv = g_strdup
1256 (full_msginfo->partial_recv);
1257 msginfo->total_size = full_msginfo->total_size;
1258 if (!msginfo->account_server && full_msginfo->account_server)
1259 msginfo->account_server = g_strdup
1260 (full_msginfo->account_server);
1261 if (!msginfo->account_login && full_msginfo->account_login)
1262 msginfo->account_login = g_strdup
1263 (full_msginfo->account_login);
1264 msginfo->planned_download = full_msginfo->planned_download;
1265 procmsg_msginfo_free(full_msginfo);
1267 return procmsg_msginfo_new_ref(msginfo);
1270 void procmsg_msginfo_free(MsgInfo *msginfo)
1272 if (msginfo == NULL) return;
1275 if (msginfo->refcnt > 0)
1278 if (msginfo->to_folder) {
1279 msginfo->to_folder->op_count--;
1280 folder_item_update(msginfo->to_folder, F_ITEM_UPDATE_MSGCNT);
1283 g_free(msginfo->fromspace);
1284 g_free(msginfo->returnreceiptto);
1285 g_free(msginfo->dispositionnotificationto);
1286 g_free(msginfo->xface);
1287 g_free(msginfo->face);
1289 g_free(msginfo->fromname);
1291 g_free(msginfo->date);
1292 g_free(msginfo->from);
1293 g_free(msginfo->to);
1294 g_free(msginfo->cc);
1295 g_free(msginfo->newsgroups);
1296 g_free(msginfo->subject);
1297 g_free(msginfo->msgid);
1298 g_free(msginfo->inreplyto);
1299 g_free(msginfo->xref);
1301 g_free(msginfo->partial_recv);
1302 g_free(msginfo->account_server);
1303 g_free(msginfo->account_login);
1305 slist_free_strings(msginfo->references);
1306 g_slist_free(msginfo->references);
1308 g_free(msginfo->plaintext_file);
1313 guint procmsg_msginfo_memusage(MsgInfo *msginfo)
1318 memusage += sizeof(MsgInfo);
1319 if (msginfo->fromname)
1320 memusage += strlen(msginfo->fromname);
1322 memusage += strlen(msginfo->date);
1324 memusage += strlen(msginfo->from);
1326 memusage += strlen(msginfo->to);
1328 memusage += strlen(msginfo->cc);
1329 if (msginfo->newsgroups)
1330 memusage += strlen(msginfo->newsgroups);
1331 if (msginfo->subject)
1332 memusage += strlen(msginfo->subject);
1334 memusage += strlen(msginfo->msgid);
1335 if (msginfo->inreplyto)
1336 memusage += strlen(msginfo->inreplyto);
1338 memusage += strlen(msginfo->xface);
1340 memusage += strlen(msginfo->face);
1341 if (msginfo->dispositionnotificationto)
1342 memusage += strlen(msginfo->dispositionnotificationto);
1343 if (msginfo->returnreceiptto)
1344 memusage += strlen(msginfo->returnreceiptto);
1345 for (refs = msginfo->references; refs; refs=refs->next) {
1346 gchar *r = (gchar *)refs->data;
1347 memusage += r?strlen(r):0;
1349 if (msginfo->fromspace)
1350 memusage += strlen(msginfo->fromspace);
1355 gint procmsg_cmp_msgnum_for_sort(gconstpointer a, gconstpointer b)
1357 const MsgInfo *msginfo1 = a;
1358 const MsgInfo *msginfo2 = b;
1365 return msginfo1->msgnum - msginfo2->msgnum;
1368 static gint procmsg_send_message_queue_full(const gchar *file, gboolean keep_session)
1370 static HeaderEntry qentry[] = {{"S:", NULL, FALSE},
1371 {"SSV:", NULL, FALSE},
1372 {"R:", NULL, FALSE},
1373 {"NG:", NULL, FALSE},
1374 {"MAID:", NULL, FALSE},
1375 {"NAID:", NULL, FALSE},
1376 {"SCF:", NULL, FALSE},
1377 {"RMID:", NULL, FALSE},
1378 {"FMID:", NULL, FALSE},
1379 {"X-Sylpheed-Privacy-System:", NULL, FALSE},
1380 {"X-Sylpheed-Encrypt:", NULL, FALSE},
1381 {"X-Sylpheed-Encrypt-Data:", NULL, FALSE},
1382 {NULL, NULL, FALSE}};
1385 gint mailval = 0, newsval = 0;
1387 gchar *smtpserver = NULL;
1388 GSList *to_list = NULL;
1389 GSList *newsgroup_list = NULL;
1390 gchar *savecopyfolder = NULL;
1391 gchar *replymessageid = NULL;
1392 gchar *fwdmessageid = NULL;
1393 gchar *privacy_system = NULL;
1394 gboolean encrypt = FALSE;
1395 gchar *encrypt_data = NULL;
1396 gchar buf[BUFFSIZE];
1398 PrefsAccount *mailac = NULL, *newsac = NULL;
1399 gboolean save_clear_text = TRUE;
1400 gchar *tmp_enc_file = NULL;
1404 g_return_val_if_fail(file != NULL, -1);
1406 if ((fp = g_fopen(file, "rb")) == NULL) {
1407 FILE_OP_ERROR(file, "fopen");
1411 while ((hnum = procheader_get_one_field(buf, sizeof(buf), fp, qentry))
1413 gchar *p = buf + strlen(qentry[hnum].name);
1421 if (smtpserver == NULL)
1422 smtpserver = g_strdup(p);
1425 to_list = address_list_append(to_list, p);
1428 newsgroup_list = newsgroup_list_append(newsgroup_list, p);
1430 case Q_MAIL_ACCOUNT_ID:
1431 mailac = account_find_from_id(atoi(p));
1433 case Q_NEWS_ACCOUNT_ID:
1434 newsac = account_find_from_id(atoi(p));
1436 case Q_SAVE_COPY_FOLDER:
1437 if (savecopyfolder == NULL)
1438 savecopyfolder = g_strdup(p);
1440 case Q_REPLY_MESSAGE_ID:
1441 if (replymessageid == NULL)
1442 replymessageid = g_strdup(p);
1444 case Q_FWD_MESSAGE_ID:
1445 if (fwdmessageid == NULL)
1446 fwdmessageid = g_strdup(p);
1448 case Q_PRIVACY_SYSTEM:
1449 if (privacy_system == NULL)
1450 privacy_system = g_strdup(p);
1456 case Q_ENCRYPT_DATA:
1457 if (encrypt_data == NULL)
1458 encrypt_data = g_strdup(p);
1462 filepos = ftell(fp);
1467 save_clear_text = (mailac != NULL && mailac->save_encrypted_as_clear_text);
1472 mimeinfo = procmime_scan_queue_file(file);
1473 if (!privacy_encrypt(privacy_system, mimeinfo, encrypt_data)
1474 || (fp = my_tmpfile()) == NULL
1475 || procmime_write_mimeinfo(mimeinfo, fp) < 0) {
1478 procmime_mimeinfo_free_all(mimeinfo);
1481 slist_free_strings(to_list);
1482 g_slist_free(to_list);
1483 slist_free_strings(newsgroup_list);
1484 g_slist_free(newsgroup_list);
1485 g_free(savecopyfolder);
1486 g_free(replymessageid);
1487 g_free(fwdmessageid);
1488 g_free(privacy_system);
1489 g_free(encrypt_data);
1494 if (!save_clear_text) {
1495 gchar *content = NULL;
1496 FILE *tmpfp = get_tmpfile_in_dir(get_mime_tmp_dir(), &tmp_enc_file);
1500 content = file_read_stream_to_str(fp);
1503 str_write_to_file(content, tmp_enc_file);
1506 g_warning("couldn't get tempfile\n");
1510 procmime_mimeinfo_free_all(mimeinfo);
1516 debug_print("Sending message by mail\n");
1518 g_warning("Queued message header is broken.\n");
1520 } else if (mailac && mailac->use_mail_command &&
1521 mailac->mail_command && (* mailac->mail_command)) {
1522 mailval = send_message_local(mailac->mail_command, fp);
1526 mailac = account_find_from_smtp_server(from, smtpserver);
1528 g_warning("Account not found. "
1529 "Using current account...\n");
1530 mailac = cur_account;
1535 mailval = send_message_smtp_full(mailac, to_list, fp, keep_session);
1537 PrefsAccount tmp_ac;
1539 g_warning("Account not found.\n");
1541 memset(&tmp_ac, 0, sizeof(PrefsAccount));
1542 tmp_ac.address = from;
1543 tmp_ac.smtp_server = smtpserver;
1544 tmp_ac.smtpport = SMTP_PORT;
1545 mailval = send_message_smtp(&tmp_ac, to_list, fp);
1550 fseek(fp, filepos, SEEK_SET);
1551 if (newsgroup_list && (mailval == 0)) {
1556 /* write to temporary file */
1557 tmp = g_strdup_printf("%s%ctmp%d", g_get_tmp_dir(),
1558 G_DIR_SEPARATOR, (gint)file);
1559 if ((tmpfp = g_fopen(tmp, "wb")) == NULL) {
1560 FILE_OP_ERROR(tmp, "fopen");
1562 alertpanel_error(_("Could not create temporary file for news sending."));
1564 if (change_file_mode_rw(tmpfp, tmp) < 0) {
1565 FILE_OP_ERROR(tmp, "chmod");
1566 g_warning("can't change file mode\n");
1569 while ((newsval == 0) && fgets(buf, sizeof(buf), fp) != NULL) {
1570 if (fputs(buf, tmpfp) == EOF) {
1571 FILE_OP_ERROR(tmp, "fputs");
1573 alertpanel_error(_("Error when writing temporary file for news sending."));
1579 debug_print("Sending message by news\n");
1581 folder = FOLDER(newsac->folder);
1583 newsval = news_post(folder, tmp);
1585 alertpanel_error(_("Error occurred while posting the message to %s ."),
1586 newsac->nntp_server);
1596 /* save message to outbox */
1597 if (mailval == 0 && newsval == 0 && savecopyfolder) {
1600 debug_print("saving sent message...\n");
1602 outbox = folder_find_item_from_identifier(savecopyfolder);
1604 outbox = folder_get_default_outbox();
1606 if (save_clear_text || tmp_enc_file == NULL) {
1607 procmsg_save_to_outbox(outbox, file, TRUE);
1609 procmsg_save_to_outbox(outbox, tmp_enc_file, FALSE);
1613 if (tmp_enc_file != NULL) {
1614 g_unlink(tmp_enc_file);
1616 tmp_enc_file = NULL;
1619 if (replymessageid != NULL || fwdmessageid != NULL) {
1623 if (replymessageid != NULL)
1624 tokens = g_strsplit(replymessageid, "\x7f", 0);
1626 tokens = g_strsplit(fwdmessageid, "\x7f", 0);
1627 item = folder_find_item_from_identifier(tokens[0]);
1629 /* check if queued message has valid folder and message id */
1630 if (item != NULL && tokens[2] != NULL) {
1633 msginfo = folder_item_get_msginfo(item, atoi(tokens[1]));
1635 /* check if referring message exists and has a message id */
1636 if ((msginfo != NULL) &&
1637 (msginfo->msgid != NULL) &&
1638 (strcmp(msginfo->msgid, tokens[2]) != 0)) {
1639 procmsg_msginfo_free(msginfo);
1643 if (msginfo == NULL) {
1644 msginfo = folder_item_get_msginfo_by_msgid(item, tokens[2]);
1647 if (msginfo != NULL) {
1648 if (replymessageid != NULL) {
1649 procmsg_msginfo_unset_flags(msginfo, MSG_FORWARDED, 0);
1650 procmsg_msginfo_set_flags(msginfo, MSG_REPLIED, 0);
1652 procmsg_msginfo_unset_flags(msginfo, MSG_REPLIED, 0);
1653 procmsg_msginfo_set_flags(msginfo, MSG_FORWARDED, 0);
1655 procmsg_msginfo_free(msginfo);
1663 slist_free_strings(to_list);
1664 g_slist_free(to_list);
1665 slist_free_strings(newsgroup_list);
1666 g_slist_free(newsgroup_list);
1667 g_free(savecopyfolder);
1668 g_free(replymessageid);
1669 g_free(fwdmessageid);
1670 g_free(privacy_system);
1671 g_free(encrypt_data);
1673 return (newsval != 0 ? newsval : mailval);
1676 gint procmsg_send_message_queue(const gchar *file)
1678 return procmsg_send_message_queue_full(file, FALSE);
1681 static void update_folder_msg_counts(FolderItem *item, MsgInfo *msginfo, MsgPermFlags old_flags)
1683 MsgPermFlags new_flags = msginfo->flags.perm_flags;
1686 if (!(old_flags & MSG_NEW) && (new_flags & MSG_NEW)) {
1690 if ((old_flags & MSG_NEW) && !(new_flags & MSG_NEW)) {
1695 if (!(old_flags & MSG_UNREAD) && (new_flags & MSG_UNREAD)) {
1696 item->unread_msgs++;
1697 if (procmsg_msg_has_marked_parent(msginfo))
1698 item->unreadmarked_msgs++;
1701 if ((old_flags & MSG_UNREAD) && !(new_flags & MSG_UNREAD)) {
1702 item->unread_msgs--;
1703 if (procmsg_msg_has_marked_parent(msginfo))
1704 item->unreadmarked_msgs--;
1708 if (!(old_flags & MSG_MARKED) && (new_flags & MSG_MARKED)) {
1709 procmsg_update_unread_children(msginfo, TRUE);
1710 item->marked_msgs++;
1713 if ((old_flags & MSG_MARKED) && !(new_flags & MSG_MARKED)) {
1714 procmsg_update_unread_children(msginfo, FALSE);
1715 item->marked_msgs--;
1719 void procmsg_msginfo_set_flags(MsgInfo *msginfo, MsgPermFlags perm_flags, MsgTmpFlags tmp_flags)
1722 MsgInfoUpdate msginfo_update;
1723 MsgPermFlags perm_flags_new, perm_flags_old;
1724 MsgTmpFlags tmp_flags_old;
1726 g_return_if_fail(msginfo != NULL);
1727 item = msginfo->folder;
1728 g_return_if_fail(item != NULL);
1730 debug_print("Setting flags for message %d in folder %s\n", msginfo->msgnum, item->path);
1732 /* Perm Flags handling */
1733 perm_flags_old = msginfo->flags.perm_flags;
1734 perm_flags_new = msginfo->flags.perm_flags | perm_flags;
1735 if ((perm_flags & MSG_IGNORE_THREAD) || (perm_flags_old & MSG_IGNORE_THREAD)) {
1736 perm_flags_new &= ~(MSG_NEW | MSG_UNREAD);
1739 if (perm_flags_old != perm_flags_new) {
1740 folder_item_change_msg_flags(msginfo->folder, msginfo, perm_flags_new);
1742 update_folder_msg_counts(item, msginfo, perm_flags_old);
1746 /* Tmp flags handling */
1747 tmp_flags_old = msginfo->flags.tmp_flags;
1748 msginfo->flags.tmp_flags |= tmp_flags;
1750 /* update notification */
1751 if ((perm_flags_old != perm_flags_new) || (tmp_flags_old != msginfo->flags.tmp_flags)) {
1752 msginfo_update.msginfo = msginfo;
1753 msginfo_update.flags = MSGINFO_UPDATE_FLAGS;
1754 hooks_invoke(MSGINFO_UPDATE_HOOKLIST, &msginfo_update);
1755 folder_item_update(msginfo->folder, F_ITEM_UPDATE_MSGCNT);
1759 void procmsg_msginfo_unset_flags(MsgInfo *msginfo, MsgPermFlags perm_flags, MsgTmpFlags tmp_flags)
1762 MsgInfoUpdate msginfo_update;
1763 MsgPermFlags perm_flags_new, perm_flags_old;
1764 MsgTmpFlags tmp_flags_old;
1766 g_return_if_fail(msginfo != NULL);
1767 item = msginfo->folder;
1768 g_return_if_fail(item != NULL);
1770 debug_print("Unsetting flags for message %d in folder %s\n", msginfo->msgnum, item->path);
1772 /* Perm Flags handling */
1773 perm_flags_old = msginfo->flags.perm_flags;
1774 perm_flags_new = msginfo->flags.perm_flags & ~perm_flags;
1776 if (perm_flags_old != perm_flags_new) {
1777 folder_item_change_msg_flags(msginfo->folder, msginfo, perm_flags_new);
1779 update_folder_msg_counts(item, msginfo, perm_flags_old);
1781 msginfo_update.msginfo = msginfo;
1782 msginfo_update.flags = MSGINFO_UPDATE_FLAGS;
1783 hooks_invoke(MSGINFO_UPDATE_HOOKLIST, &msginfo_update);
1784 folder_item_update(msginfo->folder, F_ITEM_UPDATE_MSGCNT);
1787 /* Tmp flags hanlding */
1788 tmp_flags_old = msginfo->flags.tmp_flags;
1789 msginfo->flags.tmp_flags &= ~tmp_flags;
1791 /* update notification */
1792 if ((perm_flags_old != perm_flags_new) || (tmp_flags_old != msginfo->flags.tmp_flags)) {
1793 msginfo_update.msginfo = msginfo;
1794 msginfo_update.flags = MSGINFO_UPDATE_FLAGS;
1795 hooks_invoke(MSGINFO_UPDATE_HOOKLIST, &msginfo_update);
1796 folder_item_update(msginfo->folder, F_ITEM_UPDATE_MSGCNT);
1800 void procmsg_msginfo_change_flags(MsgInfo *msginfo,
1801 MsgPermFlags add_perm_flags, MsgTmpFlags add_tmp_flags,
1802 MsgPermFlags rem_perm_flags, MsgTmpFlags rem_tmp_flags)
1805 MsgInfoUpdate msginfo_update;
1806 MsgPermFlags perm_flags_new, perm_flags_old;
1807 MsgTmpFlags tmp_flags_old;
1809 g_return_if_fail(msginfo != NULL);
1810 item = msginfo->folder;
1811 g_return_if_fail(item != NULL);
1813 debug_print("Changing flags for message %d in folder %s\n", msginfo->msgnum, item->path);
1815 /* Perm Flags handling */
1816 perm_flags_old = msginfo->flags.perm_flags;
1817 perm_flags_new = (msginfo->flags.perm_flags & ~rem_perm_flags) | add_perm_flags;
1818 if ((add_perm_flags & MSG_IGNORE_THREAD) || (perm_flags_old & MSG_IGNORE_THREAD)) {
1819 perm_flags_new &= ~(MSG_NEW | MSG_UNREAD);
1822 if (perm_flags_old != perm_flags_new) {
1823 folder_item_change_msg_flags(msginfo->folder, msginfo, perm_flags_new);
1825 update_folder_msg_counts(item, msginfo, perm_flags_old);
1829 /* Tmp flags handling */
1830 tmp_flags_old = msginfo->flags.tmp_flags;
1831 msginfo->flags.tmp_flags &= ~rem_tmp_flags;
1832 msginfo->flags.tmp_flags |= add_tmp_flags;
1834 /* update notification */
1835 if ((perm_flags_old != perm_flags_new) || (tmp_flags_old != msginfo->flags.tmp_flags)) {
1836 msginfo_update.msginfo = msginfo;
1837 msginfo_update.flags = MSGINFO_UPDATE_FLAGS;
1838 hooks_invoke(MSGINFO_UPDATE_HOOKLIST, &msginfo_update);
1839 folder_item_update(msginfo->folder, F_ITEM_UPDATE_MSGCNT);
1844 *\brief check for flags (e.g. mark) in prior msgs of current thread
1846 *\param info Current message
1847 *\param perm_flags Flags to be checked
1848 *\param parentmsgs Hash of prior msgs to avoid loops
1850 *\return gboolean TRUE if perm_flags are found
1852 gboolean procmsg_msg_has_flagged_parent_real(MsgInfo *info,
1853 MsgPermFlags perm_flags, GHashTable *parentmsgs)
1857 g_return_val_if_fail(info != NULL, FALSE);
1859 if (info != NULL && info->folder != NULL && info->inreplyto != NULL) {
1860 tmp = folder_item_get_msginfo_by_msgid(info->folder,
1862 if (tmp && (tmp->flags.perm_flags & perm_flags)) {
1863 procmsg_msginfo_free(tmp);
1865 } else if (tmp != NULL) {
1868 if (g_hash_table_lookup(parentmsgs, info)) {
1869 debug_print("loop detected: %s%c%d\n",
1870 folder_item_get_path(info->folder),
1871 G_DIR_SEPARATOR, info->msgnum);
1874 g_hash_table_insert(parentmsgs, info, "1");
1875 result = procmsg_msg_has_flagged_parent_real(
1876 tmp, perm_flags, parentmsgs);
1878 procmsg_msginfo_free(tmp);
1888 *\brief Callback for cleaning up hash of parentmsgs
1890 gboolean parentmsgs_hash_remove(gpointer key,
1898 *\brief Set up list of parentmsgs
1899 * See procmsg_msg_has_flagged_parent_real()
1901 gboolean procmsg_msg_has_flagged_parent(MsgInfo *info, MsgPermFlags perm_flags)
1904 GHashTable *parentmsgs = g_hash_table_new(NULL, NULL);
1906 result = procmsg_msg_has_flagged_parent_real(info, perm_flags, parentmsgs);
1907 g_hash_table_foreach_remove(parentmsgs, parentmsgs_hash_remove, NULL);
1908 g_hash_table_destroy(parentmsgs);
1913 *\brief Check if msgs prior in thread are marked
1914 * See procmsg_msg_has_flagged_parent_real()
1916 gboolean procmsg_msg_has_marked_parent(MsgInfo *info)
1918 return procmsg_msg_has_flagged_parent(info, MSG_MARKED);
1922 GSList *procmsg_find_children_func(MsgInfo *info,
1923 GSList *children, GSList *all)
1927 g_return_val_if_fail(info!=NULL, children);
1928 if (info->msgid == NULL)
1931 for (cur = all; cur != NULL; cur = g_slist_next(cur)) {
1932 MsgInfo *tmp = (MsgInfo *)cur->data;
1933 if (tmp->inreplyto && !strcmp(tmp->inreplyto, info->msgid)) {
1934 /* Check if message is already in the list */
1935 if ((children == NULL) ||
1936 (g_slist_index(children, tmp) == -1)) {
1937 children = g_slist_prepend(children,
1938 procmsg_msginfo_new_ref(tmp));
1939 children = procmsg_find_children_func(tmp,
1948 GSList *procmsg_find_children (MsgInfo *info)
1953 g_return_val_if_fail(info!=NULL, NULL);
1954 all = folder_item_get_msg_list(info->folder);
1955 children = procmsg_find_children_func(info, NULL, all);
1956 if (children != NULL) {
1957 for (cur = all; cur != NULL; cur = g_slist_next(cur)) {
1958 /* this will not free the used pointers
1959 created with procmsg_msginfo_new_ref */
1960 procmsg_msginfo_free((MsgInfo *)cur->data);
1968 void procmsg_update_unread_children(MsgInfo *info, gboolean newly_marked)
1970 GSList *children = procmsg_find_children(info);
1972 for (cur = children; cur != NULL; cur = g_slist_next(cur)) {
1973 MsgInfo *tmp = (MsgInfo *)cur->data;
1974 if(MSG_IS_UNREAD(tmp->flags) && !MSG_IS_IGNORE_THREAD(tmp->flags)) {
1976 info->folder->unreadmarked_msgs++;
1978 info->folder->unreadmarked_msgs--;
1979 folder_item_update(info->folder, F_ITEM_UPDATE_MSGCNT);
1981 procmsg_msginfo_free(tmp);
1983 g_slist_free(children);
1987 * Set the destination folder for a copy or move operation
1989 * \param msginfo The message which's destination folder is changed
1990 * \param to_folder The destination folder for the operation
1992 void procmsg_msginfo_set_to_folder(MsgInfo *msginfo, FolderItem *to_folder)
1994 if(msginfo->to_folder != NULL) {
1995 msginfo->to_folder->op_count--;
1996 folder_item_update(msginfo->to_folder, F_ITEM_UPDATE_MSGCNT);
1998 msginfo->to_folder = to_folder;
1999 if(to_folder != NULL) {
2000 to_folder->op_count++;
2001 folder_item_update(msginfo->to_folder, F_ITEM_UPDATE_MSGCNT);
2006 * Apply filtering actions to the msginfo
2008 * \param msginfo The MsgInfo describing the message that should be filtered
2009 * \return TRUE if the message was moved and MsgInfo is now invalid,
2012 gboolean procmsg_msginfo_filter(MsgInfo *msginfo)
2014 MailFilteringData mail_filtering_data;
2016 mail_filtering_data.msginfo = msginfo;
2017 if (hooks_invoke(MAIL_FILTERING_HOOKLIST, &mail_filtering_data)) {
2018 hooks_invoke(MAIL_POSTFILTERING_HOOKLIST, msginfo);
2022 /* filter if enabled in prefs or move to inbox if not */
2023 if((filtering_rules != NULL) &&
2024 filter_message_by_msginfo(filtering_rules, msginfo)) {
2025 hooks_invoke(MAIL_POSTFILTERING_HOOKLIST, msginfo);
2029 hooks_invoke(MAIL_POSTFILTERING_HOOKLIST, msginfo);
2033 MsgInfo *procmsg_msginfo_new_from_mimeinfo(MsgInfo *src_msginfo, MimeInfo *mimeinfo)
2035 MsgInfo *tmp_msginfo = NULL;
2036 MsgFlags flags = {0, 0};
2037 gchar *tmpfile = get_tmp_file();
2038 FILE *fp = g_fopen(tmpfile, "wb");
2040 if (!mimeinfo || mimeinfo->type != MIMETYPE_MESSAGE ||
2041 g_ascii_strcasecmp(mimeinfo->subtype, "rfc822")) {
2042 g_warning("procmsg_msginfo_new_from_mimeinfo(): unsuitable mimeinfo");
2049 if (fp && procmime_write_mimeinfo(mimeinfo, fp) >= 0) {
2052 tmp_msginfo = procheader_parse_file(
2059 if (tmp_msginfo != NULL) {
2061 tmp_msginfo->folder = src_msginfo->folder;
2062 tmp_msginfo->plaintext_file = g_strdup(tmpfile);
2064 g_warning("procmsg_msginfo_new_from_mimeinfo(): Can't generate new msginfo");
2072 static GSList *spam_learners = NULL;
2074 void procmsg_register_spam_learner (int (*learn_func)(MsgInfo *info, GSList *list, gboolean spam))
2076 if (!g_slist_find(spam_learners, learn_func))
2077 spam_learners = g_slist_append(spam_learners, learn_func);
2078 if (mainwindow_get_mainwindow()) {
2079 main_window_set_menu_sensitive(mainwindow_get_mainwindow());
2080 summary_set_menu_sensitive(
2081 mainwindow_get_mainwindow()->summaryview);
2082 toolbar_main_set_sensitive(mainwindow_get_mainwindow());
2086 void procmsg_unregister_spam_learner (int (*learn_func)(MsgInfo *info, GSList *list, gboolean spam))
2088 spam_learners = g_slist_remove(spam_learners, learn_func);
2089 if (mainwindow_get_mainwindow()) {
2090 main_window_set_menu_sensitive(mainwindow_get_mainwindow());
2091 summary_set_menu_sensitive(
2092 mainwindow_get_mainwindow()->summaryview);
2093 toolbar_main_set_sensitive(mainwindow_get_mainwindow());
2097 gboolean procmsg_spam_can_learn(void)
2099 return g_slist_length(spam_learners) > 0;
2102 int procmsg_spam_learner_learn (MsgInfo *info, GSList *list, gboolean spam)
2104 GSList *cur = spam_learners;
2106 for (; cur; cur = cur->next) {
2107 int ((*func)(MsgInfo *info, GSList *list, gboolean spam)) = cur->data;
2108 ret |= func(info, list, spam);
2113 static gchar *spam_folder_item = NULL;
2114 void procmsg_spam_set_folder (const char *item_identifier)
2116 if (spam_folder_item)
2117 g_free(spam_folder_item);
2118 if (item_identifier)
2119 spam_folder_item = g_strdup(item_identifier);
2121 spam_folder_item = NULL;
2124 FolderItem *procmsg_spam_get_folder (void)
2126 FolderItem *item = spam_folder_item ? folder_find_item_from_identifier(spam_folder_item) : NULL;
2127 return item ? item : folder_get_default_trash();