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 log_warning(_("Already trying to send\n"));
909 if (*errstr) g_free(*errstr);
910 *errstr = g_strdup_printf(_("Already trying to send."));
912 toolbar_main_set_sensitive(mainwindow_get_mainwindow());
915 send_queue_lock = TRUE;
918 queue = folder_get_default_queue();
921 send_queue_lock = FALSE;
926 toolbar_main_set_sensitive(mainwindow_get_mainwindow());
928 folder_item_scan(queue);
929 list = folder_item_get_msg_list(queue);
931 /* sort the list per sender account; this helps reusing the same SMTP server */
932 sorted_list = procmsg_list_sort_by_account(queue, list);
934 for (elem = sorted_list; elem != NULL; elem = elem->next) {
938 msginfo = (MsgInfo *)(elem->data);
939 if (!MSG_IS_LOCKED(msginfo->flags)) {
940 file = folder_item_fetch_msg(queue, msginfo->msgnum);
942 if (procmsg_send_message_queue_full(file,
943 !procmsg_is_last_for_account(queue, msginfo, elem),
944 errstr, queue, msginfo->msgnum) < 0) {
945 g_warning("Sending queued message %d failed.\n",
950 folder_item_remove_msg(queue, msginfo->msgnum);
955 /* FIXME: supposedly if only one message is locked, and queue
956 * is being flushed, the following free says something like
957 * "freeing msg ## in folder (nil)". */
958 procmsg_msginfo_free(msginfo);
961 g_slist_free(sorted_list);
962 folder_item_scan(queue);
964 if (queue->node && queue->node->children) {
965 node = queue->node->children;
966 while (node != NULL) {
969 res = procmsg_send_queue(FOLDER_ITEM(node->data), save_msgs, errstr);
977 send_queue_lock = FALSE;
979 toolbar_main_set_sensitive(mainwindow_get_mainwindow());
981 return (err != 0 ? -err : sent);
984 gboolean procmsg_is_sending(void)
986 return send_queue_lock;
990 *\brief Determine if a queue folder is empty
992 *\param queue Queue folder to process
994 *\return TRUE if the queue folder is empty, otherwise return FALSE
996 gboolean procmsg_queue_is_empty(FolderItem *queue)
999 gboolean res = FALSE;
1001 queue = folder_get_default_queue();
1002 g_return_val_if_fail(queue != NULL, TRUE);
1004 folder_item_scan(queue);
1005 list = folder_item_get_msg_list(queue);
1006 res = (list == NULL);
1007 procmsg_msg_list_free(list);
1011 if (queue->node && queue->node->children) {
1012 node = queue->node->children;
1013 while (node != NULL) {
1015 if (!procmsg_queue_is_empty(FOLDER_ITEM(node->data)))
1024 gint procmsg_remove_special_headers(const gchar *in, const gchar *out)
1027 gchar buf[BUFFSIZE];
1029 if ((fp = g_fopen(in, "rb")) == NULL) {
1030 FILE_OP_ERROR(in, "fopen");
1033 if ((outfp = g_fopen(out, "wb")) == NULL) {
1034 FILE_OP_ERROR(out, "fopen");
1038 while (fgets(buf, sizeof(buf), fp) != NULL) {
1040 if (!strncmp(buf, "X-Sylpheed-End-Special-Headers: 1",
1041 strlen("X-Sylpheed-End-Special-Headers:")))
1044 if (buf[0] == '\r' || buf[0] == '\n') break;
1045 /* from other mailers */
1046 if (!strncmp(buf, "Date: ", 6)
1047 || !strncmp(buf, "To: ", 4)
1048 || !strncmp(buf, "From: ", 6)
1049 || !strncmp(buf, "Subject: ", 9)) {
1054 while (fgets(buf, sizeof(buf), fp) != NULL)
1061 gint procmsg_save_to_outbox(FolderItem *outbox, const gchar *file,
1065 MsgInfo *msginfo, *tmp_msginfo;
1066 MsgFlags flag = {0, 0};
1068 debug_print("saving sent message...\n");
1071 outbox = folder_get_default_outbox();
1072 g_return_val_if_fail(outbox != NULL, -1);
1074 /* remove queueing headers */
1076 gchar tmp[MAXPATHLEN + 1];
1078 g_snprintf(tmp, sizeof(tmp), "%s%ctmpmsg.out.%08x",
1079 get_rc_dir(), G_DIR_SEPARATOR, (guint) rand());
1081 if (procmsg_remove_special_headers(file, tmp) !=0)
1084 folder_item_scan(outbox);
1085 if ((num = folder_item_add_msg(outbox, tmp, &flag, TRUE)) < 0) {
1086 g_warning("can't save message\n");
1091 folder_item_scan(outbox);
1092 if ((num = folder_item_add_msg
1093 (outbox, file, &flag, FALSE)) < 0) {
1094 g_warning("can't save message\n");
1098 msginfo = folder_item_get_msginfo(outbox, num); /* refcnt++ */
1099 tmp_msginfo = procmsg_msginfo_get_full_info(msginfo); /* refcnt++ */
1100 if (msginfo != NULL) {
1101 procmsg_msginfo_unset_flags(msginfo, ~0, 0);
1102 procmsg_msginfo_free(msginfo); /* refcnt-- */
1103 /* tmp_msginfo == msginfo */
1104 if (tmp_msginfo && msginfo->extradata &&
1105 (msginfo->extradata->dispositionnotificationto ||
1106 msginfo->extradata->returnreceiptto)) {
1107 procmsg_msginfo_set_flags(msginfo, MSG_RETRCPT_SENT, 0);
1109 procmsg_msginfo_free(tmp_msginfo); /* refcnt-- */
1115 void procmsg_print_message(MsgInfo *msginfo, const gchar *cmdline)
1117 static const gchar *def_cmd = "lpr %s";
1118 static guint id = 0;
1124 g_return_if_fail(msginfo);
1126 if (procmime_msginfo_is_encrypted(msginfo))
1127 tmpfp = procmime_get_first_encrypted_text_content(msginfo);
1129 tmpfp = procmime_get_first_text_content(msginfo);
1130 if (tmpfp == NULL) {
1131 g_warning("Can't get text part\n");
1135 prtmp = g_strdup_printf("%s%cprinttmp.%08x",
1136 get_mime_tmp_dir(), G_DIR_SEPARATOR, id++);
1138 if ((prfp = g_fopen(prtmp, "wb")) == NULL) {
1139 FILE_OP_ERROR(prtmp, "fopen");
1145 if (msginfo->date) fprintf(prfp, "Date: %s\n", msginfo->date);
1146 if (msginfo->from) fprintf(prfp, "From: %s\n", msginfo->from);
1147 if (msginfo->to) fprintf(prfp, "To: %s\n", msginfo->to);
1148 if (msginfo->cc) fprintf(prfp, "Cc: %s\n", msginfo->cc);
1149 if (msginfo->newsgroups)
1150 fprintf(prfp, "Newsgroups: %s\n", msginfo->newsgroups);
1151 if (msginfo->subject) fprintf(prfp, "Subject: %s\n", msginfo->subject);
1154 while (fgets(buf, sizeof(buf), tmpfp) != NULL)
1160 if (cmdline && (p = strchr(cmdline, '%')) && *(p + 1) == 's' &&
1161 !strchr(p + 2, '%'))
1162 g_snprintf(buf, sizeof(buf) - 1, cmdline, prtmp);
1165 g_warning("Print command line is invalid: '%s'\n",
1167 g_snprintf(buf, sizeof(buf) - 1, def_cmd, prtmp);
1173 if (buf[strlen(buf) - 1] != '&') strcat(buf, "&");
1177 MsgInfo *procmsg_msginfo_new_ref(MsgInfo *msginfo)
1184 MsgInfo *procmsg_msginfo_new(void)
1186 MsgInfo *newmsginfo;
1188 newmsginfo = g_new0(MsgInfo, 1);
1189 newmsginfo->refcnt = 1;
1194 MsgInfo *procmsg_msginfo_copy(MsgInfo *msginfo)
1196 MsgInfo *newmsginfo;
1199 if (msginfo == NULL) return NULL;
1201 newmsginfo = g_new0(MsgInfo, 1);
1203 newmsginfo->refcnt = 1;
1205 #define MEMBCOPY(mmb) newmsginfo->mmb = msginfo->mmb
1206 #define MEMBDUP(mmb) newmsginfo->mmb = msginfo->mmb ? \
1207 g_strdup(msginfo->mmb) : NULL
1222 MEMBDUP(newsgroups);
1229 MEMBCOPY(to_folder);
1231 if (msginfo->extradata) {
1232 newmsginfo->extradata = g_new0(MsgInfoExtraData, 1);
1233 MEMBDUP(extradata->face);
1234 MEMBDUP(extradata->xface);
1235 MEMBDUP(extradata->dispositionnotificationto);
1236 MEMBDUP(extradata->returnreceiptto);
1237 MEMBDUP(extradata->partial_recv);
1238 MEMBDUP(extradata->account_server);
1239 MEMBDUP(extradata->account_login);
1240 MEMBDUP(extradata->list_post);
1241 MEMBDUP(extradata->list_subscribe);
1242 MEMBDUP(extradata->list_unsubscribe);
1243 MEMBDUP(extradata->list_help);
1244 MEMBDUP(extradata->list_archive);
1245 MEMBDUP(extradata->list_owner);
1248 refs = msginfo->references;
1249 for (refs = msginfo->references; refs != NULL; refs = refs->next) {
1250 newmsginfo->references = g_slist_prepend
1251 (newmsginfo->references, g_strdup(refs->data));
1253 newmsginfo->references = g_slist_reverse(newmsginfo->references);
1256 MEMBDUP(plaintext_file);
1261 MsgInfo *procmsg_msginfo_get_full_info(MsgInfo *msginfo)
1263 MsgInfo *full_msginfo;
1266 if (msginfo == NULL) return NULL;
1268 file = procmsg_get_message_file_path(msginfo);
1269 if (!file || !is_file_exist(file)) {
1271 file = procmsg_get_message_file(msginfo);
1273 if (!file || !is_file_exist(file)) {
1274 g_warning("procmsg_msginfo_get_full_info(): can't get message file.\n");
1278 full_msginfo = procheader_parse_file(file, msginfo->flags, TRUE, FALSE);
1280 if (!full_msginfo) return NULL;
1282 msginfo->total_size = full_msginfo->total_size;
1283 msginfo->planned_download = full_msginfo->planned_download;
1285 if (full_msginfo->extradata) {
1286 if (!msginfo->extradata)
1287 msginfo->extradata = g_new0(MsgInfoExtraData, 1);
1288 if (!msginfo->extradata->list_post)
1289 msginfo->extradata->list_post = g_strdup(full_msginfo->extradata->list_post);
1290 if (!msginfo->extradata->list_subscribe)
1291 msginfo->extradata->list_subscribe = g_strdup(full_msginfo->extradata->list_subscribe);
1292 if (!msginfo->extradata->list_unsubscribe)
1293 msginfo->extradata->list_unsubscribe = g_strdup(full_msginfo->extradata->list_unsubscribe);
1294 if (!msginfo->extradata->list_help)
1295 msginfo->extradata->list_help = g_strdup(full_msginfo->extradata->list_help);
1296 if (!msginfo->extradata->list_archive)
1297 msginfo->extradata->list_archive= g_strdup(full_msginfo->extradata->list_archive);
1298 if (!msginfo->extradata->list_owner)
1299 msginfo->extradata->list_owner = g_strdup(full_msginfo->extradata->list_owner);
1300 if (!msginfo->extradata->xface)
1301 msginfo->extradata->xface = g_strdup(full_msginfo->extradata->xface);
1302 if (!msginfo->extradata->face)
1303 msginfo->extradata->face = g_strdup(full_msginfo->extradata->face);
1304 if (!msginfo->extradata->dispositionnotificationto)
1305 msginfo->extradata->dispositionnotificationto =
1306 g_strdup(full_msginfo->extradata->dispositionnotificationto);
1307 if (!msginfo->extradata->returnreceiptto)
1308 msginfo->extradata->returnreceiptto = g_strdup
1309 (full_msginfo->extradata->returnreceiptto);
1310 if (!msginfo->extradata->partial_recv && full_msginfo->extradata->partial_recv)
1311 msginfo->extradata->partial_recv = g_strdup
1312 (full_msginfo->extradata->partial_recv);
1313 if (!msginfo->extradata->account_server && full_msginfo->extradata->account_server)
1314 msginfo->extradata->account_server = g_strdup
1315 (full_msginfo->extradata->account_server);
1316 if (!msginfo->extradata->account_login && full_msginfo->extradata->account_login)
1317 msginfo->extradata->account_login = g_strdup
1318 (full_msginfo->extradata->account_login);
1320 procmsg_msginfo_free(full_msginfo);
1322 return procmsg_msginfo_new_ref(msginfo);
1325 void procmsg_msginfo_free(MsgInfo *msginfo)
1327 if (msginfo == NULL) return;
1330 if (msginfo->refcnt > 0)
1333 if (msginfo->to_folder) {
1334 msginfo->to_folder->op_count--;
1335 folder_item_update(msginfo->to_folder, F_ITEM_UPDATE_MSGCNT);
1338 g_free(msginfo->fromspace);
1340 g_free(msginfo->fromname);
1342 g_free(msginfo->date);
1343 g_free(msginfo->from);
1344 g_free(msginfo->to);
1345 g_free(msginfo->cc);
1346 g_free(msginfo->newsgroups);
1347 g_free(msginfo->subject);
1348 g_free(msginfo->msgid);
1349 g_free(msginfo->inreplyto);
1350 g_free(msginfo->xref);
1352 if (msginfo->extradata) {
1353 g_free(msginfo->extradata->returnreceiptto);
1354 g_free(msginfo->extradata->dispositionnotificationto);
1355 g_free(msginfo->extradata->xface);
1356 g_free(msginfo->extradata->face);
1357 g_free(msginfo->extradata->list_post);
1358 g_free(msginfo->extradata->list_subscribe);
1359 g_free(msginfo->extradata->list_unsubscribe);
1360 g_free(msginfo->extradata->list_help);
1361 g_free(msginfo->extradata->list_archive);
1362 g_free(msginfo->extradata->list_owner);
1363 g_free(msginfo->extradata->partial_recv);
1364 g_free(msginfo->extradata->account_server);
1365 g_free(msginfo->extradata->account_login);
1366 g_free(msginfo->extradata);
1368 slist_free_strings(msginfo->references);
1369 g_slist_free(msginfo->references);
1371 g_free(msginfo->plaintext_file);
1376 guint procmsg_msginfo_memusage(MsgInfo *msginfo)
1381 memusage += sizeof(MsgInfo);
1382 if (msginfo->fromname)
1383 memusage += strlen(msginfo->fromname);
1385 memusage += strlen(msginfo->date);
1387 memusage += strlen(msginfo->from);
1389 memusage += strlen(msginfo->to);
1391 memusage += strlen(msginfo->cc);
1392 if (msginfo->newsgroups)
1393 memusage += strlen(msginfo->newsgroups);
1394 if (msginfo->subject)
1395 memusage += strlen(msginfo->subject);
1397 memusage += strlen(msginfo->msgid);
1398 if (msginfo->inreplyto)
1399 memusage += strlen(msginfo->inreplyto);
1400 for (refs = msginfo->references; refs; refs=refs->next) {
1401 gchar *r = (gchar *)refs->data;
1402 memusage += r?strlen(r):0;
1404 if (msginfo->fromspace)
1405 memusage += strlen(msginfo->fromspace);
1407 if (msginfo->extradata) {
1408 memusage += sizeof(MsgInfoExtraData);
1409 if (msginfo->extradata->xface)
1410 memusage += strlen(msginfo->extradata->xface);
1411 if (msginfo->extradata->face)
1412 memusage += strlen(msginfo->extradata->face);
1413 if (msginfo->extradata->dispositionnotificationto)
1414 memusage += strlen(msginfo->extradata->dispositionnotificationto);
1415 if (msginfo->extradata->returnreceiptto)
1416 memusage += strlen(msginfo->extradata->returnreceiptto);
1418 if (msginfo->extradata->partial_recv)
1419 memusage += strlen(msginfo->extradata->partial_recv);
1420 if (msginfo->extradata->account_server)
1421 memusage += strlen(msginfo->extradata->account_server);
1422 if (msginfo->extradata->account_login)
1423 memusage += strlen(msginfo->extradata->account_login);
1425 if (msginfo->extradata->list_post)
1426 memusage += strlen(msginfo->extradata->list_post);
1427 if (msginfo->extradata->list_subscribe)
1428 memusage += strlen(msginfo->extradata->list_subscribe);
1429 if (msginfo->extradata->list_unsubscribe)
1430 memusage += strlen(msginfo->extradata->list_unsubscribe);
1431 if (msginfo->extradata->list_help)
1432 memusage += strlen(msginfo->extradata->list_help);
1433 if (msginfo->extradata->list_archive)
1434 memusage += strlen(msginfo->extradata->list_archive);
1435 if (msginfo->extradata->list_owner)
1436 memusage += strlen(msginfo->extradata->list_owner);
1441 gint procmsg_cmp_msgnum_for_sort(gconstpointer a, gconstpointer b)
1443 const MsgInfo *msginfo1 = a;
1444 const MsgInfo *msginfo2 = b;
1451 return msginfo1->msgnum - msginfo2->msgnum;
1454 static gint procmsg_send_message_queue_full(const gchar *file, gboolean keep_session, gchar **errstr,
1455 FolderItem *queue, gint msgnum)
1457 static HeaderEntry qentry[] = {{"S:", NULL, FALSE},
1458 {"SSV:", NULL, FALSE},
1459 {"R:", NULL, FALSE},
1460 {"NG:", NULL, FALSE},
1461 {"MAID:", NULL, FALSE},
1462 {"NAID:", NULL, FALSE},
1463 {"SCF:", NULL, FALSE},
1464 {"RMID:", NULL, FALSE},
1465 {"FMID:", NULL, FALSE},
1466 {"X-Sylpheed-Privacy-System:", NULL, FALSE},
1467 {"X-Sylpheed-Encrypt:", NULL, FALSE},
1468 {"X-Sylpheed-Encrypt-Data:", NULL, FALSE},
1469 {"X-Sylpheed-End-Special-Headers:", NULL, FALSE},
1470 {NULL, NULL, FALSE}};
1473 gint mailval = 0, newsval = 0;
1475 gchar *smtpserver = NULL;
1476 GSList *to_list = NULL;
1477 GSList *newsgroup_list = NULL;
1478 gchar *savecopyfolder = NULL;
1479 gchar *replymessageid = NULL;
1480 gchar *fwdmessageid = NULL;
1481 gchar *privacy_system = NULL;
1482 gboolean encrypt = FALSE;
1483 gchar *encrypt_data = NULL;
1484 gchar buf[BUFFSIZE];
1486 PrefsAccount *mailac = NULL, *newsac = NULL;
1487 gboolean save_clear_text = TRUE;
1488 gchar *tmp_enc_file = NULL;
1492 g_return_val_if_fail(file != NULL, -1);
1494 if ((fp = g_fopen(file, "rb")) == NULL) {
1495 FILE_OP_ERROR(file, "fopen");
1497 if (*errstr) g_free(*errstr);
1498 *errstr = g_strdup_printf(_("Couldn't open file %s."), file);
1503 while ((hnum = procheader_get_one_field(buf, sizeof(buf), fp, qentry))
1505 gchar *p = buf + strlen(qentry[hnum].name);
1513 if (smtpserver == NULL)
1514 smtpserver = g_strdup(p);
1517 to_list = address_list_append(to_list, p);
1520 newsgroup_list = newsgroup_list_append(newsgroup_list, p);
1522 case Q_MAIL_ACCOUNT_ID:
1523 mailac = account_find_from_id(atoi(p));
1525 case Q_NEWS_ACCOUNT_ID:
1526 newsac = account_find_from_id(atoi(p));
1528 case Q_SAVE_COPY_FOLDER:
1529 if (savecopyfolder == NULL)
1530 savecopyfolder = g_strdup(p);
1532 case Q_REPLY_MESSAGE_ID:
1533 if (replymessageid == NULL)
1534 replymessageid = g_strdup(p);
1536 case Q_FWD_MESSAGE_ID:
1537 if (fwdmessageid == NULL)
1538 fwdmessageid = g_strdup(p);
1540 case Q_PRIVACY_SYSTEM:
1541 if (privacy_system == NULL)
1542 privacy_system = g_strdup(p);
1548 case Q_ENCRYPT_DATA:
1549 if (encrypt_data == NULL)
1550 encrypt_data = g_strdup(p);
1552 case Q_SYLPHEED_HDRS:
1553 /* end of special headers reached */
1554 goto send_mail; /* can't "break;break;" */
1558 filepos = ftell(fp);
1563 if (mailac && mailac->save_encrypted_as_clear_text
1564 && !mailac->encrypt_to_self)
1565 save_clear_text = TRUE;
1567 save_clear_text = FALSE;
1572 mimeinfo = procmime_scan_queue_file(file);
1573 if (!privacy_encrypt(privacy_system, mimeinfo, encrypt_data)
1574 || (fp = my_tmpfile()) == NULL
1575 || procmime_write_mimeinfo(mimeinfo, fp) < 0) {
1578 procmime_mimeinfo_free_all(mimeinfo);
1581 slist_free_strings(to_list);
1582 g_slist_free(to_list);
1583 slist_free_strings(newsgroup_list);
1584 g_slist_free(newsgroup_list);
1585 g_free(savecopyfolder);
1586 g_free(replymessageid);
1587 g_free(fwdmessageid);
1588 g_free(privacy_system);
1589 g_free(encrypt_data);
1591 if (*errstr) g_free(*errstr);
1592 *errstr = g_strdup_printf(_("Couldn't encrypt the email: %s"),
1593 privacy_get_error());
1599 if (!save_clear_text) {
1600 gchar *content = NULL;
1601 FILE *tmpfp = get_tmpfile_in_dir(get_mime_tmp_dir(), &tmp_enc_file);
1605 content = file_read_stream_to_str(fp);
1608 str_write_to_file(content, tmp_enc_file);
1611 g_warning("couldn't get tempfile\n");
1615 procmime_mimeinfo_free_all(mimeinfo);
1621 debug_print("Sending message by mail\n");
1624 if (*errstr) g_free(*errstr);
1625 *errstr = g_strdup_printf(_("Queued message header is broken."));
1628 } else if (mailac && mailac->use_mail_command &&
1629 mailac->mail_command && (* mailac->mail_command)) {
1630 mailval = send_message_local(mailac->mail_command, fp);
1634 mailac = account_find_from_smtp_server(from, smtpserver);
1636 g_warning("Account not found. "
1637 "Using current account...\n");
1638 mailac = cur_account;
1643 mailval = send_message_smtp_full(mailac, to_list, fp, keep_session);
1644 if (mailval == -1 && errstr) {
1645 if (*errstr) g_free(*errstr);
1646 *errstr = g_strdup_printf(_("An error happened during SMTP session."));
1649 PrefsAccount tmp_ac;
1651 g_warning("Account not found.\n");
1653 memset(&tmp_ac, 0, sizeof(PrefsAccount));
1654 tmp_ac.address = from;
1655 tmp_ac.smtp_server = smtpserver;
1656 tmp_ac.smtpport = SMTP_PORT;
1657 mailval = send_message_smtp(&tmp_ac, to_list, fp);
1658 if (mailval == -1 && errstr) {
1659 if (*errstr) g_free(*errstr);
1660 *errstr = g_strdup_printf(_("No specific account has been found to "
1661 "send, and an error happened during SMTP session."));
1665 } else if (!to_list && !newsgroup_list) {
1667 if (*errstr) g_free(*errstr);
1668 *errstr = g_strdup(_("Couldn't determine sending informations. "
1669 "Maybe the email hasn't been generated by Sylpheed-Claws."));
1674 fseek(fp, filepos, SEEK_SET);
1675 if (newsgroup_list && (mailval == 0)) {
1680 /* write to temporary file */
1681 tmp = g_strdup_printf("%s%ctmp%d", g_get_tmp_dir(),
1682 G_DIR_SEPARATOR, (gint)file);
1683 if ((tmpfp = g_fopen(tmp, "wb")) == NULL) {
1684 FILE_OP_ERROR(tmp, "fopen");
1686 alertpanel_error(_("Could not create temporary file for news sending."));
1688 if (change_file_mode_rw(tmpfp, tmp) < 0) {
1689 FILE_OP_ERROR(tmp, "chmod");
1690 g_warning("can't change file mode\n");
1693 while ((newsval == 0) && fgets(buf, sizeof(buf), fp) != NULL) {
1694 if (fputs(buf, tmpfp) == EOF) {
1695 FILE_OP_ERROR(tmp, "fputs");
1698 if (*errstr) g_free(*errstr);
1699 *errstr = g_strdup_printf(_("Error when writing temporary file for news sending."));
1706 debug_print("Sending message by news\n");
1708 folder = FOLDER(newsac->folder);
1710 newsval = news_post(folder, tmp);
1711 if (newsval < 0 && errstr) {
1712 if (*errstr) g_free(*errstr);
1713 *errstr = g_strdup_printf(_("Error occurred while posting the message to %s."),
1714 newsac->nntp_server);
1724 /* save message to outbox */
1725 if (mailval == 0 && newsval == 0 && savecopyfolder) {
1728 debug_print("saving sent message...\n");
1730 outbox = folder_find_item_from_identifier(savecopyfolder);
1732 outbox = folder_get_default_outbox();
1734 if (save_clear_text || tmp_enc_file == NULL) {
1735 gboolean saved = FALSE;
1736 if (queue && msgnum > 0) {
1737 MsgInfo *queued_mail = folder_item_get_msginfo(queue, msgnum);
1738 if (folder_item_copy_msg(outbox, queued_mail) >= 0)
1740 procmsg_msginfo_free(queued_mail);
1743 procmsg_save_to_outbox(outbox, file, TRUE);
1745 procmsg_save_to_outbox(outbox, tmp_enc_file, FALSE);
1749 if (tmp_enc_file != NULL) {
1750 g_unlink(tmp_enc_file);
1752 tmp_enc_file = NULL;
1755 if (replymessageid != NULL || fwdmessageid != NULL) {
1759 if (replymessageid != NULL)
1760 tokens = g_strsplit(replymessageid, "\t", 0);
1762 tokens = g_strsplit(fwdmessageid, "\t", 0);
1763 item = folder_find_item_from_identifier(tokens[0]);
1765 /* check if queued message has valid folder and message id */
1766 if (item != NULL && tokens[2] != NULL) {
1769 msginfo = folder_item_get_msginfo(item, atoi(tokens[1]));
1771 /* check if referring message exists and has a message id */
1772 if ((msginfo != NULL) &&
1773 (msginfo->msgid != NULL) &&
1774 (strcmp(msginfo->msgid, tokens[2]) != 0)) {
1775 procmsg_msginfo_free(msginfo);
1779 if (msginfo == NULL) {
1780 msginfo = folder_item_get_msginfo_by_msgid(item, tokens[2]);
1783 if (msginfo != NULL) {
1784 if (replymessageid != NULL) {
1785 procmsg_msginfo_unset_flags(msginfo, MSG_FORWARDED, 0);
1786 procmsg_msginfo_set_flags(msginfo, MSG_REPLIED, 0);
1788 procmsg_msginfo_unset_flags(msginfo, MSG_REPLIED, 0);
1789 procmsg_msginfo_set_flags(msginfo, MSG_FORWARDED, 0);
1791 procmsg_msginfo_free(msginfo);
1799 slist_free_strings(to_list);
1800 g_slist_free(to_list);
1801 slist_free_strings(newsgroup_list);
1802 g_slist_free(newsgroup_list);
1803 g_free(savecopyfolder);
1804 g_free(replymessageid);
1805 g_free(fwdmessageid);
1806 g_free(privacy_system);
1807 g_free(encrypt_data);
1809 return (newsval != 0 ? newsval : mailval);
1812 gint procmsg_send_message_queue(const gchar *file, gchar **errstr, FolderItem *queue, gint msgnum)
1814 gint result = procmsg_send_message_queue_full(file, FALSE, errstr, queue, msgnum);
1815 toolbar_main_set_sensitive(mainwindow_get_mainwindow());
1819 static void update_folder_msg_counts(FolderItem *item, MsgInfo *msginfo, MsgPermFlags old_flags)
1821 MsgPermFlags new_flags = msginfo->flags.perm_flags;
1824 if (!(old_flags & MSG_NEW) && (new_flags & MSG_NEW)) {
1828 if ((old_flags & MSG_NEW) && !(new_flags & MSG_NEW)) {
1833 if (!(old_flags & MSG_UNREAD) && (new_flags & MSG_UNREAD)) {
1834 item->unread_msgs++;
1835 if (procmsg_msg_has_marked_parent(msginfo))
1836 item->unreadmarked_msgs++;
1839 if ((old_flags & MSG_UNREAD) && !(new_flags & MSG_UNREAD)) {
1840 item->unread_msgs--;
1841 if (procmsg_msg_has_marked_parent(msginfo))
1842 item->unreadmarked_msgs--;
1846 if (!(old_flags & MSG_MARKED) && (new_flags & MSG_MARKED)) {
1847 procmsg_update_unread_children(msginfo, TRUE);
1848 item->marked_msgs++;
1851 if ((old_flags & MSG_MARKED) && !(new_flags & MSG_MARKED)) {
1852 procmsg_update_unread_children(msginfo, FALSE);
1853 item->marked_msgs--;
1857 void procmsg_msginfo_set_flags(MsgInfo *msginfo, MsgPermFlags perm_flags, MsgTmpFlags tmp_flags)
1860 MsgInfoUpdate msginfo_update;
1861 MsgPermFlags perm_flags_new, perm_flags_old;
1862 MsgTmpFlags tmp_flags_old;
1864 g_return_if_fail(msginfo != NULL);
1865 item = msginfo->folder;
1866 g_return_if_fail(item != NULL);
1868 debug_print("Setting flags for message %d in folder %s\n", msginfo->msgnum, item->path);
1870 /* Perm Flags handling */
1871 perm_flags_old = msginfo->flags.perm_flags;
1872 perm_flags_new = msginfo->flags.perm_flags | perm_flags;
1873 if ((perm_flags & MSG_IGNORE_THREAD) || (perm_flags_old & MSG_IGNORE_THREAD)) {
1874 perm_flags_new &= ~(MSG_NEW | MSG_UNREAD);
1877 if (perm_flags_old != perm_flags_new) {
1878 folder_item_change_msg_flags(msginfo->folder, msginfo, perm_flags_new);
1880 update_folder_msg_counts(item, msginfo, perm_flags_old);
1884 /* Tmp flags handling */
1885 tmp_flags_old = msginfo->flags.tmp_flags;
1886 msginfo->flags.tmp_flags |= tmp_flags;
1888 /* update notification */
1889 if ((perm_flags_old != perm_flags_new) || (tmp_flags_old != msginfo->flags.tmp_flags)) {
1890 msginfo_update.msginfo = msginfo;
1891 msginfo_update.flags = MSGINFO_UPDATE_FLAGS;
1892 hooks_invoke(MSGINFO_UPDATE_HOOKLIST, &msginfo_update);
1893 folder_item_update(msginfo->folder, F_ITEM_UPDATE_MSGCNT);
1897 void procmsg_msginfo_unset_flags(MsgInfo *msginfo, MsgPermFlags perm_flags, MsgTmpFlags tmp_flags)
1900 MsgInfoUpdate msginfo_update;
1901 MsgPermFlags perm_flags_new, perm_flags_old;
1902 MsgTmpFlags tmp_flags_old;
1904 g_return_if_fail(msginfo != NULL);
1905 item = msginfo->folder;
1906 g_return_if_fail(item != NULL);
1908 debug_print("Unsetting flags for message %d in folder %s\n", msginfo->msgnum, item->path);
1910 /* Perm Flags handling */
1911 perm_flags_old = msginfo->flags.perm_flags;
1912 perm_flags_new = msginfo->flags.perm_flags & ~perm_flags;
1914 if (perm_flags_old != perm_flags_new) {
1915 folder_item_change_msg_flags(msginfo->folder, msginfo, perm_flags_new);
1917 update_folder_msg_counts(item, msginfo, perm_flags_old);
1920 /* Tmp flags hanlding */
1921 tmp_flags_old = msginfo->flags.tmp_flags;
1922 msginfo->flags.tmp_flags &= ~tmp_flags;
1924 /* update notification */
1925 if ((perm_flags_old != perm_flags_new) || (tmp_flags_old != msginfo->flags.tmp_flags)) {
1926 msginfo_update.msginfo = msginfo;
1927 msginfo_update.flags = MSGINFO_UPDATE_FLAGS;
1928 hooks_invoke(MSGINFO_UPDATE_HOOKLIST, &msginfo_update);
1929 folder_item_update(msginfo->folder, F_ITEM_UPDATE_MSGCNT);
1933 void procmsg_msginfo_change_flags(MsgInfo *msginfo,
1934 MsgPermFlags add_perm_flags, MsgTmpFlags add_tmp_flags,
1935 MsgPermFlags rem_perm_flags, MsgTmpFlags rem_tmp_flags)
1938 MsgInfoUpdate msginfo_update;
1939 MsgPermFlags perm_flags_new, perm_flags_old;
1940 MsgTmpFlags tmp_flags_old;
1942 g_return_if_fail(msginfo != NULL);
1943 item = msginfo->folder;
1944 g_return_if_fail(item != NULL);
1946 debug_print("Changing flags for message %d in folder %s\n", msginfo->msgnum, item->path);
1948 /* Perm Flags handling */
1949 perm_flags_old = msginfo->flags.perm_flags;
1950 perm_flags_new = (msginfo->flags.perm_flags & ~rem_perm_flags) | add_perm_flags;
1951 if ((add_perm_flags & MSG_IGNORE_THREAD) || (perm_flags_old & MSG_IGNORE_THREAD)) {
1952 perm_flags_new &= ~(MSG_NEW | MSG_UNREAD);
1955 if (perm_flags_old != perm_flags_new) {
1956 folder_item_change_msg_flags(msginfo->folder, msginfo, perm_flags_new);
1958 update_folder_msg_counts(item, msginfo, perm_flags_old);
1962 /* Tmp flags handling */
1963 tmp_flags_old = msginfo->flags.tmp_flags;
1964 msginfo->flags.tmp_flags &= ~rem_tmp_flags;
1965 msginfo->flags.tmp_flags |= add_tmp_flags;
1967 /* update notification */
1968 if ((perm_flags_old != perm_flags_new) || (tmp_flags_old != msginfo->flags.tmp_flags)) {
1969 msginfo_update.msginfo = msginfo;
1970 msginfo_update.flags = MSGINFO_UPDATE_FLAGS;
1971 hooks_invoke(MSGINFO_UPDATE_HOOKLIST, &msginfo_update);
1972 folder_item_update(msginfo->folder, F_ITEM_UPDATE_MSGCNT);
1977 *\brief check for flags (e.g. mark) in prior msgs of current thread
1979 *\param info Current message
1980 *\param perm_flags Flags to be checked
1981 *\param parentmsgs Hash of prior msgs to avoid loops
1983 *\return gboolean TRUE if perm_flags are found
1985 gboolean procmsg_msg_has_flagged_parent_real(MsgInfo *info,
1986 MsgPermFlags perm_flags, GHashTable *parentmsgs)
1990 g_return_val_if_fail(info != NULL, FALSE);
1992 if (info != NULL && info->folder != NULL && info->inreplyto != NULL) {
1993 tmp = folder_item_get_msginfo_by_msgid(info->folder,
1995 if (tmp && (tmp->flags.perm_flags & perm_flags)) {
1996 procmsg_msginfo_free(tmp);
1998 } else if (tmp != NULL) {
2001 if (g_hash_table_lookup(parentmsgs, info)) {
2002 debug_print("loop detected: %d\n",
2006 g_hash_table_insert(parentmsgs, info, "1");
2007 result = procmsg_msg_has_flagged_parent_real(
2008 tmp, perm_flags, parentmsgs);
2010 procmsg_msginfo_free(tmp);
2020 *\brief Callback for cleaning up hash of parentmsgs
2022 gboolean parentmsgs_hash_remove(gpointer key,
2030 *\brief Set up list of parentmsgs
2031 * See procmsg_msg_has_flagged_parent_real()
2033 gboolean procmsg_msg_has_flagged_parent(MsgInfo *info, MsgPermFlags perm_flags)
2036 GHashTable *parentmsgs = g_hash_table_new(NULL, NULL);
2038 result = procmsg_msg_has_flagged_parent_real(info, perm_flags, parentmsgs);
2039 g_hash_table_foreach_remove(parentmsgs, parentmsgs_hash_remove, NULL);
2040 g_hash_table_destroy(parentmsgs);
2045 *\brief Check if msgs prior in thread are marked
2046 * See procmsg_msg_has_flagged_parent_real()
2048 gboolean procmsg_msg_has_marked_parent(MsgInfo *info)
2050 return procmsg_msg_has_flagged_parent(info, MSG_MARKED);
2054 GSList *procmsg_find_children_func(MsgInfo *info,
2055 GSList *children, GSList *all)
2059 g_return_val_if_fail(info!=NULL, children);
2060 if (info->msgid == NULL)
2063 for (cur = all; cur != NULL; cur = g_slist_next(cur)) {
2064 MsgInfo *tmp = (MsgInfo *)cur->data;
2065 if (tmp->inreplyto && !strcmp(tmp->inreplyto, info->msgid)) {
2066 /* Check if message is already in the list */
2067 if ((children == NULL) ||
2068 (g_slist_index(children, tmp) == -1)) {
2069 children = g_slist_prepend(children,
2070 procmsg_msginfo_new_ref(tmp));
2071 children = procmsg_find_children_func(tmp,
2080 GSList *procmsg_find_children (MsgInfo *info)
2085 g_return_val_if_fail(info!=NULL, NULL);
2086 all = folder_item_get_msg_list(info->folder);
2087 children = procmsg_find_children_func(info, NULL, all);
2088 if (children != NULL) {
2089 for (cur = all; cur != NULL; cur = g_slist_next(cur)) {
2090 /* this will not free the used pointers
2091 created with procmsg_msginfo_new_ref */
2092 procmsg_msginfo_free((MsgInfo *)cur->data);
2100 void procmsg_update_unread_children(MsgInfo *info, gboolean newly_marked)
2102 GSList *children = procmsg_find_children(info);
2104 for (cur = children; cur != NULL; cur = g_slist_next(cur)) {
2105 MsgInfo *tmp = (MsgInfo *)cur->data;
2106 if(MSG_IS_UNREAD(tmp->flags) && !MSG_IS_IGNORE_THREAD(tmp->flags)) {
2108 info->folder->unreadmarked_msgs++;
2110 info->folder->unreadmarked_msgs--;
2111 folder_item_update(info->folder, F_ITEM_UPDATE_MSGCNT);
2113 procmsg_msginfo_free(tmp);
2115 g_slist_free(children);
2119 * Set the destination folder for a copy or move operation
2121 * \param msginfo The message which's destination folder is changed
2122 * \param to_folder The destination folder for the operation
2124 void procmsg_msginfo_set_to_folder(MsgInfo *msginfo, FolderItem *to_folder)
2126 if(msginfo->to_folder != NULL) {
2127 msginfo->to_folder->op_count--;
2128 folder_item_update(msginfo->to_folder, F_ITEM_UPDATE_MSGCNT);
2130 msginfo->to_folder = to_folder;
2131 if(to_folder != NULL) {
2132 to_folder->op_count++;
2133 folder_item_update(msginfo->to_folder, F_ITEM_UPDATE_MSGCNT);
2138 * Apply filtering actions to the msginfo
2140 * \param msginfo The MsgInfo describing the message that should be filtered
2141 * \return TRUE if the message was moved and MsgInfo is now invalid,
2144 gboolean procmsg_msginfo_filter(MsgInfo *msginfo, PrefsAccount* ac_prefs)
2146 MailFilteringData mail_filtering_data;
2148 mail_filtering_data.msginfo = msginfo;
2149 mail_filtering_data.msglist = NULL;
2150 mail_filtering_data.filtered = NULL;
2151 mail_filtering_data.unfiltered = NULL;
2152 if (hooks_invoke(MAIL_FILTERING_HOOKLIST, &mail_filtering_data)) {
2156 /* filter if enabled in prefs or move to inbox if not */
2157 if((filtering_rules != NULL) &&
2158 filter_message_by_msginfo(filtering_rules, msginfo, ac_prefs)) {
2165 void procmsg_msglist_filter(GSList *list, PrefsAccount *ac,
2166 GSList **filtered, GSList **unfiltered,
2169 GSList *cur, *to_do = NULL;
2170 gint total = 0, curnum = 0;
2171 MailFilteringData mail_filtering_data;
2173 g_return_if_fail(filtered != NULL);
2174 g_return_if_fail(unfiltered != NULL);
2182 total = g_slist_length(list);
2186 *unfiltered = g_slist_copy(list);
2190 statusbar_print_all(_("Filtering messages...\n"));
2192 mail_filtering_data.msginfo = NULL;
2193 mail_filtering_data.msglist = list;
2194 mail_filtering_data.filtered = NULL;
2195 mail_filtering_data.unfiltered = NULL;
2197 hooks_invoke(MAIL_LISTFILTERING_HOOKLIST, &mail_filtering_data);
2199 if (mail_filtering_data.filtered == NULL &&
2200 mail_filtering_data.unfiltered == NULL) {
2201 /* nothing happened */
2202 debug_print(MAIL_LISTFILTERING_HOOKLIST " did nothing. filtering whole list normally.\n");
2205 if (mail_filtering_data.filtered != NULL) {
2206 /* keep track of what's been filtered by the hooks */
2207 debug_print(MAIL_LISTFILTERING_HOOKLIST " filtered some stuff. total %d filtered %d unfilt %d.\n",
2208 g_slist_length(list),
2209 g_slist_length(mail_filtering_data.filtered),
2210 g_slist_length(mail_filtering_data.unfiltered));
2212 *filtered = g_slist_copy(mail_filtering_data.filtered);
2214 if (mail_filtering_data.unfiltered != NULL) {
2215 /* what the hooks didn't handle will go in filtered or
2216 * unfiltered in the next loop */
2217 debug_print(MAIL_LISTFILTERING_HOOKLIST " left unfiltered stuff. total %d filtered %d unfilt %d.\n",
2218 g_slist_length(list),
2219 g_slist_length(mail_filtering_data.filtered),
2220 g_slist_length(mail_filtering_data.unfiltered));
2221 to_do = mail_filtering_data.unfiltered;
2224 for (cur = to_do; cur; cur = cur->next) {
2225 MsgInfo *info = (MsgInfo *)cur->data;
2226 if (procmsg_msginfo_filter(info, ac))
2227 *filtered = g_slist_prepend(*filtered, info);
2229 *unfiltered = g_slist_prepend(*unfiltered, info);
2230 statusbar_progress_all(curnum++, total, prefs_common.statusbar_update_step);
2233 g_slist_free(mail_filtering_data.filtered);
2234 g_slist_free(mail_filtering_data.unfiltered);
2236 *filtered = g_slist_reverse(*filtered);
2237 *unfiltered = g_slist_reverse(*unfiltered);
2239 statusbar_progress_all(0,0,0);
2240 statusbar_pop_all();
2243 MsgInfo *procmsg_msginfo_new_from_mimeinfo(MsgInfo *src_msginfo, MimeInfo *mimeinfo)
2245 MsgInfo *tmp_msginfo = NULL;
2246 MsgFlags flags = {0, 0};
2247 gchar *tmpfile = get_tmp_file();
2248 FILE *fp = g_fopen(tmpfile, "wb");
2250 if (!mimeinfo || mimeinfo->type != MIMETYPE_MESSAGE ||
2251 g_ascii_strcasecmp(mimeinfo->subtype, "rfc822")) {
2252 g_warning("procmsg_msginfo_new_from_mimeinfo(): unsuitable mimeinfo");
2259 if (fp && procmime_write_mimeinfo(mimeinfo, fp) >= 0) {
2262 tmp_msginfo = procheader_parse_file(
2269 if (tmp_msginfo != NULL) {
2271 tmp_msginfo->folder = src_msginfo->folder;
2272 tmp_msginfo->plaintext_file = g_strdup(tmpfile);
2274 g_warning("procmsg_msginfo_new_from_mimeinfo(): Can't generate new msginfo");
2282 static GSList *spam_learners = NULL;
2284 void procmsg_register_spam_learner (int (*learn_func)(MsgInfo *info, GSList *list, gboolean spam))
2286 if (!g_slist_find(spam_learners, learn_func))
2287 spam_learners = g_slist_append(spam_learners, learn_func);
2288 if (mainwindow_get_mainwindow()) {
2289 main_window_set_menu_sensitive(mainwindow_get_mainwindow());
2290 summary_set_menu_sensitive(
2291 mainwindow_get_mainwindow()->summaryview);
2292 toolbar_main_set_sensitive(mainwindow_get_mainwindow());
2296 void procmsg_unregister_spam_learner (int (*learn_func)(MsgInfo *info, GSList *list, gboolean spam))
2298 spam_learners = g_slist_remove(spam_learners, learn_func);
2299 if (mainwindow_get_mainwindow()) {
2300 main_window_set_menu_sensitive(mainwindow_get_mainwindow());
2301 summary_set_menu_sensitive(
2302 mainwindow_get_mainwindow()->summaryview);
2303 toolbar_main_set_sensitive(mainwindow_get_mainwindow());
2307 gboolean procmsg_spam_can_learn(void)
2309 return g_slist_length(spam_learners) > 0;
2312 int procmsg_spam_learner_learn (MsgInfo *info, GSList *list, gboolean spam)
2314 GSList *cur = spam_learners;
2316 for (; cur; cur = cur->next) {
2317 int ((*func)(MsgInfo *info, GSList *list, gboolean spam)) = cur->data;
2318 ret |= func(info, list, spam);
2323 static gchar *spam_folder_item = NULL;
2324 void procmsg_spam_set_folder (const char *item_identifier)
2326 g_free(spam_folder_item);
2327 if (item_identifier)
2328 spam_folder_item = g_strdup(item_identifier);
2330 spam_folder_item = NULL;
2333 FolderItem *procmsg_spam_get_folder (void)
2335 FolderItem *item = spam_folder_item ? folder_find_item_from_identifier(spam_folder_item) : NULL;
2336 return item ? item : folder_get_default_trash();
2339 static void item_has_queued_mails(FolderItem *item, gpointer data)
2341 gboolean *result = (gboolean *)data;
2342 if (*result == TRUE)
2344 if (folder_has_parent_of_type(item, F_QUEUE) && item->total_msgs > 0)
2348 gboolean procmsg_have_queued_mails_fast (void)
2350 gboolean result = FALSE;
2351 folder_func_to_all_folders(item_has_queued_mails, &result);