2 * Sylpheed -- a GTK+ based, lightweight, and fast e-mail client
3 * Copyright (C) 1999-2007 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, gboolean *queued_removed);
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 gboolean queued_removed = FALSE;
954 if (procmsg_send_message_queue_full(file,
955 !procmsg_is_last_for_account(queue, msginfo, elem),
956 errstr, queue, msginfo->msgnum, &queued_removed) < 0) {
957 g_warning("Sending queued message %d failed.\n",
963 folder_item_remove_msg(queue, msginfo->msgnum);
968 /* FIXME: supposedly if only one message is locked, and queue
969 * is being flushed, the following free says something like
970 * "freeing msg ## in folder (nil)". */
971 procmsg_msginfo_free(msginfo);
974 g_slist_free(sorted_list);
975 folder_item_scan(queue);
977 if (queue->node && queue->node->children) {
978 node = queue->node->children;
979 while (node != NULL) {
982 res = procmsg_send_queue(FOLDER_ITEM(node->data), save_msgs, errstr);
990 send_queue_lock = FALSE;
992 toolbar_main_set_sensitive(mainwindow_get_mainwindow());
994 return (err != 0 ? -err : sent);
997 gboolean procmsg_is_sending(void)
999 return send_queue_lock;
1003 *\brief Determine if a queue folder is empty
1005 *\param queue Queue folder to process
1007 *\return TRUE if the queue folder is empty, otherwise return FALSE
1009 gboolean procmsg_queue_is_empty(FolderItem *queue)
1012 gboolean res = FALSE;
1014 queue = folder_get_default_queue();
1015 g_return_val_if_fail(queue != NULL, TRUE);
1017 folder_item_scan(queue);
1018 list = folder_item_get_msg_list(queue);
1019 res = (list == NULL);
1020 procmsg_msg_list_free(list);
1024 if (queue->node && queue->node->children) {
1025 node = queue->node->children;
1026 while (node != NULL) {
1028 if (!procmsg_queue_is_empty(FOLDER_ITEM(node->data)))
1037 gint procmsg_remove_special_headers(const gchar *in, const gchar *out)
1040 gchar buf[BUFFSIZE];
1042 if ((fp = g_fopen(in, "rb")) == NULL) {
1043 FILE_OP_ERROR(in, "fopen");
1046 if ((outfp = g_fopen(out, "wb")) == NULL) {
1047 FILE_OP_ERROR(out, "fopen");
1051 while (fgets(buf, sizeof(buf), fp) != NULL) {
1053 if ((!strncmp(buf, "X-Claws-End-Special-Headers: 1",
1054 strlen("X-Claws-End-Special-Headers:"))) ||
1055 (!strncmp(buf, "X-Sylpheed-End-Special-Headers: 1",
1056 strlen("X-Sylpheed-End-Special-Headers:"))))
1059 if (buf[0] == '\r' || buf[0] == '\n') break;
1060 /* from other mailers */
1061 if (!strncmp(buf, "Date: ", 6)
1062 || !strncmp(buf, "To: ", 4)
1063 || !strncmp(buf, "From: ", 6)
1064 || !strncmp(buf, "Subject: ", 9)) {
1069 while (fgets(buf, sizeof(buf), fp) != NULL)
1076 gint procmsg_save_to_outbox(FolderItem *outbox, const gchar *file,
1080 MsgInfo *msginfo, *tmp_msginfo;
1081 MsgFlags flag = {0, 0};
1083 debug_print("saving sent message...\n");
1086 outbox = folder_get_default_outbox();
1087 g_return_val_if_fail(outbox != NULL, -1);
1089 /* remove queueing headers */
1091 gchar tmp[MAXPATHLEN + 1];
1093 g_snprintf(tmp, sizeof(tmp), "%s%ctmpmsg.out.%08x",
1094 get_rc_dir(), G_DIR_SEPARATOR, (guint) rand());
1096 if (procmsg_remove_special_headers(file, tmp) !=0)
1099 folder_item_scan(outbox);
1100 if ((num = folder_item_add_msg(outbox, tmp, &flag, TRUE)) < 0) {
1101 g_warning("can't save message\n");
1106 folder_item_scan(outbox);
1107 if ((num = folder_item_add_msg
1108 (outbox, file, &flag, FALSE)) < 0) {
1109 g_warning("can't save message\n");
1113 msginfo = folder_item_get_msginfo(outbox, num); /* refcnt++ */
1114 tmp_msginfo = procmsg_msginfo_get_full_info(msginfo); /* refcnt++ */
1115 if (msginfo != NULL) {
1116 procmsg_msginfo_unset_flags(msginfo, ~0, 0);
1117 procmsg_msginfo_free(msginfo); /* refcnt-- */
1118 /* tmp_msginfo == msginfo */
1119 if (tmp_msginfo && msginfo->extradata &&
1120 (msginfo->extradata->dispositionnotificationto ||
1121 msginfo->extradata->returnreceiptto)) {
1122 procmsg_msginfo_set_flags(msginfo, MSG_RETRCPT_SENT, 0);
1124 procmsg_msginfo_free(tmp_msginfo); /* refcnt-- */
1130 void procmsg_print_message(MsgInfo *msginfo, const gchar *cmdline)
1132 static const gchar *def_cmd = "lpr %s";
1133 static guint id = 0;
1139 g_return_if_fail(msginfo);
1141 if (procmime_msginfo_is_encrypted(msginfo))
1142 tmpfp = procmime_get_first_encrypted_text_content(msginfo);
1144 tmpfp = procmime_get_first_text_content(msginfo);
1145 if (tmpfp == NULL) {
1146 g_warning("Can't get text part\n");
1150 prtmp = g_strdup_printf("%s%cprinttmp.%08x",
1151 get_mime_tmp_dir(), G_DIR_SEPARATOR, id++);
1153 if ((prfp = g_fopen(prtmp, "wb")) == NULL) {
1154 FILE_OP_ERROR(prtmp, "fopen");
1160 if (msginfo->date) fprintf(prfp, "Date: %s\n", msginfo->date);
1161 if (msginfo->from) fprintf(prfp, "From: %s\n", msginfo->from);
1162 if (msginfo->to) fprintf(prfp, "To: %s\n", msginfo->to);
1163 if (msginfo->cc) fprintf(prfp, "Cc: %s\n", msginfo->cc);
1164 if (msginfo->newsgroups)
1165 fprintf(prfp, "Newsgroups: %s\n", msginfo->newsgroups);
1166 if (msginfo->subject) fprintf(prfp, "Subject: %s\n", msginfo->subject);
1169 while (fgets(buf, sizeof(buf), tmpfp) != NULL)
1175 if (cmdline && (p = strchr(cmdline, '%')) && *(p + 1) == 's' &&
1176 !strchr(p + 2, '%'))
1177 g_snprintf(buf, sizeof(buf) - 1, cmdline, prtmp);
1180 g_warning("Print command line is invalid: '%s'\n",
1182 g_snprintf(buf, sizeof(buf) - 1, def_cmd, prtmp);
1188 if (buf[strlen(buf) - 1] != '&') strcat(buf, "&");
1192 MsgInfo *procmsg_msginfo_new_ref(MsgInfo *msginfo)
1199 MsgInfo *procmsg_msginfo_new(void)
1201 MsgInfo *newmsginfo;
1203 newmsginfo = g_new0(MsgInfo, 1);
1204 newmsginfo->refcnt = 1;
1209 MsgInfo *procmsg_msginfo_copy(MsgInfo *msginfo)
1211 MsgInfo *newmsginfo;
1214 if (msginfo == NULL) return NULL;
1216 newmsginfo = g_new0(MsgInfo, 1);
1218 newmsginfo->refcnt = 1;
1220 #define MEMBCOPY(mmb) newmsginfo->mmb = msginfo->mmb
1221 #define MEMBDUP(mmb) newmsginfo->mmb = msginfo->mmb ? \
1222 g_strdup(msginfo->mmb) : NULL
1237 MEMBDUP(newsgroups);
1244 MEMBCOPY(to_folder);
1246 if (msginfo->extradata) {
1247 newmsginfo->extradata = g_new0(MsgInfoExtraData, 1);
1248 MEMBDUP(extradata->face);
1249 MEMBDUP(extradata->xface);
1250 MEMBDUP(extradata->dispositionnotificationto);
1251 MEMBDUP(extradata->returnreceiptto);
1252 MEMBDUP(extradata->partial_recv);
1253 MEMBDUP(extradata->account_server);
1254 MEMBDUP(extradata->account_login);
1255 MEMBDUP(extradata->list_post);
1256 MEMBDUP(extradata->list_subscribe);
1257 MEMBDUP(extradata->list_unsubscribe);
1258 MEMBDUP(extradata->list_help);
1259 MEMBDUP(extradata->list_archive);
1260 MEMBDUP(extradata->list_owner);
1263 refs = msginfo->references;
1264 for (refs = msginfo->references; refs != NULL; refs = refs->next) {
1265 newmsginfo->references = g_slist_prepend
1266 (newmsginfo->references, g_strdup(refs->data));
1268 newmsginfo->references = g_slist_reverse(newmsginfo->references);
1271 MEMBDUP(plaintext_file);
1276 MsgInfo *procmsg_msginfo_get_full_info(MsgInfo *msginfo)
1278 MsgInfo *full_msginfo;
1281 if (msginfo == NULL) return NULL;
1283 file = procmsg_get_message_file_path(msginfo);
1284 if (!file || !is_file_exist(file)) {
1286 file = procmsg_get_message_file(msginfo);
1288 if (!file || !is_file_exist(file)) {
1289 g_warning("procmsg_msginfo_get_full_info(): can't get message file.\n");
1293 full_msginfo = procheader_parse_file(file, msginfo->flags, TRUE, FALSE);
1295 if (!full_msginfo) return NULL;
1297 msginfo->total_size = full_msginfo->total_size;
1298 msginfo->planned_download = full_msginfo->planned_download;
1300 if (full_msginfo->extradata) {
1301 if (!msginfo->extradata)
1302 msginfo->extradata = g_new0(MsgInfoExtraData, 1);
1303 if (!msginfo->extradata->list_post)
1304 msginfo->extradata->list_post = g_strdup(full_msginfo->extradata->list_post);
1305 if (!msginfo->extradata->list_subscribe)
1306 msginfo->extradata->list_subscribe = g_strdup(full_msginfo->extradata->list_subscribe);
1307 if (!msginfo->extradata->list_unsubscribe)
1308 msginfo->extradata->list_unsubscribe = g_strdup(full_msginfo->extradata->list_unsubscribe);
1309 if (!msginfo->extradata->list_help)
1310 msginfo->extradata->list_help = g_strdup(full_msginfo->extradata->list_help);
1311 if (!msginfo->extradata->list_archive)
1312 msginfo->extradata->list_archive= g_strdup(full_msginfo->extradata->list_archive);
1313 if (!msginfo->extradata->list_owner)
1314 msginfo->extradata->list_owner = g_strdup(full_msginfo->extradata->list_owner);
1315 if (!msginfo->extradata->xface)
1316 msginfo->extradata->xface = g_strdup(full_msginfo->extradata->xface);
1317 if (!msginfo->extradata->face)
1318 msginfo->extradata->face = g_strdup(full_msginfo->extradata->face);
1319 if (!msginfo->extradata->dispositionnotificationto)
1320 msginfo->extradata->dispositionnotificationto =
1321 g_strdup(full_msginfo->extradata->dispositionnotificationto);
1322 if (!msginfo->extradata->returnreceiptto)
1323 msginfo->extradata->returnreceiptto = g_strdup
1324 (full_msginfo->extradata->returnreceiptto);
1325 if (!msginfo->extradata->partial_recv && full_msginfo->extradata->partial_recv)
1326 msginfo->extradata->partial_recv = g_strdup
1327 (full_msginfo->extradata->partial_recv);
1328 if (!msginfo->extradata->account_server && full_msginfo->extradata->account_server)
1329 msginfo->extradata->account_server = g_strdup
1330 (full_msginfo->extradata->account_server);
1331 if (!msginfo->extradata->account_login && full_msginfo->extradata->account_login)
1332 msginfo->extradata->account_login = g_strdup
1333 (full_msginfo->extradata->account_login);
1335 procmsg_msginfo_free(full_msginfo);
1337 return procmsg_msginfo_new_ref(msginfo);
1340 void procmsg_msginfo_free(MsgInfo *msginfo)
1342 if (msginfo == NULL) return;
1345 if (msginfo->refcnt > 0)
1348 if (msginfo->to_folder) {
1349 msginfo->to_folder->op_count--;
1350 folder_item_update(msginfo->to_folder, F_ITEM_UPDATE_MSGCNT);
1353 g_free(msginfo->fromspace);
1355 g_free(msginfo->fromname);
1357 g_free(msginfo->date);
1358 g_free(msginfo->from);
1359 g_free(msginfo->to);
1360 g_free(msginfo->cc);
1361 g_free(msginfo->newsgroups);
1362 g_free(msginfo->subject);
1363 g_free(msginfo->msgid);
1364 g_free(msginfo->inreplyto);
1365 g_free(msginfo->xref);
1367 if (msginfo->extradata) {
1368 g_free(msginfo->extradata->returnreceiptto);
1369 g_free(msginfo->extradata->dispositionnotificationto);
1370 g_free(msginfo->extradata->xface);
1371 g_free(msginfo->extradata->face);
1372 g_free(msginfo->extradata->list_post);
1373 g_free(msginfo->extradata->list_subscribe);
1374 g_free(msginfo->extradata->list_unsubscribe);
1375 g_free(msginfo->extradata->list_help);
1376 g_free(msginfo->extradata->list_archive);
1377 g_free(msginfo->extradata->list_owner);
1378 g_free(msginfo->extradata->partial_recv);
1379 g_free(msginfo->extradata->account_server);
1380 g_free(msginfo->extradata->account_login);
1381 g_free(msginfo->extradata);
1383 slist_free_strings(msginfo->references);
1384 g_slist_free(msginfo->references);
1386 g_free(msginfo->plaintext_file);
1391 guint procmsg_msginfo_memusage(MsgInfo *msginfo)
1396 memusage += sizeof(MsgInfo);
1397 if (msginfo->fromname)
1398 memusage += strlen(msginfo->fromname);
1400 memusage += strlen(msginfo->date);
1402 memusage += strlen(msginfo->from);
1404 memusage += strlen(msginfo->to);
1406 memusage += strlen(msginfo->cc);
1407 if (msginfo->newsgroups)
1408 memusage += strlen(msginfo->newsgroups);
1409 if (msginfo->subject)
1410 memusage += strlen(msginfo->subject);
1412 memusage += strlen(msginfo->msgid);
1413 if (msginfo->inreplyto)
1414 memusage += strlen(msginfo->inreplyto);
1415 for (refs = msginfo->references; refs; refs=refs->next) {
1416 gchar *r = (gchar *)refs->data;
1417 memusage += r?strlen(r):0;
1419 if (msginfo->fromspace)
1420 memusage += strlen(msginfo->fromspace);
1422 if (msginfo->extradata) {
1423 memusage += sizeof(MsgInfoExtraData);
1424 if (msginfo->extradata->xface)
1425 memusage += strlen(msginfo->extradata->xface);
1426 if (msginfo->extradata->face)
1427 memusage += strlen(msginfo->extradata->face);
1428 if (msginfo->extradata->dispositionnotificationto)
1429 memusage += strlen(msginfo->extradata->dispositionnotificationto);
1430 if (msginfo->extradata->returnreceiptto)
1431 memusage += strlen(msginfo->extradata->returnreceiptto);
1433 if (msginfo->extradata->partial_recv)
1434 memusage += strlen(msginfo->extradata->partial_recv);
1435 if (msginfo->extradata->account_server)
1436 memusage += strlen(msginfo->extradata->account_server);
1437 if (msginfo->extradata->account_login)
1438 memusage += strlen(msginfo->extradata->account_login);
1440 if (msginfo->extradata->list_post)
1441 memusage += strlen(msginfo->extradata->list_post);
1442 if (msginfo->extradata->list_subscribe)
1443 memusage += strlen(msginfo->extradata->list_subscribe);
1444 if (msginfo->extradata->list_unsubscribe)
1445 memusage += strlen(msginfo->extradata->list_unsubscribe);
1446 if (msginfo->extradata->list_help)
1447 memusage += strlen(msginfo->extradata->list_help);
1448 if (msginfo->extradata->list_archive)
1449 memusage += strlen(msginfo->extradata->list_archive);
1450 if (msginfo->extradata->list_owner)
1451 memusage += strlen(msginfo->extradata->list_owner);
1456 gint procmsg_cmp_msgnum_for_sort(gconstpointer a, gconstpointer b)
1458 const MsgInfo *msginfo1 = a;
1459 const MsgInfo *msginfo2 = b;
1466 return msginfo1->msgnum - msginfo2->msgnum;
1469 static gint procmsg_send_message_queue_full(const gchar *file, gboolean keep_session, gchar **errstr,
1470 FolderItem *queue, gint msgnum, gboolean *queued_removed)
1472 static HeaderEntry qentry[] = {{"S:", NULL, FALSE},
1473 {"SSV:", NULL, FALSE},
1474 {"R:", NULL, FALSE},
1475 {"NG:", NULL, FALSE},
1476 {"MAID:", NULL, FALSE},
1477 {"NAID:", NULL, FALSE},
1478 {"SCF:", NULL, FALSE},
1479 {"RMID:", NULL, FALSE},
1480 {"FMID:", NULL, FALSE},
1481 {"X-Claws-Privacy-System:", NULL, FALSE},
1482 {"X-Claws-Encrypt:", NULL, FALSE},
1483 {"X-Claws-Encrypt-Data:", NULL, FALSE},
1484 {"X-Claws-End-Special-Headers:", NULL, FALSE},
1485 {"X-Sylpheed-Privacy-System:", NULL, FALSE},
1486 {"X-Sylpheed-Encrypt:", NULL, FALSE},
1487 {"X-Sylpheed-Encrypt-Data:", NULL, FALSE},
1488 {"X-Sylpheed-End-Special-Headers:", NULL, FALSE},
1489 {NULL, NULL, FALSE}};
1492 gint mailval = 0, newsval = 0;
1494 gchar *smtpserver = NULL;
1495 GSList *to_list = NULL;
1496 GSList *newsgroup_list = NULL;
1497 gchar *savecopyfolder = NULL;
1498 gchar *replymessageid = NULL;
1499 gchar *fwdmessageid = NULL;
1500 gchar *privacy_system = NULL;
1501 gboolean encrypt = FALSE;
1502 gchar *encrypt_data = NULL;
1503 gchar buf[BUFFSIZE];
1505 PrefsAccount *mailac = NULL, *newsac = NULL;
1506 gboolean save_clear_text = TRUE;
1507 gchar *tmp_enc_file = NULL;
1511 g_return_val_if_fail(file != NULL, -1);
1513 if ((fp = g_fopen(file, "rb")) == NULL) {
1514 FILE_OP_ERROR(file, "fopen");
1516 if (*errstr) g_free(*errstr);
1517 *errstr = g_strdup_printf(_("Couldn't open file %s."), file);
1522 while ((hnum = procheader_get_one_field(buf, sizeof(buf), fp, qentry))
1524 gchar *p = buf + strlen(qentry[hnum].name);
1532 if (smtpserver == NULL)
1533 smtpserver = g_strdup(p);
1536 to_list = address_list_append(to_list, p);
1539 newsgroup_list = newsgroup_list_append(newsgroup_list, p);
1541 case Q_MAIL_ACCOUNT_ID:
1542 mailac = account_find_from_id(atoi(p));
1544 case Q_NEWS_ACCOUNT_ID:
1545 newsac = account_find_from_id(atoi(p));
1547 case Q_SAVE_COPY_FOLDER:
1548 if (savecopyfolder == NULL)
1549 savecopyfolder = g_strdup(p);
1551 case Q_REPLY_MESSAGE_ID:
1552 if (replymessageid == NULL)
1553 replymessageid = g_strdup(p);
1555 case Q_FWD_MESSAGE_ID:
1556 if (fwdmessageid == NULL)
1557 fwdmessageid = g_strdup(p);
1559 case Q_PRIVACY_SYSTEM:
1560 case Q_PRIVACY_SYSTEM_OLD:
1561 if (privacy_system == NULL)
1562 privacy_system = g_strdup(p);
1569 case Q_ENCRYPT_DATA:
1570 case Q_ENCRYPT_DATA_OLD:
1571 if (encrypt_data == NULL)
1572 encrypt_data = g_strdup(p);
1575 case Q_CLAWS_HDRS_OLD:
1576 /* end of special headers reached */
1577 goto send_mail; /* can't "break;break;" */
1581 filepos = ftell(fp);
1586 if (mailac && mailac->save_encrypted_as_clear_text
1587 && !mailac->encrypt_to_self)
1588 save_clear_text = TRUE;
1590 save_clear_text = FALSE;
1595 mimeinfo = procmime_scan_queue_file(file);
1596 if (!privacy_encrypt(privacy_system, mimeinfo, encrypt_data)
1597 || (fp = my_tmpfile()) == NULL
1598 || procmime_write_mimeinfo(mimeinfo, fp) < 0) {
1601 procmime_mimeinfo_free_all(mimeinfo);
1604 slist_free_strings(to_list);
1605 g_slist_free(to_list);
1606 slist_free_strings(newsgroup_list);
1607 g_slist_free(newsgroup_list);
1608 g_free(savecopyfolder);
1609 g_free(replymessageid);
1610 g_free(fwdmessageid);
1611 g_free(privacy_system);
1612 g_free(encrypt_data);
1614 if (*errstr) g_free(*errstr);
1615 *errstr = g_strdup_printf(_("Couldn't encrypt the email: %s"),
1616 privacy_get_error());
1622 if (!save_clear_text) {
1623 gchar *content = NULL;
1624 FILE *tmpfp = get_tmpfile_in_dir(get_mime_tmp_dir(), &tmp_enc_file);
1628 content = file_read_stream_to_str(fp);
1631 str_write_to_file(content, tmp_enc_file);
1634 g_warning("couldn't get tempfile\n");
1638 procmime_mimeinfo_free_all(mimeinfo);
1644 debug_print("Sending message by mail\n");
1647 if (*errstr) g_free(*errstr);
1648 *errstr = g_strdup_printf(_("Queued message header is broken."));
1651 } else if (mailac && mailac->use_mail_command &&
1652 mailac->mail_command && (* mailac->mail_command)) {
1653 mailval = send_message_local(mailac->mail_command, fp);
1657 mailac = account_find_from_smtp_server(from, smtpserver);
1659 g_warning("Account not found. "
1660 "Using current account...\n");
1661 mailac = cur_account;
1666 mailval = send_message_smtp_full(mailac, to_list, fp, keep_session);
1667 if (mailval == -1 && errstr) {
1668 if (*errstr) g_free(*errstr);
1669 *errstr = g_strdup_printf(_("An error happened during SMTP session."));
1672 PrefsAccount tmp_ac;
1674 g_warning("Account not found.\n");
1676 memset(&tmp_ac, 0, sizeof(PrefsAccount));
1677 tmp_ac.address = from;
1678 tmp_ac.smtp_server = smtpserver;
1679 tmp_ac.smtpport = SMTP_PORT;
1680 mailval = send_message_smtp(&tmp_ac, to_list, fp);
1681 if (mailval == -1 && errstr) {
1682 if (*errstr) g_free(*errstr);
1683 *errstr = g_strdup_printf(_("No specific account has been found to "
1684 "send, and an error happened during SMTP session."));
1688 } else if (!to_list && !newsgroup_list) {
1690 if (*errstr) g_free(*errstr);
1691 *errstr = g_strdup(_("Couldn't determine sending informations. "
1692 "Maybe the email hasn't been generated by Claws Mail."));
1697 fseek(fp, filepos, SEEK_SET);
1698 if (newsgroup_list && (mailval == 0)) {
1703 /* write to temporary file */
1704 tmp = g_strdup_printf("%s%ctmp%p", g_get_tmp_dir(),
1705 G_DIR_SEPARATOR, file);
1706 if ((tmpfp = g_fopen(tmp, "wb")) == NULL) {
1707 FILE_OP_ERROR(tmp, "fopen");
1709 alertpanel_error(_("Couldn't create temporary file for news sending."));
1711 if (change_file_mode_rw(tmpfp, tmp) < 0) {
1712 FILE_OP_ERROR(tmp, "chmod");
1713 g_warning("can't change file mode\n");
1716 while ((newsval == 0) && fgets(buf, sizeof(buf), fp) != NULL) {
1717 if (fputs(buf, tmpfp) == EOF) {
1718 FILE_OP_ERROR(tmp, "fputs");
1721 if (*errstr) g_free(*errstr);
1722 *errstr = g_strdup_printf(_("Error when writing temporary file for news sending."));
1729 debug_print("Sending message by news\n");
1731 folder = FOLDER(newsac->folder);
1733 newsval = news_post(folder, tmp);
1734 if (newsval < 0 && errstr) {
1735 if (*errstr) g_free(*errstr);
1736 *errstr = g_strdup_printf(_("Error occurred while posting the message to %s."),
1737 newsac->nntp_server);
1747 /* save message to outbox */
1748 if (mailval == 0 && newsval == 0 && savecopyfolder) {
1751 debug_print("saving sent message...\n");
1753 outbox = folder_find_item_from_identifier(savecopyfolder);
1755 outbox = folder_get_default_outbox();
1757 if (save_clear_text || tmp_enc_file == NULL) {
1758 gboolean saved = FALSE;
1759 *queued_removed = FALSE;
1760 if (queue && msgnum > 0) {
1761 MsgInfo *queued_mail = folder_item_get_msginfo(queue, msgnum);
1762 if (folder_item_move_msg(outbox, queued_mail) >= 0) {
1763 debug_print("moved queued mail %d to sent folder\n", msgnum);
1765 *queued_removed = TRUE;
1766 } else if (folder_item_copy_msg(outbox, queued_mail) >= 0) {
1767 debug_print("copied queued mail %d to sent folder\n", msgnum);
1770 procmsg_msginfo_free(queued_mail);
1773 debug_print("resaving clear text queued mail to sent folder\n");
1774 procmsg_save_to_outbox(outbox, file, TRUE);
1777 debug_print("saving encrpyted queued mail to sent folder\n");
1778 procmsg_save_to_outbox(outbox, tmp_enc_file, FALSE);
1782 if (tmp_enc_file != NULL) {
1783 g_unlink(tmp_enc_file);
1785 tmp_enc_file = NULL;
1788 if (replymessageid != NULL || fwdmessageid != NULL) {
1792 if (replymessageid != NULL)
1793 tokens = g_strsplit(replymessageid, "\t", 0);
1795 tokens = g_strsplit(fwdmessageid, "\t", 0);
1796 item = folder_find_item_from_identifier(tokens[0]);
1798 /* check if queued message has valid folder and message id */
1799 if (item != NULL && tokens[2] != NULL) {
1802 msginfo = folder_item_get_msginfo(item, atoi(tokens[1]));
1804 /* check if referring message exists and has a message id */
1805 if ((msginfo != NULL) &&
1806 (msginfo->msgid != NULL) &&
1807 (strcmp(msginfo->msgid, tokens[2]) != 0)) {
1808 procmsg_msginfo_free(msginfo);
1812 if (msginfo == NULL) {
1813 msginfo = folder_item_get_msginfo_by_msgid(item, tokens[2]);
1816 if (msginfo != NULL) {
1817 if (replymessageid != NULL) {
1818 procmsg_msginfo_unset_flags(msginfo, MSG_FORWARDED, 0);
1819 procmsg_msginfo_set_flags(msginfo, MSG_REPLIED, 0);
1821 procmsg_msginfo_unset_flags(msginfo, MSG_REPLIED, 0);
1822 procmsg_msginfo_set_flags(msginfo, MSG_FORWARDED, 0);
1824 procmsg_msginfo_free(msginfo);
1832 slist_free_strings(to_list);
1833 g_slist_free(to_list);
1834 slist_free_strings(newsgroup_list);
1835 g_slist_free(newsgroup_list);
1836 g_free(savecopyfolder);
1837 g_free(replymessageid);
1838 g_free(fwdmessageid);
1839 g_free(privacy_system);
1840 g_free(encrypt_data);
1842 return (newsval != 0 ? newsval : mailval);
1845 gint procmsg_send_message_queue(const gchar *file, gchar **errstr, FolderItem *queue, gint msgnum, gboolean *queued_removed)
1847 gint result = procmsg_send_message_queue_full(file, FALSE, errstr, queue, msgnum, queued_removed);
1848 toolbar_main_set_sensitive(mainwindow_get_mainwindow());
1852 static void update_folder_msg_counts(FolderItem *item, MsgInfo *msginfo, MsgPermFlags old_flags)
1854 MsgPermFlags new_flags = msginfo->flags.perm_flags;
1857 if (!(old_flags & MSG_NEW) && (new_flags & MSG_NEW)) {
1861 if ((old_flags & MSG_NEW) && !(new_flags & MSG_NEW)) {
1866 if (!(old_flags & MSG_UNREAD) && (new_flags & MSG_UNREAD)) {
1867 item->unread_msgs++;
1868 if (procmsg_msg_has_marked_parent(msginfo))
1869 item->unreadmarked_msgs++;
1872 if ((old_flags & MSG_UNREAD) && !(new_flags & MSG_UNREAD)) {
1873 item->unread_msgs--;
1874 if (procmsg_msg_has_marked_parent(msginfo))
1875 item->unreadmarked_msgs--;
1879 if (!(old_flags & MSG_MARKED) && (new_flags & MSG_MARKED)) {
1880 procmsg_update_unread_children(msginfo, TRUE);
1881 item->marked_msgs++;
1884 if ((old_flags & MSG_MARKED) && !(new_flags & MSG_MARKED)) {
1885 procmsg_update_unread_children(msginfo, FALSE);
1886 item->marked_msgs--;
1890 void procmsg_msginfo_set_flags(MsgInfo *msginfo, MsgPermFlags perm_flags, MsgTmpFlags tmp_flags)
1893 MsgInfoUpdate msginfo_update;
1894 MsgPermFlags perm_flags_new, perm_flags_old;
1895 MsgTmpFlags tmp_flags_old;
1897 g_return_if_fail(msginfo != NULL);
1898 item = msginfo->folder;
1899 g_return_if_fail(item != NULL);
1901 debug_print("Setting flags for message %d in folder %s\n", msginfo->msgnum, item->path);
1903 /* Perm Flags handling */
1904 perm_flags_old = msginfo->flags.perm_flags;
1905 perm_flags_new = msginfo->flags.perm_flags | perm_flags;
1906 if ((perm_flags & MSG_IGNORE_THREAD) || (perm_flags_old & MSG_IGNORE_THREAD)) {
1907 perm_flags_new &= ~(MSG_NEW | MSG_UNREAD);
1910 if (perm_flags_old != perm_flags_new) {
1911 folder_item_change_msg_flags(msginfo->folder, msginfo, perm_flags_new);
1913 update_folder_msg_counts(item, msginfo, perm_flags_old);
1917 /* Tmp flags handling */
1918 tmp_flags_old = msginfo->flags.tmp_flags;
1919 msginfo->flags.tmp_flags |= tmp_flags;
1921 /* update notification */
1922 if ((perm_flags_old != perm_flags_new) || (tmp_flags_old != msginfo->flags.tmp_flags)) {
1923 msginfo_update.msginfo = msginfo;
1924 msginfo_update.flags = MSGINFO_UPDATE_FLAGS;
1925 hooks_invoke(MSGINFO_UPDATE_HOOKLIST, &msginfo_update);
1926 folder_item_update(msginfo->folder, F_ITEM_UPDATE_MSGCNT);
1930 void procmsg_msginfo_unset_flags(MsgInfo *msginfo, MsgPermFlags perm_flags, MsgTmpFlags tmp_flags)
1933 MsgInfoUpdate msginfo_update;
1934 MsgPermFlags perm_flags_new, perm_flags_old;
1935 MsgTmpFlags tmp_flags_old;
1937 g_return_if_fail(msginfo != NULL);
1938 item = msginfo->folder;
1939 g_return_if_fail(item != NULL);
1941 debug_print("Unsetting flags for message %d in folder %s\n", msginfo->msgnum, item->path);
1943 /* Perm Flags handling */
1944 perm_flags_old = msginfo->flags.perm_flags;
1945 perm_flags_new = msginfo->flags.perm_flags & ~perm_flags;
1947 if (perm_flags_old != perm_flags_new) {
1948 folder_item_change_msg_flags(msginfo->folder, msginfo, perm_flags_new);
1950 update_folder_msg_counts(item, msginfo, perm_flags_old);
1953 /* Tmp flags hanlding */
1954 tmp_flags_old = msginfo->flags.tmp_flags;
1955 msginfo->flags.tmp_flags &= ~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);
1966 void procmsg_msginfo_change_flags(MsgInfo *msginfo,
1967 MsgPermFlags add_perm_flags, MsgTmpFlags add_tmp_flags,
1968 MsgPermFlags rem_perm_flags, MsgTmpFlags rem_tmp_flags)
1971 MsgInfoUpdate msginfo_update;
1972 MsgPermFlags perm_flags_new, perm_flags_old;
1973 MsgTmpFlags tmp_flags_old;
1975 g_return_if_fail(msginfo != NULL);
1976 item = msginfo->folder;
1977 g_return_if_fail(item != NULL);
1979 debug_print("Changing flags for message %d in folder %s\n", msginfo->msgnum, item->path);
1981 /* Perm Flags handling */
1982 perm_flags_old = msginfo->flags.perm_flags;
1983 perm_flags_new = (msginfo->flags.perm_flags & ~rem_perm_flags) | add_perm_flags;
1984 if ((add_perm_flags & MSG_IGNORE_THREAD) || (perm_flags_old & MSG_IGNORE_THREAD)) {
1985 perm_flags_new &= ~(MSG_NEW | MSG_UNREAD);
1988 if (perm_flags_old != perm_flags_new) {
1989 folder_item_change_msg_flags(msginfo->folder, msginfo, perm_flags_new);
1991 update_folder_msg_counts(item, msginfo, perm_flags_old);
1995 /* Tmp flags handling */
1996 tmp_flags_old = msginfo->flags.tmp_flags;
1997 msginfo->flags.tmp_flags &= ~rem_tmp_flags;
1998 msginfo->flags.tmp_flags |= add_tmp_flags;
2000 /* update notification */
2001 if ((perm_flags_old != perm_flags_new) || (tmp_flags_old != msginfo->flags.tmp_flags)) {
2002 msginfo_update.msginfo = msginfo;
2003 msginfo_update.flags = MSGINFO_UPDATE_FLAGS;
2004 hooks_invoke(MSGINFO_UPDATE_HOOKLIST, &msginfo_update);
2005 folder_item_update(msginfo->folder, F_ITEM_UPDATE_MSGCNT);
2010 *\brief check for flags (e.g. mark) in prior msgs of current thread
2012 *\param info Current message
2013 *\param perm_flags Flags to be checked
2014 *\param parentmsgs Hash of prior msgs to avoid loops
2016 *\return gboolean TRUE if perm_flags are found
2018 static gboolean procmsg_msg_has_flagged_parent_real(MsgInfo *info,
2019 MsgPermFlags perm_flags, GHashTable *parentmsgs)
2023 g_return_val_if_fail(info != NULL, FALSE);
2025 if (info != NULL && info->folder != NULL && info->inreplyto != NULL) {
2026 tmp = folder_item_get_msginfo_by_msgid(info->folder,
2028 if (tmp && (tmp->flags.perm_flags & perm_flags)) {
2029 procmsg_msginfo_free(tmp);
2031 } else if (tmp != NULL) {
2034 if (g_hash_table_lookup(parentmsgs, info)) {
2035 debug_print("loop detected: %d\n",
2039 g_hash_table_insert(parentmsgs, info, "1");
2040 result = procmsg_msg_has_flagged_parent_real(
2041 tmp, perm_flags, parentmsgs);
2043 procmsg_msginfo_free(tmp);
2053 *\brief Callback for cleaning up hash of parentmsgs
2055 static gboolean parentmsgs_hash_remove(gpointer key,
2063 *\brief Set up list of parentmsgs
2064 * See procmsg_msg_has_flagged_parent_real()
2066 gboolean procmsg_msg_has_flagged_parent(MsgInfo *info, MsgPermFlags perm_flags)
2069 static GHashTable *parentmsgs = NULL;
2071 if (parentmsgs == NULL)
2072 parentmsgs = g_hash_table_new(NULL, NULL);
2074 result = procmsg_msg_has_flagged_parent_real(info, perm_flags, parentmsgs);
2075 g_hash_table_foreach_remove(parentmsgs, parentmsgs_hash_remove, NULL);
2081 *\brief Check if msgs prior in thread are marked
2082 * See procmsg_msg_has_flagged_parent_real()
2084 gboolean procmsg_msg_has_marked_parent(MsgInfo *info)
2086 return procmsg_msg_has_flagged_parent(info, MSG_MARKED);
2090 static GSList *procmsg_find_children_func(MsgInfo *info,
2091 GSList *children, GSList *all)
2095 g_return_val_if_fail(info!=NULL, children);
2096 if (info->msgid == NULL)
2099 for (cur = all; cur != NULL; cur = g_slist_next(cur)) {
2100 MsgInfo *tmp = (MsgInfo *)cur->data;
2101 if (tmp->inreplyto && !strcmp(tmp->inreplyto, info->msgid)) {
2102 /* Check if message is already in the list */
2103 if ((children == NULL) ||
2104 (g_slist_index(children, tmp) == -1)) {
2105 children = g_slist_prepend(children,
2106 procmsg_msginfo_new_ref(tmp));
2107 children = procmsg_find_children_func(tmp,
2116 GSList *procmsg_find_children (MsgInfo *info)
2121 g_return_val_if_fail(info!=NULL, NULL);
2122 all = folder_item_get_msg_list(info->folder);
2123 children = procmsg_find_children_func(info, NULL, all);
2124 if (children != NULL) {
2125 for (cur = all; cur != NULL; cur = g_slist_next(cur)) {
2126 /* this will not free the used pointers
2127 created with procmsg_msginfo_new_ref */
2128 procmsg_msginfo_free((MsgInfo *)cur->data);
2136 void procmsg_update_unread_children(MsgInfo *info, gboolean newly_marked)
2138 GSList *children = procmsg_find_children(info);
2140 for (cur = children; cur != NULL; cur = g_slist_next(cur)) {
2141 MsgInfo *tmp = (MsgInfo *)cur->data;
2142 if(MSG_IS_UNREAD(tmp->flags) && !MSG_IS_IGNORE_THREAD(tmp->flags)) {
2144 info->folder->unreadmarked_msgs++;
2146 info->folder->unreadmarked_msgs--;
2147 folder_item_update(info->folder, F_ITEM_UPDATE_MSGCNT);
2149 procmsg_msginfo_free(tmp);
2151 g_slist_free(children);
2155 * Set the destination folder for a copy or move operation
2157 * \param msginfo The message which's destination folder is changed
2158 * \param to_folder The destination folder for the operation
2160 void procmsg_msginfo_set_to_folder(MsgInfo *msginfo, FolderItem *to_folder)
2162 if(msginfo->to_folder != NULL) {
2163 msginfo->to_folder->op_count--;
2164 folder_item_update(msginfo->to_folder, F_ITEM_UPDATE_MSGCNT);
2166 msginfo->to_folder = to_folder;
2167 if(to_folder != NULL) {
2168 to_folder->op_count++;
2169 folder_item_update(msginfo->to_folder, F_ITEM_UPDATE_MSGCNT);
2174 * Apply filtering actions to the msginfo
2176 * \param msginfo The MsgInfo describing the message that should be filtered
2177 * \return TRUE if the message was moved and MsgInfo is now invalid,
2180 gboolean procmsg_msginfo_filter(MsgInfo *msginfo, PrefsAccount* ac_prefs)
2182 MailFilteringData mail_filtering_data;
2184 mail_filtering_data.msginfo = msginfo;
2185 mail_filtering_data.msglist = NULL;
2186 mail_filtering_data.filtered = NULL;
2187 mail_filtering_data.unfiltered = NULL;
2188 if (hooks_invoke(MAIL_FILTERING_HOOKLIST, &mail_filtering_data)) {
2192 /* filter if enabled in prefs or move to inbox if not */
2193 if((filtering_rules != NULL) &&
2194 filter_message_by_msginfo(filtering_rules, msginfo, ac_prefs)) {
2201 void procmsg_msglist_filter(GSList *list, PrefsAccount *ac,
2202 GSList **filtered, GSList **unfiltered,
2205 GSList *cur, *to_do = NULL;
2206 gint total = 0, curnum = 0;
2207 MailFilteringData mail_filtering_data;
2209 g_return_if_fail(filtered != NULL);
2210 g_return_if_fail(unfiltered != NULL);
2218 total = g_slist_length(list);
2222 *unfiltered = g_slist_copy(list);
2226 statusbar_print_all(_("Filtering messages...\n"));
2228 mail_filtering_data.msginfo = NULL;
2229 mail_filtering_data.msglist = list;
2230 mail_filtering_data.filtered = NULL;
2231 mail_filtering_data.unfiltered = NULL;
2233 hooks_invoke(MAIL_LISTFILTERING_HOOKLIST, &mail_filtering_data);
2235 if (mail_filtering_data.filtered == NULL &&
2236 mail_filtering_data.unfiltered == NULL) {
2237 /* nothing happened */
2238 debug_print(MAIL_LISTFILTERING_HOOKLIST " did nothing. filtering whole list normally.\n");
2241 if (mail_filtering_data.filtered != NULL) {
2242 /* keep track of what's been filtered by the hooks */
2243 debug_print(MAIL_LISTFILTERING_HOOKLIST " filtered some stuff. total %d filtered %d unfilt %d.\n",
2244 g_slist_length(list),
2245 g_slist_length(mail_filtering_data.filtered),
2246 g_slist_length(mail_filtering_data.unfiltered));
2248 *filtered = g_slist_copy(mail_filtering_data.filtered);
2250 if (mail_filtering_data.unfiltered != NULL) {
2251 /* what the hooks didn't handle will go in filtered or
2252 * unfiltered in the next loop */
2253 debug_print(MAIL_LISTFILTERING_HOOKLIST " left unfiltered stuff. total %d filtered %d unfilt %d.\n",
2254 g_slist_length(list),
2255 g_slist_length(mail_filtering_data.filtered),
2256 g_slist_length(mail_filtering_data.unfiltered));
2257 to_do = mail_filtering_data.unfiltered;
2260 for (cur = to_do; cur; cur = cur->next) {
2261 MsgInfo *info = (MsgInfo *)cur->data;
2262 if (procmsg_msginfo_filter(info, ac))
2263 *filtered = g_slist_prepend(*filtered, info);
2265 *unfiltered = g_slist_prepend(*unfiltered, info);
2266 statusbar_progress_all(curnum++, total, prefs_common.statusbar_update_step);
2269 g_slist_free(mail_filtering_data.filtered);
2270 g_slist_free(mail_filtering_data.unfiltered);
2272 *filtered = g_slist_reverse(*filtered);
2273 *unfiltered = g_slist_reverse(*unfiltered);
2275 statusbar_progress_all(0,0,0);
2276 statusbar_pop_all();
2279 MsgInfo *procmsg_msginfo_new_from_mimeinfo(MsgInfo *src_msginfo, MimeInfo *mimeinfo)
2281 MsgInfo *tmp_msginfo = NULL;
2282 MsgFlags flags = {0, 0};
2283 gchar *tmpfile = get_tmp_file();
2284 FILE *fp = g_fopen(tmpfile, "wb");
2286 if (!mimeinfo || mimeinfo->type != MIMETYPE_MESSAGE ||
2287 g_ascii_strcasecmp(mimeinfo->subtype, "rfc822")) {
2288 g_warning("procmsg_msginfo_new_from_mimeinfo(): unsuitable mimeinfo");
2295 if (fp && procmime_write_mimeinfo(mimeinfo, fp) >= 0) {
2298 tmp_msginfo = procheader_parse_file(
2305 if (tmp_msginfo != NULL) {
2307 tmp_msginfo->folder = src_msginfo->folder;
2308 tmp_msginfo->plaintext_file = g_strdup(tmpfile);
2310 g_warning("procmsg_msginfo_new_from_mimeinfo(): Can't generate new msginfo");
2318 static GSList *spam_learners = NULL;
2320 void procmsg_register_spam_learner (int (*learn_func)(MsgInfo *info, GSList *list, gboolean spam))
2322 if (!g_slist_find(spam_learners, learn_func))
2323 spam_learners = g_slist_append(spam_learners, learn_func);
2324 if (mainwindow_get_mainwindow()) {
2325 main_window_set_menu_sensitive(mainwindow_get_mainwindow());
2326 summary_set_menu_sensitive(
2327 mainwindow_get_mainwindow()->summaryview);
2328 toolbar_main_set_sensitive(mainwindow_get_mainwindow());
2332 void procmsg_unregister_spam_learner (int (*learn_func)(MsgInfo *info, GSList *list, gboolean spam))
2334 spam_learners = g_slist_remove(spam_learners, learn_func);
2335 if (mainwindow_get_mainwindow()) {
2336 main_window_set_menu_sensitive(mainwindow_get_mainwindow());
2337 summary_set_menu_sensitive(
2338 mainwindow_get_mainwindow()->summaryview);
2339 toolbar_main_set_sensitive(mainwindow_get_mainwindow());
2343 gboolean procmsg_spam_can_learn(void)
2345 return g_slist_length(spam_learners) > 0;
2348 int procmsg_spam_learner_learn (MsgInfo *info, GSList *list, gboolean spam)
2350 GSList *cur = spam_learners;
2352 for (; cur; cur = cur->next) {
2353 int ((*func)(MsgInfo *info, GSList *list, gboolean spam)) = cur->data;
2354 ret |= func(info, list, spam);
2359 static gchar *spam_folder_item = NULL;
2360 void procmsg_spam_set_folder (const char *item_identifier)
2362 g_free(spam_folder_item);
2363 if (item_identifier)
2364 spam_folder_item = g_strdup(item_identifier);
2366 spam_folder_item = NULL;
2369 FolderItem *procmsg_spam_get_folder (void)
2371 FolderItem *item = spam_folder_item ? folder_find_item_from_identifier(spam_folder_item) : NULL;
2372 return item ? item : folder_get_default_trash();
2375 static void item_has_queued_mails(FolderItem *item, gpointer data)
2377 gboolean *result = (gboolean *)data;
2378 if (*result == TRUE)
2380 if (folder_has_parent_of_type(item, F_QUEUE) && item->total_msgs > 0)
2384 gboolean procmsg_have_queued_mails_fast (void)
2386 gboolean result = FALSE;
2387 folder_func_to_all_folders(item_has_queued_mails, &result);