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);
59 Q_MAIL_ACCOUNT_ID = 4,
60 Q_NEWS_ACCOUNT_ID = 5,
61 Q_SAVE_COPY_FOLDER = 6,
62 Q_REPLY_MESSAGE_ID = 7,
70 GHashTable *procmsg_msg_hash_table_create(GSList *mlist)
72 GHashTable *msg_table;
74 if (mlist == NULL) return NULL;
76 msg_table = g_hash_table_new(NULL, g_direct_equal);
77 procmsg_msg_hash_table_append(msg_table, mlist);
82 void procmsg_msg_hash_table_append(GHashTable *msg_table, GSList *mlist)
87 if (msg_table == NULL || mlist == NULL) return;
89 for (cur = mlist; cur != NULL; cur = cur->next) {
90 msginfo = (MsgInfo *)cur->data;
92 g_hash_table_insert(msg_table,
93 GUINT_TO_POINTER(msginfo->msgnum),
98 GHashTable *procmsg_to_folder_hash_table_create(GSList *mlist)
100 GHashTable *msg_table;
104 if (mlist == NULL) return NULL;
106 msg_table = g_hash_table_new(NULL, g_direct_equal);
108 for (cur = mlist; cur != NULL; cur = cur->next) {
109 msginfo = (MsgInfo *)cur->data;
110 g_hash_table_insert(msg_table, msginfo->to_folder, msginfo);
116 gint procmsg_get_last_num_in_msg_list(GSList *mlist)
122 for (cur = mlist; cur != NULL; cur = cur->next) {
123 msginfo = (MsgInfo *)cur->data;
124 if (msginfo && msginfo->msgnum > last)
125 last = msginfo->msgnum;
131 void procmsg_msg_list_free(GSList *mlist)
136 for (cur = mlist; cur != NULL; cur = cur->next) {
137 msginfo = (MsgInfo *)cur->data;
138 procmsg_msginfo_free(msginfo);
152 /* CLAWS subject threading:
154 in the first round it inserts subject lines in a
155 relation (subject <-> node)
157 the second round finishes the threads by attaching
158 matching subject lines to the one found in the
159 relation. will use the oldest node with the same
160 subject that is not more then thread_by_subject_max_age
161 days old (see subject_relation_lookup)
164 static void subject_relation_insert(GRelation *relation, GNode *node)
169 g_return_if_fail(relation != NULL);
170 g_return_if_fail(node != NULL);
171 msginfo = (MsgInfo *) node->data;
172 g_return_if_fail(msginfo != NULL);
174 subject = msginfo->subject;
177 subject += subject_get_prefix_length(subject);
179 g_relation_insert(relation, subject, node);
182 static GNode *subject_relation_lookup(GRelation *relation, MsgInfo *msginfo)
189 g_return_val_if_fail(relation != NULL, NULL);
191 subject = msginfo->subject;
194 prefix_length = subject_get_prefix_length(subject);
195 if (prefix_length <= 0)
197 subject += prefix_length;
199 tuples = g_relation_select(relation, subject, 0);
203 if (tuples->len > 0) {
205 GNode *relation_node;
206 MsgInfo *relation_msginfo = NULL, *best_msginfo = NULL;
209 /* check all nodes with the same subject to find the best parent */
210 for (i = 0; i < tuples->len; i++) {
211 relation_node = (GNode *) g_tuples_index(tuples, i, 1);
212 relation_msginfo = (MsgInfo *) relation_node->data;
215 /* best node should be the oldest in the found nodes */
216 /* parent node must not be older then msginfo */
217 if ((relation_msginfo->date_t < msginfo->date_t) &&
218 ((best_msginfo == NULL) ||
219 (best_msginfo->date_t > relation_msginfo->date_t)))
222 /* parent node must not be more then thread_by_subject_max_age
223 days older then msginfo */
224 if (abs(difftime(msginfo->date_t, relation_msginfo->date_t)) >
225 prefs_common.thread_by_subject_max_age * 3600 * 24)
228 /* can add new tests for all matching
229 nodes found by subject */
232 node = relation_node;
233 best_msginfo = relation_msginfo;
238 g_tuples_destroy(tuples);
242 /* return the reversed thread tree */
243 GNode *procmsg_get_thread_tree(GSList *mlist)
245 GNode *root, *parent, *node, *next;
246 GHashTable *msgid_table;
247 GRelation *subject_relation = NULL;
251 START_TIMING("procmsg_get_thread_tree");
252 root = g_node_new(NULL);
253 msgid_table = g_hash_table_new(g_str_hash, g_str_equal);
255 if (prefs_common.thread_by_subject) {
256 subject_relation = g_relation_new(2);
257 g_relation_index(subject_relation, 0, g_str_hash, g_str_equal);
260 for (; mlist != NULL; mlist = mlist->next) {
261 msginfo = (MsgInfo *)mlist->data;
264 if (msginfo->inreplyto) {
265 parent = g_hash_table_lookup(msgid_table, msginfo->inreplyto);
266 if (parent == NULL) {
270 node = g_node_insert_data_before
271 (parent, parent == root ? parent->children : NULL,
273 if ((msgid = msginfo->msgid) && g_hash_table_lookup(msgid_table, msgid) == NULL)
274 g_hash_table_insert(msgid_table, (gchar *)msgid, node);
276 /* CLAWS: add subject to relation (without prefix) */
277 if (prefs_common.thread_by_subject) {
278 subject_relation_insert(subject_relation, node);
282 /* complete the unfinished threads */
283 for (node = root->children; node != NULL; ) {
285 msginfo = (MsgInfo *)node->data;
288 if (msginfo->inreplyto)
289 parent = g_hash_table_lookup(msgid_table, msginfo->inreplyto);
291 /* try looking for the indirect parent */
292 if (!parent && msginfo->references) {
293 for (reflist = msginfo->references;
294 reflist != NULL; reflist = reflist->next)
295 if ((parent = g_hash_table_lookup
296 (msgid_table, reflist->data)) != NULL)
300 /* node should not be the parent, and node should not
301 be an ancestor of parent (circular reference) */
302 if (parent && parent != node &&
303 !g_node_is_ancestor(node, parent)) {
306 (parent, parent->children, node);
312 if (prefs_common.thread_by_subject) {
313 START_TIMING("procmsg_get_thread_tree(1)");
314 for (node = root->children; node && node != NULL;) {
316 msginfo = (MsgInfo *) node->data;
318 parent = subject_relation_lookup(subject_relation, msginfo);
320 /* the node may already be threaded by IN-REPLY-TO, so go up
322 find the parent node */
323 if (parent != NULL) {
324 if (g_node_is_ancestor(node, parent))
332 g_node_append(parent, node);
340 if (prefs_common.thread_by_subject)
341 g_relation_destroy(subject_relation);
343 g_hash_table_destroy(msgid_table);
348 gint procmsg_move_messages(GSList *mlist)
350 GSList *cur, *movelist = NULL;
352 FolderItem *dest = NULL;
354 gboolean finished = TRUE;
355 if (!mlist) return 0;
357 folder_item_update_freeze();
360 for (cur = mlist; cur != NULL; cur = cur->next) {
361 msginfo = (MsgInfo *)cur->data;
362 if (!msginfo->to_folder) {
368 dest = msginfo->to_folder;
369 movelist = g_slist_prepend(movelist, msginfo);
370 } else if (dest == msginfo->to_folder) {
371 movelist = g_slist_prepend(movelist, msginfo);
375 procmsg_msginfo_set_to_folder(msginfo, NULL);
378 movelist = g_slist_reverse(movelist);
379 retval |= folder_item_move_msgs(dest, movelist);
380 g_slist_free(movelist);
383 if (finished == FALSE) {
389 folder_item_update_thaw();
393 void procmsg_copy_messages(GSList *mlist)
395 GSList *cur, *copylist = NULL;
397 FolderItem *dest = NULL;
398 gboolean finished = TRUE;
401 folder_item_update_freeze();
404 for (cur = mlist; cur != NULL; cur = cur->next) {
405 msginfo = (MsgInfo *)cur->data;
406 if (!msginfo->to_folder) {
412 dest = msginfo->to_folder;
413 copylist = g_slist_prepend(copylist, msginfo);
414 } else if (dest == msginfo->to_folder) {
415 copylist = g_slist_prepend(copylist, msginfo);
419 procmsg_msginfo_set_to_folder(msginfo, NULL);
422 copylist = g_slist_reverse(copylist);
423 folder_item_copy_msgs(dest, copylist);
424 g_slist_free(copylist);
427 if (finished == FALSE) {
433 folder_item_update_thaw();
436 gchar *procmsg_get_message_file_path(MsgInfo *msginfo)
440 g_return_val_if_fail(msginfo != NULL, NULL);
442 if (msginfo->plaintext_file)
443 file = g_strdup(msginfo->plaintext_file);
445 file = folder_item_fetch_msg(msginfo->folder, msginfo->msgnum);
451 gchar *procmsg_get_message_file(MsgInfo *msginfo)
453 gchar *filename = NULL;
455 g_return_val_if_fail(msginfo != NULL, NULL);
457 filename = folder_item_fetch_msg(msginfo->folder, msginfo->msgnum);
459 debug_print("can't fetch message %d\n", msginfo->msgnum);
464 gchar *procmsg_get_message_file_full(MsgInfo *msginfo, gboolean headers, gboolean body)
466 gchar *filename = NULL;
468 g_return_val_if_fail(msginfo != NULL, NULL);
470 filename = folder_item_fetch_msg_full(msginfo->folder, msginfo->msgnum,
473 debug_print("can't fetch message %d\n", msginfo->msgnum);
478 GSList *procmsg_get_message_file_list(GSList *mlist)
480 GSList *file_list = NULL;
482 MsgFileInfo *fileinfo;
485 while (mlist != NULL) {
486 msginfo = (MsgInfo *)mlist->data;
487 file = procmsg_get_message_file(msginfo);
489 procmsg_message_file_list_free(file_list);
492 fileinfo = g_new(MsgFileInfo, 1);
493 fileinfo->msginfo = procmsg_msginfo_new_ref(msginfo);
494 fileinfo->file = file;
495 fileinfo->flags = g_new(MsgFlags, 1);
496 *fileinfo->flags = msginfo->flags;
497 file_list = g_slist_prepend(file_list, fileinfo);
501 file_list = g_slist_reverse(file_list);
506 void procmsg_message_file_list_free(MsgInfoList *file_list)
509 MsgFileInfo *fileinfo;
511 for (cur = file_list; cur != NULL; cur = cur->next) {
512 fileinfo = (MsgFileInfo *)cur->data;
513 procmsg_msginfo_free(fileinfo->msginfo);
514 g_free(fileinfo->file);
515 g_free(fileinfo->flags);
519 g_slist_free(file_list);
522 FILE *procmsg_open_message(MsgInfo *msginfo)
527 g_return_val_if_fail(msginfo != NULL, NULL);
529 file = procmsg_get_message_file_path(msginfo);
530 g_return_val_if_fail(file != NULL, NULL);
532 if (!is_file_exist(file)) {
534 file = procmsg_get_message_file(msginfo);
539 if ((fp = g_fopen(file, "rb")) == NULL) {
540 FILE_OP_ERROR(file, "fopen");
547 if (MSG_IS_QUEUED(msginfo->flags) || MSG_IS_DRAFT(msginfo->flags)) {
550 while (fgets(buf, sizeof(buf), fp) != NULL) {
552 if (!strncmp(buf, "X-Sylpheed-End-Special-Headers: 1",
553 strlen("X-Sylpheed-End-Special-Headers:")))
556 if (buf[0] == '\r' || buf[0] == '\n') break;
557 /* from other mailers */
558 if (!strncmp(buf, "Date: ", 6)
559 || !strncmp(buf, "To: ", 4)
560 || !strncmp(buf, "From: ", 6)
561 || !strncmp(buf, "Subject: ", 9)) {
571 gboolean procmsg_msg_exist(MsgInfo *msginfo)
576 if (!msginfo) return FALSE;
578 path = folder_item_get_path(msginfo->folder);
580 ret = !folder_item_is_msg_changed(msginfo->folder, msginfo);
586 void procmsg_get_filter_keyword(MsgInfo *msginfo, gchar **header, gchar **key,
587 PrefsFilterType type)
589 static HeaderEntry hentry[] = {{"X-BeenThere:", NULL, TRUE},
590 {"X-ML-Name:", NULL, TRUE},
591 {"X-List:", NULL, TRUE},
592 {"X-Mailing-list:", NULL, TRUE},
593 {"List-Id:", NULL, TRUE},
594 {"X-Sequence:", NULL, TRUE},
595 {"Sender:", NULL, TRUE},
596 {"List-Post:", NULL, TRUE},
597 {NULL, NULL, FALSE}};
603 H_X_MAILING_LIST = 3,
612 g_return_if_fail(msginfo != NULL);
613 g_return_if_fail(header != NULL);
614 g_return_if_fail(key != NULL);
623 if ((fp = procmsg_open_message(msginfo)) == NULL)
625 procheader_get_header_fields(fp, hentry);
628 #define SET_FILTER_KEY(hstr, idx) \
630 *header = g_strdup(hstr); \
631 *key = hentry[idx].body; \
632 hentry[idx].body = NULL; \
635 if (hentry[H_X_BEENTHERE].body != NULL) {
636 SET_FILTER_KEY("header \"X-BeenThere\"", H_X_BEENTHERE);
637 } else if (hentry[H_X_ML_NAME].body != NULL) {
638 SET_FILTER_KEY("header \"X-ML-Name\"", H_X_ML_NAME);
639 } else if (hentry[H_X_LIST].body != NULL) {
640 SET_FILTER_KEY("header \"X-List\"", H_X_LIST);
641 } else if (hentry[H_X_MAILING_LIST].body != NULL) {
642 SET_FILTER_KEY("header \"X-Mailing-List\"", H_X_MAILING_LIST);
643 } else if (hentry[H_LIST_ID].body != NULL) {
644 SET_FILTER_KEY("header \"List-Id\"", H_LIST_ID);
645 extract_list_id_str(*key);
646 } else if (hentry[H_X_SEQUENCE].body != NULL) {
649 SET_FILTER_KEY("X-Sequence", H_X_SEQUENCE);
652 while (*p != '\0' && !g_ascii_isspace(*p)) p++;
653 while (g_ascii_isspace(*p)) p++;
654 if (g_ascii_isdigit(*p)) {
660 } else if (hentry[H_SENDER].body != NULL) {
661 SET_FILTER_KEY("header \"Sender\"", H_SENDER);
662 } else if (hentry[H_LIST_POST].body != NULL) {
663 SET_FILTER_KEY("header \"List-Post\"", H_LIST_POST);
664 } else if (msginfo->to) {
665 *header = g_strdup("to");
666 *key = g_strdup(msginfo->to);
667 } else if (msginfo->subject) {
668 *header = g_strdup("subject");
669 *key = g_strdup(msginfo->subject);
672 #undef SET_FILTER_KEY
674 g_free(hentry[H_X_BEENTHERE].body);
675 hentry[H_X_BEENTHERE].body = NULL;
676 g_free(hentry[H_X_ML_NAME].body);
677 hentry[H_X_ML_NAME].body = NULL;
678 g_free(hentry[H_X_LIST].body);
679 hentry[H_X_LIST].body = NULL;
680 g_free(hentry[H_X_MAILING_LIST].body);
681 hentry[H_X_MAILING_LIST].body = NULL;
682 g_free(hentry[H_LIST_ID].body);
683 hentry[H_LIST_ID].body = NULL;
684 g_free(hentry[H_SENDER].body);
685 hentry[H_SENDER].body = NULL;
686 g_free(hentry[H_LIST_POST].body);
687 hentry[H_LIST_POST].body = NULL;
691 *header = g_strdup("from");
692 *key = g_strdup(msginfo->from);
695 *header = g_strdup("to");
696 *key = g_strdup(msginfo->to);
698 case FILTER_BY_SUBJECT:
699 *header = g_strdup("subject");
700 *key = g_strdup(msginfo->subject);
707 void procmsg_empty_trash(FolderItem *trash)
712 (trash->stype != F_TRASH &&
713 !folder_has_parent_of_type(trash, F_TRASH)))
716 if (trash && trash->total_msgs > 0) {
717 GSList *mlist = folder_item_get_msg_list(trash);
719 for (cur = mlist ; cur != NULL ; cur = cur->next) {
720 MsgInfo * msginfo = (MsgInfo *) cur->data;
721 if (MSG_IS_LOCKED(msginfo->flags))
723 if (msginfo->total_size != 0 &&
724 msginfo->size != (off_t)msginfo->total_size)
725 partial_mark_for_delete(msginfo);
727 procmsg_msginfo_free(msginfo);
730 folder_item_remove_all_msg(trash);
733 if (!trash->node || !trash->node->children)
736 node = trash->node->children;
737 while (node != NULL) {
739 procmsg_empty_trash(FOLDER_ITEM(node->data));
744 void procmsg_empty_all_trash(void)
749 for (cur = folder_get_list(); cur != NULL; cur = cur->next) {
750 Folder *folder = FOLDER(cur->data);
751 trash = folder->trash;
752 procmsg_empty_trash(trash);
753 if (folder->account && folder->account->set_trash_folder &&
754 folder_find_item_from_identifier(folder->account->trash_folder))
756 folder_find_item_from_identifier(folder->account->trash_folder));
760 static PrefsAccount *procmsg_get_account_from_file(const gchar *file)
762 PrefsAccount *mailac = NULL;
766 static HeaderEntry qentry[] = {{"S:", NULL, FALSE},
767 {"SSV:", NULL, FALSE},
769 {"NG:", NULL, FALSE},
770 {"MAID:", NULL, FALSE},
771 {"NAID:", NULL, FALSE},
772 {"SCF:", NULL, FALSE},
773 {"RMID:", NULL, FALSE},
774 {"FMID:", NULL, FALSE},
775 {"X-Sylpheed-Privacy-System:", NULL, FALSE},
776 {"X-Sylpheed-Encrypt:", NULL, FALSE},
777 {"X-Sylpheed-Encrypt-Data:", NULL, FALSE},
778 {NULL, NULL, FALSE}};
780 g_return_val_if_fail(file != NULL, NULL);
782 if ((fp = g_fopen(file, "rb")) == NULL) {
783 FILE_OP_ERROR(file, "fopen");
787 while ((hnum = procheader_get_one_field(buf, sizeof(buf), fp, qentry))
789 gchar *p = buf + strlen(qentry[hnum].name);
791 if (hnum == Q_MAIL_ACCOUNT_ID) {
792 mailac = account_find_from_id(atoi(p));
800 static GSList *procmsg_list_sort_by_account(FolderItem *queue, GSList *list)
802 GSList *result = NULL;
804 PrefsAccount *last_account = NULL;
807 gboolean nothing_to_sort = TRUE;
812 orig = g_slist_copy(list);
814 msg = (MsgInfo *)orig->data;
816 for (cur = orig; cur; cur = cur->next)
817 debug_print("sort before %s\n", ((MsgInfo *)cur->data)->from);
822 nothing_to_sort = TRUE;
826 PrefsAccount *ac = NULL;
827 msg = (MsgInfo *)cur->data;
828 file = folder_item_fetch_msg(queue, msg->msgnum);
829 ac = procmsg_get_account_from_file(file);
832 if (last_account == NULL || (ac != NULL && ac == last_account)) {
833 result = g_slist_append(result, msg);
834 orig = g_slist_remove(orig, msg);
836 nothing_to_sort = FALSE;
842 if (orig || g_slist_length(orig)) {
843 if (!last_account && nothing_to_sort) {
844 /* can't find an account for the rest of the list */
847 result = g_slist_append(result, cur->data);
858 for (cur = result; cur; cur = cur->next)
859 debug_print("sort after %s\n", ((MsgInfo *)cur->data)->from);
866 static gboolean procmsg_is_last_for_account(FolderItem *queue, MsgInfo *msginfo, GSList *elem)
868 gchar *file = folder_item_fetch_msg(queue, msginfo->msgnum);
869 PrefsAccount *ac = procmsg_get_account_from_file(file);
872 for (cur = elem; cur; cur = cur->next) {
873 MsgInfo *cur_msginfo = (MsgInfo *)cur->data;
874 file = folder_item_fetch_msg(queue, cur_msginfo->msgnum);
876 if (cur_msginfo != msginfo && !MSG_IS_LOCKED(cur_msginfo->flags)) {
877 if (procmsg_get_account_from_file(file) == ac) {
888 static gboolean send_queue_lock = FALSE;
890 *\brief Send messages in queue
892 *\param queue Queue folder to process
893 *\param save_msgs Unused
895 *\return Number of messages sent, negative if an error occurred
896 * positive if no error occurred
898 gint procmsg_send_queue(FolderItem *queue, gboolean save_msgs, gchar **errstr)
900 gint sent = 0, err = 0;
902 GSList *sorted_list = NULL;
905 if (send_queue_lock) {
906 log_error(_("Already trying to send\n"));
908 if (*errstr) g_free(*errstr);
909 *errstr = g_strdup_printf(_("Already trying to send."));
911 toolbar_main_set_sensitive(mainwindow_get_mainwindow());
914 send_queue_lock = TRUE;
917 queue = folder_get_default_queue();
920 send_queue_lock = FALSE;
925 toolbar_main_set_sensitive(mainwindow_get_mainwindow());
927 folder_item_scan(queue);
928 list = folder_item_get_msg_list(queue);
930 /* sort the list per sender account; this helps reusing the same SMTP server */
931 sorted_list = procmsg_list_sort_by_account(queue, list);
933 for (elem = sorted_list; elem != NULL; elem = elem->next) {
937 msginfo = (MsgInfo *)(elem->data);
938 if (!MSG_IS_LOCKED(msginfo->flags)) {
939 file = folder_item_fetch_msg(queue, msginfo->msgnum);
941 if (procmsg_send_message_queue_full(file,
942 !procmsg_is_last_for_account(queue, msginfo, elem),
944 g_warning("Sending queued message %d failed.\n",
949 folder_item_remove_msg(queue, msginfo->msgnum);
954 /* FIXME: supposedly if only one message is locked, and queue
955 * is being flushed, the following free says something like
956 * "freeing msg ## in folder (nil)". */
957 procmsg_msginfo_free(msginfo);
960 g_slist_free(sorted_list);
961 folder_item_scan(queue);
963 if (queue->node && queue->node->children) {
964 node = queue->node->children;
965 while (node != NULL) {
968 res = procmsg_send_queue(FOLDER_ITEM(node->data), save_msgs, errstr);
976 send_queue_lock = FALSE;
978 toolbar_main_set_sensitive(mainwindow_get_mainwindow());
980 return (err != 0 ? -err : sent);
983 gboolean procmsg_is_sending(void)
985 return send_queue_lock;
989 *\brief Determine if a queue folder is empty
991 *\param queue Queue folder to process
993 *\return TRUE if the queue folder is empty, otherwise return FALSE
995 gboolean procmsg_queue_is_empty(FolderItem *queue)
998 gboolean res = FALSE;
1000 queue = folder_get_default_queue();
1001 g_return_val_if_fail(queue != NULL, TRUE);
1003 folder_item_scan(queue);
1004 list = folder_item_get_msg_list(queue);
1005 res = (list == NULL);
1006 procmsg_msg_list_free(list);
1010 if (queue->node && queue->node->children) {
1011 node = queue->node->children;
1012 while (node != NULL) {
1014 if (!procmsg_queue_is_empty(FOLDER_ITEM(node->data)))
1023 gint procmsg_remove_special_headers(const gchar *in, const gchar *out)
1026 gchar buf[BUFFSIZE];
1028 if ((fp = g_fopen(in, "rb")) == NULL) {
1029 FILE_OP_ERROR(in, "fopen");
1032 if ((outfp = g_fopen(out, "wb")) == NULL) {
1033 FILE_OP_ERROR(out, "fopen");
1037 while (fgets(buf, sizeof(buf), fp) != NULL) {
1039 if (!strncmp(buf, "X-Sylpheed-End-Special-Headers: 1",
1040 strlen("X-Sylpheed-End-Special-Headers:")))
1043 if (buf[0] == '\r' || buf[0] == '\n') break;
1044 /* from other mailers */
1045 if (!strncmp(buf, "Date: ", 6)
1046 || !strncmp(buf, "To: ", 4)
1047 || !strncmp(buf, "From: ", 6)
1048 || !strncmp(buf, "Subject: ", 9)) {
1053 while (fgets(buf, sizeof(buf), fp) != NULL)
1060 gint procmsg_save_to_outbox(FolderItem *outbox, const gchar *file,
1064 MsgInfo *msginfo, *tmp_msginfo;
1065 MsgFlags flag = {0, 0};
1067 debug_print("saving sent message...\n");
1070 outbox = folder_get_default_outbox();
1071 g_return_val_if_fail(outbox != NULL, -1);
1073 /* remove queueing headers */
1075 gchar tmp[MAXPATHLEN + 1];
1077 g_snprintf(tmp, sizeof(tmp), "%s%ctmpmsg.out.%08x",
1078 get_rc_dir(), G_DIR_SEPARATOR, (guint) rand());
1080 if (procmsg_remove_special_headers(file, tmp) !=0)
1083 folder_item_scan(outbox);
1084 if ((num = folder_item_add_msg(outbox, tmp, &flag, TRUE)) < 0) {
1085 g_warning("can't save message\n");
1090 folder_item_scan(outbox);
1091 if ((num = folder_item_add_msg
1092 (outbox, file, &flag, FALSE)) < 0) {
1093 g_warning("can't save message\n");
1097 msginfo = folder_item_get_msginfo(outbox, num); /* refcnt++ */
1098 tmp_msginfo = procmsg_msginfo_get_full_info(msginfo); /* refcnt++ */
1099 if (msginfo != NULL) {
1100 procmsg_msginfo_unset_flags(msginfo, ~0, 0);
1101 procmsg_msginfo_free(msginfo); /* refcnt-- */
1102 /* tmp_msginfo == msginfo */
1103 if (tmp_msginfo && msginfo->extradata &&
1104 (msginfo->extradata->dispositionnotificationto ||
1105 msginfo->extradata->returnreceiptto)) {
1106 procmsg_msginfo_set_flags(msginfo, MSG_RETRCPT_SENT, 0);
1108 procmsg_msginfo_free(tmp_msginfo); /* refcnt-- */
1114 void procmsg_print_message(MsgInfo *msginfo, const gchar *cmdline)
1116 static const gchar *def_cmd = "lpr %s";
1117 static guint id = 0;
1123 g_return_if_fail(msginfo);
1125 if (procmime_msginfo_is_encrypted(msginfo))
1126 tmpfp = procmime_get_first_encrypted_text_content(msginfo);
1128 tmpfp = procmime_get_first_text_content(msginfo);
1129 if (tmpfp == NULL) {
1130 g_warning("Can't get text part\n");
1134 prtmp = g_strdup_printf("%s%cprinttmp.%08x",
1135 get_mime_tmp_dir(), G_DIR_SEPARATOR, id++);
1137 if ((prfp = g_fopen(prtmp, "wb")) == NULL) {
1138 FILE_OP_ERROR(prtmp, "fopen");
1144 if (msginfo->date) fprintf(prfp, "Date: %s\n", msginfo->date);
1145 if (msginfo->from) fprintf(prfp, "From: %s\n", msginfo->from);
1146 if (msginfo->to) fprintf(prfp, "To: %s\n", msginfo->to);
1147 if (msginfo->cc) fprintf(prfp, "Cc: %s\n", msginfo->cc);
1148 if (msginfo->newsgroups)
1149 fprintf(prfp, "Newsgroups: %s\n", msginfo->newsgroups);
1150 if (msginfo->subject) fprintf(prfp, "Subject: %s\n", msginfo->subject);
1153 while (fgets(buf, sizeof(buf), tmpfp) != NULL)
1159 if (cmdline && (p = strchr(cmdline, '%')) && *(p + 1) == 's' &&
1160 !strchr(p + 2, '%'))
1161 g_snprintf(buf, sizeof(buf) - 1, cmdline, prtmp);
1164 g_warning("Print command line is invalid: '%s'\n",
1166 g_snprintf(buf, sizeof(buf) - 1, def_cmd, prtmp);
1172 if (buf[strlen(buf) - 1] != '&') strcat(buf, "&");
1176 MsgInfo *procmsg_msginfo_new_ref(MsgInfo *msginfo)
1183 MsgInfo *procmsg_msginfo_new(void)
1185 MsgInfo *newmsginfo;
1187 newmsginfo = g_new0(MsgInfo, 1);
1188 newmsginfo->refcnt = 1;
1193 MsgInfo *procmsg_msginfo_copy(MsgInfo *msginfo)
1195 MsgInfo *newmsginfo;
1198 if (msginfo == NULL) return NULL;
1200 newmsginfo = g_new0(MsgInfo, 1);
1202 newmsginfo->refcnt = 1;
1204 #define MEMBCOPY(mmb) newmsginfo->mmb = msginfo->mmb
1205 #define MEMBDUP(mmb) newmsginfo->mmb = msginfo->mmb ? \
1206 g_strdup(msginfo->mmb) : NULL
1221 MEMBDUP(newsgroups);
1228 MEMBCOPY(to_folder);
1230 if (msginfo->extradata) {
1231 newmsginfo->extradata = g_new0(MsgInfoExtraData, 1);
1232 MEMBDUP(extradata->face);
1233 MEMBDUP(extradata->xface);
1234 MEMBDUP(extradata->dispositionnotificationto);
1235 MEMBDUP(extradata->returnreceiptto);
1236 MEMBDUP(extradata->partial_recv);
1237 MEMBDUP(extradata->account_server);
1238 MEMBDUP(extradata->account_login);
1239 MEMBDUP(extradata->list_post);
1240 MEMBDUP(extradata->list_subscribe);
1241 MEMBDUP(extradata->list_unsubscribe);
1242 MEMBDUP(extradata->list_help);
1243 MEMBDUP(extradata->list_archive);
1244 MEMBDUP(extradata->list_owner);
1247 refs = msginfo->references;
1248 for (refs = msginfo->references; refs != NULL; refs = refs->next) {
1249 newmsginfo->references = g_slist_prepend
1250 (newmsginfo->references, g_strdup(refs->data));
1252 newmsginfo->references = g_slist_reverse(newmsginfo->references);
1255 MEMBDUP(plaintext_file);
1260 MsgInfo *procmsg_msginfo_get_full_info(MsgInfo *msginfo)
1262 MsgInfo *full_msginfo;
1265 if (msginfo == NULL) return NULL;
1267 file = procmsg_get_message_file_path(msginfo);
1268 if (!file || !is_file_exist(file)) {
1270 file = procmsg_get_message_file(msginfo);
1272 if (!file || !is_file_exist(file)) {
1273 g_warning("procmsg_msginfo_get_full_info(): can't get message file.\n");
1277 full_msginfo = procheader_parse_file(file, msginfo->flags, TRUE, FALSE);
1279 if (!full_msginfo) return NULL;
1281 msginfo->total_size = full_msginfo->total_size;
1282 msginfo->planned_download = full_msginfo->planned_download;
1284 if (full_msginfo->extradata) {
1285 if (!msginfo->extradata)
1286 msginfo->extradata = g_new0(MsgInfoExtraData, 1);
1287 if (!msginfo->extradata->list_post)
1288 msginfo->extradata->list_post = g_strdup(full_msginfo->extradata->list_post);
1289 if (!msginfo->extradata->list_subscribe)
1290 msginfo->extradata->list_subscribe = g_strdup(full_msginfo->extradata->list_subscribe);
1291 if (!msginfo->extradata->list_unsubscribe)
1292 msginfo->extradata->list_unsubscribe = g_strdup(full_msginfo->extradata->list_unsubscribe);
1293 if (!msginfo->extradata->list_help)
1294 msginfo->extradata->list_help = g_strdup(full_msginfo->extradata->list_help);
1295 if (!msginfo->extradata->list_archive)
1296 msginfo->extradata->list_archive= g_strdup(full_msginfo->extradata->list_archive);
1297 if (!msginfo->extradata->list_owner)
1298 msginfo->extradata->list_owner = g_strdup(full_msginfo->extradata->list_owner);
1299 if (!msginfo->extradata->xface)
1300 msginfo->extradata->xface = g_strdup(full_msginfo->extradata->xface);
1301 if (!msginfo->extradata->face)
1302 msginfo->extradata->face = g_strdup(full_msginfo->extradata->face);
1303 if (!msginfo->extradata->dispositionnotificationto)
1304 msginfo->extradata->dispositionnotificationto =
1305 g_strdup(full_msginfo->extradata->dispositionnotificationto);
1306 if (!msginfo->extradata->returnreceiptto)
1307 msginfo->extradata->returnreceiptto = g_strdup
1308 (full_msginfo->extradata->returnreceiptto);
1309 if (!msginfo->extradata->partial_recv && full_msginfo->extradata->partial_recv)
1310 msginfo->extradata->partial_recv = g_strdup
1311 (full_msginfo->extradata->partial_recv);
1312 if (!msginfo->extradata->account_server && full_msginfo->extradata->account_server)
1313 msginfo->extradata->account_server = g_strdup
1314 (full_msginfo->extradata->account_server);
1315 if (!msginfo->extradata->account_login && full_msginfo->extradata->account_login)
1316 msginfo->extradata->account_login = g_strdup
1317 (full_msginfo->extradata->account_login);
1319 procmsg_msginfo_free(full_msginfo);
1321 return procmsg_msginfo_new_ref(msginfo);
1324 void procmsg_msginfo_free(MsgInfo *msginfo)
1326 if (msginfo == NULL) return;
1329 if (msginfo->refcnt > 0)
1332 if (msginfo->to_folder) {
1333 msginfo->to_folder->op_count--;
1334 folder_item_update(msginfo->to_folder, F_ITEM_UPDATE_MSGCNT);
1337 g_free(msginfo->fromspace);
1339 g_free(msginfo->fromname);
1341 g_free(msginfo->date);
1342 g_free(msginfo->from);
1343 g_free(msginfo->to);
1344 g_free(msginfo->cc);
1345 g_free(msginfo->newsgroups);
1346 g_free(msginfo->subject);
1347 g_free(msginfo->msgid);
1348 g_free(msginfo->inreplyto);
1349 g_free(msginfo->xref);
1351 if (msginfo->extradata) {
1352 g_free(msginfo->extradata->returnreceiptto);
1353 g_free(msginfo->extradata->dispositionnotificationto);
1354 g_free(msginfo->extradata->xface);
1355 g_free(msginfo->extradata->face);
1356 g_free(msginfo->extradata->list_post);
1357 g_free(msginfo->extradata->list_subscribe);
1358 g_free(msginfo->extradata->list_unsubscribe);
1359 g_free(msginfo->extradata->list_help);
1360 g_free(msginfo->extradata->list_archive);
1361 g_free(msginfo->extradata->list_owner);
1362 g_free(msginfo->extradata->partial_recv);
1363 g_free(msginfo->extradata->account_server);
1364 g_free(msginfo->extradata->account_login);
1365 g_free(msginfo->extradata);
1367 slist_free_strings(msginfo->references);
1368 g_slist_free(msginfo->references);
1370 g_free(msginfo->plaintext_file);
1375 guint procmsg_msginfo_memusage(MsgInfo *msginfo)
1380 memusage += sizeof(MsgInfo);
1381 if (msginfo->fromname)
1382 memusage += strlen(msginfo->fromname);
1384 memusage += strlen(msginfo->date);
1386 memusage += strlen(msginfo->from);
1388 memusage += strlen(msginfo->to);
1390 memusage += strlen(msginfo->cc);
1391 if (msginfo->newsgroups)
1392 memusage += strlen(msginfo->newsgroups);
1393 if (msginfo->subject)
1394 memusage += strlen(msginfo->subject);
1396 memusage += strlen(msginfo->msgid);
1397 if (msginfo->inreplyto)
1398 memusage += strlen(msginfo->inreplyto);
1399 for (refs = msginfo->references; refs; refs=refs->next) {
1400 gchar *r = (gchar *)refs->data;
1401 memusage += r?strlen(r):0;
1403 if (msginfo->fromspace)
1404 memusage += strlen(msginfo->fromspace);
1406 if (msginfo->extradata) {
1407 memusage += sizeof(MsgInfoExtraData);
1408 if (msginfo->extradata->xface)
1409 memusage += strlen(msginfo->extradata->xface);
1410 if (msginfo->extradata->face)
1411 memusage += strlen(msginfo->extradata->face);
1412 if (msginfo->extradata->dispositionnotificationto)
1413 memusage += strlen(msginfo->extradata->dispositionnotificationto);
1414 if (msginfo->extradata->returnreceiptto)
1415 memusage += strlen(msginfo->extradata->returnreceiptto);
1417 if (msginfo->extradata->partial_recv)
1418 memusage += strlen(msginfo->extradata->partial_recv);
1419 if (msginfo->extradata->account_server)
1420 memusage += strlen(msginfo->extradata->account_server);
1421 if (msginfo->extradata->account_login)
1422 memusage += strlen(msginfo->extradata->account_login);
1424 if (msginfo->extradata->list_post)
1425 memusage += strlen(msginfo->extradata->list_post);
1426 if (msginfo->extradata->list_subscribe)
1427 memusage += strlen(msginfo->extradata->list_subscribe);
1428 if (msginfo->extradata->list_unsubscribe)
1429 memusage += strlen(msginfo->extradata->list_unsubscribe);
1430 if (msginfo->extradata->list_help)
1431 memusage += strlen(msginfo->extradata->list_help);
1432 if (msginfo->extradata->list_archive)
1433 memusage += strlen(msginfo->extradata->list_archive);
1434 if (msginfo->extradata->list_owner)
1435 memusage += strlen(msginfo->extradata->list_owner);
1440 gint procmsg_cmp_msgnum_for_sort(gconstpointer a, gconstpointer b)
1442 const MsgInfo *msginfo1 = a;
1443 const MsgInfo *msginfo2 = b;
1450 return msginfo1->msgnum - msginfo2->msgnum;
1453 static gint procmsg_send_message_queue_full(const gchar *file, gboolean keep_session, gchar **errstr)
1455 static HeaderEntry qentry[] = {{"S:", NULL, FALSE},
1456 {"SSV:", NULL, FALSE},
1457 {"R:", NULL, FALSE},
1458 {"NG:", NULL, FALSE},
1459 {"MAID:", NULL, FALSE},
1460 {"NAID:", NULL, FALSE},
1461 {"SCF:", NULL, FALSE},
1462 {"RMID:", NULL, FALSE},
1463 {"FMID:", NULL, FALSE},
1464 {"X-Sylpheed-Privacy-System:", NULL, FALSE},
1465 {"X-Sylpheed-Encrypt:", NULL, FALSE},
1466 {"X-Sylpheed-Encrypt-Data:", NULL, FALSE},
1467 {"X-Sylpheed-End-Special-Headers:", NULL, FALSE},
1468 {NULL, NULL, FALSE}};
1471 gint mailval = 0, newsval = 0;
1473 gchar *smtpserver = NULL;
1474 GSList *to_list = NULL;
1475 GSList *newsgroup_list = NULL;
1476 gchar *savecopyfolder = NULL;
1477 gchar *replymessageid = NULL;
1478 gchar *fwdmessageid = NULL;
1479 gchar *privacy_system = NULL;
1480 gboolean encrypt = FALSE;
1481 gchar *encrypt_data = NULL;
1482 gchar buf[BUFFSIZE];
1484 PrefsAccount *mailac = NULL, *newsac = NULL;
1485 gboolean save_clear_text = TRUE;
1486 gchar *tmp_enc_file = NULL;
1490 g_return_val_if_fail(file != NULL, -1);
1492 if ((fp = g_fopen(file, "rb")) == NULL) {
1493 FILE_OP_ERROR(file, "fopen");
1495 if (*errstr) g_free(*errstr);
1496 *errstr = g_strdup_printf(_("Couldn't open file %s."), file);
1501 while ((hnum = procheader_get_one_field(buf, sizeof(buf), fp, qentry))
1503 gchar *p = buf + strlen(qentry[hnum].name);
1511 if (smtpserver == NULL)
1512 smtpserver = g_strdup(p);
1515 to_list = address_list_append(to_list, p);
1518 newsgroup_list = newsgroup_list_append(newsgroup_list, p);
1520 case Q_MAIL_ACCOUNT_ID:
1521 mailac = account_find_from_id(atoi(p));
1523 case Q_NEWS_ACCOUNT_ID:
1524 newsac = account_find_from_id(atoi(p));
1526 case Q_SAVE_COPY_FOLDER:
1527 if (savecopyfolder == NULL)
1528 savecopyfolder = g_strdup(p);
1530 case Q_REPLY_MESSAGE_ID:
1531 if (replymessageid == NULL)
1532 replymessageid = g_strdup(p);
1534 case Q_FWD_MESSAGE_ID:
1535 if (fwdmessageid == NULL)
1536 fwdmessageid = g_strdup(p);
1538 case Q_PRIVACY_SYSTEM:
1539 if (privacy_system == NULL)
1540 privacy_system = g_strdup(p);
1546 case Q_ENCRYPT_DATA:
1547 if (encrypt_data == NULL)
1548 encrypt_data = g_strdup(p);
1550 case Q_SYLPHEED_HDRS:
1551 /* end of special headers reached */
1552 goto send_mail; /* can't "break;break;" */
1556 filepos = ftell(fp);
1561 if (mailac && mailac->save_encrypted_as_clear_text
1562 && !mailac->encrypt_to_self)
1563 save_clear_text = TRUE;
1565 save_clear_text = FALSE;
1570 mimeinfo = procmime_scan_queue_file(file);
1571 if (!privacy_encrypt(privacy_system, mimeinfo, encrypt_data)
1572 || (fp = my_tmpfile()) == NULL
1573 || procmime_write_mimeinfo(mimeinfo, fp) < 0) {
1576 procmime_mimeinfo_free_all(mimeinfo);
1579 slist_free_strings(to_list);
1580 g_slist_free(to_list);
1581 slist_free_strings(newsgroup_list);
1582 g_slist_free(newsgroup_list);
1583 g_free(savecopyfolder);
1584 g_free(replymessageid);
1585 g_free(fwdmessageid);
1586 g_free(privacy_system);
1587 g_free(encrypt_data);
1589 if (*errstr) g_free(*errstr);
1590 *errstr = g_strdup_printf(_("Couldn't encrypt the email: %s"),
1591 privacy_get_error());
1597 if (!save_clear_text) {
1598 gchar *content = NULL;
1599 FILE *tmpfp = get_tmpfile_in_dir(get_mime_tmp_dir(), &tmp_enc_file);
1603 content = file_read_stream_to_str(fp);
1606 str_write_to_file(content, tmp_enc_file);
1609 g_warning("couldn't get tempfile\n");
1613 procmime_mimeinfo_free_all(mimeinfo);
1619 debug_print("Sending message by mail\n");
1622 if (*errstr) g_free(*errstr);
1623 *errstr = g_strdup_printf(_("Queued message header is broken."));
1626 } else if (mailac && mailac->use_mail_command &&
1627 mailac->mail_command && (* mailac->mail_command)) {
1628 mailval = send_message_local(mailac->mail_command, fp);
1632 mailac = account_find_from_smtp_server(from, smtpserver);
1634 g_warning("Account not found. "
1635 "Using current account...\n");
1636 mailac = cur_account;
1641 mailval = send_message_smtp_full(mailac, to_list, fp, keep_session);
1642 if (mailval == -1 && errstr) {
1643 if (*errstr) g_free(*errstr);
1644 *errstr = g_strdup_printf(_("An error happened during SMTP session."));
1647 PrefsAccount tmp_ac;
1649 g_warning("Account not found.\n");
1651 memset(&tmp_ac, 0, sizeof(PrefsAccount));
1652 tmp_ac.address = from;
1653 tmp_ac.smtp_server = smtpserver;
1654 tmp_ac.smtpport = SMTP_PORT;
1655 mailval = send_message_smtp(&tmp_ac, to_list, fp);
1656 if (mailval == -1 && errstr) {
1657 if (*errstr) g_free(*errstr);
1658 *errstr = g_strdup_printf(_("No specific account has been found to "
1659 "send, and an error happened during SMTP session."));
1663 } else if (!to_list && !newsgroup_list) {
1665 if (*errstr) g_free(*errstr);
1666 *errstr = g_strdup(_("Couldn't determine sending informations. "
1667 "Maybe the email hasn't been generated by Sylpheed-Claws."));
1672 fseek(fp, filepos, SEEK_SET);
1673 if (newsgroup_list && (mailval == 0)) {
1678 /* write to temporary file */
1679 tmp = g_strdup_printf("%s%ctmp%d", g_get_tmp_dir(),
1680 G_DIR_SEPARATOR, (gint)file);
1681 if ((tmpfp = g_fopen(tmp, "wb")) == NULL) {
1682 FILE_OP_ERROR(tmp, "fopen");
1684 alertpanel_error(_("Could not create temporary file for news sending."));
1686 if (change_file_mode_rw(tmpfp, tmp) < 0) {
1687 FILE_OP_ERROR(tmp, "chmod");
1688 g_warning("can't change file mode\n");
1691 while ((newsval == 0) && fgets(buf, sizeof(buf), fp) != NULL) {
1692 if (fputs(buf, tmpfp) == EOF) {
1693 FILE_OP_ERROR(tmp, "fputs");
1696 if (*errstr) g_free(*errstr);
1697 *errstr = g_strdup_printf(_("Error when writing temporary file for news sending."));
1704 debug_print("Sending message by news\n");
1706 folder = FOLDER(newsac->folder);
1708 newsval = news_post(folder, tmp);
1709 if (newsval < 0 && errstr) {
1710 if (*errstr) g_free(*errstr);
1711 *errstr = g_strdup_printf(_("Error occurred while posting the message to %s."),
1712 newsac->nntp_server);
1722 /* save message to outbox */
1723 if (mailval == 0 && newsval == 0 && savecopyfolder) {
1726 debug_print("saving sent message...\n");
1728 outbox = folder_find_item_from_identifier(savecopyfolder);
1730 outbox = folder_get_default_outbox();
1732 if (save_clear_text || tmp_enc_file == NULL) {
1733 procmsg_save_to_outbox(outbox, file, TRUE);
1735 procmsg_save_to_outbox(outbox, tmp_enc_file, FALSE);
1739 if (tmp_enc_file != NULL) {
1740 g_unlink(tmp_enc_file);
1742 tmp_enc_file = NULL;
1745 if (replymessageid != NULL || fwdmessageid != NULL) {
1749 if (replymessageid != NULL)
1750 tokens = g_strsplit(replymessageid, "\t", 0);
1752 tokens = g_strsplit(fwdmessageid, "\t", 0);
1753 item = folder_find_item_from_identifier(tokens[0]);
1755 /* check if queued message has valid folder and message id */
1756 if (item != NULL && tokens[2] != NULL) {
1759 msginfo = folder_item_get_msginfo(item, atoi(tokens[1]));
1761 /* check if referring message exists and has a message id */
1762 if ((msginfo != NULL) &&
1763 (msginfo->msgid != NULL) &&
1764 (strcmp(msginfo->msgid, tokens[2]) != 0)) {
1765 procmsg_msginfo_free(msginfo);
1769 if (msginfo == NULL) {
1770 msginfo = folder_item_get_msginfo_by_msgid(item, tokens[2]);
1773 if (msginfo != NULL) {
1774 if (replymessageid != NULL) {
1775 procmsg_msginfo_unset_flags(msginfo, MSG_FORWARDED, 0);
1776 procmsg_msginfo_set_flags(msginfo, MSG_REPLIED, 0);
1778 procmsg_msginfo_unset_flags(msginfo, MSG_REPLIED, 0);
1779 procmsg_msginfo_set_flags(msginfo, MSG_FORWARDED, 0);
1781 procmsg_msginfo_free(msginfo);
1789 slist_free_strings(to_list);
1790 g_slist_free(to_list);
1791 slist_free_strings(newsgroup_list);
1792 g_slist_free(newsgroup_list);
1793 g_free(savecopyfolder);
1794 g_free(replymessageid);
1795 g_free(fwdmessageid);
1796 g_free(privacy_system);
1797 g_free(encrypt_data);
1799 return (newsval != 0 ? newsval : mailval);
1802 gint procmsg_send_message_queue(const gchar *file, gchar **errstr)
1804 gint result = procmsg_send_message_queue_full(file, FALSE, errstr);
1805 toolbar_main_set_sensitive(mainwindow_get_mainwindow());
1809 static void update_folder_msg_counts(FolderItem *item, MsgInfo *msginfo, MsgPermFlags old_flags)
1811 MsgPermFlags new_flags = msginfo->flags.perm_flags;
1814 if (!(old_flags & MSG_NEW) && (new_flags & MSG_NEW)) {
1818 if ((old_flags & MSG_NEW) && !(new_flags & MSG_NEW)) {
1823 if (!(old_flags & MSG_UNREAD) && (new_flags & MSG_UNREAD)) {
1824 item->unread_msgs++;
1825 if (procmsg_msg_has_marked_parent(msginfo))
1826 item->unreadmarked_msgs++;
1829 if ((old_flags & MSG_UNREAD) && !(new_flags & MSG_UNREAD)) {
1830 item->unread_msgs--;
1831 if (procmsg_msg_has_marked_parent(msginfo))
1832 item->unreadmarked_msgs--;
1836 if (!(old_flags & MSG_MARKED) && (new_flags & MSG_MARKED)) {
1837 procmsg_update_unread_children(msginfo, TRUE);
1838 item->marked_msgs++;
1841 if ((old_flags & MSG_MARKED) && !(new_flags & MSG_MARKED)) {
1842 procmsg_update_unread_children(msginfo, FALSE);
1843 item->marked_msgs--;
1847 void procmsg_msginfo_set_flags(MsgInfo *msginfo, MsgPermFlags perm_flags, MsgTmpFlags tmp_flags)
1850 MsgInfoUpdate msginfo_update;
1851 MsgPermFlags perm_flags_new, perm_flags_old;
1852 MsgTmpFlags tmp_flags_old;
1854 g_return_if_fail(msginfo != NULL);
1855 item = msginfo->folder;
1856 g_return_if_fail(item != NULL);
1858 debug_print("Setting flags for message %d in folder %s\n", msginfo->msgnum, item->path);
1860 /* Perm Flags handling */
1861 perm_flags_old = msginfo->flags.perm_flags;
1862 perm_flags_new = msginfo->flags.perm_flags | perm_flags;
1863 if ((perm_flags & MSG_IGNORE_THREAD) || (perm_flags_old & MSG_IGNORE_THREAD)) {
1864 perm_flags_new &= ~(MSG_NEW | MSG_UNREAD);
1867 if (perm_flags_old != perm_flags_new) {
1868 folder_item_change_msg_flags(msginfo->folder, msginfo, perm_flags_new);
1870 update_folder_msg_counts(item, msginfo, perm_flags_old);
1874 /* Tmp flags handling */
1875 tmp_flags_old = msginfo->flags.tmp_flags;
1876 msginfo->flags.tmp_flags |= tmp_flags;
1878 /* update notification */
1879 if ((perm_flags_old != perm_flags_new) || (tmp_flags_old != msginfo->flags.tmp_flags)) {
1880 msginfo_update.msginfo = msginfo;
1881 msginfo_update.flags = MSGINFO_UPDATE_FLAGS;
1882 hooks_invoke(MSGINFO_UPDATE_HOOKLIST, &msginfo_update);
1883 folder_item_update(msginfo->folder, F_ITEM_UPDATE_MSGCNT);
1887 void procmsg_msginfo_unset_flags(MsgInfo *msginfo, MsgPermFlags perm_flags, MsgTmpFlags tmp_flags)
1890 MsgInfoUpdate msginfo_update;
1891 MsgPermFlags perm_flags_new, perm_flags_old;
1892 MsgTmpFlags tmp_flags_old;
1894 g_return_if_fail(msginfo != NULL);
1895 item = msginfo->folder;
1896 g_return_if_fail(item != NULL);
1898 debug_print("Unsetting flags for message %d in folder %s\n", msginfo->msgnum, item->path);
1900 /* Perm Flags handling */
1901 perm_flags_old = msginfo->flags.perm_flags;
1902 perm_flags_new = msginfo->flags.perm_flags & ~perm_flags;
1904 if (perm_flags_old != perm_flags_new) {
1905 folder_item_change_msg_flags(msginfo->folder, msginfo, perm_flags_new);
1907 update_folder_msg_counts(item, msginfo, perm_flags_old);
1910 /* Tmp flags hanlding */
1911 tmp_flags_old = msginfo->flags.tmp_flags;
1912 msginfo->flags.tmp_flags &= ~tmp_flags;
1914 /* update notification */
1915 if ((perm_flags_old != perm_flags_new) || (tmp_flags_old != msginfo->flags.tmp_flags)) {
1916 msginfo_update.msginfo = msginfo;
1917 msginfo_update.flags = MSGINFO_UPDATE_FLAGS;
1918 hooks_invoke(MSGINFO_UPDATE_HOOKLIST, &msginfo_update);
1919 folder_item_update(msginfo->folder, F_ITEM_UPDATE_MSGCNT);
1923 void procmsg_msginfo_change_flags(MsgInfo *msginfo,
1924 MsgPermFlags add_perm_flags, MsgTmpFlags add_tmp_flags,
1925 MsgPermFlags rem_perm_flags, MsgTmpFlags rem_tmp_flags)
1928 MsgInfoUpdate msginfo_update;
1929 MsgPermFlags perm_flags_new, perm_flags_old;
1930 MsgTmpFlags tmp_flags_old;
1932 g_return_if_fail(msginfo != NULL);
1933 item = msginfo->folder;
1934 g_return_if_fail(item != NULL);
1936 debug_print("Changing flags for message %d in folder %s\n", msginfo->msgnum, item->path);
1938 /* Perm Flags handling */
1939 perm_flags_old = msginfo->flags.perm_flags;
1940 perm_flags_new = (msginfo->flags.perm_flags & ~rem_perm_flags) | add_perm_flags;
1941 if ((add_perm_flags & MSG_IGNORE_THREAD) || (perm_flags_old & MSG_IGNORE_THREAD)) {
1942 perm_flags_new &= ~(MSG_NEW | MSG_UNREAD);
1945 if (perm_flags_old != perm_flags_new) {
1946 folder_item_change_msg_flags(msginfo->folder, msginfo, perm_flags_new);
1948 update_folder_msg_counts(item, msginfo, perm_flags_old);
1952 /* Tmp flags handling */
1953 tmp_flags_old = msginfo->flags.tmp_flags;
1954 msginfo->flags.tmp_flags &= ~rem_tmp_flags;
1955 msginfo->flags.tmp_flags |= add_tmp_flags;
1957 /* update notification */
1958 if ((perm_flags_old != perm_flags_new) || (tmp_flags_old != msginfo->flags.tmp_flags)) {
1959 msginfo_update.msginfo = msginfo;
1960 msginfo_update.flags = MSGINFO_UPDATE_FLAGS;
1961 hooks_invoke(MSGINFO_UPDATE_HOOKLIST, &msginfo_update);
1962 folder_item_update(msginfo->folder, F_ITEM_UPDATE_MSGCNT);
1967 *\brief check for flags (e.g. mark) in prior msgs of current thread
1969 *\param info Current message
1970 *\param perm_flags Flags to be checked
1971 *\param parentmsgs Hash of prior msgs to avoid loops
1973 *\return gboolean TRUE if perm_flags are found
1975 gboolean procmsg_msg_has_flagged_parent_real(MsgInfo *info,
1976 MsgPermFlags perm_flags, GHashTable *parentmsgs)
1980 g_return_val_if_fail(info != NULL, FALSE);
1982 if (info != NULL && info->folder != NULL && info->inreplyto != NULL) {
1983 tmp = folder_item_get_msginfo_by_msgid(info->folder,
1985 if (tmp && (tmp->flags.perm_flags & perm_flags)) {
1986 procmsg_msginfo_free(tmp);
1988 } else if (tmp != NULL) {
1991 if (g_hash_table_lookup(parentmsgs, info)) {
1992 debug_print("loop detected: %d\n",
1996 g_hash_table_insert(parentmsgs, info, "1");
1997 result = procmsg_msg_has_flagged_parent_real(
1998 tmp, perm_flags, parentmsgs);
2000 procmsg_msginfo_free(tmp);
2010 *\brief Callback for cleaning up hash of parentmsgs
2012 gboolean parentmsgs_hash_remove(gpointer key,
2020 *\brief Set up list of parentmsgs
2021 * See procmsg_msg_has_flagged_parent_real()
2023 gboolean procmsg_msg_has_flagged_parent(MsgInfo *info, MsgPermFlags perm_flags)
2026 GHashTable *parentmsgs = g_hash_table_new(NULL, NULL);
2028 result = procmsg_msg_has_flagged_parent_real(info, perm_flags, parentmsgs);
2029 g_hash_table_foreach_remove(parentmsgs, parentmsgs_hash_remove, NULL);
2030 g_hash_table_destroy(parentmsgs);
2035 *\brief Check if msgs prior in thread are marked
2036 * See procmsg_msg_has_flagged_parent_real()
2038 gboolean procmsg_msg_has_marked_parent(MsgInfo *info)
2040 return procmsg_msg_has_flagged_parent(info, MSG_MARKED);
2044 GSList *procmsg_find_children_func(MsgInfo *info,
2045 GSList *children, GSList *all)
2049 g_return_val_if_fail(info!=NULL, children);
2050 if (info->msgid == NULL)
2053 for (cur = all; cur != NULL; cur = g_slist_next(cur)) {
2054 MsgInfo *tmp = (MsgInfo *)cur->data;
2055 if (tmp->inreplyto && !strcmp(tmp->inreplyto, info->msgid)) {
2056 /* Check if message is already in the list */
2057 if ((children == NULL) ||
2058 (g_slist_index(children, tmp) == -1)) {
2059 children = g_slist_prepend(children,
2060 procmsg_msginfo_new_ref(tmp));
2061 children = procmsg_find_children_func(tmp,
2070 GSList *procmsg_find_children (MsgInfo *info)
2075 g_return_val_if_fail(info!=NULL, NULL);
2076 all = folder_item_get_msg_list(info->folder);
2077 children = procmsg_find_children_func(info, NULL, all);
2078 if (children != NULL) {
2079 for (cur = all; cur != NULL; cur = g_slist_next(cur)) {
2080 /* this will not free the used pointers
2081 created with procmsg_msginfo_new_ref */
2082 procmsg_msginfo_free((MsgInfo *)cur->data);
2090 void procmsg_update_unread_children(MsgInfo *info, gboolean newly_marked)
2092 GSList *children = procmsg_find_children(info);
2094 for (cur = children; cur != NULL; cur = g_slist_next(cur)) {
2095 MsgInfo *tmp = (MsgInfo *)cur->data;
2096 if(MSG_IS_UNREAD(tmp->flags) && !MSG_IS_IGNORE_THREAD(tmp->flags)) {
2098 info->folder->unreadmarked_msgs++;
2100 info->folder->unreadmarked_msgs--;
2101 folder_item_update(info->folder, F_ITEM_UPDATE_MSGCNT);
2103 procmsg_msginfo_free(tmp);
2105 g_slist_free(children);
2109 * Set the destination folder for a copy or move operation
2111 * \param msginfo The message which's destination folder is changed
2112 * \param to_folder The destination folder for the operation
2114 void procmsg_msginfo_set_to_folder(MsgInfo *msginfo, FolderItem *to_folder)
2116 if(msginfo->to_folder != NULL) {
2117 msginfo->to_folder->op_count--;
2118 folder_item_update(msginfo->to_folder, F_ITEM_UPDATE_MSGCNT);
2120 msginfo->to_folder = to_folder;
2121 if(to_folder != NULL) {
2122 to_folder->op_count++;
2123 folder_item_update(msginfo->to_folder, F_ITEM_UPDATE_MSGCNT);
2128 * Apply filtering actions to the msginfo
2130 * \param msginfo The MsgInfo describing the message that should be filtered
2131 * \return TRUE if the message was moved and MsgInfo is now invalid,
2134 gboolean procmsg_msginfo_filter(MsgInfo *msginfo, PrefsAccount* ac_prefs)
2136 MailFilteringData mail_filtering_data;
2138 mail_filtering_data.msginfo = msginfo;
2139 if (hooks_invoke(MAIL_FILTERING_HOOKLIST, &mail_filtering_data)) {
2143 /* filter if enabled in prefs or move to inbox if not */
2144 if((filtering_rules != NULL) &&
2145 filter_message_by_msginfo(filtering_rules, msginfo, ac_prefs)) {
2152 MsgInfo *procmsg_msginfo_new_from_mimeinfo(MsgInfo *src_msginfo, MimeInfo *mimeinfo)
2154 MsgInfo *tmp_msginfo = NULL;
2155 MsgFlags flags = {0, 0};
2156 gchar *tmpfile = get_tmp_file();
2157 FILE *fp = g_fopen(tmpfile, "wb");
2159 if (!mimeinfo || mimeinfo->type != MIMETYPE_MESSAGE ||
2160 g_ascii_strcasecmp(mimeinfo->subtype, "rfc822")) {
2161 g_warning("procmsg_msginfo_new_from_mimeinfo(): unsuitable mimeinfo");
2168 if (fp && procmime_write_mimeinfo(mimeinfo, fp) >= 0) {
2171 tmp_msginfo = procheader_parse_file(
2178 if (tmp_msginfo != NULL) {
2180 tmp_msginfo->folder = src_msginfo->folder;
2181 tmp_msginfo->plaintext_file = g_strdup(tmpfile);
2183 g_warning("procmsg_msginfo_new_from_mimeinfo(): Can't generate new msginfo");
2191 static GSList *spam_learners = NULL;
2193 void procmsg_register_spam_learner (int (*learn_func)(MsgInfo *info, GSList *list, gboolean spam))
2195 if (!g_slist_find(spam_learners, learn_func))
2196 spam_learners = g_slist_append(spam_learners, learn_func);
2197 if (mainwindow_get_mainwindow()) {
2198 main_window_set_menu_sensitive(mainwindow_get_mainwindow());
2199 summary_set_menu_sensitive(
2200 mainwindow_get_mainwindow()->summaryview);
2201 toolbar_main_set_sensitive(mainwindow_get_mainwindow());
2205 void procmsg_unregister_spam_learner (int (*learn_func)(MsgInfo *info, GSList *list, gboolean spam))
2207 spam_learners = g_slist_remove(spam_learners, learn_func);
2208 if (mainwindow_get_mainwindow()) {
2209 main_window_set_menu_sensitive(mainwindow_get_mainwindow());
2210 summary_set_menu_sensitive(
2211 mainwindow_get_mainwindow()->summaryview);
2212 toolbar_main_set_sensitive(mainwindow_get_mainwindow());
2216 gboolean procmsg_spam_can_learn(void)
2218 return g_slist_length(spam_learners) > 0;
2221 int procmsg_spam_learner_learn (MsgInfo *info, GSList *list, gboolean spam)
2223 GSList *cur = spam_learners;
2225 for (; cur; cur = cur->next) {
2226 int ((*func)(MsgInfo *info, GSList *list, gboolean spam)) = cur->data;
2227 ret |= func(info, list, spam);
2232 static gchar *spam_folder_item = NULL;
2233 void procmsg_spam_set_folder (const char *item_identifier)
2235 g_free(spam_folder_item);
2236 if (item_identifier)
2237 spam_folder_item = g_strdup(item_identifier);
2239 spam_folder_item = NULL;
2242 FolderItem *procmsg_spam_get_folder (void)
2244 FolderItem *item = spam_folder_item ? folder_find_item_from_identifier(spam_folder_item) : NULL;
2245 return item ? item : folder_get_default_trash();
2248 static void item_has_queued_mails(FolderItem *item, gpointer data)
2250 gboolean *result = (gboolean *)data;
2251 if (*result == TRUE)
2253 if (folder_has_parent_of_type(item, F_QUEUE) && item->total_msgs > 0)
2257 gboolean procmsg_have_queued_mails_fast (void)
2259 gboolean result = FALSE;
2260 folder_func_to_all_folders(item_has_queued_mails, &result);