2 * Sylpheed -- a GTK+ based, lightweight, and fast e-mail client
3 * Copyright (C) 1999-2006 Hiroyuki Yamamoto and the Claws Mail 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,
69 Q_PRIVACY_SYSTEM_OLD = 13,
71 Q_ENCRYPT_DATA_OLD = 15,
72 Q_CLAWS_HDRS_OLD = 16,
75 GHashTable *procmsg_msg_hash_table_create(GSList *mlist)
77 GHashTable *msg_table;
79 if (mlist == NULL) return NULL;
81 msg_table = g_hash_table_new(NULL, g_direct_equal);
82 procmsg_msg_hash_table_append(msg_table, mlist);
87 void procmsg_msg_hash_table_append(GHashTable *msg_table, GSList *mlist)
92 if (msg_table == NULL || mlist == NULL) return;
94 for (cur = mlist; cur != NULL; cur = cur->next) {
95 msginfo = (MsgInfo *)cur->data;
97 g_hash_table_insert(msg_table,
98 GUINT_TO_POINTER(msginfo->msgnum),
103 GHashTable *procmsg_to_folder_hash_table_create(GSList *mlist)
105 GHashTable *msg_table;
109 if (mlist == NULL) return NULL;
111 msg_table = g_hash_table_new(NULL, g_direct_equal);
113 for (cur = mlist; cur != NULL; cur = cur->next) {
114 msginfo = (MsgInfo *)cur->data;
115 g_hash_table_insert(msg_table, msginfo->to_folder, msginfo);
121 gint procmsg_get_last_num_in_msg_list(GSList *mlist)
127 for (cur = mlist; cur != NULL; cur = cur->next) {
128 msginfo = (MsgInfo *)cur->data;
129 if (msginfo && msginfo->msgnum > last)
130 last = msginfo->msgnum;
136 void procmsg_msg_list_free(GSList *mlist)
141 for (cur = mlist; cur != NULL; cur = cur->next) {
142 msginfo = (MsgInfo *)cur->data;
143 procmsg_msginfo_free(msginfo);
157 /* CLAWS subject threading:
159 in the first round it inserts subject lines in a
160 relation (subject <-> node)
162 the second round finishes the threads by attaching
163 matching subject lines to the one found in the
164 relation. will use the oldest node with the same
165 subject that is not more then thread_by_subject_max_age
166 days old (see subject_relation_lookup)
169 static void subject_relation_insert(GRelation *relation, GNode *node)
174 g_return_if_fail(relation != NULL);
175 g_return_if_fail(node != NULL);
176 msginfo = (MsgInfo *) node->data;
177 g_return_if_fail(msginfo != NULL);
179 subject = msginfo->subject;
182 subject += subject_get_prefix_length(subject);
184 g_relation_insert(relation, subject, node);
187 static GNode *subject_relation_lookup(GRelation *relation, MsgInfo *msginfo)
194 g_return_val_if_fail(relation != NULL, NULL);
196 subject = msginfo->subject;
199 prefix_length = subject_get_prefix_length(subject);
200 if (prefix_length <= 0)
202 subject += prefix_length;
204 tuples = g_relation_select(relation, subject, 0);
208 if (tuples->len > 0) {
210 GNode *relation_node;
211 MsgInfo *relation_msginfo = NULL, *best_msginfo = NULL;
214 /* check all nodes with the same subject to find the best parent */
215 for (i = 0; i < tuples->len; i++) {
216 relation_node = (GNode *) g_tuples_index(tuples, i, 1);
217 relation_msginfo = (MsgInfo *) relation_node->data;
220 /* best node should be the oldest in the found nodes */
221 /* parent node must not be older then msginfo */
222 if ((relation_msginfo->date_t < msginfo->date_t) &&
223 ((best_msginfo == NULL) ||
224 (best_msginfo->date_t > relation_msginfo->date_t)))
227 /* parent node must not be more then thread_by_subject_max_age
228 days older then msginfo */
229 if (abs(difftime(msginfo->date_t, relation_msginfo->date_t)) >
230 prefs_common.thread_by_subject_max_age * 3600 * 24)
233 /* can add new tests for all matching
234 nodes found by subject */
237 node = relation_node;
238 best_msginfo = relation_msginfo;
243 g_tuples_destroy(tuples);
247 /* return the reversed thread tree */
248 GNode *procmsg_get_thread_tree(GSList *mlist)
250 GNode *root, *parent, *node, *next;
251 GHashTable *msgid_table;
252 GRelation *subject_relation = NULL;
257 root = g_node_new(NULL);
258 msgid_table = g_hash_table_new(g_str_hash, g_str_equal);
260 if (prefs_common.thread_by_subject) {
261 subject_relation = g_relation_new(2);
262 g_relation_index(subject_relation, 0, g_str_hash, g_str_equal);
265 for (; mlist != NULL; mlist = mlist->next) {
266 msginfo = (MsgInfo *)mlist->data;
269 if (msginfo->inreplyto) {
270 parent = g_hash_table_lookup(msgid_table, msginfo->inreplyto);
271 if (parent == NULL) {
275 node = g_node_insert_data_before
276 (parent, parent == root ? parent->children : NULL,
278 if ((msgid = msginfo->msgid) && g_hash_table_lookup(msgid_table, msgid) == NULL)
279 g_hash_table_insert(msgid_table, (gchar *)msgid, node);
281 /* CLAWS: add subject to relation (without prefix) */
282 if (prefs_common.thread_by_subject) {
283 subject_relation_insert(subject_relation, node);
287 /* complete the unfinished threads */
288 for (node = root->children; node != NULL; ) {
290 msginfo = (MsgInfo *)node->data;
293 if (msginfo->inreplyto)
294 parent = g_hash_table_lookup(msgid_table, msginfo->inreplyto);
296 /* try looking for the indirect parent */
297 if (!parent && msginfo->references) {
298 for (reflist = msginfo->references;
299 reflist != NULL; reflist = reflist->next)
300 if ((parent = g_hash_table_lookup
301 (msgid_table, reflist->data)) != NULL)
305 /* node should not be the parent, and node should not
306 be an ancestor of parent (circular reference) */
307 if (parent && parent != node &&
308 !g_node_is_ancestor(node, parent)) {
311 (parent, parent->children, node);
317 if (prefs_common.thread_by_subject) {
318 START_TIMING("thread by subject");
319 for (node = root->children; node && node != NULL;) {
321 msginfo = (MsgInfo *) node->data;
323 parent = subject_relation_lookup(subject_relation, msginfo);
325 /* the node may already be threaded by IN-REPLY-TO, so go up
327 find the parent node */
328 if (parent != NULL) {
329 if (g_node_is_ancestor(node, parent))
337 g_node_append(parent, node);
345 if (prefs_common.thread_by_subject)
346 g_relation_destroy(subject_relation);
348 g_hash_table_destroy(msgid_table);
353 gint procmsg_move_messages(GSList *mlist)
355 GSList *cur, *movelist = NULL;
357 FolderItem *dest = NULL;
359 gboolean finished = TRUE;
360 if (!mlist) return 0;
362 folder_item_update_freeze();
365 for (cur = mlist; cur != NULL; cur = cur->next) {
366 msginfo = (MsgInfo *)cur->data;
367 if (!msginfo->to_folder) {
373 dest = msginfo->to_folder;
374 movelist = g_slist_prepend(movelist, msginfo);
375 } else if (dest == msginfo->to_folder) {
376 movelist = g_slist_prepend(movelist, msginfo);
380 procmsg_msginfo_set_to_folder(msginfo, NULL);
383 movelist = g_slist_reverse(movelist);
384 retval |= folder_item_move_msgs(dest, movelist);
385 g_slist_free(movelist);
388 if (finished == FALSE) {
394 folder_item_update_thaw();
398 void procmsg_copy_messages(GSList *mlist)
400 GSList *cur, *copylist = NULL;
402 FolderItem *dest = NULL;
403 gboolean finished = TRUE;
406 folder_item_update_freeze();
409 for (cur = mlist; cur != NULL; cur = cur->next) {
410 msginfo = (MsgInfo *)cur->data;
411 if (!msginfo->to_folder) {
417 dest = msginfo->to_folder;
418 copylist = g_slist_prepend(copylist, msginfo);
419 } else if (dest == msginfo->to_folder) {
420 copylist = g_slist_prepend(copylist, msginfo);
424 procmsg_msginfo_set_to_folder(msginfo, NULL);
427 copylist = g_slist_reverse(copylist);
428 folder_item_copy_msgs(dest, copylist);
429 g_slist_free(copylist);
432 if (finished == FALSE) {
438 folder_item_update_thaw();
441 gchar *procmsg_get_message_file_path(MsgInfo *msginfo)
445 g_return_val_if_fail(msginfo != NULL, NULL);
447 if (msginfo->plaintext_file)
448 file = g_strdup(msginfo->plaintext_file);
450 file = folder_item_fetch_msg(msginfo->folder, msginfo->msgnum);
456 gchar *procmsg_get_message_file(MsgInfo *msginfo)
458 gchar *filename = NULL;
460 g_return_val_if_fail(msginfo != NULL, NULL);
462 filename = folder_item_fetch_msg(msginfo->folder, msginfo->msgnum);
464 debug_print("can't fetch message %d\n", msginfo->msgnum);
469 gchar *procmsg_get_message_file_full(MsgInfo *msginfo, gboolean headers, gboolean body)
471 gchar *filename = NULL;
473 g_return_val_if_fail(msginfo != NULL, NULL);
475 filename = folder_item_fetch_msg_full(msginfo->folder, msginfo->msgnum,
478 debug_print("can't fetch message %d\n", msginfo->msgnum);
483 GSList *procmsg_get_message_file_list(GSList *mlist)
485 GSList *file_list = NULL;
487 MsgFileInfo *fileinfo;
490 while (mlist != NULL) {
491 msginfo = (MsgInfo *)mlist->data;
492 file = procmsg_get_message_file(msginfo);
494 procmsg_message_file_list_free(file_list);
497 fileinfo = g_new(MsgFileInfo, 1);
498 fileinfo->msginfo = procmsg_msginfo_new_ref(msginfo);
499 fileinfo->file = file;
500 fileinfo->flags = g_new(MsgFlags, 1);
501 *fileinfo->flags = msginfo->flags;
502 file_list = g_slist_prepend(file_list, fileinfo);
506 file_list = g_slist_reverse(file_list);
511 void procmsg_message_file_list_free(MsgInfoList *file_list)
514 MsgFileInfo *fileinfo;
516 for (cur = file_list; cur != NULL; cur = cur->next) {
517 fileinfo = (MsgFileInfo *)cur->data;
518 procmsg_msginfo_free(fileinfo->msginfo);
519 g_free(fileinfo->file);
520 g_free(fileinfo->flags);
524 g_slist_free(file_list);
527 FILE *procmsg_open_message(MsgInfo *msginfo)
532 g_return_val_if_fail(msginfo != NULL, NULL);
534 file = procmsg_get_message_file_path(msginfo);
535 g_return_val_if_fail(file != NULL, NULL);
537 if (!is_file_exist(file)) {
539 file = procmsg_get_message_file(msginfo);
544 if ((fp = g_fopen(file, "rb")) == NULL) {
545 FILE_OP_ERROR(file, "fopen");
552 if (MSG_IS_QUEUED(msginfo->flags) || MSG_IS_DRAFT(msginfo->flags)) {
555 while (fgets(buf, sizeof(buf), fp) != NULL) {
557 if ((!strncmp(buf, "X-Claws-End-Special-Headers: 1",
558 strlen("X-Claws-End-Special-Headers:"))) ||
559 (!strncmp(buf, "X-Sylpheed-End-Special-Headers: 1",
560 strlen("X-Sylpheed-End-Special-Headers:"))))
563 if (buf[0] == '\r' || buf[0] == '\n') break;
564 /* from other mailers */
565 if (!strncmp(buf, "Date: ", 6)
566 || !strncmp(buf, "To: ", 4)
567 || !strncmp(buf, "From: ", 6)
568 || !strncmp(buf, "Subject: ", 9)) {
578 gboolean procmsg_msg_exist(MsgInfo *msginfo)
583 if (!msginfo) return FALSE;
585 path = folder_item_get_path(msginfo->folder);
587 ret = !folder_item_is_msg_changed(msginfo->folder, msginfo);
593 void procmsg_get_filter_keyword(MsgInfo *msginfo, gchar **header, gchar **key,
594 PrefsFilterType type)
596 static HeaderEntry hentry[] = {{"X-BeenThere:", NULL, TRUE},
597 {"X-ML-Name:", NULL, TRUE},
598 {"X-List:", NULL, TRUE},
599 {"X-Mailing-list:", NULL, TRUE},
600 {"List-Id:", NULL, TRUE},
601 {"X-Sequence:", NULL, TRUE},
602 {"Sender:", NULL, TRUE},
603 {"List-Post:", NULL, TRUE},
604 {NULL, NULL, FALSE}};
610 H_X_MAILING_LIST = 3,
619 g_return_if_fail(msginfo != NULL);
620 g_return_if_fail(header != NULL);
621 g_return_if_fail(key != NULL);
630 if ((fp = procmsg_open_message(msginfo)) == NULL)
632 procheader_get_header_fields(fp, hentry);
635 #define SET_FILTER_KEY(hstr, idx) \
637 *header = g_strdup(hstr); \
638 *key = hentry[idx].body; \
639 hentry[idx].body = NULL; \
642 if (hentry[H_X_BEENTHERE].body != NULL) {
643 SET_FILTER_KEY("header \"X-BeenThere\"", H_X_BEENTHERE);
644 } else if (hentry[H_X_ML_NAME].body != NULL) {
645 SET_FILTER_KEY("header \"X-ML-Name\"", H_X_ML_NAME);
646 } else if (hentry[H_X_LIST].body != NULL) {
647 SET_FILTER_KEY("header \"X-List\"", H_X_LIST);
648 } else if (hentry[H_X_MAILING_LIST].body != NULL) {
649 SET_FILTER_KEY("header \"X-Mailing-List\"", H_X_MAILING_LIST);
650 } else if (hentry[H_LIST_ID].body != NULL) {
651 SET_FILTER_KEY("header \"List-Id\"", H_LIST_ID);
652 extract_list_id_str(*key);
653 } else if (hentry[H_X_SEQUENCE].body != NULL) {
656 SET_FILTER_KEY("X-Sequence", H_X_SEQUENCE);
659 while (*p != '\0' && !g_ascii_isspace(*p)) p++;
660 while (g_ascii_isspace(*p)) p++;
661 if (g_ascii_isdigit(*p)) {
667 } else if (hentry[H_SENDER].body != NULL) {
668 SET_FILTER_KEY("header \"Sender\"", H_SENDER);
669 } else if (hentry[H_LIST_POST].body != NULL) {
670 SET_FILTER_KEY("header \"List-Post\"", H_LIST_POST);
671 } else if (msginfo->to) {
672 *header = g_strdup("to");
673 *key = g_strdup(msginfo->to);
674 } else if (msginfo->subject) {
675 *header = g_strdup("subject");
676 *key = g_strdup(msginfo->subject);
679 #undef SET_FILTER_KEY
681 g_free(hentry[H_X_BEENTHERE].body);
682 hentry[H_X_BEENTHERE].body = NULL;
683 g_free(hentry[H_X_ML_NAME].body);
684 hentry[H_X_ML_NAME].body = NULL;
685 g_free(hentry[H_X_LIST].body);
686 hentry[H_X_LIST].body = NULL;
687 g_free(hentry[H_X_MAILING_LIST].body);
688 hentry[H_X_MAILING_LIST].body = NULL;
689 g_free(hentry[H_LIST_ID].body);
690 hentry[H_LIST_ID].body = NULL;
691 g_free(hentry[H_SENDER].body);
692 hentry[H_SENDER].body = NULL;
693 g_free(hentry[H_LIST_POST].body);
694 hentry[H_LIST_POST].body = NULL;
698 *header = g_strdup("from");
699 *key = g_strdup(msginfo->from);
702 *header = g_strdup("to");
703 *key = g_strdup(msginfo->to);
705 case FILTER_BY_SUBJECT:
706 *header = g_strdup("subject");
707 *key = g_strdup(msginfo->subject);
714 void procmsg_empty_trash(FolderItem *trash)
719 (trash->stype != F_TRASH &&
720 !folder_has_parent_of_type(trash, F_TRASH)))
723 if (trash && trash->total_msgs > 0) {
724 GSList *mlist = folder_item_get_msg_list(trash);
726 for (cur = mlist ; cur != NULL ; cur = cur->next) {
727 MsgInfo * msginfo = (MsgInfo *) cur->data;
728 if (MSG_IS_LOCKED(msginfo->flags))
730 if (msginfo->total_size != 0 &&
731 msginfo->size != (off_t)msginfo->total_size)
732 partial_mark_for_delete(msginfo);
734 procmsg_msginfo_free(msginfo);
737 folder_item_remove_all_msg(trash);
740 if (!trash->node || !trash->node->children)
743 node = trash->node->children;
744 while (node != NULL) {
746 procmsg_empty_trash(FOLDER_ITEM(node->data));
751 void procmsg_empty_all_trash(void)
756 for (cur = folder_get_list(); cur != NULL; cur = cur->next) {
757 Folder *folder = FOLDER(cur->data);
758 trash = folder->trash;
759 procmsg_empty_trash(trash);
760 if (folder->account && folder->account->set_trash_folder &&
761 folder_find_item_from_identifier(folder->account->trash_folder))
763 folder_find_item_from_identifier(folder->account->trash_folder));
767 static PrefsAccount *procmsg_get_account_from_file(const gchar *file)
769 PrefsAccount *mailac = NULL;
773 static HeaderEntry qentry[] = {{"S:", NULL, FALSE},
774 {"SSV:", NULL, FALSE},
776 {"NG:", NULL, FALSE},
777 {"MAID:", NULL, FALSE},
778 {"NAID:", NULL, FALSE},
779 {"SCF:", NULL, FALSE},
780 {"RMID:", NULL, FALSE},
781 {"FMID:", NULL, FALSE},
782 {"X-Claws-Privacy-System:", NULL, FALSE},
783 {"X-Claws-Encrypt:", NULL, FALSE},
784 {"X-Claws-Encrypt-Data:", NULL, FALSE},
785 {"X-Claws-End-Special-Headers", NULL, FALSE},
786 {"X-Sylpheed-Privacy-System:", NULL, FALSE},
787 {"X-Sylpheed-Encrypt:", NULL, FALSE},
788 {"X-Sylpheed-Encrypt-Data:", NULL, FALSE},
789 {NULL, NULL, FALSE}};
791 g_return_val_if_fail(file != NULL, NULL);
793 if ((fp = g_fopen(file, "rb")) == NULL) {
794 FILE_OP_ERROR(file, "fopen");
798 while ((hnum = procheader_get_one_field(buf, sizeof(buf), fp, qentry))
800 gchar *p = buf + strlen(qentry[hnum].name);
802 if (hnum == Q_MAIL_ACCOUNT_ID) {
803 mailac = account_find_from_id(atoi(p));
811 static GSList *procmsg_list_sort_by_account(FolderItem *queue, GSList *list)
813 GSList *result = NULL;
815 PrefsAccount *last_account = NULL;
818 gboolean nothing_to_sort = TRUE;
823 orig = g_slist_copy(list);
825 msg = (MsgInfo *)orig->data;
827 for (cur = orig; cur; cur = cur->next)
828 debug_print("sort before %s\n", ((MsgInfo *)cur->data)->from);
833 nothing_to_sort = TRUE;
837 PrefsAccount *ac = NULL;
838 msg = (MsgInfo *)cur->data;
839 file = folder_item_fetch_msg(queue, msg->msgnum);
840 ac = procmsg_get_account_from_file(file);
843 if (last_account == NULL || (ac != NULL && ac == last_account)) {
844 result = g_slist_append(result, msg);
845 orig = g_slist_remove(orig, msg);
847 nothing_to_sort = FALSE;
853 if (orig || g_slist_length(orig)) {
854 if (!last_account && nothing_to_sort) {
855 /* can't find an account for the rest of the list */
858 result = g_slist_append(result, cur->data);
869 for (cur = result; cur; cur = cur->next)
870 debug_print("sort after %s\n", ((MsgInfo *)cur->data)->from);
877 static gboolean procmsg_is_last_for_account(FolderItem *queue, MsgInfo *msginfo, GSList *elem)
879 gchar *file = folder_item_fetch_msg(queue, msginfo->msgnum);
880 PrefsAccount *ac = procmsg_get_account_from_file(file);
883 for (cur = elem; cur; cur = cur->next) {
884 MsgInfo *cur_msginfo = (MsgInfo *)cur->data;
885 file = folder_item_fetch_msg(queue, cur_msginfo->msgnum);
887 if (cur_msginfo != msginfo && !MSG_IS_LOCKED(cur_msginfo->flags)) {
888 if (procmsg_get_account_from_file(file) == ac) {
899 static gboolean send_queue_lock = FALSE;
901 *\brief Send messages in queue
903 *\param queue Queue folder to process
904 *\param save_msgs Unused
906 *\return Number of messages sent, negative if an error occurred
907 * positive if no error occurred
909 gint procmsg_send_queue(FolderItem *queue, gboolean save_msgs, gchar **errstr)
911 gint sent = 0, err = 0;
913 GSList *sorted_list = NULL;
916 if (send_queue_lock) {
917 /* Avoid having to translate two similar strings */
918 log_warning("%s\n", _("Already trying to send."));
920 if (*errstr) g_free(*errstr);
921 *errstr = g_strdup_printf(_("Already trying to send."));
923 toolbar_main_set_sensitive(mainwindow_get_mainwindow());
926 send_queue_lock = TRUE;
929 queue = folder_get_default_queue();
932 send_queue_lock = FALSE;
937 toolbar_main_set_sensitive(mainwindow_get_mainwindow());
939 folder_item_scan(queue);
940 list = folder_item_get_msg_list(queue);
942 /* sort the list per sender account; this helps reusing the same SMTP server */
943 sorted_list = procmsg_list_sort_by_account(queue, list);
945 for (elem = sorted_list; elem != NULL; elem = elem->next) {
949 msginfo = (MsgInfo *)(elem->data);
950 if (!MSG_IS_LOCKED(msginfo->flags)) {
951 file = folder_item_fetch_msg(queue, msginfo->msgnum);
953 if (procmsg_send_message_queue_full(file,
954 !procmsg_is_last_for_account(queue, msginfo, elem),
955 errstr, queue, msginfo->msgnum) < 0) {
956 g_warning("Sending queued message %d failed.\n",
961 folder_item_remove_msg(queue, msginfo->msgnum);
966 /* FIXME: supposedly if only one message is locked, and queue
967 * is being flushed, the following free says something like
968 * "freeing msg ## in folder (nil)". */
969 procmsg_msginfo_free(msginfo);
972 g_slist_free(sorted_list);
973 folder_item_scan(queue);
975 if (queue->node && queue->node->children) {
976 node = queue->node->children;
977 while (node != NULL) {
980 res = procmsg_send_queue(FOLDER_ITEM(node->data), save_msgs, errstr);
988 send_queue_lock = FALSE;
990 toolbar_main_set_sensitive(mainwindow_get_mainwindow());
992 return (err != 0 ? -err : sent);
995 gboolean procmsg_is_sending(void)
997 return send_queue_lock;
1001 *\brief Determine if a queue folder is empty
1003 *\param queue Queue folder to process
1005 *\return TRUE if the queue folder is empty, otherwise return FALSE
1007 gboolean procmsg_queue_is_empty(FolderItem *queue)
1010 gboolean res = FALSE;
1012 queue = folder_get_default_queue();
1013 g_return_val_if_fail(queue != NULL, TRUE);
1015 folder_item_scan(queue);
1016 list = folder_item_get_msg_list(queue);
1017 res = (list == NULL);
1018 procmsg_msg_list_free(list);
1022 if (queue->node && queue->node->children) {
1023 node = queue->node->children;
1024 while (node != NULL) {
1026 if (!procmsg_queue_is_empty(FOLDER_ITEM(node->data)))
1035 gint procmsg_remove_special_headers(const gchar *in, const gchar *out)
1038 gchar buf[BUFFSIZE];
1040 if ((fp = g_fopen(in, "rb")) == NULL) {
1041 FILE_OP_ERROR(in, "fopen");
1044 if ((outfp = g_fopen(out, "wb")) == NULL) {
1045 FILE_OP_ERROR(out, "fopen");
1049 while (fgets(buf, sizeof(buf), fp) != NULL) {
1051 if ((!strncmp(buf, "X-Claws-End-Special-Headers: 1",
1052 strlen("X-Claws-End-Special-Headers:"))) ||
1053 (!strncmp(buf, "X-Sylpheed-End-Special-Headers: 1",
1054 strlen("X-Sylpheed-End-Special-Headers:"))))
1057 if (buf[0] == '\r' || buf[0] == '\n') break;
1058 /* from other mailers */
1059 if (!strncmp(buf, "Date: ", 6)
1060 || !strncmp(buf, "To: ", 4)
1061 || !strncmp(buf, "From: ", 6)
1062 || !strncmp(buf, "Subject: ", 9)) {
1067 while (fgets(buf, sizeof(buf), fp) != NULL)
1074 gint procmsg_save_to_outbox(FolderItem *outbox, const gchar *file,
1078 MsgInfo *msginfo, *tmp_msginfo;
1079 MsgFlags flag = {0, 0};
1081 debug_print("saving sent message...\n");
1084 outbox = folder_get_default_outbox();
1085 g_return_val_if_fail(outbox != NULL, -1);
1087 /* remove queueing headers */
1089 gchar tmp[MAXPATHLEN + 1];
1091 g_snprintf(tmp, sizeof(tmp), "%s%ctmpmsg.out.%08x",
1092 get_rc_dir(), G_DIR_SEPARATOR, (guint) rand());
1094 if (procmsg_remove_special_headers(file, tmp) !=0)
1097 folder_item_scan(outbox);
1098 if ((num = folder_item_add_msg(outbox, tmp, &flag, TRUE)) < 0) {
1099 g_warning("can't save message\n");
1104 folder_item_scan(outbox);
1105 if ((num = folder_item_add_msg
1106 (outbox, file, &flag, FALSE)) < 0) {
1107 g_warning("can't save message\n");
1111 msginfo = folder_item_get_msginfo(outbox, num); /* refcnt++ */
1112 tmp_msginfo = procmsg_msginfo_get_full_info(msginfo); /* refcnt++ */
1113 if (msginfo != NULL) {
1114 procmsg_msginfo_unset_flags(msginfo, ~0, 0);
1115 procmsg_msginfo_free(msginfo); /* refcnt-- */
1116 /* tmp_msginfo == msginfo */
1117 if (tmp_msginfo && msginfo->extradata &&
1118 (msginfo->extradata->dispositionnotificationto ||
1119 msginfo->extradata->returnreceiptto)) {
1120 procmsg_msginfo_set_flags(msginfo, MSG_RETRCPT_SENT, 0);
1122 procmsg_msginfo_free(tmp_msginfo); /* refcnt-- */
1128 void procmsg_print_message(MsgInfo *msginfo, const gchar *cmdline)
1130 static const gchar *def_cmd = "lpr %s";
1131 static guint id = 0;
1137 g_return_if_fail(msginfo);
1139 if (procmime_msginfo_is_encrypted(msginfo))
1140 tmpfp = procmime_get_first_encrypted_text_content(msginfo);
1142 tmpfp = procmime_get_first_text_content(msginfo);
1143 if (tmpfp == NULL) {
1144 g_warning("Can't get text part\n");
1148 prtmp = g_strdup_printf("%s%cprinttmp.%08x",
1149 get_mime_tmp_dir(), G_DIR_SEPARATOR, id++);
1151 if ((prfp = g_fopen(prtmp, "wb")) == NULL) {
1152 FILE_OP_ERROR(prtmp, "fopen");
1158 if (msginfo->date) fprintf(prfp, "Date: %s\n", msginfo->date);
1159 if (msginfo->from) fprintf(prfp, "From: %s\n", msginfo->from);
1160 if (msginfo->to) fprintf(prfp, "To: %s\n", msginfo->to);
1161 if (msginfo->cc) fprintf(prfp, "Cc: %s\n", msginfo->cc);
1162 if (msginfo->newsgroups)
1163 fprintf(prfp, "Newsgroups: %s\n", msginfo->newsgroups);
1164 if (msginfo->subject) fprintf(prfp, "Subject: %s\n", msginfo->subject);
1167 while (fgets(buf, sizeof(buf), tmpfp) != NULL)
1173 if (cmdline && (p = strchr(cmdline, '%')) && *(p + 1) == 's' &&
1174 !strchr(p + 2, '%'))
1175 g_snprintf(buf, sizeof(buf) - 1, cmdline, prtmp);
1178 g_warning("Print command line is invalid: '%s'\n",
1180 g_snprintf(buf, sizeof(buf) - 1, def_cmd, prtmp);
1186 if (buf[strlen(buf) - 1] != '&') strcat(buf, "&");
1190 MsgInfo *procmsg_msginfo_new_ref(MsgInfo *msginfo)
1197 MsgInfo *procmsg_msginfo_new(void)
1199 MsgInfo *newmsginfo;
1201 newmsginfo = g_new0(MsgInfo, 1);
1202 newmsginfo->refcnt = 1;
1207 MsgInfo *procmsg_msginfo_copy(MsgInfo *msginfo)
1209 MsgInfo *newmsginfo;
1212 if (msginfo == NULL) return NULL;
1214 newmsginfo = g_new0(MsgInfo, 1);
1216 newmsginfo->refcnt = 1;
1218 #define MEMBCOPY(mmb) newmsginfo->mmb = msginfo->mmb
1219 #define MEMBDUP(mmb) newmsginfo->mmb = msginfo->mmb ? \
1220 g_strdup(msginfo->mmb) : NULL
1235 MEMBDUP(newsgroups);
1242 MEMBCOPY(to_folder);
1244 if (msginfo->extradata) {
1245 newmsginfo->extradata = g_new0(MsgInfoExtraData, 1);
1246 MEMBDUP(extradata->face);
1247 MEMBDUP(extradata->xface);
1248 MEMBDUP(extradata->dispositionnotificationto);
1249 MEMBDUP(extradata->returnreceiptto);
1250 MEMBDUP(extradata->partial_recv);
1251 MEMBDUP(extradata->account_server);
1252 MEMBDUP(extradata->account_login);
1253 MEMBDUP(extradata->list_post);
1254 MEMBDUP(extradata->list_subscribe);
1255 MEMBDUP(extradata->list_unsubscribe);
1256 MEMBDUP(extradata->list_help);
1257 MEMBDUP(extradata->list_archive);
1258 MEMBDUP(extradata->list_owner);
1261 refs = msginfo->references;
1262 for (refs = msginfo->references; refs != NULL; refs = refs->next) {
1263 newmsginfo->references = g_slist_prepend
1264 (newmsginfo->references, g_strdup(refs->data));
1266 newmsginfo->references = g_slist_reverse(newmsginfo->references);
1269 MEMBDUP(plaintext_file);
1274 MsgInfo *procmsg_msginfo_get_full_info(MsgInfo *msginfo)
1276 MsgInfo *full_msginfo;
1279 if (msginfo == NULL) return NULL;
1281 file = procmsg_get_message_file_path(msginfo);
1282 if (!file || !is_file_exist(file)) {
1284 file = procmsg_get_message_file(msginfo);
1286 if (!file || !is_file_exist(file)) {
1287 g_warning("procmsg_msginfo_get_full_info(): can't get message file.\n");
1291 full_msginfo = procheader_parse_file(file, msginfo->flags, TRUE, FALSE);
1293 if (!full_msginfo) return NULL;
1295 msginfo->total_size = full_msginfo->total_size;
1296 msginfo->planned_download = full_msginfo->planned_download;
1298 if (full_msginfo->extradata) {
1299 if (!msginfo->extradata)
1300 msginfo->extradata = g_new0(MsgInfoExtraData, 1);
1301 if (!msginfo->extradata->list_post)
1302 msginfo->extradata->list_post = g_strdup(full_msginfo->extradata->list_post);
1303 if (!msginfo->extradata->list_subscribe)
1304 msginfo->extradata->list_subscribe = g_strdup(full_msginfo->extradata->list_subscribe);
1305 if (!msginfo->extradata->list_unsubscribe)
1306 msginfo->extradata->list_unsubscribe = g_strdup(full_msginfo->extradata->list_unsubscribe);
1307 if (!msginfo->extradata->list_help)
1308 msginfo->extradata->list_help = g_strdup(full_msginfo->extradata->list_help);
1309 if (!msginfo->extradata->list_archive)
1310 msginfo->extradata->list_archive= g_strdup(full_msginfo->extradata->list_archive);
1311 if (!msginfo->extradata->list_owner)
1312 msginfo->extradata->list_owner = g_strdup(full_msginfo->extradata->list_owner);
1313 if (!msginfo->extradata->xface)
1314 msginfo->extradata->xface = g_strdup(full_msginfo->extradata->xface);
1315 if (!msginfo->extradata->face)
1316 msginfo->extradata->face = g_strdup(full_msginfo->extradata->face);
1317 if (!msginfo->extradata->dispositionnotificationto)
1318 msginfo->extradata->dispositionnotificationto =
1319 g_strdup(full_msginfo->extradata->dispositionnotificationto);
1320 if (!msginfo->extradata->returnreceiptto)
1321 msginfo->extradata->returnreceiptto = g_strdup
1322 (full_msginfo->extradata->returnreceiptto);
1323 if (!msginfo->extradata->partial_recv && full_msginfo->extradata->partial_recv)
1324 msginfo->extradata->partial_recv = g_strdup
1325 (full_msginfo->extradata->partial_recv);
1326 if (!msginfo->extradata->account_server && full_msginfo->extradata->account_server)
1327 msginfo->extradata->account_server = g_strdup
1328 (full_msginfo->extradata->account_server);
1329 if (!msginfo->extradata->account_login && full_msginfo->extradata->account_login)
1330 msginfo->extradata->account_login = g_strdup
1331 (full_msginfo->extradata->account_login);
1333 procmsg_msginfo_free(full_msginfo);
1335 return procmsg_msginfo_new_ref(msginfo);
1338 void procmsg_msginfo_free(MsgInfo *msginfo)
1340 if (msginfo == NULL) return;
1343 if (msginfo->refcnt > 0)
1346 if (msginfo->to_folder) {
1347 msginfo->to_folder->op_count--;
1348 folder_item_update(msginfo->to_folder, F_ITEM_UPDATE_MSGCNT);
1351 g_free(msginfo->fromspace);
1353 g_free(msginfo->fromname);
1355 g_free(msginfo->date);
1356 g_free(msginfo->from);
1357 g_free(msginfo->to);
1358 g_free(msginfo->cc);
1359 g_free(msginfo->newsgroups);
1360 g_free(msginfo->subject);
1361 g_free(msginfo->msgid);
1362 g_free(msginfo->inreplyto);
1363 g_free(msginfo->xref);
1365 if (msginfo->extradata) {
1366 g_free(msginfo->extradata->returnreceiptto);
1367 g_free(msginfo->extradata->dispositionnotificationto);
1368 g_free(msginfo->extradata->xface);
1369 g_free(msginfo->extradata->face);
1370 g_free(msginfo->extradata->list_post);
1371 g_free(msginfo->extradata->list_subscribe);
1372 g_free(msginfo->extradata->list_unsubscribe);
1373 g_free(msginfo->extradata->list_help);
1374 g_free(msginfo->extradata->list_archive);
1375 g_free(msginfo->extradata->list_owner);
1376 g_free(msginfo->extradata->partial_recv);
1377 g_free(msginfo->extradata->account_server);
1378 g_free(msginfo->extradata->account_login);
1379 g_free(msginfo->extradata);
1381 slist_free_strings(msginfo->references);
1382 g_slist_free(msginfo->references);
1384 g_free(msginfo->plaintext_file);
1389 guint procmsg_msginfo_memusage(MsgInfo *msginfo)
1394 memusage += sizeof(MsgInfo);
1395 if (msginfo->fromname)
1396 memusage += strlen(msginfo->fromname);
1398 memusage += strlen(msginfo->date);
1400 memusage += strlen(msginfo->from);
1402 memusage += strlen(msginfo->to);
1404 memusage += strlen(msginfo->cc);
1405 if (msginfo->newsgroups)
1406 memusage += strlen(msginfo->newsgroups);
1407 if (msginfo->subject)
1408 memusage += strlen(msginfo->subject);
1410 memusage += strlen(msginfo->msgid);
1411 if (msginfo->inreplyto)
1412 memusage += strlen(msginfo->inreplyto);
1413 for (refs = msginfo->references; refs; refs=refs->next) {
1414 gchar *r = (gchar *)refs->data;
1415 memusage += r?strlen(r):0;
1417 if (msginfo->fromspace)
1418 memusage += strlen(msginfo->fromspace);
1420 if (msginfo->extradata) {
1421 memusage += sizeof(MsgInfoExtraData);
1422 if (msginfo->extradata->xface)
1423 memusage += strlen(msginfo->extradata->xface);
1424 if (msginfo->extradata->face)
1425 memusage += strlen(msginfo->extradata->face);
1426 if (msginfo->extradata->dispositionnotificationto)
1427 memusage += strlen(msginfo->extradata->dispositionnotificationto);
1428 if (msginfo->extradata->returnreceiptto)
1429 memusage += strlen(msginfo->extradata->returnreceiptto);
1431 if (msginfo->extradata->partial_recv)
1432 memusage += strlen(msginfo->extradata->partial_recv);
1433 if (msginfo->extradata->account_server)
1434 memusage += strlen(msginfo->extradata->account_server);
1435 if (msginfo->extradata->account_login)
1436 memusage += strlen(msginfo->extradata->account_login);
1438 if (msginfo->extradata->list_post)
1439 memusage += strlen(msginfo->extradata->list_post);
1440 if (msginfo->extradata->list_subscribe)
1441 memusage += strlen(msginfo->extradata->list_subscribe);
1442 if (msginfo->extradata->list_unsubscribe)
1443 memusage += strlen(msginfo->extradata->list_unsubscribe);
1444 if (msginfo->extradata->list_help)
1445 memusage += strlen(msginfo->extradata->list_help);
1446 if (msginfo->extradata->list_archive)
1447 memusage += strlen(msginfo->extradata->list_archive);
1448 if (msginfo->extradata->list_owner)
1449 memusage += strlen(msginfo->extradata->list_owner);
1454 gint procmsg_cmp_msgnum_for_sort(gconstpointer a, gconstpointer b)
1456 const MsgInfo *msginfo1 = a;
1457 const MsgInfo *msginfo2 = b;
1464 return msginfo1->msgnum - msginfo2->msgnum;
1467 static gint procmsg_send_message_queue_full(const gchar *file, gboolean keep_session, gchar **errstr,
1468 FolderItem *queue, gint msgnum)
1470 static HeaderEntry qentry[] = {{"S:", NULL, FALSE},
1471 {"SSV:", NULL, FALSE},
1472 {"R:", NULL, FALSE},
1473 {"NG:", NULL, FALSE},
1474 {"MAID:", NULL, FALSE},
1475 {"NAID:", NULL, FALSE},
1476 {"SCF:", NULL, FALSE},
1477 {"RMID:", NULL, FALSE},
1478 {"FMID:", NULL, FALSE},
1479 {"X-Claws-Privacy-System:", NULL, FALSE},
1480 {"X-Claws-Encrypt:", NULL, FALSE},
1481 {"X-Claws-Encrypt-Data:", NULL, FALSE},
1482 {"X-Claws-End-Special-Headers:", NULL, FALSE},
1483 {"X-Sylpheed-Privacy-System:", NULL, FALSE},
1484 {"X-Sylpheed-Encrypt:", NULL, FALSE},
1485 {"X-Sylpheed-Encrypt-Data:", NULL, FALSE},
1486 {"X-Sylpheed-End-Special-Headers:", NULL, FALSE},
1487 {NULL, NULL, FALSE}};
1490 gint mailval = 0, newsval = 0;
1492 gchar *smtpserver = NULL;
1493 GSList *to_list = NULL;
1494 GSList *newsgroup_list = NULL;
1495 gchar *savecopyfolder = NULL;
1496 gchar *replymessageid = NULL;
1497 gchar *fwdmessageid = NULL;
1498 gchar *privacy_system = NULL;
1499 gboolean encrypt = FALSE;
1500 gchar *encrypt_data = NULL;
1501 gchar buf[BUFFSIZE];
1503 PrefsAccount *mailac = NULL, *newsac = NULL;
1504 gboolean save_clear_text = TRUE;
1505 gchar *tmp_enc_file = NULL;
1509 g_return_val_if_fail(file != NULL, -1);
1511 if ((fp = g_fopen(file, "rb")) == NULL) {
1512 FILE_OP_ERROR(file, "fopen");
1514 if (*errstr) g_free(*errstr);
1515 *errstr = g_strdup_printf(_("Couldn't open file %s."), file);
1520 while ((hnum = procheader_get_one_field(buf, sizeof(buf), fp, qentry))
1522 gchar *p = buf + strlen(qentry[hnum].name);
1530 if (smtpserver == NULL)
1531 smtpserver = g_strdup(p);
1534 to_list = address_list_append(to_list, p);
1537 newsgroup_list = newsgroup_list_append(newsgroup_list, p);
1539 case Q_MAIL_ACCOUNT_ID:
1540 mailac = account_find_from_id(atoi(p));
1542 case Q_NEWS_ACCOUNT_ID:
1543 newsac = account_find_from_id(atoi(p));
1545 case Q_SAVE_COPY_FOLDER:
1546 if (savecopyfolder == NULL)
1547 savecopyfolder = g_strdup(p);
1549 case Q_REPLY_MESSAGE_ID:
1550 if (replymessageid == NULL)
1551 replymessageid = g_strdup(p);
1553 case Q_FWD_MESSAGE_ID:
1554 if (fwdmessageid == NULL)
1555 fwdmessageid = g_strdup(p);
1557 case Q_PRIVACY_SYSTEM:
1558 case Q_PRIVACY_SYSTEM_OLD:
1559 if (privacy_system == NULL)
1560 privacy_system = g_strdup(p);
1567 case Q_ENCRYPT_DATA:
1568 case Q_ENCRYPT_DATA_OLD:
1569 if (encrypt_data == NULL)
1570 encrypt_data = g_strdup(p);
1573 case Q_CLAWS_HDRS_OLD:
1574 /* end of special headers reached */
1575 goto send_mail; /* can't "break;break;" */
1579 filepos = ftell(fp);
1584 if (mailac && mailac->save_encrypted_as_clear_text
1585 && !mailac->encrypt_to_self)
1586 save_clear_text = TRUE;
1588 save_clear_text = FALSE;
1593 mimeinfo = procmime_scan_queue_file(file);
1594 if (!privacy_encrypt(privacy_system, mimeinfo, encrypt_data)
1595 || (fp = my_tmpfile()) == NULL
1596 || procmime_write_mimeinfo(mimeinfo, fp) < 0) {
1599 procmime_mimeinfo_free_all(mimeinfo);
1602 slist_free_strings(to_list);
1603 g_slist_free(to_list);
1604 slist_free_strings(newsgroup_list);
1605 g_slist_free(newsgroup_list);
1606 g_free(savecopyfolder);
1607 g_free(replymessageid);
1608 g_free(fwdmessageid);
1609 g_free(privacy_system);
1610 g_free(encrypt_data);
1612 if (*errstr) g_free(*errstr);
1613 *errstr = g_strdup_printf(_("Couldn't encrypt the email: %s"),
1614 privacy_get_error());
1620 if (!save_clear_text) {
1621 gchar *content = NULL;
1622 FILE *tmpfp = get_tmpfile_in_dir(get_mime_tmp_dir(), &tmp_enc_file);
1626 content = file_read_stream_to_str(fp);
1629 str_write_to_file(content, tmp_enc_file);
1632 g_warning("couldn't get tempfile\n");
1636 procmime_mimeinfo_free_all(mimeinfo);
1642 debug_print("Sending message by mail\n");
1645 if (*errstr) g_free(*errstr);
1646 *errstr = g_strdup_printf(_("Queued message header is broken."));
1649 } else if (mailac && mailac->use_mail_command &&
1650 mailac->mail_command && (* mailac->mail_command)) {
1651 mailval = send_message_local(mailac->mail_command, fp);
1655 mailac = account_find_from_smtp_server(from, smtpserver);
1657 g_warning("Account not found. "
1658 "Using current account...\n");
1659 mailac = cur_account;
1664 mailval = send_message_smtp_full(mailac, to_list, fp, keep_session);
1665 if (mailval == -1 && errstr) {
1666 if (*errstr) g_free(*errstr);
1667 *errstr = g_strdup_printf(_("An error happened during SMTP session."));
1670 PrefsAccount tmp_ac;
1672 g_warning("Account not found.\n");
1674 memset(&tmp_ac, 0, sizeof(PrefsAccount));
1675 tmp_ac.address = from;
1676 tmp_ac.smtp_server = smtpserver;
1677 tmp_ac.smtpport = SMTP_PORT;
1678 mailval = send_message_smtp(&tmp_ac, to_list, fp);
1679 if (mailval == -1 && errstr) {
1680 if (*errstr) g_free(*errstr);
1681 *errstr = g_strdup_printf(_("No specific account has been found to "
1682 "send, and an error happened during SMTP session."));
1686 } else if (!to_list && !newsgroup_list) {
1688 if (*errstr) g_free(*errstr);
1689 *errstr = g_strdup(_("Couldn't determine sending informations. "
1690 "Maybe the email hasn't been generated by Claws Mail."));
1695 fseek(fp, filepos, SEEK_SET);
1696 if (newsgroup_list && (mailval == 0)) {
1701 /* write to temporary file */
1702 tmp = g_strdup_printf("%s%ctmp%p", g_get_tmp_dir(),
1703 G_DIR_SEPARATOR, file);
1704 if ((tmpfp = g_fopen(tmp, "wb")) == NULL) {
1705 FILE_OP_ERROR(tmp, "fopen");
1707 alertpanel_error(_("Couldn't create temporary file for news sending."));
1709 if (change_file_mode_rw(tmpfp, tmp) < 0) {
1710 FILE_OP_ERROR(tmp, "chmod");
1711 g_warning("can't change file mode\n");
1714 while ((newsval == 0) && fgets(buf, sizeof(buf), fp) != NULL) {
1715 if (fputs(buf, tmpfp) == EOF) {
1716 FILE_OP_ERROR(tmp, "fputs");
1719 if (*errstr) g_free(*errstr);
1720 *errstr = g_strdup_printf(_("Error when writing temporary file for news sending."));
1727 debug_print("Sending message by news\n");
1729 folder = FOLDER(newsac->folder);
1731 newsval = news_post(folder, tmp);
1732 if (newsval < 0 && errstr) {
1733 if (*errstr) g_free(*errstr);
1734 *errstr = g_strdup_printf(_("Error occurred while posting the message to %s."),
1735 newsac->nntp_server);
1745 /* save message to outbox */
1746 if (mailval == 0 && newsval == 0 && savecopyfolder) {
1749 debug_print("saving sent message...\n");
1751 outbox = folder_find_item_from_identifier(savecopyfolder);
1753 outbox = folder_get_default_outbox();
1755 if (save_clear_text || tmp_enc_file == NULL) {
1756 gboolean saved = FALSE;
1757 if (queue && msgnum > 0) {
1758 MsgInfo *queued_mail = folder_item_get_msginfo(queue, msgnum);
1759 if (folder_item_copy_msg(outbox, queued_mail) >= 0)
1761 procmsg_msginfo_free(queued_mail);
1764 procmsg_save_to_outbox(outbox, file, TRUE);
1766 procmsg_save_to_outbox(outbox, tmp_enc_file, FALSE);
1770 if (tmp_enc_file != NULL) {
1771 g_unlink(tmp_enc_file);
1773 tmp_enc_file = NULL;
1776 if (replymessageid != NULL || fwdmessageid != NULL) {
1780 if (replymessageid != NULL)
1781 tokens = g_strsplit(replymessageid, "\t", 0);
1783 tokens = g_strsplit(fwdmessageid, "\t", 0);
1784 item = folder_find_item_from_identifier(tokens[0]);
1786 /* check if queued message has valid folder and message id */
1787 if (item != NULL && tokens[2] != NULL) {
1790 msginfo = folder_item_get_msginfo(item, atoi(tokens[1]));
1792 /* check if referring message exists and has a message id */
1793 if ((msginfo != NULL) &&
1794 (msginfo->msgid != NULL) &&
1795 (strcmp(msginfo->msgid, tokens[2]) != 0)) {
1796 procmsg_msginfo_free(msginfo);
1800 if (msginfo == NULL) {
1801 msginfo = folder_item_get_msginfo_by_msgid(item, tokens[2]);
1804 if (msginfo != NULL) {
1805 if (replymessageid != NULL) {
1806 procmsg_msginfo_unset_flags(msginfo, MSG_FORWARDED, 0);
1807 procmsg_msginfo_set_flags(msginfo, MSG_REPLIED, 0);
1809 procmsg_msginfo_unset_flags(msginfo, MSG_REPLIED, 0);
1810 procmsg_msginfo_set_flags(msginfo, MSG_FORWARDED, 0);
1812 procmsg_msginfo_free(msginfo);
1820 slist_free_strings(to_list);
1821 g_slist_free(to_list);
1822 slist_free_strings(newsgroup_list);
1823 g_slist_free(newsgroup_list);
1824 g_free(savecopyfolder);
1825 g_free(replymessageid);
1826 g_free(fwdmessageid);
1827 g_free(privacy_system);
1828 g_free(encrypt_data);
1830 return (newsval != 0 ? newsval : mailval);
1833 gint procmsg_send_message_queue(const gchar *file, gchar **errstr, FolderItem *queue, gint msgnum)
1835 gint result = procmsg_send_message_queue_full(file, FALSE, errstr, queue, msgnum);
1836 toolbar_main_set_sensitive(mainwindow_get_mainwindow());
1840 static void update_folder_msg_counts(FolderItem *item, MsgInfo *msginfo, MsgPermFlags old_flags)
1842 MsgPermFlags new_flags = msginfo->flags.perm_flags;
1845 if (!(old_flags & MSG_NEW) && (new_flags & MSG_NEW)) {
1849 if ((old_flags & MSG_NEW) && !(new_flags & MSG_NEW)) {
1854 if (!(old_flags & MSG_UNREAD) && (new_flags & MSG_UNREAD)) {
1855 item->unread_msgs++;
1856 if (procmsg_msg_has_marked_parent(msginfo))
1857 item->unreadmarked_msgs++;
1860 if ((old_flags & MSG_UNREAD) && !(new_flags & MSG_UNREAD)) {
1861 item->unread_msgs--;
1862 if (procmsg_msg_has_marked_parent(msginfo))
1863 item->unreadmarked_msgs--;
1867 if (!(old_flags & MSG_MARKED) && (new_flags & MSG_MARKED)) {
1868 procmsg_update_unread_children(msginfo, TRUE);
1869 item->marked_msgs++;
1872 if ((old_flags & MSG_MARKED) && !(new_flags & MSG_MARKED)) {
1873 procmsg_update_unread_children(msginfo, FALSE);
1874 item->marked_msgs--;
1878 void procmsg_msginfo_set_flags(MsgInfo *msginfo, MsgPermFlags perm_flags, MsgTmpFlags tmp_flags)
1881 MsgInfoUpdate msginfo_update;
1882 MsgPermFlags perm_flags_new, perm_flags_old;
1883 MsgTmpFlags tmp_flags_old;
1885 g_return_if_fail(msginfo != NULL);
1886 item = msginfo->folder;
1887 g_return_if_fail(item != NULL);
1889 debug_print("Setting flags for message %d in folder %s\n", msginfo->msgnum, item->path);
1891 /* Perm Flags handling */
1892 perm_flags_old = msginfo->flags.perm_flags;
1893 perm_flags_new = msginfo->flags.perm_flags | perm_flags;
1894 if ((perm_flags & MSG_IGNORE_THREAD) || (perm_flags_old & MSG_IGNORE_THREAD)) {
1895 perm_flags_new &= ~(MSG_NEW | MSG_UNREAD);
1898 if (perm_flags_old != perm_flags_new) {
1899 folder_item_change_msg_flags(msginfo->folder, msginfo, perm_flags_new);
1901 update_folder_msg_counts(item, msginfo, perm_flags_old);
1905 /* Tmp flags handling */
1906 tmp_flags_old = msginfo->flags.tmp_flags;
1907 msginfo->flags.tmp_flags |= tmp_flags;
1909 /* update notification */
1910 if ((perm_flags_old != perm_flags_new) || (tmp_flags_old != msginfo->flags.tmp_flags)) {
1911 msginfo_update.msginfo = msginfo;
1912 msginfo_update.flags = MSGINFO_UPDATE_FLAGS;
1913 hooks_invoke(MSGINFO_UPDATE_HOOKLIST, &msginfo_update);
1914 folder_item_update(msginfo->folder, F_ITEM_UPDATE_MSGCNT);
1918 void procmsg_msginfo_unset_flags(MsgInfo *msginfo, MsgPermFlags perm_flags, MsgTmpFlags tmp_flags)
1921 MsgInfoUpdate msginfo_update;
1922 MsgPermFlags perm_flags_new, perm_flags_old;
1923 MsgTmpFlags tmp_flags_old;
1925 g_return_if_fail(msginfo != NULL);
1926 item = msginfo->folder;
1927 g_return_if_fail(item != NULL);
1929 debug_print("Unsetting flags for message %d in folder %s\n", msginfo->msgnum, item->path);
1931 /* Perm Flags handling */
1932 perm_flags_old = msginfo->flags.perm_flags;
1933 perm_flags_new = msginfo->flags.perm_flags & ~perm_flags;
1935 if (perm_flags_old != perm_flags_new) {
1936 folder_item_change_msg_flags(msginfo->folder, msginfo, perm_flags_new);
1938 update_folder_msg_counts(item, msginfo, perm_flags_old);
1941 /* Tmp flags hanlding */
1942 tmp_flags_old = msginfo->flags.tmp_flags;
1943 msginfo->flags.tmp_flags &= ~tmp_flags;
1945 /* update notification */
1946 if ((perm_flags_old != perm_flags_new) || (tmp_flags_old != msginfo->flags.tmp_flags)) {
1947 msginfo_update.msginfo = msginfo;
1948 msginfo_update.flags = MSGINFO_UPDATE_FLAGS;
1949 hooks_invoke(MSGINFO_UPDATE_HOOKLIST, &msginfo_update);
1950 folder_item_update(msginfo->folder, F_ITEM_UPDATE_MSGCNT);
1954 void procmsg_msginfo_change_flags(MsgInfo *msginfo,
1955 MsgPermFlags add_perm_flags, MsgTmpFlags add_tmp_flags,
1956 MsgPermFlags rem_perm_flags, MsgTmpFlags rem_tmp_flags)
1959 MsgInfoUpdate msginfo_update;
1960 MsgPermFlags perm_flags_new, perm_flags_old;
1961 MsgTmpFlags tmp_flags_old;
1963 g_return_if_fail(msginfo != NULL);
1964 item = msginfo->folder;
1965 g_return_if_fail(item != NULL);
1967 debug_print("Changing flags for message %d in folder %s\n", msginfo->msgnum, item->path);
1969 /* Perm Flags handling */
1970 perm_flags_old = msginfo->flags.perm_flags;
1971 perm_flags_new = (msginfo->flags.perm_flags & ~rem_perm_flags) | add_perm_flags;
1972 if ((add_perm_flags & MSG_IGNORE_THREAD) || (perm_flags_old & MSG_IGNORE_THREAD)) {
1973 perm_flags_new &= ~(MSG_NEW | MSG_UNREAD);
1976 if (perm_flags_old != perm_flags_new) {
1977 folder_item_change_msg_flags(msginfo->folder, msginfo, perm_flags_new);
1979 update_folder_msg_counts(item, msginfo, perm_flags_old);
1983 /* Tmp flags handling */
1984 tmp_flags_old = msginfo->flags.tmp_flags;
1985 msginfo->flags.tmp_flags &= ~rem_tmp_flags;
1986 msginfo->flags.tmp_flags |= add_tmp_flags;
1988 /* update notification */
1989 if ((perm_flags_old != perm_flags_new) || (tmp_flags_old != msginfo->flags.tmp_flags)) {
1990 msginfo_update.msginfo = msginfo;
1991 msginfo_update.flags = MSGINFO_UPDATE_FLAGS;
1992 hooks_invoke(MSGINFO_UPDATE_HOOKLIST, &msginfo_update);
1993 folder_item_update(msginfo->folder, F_ITEM_UPDATE_MSGCNT);
1998 *\brief check for flags (e.g. mark) in prior msgs of current thread
2000 *\param info Current message
2001 *\param perm_flags Flags to be checked
2002 *\param parentmsgs Hash of prior msgs to avoid loops
2004 *\return gboolean TRUE if perm_flags are found
2006 gboolean procmsg_msg_has_flagged_parent_real(MsgInfo *info,
2007 MsgPermFlags perm_flags, GHashTable *parentmsgs)
2011 g_return_val_if_fail(info != NULL, FALSE);
2013 if (info != NULL && info->folder != NULL && info->inreplyto != NULL) {
2014 tmp = folder_item_get_msginfo_by_msgid(info->folder,
2016 if (tmp && (tmp->flags.perm_flags & perm_flags)) {
2017 procmsg_msginfo_free(tmp);
2019 } else if (tmp != NULL) {
2022 if (g_hash_table_lookup(parentmsgs, info)) {
2023 debug_print("loop detected: %d\n",
2027 g_hash_table_insert(parentmsgs, info, "1");
2028 result = procmsg_msg_has_flagged_parent_real(
2029 tmp, perm_flags, parentmsgs);
2031 procmsg_msginfo_free(tmp);
2041 *\brief Callback for cleaning up hash of parentmsgs
2043 gboolean parentmsgs_hash_remove(gpointer key,
2051 *\brief Set up list of parentmsgs
2052 * See procmsg_msg_has_flagged_parent_real()
2054 gboolean procmsg_msg_has_flagged_parent(MsgInfo *info, MsgPermFlags perm_flags)
2057 static GHashTable *parentmsgs = NULL;
2059 if (parentmsgs == NULL)
2060 parentmsgs = g_hash_table_new(NULL, NULL);
2062 result = procmsg_msg_has_flagged_parent_real(info, perm_flags, parentmsgs);
2063 g_hash_table_foreach_remove(parentmsgs, parentmsgs_hash_remove, NULL);
2069 *\brief Check if msgs prior in thread are marked
2070 * See procmsg_msg_has_flagged_parent_real()
2072 gboolean procmsg_msg_has_marked_parent(MsgInfo *info)
2074 return procmsg_msg_has_flagged_parent(info, MSG_MARKED);
2078 GSList *procmsg_find_children_func(MsgInfo *info,
2079 GSList *children, GSList *all)
2083 g_return_val_if_fail(info!=NULL, children);
2084 if (info->msgid == NULL)
2087 for (cur = all; cur != NULL; cur = g_slist_next(cur)) {
2088 MsgInfo *tmp = (MsgInfo *)cur->data;
2089 if (tmp->inreplyto && !strcmp(tmp->inreplyto, info->msgid)) {
2090 /* Check if message is already in the list */
2091 if ((children == NULL) ||
2092 (g_slist_index(children, tmp) == -1)) {
2093 children = g_slist_prepend(children,
2094 procmsg_msginfo_new_ref(tmp));
2095 children = procmsg_find_children_func(tmp,
2104 GSList *procmsg_find_children (MsgInfo *info)
2109 g_return_val_if_fail(info!=NULL, NULL);
2110 all = folder_item_get_msg_list(info->folder);
2111 children = procmsg_find_children_func(info, NULL, all);
2112 if (children != NULL) {
2113 for (cur = all; cur != NULL; cur = g_slist_next(cur)) {
2114 /* this will not free the used pointers
2115 created with procmsg_msginfo_new_ref */
2116 procmsg_msginfo_free((MsgInfo *)cur->data);
2124 void procmsg_update_unread_children(MsgInfo *info, gboolean newly_marked)
2126 GSList *children = procmsg_find_children(info);
2128 for (cur = children; cur != NULL; cur = g_slist_next(cur)) {
2129 MsgInfo *tmp = (MsgInfo *)cur->data;
2130 if(MSG_IS_UNREAD(tmp->flags) && !MSG_IS_IGNORE_THREAD(tmp->flags)) {
2132 info->folder->unreadmarked_msgs++;
2134 info->folder->unreadmarked_msgs--;
2135 folder_item_update(info->folder, F_ITEM_UPDATE_MSGCNT);
2137 procmsg_msginfo_free(tmp);
2139 g_slist_free(children);
2143 * Set the destination folder for a copy or move operation
2145 * \param msginfo The message which's destination folder is changed
2146 * \param to_folder The destination folder for the operation
2148 void procmsg_msginfo_set_to_folder(MsgInfo *msginfo, FolderItem *to_folder)
2150 if(msginfo->to_folder != NULL) {
2151 msginfo->to_folder->op_count--;
2152 folder_item_update(msginfo->to_folder, F_ITEM_UPDATE_MSGCNT);
2154 msginfo->to_folder = to_folder;
2155 if(to_folder != NULL) {
2156 to_folder->op_count++;
2157 folder_item_update(msginfo->to_folder, F_ITEM_UPDATE_MSGCNT);
2162 * Apply filtering actions to the msginfo
2164 * \param msginfo The MsgInfo describing the message that should be filtered
2165 * \return TRUE if the message was moved and MsgInfo is now invalid,
2168 gboolean procmsg_msginfo_filter(MsgInfo *msginfo, PrefsAccount* ac_prefs)
2170 MailFilteringData mail_filtering_data;
2172 mail_filtering_data.msginfo = msginfo;
2173 mail_filtering_data.msglist = NULL;
2174 mail_filtering_data.filtered = NULL;
2175 mail_filtering_data.unfiltered = NULL;
2176 if (hooks_invoke(MAIL_FILTERING_HOOKLIST, &mail_filtering_data)) {
2180 /* filter if enabled in prefs or move to inbox if not */
2181 if((filtering_rules != NULL) &&
2182 filter_message_by_msginfo(filtering_rules, msginfo, ac_prefs)) {
2189 void procmsg_msglist_filter(GSList *list, PrefsAccount *ac,
2190 GSList **filtered, GSList **unfiltered,
2193 GSList *cur, *to_do = NULL;
2194 gint total = 0, curnum = 0;
2195 MailFilteringData mail_filtering_data;
2197 g_return_if_fail(filtered != NULL);
2198 g_return_if_fail(unfiltered != NULL);
2206 total = g_slist_length(list);
2210 *unfiltered = g_slist_copy(list);
2214 statusbar_print_all(_("Filtering messages...\n"));
2216 mail_filtering_data.msginfo = NULL;
2217 mail_filtering_data.msglist = list;
2218 mail_filtering_data.filtered = NULL;
2219 mail_filtering_data.unfiltered = NULL;
2221 hooks_invoke(MAIL_LISTFILTERING_HOOKLIST, &mail_filtering_data);
2223 if (mail_filtering_data.filtered == NULL &&
2224 mail_filtering_data.unfiltered == NULL) {
2225 /* nothing happened */
2226 debug_print(MAIL_LISTFILTERING_HOOKLIST " did nothing. filtering whole list normally.\n");
2229 if (mail_filtering_data.filtered != NULL) {
2230 /* keep track of what's been filtered by the hooks */
2231 debug_print(MAIL_LISTFILTERING_HOOKLIST " filtered some stuff. total %d filtered %d unfilt %d.\n",
2232 g_slist_length(list),
2233 g_slist_length(mail_filtering_data.filtered),
2234 g_slist_length(mail_filtering_data.unfiltered));
2236 *filtered = g_slist_copy(mail_filtering_data.filtered);
2238 if (mail_filtering_data.unfiltered != NULL) {
2239 /* what the hooks didn't handle will go in filtered or
2240 * unfiltered in the next loop */
2241 debug_print(MAIL_LISTFILTERING_HOOKLIST " left unfiltered stuff. total %d filtered %d unfilt %d.\n",
2242 g_slist_length(list),
2243 g_slist_length(mail_filtering_data.filtered),
2244 g_slist_length(mail_filtering_data.unfiltered));
2245 to_do = mail_filtering_data.unfiltered;
2248 for (cur = to_do; cur; cur = cur->next) {
2249 MsgInfo *info = (MsgInfo *)cur->data;
2250 if (procmsg_msginfo_filter(info, ac))
2251 *filtered = g_slist_prepend(*filtered, info);
2253 *unfiltered = g_slist_prepend(*unfiltered, info);
2254 statusbar_progress_all(curnum++, total, prefs_common.statusbar_update_step);
2257 g_slist_free(mail_filtering_data.filtered);
2258 g_slist_free(mail_filtering_data.unfiltered);
2260 *filtered = g_slist_reverse(*filtered);
2261 *unfiltered = g_slist_reverse(*unfiltered);
2263 statusbar_progress_all(0,0,0);
2264 statusbar_pop_all();
2267 MsgInfo *procmsg_msginfo_new_from_mimeinfo(MsgInfo *src_msginfo, MimeInfo *mimeinfo)
2269 MsgInfo *tmp_msginfo = NULL;
2270 MsgFlags flags = {0, 0};
2271 gchar *tmpfile = get_tmp_file();
2272 FILE *fp = g_fopen(tmpfile, "wb");
2274 if (!mimeinfo || mimeinfo->type != MIMETYPE_MESSAGE ||
2275 g_ascii_strcasecmp(mimeinfo->subtype, "rfc822")) {
2276 g_warning("procmsg_msginfo_new_from_mimeinfo(): unsuitable mimeinfo");
2283 if (fp && procmime_write_mimeinfo(mimeinfo, fp) >= 0) {
2286 tmp_msginfo = procheader_parse_file(
2293 if (tmp_msginfo != NULL) {
2295 tmp_msginfo->folder = src_msginfo->folder;
2296 tmp_msginfo->plaintext_file = g_strdup(tmpfile);
2298 g_warning("procmsg_msginfo_new_from_mimeinfo(): Can't generate new msginfo");
2306 static GSList *spam_learners = NULL;
2308 void procmsg_register_spam_learner (int (*learn_func)(MsgInfo *info, GSList *list, gboolean spam))
2310 if (!g_slist_find(spam_learners, learn_func))
2311 spam_learners = g_slist_append(spam_learners, learn_func);
2312 if (mainwindow_get_mainwindow()) {
2313 main_window_set_menu_sensitive(mainwindow_get_mainwindow());
2314 summary_set_menu_sensitive(
2315 mainwindow_get_mainwindow()->summaryview);
2316 toolbar_main_set_sensitive(mainwindow_get_mainwindow());
2320 void procmsg_unregister_spam_learner (int (*learn_func)(MsgInfo *info, GSList *list, gboolean spam))
2322 spam_learners = g_slist_remove(spam_learners, learn_func);
2323 if (mainwindow_get_mainwindow()) {
2324 main_window_set_menu_sensitive(mainwindow_get_mainwindow());
2325 summary_set_menu_sensitive(
2326 mainwindow_get_mainwindow()->summaryview);
2327 toolbar_main_set_sensitive(mainwindow_get_mainwindow());
2331 gboolean procmsg_spam_can_learn(void)
2333 return g_slist_length(spam_learners) > 0;
2336 int procmsg_spam_learner_learn (MsgInfo *info, GSList *list, gboolean spam)
2338 GSList *cur = spam_learners;
2340 for (; cur; cur = cur->next) {
2341 int ((*func)(MsgInfo *info, GSList *list, gboolean spam)) = cur->data;
2342 ret |= func(info, list, spam);
2347 static gchar *spam_folder_item = NULL;
2348 void procmsg_spam_set_folder (const char *item_identifier)
2350 g_free(spam_folder_item);
2351 if (item_identifier)
2352 spam_folder_item = g_strdup(item_identifier);
2354 spam_folder_item = NULL;
2357 FolderItem *procmsg_spam_get_folder (void)
2359 FolderItem *item = spam_folder_item ? folder_find_item_from_identifier(spam_folder_item) : NULL;
2360 return item ? item : folder_get_default_trash();
2363 static void item_has_queued_mails(FolderItem *item, gpointer data)
2365 gboolean *result = (gboolean *)data;
2366 if (*result == TRUE)
2368 if (folder_has_parent_of_type(item, F_QUEUE) && item->total_msgs > 0)
2372 gboolean procmsg_have_queued_mails_fast (void)
2374 gboolean result = FALSE;
2375 folder_func_to_all_folders(item_has_queued_mails, &result);