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"
51 static gint procmsg_send_message_queue_full(const gchar *file, gboolean keep_session, gchar **errstr,
52 FolderItem *queue, gint msgnum);
60 Q_MAIL_ACCOUNT_ID = 4,
61 Q_NEWS_ACCOUNT_ID = 5,
62 Q_SAVE_COPY_FOLDER = 6,
63 Q_REPLY_MESSAGE_ID = 7,
71 GHashTable *procmsg_msg_hash_table_create(GSList *mlist)
73 GHashTable *msg_table;
75 if (mlist == NULL) return NULL;
77 msg_table = g_hash_table_new(NULL, g_direct_equal);
78 procmsg_msg_hash_table_append(msg_table, mlist);
83 void procmsg_msg_hash_table_append(GHashTable *msg_table, GSList *mlist)
88 if (msg_table == NULL || mlist == NULL) return;
90 for (cur = mlist; cur != NULL; cur = cur->next) {
91 msginfo = (MsgInfo *)cur->data;
93 g_hash_table_insert(msg_table,
94 GUINT_TO_POINTER(msginfo->msgnum),
99 GHashTable *procmsg_to_folder_hash_table_create(GSList *mlist)
101 GHashTable *msg_table;
105 if (mlist == NULL) return NULL;
107 msg_table = g_hash_table_new(NULL, g_direct_equal);
109 for (cur = mlist; cur != NULL; cur = cur->next) {
110 msginfo = (MsgInfo *)cur->data;
111 g_hash_table_insert(msg_table, msginfo->to_folder, msginfo);
117 gint procmsg_get_last_num_in_msg_list(GSList *mlist)
123 for (cur = mlist; cur != NULL; cur = cur->next) {
124 msginfo = (MsgInfo *)cur->data;
125 if (msginfo && msginfo->msgnum > last)
126 last = msginfo->msgnum;
132 void procmsg_msg_list_free(GSList *mlist)
137 for (cur = mlist; cur != NULL; cur = cur->next) {
138 msginfo = (MsgInfo *)cur->data;
139 procmsg_msginfo_free(msginfo);
153 /* CLAWS subject threading:
155 in the first round it inserts subject lines in a
156 relation (subject <-> node)
158 the second round finishes the threads by attaching
159 matching subject lines to the one found in the
160 relation. will use the oldest node with the same
161 subject that is not more then thread_by_subject_max_age
162 days old (see subject_relation_lookup)
165 static void subject_relation_insert(GRelation *relation, GNode *node)
170 g_return_if_fail(relation != NULL);
171 g_return_if_fail(node != NULL);
172 msginfo = (MsgInfo *) node->data;
173 g_return_if_fail(msginfo != NULL);
175 subject = msginfo->subject;
178 subject += subject_get_prefix_length(subject);
180 g_relation_insert(relation, subject, node);
183 static GNode *subject_relation_lookup(GRelation *relation, MsgInfo *msginfo)
190 g_return_val_if_fail(relation != NULL, NULL);
192 subject = msginfo->subject;
195 prefix_length = subject_get_prefix_length(subject);
196 if (prefix_length <= 0)
198 subject += prefix_length;
200 tuples = g_relation_select(relation, subject, 0);
204 if (tuples->len > 0) {
206 GNode *relation_node;
207 MsgInfo *relation_msginfo = NULL, *best_msginfo = NULL;
210 /* check all nodes with the same subject to find the best parent */
211 for (i = 0; i < tuples->len; i++) {
212 relation_node = (GNode *) g_tuples_index(tuples, i, 1);
213 relation_msginfo = (MsgInfo *) relation_node->data;
216 /* best node should be the oldest in the found nodes */
217 /* parent node must not be older then msginfo */
218 if ((relation_msginfo->date_t < msginfo->date_t) &&
219 ((best_msginfo == NULL) ||
220 (best_msginfo->date_t > relation_msginfo->date_t)))
223 /* parent node must not be more then thread_by_subject_max_age
224 days older then msginfo */
225 if (abs(difftime(msginfo->date_t, relation_msginfo->date_t)) >
226 prefs_common.thread_by_subject_max_age * 3600 * 24)
229 /* can add new tests for all matching
230 nodes found by subject */
233 node = relation_node;
234 best_msginfo = relation_msginfo;
239 g_tuples_destroy(tuples);
243 /* return the reversed thread tree */
244 GNode *procmsg_get_thread_tree(GSList *mlist)
246 GNode *root, *parent, *node, *next;
247 GHashTable *msgid_table;
248 GRelation *subject_relation = NULL;
253 root = g_node_new(NULL);
254 msgid_table = g_hash_table_new(g_str_hash, g_str_equal);
256 if (prefs_common.thread_by_subject) {
257 subject_relation = g_relation_new(2);
258 g_relation_index(subject_relation, 0, g_str_hash, g_str_equal);
261 for (; mlist != NULL; mlist = mlist->next) {
262 msginfo = (MsgInfo *)mlist->data;
265 if (msginfo->inreplyto) {
266 parent = g_hash_table_lookup(msgid_table, msginfo->inreplyto);
267 if (parent == NULL) {
271 node = g_node_insert_data_before
272 (parent, parent == root ? parent->children : NULL,
274 if ((msgid = msginfo->msgid) && g_hash_table_lookup(msgid_table, msgid) == NULL)
275 g_hash_table_insert(msgid_table, (gchar *)msgid, node);
277 /* CLAWS: add subject to relation (without prefix) */
278 if (prefs_common.thread_by_subject) {
279 subject_relation_insert(subject_relation, node);
283 /* complete the unfinished threads */
284 for (node = root->children; node != NULL; ) {
286 msginfo = (MsgInfo *)node->data;
289 if (msginfo->inreplyto)
290 parent = g_hash_table_lookup(msgid_table, msginfo->inreplyto);
292 /* try looking for the indirect parent */
293 if (!parent && msginfo->references) {
294 for (reflist = msginfo->references;
295 reflist != NULL; reflist = reflist->next)
296 if ((parent = g_hash_table_lookup
297 (msgid_table, reflist->data)) != NULL)
301 /* node should not be the parent, and node should not
302 be an ancestor of parent (circular reference) */
303 if (parent && parent != node &&
304 !g_node_is_ancestor(node, parent)) {
307 (parent, parent->children, node);
313 if (prefs_common.thread_by_subject) {
314 START_TIMING("thread by subject");
315 for (node = root->children; node && node != NULL;) {
317 msginfo = (MsgInfo *) node->data;
319 parent = subject_relation_lookup(subject_relation, msginfo);
321 /* the node may already be threaded by IN-REPLY-TO, so go up
323 find the parent node */
324 if (parent != NULL) {
325 if (g_node_is_ancestor(node, parent))
333 g_node_append(parent, node);
341 if (prefs_common.thread_by_subject)
342 g_relation_destroy(subject_relation);
344 g_hash_table_destroy(msgid_table);
349 gint procmsg_move_messages(GSList *mlist)
351 GSList *cur, *movelist = NULL;
353 FolderItem *dest = NULL;
355 gboolean finished = TRUE;
356 if (!mlist) return 0;
358 folder_item_update_freeze();
361 for (cur = mlist; cur != NULL; cur = cur->next) {
362 msginfo = (MsgInfo *)cur->data;
363 if (!msginfo->to_folder) {
369 dest = msginfo->to_folder;
370 movelist = g_slist_prepend(movelist, msginfo);
371 } else if (dest == msginfo->to_folder) {
372 movelist = g_slist_prepend(movelist, msginfo);
376 procmsg_msginfo_set_to_folder(msginfo, NULL);
379 movelist = g_slist_reverse(movelist);
380 retval |= folder_item_move_msgs(dest, movelist);
381 g_slist_free(movelist);
384 if (finished == FALSE) {
390 folder_item_update_thaw();
394 void procmsg_copy_messages(GSList *mlist)
396 GSList *cur, *copylist = NULL;
398 FolderItem *dest = NULL;
399 gboolean finished = TRUE;
402 folder_item_update_freeze();
405 for (cur = mlist; cur != NULL; cur = cur->next) {
406 msginfo = (MsgInfo *)cur->data;
407 if (!msginfo->to_folder) {
413 dest = msginfo->to_folder;
414 copylist = g_slist_prepend(copylist, msginfo);
415 } else if (dest == msginfo->to_folder) {
416 copylist = g_slist_prepend(copylist, msginfo);
420 procmsg_msginfo_set_to_folder(msginfo, NULL);
423 copylist = g_slist_reverse(copylist);
424 folder_item_copy_msgs(dest, copylist);
425 g_slist_free(copylist);
428 if (finished == FALSE) {
434 folder_item_update_thaw();
437 gchar *procmsg_get_message_file_path(MsgInfo *msginfo)
441 g_return_val_if_fail(msginfo != NULL, NULL);
443 if (msginfo->plaintext_file)
444 file = g_strdup(msginfo->plaintext_file);
446 file = folder_item_fetch_msg(msginfo->folder, msginfo->msgnum);
452 gchar *procmsg_get_message_file(MsgInfo *msginfo)
454 gchar *filename = NULL;
456 g_return_val_if_fail(msginfo != NULL, NULL);
458 filename = folder_item_fetch_msg(msginfo->folder, msginfo->msgnum);
460 debug_print("can't fetch message %d\n", msginfo->msgnum);
465 gchar *procmsg_get_message_file_full(MsgInfo *msginfo, gboolean headers, gboolean body)
467 gchar *filename = NULL;
469 g_return_val_if_fail(msginfo != NULL, NULL);
471 filename = folder_item_fetch_msg_full(msginfo->folder, msginfo->msgnum,
474 debug_print("can't fetch message %d\n", msginfo->msgnum);
479 GSList *procmsg_get_message_file_list(GSList *mlist)
481 GSList *file_list = NULL;
483 MsgFileInfo *fileinfo;
486 while (mlist != NULL) {
487 msginfo = (MsgInfo *)mlist->data;
488 file = procmsg_get_message_file(msginfo);
490 procmsg_message_file_list_free(file_list);
493 fileinfo = g_new(MsgFileInfo, 1);
494 fileinfo->msginfo = procmsg_msginfo_new_ref(msginfo);
495 fileinfo->file = file;
496 fileinfo->flags = g_new(MsgFlags, 1);
497 *fileinfo->flags = msginfo->flags;
498 file_list = g_slist_prepend(file_list, fileinfo);
502 file_list = g_slist_reverse(file_list);
507 void procmsg_message_file_list_free(MsgInfoList *file_list)
510 MsgFileInfo *fileinfo;
512 for (cur = file_list; cur != NULL; cur = cur->next) {
513 fileinfo = (MsgFileInfo *)cur->data;
514 procmsg_msginfo_free(fileinfo->msginfo);
515 g_free(fileinfo->file);
516 g_free(fileinfo->flags);
520 g_slist_free(file_list);
523 FILE *procmsg_open_message(MsgInfo *msginfo)
528 g_return_val_if_fail(msginfo != NULL, NULL);
530 file = procmsg_get_message_file_path(msginfo);
531 g_return_val_if_fail(file != NULL, NULL);
533 if (!is_file_exist(file)) {
535 file = procmsg_get_message_file(msginfo);
540 if ((fp = g_fopen(file, "rb")) == NULL) {
541 FILE_OP_ERROR(file, "fopen");
548 if (MSG_IS_QUEUED(msginfo->flags) || MSG_IS_DRAFT(msginfo->flags)) {
551 while (fgets(buf, sizeof(buf), fp) != NULL) {
553 if (!strncmp(buf, "X-Sylpheed-End-Special-Headers: 1",
554 strlen("X-Sylpheed-End-Special-Headers:")))
557 if (buf[0] == '\r' || buf[0] == '\n') break;
558 /* from other mailers */
559 if (!strncmp(buf, "Date: ", 6)
560 || !strncmp(buf, "To: ", 4)
561 || !strncmp(buf, "From: ", 6)
562 || !strncmp(buf, "Subject: ", 9)) {
572 gboolean procmsg_msg_exist(MsgInfo *msginfo)
577 if (!msginfo) return FALSE;
579 path = folder_item_get_path(msginfo->folder);
581 ret = !folder_item_is_msg_changed(msginfo->folder, msginfo);
587 void procmsg_get_filter_keyword(MsgInfo *msginfo, gchar **header, gchar **key,
588 PrefsFilterType type)
590 static HeaderEntry hentry[] = {{"X-BeenThere:", NULL, TRUE},
591 {"X-ML-Name:", NULL, TRUE},
592 {"X-List:", NULL, TRUE},
593 {"X-Mailing-list:", NULL, TRUE},
594 {"List-Id:", NULL, TRUE},
595 {"X-Sequence:", NULL, TRUE},
596 {"Sender:", NULL, TRUE},
597 {"List-Post:", NULL, TRUE},
598 {NULL, NULL, FALSE}};
604 H_X_MAILING_LIST = 3,
613 g_return_if_fail(msginfo != NULL);
614 g_return_if_fail(header != NULL);
615 g_return_if_fail(key != NULL);
624 if ((fp = procmsg_open_message(msginfo)) == NULL)
626 procheader_get_header_fields(fp, hentry);
629 #define SET_FILTER_KEY(hstr, idx) \
631 *header = g_strdup(hstr); \
632 *key = hentry[idx].body; \
633 hentry[idx].body = NULL; \
636 if (hentry[H_X_BEENTHERE].body != NULL) {
637 SET_FILTER_KEY("header \"X-BeenThere\"", H_X_BEENTHERE);
638 } else if (hentry[H_X_ML_NAME].body != NULL) {
639 SET_FILTER_KEY("header \"X-ML-Name\"", H_X_ML_NAME);
640 } else if (hentry[H_X_LIST].body != NULL) {
641 SET_FILTER_KEY("header \"X-List\"", H_X_LIST);
642 } else if (hentry[H_X_MAILING_LIST].body != NULL) {
643 SET_FILTER_KEY("header \"X-Mailing-List\"", H_X_MAILING_LIST);
644 } else if (hentry[H_LIST_ID].body != NULL) {
645 SET_FILTER_KEY("header \"List-Id\"", H_LIST_ID);
646 extract_list_id_str(*key);
647 } else if (hentry[H_X_SEQUENCE].body != NULL) {
650 SET_FILTER_KEY("X-Sequence", H_X_SEQUENCE);
653 while (*p != '\0' && !g_ascii_isspace(*p)) p++;
654 while (g_ascii_isspace(*p)) p++;
655 if (g_ascii_isdigit(*p)) {
661 } else if (hentry[H_SENDER].body != NULL) {
662 SET_FILTER_KEY("header \"Sender\"", H_SENDER);
663 } else if (hentry[H_LIST_POST].body != NULL) {
664 SET_FILTER_KEY("header \"List-Post\"", H_LIST_POST);
665 } else if (msginfo->to) {
666 *header = g_strdup("to");
667 *key = g_strdup(msginfo->to);
668 } else if (msginfo->subject) {
669 *header = g_strdup("subject");
670 *key = g_strdup(msginfo->subject);
673 #undef SET_FILTER_KEY
675 g_free(hentry[H_X_BEENTHERE].body);
676 hentry[H_X_BEENTHERE].body = NULL;
677 g_free(hentry[H_X_ML_NAME].body);
678 hentry[H_X_ML_NAME].body = NULL;
679 g_free(hentry[H_X_LIST].body);
680 hentry[H_X_LIST].body = NULL;
681 g_free(hentry[H_X_MAILING_LIST].body);
682 hentry[H_X_MAILING_LIST].body = NULL;
683 g_free(hentry[H_LIST_ID].body);
684 hentry[H_LIST_ID].body = NULL;
685 g_free(hentry[H_SENDER].body);
686 hentry[H_SENDER].body = NULL;
687 g_free(hentry[H_LIST_POST].body);
688 hentry[H_LIST_POST].body = NULL;
692 *header = g_strdup("from");
693 *key = g_strdup(msginfo->from);
696 *header = g_strdup("to");
697 *key = g_strdup(msginfo->to);
699 case FILTER_BY_SUBJECT:
700 *header = g_strdup("subject");
701 *key = g_strdup(msginfo->subject);
708 void procmsg_empty_trash(FolderItem *trash)
713 (trash->stype != F_TRASH &&
714 !folder_has_parent_of_type(trash, F_TRASH)))
717 if (trash && trash->total_msgs > 0) {
718 GSList *mlist = folder_item_get_msg_list(trash);
720 for (cur = mlist ; cur != NULL ; cur = cur->next) {
721 MsgInfo * msginfo = (MsgInfo *) cur->data;
722 if (MSG_IS_LOCKED(msginfo->flags))
724 if (msginfo->total_size != 0 &&
725 msginfo->size != (off_t)msginfo->total_size)
726 partial_mark_for_delete(msginfo);
728 procmsg_msginfo_free(msginfo);
731 folder_item_remove_all_msg(trash);
734 if (!trash->node || !trash->node->children)
737 node = trash->node->children;
738 while (node != NULL) {
740 procmsg_empty_trash(FOLDER_ITEM(node->data));
745 void procmsg_empty_all_trash(void)
750 for (cur = folder_get_list(); cur != NULL; cur = cur->next) {
751 Folder *folder = FOLDER(cur->data);
752 trash = folder->trash;
753 procmsg_empty_trash(trash);
754 if (folder->account && folder->account->set_trash_folder &&
755 folder_find_item_from_identifier(folder->account->trash_folder))
757 folder_find_item_from_identifier(folder->account->trash_folder));
761 static PrefsAccount *procmsg_get_account_from_file(const gchar *file)
763 PrefsAccount *mailac = NULL;
767 static HeaderEntry qentry[] = {{"S:", NULL, FALSE},
768 {"SSV:", NULL, FALSE},
770 {"NG:", NULL, FALSE},
771 {"MAID:", NULL, FALSE},
772 {"NAID:", NULL, FALSE},
773 {"SCF:", NULL, FALSE},
774 {"RMID:", NULL, FALSE},
775 {"FMID:", NULL, FALSE},
776 {"X-Sylpheed-Privacy-System:", NULL, FALSE},
777 {"X-Sylpheed-Encrypt:", NULL, FALSE},
778 {"X-Sylpheed-Encrypt-Data:", NULL, FALSE},
779 {NULL, NULL, FALSE}};
781 g_return_val_if_fail(file != NULL, NULL);
783 if ((fp = g_fopen(file, "rb")) == NULL) {
784 FILE_OP_ERROR(file, "fopen");
788 while ((hnum = procheader_get_one_field(buf, sizeof(buf), fp, qentry))
790 gchar *p = buf + strlen(qentry[hnum].name);
792 if (hnum == Q_MAIL_ACCOUNT_ID) {
793 mailac = account_find_from_id(atoi(p));
801 static GSList *procmsg_list_sort_by_account(FolderItem *queue, GSList *list)
803 GSList *result = NULL;
805 PrefsAccount *last_account = NULL;
808 gboolean nothing_to_sort = TRUE;
813 orig = g_slist_copy(list);
815 msg = (MsgInfo *)orig->data;
817 for (cur = orig; cur; cur = cur->next)
818 debug_print("sort before %s\n", ((MsgInfo *)cur->data)->from);
823 nothing_to_sort = TRUE;
827 PrefsAccount *ac = NULL;
828 msg = (MsgInfo *)cur->data;
829 file = folder_item_fetch_msg(queue, msg->msgnum);
830 ac = procmsg_get_account_from_file(file);
833 if (last_account == NULL || (ac != NULL && ac == last_account)) {
834 result = g_slist_append(result, msg);
835 orig = g_slist_remove(orig, msg);
837 nothing_to_sort = FALSE;
843 if (orig || g_slist_length(orig)) {
844 if (!last_account && nothing_to_sort) {
845 /* can't find an account for the rest of the list */
848 result = g_slist_append(result, cur->data);
859 for (cur = result; cur; cur = cur->next)
860 debug_print("sort after %s\n", ((MsgInfo *)cur->data)->from);
867 static gboolean procmsg_is_last_for_account(FolderItem *queue, MsgInfo *msginfo, GSList *elem)
869 gchar *file = folder_item_fetch_msg(queue, msginfo->msgnum);
870 PrefsAccount *ac = procmsg_get_account_from_file(file);
873 for (cur = elem; cur; cur = cur->next) {
874 MsgInfo *cur_msginfo = (MsgInfo *)cur->data;
875 file = folder_item_fetch_msg(queue, cur_msginfo->msgnum);
877 if (cur_msginfo != msginfo && !MSG_IS_LOCKED(cur_msginfo->flags)) {
878 if (procmsg_get_account_from_file(file) == ac) {
889 static gboolean send_queue_lock = FALSE;
891 *\brief Send messages in queue
893 *\param queue Queue folder to process
894 *\param save_msgs Unused
896 *\return Number of messages sent, negative if an error occurred
897 * positive if no error occurred
899 gint procmsg_send_queue(FolderItem *queue, gboolean save_msgs, gchar **errstr)
901 gint sent = 0, err = 0;
903 GSList *sorted_list = NULL;
906 if (send_queue_lock) {
907 /* Avoid having to translate two similar strings */
908 log_warning("%s\n", _("Already trying to send."));
910 if (*errstr) g_free(*errstr);
911 *errstr = g_strdup_printf(_("Already trying to send."));
913 toolbar_main_set_sensitive(mainwindow_get_mainwindow());
916 send_queue_lock = TRUE;
919 queue = folder_get_default_queue();
922 send_queue_lock = FALSE;
927 toolbar_main_set_sensitive(mainwindow_get_mainwindow());
929 folder_item_scan(queue);
930 list = folder_item_get_msg_list(queue);
932 /* sort the list per sender account; this helps reusing the same SMTP server */
933 sorted_list = procmsg_list_sort_by_account(queue, list);
935 for (elem = sorted_list; elem != NULL; elem = elem->next) {
939 msginfo = (MsgInfo *)(elem->data);
940 if (!MSG_IS_LOCKED(msginfo->flags)) {
941 file = folder_item_fetch_msg(queue, msginfo->msgnum);
943 if (procmsg_send_message_queue_full(file,
944 !procmsg_is_last_for_account(queue, msginfo, elem),
945 errstr, queue, msginfo->msgnum) < 0) {
946 g_warning("Sending queued message %d failed.\n",
951 folder_item_remove_msg(queue, msginfo->msgnum);
956 /* FIXME: supposedly if only one message is locked, and queue
957 * is being flushed, the following free says something like
958 * "freeing msg ## in folder (nil)". */
959 procmsg_msginfo_free(msginfo);
962 g_slist_free(sorted_list);
963 folder_item_scan(queue);
965 if (queue->node && queue->node->children) {
966 node = queue->node->children;
967 while (node != NULL) {
970 res = procmsg_send_queue(FOLDER_ITEM(node->data), save_msgs, errstr);
978 send_queue_lock = FALSE;
980 toolbar_main_set_sensitive(mainwindow_get_mainwindow());
982 return (err != 0 ? -err : sent);
985 gboolean procmsg_is_sending(void)
987 return send_queue_lock;
991 *\brief Determine if a queue folder is empty
993 *\param queue Queue folder to process
995 *\return TRUE if the queue folder is empty, otherwise return FALSE
997 gboolean procmsg_queue_is_empty(FolderItem *queue)
1000 gboolean res = FALSE;
1002 queue = folder_get_default_queue();
1003 g_return_val_if_fail(queue != NULL, TRUE);
1005 folder_item_scan(queue);
1006 list = folder_item_get_msg_list(queue);
1007 res = (list == NULL);
1008 procmsg_msg_list_free(list);
1012 if (queue->node && queue->node->children) {
1013 node = queue->node->children;
1014 while (node != NULL) {
1016 if (!procmsg_queue_is_empty(FOLDER_ITEM(node->data)))
1025 gint procmsg_remove_special_headers(const gchar *in, const gchar *out)
1028 gchar buf[BUFFSIZE];
1030 if ((fp = g_fopen(in, "rb")) == NULL) {
1031 FILE_OP_ERROR(in, "fopen");
1034 if ((outfp = g_fopen(out, "wb")) == NULL) {
1035 FILE_OP_ERROR(out, "fopen");
1039 while (fgets(buf, sizeof(buf), fp) != NULL) {
1041 if (!strncmp(buf, "X-Sylpheed-End-Special-Headers: 1",
1042 strlen("X-Sylpheed-End-Special-Headers:")))
1045 if (buf[0] == '\r' || buf[0] == '\n') break;
1046 /* from other mailers */
1047 if (!strncmp(buf, "Date: ", 6)
1048 || !strncmp(buf, "To: ", 4)
1049 || !strncmp(buf, "From: ", 6)
1050 || !strncmp(buf, "Subject: ", 9)) {
1055 while (fgets(buf, sizeof(buf), fp) != NULL)
1062 gint procmsg_save_to_outbox(FolderItem *outbox, const gchar *file,
1066 MsgInfo *msginfo, *tmp_msginfo;
1067 MsgFlags flag = {0, 0};
1069 debug_print("saving sent message...\n");
1072 outbox = folder_get_default_outbox();
1073 g_return_val_if_fail(outbox != NULL, -1);
1075 /* remove queueing headers */
1077 gchar tmp[MAXPATHLEN + 1];
1079 g_snprintf(tmp, sizeof(tmp), "%s%ctmpmsg.out.%08x",
1080 get_rc_dir(), G_DIR_SEPARATOR, (guint) rand());
1082 if (procmsg_remove_special_headers(file, tmp) !=0)
1085 folder_item_scan(outbox);
1086 if ((num = folder_item_add_msg(outbox, tmp, &flag, TRUE)) < 0) {
1087 g_warning("can't save message\n");
1092 folder_item_scan(outbox);
1093 if ((num = folder_item_add_msg
1094 (outbox, file, &flag, FALSE)) < 0) {
1095 g_warning("can't save message\n");
1099 msginfo = folder_item_get_msginfo(outbox, num); /* refcnt++ */
1100 tmp_msginfo = procmsg_msginfo_get_full_info(msginfo); /* refcnt++ */
1101 if (msginfo != NULL) {
1102 procmsg_msginfo_unset_flags(msginfo, ~0, 0);
1103 procmsg_msginfo_free(msginfo); /* refcnt-- */
1104 /* tmp_msginfo == msginfo */
1105 if (tmp_msginfo && msginfo->extradata &&
1106 (msginfo->extradata->dispositionnotificationto ||
1107 msginfo->extradata->returnreceiptto)) {
1108 procmsg_msginfo_set_flags(msginfo, MSG_RETRCPT_SENT, 0);
1110 procmsg_msginfo_free(tmp_msginfo); /* refcnt-- */
1116 void procmsg_print_message(MsgInfo *msginfo, const gchar *cmdline)
1118 static const gchar *def_cmd = "lpr %s";
1119 static guint id = 0;
1125 g_return_if_fail(msginfo);
1127 if (procmime_msginfo_is_encrypted(msginfo))
1128 tmpfp = procmime_get_first_encrypted_text_content(msginfo);
1130 tmpfp = procmime_get_first_text_content(msginfo);
1131 if (tmpfp == NULL) {
1132 g_warning("Can't get text part\n");
1136 prtmp = g_strdup_printf("%s%cprinttmp.%08x",
1137 get_mime_tmp_dir(), G_DIR_SEPARATOR, id++);
1139 if ((prfp = g_fopen(prtmp, "wb")) == NULL) {
1140 FILE_OP_ERROR(prtmp, "fopen");
1146 if (msginfo->date) fprintf(prfp, "Date: %s\n", msginfo->date);
1147 if (msginfo->from) fprintf(prfp, "From: %s\n", msginfo->from);
1148 if (msginfo->to) fprintf(prfp, "To: %s\n", msginfo->to);
1149 if (msginfo->cc) fprintf(prfp, "Cc: %s\n", msginfo->cc);
1150 if (msginfo->newsgroups)
1151 fprintf(prfp, "Newsgroups: %s\n", msginfo->newsgroups);
1152 if (msginfo->subject) fprintf(prfp, "Subject: %s\n", msginfo->subject);
1155 while (fgets(buf, sizeof(buf), tmpfp) != NULL)
1161 if (cmdline && (p = strchr(cmdline, '%')) && *(p + 1) == 's' &&
1162 !strchr(p + 2, '%'))
1163 g_snprintf(buf, sizeof(buf) - 1, cmdline, prtmp);
1166 g_warning("Print command line is invalid: '%s'\n",
1168 g_snprintf(buf, sizeof(buf) - 1, def_cmd, prtmp);
1174 if (buf[strlen(buf) - 1] != '&') strcat(buf, "&");
1178 MsgInfo *procmsg_msginfo_new_ref(MsgInfo *msginfo)
1185 MsgInfo *procmsg_msginfo_new(void)
1187 MsgInfo *newmsginfo;
1189 newmsginfo = g_new0(MsgInfo, 1);
1190 newmsginfo->refcnt = 1;
1195 MsgInfo *procmsg_msginfo_copy(MsgInfo *msginfo)
1197 MsgInfo *newmsginfo;
1200 if (msginfo == NULL) return NULL;
1202 newmsginfo = g_new0(MsgInfo, 1);
1204 newmsginfo->refcnt = 1;
1206 #define MEMBCOPY(mmb) newmsginfo->mmb = msginfo->mmb
1207 #define MEMBDUP(mmb) newmsginfo->mmb = msginfo->mmb ? \
1208 g_strdup(msginfo->mmb) : NULL
1223 MEMBDUP(newsgroups);
1230 MEMBCOPY(to_folder);
1232 if (msginfo->extradata) {
1233 newmsginfo->extradata = g_new0(MsgInfoExtraData, 1);
1234 MEMBDUP(extradata->face);
1235 MEMBDUP(extradata->xface);
1236 MEMBDUP(extradata->dispositionnotificationto);
1237 MEMBDUP(extradata->returnreceiptto);
1238 MEMBDUP(extradata->partial_recv);
1239 MEMBDUP(extradata->account_server);
1240 MEMBDUP(extradata->account_login);
1241 MEMBDUP(extradata->list_post);
1242 MEMBDUP(extradata->list_subscribe);
1243 MEMBDUP(extradata->list_unsubscribe);
1244 MEMBDUP(extradata->list_help);
1245 MEMBDUP(extradata->list_archive);
1246 MEMBDUP(extradata->list_owner);
1249 refs = msginfo->references;
1250 for (refs = msginfo->references; refs != NULL; refs = refs->next) {
1251 newmsginfo->references = g_slist_prepend
1252 (newmsginfo->references, g_strdup(refs->data));
1254 newmsginfo->references = g_slist_reverse(newmsginfo->references);
1257 MEMBDUP(plaintext_file);
1262 MsgInfo *procmsg_msginfo_get_full_info(MsgInfo *msginfo)
1264 MsgInfo *full_msginfo;
1267 if (msginfo == NULL) return NULL;
1269 file = procmsg_get_message_file_path(msginfo);
1270 if (!file || !is_file_exist(file)) {
1272 file = procmsg_get_message_file(msginfo);
1274 if (!file || !is_file_exist(file)) {
1275 g_warning("procmsg_msginfo_get_full_info(): can't get message file.\n");
1279 full_msginfo = procheader_parse_file(file, msginfo->flags, TRUE, FALSE);
1281 if (!full_msginfo) return NULL;
1283 msginfo->total_size = full_msginfo->total_size;
1284 msginfo->planned_download = full_msginfo->planned_download;
1286 if (full_msginfo->extradata) {
1287 if (!msginfo->extradata)
1288 msginfo->extradata = g_new0(MsgInfoExtraData, 1);
1289 if (!msginfo->extradata->list_post)
1290 msginfo->extradata->list_post = g_strdup(full_msginfo->extradata->list_post);
1291 if (!msginfo->extradata->list_subscribe)
1292 msginfo->extradata->list_subscribe = g_strdup(full_msginfo->extradata->list_subscribe);
1293 if (!msginfo->extradata->list_unsubscribe)
1294 msginfo->extradata->list_unsubscribe = g_strdup(full_msginfo->extradata->list_unsubscribe);
1295 if (!msginfo->extradata->list_help)
1296 msginfo->extradata->list_help = g_strdup(full_msginfo->extradata->list_help);
1297 if (!msginfo->extradata->list_archive)
1298 msginfo->extradata->list_archive= g_strdup(full_msginfo->extradata->list_archive);
1299 if (!msginfo->extradata->list_owner)
1300 msginfo->extradata->list_owner = g_strdup(full_msginfo->extradata->list_owner);
1301 if (!msginfo->extradata->xface)
1302 msginfo->extradata->xface = g_strdup(full_msginfo->extradata->xface);
1303 if (!msginfo->extradata->face)
1304 msginfo->extradata->face = g_strdup(full_msginfo->extradata->face);
1305 if (!msginfo->extradata->dispositionnotificationto)
1306 msginfo->extradata->dispositionnotificationto =
1307 g_strdup(full_msginfo->extradata->dispositionnotificationto);
1308 if (!msginfo->extradata->returnreceiptto)
1309 msginfo->extradata->returnreceiptto = g_strdup
1310 (full_msginfo->extradata->returnreceiptto);
1311 if (!msginfo->extradata->partial_recv && full_msginfo->extradata->partial_recv)
1312 msginfo->extradata->partial_recv = g_strdup
1313 (full_msginfo->extradata->partial_recv);
1314 if (!msginfo->extradata->account_server && full_msginfo->extradata->account_server)
1315 msginfo->extradata->account_server = g_strdup
1316 (full_msginfo->extradata->account_server);
1317 if (!msginfo->extradata->account_login && full_msginfo->extradata->account_login)
1318 msginfo->extradata->account_login = g_strdup
1319 (full_msginfo->extradata->account_login);
1321 procmsg_msginfo_free(full_msginfo);
1323 return procmsg_msginfo_new_ref(msginfo);
1326 void procmsg_msginfo_free(MsgInfo *msginfo)
1328 if (msginfo == NULL) return;
1331 if (msginfo->refcnt > 0)
1334 if (msginfo->to_folder) {
1335 msginfo->to_folder->op_count--;
1336 folder_item_update(msginfo->to_folder, F_ITEM_UPDATE_MSGCNT);
1339 g_free(msginfo->fromspace);
1341 g_free(msginfo->fromname);
1343 g_free(msginfo->date);
1344 g_free(msginfo->from);
1345 g_free(msginfo->to);
1346 g_free(msginfo->cc);
1347 g_free(msginfo->newsgroups);
1348 g_free(msginfo->subject);
1349 g_free(msginfo->msgid);
1350 g_free(msginfo->inreplyto);
1351 g_free(msginfo->xref);
1353 if (msginfo->extradata) {
1354 g_free(msginfo->extradata->returnreceiptto);
1355 g_free(msginfo->extradata->dispositionnotificationto);
1356 g_free(msginfo->extradata->xface);
1357 g_free(msginfo->extradata->face);
1358 g_free(msginfo->extradata->list_post);
1359 g_free(msginfo->extradata->list_subscribe);
1360 g_free(msginfo->extradata->list_unsubscribe);
1361 g_free(msginfo->extradata->list_help);
1362 g_free(msginfo->extradata->list_archive);
1363 g_free(msginfo->extradata->list_owner);
1364 g_free(msginfo->extradata->partial_recv);
1365 g_free(msginfo->extradata->account_server);
1366 g_free(msginfo->extradata->account_login);
1367 g_free(msginfo->extradata);
1369 slist_free_strings(msginfo->references);
1370 g_slist_free(msginfo->references);
1372 g_free(msginfo->plaintext_file);
1377 guint procmsg_msginfo_memusage(MsgInfo *msginfo)
1382 memusage += sizeof(MsgInfo);
1383 if (msginfo->fromname)
1384 memusage += strlen(msginfo->fromname);
1386 memusage += strlen(msginfo->date);
1388 memusage += strlen(msginfo->from);
1390 memusage += strlen(msginfo->to);
1392 memusage += strlen(msginfo->cc);
1393 if (msginfo->newsgroups)
1394 memusage += strlen(msginfo->newsgroups);
1395 if (msginfo->subject)
1396 memusage += strlen(msginfo->subject);
1398 memusage += strlen(msginfo->msgid);
1399 if (msginfo->inreplyto)
1400 memusage += strlen(msginfo->inreplyto);
1401 for (refs = msginfo->references; refs; refs=refs->next) {
1402 gchar *r = (gchar *)refs->data;
1403 memusage += r?strlen(r):0;
1405 if (msginfo->fromspace)
1406 memusage += strlen(msginfo->fromspace);
1408 if (msginfo->extradata) {
1409 memusage += sizeof(MsgInfoExtraData);
1410 if (msginfo->extradata->xface)
1411 memusage += strlen(msginfo->extradata->xface);
1412 if (msginfo->extradata->face)
1413 memusage += strlen(msginfo->extradata->face);
1414 if (msginfo->extradata->dispositionnotificationto)
1415 memusage += strlen(msginfo->extradata->dispositionnotificationto);
1416 if (msginfo->extradata->returnreceiptto)
1417 memusage += strlen(msginfo->extradata->returnreceiptto);
1419 if (msginfo->extradata->partial_recv)
1420 memusage += strlen(msginfo->extradata->partial_recv);
1421 if (msginfo->extradata->account_server)
1422 memusage += strlen(msginfo->extradata->account_server);
1423 if (msginfo->extradata->account_login)
1424 memusage += strlen(msginfo->extradata->account_login);
1426 if (msginfo->extradata->list_post)
1427 memusage += strlen(msginfo->extradata->list_post);
1428 if (msginfo->extradata->list_subscribe)
1429 memusage += strlen(msginfo->extradata->list_subscribe);
1430 if (msginfo->extradata->list_unsubscribe)
1431 memusage += strlen(msginfo->extradata->list_unsubscribe);
1432 if (msginfo->extradata->list_help)
1433 memusage += strlen(msginfo->extradata->list_help);
1434 if (msginfo->extradata->list_archive)
1435 memusage += strlen(msginfo->extradata->list_archive);
1436 if (msginfo->extradata->list_owner)
1437 memusage += strlen(msginfo->extradata->list_owner);
1442 gint procmsg_cmp_msgnum_for_sort(gconstpointer a, gconstpointer b)
1444 const MsgInfo *msginfo1 = a;
1445 const MsgInfo *msginfo2 = b;
1452 return msginfo1->msgnum - msginfo2->msgnum;
1455 static gint procmsg_send_message_queue_full(const gchar *file, gboolean keep_session, gchar **errstr,
1456 FolderItem *queue, gint msgnum)
1458 static HeaderEntry qentry[] = {{"S:", NULL, FALSE},
1459 {"SSV:", NULL, FALSE},
1460 {"R:", NULL, FALSE},
1461 {"NG:", NULL, FALSE},
1462 {"MAID:", NULL, FALSE},
1463 {"NAID:", NULL, FALSE},
1464 {"SCF:", NULL, FALSE},
1465 {"RMID:", NULL, FALSE},
1466 {"FMID:", NULL, FALSE},
1467 {"X-Sylpheed-Privacy-System:", NULL, FALSE},
1468 {"X-Sylpheed-Encrypt:", NULL, FALSE},
1469 {"X-Sylpheed-Encrypt-Data:", NULL, FALSE},
1470 {"X-Sylpheed-End-Special-Headers:", NULL, FALSE},
1471 {NULL, NULL, FALSE}};
1474 gint mailval = 0, newsval = 0;
1476 gchar *smtpserver = NULL;
1477 GSList *to_list = NULL;
1478 GSList *newsgroup_list = NULL;
1479 gchar *savecopyfolder = NULL;
1480 gchar *replymessageid = NULL;
1481 gchar *fwdmessageid = NULL;
1482 gchar *privacy_system = NULL;
1483 gboolean encrypt = FALSE;
1484 gchar *encrypt_data = NULL;
1485 gchar buf[BUFFSIZE];
1487 PrefsAccount *mailac = NULL, *newsac = NULL;
1488 gboolean save_clear_text = TRUE;
1489 gchar *tmp_enc_file = NULL;
1493 g_return_val_if_fail(file != NULL, -1);
1495 if ((fp = g_fopen(file, "rb")) == NULL) {
1496 FILE_OP_ERROR(file, "fopen");
1498 if (*errstr) g_free(*errstr);
1499 *errstr = g_strdup_printf(_("Couldn't open file %s."), file);
1504 while ((hnum = procheader_get_one_field(buf, sizeof(buf), fp, qentry))
1506 gchar *p = buf + strlen(qentry[hnum].name);
1514 if (smtpserver == NULL)
1515 smtpserver = g_strdup(p);
1518 to_list = address_list_append(to_list, p);
1521 newsgroup_list = newsgroup_list_append(newsgroup_list, p);
1523 case Q_MAIL_ACCOUNT_ID:
1524 mailac = account_find_from_id(atoi(p));
1526 case Q_NEWS_ACCOUNT_ID:
1527 newsac = account_find_from_id(atoi(p));
1529 case Q_SAVE_COPY_FOLDER:
1530 if (savecopyfolder == NULL)
1531 savecopyfolder = g_strdup(p);
1533 case Q_REPLY_MESSAGE_ID:
1534 if (replymessageid == NULL)
1535 replymessageid = g_strdup(p);
1537 case Q_FWD_MESSAGE_ID:
1538 if (fwdmessageid == NULL)
1539 fwdmessageid = g_strdup(p);
1541 case Q_PRIVACY_SYSTEM:
1542 if (privacy_system == NULL)
1543 privacy_system = g_strdup(p);
1549 case Q_ENCRYPT_DATA:
1550 if (encrypt_data == NULL)
1551 encrypt_data = g_strdup(p);
1553 case Q_SYLPHEED_HDRS:
1554 /* end of special headers reached */
1555 goto send_mail; /* can't "break;break;" */
1559 filepos = ftell(fp);
1564 if (mailac && mailac->save_encrypted_as_clear_text
1565 && !mailac->encrypt_to_self)
1566 save_clear_text = TRUE;
1568 save_clear_text = FALSE;
1573 mimeinfo = procmime_scan_queue_file(file);
1574 if (!privacy_encrypt(privacy_system, mimeinfo, encrypt_data)
1575 || (fp = my_tmpfile()) == NULL
1576 || procmime_write_mimeinfo(mimeinfo, fp) < 0) {
1579 procmime_mimeinfo_free_all(mimeinfo);
1582 slist_free_strings(to_list);
1583 g_slist_free(to_list);
1584 slist_free_strings(newsgroup_list);
1585 g_slist_free(newsgroup_list);
1586 g_free(savecopyfolder);
1587 g_free(replymessageid);
1588 g_free(fwdmessageid);
1589 g_free(privacy_system);
1590 g_free(encrypt_data);
1592 if (*errstr) g_free(*errstr);
1593 *errstr = g_strdup_printf(_("Couldn't encrypt the email: %s"),
1594 privacy_get_error());
1600 if (!save_clear_text) {
1601 gchar *content = NULL;
1602 FILE *tmpfp = get_tmpfile_in_dir(get_mime_tmp_dir(), &tmp_enc_file);
1606 content = file_read_stream_to_str(fp);
1609 str_write_to_file(content, tmp_enc_file);
1612 g_warning("couldn't get tempfile\n");
1616 procmime_mimeinfo_free_all(mimeinfo);
1622 debug_print("Sending message by mail\n");
1625 if (*errstr) g_free(*errstr);
1626 *errstr = g_strdup_printf(_("Queued message header is broken."));
1629 } else if (mailac && mailac->use_mail_command &&
1630 mailac->mail_command && (* mailac->mail_command)) {
1631 mailval = send_message_local(mailac->mail_command, fp);
1635 mailac = account_find_from_smtp_server(from, smtpserver);
1637 g_warning("Account not found. "
1638 "Using current account...\n");
1639 mailac = cur_account;
1644 mailval = send_message_smtp_full(mailac, to_list, fp, keep_session);
1645 if (mailval == -1 && errstr) {
1646 if (*errstr) g_free(*errstr);
1647 *errstr = g_strdup_printf(_("An error happened during SMTP session."));
1650 PrefsAccount tmp_ac;
1652 g_warning("Account not found.\n");
1654 memset(&tmp_ac, 0, sizeof(PrefsAccount));
1655 tmp_ac.address = from;
1656 tmp_ac.smtp_server = smtpserver;
1657 tmp_ac.smtpport = SMTP_PORT;
1658 mailval = send_message_smtp(&tmp_ac, to_list, fp);
1659 if (mailval == -1 && errstr) {
1660 if (*errstr) g_free(*errstr);
1661 *errstr = g_strdup_printf(_("No specific account has been found to "
1662 "send, and an error happened during SMTP session."));
1666 } else if (!to_list && !newsgroup_list) {
1668 if (*errstr) g_free(*errstr);
1669 *errstr = g_strdup(_("Couldn't determine sending informations. "
1670 "Maybe the email hasn't been generated by Sylpheed-Claws."));
1675 fseek(fp, filepos, SEEK_SET);
1676 if (newsgroup_list && (mailval == 0)) {
1681 /* write to temporary file */
1682 tmp = g_strdup_printf("%s%ctmp%d", g_get_tmp_dir(),
1683 G_DIR_SEPARATOR, (gint)file);
1684 if ((tmpfp = g_fopen(tmp, "wb")) == NULL) {
1685 FILE_OP_ERROR(tmp, "fopen");
1687 alertpanel_error(_("Couldn't create temporary file for news sending."));
1689 if (change_file_mode_rw(tmpfp, tmp) < 0) {
1690 FILE_OP_ERROR(tmp, "chmod");
1691 g_warning("can't change file mode\n");
1694 while ((newsval == 0) && fgets(buf, sizeof(buf), fp) != NULL) {
1695 if (fputs(buf, tmpfp) == EOF) {
1696 FILE_OP_ERROR(tmp, "fputs");
1699 if (*errstr) g_free(*errstr);
1700 *errstr = g_strdup_printf(_("Error when writing temporary file for news sending."));
1707 debug_print("Sending message by news\n");
1709 folder = FOLDER(newsac->folder);
1711 newsval = news_post(folder, tmp);
1712 if (newsval < 0 && errstr) {
1713 if (*errstr) g_free(*errstr);
1714 *errstr = g_strdup_printf(_("Error occurred while posting the message to %s."),
1715 newsac->nntp_server);
1725 /* save message to outbox */
1726 if (mailval == 0 && newsval == 0 && savecopyfolder) {
1729 debug_print("saving sent message...\n");
1731 outbox = folder_find_item_from_identifier(savecopyfolder);
1733 outbox = folder_get_default_outbox();
1735 if (save_clear_text || tmp_enc_file == NULL) {
1736 gboolean saved = FALSE;
1737 if (queue && msgnum > 0) {
1738 MsgInfo *queued_mail = folder_item_get_msginfo(queue, msgnum);
1739 if (folder_item_copy_msg(outbox, queued_mail) >= 0)
1741 procmsg_msginfo_free(queued_mail);
1744 procmsg_save_to_outbox(outbox, file, TRUE);
1746 procmsg_save_to_outbox(outbox, tmp_enc_file, FALSE);
1750 if (tmp_enc_file != NULL) {
1751 g_unlink(tmp_enc_file);
1753 tmp_enc_file = NULL;
1756 if (replymessageid != NULL || fwdmessageid != NULL) {
1760 if (replymessageid != NULL)
1761 tokens = g_strsplit(replymessageid, "\t", 0);
1763 tokens = g_strsplit(fwdmessageid, "\t", 0);
1764 item = folder_find_item_from_identifier(tokens[0]);
1766 /* check if queued message has valid folder and message id */
1767 if (item != NULL && tokens[2] != NULL) {
1770 msginfo = folder_item_get_msginfo(item, atoi(tokens[1]));
1772 /* check if referring message exists and has a message id */
1773 if ((msginfo != NULL) &&
1774 (msginfo->msgid != NULL) &&
1775 (strcmp(msginfo->msgid, tokens[2]) != 0)) {
1776 procmsg_msginfo_free(msginfo);
1780 if (msginfo == NULL) {
1781 msginfo = folder_item_get_msginfo_by_msgid(item, tokens[2]);
1784 if (msginfo != NULL) {
1785 if (replymessageid != NULL) {
1786 procmsg_msginfo_unset_flags(msginfo, MSG_FORWARDED, 0);
1787 procmsg_msginfo_set_flags(msginfo, MSG_REPLIED, 0);
1789 procmsg_msginfo_unset_flags(msginfo, MSG_REPLIED, 0);
1790 procmsg_msginfo_set_flags(msginfo, MSG_FORWARDED, 0);
1792 procmsg_msginfo_free(msginfo);
1800 slist_free_strings(to_list);
1801 g_slist_free(to_list);
1802 slist_free_strings(newsgroup_list);
1803 g_slist_free(newsgroup_list);
1804 g_free(savecopyfolder);
1805 g_free(replymessageid);
1806 g_free(fwdmessageid);
1807 g_free(privacy_system);
1808 g_free(encrypt_data);
1810 return (newsval != 0 ? newsval : mailval);
1813 gint procmsg_send_message_queue(const gchar *file, gchar **errstr, FolderItem *queue, gint msgnum)
1815 gint result = procmsg_send_message_queue_full(file, FALSE, errstr, queue, msgnum);
1816 toolbar_main_set_sensitive(mainwindow_get_mainwindow());
1820 static void update_folder_msg_counts(FolderItem *item, MsgInfo *msginfo, MsgPermFlags old_flags)
1822 MsgPermFlags new_flags = msginfo->flags.perm_flags;
1825 if (!(old_flags & MSG_NEW) && (new_flags & MSG_NEW)) {
1829 if ((old_flags & MSG_NEW) && !(new_flags & MSG_NEW)) {
1834 if (!(old_flags & MSG_UNREAD) && (new_flags & MSG_UNREAD)) {
1835 item->unread_msgs++;
1836 if (procmsg_msg_has_marked_parent(msginfo))
1837 item->unreadmarked_msgs++;
1840 if ((old_flags & MSG_UNREAD) && !(new_flags & MSG_UNREAD)) {
1841 item->unread_msgs--;
1842 if (procmsg_msg_has_marked_parent(msginfo))
1843 item->unreadmarked_msgs--;
1847 if (!(old_flags & MSG_MARKED) && (new_flags & MSG_MARKED)) {
1848 procmsg_update_unread_children(msginfo, TRUE);
1849 item->marked_msgs++;
1852 if ((old_flags & MSG_MARKED) && !(new_flags & MSG_MARKED)) {
1853 procmsg_update_unread_children(msginfo, FALSE);
1854 item->marked_msgs--;
1858 void procmsg_msginfo_set_flags(MsgInfo *msginfo, MsgPermFlags perm_flags, MsgTmpFlags tmp_flags)
1861 MsgInfoUpdate msginfo_update;
1862 MsgPermFlags perm_flags_new, perm_flags_old;
1863 MsgTmpFlags tmp_flags_old;
1865 g_return_if_fail(msginfo != NULL);
1866 item = msginfo->folder;
1867 g_return_if_fail(item != NULL);
1869 debug_print("Setting flags for message %d in folder %s\n", msginfo->msgnum, item->path);
1871 /* Perm Flags handling */
1872 perm_flags_old = msginfo->flags.perm_flags;
1873 perm_flags_new = msginfo->flags.perm_flags | perm_flags;
1874 if ((perm_flags & MSG_IGNORE_THREAD) || (perm_flags_old & MSG_IGNORE_THREAD)) {
1875 perm_flags_new &= ~(MSG_NEW | MSG_UNREAD);
1878 if (perm_flags_old != perm_flags_new) {
1879 folder_item_change_msg_flags(msginfo->folder, msginfo, perm_flags_new);
1881 update_folder_msg_counts(item, msginfo, perm_flags_old);
1885 /* Tmp flags handling */
1886 tmp_flags_old = msginfo->flags.tmp_flags;
1887 msginfo->flags.tmp_flags |= tmp_flags;
1889 /* update notification */
1890 if ((perm_flags_old != perm_flags_new) || (tmp_flags_old != msginfo->flags.tmp_flags)) {
1891 msginfo_update.msginfo = msginfo;
1892 msginfo_update.flags = MSGINFO_UPDATE_FLAGS;
1893 hooks_invoke(MSGINFO_UPDATE_HOOKLIST, &msginfo_update);
1894 folder_item_update(msginfo->folder, F_ITEM_UPDATE_MSGCNT);
1898 void procmsg_msginfo_unset_flags(MsgInfo *msginfo, MsgPermFlags perm_flags, MsgTmpFlags tmp_flags)
1901 MsgInfoUpdate msginfo_update;
1902 MsgPermFlags perm_flags_new, perm_flags_old;
1903 MsgTmpFlags tmp_flags_old;
1905 g_return_if_fail(msginfo != NULL);
1906 item = msginfo->folder;
1907 g_return_if_fail(item != NULL);
1909 debug_print("Unsetting flags for message %d in folder %s\n", msginfo->msgnum, item->path);
1911 /* Perm Flags handling */
1912 perm_flags_old = msginfo->flags.perm_flags;
1913 perm_flags_new = msginfo->flags.perm_flags & ~perm_flags;
1915 if (perm_flags_old != perm_flags_new) {
1916 folder_item_change_msg_flags(msginfo->folder, msginfo, perm_flags_new);
1918 update_folder_msg_counts(item, msginfo, perm_flags_old);
1921 /* Tmp flags hanlding */
1922 tmp_flags_old = msginfo->flags.tmp_flags;
1923 msginfo->flags.tmp_flags &= ~tmp_flags;
1925 /* update notification */
1926 if ((perm_flags_old != perm_flags_new) || (tmp_flags_old != msginfo->flags.tmp_flags)) {
1927 msginfo_update.msginfo = msginfo;
1928 msginfo_update.flags = MSGINFO_UPDATE_FLAGS;
1929 hooks_invoke(MSGINFO_UPDATE_HOOKLIST, &msginfo_update);
1930 folder_item_update(msginfo->folder, F_ITEM_UPDATE_MSGCNT);
1934 void procmsg_msginfo_change_flags(MsgInfo *msginfo,
1935 MsgPermFlags add_perm_flags, MsgTmpFlags add_tmp_flags,
1936 MsgPermFlags rem_perm_flags, MsgTmpFlags rem_tmp_flags)
1939 MsgInfoUpdate msginfo_update;
1940 MsgPermFlags perm_flags_new, perm_flags_old;
1941 MsgTmpFlags tmp_flags_old;
1943 g_return_if_fail(msginfo != NULL);
1944 item = msginfo->folder;
1945 g_return_if_fail(item != NULL);
1947 debug_print("Changing flags for message %d in folder %s\n", msginfo->msgnum, item->path);
1949 /* Perm Flags handling */
1950 perm_flags_old = msginfo->flags.perm_flags;
1951 perm_flags_new = (msginfo->flags.perm_flags & ~rem_perm_flags) | add_perm_flags;
1952 if ((add_perm_flags & MSG_IGNORE_THREAD) || (perm_flags_old & MSG_IGNORE_THREAD)) {
1953 perm_flags_new &= ~(MSG_NEW | MSG_UNREAD);
1956 if (perm_flags_old != perm_flags_new) {
1957 folder_item_change_msg_flags(msginfo->folder, msginfo, perm_flags_new);
1959 update_folder_msg_counts(item, msginfo, perm_flags_old);
1963 /* Tmp flags handling */
1964 tmp_flags_old = msginfo->flags.tmp_flags;
1965 msginfo->flags.tmp_flags &= ~rem_tmp_flags;
1966 msginfo->flags.tmp_flags |= add_tmp_flags;
1968 /* update notification */
1969 if ((perm_flags_old != perm_flags_new) || (tmp_flags_old != msginfo->flags.tmp_flags)) {
1970 msginfo_update.msginfo = msginfo;
1971 msginfo_update.flags = MSGINFO_UPDATE_FLAGS;
1972 hooks_invoke(MSGINFO_UPDATE_HOOKLIST, &msginfo_update);
1973 folder_item_update(msginfo->folder, F_ITEM_UPDATE_MSGCNT);
1978 *\brief check for flags (e.g. mark) in prior msgs of current thread
1980 *\param info Current message
1981 *\param perm_flags Flags to be checked
1982 *\param parentmsgs Hash of prior msgs to avoid loops
1984 *\return gboolean TRUE if perm_flags are found
1986 gboolean procmsg_msg_has_flagged_parent_real(MsgInfo *info,
1987 MsgPermFlags perm_flags, GHashTable *parentmsgs)
1991 g_return_val_if_fail(info != NULL, FALSE);
1993 if (info != NULL && info->folder != NULL && info->inreplyto != NULL) {
1994 tmp = folder_item_get_msginfo_by_msgid(info->folder,
1996 if (tmp && (tmp->flags.perm_flags & perm_flags)) {
1997 procmsg_msginfo_free(tmp);
1999 } else if (tmp != NULL) {
2002 if (g_hash_table_lookup(parentmsgs, info)) {
2003 debug_print("loop detected: %d\n",
2007 g_hash_table_insert(parentmsgs, info, "1");
2008 result = procmsg_msg_has_flagged_parent_real(
2009 tmp, perm_flags, parentmsgs);
2011 procmsg_msginfo_free(tmp);
2021 *\brief Callback for cleaning up hash of parentmsgs
2023 gboolean parentmsgs_hash_remove(gpointer key,
2031 *\brief Set up list of parentmsgs
2032 * See procmsg_msg_has_flagged_parent_real()
2034 gboolean procmsg_msg_has_flagged_parent(MsgInfo *info, MsgPermFlags perm_flags)
2037 static GHashTable *parentmsgs = NULL;
2039 if (parentmsgs == NULL)
2040 parentmsgs = g_hash_table_new(NULL, NULL);
2042 result = procmsg_msg_has_flagged_parent_real(info, perm_flags, parentmsgs);
2043 g_hash_table_foreach_remove(parentmsgs, parentmsgs_hash_remove, NULL);
2049 *\brief Check if msgs prior in thread are marked
2050 * See procmsg_msg_has_flagged_parent_real()
2052 gboolean procmsg_msg_has_marked_parent(MsgInfo *info)
2054 return procmsg_msg_has_flagged_parent(info, MSG_MARKED);
2058 GSList *procmsg_find_children_func(MsgInfo *info,
2059 GSList *children, GSList *all)
2063 g_return_val_if_fail(info!=NULL, children);
2064 if (info->msgid == NULL)
2067 for (cur = all; cur != NULL; cur = g_slist_next(cur)) {
2068 MsgInfo *tmp = (MsgInfo *)cur->data;
2069 if (tmp->inreplyto && !strcmp(tmp->inreplyto, info->msgid)) {
2070 /* Check if message is already in the list */
2071 if ((children == NULL) ||
2072 (g_slist_index(children, tmp) == -1)) {
2073 children = g_slist_prepend(children,
2074 procmsg_msginfo_new_ref(tmp));
2075 children = procmsg_find_children_func(tmp,
2084 GSList *procmsg_find_children (MsgInfo *info)
2089 g_return_val_if_fail(info!=NULL, NULL);
2090 all = folder_item_get_msg_list(info->folder);
2091 children = procmsg_find_children_func(info, NULL, all);
2092 if (children != NULL) {
2093 for (cur = all; cur != NULL; cur = g_slist_next(cur)) {
2094 /* this will not free the used pointers
2095 created with procmsg_msginfo_new_ref */
2096 procmsg_msginfo_free((MsgInfo *)cur->data);
2104 void procmsg_update_unread_children(MsgInfo *info, gboolean newly_marked)
2106 GSList *children = procmsg_find_children(info);
2108 for (cur = children; cur != NULL; cur = g_slist_next(cur)) {
2109 MsgInfo *tmp = (MsgInfo *)cur->data;
2110 if(MSG_IS_UNREAD(tmp->flags) && !MSG_IS_IGNORE_THREAD(tmp->flags)) {
2112 info->folder->unreadmarked_msgs++;
2114 info->folder->unreadmarked_msgs--;
2115 folder_item_update(info->folder, F_ITEM_UPDATE_MSGCNT);
2117 procmsg_msginfo_free(tmp);
2119 g_slist_free(children);
2123 * Set the destination folder for a copy or move operation
2125 * \param msginfo The message which's destination folder is changed
2126 * \param to_folder The destination folder for the operation
2128 void procmsg_msginfo_set_to_folder(MsgInfo *msginfo, FolderItem *to_folder)
2130 if(msginfo->to_folder != NULL) {
2131 msginfo->to_folder->op_count--;
2132 folder_item_update(msginfo->to_folder, F_ITEM_UPDATE_MSGCNT);
2134 msginfo->to_folder = to_folder;
2135 if(to_folder != NULL) {
2136 to_folder->op_count++;
2137 folder_item_update(msginfo->to_folder, F_ITEM_UPDATE_MSGCNT);
2142 * Apply filtering actions to the msginfo
2144 * \param msginfo The MsgInfo describing the message that should be filtered
2145 * \return TRUE if the message was moved and MsgInfo is now invalid,
2148 gboolean procmsg_msginfo_filter(MsgInfo *msginfo, PrefsAccount* ac_prefs)
2150 MailFilteringData mail_filtering_data;
2152 mail_filtering_data.msginfo = msginfo;
2153 mail_filtering_data.msglist = NULL;
2154 mail_filtering_data.filtered = NULL;
2155 mail_filtering_data.unfiltered = NULL;
2156 if (hooks_invoke(MAIL_FILTERING_HOOKLIST, &mail_filtering_data)) {
2160 /* filter if enabled in prefs or move to inbox if not */
2161 if((filtering_rules != NULL) &&
2162 filter_message_by_msginfo(filtering_rules, msginfo, ac_prefs)) {
2169 void procmsg_msglist_filter(GSList *list, PrefsAccount *ac,
2170 GSList **filtered, GSList **unfiltered,
2173 GSList *cur, *to_do = NULL;
2174 gint total = 0, curnum = 0;
2175 MailFilteringData mail_filtering_data;
2177 g_return_if_fail(filtered != NULL);
2178 g_return_if_fail(unfiltered != NULL);
2186 total = g_slist_length(list);
2190 *unfiltered = g_slist_copy(list);
2194 statusbar_print_all(_("Filtering messages...\n"));
2196 mail_filtering_data.msginfo = NULL;
2197 mail_filtering_data.msglist = list;
2198 mail_filtering_data.filtered = NULL;
2199 mail_filtering_data.unfiltered = NULL;
2201 hooks_invoke(MAIL_LISTFILTERING_HOOKLIST, &mail_filtering_data);
2203 if (mail_filtering_data.filtered == NULL &&
2204 mail_filtering_data.unfiltered == NULL) {
2205 /* nothing happened */
2206 debug_print(MAIL_LISTFILTERING_HOOKLIST " did nothing. filtering whole list normally.\n");
2209 if (mail_filtering_data.filtered != NULL) {
2210 /* keep track of what's been filtered by the hooks */
2211 debug_print(MAIL_LISTFILTERING_HOOKLIST " filtered some stuff. total %d filtered %d unfilt %d.\n",
2212 g_slist_length(list),
2213 g_slist_length(mail_filtering_data.filtered),
2214 g_slist_length(mail_filtering_data.unfiltered));
2216 *filtered = g_slist_copy(mail_filtering_data.filtered);
2218 if (mail_filtering_data.unfiltered != NULL) {
2219 /* what the hooks didn't handle will go in filtered or
2220 * unfiltered in the next loop */
2221 debug_print(MAIL_LISTFILTERING_HOOKLIST " left unfiltered stuff. total %d filtered %d unfilt %d.\n",
2222 g_slist_length(list),
2223 g_slist_length(mail_filtering_data.filtered),
2224 g_slist_length(mail_filtering_data.unfiltered));
2225 to_do = mail_filtering_data.unfiltered;
2228 for (cur = to_do; cur; cur = cur->next) {
2229 MsgInfo *info = (MsgInfo *)cur->data;
2230 if (procmsg_msginfo_filter(info, ac))
2231 *filtered = g_slist_prepend(*filtered, info);
2233 *unfiltered = g_slist_prepend(*unfiltered, info);
2234 statusbar_progress_all(curnum++, total, prefs_common.statusbar_update_step);
2237 g_slist_free(mail_filtering_data.filtered);
2238 g_slist_free(mail_filtering_data.unfiltered);
2240 *filtered = g_slist_reverse(*filtered);
2241 *unfiltered = g_slist_reverse(*unfiltered);
2243 statusbar_progress_all(0,0,0);
2244 statusbar_pop_all();
2247 MsgInfo *procmsg_msginfo_new_from_mimeinfo(MsgInfo *src_msginfo, MimeInfo *mimeinfo)
2249 MsgInfo *tmp_msginfo = NULL;
2250 MsgFlags flags = {0, 0};
2251 gchar *tmpfile = get_tmp_file();
2252 FILE *fp = g_fopen(tmpfile, "wb");
2254 if (!mimeinfo || mimeinfo->type != MIMETYPE_MESSAGE ||
2255 g_ascii_strcasecmp(mimeinfo->subtype, "rfc822")) {
2256 g_warning("procmsg_msginfo_new_from_mimeinfo(): unsuitable mimeinfo");
2263 if (fp && procmime_write_mimeinfo(mimeinfo, fp) >= 0) {
2266 tmp_msginfo = procheader_parse_file(
2273 if (tmp_msginfo != NULL) {
2275 tmp_msginfo->folder = src_msginfo->folder;
2276 tmp_msginfo->plaintext_file = g_strdup(tmpfile);
2278 g_warning("procmsg_msginfo_new_from_mimeinfo(): Can't generate new msginfo");
2286 static GSList *spam_learners = NULL;
2288 void procmsg_register_spam_learner (int (*learn_func)(MsgInfo *info, GSList *list, gboolean spam))
2290 if (!g_slist_find(spam_learners, learn_func))
2291 spam_learners = g_slist_append(spam_learners, learn_func);
2292 if (mainwindow_get_mainwindow()) {
2293 main_window_set_menu_sensitive(mainwindow_get_mainwindow());
2294 summary_set_menu_sensitive(
2295 mainwindow_get_mainwindow()->summaryview);
2296 toolbar_main_set_sensitive(mainwindow_get_mainwindow());
2300 void procmsg_unregister_spam_learner (int (*learn_func)(MsgInfo *info, GSList *list, gboolean spam))
2302 spam_learners = g_slist_remove(spam_learners, learn_func);
2303 if (mainwindow_get_mainwindow()) {
2304 main_window_set_menu_sensitive(mainwindow_get_mainwindow());
2305 summary_set_menu_sensitive(
2306 mainwindow_get_mainwindow()->summaryview);
2307 toolbar_main_set_sensitive(mainwindow_get_mainwindow());
2311 gboolean procmsg_spam_can_learn(void)
2313 return g_slist_length(spam_learners) > 0;
2316 int procmsg_spam_learner_learn (MsgInfo *info, GSList *list, gboolean spam)
2318 GSList *cur = spam_learners;
2320 for (; cur; cur = cur->next) {
2321 int ((*func)(MsgInfo *info, GSList *list, gboolean spam)) = cur->data;
2322 ret |= func(info, list, spam);
2327 static gchar *spam_folder_item = NULL;
2328 void procmsg_spam_set_folder (const char *item_identifier)
2330 g_free(spam_folder_item);
2331 if (item_identifier)
2332 spam_folder_item = g_strdup(item_identifier);
2334 spam_folder_item = NULL;
2337 FolderItem *procmsg_spam_get_folder (void)
2339 FolderItem *item = spam_folder_item ? folder_find_item_from_identifier(spam_folder_item) : NULL;
2340 return item ? item : folder_get_default_trash();
2343 static void item_has_queued_mails(FolderItem *item, gpointer data)
2345 gboolean *result = (gboolean *)data;
2346 if (*result == TRUE)
2348 if (folder_has_parent_of_type(item, F_QUEUE) && item->total_msgs > 0)
2352 gboolean procmsg_have_queued_mails_fast (void)
2354 gboolean result = FALSE;
2355 folder_func_to_all_folders(item_has_queued_mails, &result);