2 * Sylpheed -- a GTK+ based, lightweight, and fast e-mail client
3 * Copyright (C) 1999-2011 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 3 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, see <http://www.gnu.org/licenses/>.
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"
52 static gint procmsg_send_message_queue_full(const gchar *file, gboolean keep_session, gchar **errstr,
53 FolderItem *queue, gint msgnum, gboolean *queued_removed);
54 static void procmsg_update_unread_children (MsgInfo *info,
55 gboolean newly_marked);
62 Q_MAIL_ACCOUNT_ID = 4,
63 Q_NEWS_ACCOUNT_ID = 5,
64 Q_SAVE_COPY_FOLDER = 6,
65 Q_REPLY_MESSAGE_ID = 7,
71 Q_PRIVACY_SYSTEM_OLD = 13,
73 Q_ENCRYPT_DATA_OLD = 15,
74 Q_CLAWS_HDRS_OLD = 16,
77 void procmsg_msg_list_free(GSList *mlist)
82 for (cur = mlist; cur != NULL; cur = cur->next) {
83 msginfo = (MsgInfo *)cur->data;
84 procmsg_msginfo_free(msginfo);
98 /* CLAWS subject threading:
100 in the first round it inserts subject lines in a
101 hashtable (subject <-> node)
103 the second round finishes the threads by attaching
104 matching subject lines to the one found in the
105 hashtable. will use the oldest node with the same
106 subject that is not more then thread_by_subject_max_age
107 days old (see subject_hashtable_lookup)
110 static void subject_hashtable_insert(GHashTable *hashtable, GNode *node)
116 cm_return_if_fail(hashtable != NULL);
117 cm_return_if_fail(node != NULL);
118 msginfo = (MsgInfo *) node->data;
119 cm_return_if_fail(msginfo != NULL);
121 subject = msginfo->subject;
125 subject += subject_get_prefix_length(subject);
127 list = g_hash_table_lookup(hashtable, subject);
128 list = g_slist_prepend(list, node);
129 g_hash_table_insert(hashtable, subject, list);
132 static GNode *subject_hashtable_lookup(GHashTable *hashtable, MsgInfo *msginfo)
136 GNode *node = NULL, *hashtable_node = NULL;
138 MsgInfo *hashtable_msginfo = NULL, *best_msginfo = NULL;
141 cm_return_val_if_fail(hashtable != NULL, NULL);
143 subject = msginfo->subject;
146 prefix_length = subject_get_prefix_length(subject);
147 if (prefix_length <= 0)
149 subject += prefix_length;
151 list = g_hash_table_lookup(hashtable, subject);
155 /* check all nodes with the same subject to find the best parent */
156 for (cur = list; cur; cur = cur->next) {
157 hashtable_node = (GNode *)cur->data;
158 hashtable_msginfo = (MsgInfo *) hashtable_node->data;
161 /* best node should be the oldest in the found nodes */
162 /* parent node must not be older then msginfo */
163 if ((hashtable_msginfo->date_t < msginfo->date_t) &&
164 ((best_msginfo == NULL) ||
165 (best_msginfo->date_t > hashtable_msginfo->date_t)))
168 /* parent node must not be more then thread_by_subject_max_age
169 days older then msginfo */
170 if (abs(difftime(msginfo->date_t, hashtable_msginfo->date_t)) >
171 prefs_common.thread_by_subject_max_age * 3600 * 24)
174 /* can add new tests for all matching
175 nodes found by subject */
178 node = hashtable_node;
179 best_msginfo = hashtable_msginfo;
186 static void subject_hashtable_free(gpointer key, gpointer value, gpointer data)
191 /* return the reversed thread tree */
192 GNode *procmsg_get_thread_tree(GSList *mlist)
194 GNode *root, *parent, *node, *next;
195 GHashTable *msgid_table;
196 GHashTable *subject_hashtable = NULL;
201 root = g_node_new(NULL);
202 msgid_table = g_hash_table_new(g_str_hash, g_str_equal);
204 if (prefs_common.thread_by_subject) {
205 subject_hashtable = g_hash_table_new(g_str_hash, g_str_equal);
208 for (; mlist != NULL; mlist = mlist->next) {
209 msginfo = (MsgInfo *)mlist->data;
212 if (msginfo->inreplyto) {
213 parent = g_hash_table_lookup(msgid_table, msginfo->inreplyto);
214 if (parent == NULL) {
218 node = g_node_insert_data_before
219 (parent, parent == root ? parent->children : NULL,
221 if ((msgid = msginfo->msgid) && g_hash_table_lookup(msgid_table, msgid) == NULL)
222 g_hash_table_insert(msgid_table, (gchar *)msgid, node);
224 /* CLAWS: add subject to hashtable (without prefix) */
225 if (prefs_common.thread_by_subject) {
226 subject_hashtable_insert(subject_hashtable, node);
230 /* complete the unfinished threads */
231 for (node = root->children; node != NULL; ) {
233 msginfo = (MsgInfo *)node->data;
236 if (msginfo->inreplyto)
237 parent = g_hash_table_lookup(msgid_table, msginfo->inreplyto);
239 /* try looking for the indirect parent */
240 if (!parent && msginfo->references) {
241 for (reflist = msginfo->references;
242 reflist != NULL; reflist = reflist->next)
243 if ((parent = g_hash_table_lookup
244 (msgid_table, reflist->data)) != NULL)
248 /* node should not be the parent, and node should not
249 be an ancestor of parent (circular reference) */
250 if (parent && parent != node &&
251 !g_node_is_ancestor(node, parent)) {
254 (parent, parent->children, node);
260 if (prefs_common.thread_by_subject) {
261 START_TIMING("thread by subject");
262 for (node = root->children; node && node != NULL;) {
264 msginfo = (MsgInfo *) node->data;
266 parent = subject_hashtable_lookup(subject_hashtable, msginfo);
268 /* the node may already be threaded by IN-REPLY-TO, so go up
270 find the parent node */
271 if (parent != NULL) {
272 if (g_node_is_ancestor(node, parent))
280 g_node_append(parent, node);
288 if (prefs_common.thread_by_subject)
290 g_hash_table_foreach(subject_hashtable, subject_hashtable_free, NULL);
291 g_hash_table_destroy(subject_hashtable);
294 g_hash_table_destroy(msgid_table);
299 gint procmsg_move_messages(GSList *mlist)
301 GSList *cur, *movelist = NULL;
303 FolderItem *dest = NULL;
305 gboolean finished = TRUE;
306 if (!mlist) return 0;
308 folder_item_update_freeze();
311 for (cur = mlist; cur != NULL; cur = cur->next) {
312 msginfo = (MsgInfo *)cur->data;
313 if (!msginfo->to_folder) {
319 dest = msginfo->to_folder;
320 movelist = g_slist_prepend(movelist, msginfo);
321 } else if (dest == msginfo->to_folder) {
322 movelist = g_slist_prepend(movelist, msginfo);
326 procmsg_msginfo_set_to_folder(msginfo, NULL);
329 movelist = g_slist_reverse(movelist);
330 retval |= folder_item_move_msgs(dest, movelist);
331 g_slist_free(movelist);
334 if (finished == FALSE) {
340 folder_item_update_thaw();
344 void procmsg_copy_messages(GSList *mlist)
346 GSList *cur, *copylist = NULL;
348 FolderItem *dest = NULL;
349 gboolean finished = TRUE;
352 folder_item_update_freeze();
355 for (cur = mlist; cur != NULL; cur = cur->next) {
356 msginfo = (MsgInfo *)cur->data;
357 if (!msginfo->to_folder) {
363 dest = msginfo->to_folder;
364 copylist = g_slist_prepend(copylist, msginfo);
365 } else if (dest == msginfo->to_folder) {
366 copylist = g_slist_prepend(copylist, msginfo);
370 procmsg_msginfo_set_to_folder(msginfo, NULL);
373 copylist = g_slist_reverse(copylist);
374 folder_item_copy_msgs(dest, copylist);
375 g_slist_free(copylist);
378 if (finished == FALSE) {
384 folder_item_update_thaw();
387 gchar *procmsg_get_message_file_path(MsgInfo *msginfo)
391 cm_return_val_if_fail(msginfo != NULL, NULL);
393 if (msginfo->plaintext_file)
394 file = g_strdup(msginfo->plaintext_file);
396 file = folder_item_fetch_msg(msginfo->folder, msginfo->msgnum);
402 gchar *procmsg_get_message_file(MsgInfo *msginfo)
404 gchar *filename = NULL;
406 cm_return_val_if_fail(msginfo != NULL, NULL);
408 filename = folder_item_fetch_msg(msginfo->folder, msginfo->msgnum);
410 debug_print("can't fetch message %d\n", msginfo->msgnum);
415 gchar *procmsg_get_message_file_full(MsgInfo *msginfo, gboolean headers, gboolean body)
417 gchar *filename = NULL;
419 cm_return_val_if_fail(msginfo != NULL, NULL);
421 filename = folder_item_fetch_msg_full(msginfo->folder, msginfo->msgnum,
424 debug_print("can't fetch message %d\n", msginfo->msgnum);
429 GSList *procmsg_get_message_file_list(GSList *mlist)
431 GSList *file_list = NULL;
433 MsgFileInfo *fileinfo;
436 while (mlist != NULL) {
437 msginfo = (MsgInfo *)mlist->data;
438 file = procmsg_get_message_file(msginfo);
440 procmsg_message_file_list_free(file_list);
443 fileinfo = g_new(MsgFileInfo, 1);
444 fileinfo->msginfo = procmsg_msginfo_new_ref(msginfo);
445 fileinfo->file = file;
446 fileinfo->flags = g_new(MsgFlags, 1);
447 *fileinfo->flags = msginfo->flags;
448 file_list = g_slist_prepend(file_list, fileinfo);
452 file_list = g_slist_reverse(file_list);
457 void procmsg_message_file_list_free(MsgInfoList *file_list)
460 MsgFileInfo *fileinfo;
462 for (cur = file_list; cur != NULL; cur = cur->next) {
463 fileinfo = (MsgFileInfo *)cur->data;
464 procmsg_msginfo_free(fileinfo->msginfo);
465 g_free(fileinfo->file);
466 g_free(fileinfo->flags);
470 g_slist_free(file_list);
473 FILE *procmsg_open_message(MsgInfo *msginfo)
478 cm_return_val_if_fail(msginfo != NULL, NULL);
480 file = procmsg_get_message_file_path(msginfo);
481 cm_return_val_if_fail(file != NULL, NULL);
483 if (!is_file_exist(file)) {
485 file = procmsg_get_message_file(msginfo);
490 if ((fp = g_fopen(file, "rb")) == NULL) {
491 FILE_OP_ERROR(file, "fopen");
498 if (MSG_IS_QUEUED(msginfo->flags) || MSG_IS_DRAFT(msginfo->flags)) {
501 while (fgets(buf, sizeof(buf), fp) != NULL) {
503 if ((!strncmp(buf, "X-Claws-End-Special-Headers: 1",
504 strlen("X-Claws-End-Special-Headers:"))) ||
505 (!strncmp(buf, "X-Sylpheed-End-Special-Headers: 1",
506 strlen("X-Sylpheed-End-Special-Headers:"))))
509 if (buf[0] == '\r' || buf[0] == '\n') break;
510 /* from other mailers */
511 if (!strncmp(buf, "Date: ", 6)
512 || !strncmp(buf, "To: ", 4)
513 || !strncmp(buf, "From: ", 6)
514 || !strncmp(buf, "Subject: ", 9)) {
524 gboolean procmsg_msg_exist(MsgInfo *msginfo)
529 if (!msginfo) return FALSE;
531 path = folder_item_get_path(msginfo->folder);
533 ret = !folder_item_is_msg_changed(msginfo->folder, msginfo);
539 void procmsg_get_filter_keyword(MsgInfo *msginfo, gchar **header, gchar **key,
540 PrefsFilterType type)
542 static HeaderEntry hentry[] = {{"X-BeenThere:", NULL, TRUE},
543 {"X-ML-Name:", NULL, TRUE},
544 {"X-List:", NULL, TRUE},
545 {"X-Mailing-list:", NULL, TRUE},
546 {"List-Id:", NULL, TRUE},
547 {"X-Sequence:", NULL, TRUE},
548 {"Sender:", NULL, TRUE},
549 {"List-Post:", NULL, TRUE},
550 {NULL, NULL, FALSE}};
556 H_X_MAILING_LIST = 3,
565 cm_return_if_fail(msginfo != NULL);
566 cm_return_if_fail(header != NULL);
567 cm_return_if_fail(key != NULL);
576 if ((fp = procmsg_open_message(msginfo)) == NULL)
578 procheader_get_header_fields(fp, hentry);
581 #define SET_FILTER_KEY(hstr, idx) \
583 *header = g_strdup(hstr); \
584 *key = hentry[idx].body; \
585 hentry[idx].body = NULL; \
588 if (hentry[H_LIST_ID].body != NULL) {
589 SET_FILTER_KEY("header \"List-Id\"", H_LIST_ID);
590 extract_list_id_str(*key);
591 } else if (hentry[H_X_BEENTHERE].body != NULL) {
592 SET_FILTER_KEY("header \"X-BeenThere\"", H_X_BEENTHERE);
593 } else if (hentry[H_X_ML_NAME].body != NULL) {
594 SET_FILTER_KEY("header \"X-ML-Name\"", H_X_ML_NAME);
595 } else if (hentry[H_X_LIST].body != NULL) {
596 SET_FILTER_KEY("header \"X-List\"", H_X_LIST);
597 } else if (hentry[H_X_MAILING_LIST].body != NULL) {
598 SET_FILTER_KEY("header \"X-Mailing-List\"", H_X_MAILING_LIST);
599 } else if (hentry[H_X_SEQUENCE].body != NULL) {
602 SET_FILTER_KEY("X-Sequence", H_X_SEQUENCE);
605 while (*p != '\0' && !g_ascii_isspace(*p)) p++;
606 while (g_ascii_isspace(*p)) p++;
607 if (g_ascii_isdigit(*p)) {
613 } else if (hentry[H_SENDER].body != NULL) {
614 SET_FILTER_KEY("header \"Sender\"", H_SENDER);
615 } else if (hentry[H_LIST_POST].body != NULL) {
616 SET_FILTER_KEY("header \"List-Post\"", H_LIST_POST);
617 } else if (msginfo->to) {
618 *header = g_strdup("to");
619 *key = g_strdup(msginfo->to);
620 } else if (msginfo->subject) {
621 *header = g_strdup("subject");
622 *key = g_strdup(msginfo->subject);
625 #undef SET_FILTER_KEY
627 g_free(hentry[H_X_BEENTHERE].body);
628 hentry[H_X_BEENTHERE].body = NULL;
629 g_free(hentry[H_X_ML_NAME].body);
630 hentry[H_X_ML_NAME].body = NULL;
631 g_free(hentry[H_X_LIST].body);
632 hentry[H_X_LIST].body = NULL;
633 g_free(hentry[H_X_MAILING_LIST].body);
634 hentry[H_X_MAILING_LIST].body = NULL;
635 g_free(hentry[H_LIST_ID].body);
636 hentry[H_LIST_ID].body = NULL;
637 g_free(hentry[H_SENDER].body);
638 hentry[H_SENDER].body = NULL;
639 g_free(hentry[H_LIST_POST].body);
640 hentry[H_LIST_POST].body = NULL;
644 *header = g_strdup("from");
645 *key = g_strdup(msginfo->from);
648 *header = g_strdup("to");
649 *key = g_strdup(msginfo->to);
651 case FILTER_BY_SUBJECT:
652 *header = g_strdup("subject");
653 *key = g_strdup(msginfo->subject);
660 static void procmsg_empty_trash(FolderItem *trash)
665 (trash->stype != F_TRASH &&
666 !folder_has_parent_of_type(trash, F_TRASH)))
669 if (trash && trash->total_msgs > 0) {
670 GSList *mlist = folder_item_get_msg_list(trash);
672 for (cur = mlist ; cur != NULL ; cur = cur->next) {
673 MsgInfo * msginfo = (MsgInfo *) cur->data;
674 if (MSG_IS_LOCKED(msginfo->flags)) {
675 procmsg_msginfo_free(msginfo);
678 if (msginfo->total_size != 0 &&
679 msginfo->size != (off_t)msginfo->total_size)
680 partial_mark_for_delete(msginfo);
682 procmsg_msginfo_free(msginfo);
685 folder_item_remove_all_msg(trash);
688 if (!trash->node || !trash->node->children)
691 node = trash->node->children;
692 while (node != NULL) {
694 procmsg_empty_trash(FOLDER_ITEM(node->data));
699 void procmsg_empty_all_trash(void)
704 for (cur = folder_get_list(); cur != NULL; cur = cur->next) {
705 Folder *folder = FOLDER(cur->data);
706 trash = folder->trash;
707 procmsg_empty_trash(trash);
708 if (folder->account && folder->account->set_trash_folder &&
709 folder_find_item_from_identifier(folder->account->trash_folder))
711 folder_find_item_from_identifier(folder->account->trash_folder));
715 static PrefsAccount *procmsg_get_account_from_file(const gchar *file)
717 PrefsAccount *mailac = NULL;
721 static HeaderEntry qentry[] = {{"S:", NULL, FALSE},
722 {"SSV:", NULL, FALSE},
724 {"NG:", NULL, FALSE},
725 {"MAID:", NULL, FALSE},
726 {"NAID:", NULL, FALSE},
727 {"SCF:", NULL, FALSE},
728 {"RMID:", NULL, FALSE},
729 {"FMID:", NULL, FALSE},
730 {"X-Claws-Privacy-System:", NULL, FALSE},
731 {"X-Claws-Encrypt:", NULL, FALSE},
732 {"X-Claws-Encrypt-Data:", NULL, FALSE},
733 {"X-Claws-End-Special-Headers", NULL, FALSE},
734 {"X-Sylpheed-Privacy-System:", NULL, FALSE},
735 {"X-Sylpheed-Encrypt:", NULL, FALSE},
736 {"X-Sylpheed-Encrypt-Data:", NULL, FALSE},
737 {NULL, NULL, FALSE}};
739 cm_return_val_if_fail(file != NULL, NULL);
741 if ((fp = g_fopen(file, "rb")) == NULL) {
742 FILE_OP_ERROR(file, "fopen");
746 while ((hnum = procheader_get_one_field(buf, sizeof(buf), fp, qentry))
748 gchar *p = buf + strlen(qentry[hnum].name);
750 if (hnum == Q_MAIL_ACCOUNT_ID) {
751 mailac = account_find_from_id(atoi(p));
759 static GSList *procmsg_list_sort_by_account(FolderItem *queue, GSList *list)
761 GSList *result = NULL;
763 PrefsAccount *last_account = NULL;
766 gboolean nothing_to_sort = TRUE;
771 orig = g_slist_copy(list);
773 msg = (MsgInfo *)orig->data;
775 for (cur = orig; cur; cur = cur->next)
776 debug_print("sort before %s\n", ((MsgInfo *)cur->data)->from);
781 nothing_to_sort = TRUE;
785 PrefsAccount *ac = NULL;
786 msg = (MsgInfo *)cur->data;
787 file = folder_item_fetch_msg(queue, msg->msgnum);
788 ac = procmsg_get_account_from_file(file);
791 if (last_account == NULL || (ac != NULL && ac == last_account)) {
792 result = g_slist_append(result, msg);
793 orig = g_slist_remove(orig, msg);
795 nothing_to_sort = FALSE;
801 if (orig || g_slist_length(orig)) {
802 if (!last_account && nothing_to_sort) {
803 /* can't find an account for the rest of the list */
806 result = g_slist_append(result, cur->data);
817 for (cur = result; cur; cur = cur->next)
818 debug_print("sort after %s\n", ((MsgInfo *)cur->data)->from);
825 static gboolean procmsg_is_last_for_account(FolderItem *queue, MsgInfo *msginfo, GSList *elem)
827 gchar *file = folder_item_fetch_msg(queue, msginfo->msgnum);
828 PrefsAccount *ac = procmsg_get_account_from_file(file);
831 for (cur = elem; cur; cur = cur->next) {
832 MsgInfo *cur_msginfo = (MsgInfo *)cur->data;
833 file = folder_item_fetch_msg(queue, cur_msginfo->msgnum);
835 if (cur_msginfo != msginfo && !MSG_IS_LOCKED(cur_msginfo->flags)) {
836 if (procmsg_get_account_from_file(file) == ac) {
847 static gboolean send_queue_lock = FALSE;
849 gboolean procmsg_queue_lock(char **errstr)
851 if (send_queue_lock) {
852 /* Avoid having to translate two similar strings */
853 log_warning(LOG_PROTOCOL, "%s\n", _("Already trying to send."));
855 if (*errstr) g_free(*errstr);
856 *errstr = g_strdup_printf(_("Already trying to send."));
860 send_queue_lock = TRUE;
863 void procmsg_queue_unlock(void)
865 send_queue_lock = FALSE;
868 *\brief Send messages in queue
870 *\param queue Queue folder to process
871 *\param save_msgs Unused
873 *\return Number of messages sent, negative if an error occurred
874 * positive if no error occurred
876 gint procmsg_send_queue(FolderItem *queue, gboolean save_msgs, gchar **errstr)
878 gint sent = 0, err = 0;
880 GSList *sorted_list = NULL;
883 if (!procmsg_queue_lock(errstr)) {
884 toolbar_main_set_sensitive(mainwindow_get_mainwindow());
889 queue = folder_get_default_queue();
892 procmsg_queue_unlock();
897 toolbar_main_set_sensitive(mainwindow_get_mainwindow());
899 folder_item_scan(queue);
900 list = folder_item_get_msg_list(queue);
902 /* sort the list per sender account; this helps reusing the same SMTP server */
903 sorted_list = procmsg_list_sort_by_account(queue, list);
905 for (elem = sorted_list; elem != NULL; elem = elem->next) {
909 msginfo = (MsgInfo *)(elem->data);
910 if (!MSG_IS_LOCKED(msginfo->flags) && !MSG_IS_DELETED(msginfo->flags)) {
911 file = folder_item_fetch_msg(queue, msginfo->msgnum);
913 gboolean queued_removed = FALSE;
914 if (procmsg_send_message_queue_full(file,
915 !procmsg_is_last_for_account(queue, msginfo, elem),
916 errstr, queue, msginfo->msgnum, &queued_removed) < 0) {
917 g_warning("Sending queued message %d failed.\n",
923 folder_item_remove_msg(queue, msginfo->msgnum);
928 /* FIXME: supposedly if only one message is locked, and queue
929 * is being flushed, the following free says something like
930 * "freeing msg ## in folder (nil)". */
931 procmsg_msginfo_free(msginfo);
934 g_slist_free(sorted_list);
935 folder_item_scan(queue);
937 if (queue->node && queue->node->children) {
938 node = queue->node->children;
939 while (node != NULL) {
942 send_queue_lock = FALSE;
943 res = procmsg_send_queue(FOLDER_ITEM(node->data), save_msgs, errstr);
944 send_queue_lock = TRUE;
952 procmsg_queue_unlock();
954 toolbar_main_set_sensitive(mainwindow_get_mainwindow());
956 return (err != 0 ? -err : sent);
959 gboolean procmsg_is_sending(void)
961 return send_queue_lock;
965 *\brief Determine if a queue folder is empty
967 *\param queue Queue folder to process
969 *\return TRUE if the queue folder is empty, otherwise return FALSE
971 gboolean procmsg_queue_is_empty(FolderItem *queue)
974 gboolean res = FALSE;
976 queue = folder_get_default_queue();
977 cm_return_val_if_fail(queue != NULL, TRUE);
979 folder_item_scan(queue);
980 list = folder_item_get_msg_list(queue);
981 res = (list == NULL);
982 procmsg_msg_list_free(list);
986 if (queue->node && queue->node->children) {
987 node = queue->node->children;
988 while (node != NULL) {
990 if (!procmsg_queue_is_empty(FOLDER_ITEM(node->data)))
999 gint procmsg_remove_special_headers(const gchar *in, const gchar *out)
1002 gchar buf[BUFFSIZE];
1004 if ((fp = g_fopen(in, "rb")) == NULL) {
1005 FILE_OP_ERROR(in, "fopen");
1008 if ((outfp = g_fopen(out, "wb")) == NULL) {
1009 FILE_OP_ERROR(out, "fopen");
1013 while (fgets(buf, sizeof(buf), fp) != NULL) {
1015 if ((!strncmp(buf, "X-Claws-End-Special-Headers: 1",
1016 strlen("X-Claws-End-Special-Headers:"))) ||
1017 (!strncmp(buf, "X-Sylpheed-End-Special-Headers: 1",
1018 strlen("X-Sylpheed-End-Special-Headers:"))))
1021 if (buf[0] == '\r' || buf[0] == '\n') break;
1022 /* from other mailers */
1023 if (!strncmp(buf, "Date: ", 6)
1024 || !strncmp(buf, "To: ", 4)
1025 || !strncmp(buf, "From: ", 6)
1026 || !strncmp(buf, "Subject: ", 9)) {
1031 while (fgets(buf, sizeof(buf), fp) != NULL)
1038 static gint procmsg_save_to_outbox(FolderItem *outbox, const gchar *file,
1042 MsgInfo *msginfo, *tmp_msginfo;
1043 MsgFlags flag = {0, 0};
1045 debug_print("saving sent message...\n");
1048 outbox = folder_get_default_outbox();
1049 cm_return_val_if_fail(outbox != NULL, -1);
1051 /* remove queueing headers */
1053 gchar tmp[MAXPATHLEN + 1];
1055 g_snprintf(tmp, sizeof(tmp), "%s%ctmpmsg.out.%08x",
1056 get_rc_dir(), G_DIR_SEPARATOR, (guint) rand());
1058 if (procmsg_remove_special_headers(file, tmp) !=0)
1061 folder_item_scan(outbox);
1062 if ((num = folder_item_add_msg(outbox, tmp, &flag, TRUE)) < 0) {
1063 g_warning("can't save message\n");
1068 folder_item_scan(outbox);
1069 if ((num = folder_item_add_msg
1070 (outbox, file, &flag, FALSE)) < 0) {
1071 g_warning("can't save message\n");
1075 msginfo = folder_item_get_msginfo(outbox, num); /* refcnt++ */
1076 tmp_msginfo = procmsg_msginfo_get_full_info(msginfo); /* refcnt++ */
1077 if (msginfo != NULL) {
1078 procmsg_msginfo_unset_flags(msginfo, ~0, 0);
1079 procmsg_msginfo_free(msginfo); /* refcnt-- */
1080 /* tmp_msginfo == msginfo */
1081 if (tmp_msginfo && msginfo->extradata &&
1082 (msginfo->extradata->dispositionnotificationto ||
1083 msginfo->extradata->returnreceiptto)) {
1084 procmsg_msginfo_set_flags(msginfo, MSG_RETRCPT_SENT, 0);
1086 procmsg_msginfo_free(tmp_msginfo); /* refcnt-- */
1092 void procmsg_print_message(MsgInfo *msginfo, const gchar *cmdline)
1094 static const gchar *def_cmd = "lpr %s";
1095 static guint id = 0;
1101 cm_return_if_fail(msginfo);
1103 if (procmime_msginfo_is_encrypted(msginfo))
1104 tmpfp = procmime_get_first_encrypted_text_content(msginfo);
1106 tmpfp = procmime_get_first_text_content(msginfo);
1107 if (tmpfp == NULL) {
1108 g_warning("Can't get text part\n");
1112 prtmp = g_strdup_printf("%s%cprinttmp.%08x",
1113 get_mime_tmp_dir(), G_DIR_SEPARATOR, id++);
1115 if ((prfp = g_fopen(prtmp, "wb")) == NULL) {
1116 FILE_OP_ERROR(prtmp, "fopen");
1122 if (msginfo->date) r = fprintf(prfp, "Date: %s\n", msginfo->date);
1123 if (msginfo->from) r = fprintf(prfp, "From: %s\n", msginfo->from);
1124 if (msginfo->to) r = fprintf(prfp, "To: %s\n", msginfo->to);
1125 if (msginfo->cc) r = fprintf(prfp, "Cc: %s\n", msginfo->cc);
1126 if (msginfo->newsgroups)
1127 r = fprintf(prfp, "Newsgroups: %s\n", msginfo->newsgroups);
1128 if (msginfo->subject) r = fprintf(prfp, "Subject: %s\n", msginfo->subject);
1131 while (fgets(buf, sizeof(buf), tmpfp) != NULL)
1132 r = fputs(buf, prfp);
1137 if (cmdline && (p = strchr(cmdline, '%')) && *(p + 1) == 's' &&
1138 !strchr(p + 2, '%'))
1139 g_snprintf(buf, sizeof(buf) - 1, cmdline, prtmp);
1142 g_warning("Print command-line is invalid: '%s'\n",
1144 g_snprintf(buf, sizeof(buf) - 1, def_cmd, prtmp);
1150 if (buf[strlen(buf) - 1] != '&') strncat(buf, "&", sizeof(buf));
1151 if (system(buf) == -1)
1152 g_warning("system(%s) failed.", buf);
1155 MsgInfo *procmsg_msginfo_new_ref(MsgInfo *msginfo)
1162 MsgInfo *procmsg_msginfo_new(void)
1164 MsgInfo *newmsginfo;
1166 newmsginfo = g_new0(MsgInfo, 1);
1167 newmsginfo->refcnt = 1;
1172 MsgInfo *procmsg_msginfo_copy(MsgInfo *msginfo)
1174 MsgInfo *newmsginfo;
1177 if (msginfo == NULL) return NULL;
1179 newmsginfo = g_new0(MsgInfo, 1);
1181 newmsginfo->refcnt = 1;
1183 #define MEMBCOPY(mmb) newmsginfo->mmb = msginfo->mmb
1184 #define MEMBDUP(mmb) newmsginfo->mmb = msginfo->mmb ? \
1185 g_strdup(msginfo->mmb) : NULL
1200 MEMBDUP(newsgroups);
1207 MEMBCOPY(to_folder);
1209 if (msginfo->extradata) {
1210 newmsginfo->extradata = g_new0(MsgInfoExtraData, 1);
1211 MEMBDUP(extradata->face);
1212 MEMBDUP(extradata->xface);
1213 MEMBDUP(extradata->dispositionnotificationto);
1214 MEMBDUP(extradata->returnreceiptto);
1215 MEMBDUP(extradata->partial_recv);
1216 MEMBDUP(extradata->account_server);
1217 MEMBDUP(extradata->account_login);
1218 MEMBDUP(extradata->list_post);
1219 MEMBDUP(extradata->list_subscribe);
1220 MEMBDUP(extradata->list_unsubscribe);
1221 MEMBDUP(extradata->list_help);
1222 MEMBDUP(extradata->list_archive);
1223 MEMBDUP(extradata->list_owner);
1226 refs = msginfo->references;
1227 for (refs = msginfo->references; refs != NULL; refs = refs->next) {
1228 newmsginfo->references = g_slist_prepend
1229 (newmsginfo->references, g_strdup(refs->data));
1231 newmsginfo->references = g_slist_reverse(newmsginfo->references);
1234 MEMBDUP(plaintext_file);
1239 MsgInfo *procmsg_msginfo_get_full_info_from_file(MsgInfo *msginfo, const gchar *file)
1241 MsgInfo *full_msginfo;
1243 if (msginfo == NULL) return NULL;
1245 if (!file || !is_file_exist(file)) {
1246 g_warning("procmsg_msginfo_get_full_info_from_file(): can't get message file.\n");
1250 full_msginfo = procheader_parse_file(file, msginfo->flags, TRUE, FALSE);
1251 if (!full_msginfo) return NULL;
1253 msginfo->total_size = full_msginfo->total_size;
1254 msginfo->planned_download = full_msginfo->planned_download;
1256 if (full_msginfo->extradata) {
1257 if (!msginfo->extradata)
1258 msginfo->extradata = g_new0(MsgInfoExtraData, 1);
1259 if (!msginfo->extradata->list_post)
1260 msginfo->extradata->list_post = g_strdup(full_msginfo->extradata->list_post);
1261 if (!msginfo->extradata->list_subscribe)
1262 msginfo->extradata->list_subscribe = g_strdup(full_msginfo->extradata->list_subscribe);
1263 if (!msginfo->extradata->list_unsubscribe)
1264 msginfo->extradata->list_unsubscribe = g_strdup(full_msginfo->extradata->list_unsubscribe);
1265 if (!msginfo->extradata->list_help)
1266 msginfo->extradata->list_help = g_strdup(full_msginfo->extradata->list_help);
1267 if (!msginfo->extradata->list_archive)
1268 msginfo->extradata->list_archive= g_strdup(full_msginfo->extradata->list_archive);
1269 if (!msginfo->extradata->list_owner)
1270 msginfo->extradata->list_owner = g_strdup(full_msginfo->extradata->list_owner);
1271 if (!msginfo->extradata->xface)
1272 msginfo->extradata->xface = g_strdup(full_msginfo->extradata->xface);
1273 if (!msginfo->extradata->face)
1274 msginfo->extradata->face = g_strdup(full_msginfo->extradata->face);
1275 if (!msginfo->extradata->dispositionnotificationto)
1276 msginfo->extradata->dispositionnotificationto =
1277 g_strdup(full_msginfo->extradata->dispositionnotificationto);
1278 if (!msginfo->extradata->returnreceiptto)
1279 msginfo->extradata->returnreceiptto = g_strdup
1280 (full_msginfo->extradata->returnreceiptto);
1281 if (!msginfo->extradata->partial_recv && full_msginfo->extradata->partial_recv)
1282 msginfo->extradata->partial_recv = g_strdup
1283 (full_msginfo->extradata->partial_recv);
1284 if (!msginfo->extradata->account_server && full_msginfo->extradata->account_server)
1285 msginfo->extradata->account_server = g_strdup
1286 (full_msginfo->extradata->account_server);
1287 if (!msginfo->extradata->account_login && full_msginfo->extradata->account_login)
1288 msginfo->extradata->account_login = g_strdup
1289 (full_msginfo->extradata->account_login);
1291 procmsg_msginfo_free(full_msginfo);
1293 return procmsg_msginfo_new_ref(msginfo);
1296 MsgInfo *procmsg_msginfo_get_full_info(MsgInfo *msginfo)
1298 MsgInfo *full_msginfo;
1301 if (msginfo == NULL) return NULL;
1303 file = procmsg_get_message_file_path(msginfo);
1304 if (!file || !is_file_exist(file)) {
1306 file = procmsg_get_message_file(msginfo);
1308 if (!file || !is_file_exist(file)) {
1309 g_warning("procmsg_msginfo_get_full_info(): can't get message file.\n");
1313 full_msginfo = procmsg_msginfo_get_full_info_from_file(msginfo, file);
1315 return full_msginfo;
1318 void procmsg_msginfo_free(MsgInfo *msginfo)
1320 if (msginfo == NULL) return;
1323 if (msginfo->refcnt > 0)
1326 if (msginfo->to_folder) {
1327 msginfo->to_folder->op_count--;
1328 folder_item_update(msginfo->to_folder, F_ITEM_UPDATE_MSGCNT);
1331 g_free(msginfo->fromspace);
1333 g_free(msginfo->fromname);
1335 g_free(msginfo->date);
1336 g_free(msginfo->from);
1337 g_free(msginfo->to);
1338 g_free(msginfo->cc);
1339 g_free(msginfo->newsgroups);
1340 g_free(msginfo->subject);
1341 g_free(msginfo->msgid);
1342 g_free(msginfo->inreplyto);
1343 g_free(msginfo->xref);
1345 if (msginfo->extradata) {
1346 g_free(msginfo->extradata->returnreceiptto);
1347 g_free(msginfo->extradata->dispositionnotificationto);
1348 g_free(msginfo->extradata->xface);
1349 g_free(msginfo->extradata->face);
1350 g_free(msginfo->extradata->list_post);
1351 g_free(msginfo->extradata->list_subscribe);
1352 g_free(msginfo->extradata->list_unsubscribe);
1353 g_free(msginfo->extradata->list_help);
1354 g_free(msginfo->extradata->list_archive);
1355 g_free(msginfo->extradata->list_owner);
1356 g_free(msginfo->extradata->partial_recv);
1357 g_free(msginfo->extradata->account_server);
1358 g_free(msginfo->extradata->account_login);
1359 g_free(msginfo->extradata);
1361 slist_free_strings(msginfo->references);
1362 g_slist_free(msginfo->references);
1363 g_slist_free(msginfo->tags);
1365 g_free(msginfo->plaintext_file);
1370 guint procmsg_msginfo_memusage(MsgInfo *msginfo)
1375 memusage += sizeof(MsgInfo);
1376 if (msginfo->fromname)
1377 memusage += strlen(msginfo->fromname);
1379 memusage += strlen(msginfo->date);
1381 memusage += strlen(msginfo->from);
1383 memusage += strlen(msginfo->to);
1385 memusage += strlen(msginfo->cc);
1386 if (msginfo->newsgroups)
1387 memusage += strlen(msginfo->newsgroups);
1388 if (msginfo->subject)
1389 memusage += strlen(msginfo->subject);
1391 memusage += strlen(msginfo->msgid);
1392 if (msginfo->inreplyto)
1393 memusage += strlen(msginfo->inreplyto);
1395 for (tmp = msginfo->references; tmp; tmp=tmp->next) {
1396 gchar *r = (gchar *)tmp->data;
1397 memusage += r?strlen(r):0 + sizeof(GSList);
1399 if (msginfo->fromspace)
1400 memusage += strlen(msginfo->fromspace);
1402 for (tmp = msginfo->tags; tmp; tmp=tmp->next) {
1403 memusage += sizeof(GSList);
1405 if (msginfo->extradata) {
1406 memusage += sizeof(MsgInfoExtraData);
1407 if (msginfo->extradata->xface)
1408 memusage += strlen(msginfo->extradata->xface);
1409 if (msginfo->extradata->face)
1410 memusage += strlen(msginfo->extradata->face);
1411 if (msginfo->extradata->dispositionnotificationto)
1412 memusage += strlen(msginfo->extradata->dispositionnotificationto);
1413 if (msginfo->extradata->returnreceiptto)
1414 memusage += strlen(msginfo->extradata->returnreceiptto);
1416 if (msginfo->extradata->partial_recv)
1417 memusage += strlen(msginfo->extradata->partial_recv);
1418 if (msginfo->extradata->account_server)
1419 memusage += strlen(msginfo->extradata->account_server);
1420 if (msginfo->extradata->account_login)
1421 memusage += strlen(msginfo->extradata->account_login);
1423 if (msginfo->extradata->list_post)
1424 memusage += strlen(msginfo->extradata->list_post);
1425 if (msginfo->extradata->list_subscribe)
1426 memusage += strlen(msginfo->extradata->list_subscribe);
1427 if (msginfo->extradata->list_unsubscribe)
1428 memusage += strlen(msginfo->extradata->list_unsubscribe);
1429 if (msginfo->extradata->list_help)
1430 memusage += strlen(msginfo->extradata->list_help);
1431 if (msginfo->extradata->list_archive)
1432 memusage += strlen(msginfo->extradata->list_archive);
1433 if (msginfo->extradata->list_owner)
1434 memusage += strlen(msginfo->extradata->list_owner);
1439 static gint procmsg_send_message_queue_full(const gchar *file, gboolean keep_session, gchar **errstr,
1440 FolderItem *queue, gint msgnum, gboolean *queued_removed)
1442 static HeaderEntry qentry[] = {{"S:", NULL, FALSE},
1443 {"SSV:", NULL, FALSE},
1444 {"R:", NULL, FALSE},
1445 {"NG:", NULL, FALSE},
1446 {"MAID:", NULL, FALSE},
1447 {"NAID:", NULL, FALSE},
1448 {"SCF:", NULL, FALSE},
1449 {"RMID:", NULL, FALSE},
1450 {"FMID:", NULL, FALSE},
1451 {"X-Claws-Privacy-System:", NULL, FALSE},
1452 {"X-Claws-Encrypt:", NULL, FALSE},
1453 {"X-Claws-Encrypt-Data:", NULL, FALSE},
1454 {"X-Claws-End-Special-Headers:", NULL, FALSE},
1455 {"X-Sylpheed-Privacy-System:", NULL, FALSE},
1456 {"X-Sylpheed-Encrypt:", NULL, FALSE},
1457 {"X-Sylpheed-Encrypt-Data:", NULL, FALSE},
1458 {"X-Sylpheed-End-Special-Headers:", NULL, FALSE},
1459 {NULL, NULL, FALSE}};
1462 gint mailval = 0, newsval = 0;
1464 gchar *smtpserver = NULL;
1465 GSList *to_list = NULL;
1466 GSList *newsgroup_list = NULL;
1467 gchar *savecopyfolder = NULL;
1468 gchar *replymessageid = NULL;
1469 gchar *fwdmessageid = NULL;
1470 gchar *privacy_system = NULL;
1471 gboolean encrypt = FALSE;
1472 gchar *encrypt_data = NULL;
1473 gchar buf[BUFFSIZE];
1475 PrefsAccount *mailac = NULL, *newsac = NULL;
1476 gboolean save_clear_text = TRUE;
1477 gchar *tmp_enc_file = NULL;
1481 cm_return_val_if_fail(file != NULL, -1);
1483 if ((fp = g_fopen(file, "rb")) == NULL) {
1484 FILE_OP_ERROR(file, "fopen");
1486 if (*errstr) g_free(*errstr);
1487 *errstr = g_strdup_printf(_("Couldn't open file %s."), file);
1492 while ((hnum = procheader_get_one_field(buf, sizeof(buf), fp, qentry))
1494 gchar *p = buf + strlen(qentry[hnum].name);
1502 if (smtpserver == NULL)
1503 smtpserver = g_strdup(p);
1506 to_list = address_list_append(to_list, p);
1509 newsgroup_list = newsgroup_list_append(newsgroup_list, p);
1511 case Q_MAIL_ACCOUNT_ID:
1512 mailac = account_find_from_id(atoi(p));
1514 case Q_NEWS_ACCOUNT_ID:
1515 newsac = account_find_from_id(atoi(p));
1517 case Q_SAVE_COPY_FOLDER:
1518 if (savecopyfolder == NULL)
1519 savecopyfolder = g_strdup(p);
1521 case Q_REPLY_MESSAGE_ID:
1522 if (replymessageid == NULL)
1523 replymessageid = g_strdup(p);
1525 case Q_FWD_MESSAGE_ID:
1526 if (fwdmessageid == NULL)
1527 fwdmessageid = g_strdup(p);
1529 case Q_PRIVACY_SYSTEM:
1530 case Q_PRIVACY_SYSTEM_OLD:
1531 if (privacy_system == NULL)
1532 privacy_system = g_strdup(p);
1539 case Q_ENCRYPT_DATA:
1540 case Q_ENCRYPT_DATA_OLD:
1541 if (encrypt_data == NULL)
1542 encrypt_data = g_strdup(p);
1545 case Q_CLAWS_HDRS_OLD:
1546 /* end of special headers reached */
1547 goto send_mail; /* can't "break;break;" */
1551 filepos = ftell(fp);
1556 if (mailac && mailac->save_encrypted_as_clear_text
1557 && !mailac->encrypt_to_self)
1558 save_clear_text = TRUE;
1560 save_clear_text = FALSE;
1565 mimeinfo = procmime_scan_queue_file(file);
1566 if (!privacy_encrypt(privacy_system, mimeinfo, encrypt_data)
1567 || (fp = my_tmpfile()) == NULL
1568 || procmime_write_mimeinfo(mimeinfo, fp) < 0) {
1571 procmime_mimeinfo_free_all(mimeinfo);
1574 slist_free_strings(to_list);
1575 g_slist_free(to_list);
1576 slist_free_strings(newsgroup_list);
1577 g_slist_free(newsgroup_list);
1578 g_free(savecopyfolder);
1579 g_free(replymessageid);
1580 g_free(fwdmessageid);
1581 g_free(privacy_system);
1582 g_free(encrypt_data);
1584 if (*errstr) g_free(*errstr);
1585 *errstr = g_strdup_printf(_("Couldn't encrypt the email: %s"),
1586 privacy_get_error());
1592 if (!save_clear_text) {
1593 gchar *content = NULL;
1594 FILE *tmpfp = get_tmpfile_in_dir(get_mime_tmp_dir(), &tmp_enc_file);
1598 content = file_read_stream_to_str(fp);
1601 str_write_to_file(content, tmp_enc_file);
1604 g_warning("couldn't get tempfile\n");
1608 procmime_mimeinfo_free_all(mimeinfo);
1614 debug_print("Sending message by mail\n");
1617 if (*errstr) g_free(*errstr);
1618 *errstr = g_strdup_printf(_("Queued message header is broken."));
1621 } else if (mailac && mailac->use_mail_command &&
1622 mailac->mail_command && (* mailac->mail_command)) {
1623 mailval = send_message_local(mailac->mail_command, fp);
1627 mailac = account_find_from_smtp_server(from, smtpserver);
1629 g_warning("Account not found. "
1630 "Using current account...\n");
1631 mailac = cur_account;
1636 mailval = send_message_smtp_full(mailac, to_list, fp, keep_session);
1637 if (mailval == -1 && errstr) {
1638 if (*errstr) g_free(*errstr);
1639 *errstr = g_strdup_printf(_("An error happened during SMTP session."));
1642 PrefsAccount tmp_ac;
1644 g_warning("Account not found.\n");
1646 memset(&tmp_ac, 0, sizeof(PrefsAccount));
1647 tmp_ac.address = from;
1648 tmp_ac.smtp_server = smtpserver;
1649 tmp_ac.smtpport = SMTP_PORT;
1650 mailval = send_message_smtp(&tmp_ac, to_list, fp);
1651 if (mailval == -1 && errstr) {
1652 if (*errstr) g_free(*errstr);
1653 *errstr = g_strdup_printf(_("No specific account has been found to "
1654 "send, and an error happened during SMTP session."));
1658 } else if (!to_list && !newsgroup_list) {
1660 if (*errstr) g_free(*errstr);
1661 *errstr = g_strdup(_("Couldn't determine sending informations. "
1662 "Maybe the email hasn't been generated by Claws Mail."));
1667 fseek(fp, filepos, SEEK_SET);
1668 if (newsgroup_list && newsac && (mailval == 0)) {
1673 /* write to temporary file */
1674 tmp = g_strdup_printf("%s%cnntp%p", get_tmp_dir(),
1675 G_DIR_SEPARATOR, file);
1676 if ((tmpfp = g_fopen(tmp, "wb")) == NULL) {
1677 FILE_OP_ERROR(tmp, "fopen");
1679 alertpanel_error(_("Couldn't create temporary file for news sending."));
1681 if (change_file_mode_rw(tmpfp, tmp) < 0) {
1682 FILE_OP_ERROR(tmp, "chmod");
1683 g_warning("can't change file mode\n");
1686 while ((newsval == 0) && fgets(buf, sizeof(buf), fp) != NULL) {
1687 if (fputs(buf, tmpfp) == EOF) {
1688 FILE_OP_ERROR(tmp, "fputs");
1691 if (*errstr) g_free(*errstr);
1692 *errstr = g_strdup_printf(_("Error when writing temporary file for news sending."));
1699 debug_print("Sending message by news\n");
1701 folder = FOLDER(newsac->folder);
1703 newsval = news_post(folder, tmp);
1704 if (newsval < 0 && errstr) {
1705 if (*errstr) g_free(*errstr);
1706 *errstr = g_strdup_printf(_("Error occurred while posting the message to %s."),
1707 newsac->nntp_server);
1717 /* save message to outbox */
1718 if (mailval == 0 && newsval == 0 && savecopyfolder) {
1721 debug_print("saving sent message...\n");
1723 outbox = folder_find_item_from_identifier(savecopyfolder);
1725 outbox = folder_get_default_outbox();
1727 if (save_clear_text || tmp_enc_file == NULL) {
1728 gboolean saved = FALSE;
1729 *queued_removed = FALSE;
1730 if (queue && msgnum > 0) {
1731 MsgInfo *queued_mail = folder_item_get_msginfo(queue, msgnum);
1732 if (folder_item_move_msg(outbox, queued_mail) >= 0) {
1733 debug_print("moved queued mail %d to sent folder\n", msgnum);
1735 *queued_removed = TRUE;
1736 } else if (folder_item_copy_msg(outbox, queued_mail) >= 0) {
1737 debug_print("copied queued mail %d to sent folder\n", msgnum);
1740 procmsg_msginfo_free(queued_mail);
1743 debug_print("resaving clear text queued mail to sent folder\n");
1744 procmsg_save_to_outbox(outbox, file, TRUE);
1747 debug_print("saving encrpyted queued mail to sent folder\n");
1748 procmsg_save_to_outbox(outbox, tmp_enc_file, FALSE);
1752 if (tmp_enc_file != NULL) {
1753 claws_unlink(tmp_enc_file);
1755 tmp_enc_file = NULL;
1758 if (replymessageid != NULL || fwdmessageid != NULL) {
1762 if (replymessageid != NULL)
1763 tokens = g_strsplit(replymessageid, "\t", 0);
1765 tokens = g_strsplit(fwdmessageid, "\t", 0);
1766 item = folder_find_item_from_identifier(tokens[0]);
1768 /* check if queued message has valid folder and message id */
1769 if (item != NULL && tokens[2] != NULL) {
1772 msginfo = folder_item_get_msginfo(item, atoi(tokens[1]));
1774 /* check if referring message exists and has a message id */
1775 if ((msginfo != NULL) &&
1776 (msginfo->msgid != NULL) &&
1777 (strcmp(msginfo->msgid, tokens[2]) != 0)) {
1778 procmsg_msginfo_free(msginfo);
1782 if (msginfo == NULL) {
1783 msginfo = folder_item_get_msginfo_by_msgid(item, tokens[2]);
1786 if (msginfo != NULL) {
1787 if (replymessageid != NULL) {
1788 MsgPermFlags to_unset = 0;
1790 if (prefs_common.mark_as_read_on_new_window)
1791 to_unset = (MSG_NEW|MSG_UNREAD);
1793 procmsg_msginfo_unset_flags(msginfo, to_unset, 0);
1794 procmsg_msginfo_set_flags(msginfo, MSG_REPLIED, 0);
1796 procmsg_msginfo_set_flags(msginfo, MSG_FORWARDED, 0);
1798 procmsg_msginfo_free(msginfo);
1806 slist_free_strings(to_list);
1807 g_slist_free(to_list);
1808 slist_free_strings(newsgroup_list);
1809 g_slist_free(newsgroup_list);
1810 g_free(savecopyfolder);
1811 g_free(replymessageid);
1812 g_free(fwdmessageid);
1813 g_free(privacy_system);
1814 g_free(encrypt_data);
1816 return (newsval != 0 ? newsval : mailval);
1819 gint procmsg_send_message_queue(const gchar *file, gchar **errstr, FolderItem *queue, gint msgnum, gboolean *queued_removed)
1821 gint result = procmsg_send_message_queue_full(file, FALSE, errstr, queue, msgnum, queued_removed);
1822 toolbar_main_set_sensitive(mainwindow_get_mainwindow());
1826 gint procmsg_send_message_queue_with_lock(const gchar *file, gchar **errstr, FolderItem *queue, gint msgnum, gboolean *queued_removed)
1829 if (procmsg_queue_lock(errstr)) {
1830 val = procmsg_send_message_queue(file, errstr, queue, msgnum, queued_removed);
1831 procmsg_queue_unlock();
1837 static void update_folder_msg_counts(FolderItem *item, MsgInfo *msginfo, MsgPermFlags old_flags)
1839 MsgPermFlags new_flags = msginfo->flags.perm_flags;
1842 if (!(old_flags & MSG_NEW) && (new_flags & MSG_NEW)) {
1846 if ((old_flags & MSG_NEW) && !(new_flags & MSG_NEW)) {
1851 if (!(old_flags & MSG_UNREAD) && (new_flags & MSG_UNREAD)) {
1852 item->unread_msgs++;
1853 if (procmsg_msg_has_marked_parent(msginfo))
1854 item->unreadmarked_msgs++;
1857 if ((old_flags & MSG_UNREAD) && !(new_flags & MSG_UNREAD)) {
1858 item->unread_msgs--;
1859 if (procmsg_msg_has_marked_parent(msginfo))
1860 item->unreadmarked_msgs--;
1864 if (!(old_flags & MSG_MARKED) && (new_flags & MSG_MARKED)) {
1865 procmsg_update_unread_children(msginfo, TRUE);
1866 item->marked_msgs++;
1869 if ((old_flags & MSG_MARKED) && !(new_flags & MSG_MARKED)) {
1870 procmsg_update_unread_children(msginfo, FALSE);
1871 item->marked_msgs--;
1874 if (!(old_flags & MSG_REPLIED) && (new_flags & MSG_REPLIED)) {
1875 item->replied_msgs++;
1878 if ((old_flags & MSG_REPLIED) && !(new_flags & MSG_REPLIED)) {
1879 item->replied_msgs--;
1882 if (!(old_flags & MSG_FORWARDED) && (new_flags & MSG_FORWARDED)) {
1883 item->forwarded_msgs++;
1886 if ((old_flags & MSG_FORWARDED) && !(new_flags & MSG_FORWARDED)) {
1887 item->forwarded_msgs--;
1890 if (!(old_flags & MSG_LOCKED) && (new_flags & MSG_LOCKED)) {
1891 item->locked_msgs++;
1894 if ((old_flags & MSG_LOCKED) && !(new_flags & MSG_LOCKED)) {
1895 item->locked_msgs--;
1898 if ((old_flags & MSG_IGNORE_THREAD) && !(new_flags & MSG_IGNORE_THREAD)) {
1899 item->ignored_msgs--;
1902 if (!(old_flags & MSG_IGNORE_THREAD) && (new_flags & MSG_IGNORE_THREAD)) {
1903 item->ignored_msgs++;
1906 if ((old_flags & MSG_WATCH_THREAD) && !(new_flags & MSG_WATCH_THREAD)) {
1907 item->watched_msgs--;
1910 if (!(old_flags & MSG_WATCH_THREAD) && (new_flags & MSG_WATCH_THREAD)) {
1911 item->watched_msgs++;
1915 void procmsg_msginfo_set_flags(MsgInfo *msginfo, MsgPermFlags perm_flags, MsgTmpFlags tmp_flags)
1918 MsgInfoUpdate msginfo_update;
1919 MsgPermFlags perm_flags_new, perm_flags_old;
1920 MsgTmpFlags tmp_flags_old;
1922 cm_return_if_fail(msginfo != NULL);
1923 item = msginfo->folder;
1924 cm_return_if_fail(item != NULL);
1926 debug_print("Setting flags for message %d in folder %s\n", msginfo->msgnum, item->path);
1928 /* Perm Flags handling */
1929 perm_flags_old = msginfo->flags.perm_flags;
1930 perm_flags_new = msginfo->flags.perm_flags | perm_flags;
1931 if ((perm_flags & MSG_IGNORE_THREAD) || (perm_flags_old & MSG_IGNORE_THREAD)) {
1932 perm_flags_new &= ~(MSG_NEW | MSG_UNREAD);
1934 if ((perm_flags & MSG_WATCH_THREAD) || (perm_flags_old & MSG_WATCH_THREAD)) {
1935 perm_flags_new &= ~(MSG_IGNORE_THREAD);
1938 if (perm_flags_old != perm_flags_new) {
1939 folder_item_change_msg_flags(msginfo->folder, msginfo, perm_flags_new);
1941 update_folder_msg_counts(item, msginfo, perm_flags_old);
1942 summary_update_unread(mainwindow_get_mainwindow()->summaryview, NULL);
1945 /* Tmp flags handling */
1946 tmp_flags_old = msginfo->flags.tmp_flags;
1947 msginfo->flags.tmp_flags |= tmp_flags;
1949 /* update notification */
1950 if ((perm_flags_old != perm_flags_new) || (tmp_flags_old != msginfo->flags.tmp_flags)) {
1951 msginfo_update.msginfo = msginfo;
1952 msginfo_update.flags = MSGINFO_UPDATE_FLAGS;
1953 hooks_invoke(MSGINFO_UPDATE_HOOKLIST, &msginfo_update);
1954 folder_item_update(msginfo->folder, F_ITEM_UPDATE_MSGCNT);
1958 void procmsg_msginfo_unset_flags(MsgInfo *msginfo, MsgPermFlags perm_flags, MsgTmpFlags tmp_flags)
1961 MsgInfoUpdate msginfo_update;
1962 MsgPermFlags perm_flags_new, perm_flags_old;
1963 MsgTmpFlags tmp_flags_old;
1965 cm_return_if_fail(msginfo != NULL);
1966 item = msginfo->folder;
1967 cm_return_if_fail(item != NULL);
1969 debug_print("Unsetting flags for message %d in folder %s\n", msginfo->msgnum, item->path);
1971 /* Perm Flags handling */
1972 perm_flags_old = msginfo->flags.perm_flags;
1973 perm_flags_new = msginfo->flags.perm_flags & ~perm_flags;
1975 if (perm_flags_old != perm_flags_new) {
1976 folder_item_change_msg_flags(msginfo->folder, msginfo, perm_flags_new);
1978 update_folder_msg_counts(item, msginfo, perm_flags_old);
1981 /* Tmp flags hanlding */
1982 tmp_flags_old = msginfo->flags.tmp_flags;
1983 msginfo->flags.tmp_flags &= ~tmp_flags;
1985 /* update notification */
1986 if ((perm_flags_old != perm_flags_new) || (tmp_flags_old != msginfo->flags.tmp_flags)) {
1987 msginfo_update.msginfo = msginfo;
1988 msginfo_update.flags = MSGINFO_UPDATE_FLAGS;
1989 hooks_invoke(MSGINFO_UPDATE_HOOKLIST, &msginfo_update);
1990 folder_item_update(msginfo->folder, F_ITEM_UPDATE_MSGCNT);
1994 void procmsg_msginfo_change_flags(MsgInfo *msginfo,
1995 MsgPermFlags add_perm_flags, MsgTmpFlags add_tmp_flags,
1996 MsgPermFlags rem_perm_flags, MsgTmpFlags rem_tmp_flags)
1999 MsgInfoUpdate msginfo_update;
2000 MsgPermFlags perm_flags_new, perm_flags_old;
2001 MsgTmpFlags tmp_flags_old;
2003 cm_return_if_fail(msginfo != NULL);
2004 item = msginfo->folder;
2005 cm_return_if_fail(item != NULL);
2007 debug_print("Changing flags for message %d in folder %s\n", msginfo->msgnum, item->path);
2009 /* Perm Flags handling */
2010 perm_flags_old = msginfo->flags.perm_flags;
2011 perm_flags_new = (msginfo->flags.perm_flags & ~rem_perm_flags) | add_perm_flags;
2012 if ((add_perm_flags & MSG_IGNORE_THREAD) || (perm_flags_old & MSG_IGNORE_THREAD)) {
2013 perm_flags_new &= ~(MSG_NEW | MSG_UNREAD);
2015 if ((add_perm_flags & MSG_WATCH_THREAD) || (perm_flags_old & MSG_WATCH_THREAD)) {
2016 perm_flags_new &= ~(MSG_IGNORE_THREAD);
2019 if (perm_flags_old != perm_flags_new) {
2020 folder_item_change_msg_flags(msginfo->folder, msginfo, perm_flags_new);
2022 update_folder_msg_counts(item, msginfo, perm_flags_old);
2026 /* Tmp flags handling */
2027 tmp_flags_old = msginfo->flags.tmp_flags;
2028 msginfo->flags.tmp_flags &= ~rem_tmp_flags;
2029 msginfo->flags.tmp_flags |= add_tmp_flags;
2031 /* update notification */
2032 if ((perm_flags_old != perm_flags_new) || (tmp_flags_old != msginfo->flags.tmp_flags)) {
2033 msginfo_update.msginfo = msginfo;
2034 msginfo_update.flags = MSGINFO_UPDATE_FLAGS;
2035 hooks_invoke(MSGINFO_UPDATE_HOOKLIST, &msginfo_update);
2036 folder_item_update(msginfo->folder, F_ITEM_UPDATE_MSGCNT);
2041 *\brief check for flags (e.g. mark) in prior msgs of current thread
2043 *\param info Current message
2044 *\param perm_flags Flags to be checked
2045 *\param parentmsgs Hash of prior msgs to avoid loops
2047 *\return gboolean TRUE if perm_flags are found
2049 static gboolean procmsg_msg_has_flagged_parent_real(MsgInfo *info,
2050 MsgPermFlags perm_flags, GHashTable *parentmsgs)
2054 cm_return_val_if_fail(info != NULL, FALSE);
2056 if (info != NULL && info->folder != NULL && info->inreplyto != NULL) {
2057 tmp = folder_item_get_msginfo_by_msgid(info->folder,
2059 if (tmp && (tmp->flags.perm_flags & perm_flags)) {
2060 procmsg_msginfo_free(tmp);
2062 } else if (tmp != NULL) {
2065 if (g_hash_table_lookup(parentmsgs, info)) {
2066 debug_print("loop detected: %d\n",
2070 g_hash_table_insert(parentmsgs, info, "1");
2071 result = procmsg_msg_has_flagged_parent_real(
2072 tmp, perm_flags, parentmsgs);
2074 procmsg_msginfo_free(tmp);
2084 *\brief Callback for cleaning up hash of parentmsgs
2086 static gboolean parentmsgs_hash_remove(gpointer key,
2094 *\brief Set up list of parentmsgs
2095 * See procmsg_msg_has_flagged_parent_real()
2097 gboolean procmsg_msg_has_flagged_parent(MsgInfo *info, MsgPermFlags perm_flags)
2100 static GHashTable *parentmsgs = NULL;
2102 if (parentmsgs == NULL)
2103 parentmsgs = g_hash_table_new(NULL, NULL);
2105 result = procmsg_msg_has_flagged_parent_real(info, perm_flags, parentmsgs);
2106 g_hash_table_foreach_remove(parentmsgs, parentmsgs_hash_remove, NULL);
2112 *\brief Check if msgs prior in thread are marked
2113 * See procmsg_msg_has_flagged_parent_real()
2115 gboolean procmsg_msg_has_marked_parent(MsgInfo *info)
2117 return procmsg_msg_has_flagged_parent(info, MSG_MARKED);
2121 static GSList *procmsg_find_children_func(MsgInfo *info,
2122 GSList *children, GSList *all)
2126 cm_return_val_if_fail(info!=NULL, children);
2127 if (info->msgid == NULL)
2130 for (cur = all; cur != NULL; cur = g_slist_next(cur)) {
2131 MsgInfo *tmp = (MsgInfo *)cur->data;
2132 if (tmp->inreplyto && !strcmp(tmp->inreplyto, info->msgid)) {
2133 /* Check if message is already in the list */
2134 if ((children == NULL) ||
2135 (g_slist_index(children, tmp) == -1)) {
2136 children = g_slist_prepend(children,
2137 procmsg_msginfo_new_ref(tmp));
2138 children = procmsg_find_children_func(tmp,
2147 static GSList *procmsg_find_children (MsgInfo *info)
2152 cm_return_val_if_fail(info!=NULL, NULL);
2153 all = folder_item_get_msg_list(info->folder);
2154 children = procmsg_find_children_func(info, NULL, all);
2155 if (children != NULL) {
2156 for (cur = all; cur != NULL; cur = g_slist_next(cur)) {
2157 /* this will not free the used pointers
2158 created with procmsg_msginfo_new_ref */
2159 procmsg_msginfo_free((MsgInfo *)cur->data);
2167 static void procmsg_update_unread_children(MsgInfo *info, gboolean newly_marked)
2169 GSList *children = procmsg_find_children(info);
2171 for (cur = children; cur != NULL; cur = g_slist_next(cur)) {
2172 MsgInfo *tmp = (MsgInfo *)cur->data;
2173 if(MSG_IS_UNREAD(tmp->flags) && !MSG_IS_IGNORE_THREAD(tmp->flags)) {
2175 info->folder->unreadmarked_msgs++;
2177 info->folder->unreadmarked_msgs--;
2178 folder_item_update(info->folder, F_ITEM_UPDATE_MSGCNT);
2180 procmsg_msginfo_free(tmp);
2182 g_slist_free(children);
2186 * Set the destination folder for a copy or move operation
2188 * \param msginfo The message which's destination folder is changed
2189 * \param to_folder The destination folder for the operation
2191 void procmsg_msginfo_set_to_folder(MsgInfo *msginfo, FolderItem *to_folder)
2193 if(msginfo->to_folder != NULL) {
2194 msginfo->to_folder->op_count--;
2195 folder_item_update(msginfo->to_folder, F_ITEM_UPDATE_MSGCNT);
2197 msginfo->to_folder = to_folder;
2198 if(to_folder != NULL) {
2199 to_folder->op_count++;
2200 folder_item_update(msginfo->to_folder, F_ITEM_UPDATE_MSGCNT);
2205 * Apply filtering actions to the msginfo
2207 * \param msginfo The MsgInfo describing the message that should be filtered
2208 * \return TRUE if the message was moved and MsgInfo is now invalid,
2211 static gboolean procmsg_msginfo_filter(MsgInfo *msginfo, PrefsAccount* ac_prefs)
2213 MailFilteringData mail_filtering_data;
2215 mail_filtering_data.msginfo = msginfo;
2216 mail_filtering_data.msglist = NULL;
2217 mail_filtering_data.filtered = NULL;
2218 mail_filtering_data.unfiltered = NULL;
2219 mail_filtering_data.account = ac_prefs;
2221 if (!ac_prefs || ac_prefs->filterhook_on_recv)
2222 if (hooks_invoke(MAIL_FILTERING_HOOKLIST, &mail_filtering_data))
2225 /* filter if enabled in prefs or move to inbox if not */
2226 if((filtering_rules != NULL) &&
2227 filter_message_by_msginfo(filtering_rules, msginfo, ac_prefs,
2228 FILTERING_INCORPORATION, NULL)) {
2235 void procmsg_msglist_filter(GSList *list, PrefsAccount *ac,
2236 GSList **filtered, GSList **unfiltered,
2239 GSList *cur, *to_do = NULL;
2240 gint total = 0, curnum = 0;
2241 MailFilteringData mail_filtering_data;
2243 cm_return_if_fail(filtered != NULL);
2244 cm_return_if_fail(unfiltered != NULL);
2252 total = g_slist_length(list);
2256 *unfiltered = g_slist_copy(list);
2260 statusbar_print_all(_("Filtering messages...\n"));
2262 mail_filtering_data.msginfo = NULL;
2263 mail_filtering_data.msglist = list;
2264 mail_filtering_data.filtered = NULL;
2265 mail_filtering_data.unfiltered = NULL;
2266 mail_filtering_data.account = ac;
2268 if (!ac || ac->filterhook_on_recv)
2269 hooks_invoke(MAIL_LISTFILTERING_HOOKLIST, &mail_filtering_data);
2271 if (mail_filtering_data.filtered == NULL &&
2272 mail_filtering_data.unfiltered == NULL) {
2273 /* nothing happened */
2274 debug_print(MAIL_LISTFILTERING_HOOKLIST " did nothing. filtering whole list normally.\n");
2277 if (mail_filtering_data.filtered != NULL) {
2278 /* keep track of what's been filtered by the hooks */
2279 debug_print(MAIL_LISTFILTERING_HOOKLIST " filtered some stuff. total %d filtered %d unfilt %d.\n",
2280 g_slist_length(list),
2281 g_slist_length(mail_filtering_data.filtered),
2282 g_slist_length(mail_filtering_data.unfiltered));
2284 *filtered = g_slist_copy(mail_filtering_data.filtered);
2286 if (mail_filtering_data.unfiltered != NULL) {
2287 /* what the hooks didn't handle will go in filtered or
2288 * unfiltered in the next loop */
2289 debug_print(MAIL_LISTFILTERING_HOOKLIST " left unfiltered stuff. total %d filtered %d unfilt %d.\n",
2290 g_slist_length(list),
2291 g_slist_length(mail_filtering_data.filtered),
2292 g_slist_length(mail_filtering_data.unfiltered));
2293 to_do = mail_filtering_data.unfiltered;
2296 for (cur = to_do; cur; cur = cur->next) {
2297 MsgInfo *info = (MsgInfo *)cur->data;
2298 if (procmsg_msginfo_filter(info, ac))
2299 *filtered = g_slist_prepend(*filtered, info);
2301 *unfiltered = g_slist_prepend(*unfiltered, info);
2302 statusbar_progress_all(curnum++, total, prefs_common.statusbar_update_step);
2305 g_slist_free(mail_filtering_data.filtered);
2306 g_slist_free(mail_filtering_data.unfiltered);
2308 *filtered = g_slist_reverse(*filtered);
2309 *unfiltered = g_slist_reverse(*unfiltered);
2311 statusbar_progress_all(0,0,0);
2312 statusbar_pop_all();
2315 MsgInfo *procmsg_msginfo_new_from_mimeinfo(MsgInfo *src_msginfo, MimeInfo *mimeinfo)
2317 MsgInfo *tmp_msginfo = NULL;
2318 MsgFlags flags = {0, 0};
2319 gchar *tmpfile = get_tmp_file();
2320 FILE *fp = g_fopen(tmpfile, "wb");
2322 if (!mimeinfo || mimeinfo->type != MIMETYPE_MESSAGE ||
2323 g_ascii_strcasecmp(mimeinfo->subtype, "rfc822")) {
2324 g_warning("procmsg_msginfo_new_from_mimeinfo(): unsuitable mimeinfo");
2331 if (fp && procmime_write_mimeinfo(mimeinfo, fp) >= 0) {
2334 tmp_msginfo = procheader_parse_file(
2341 if (tmp_msginfo != NULL) {
2343 tmp_msginfo->folder = src_msginfo->folder;
2344 tmp_msginfo->plaintext_file = g_strdup(tmpfile);
2346 g_warning("procmsg_msginfo_new_from_mimeinfo(): Can't generate new msginfo");
2354 static GSList *spam_learners = NULL;
2356 void procmsg_register_spam_learner (int (*learn_func)(MsgInfo *info, GSList *list, gboolean spam))
2358 if (!g_slist_find(spam_learners, learn_func))
2359 spam_learners = g_slist_append(spam_learners, learn_func);
2360 if (mainwindow_get_mainwindow()) {
2361 main_window_set_menu_sensitive(mainwindow_get_mainwindow());
2362 summary_set_menu_sensitive(
2363 mainwindow_get_mainwindow()->summaryview);
2364 toolbar_main_set_sensitive(mainwindow_get_mainwindow());
2368 void procmsg_unregister_spam_learner (int (*learn_func)(MsgInfo *info, GSList *list, gboolean spam))
2370 spam_learners = g_slist_remove(spam_learners, learn_func);
2371 if (mainwindow_get_mainwindow()) {
2372 main_window_set_menu_sensitive(mainwindow_get_mainwindow());
2373 summary_set_menu_sensitive(
2374 mainwindow_get_mainwindow()->summaryview);
2375 toolbar_main_set_sensitive(mainwindow_get_mainwindow());
2379 gboolean procmsg_spam_can_learn(void)
2381 return g_slist_length(spam_learners) > 0;
2384 int procmsg_spam_learner_learn (MsgInfo *info, GSList *list, gboolean spam)
2386 GSList *cur = spam_learners;
2388 for (; cur; cur = cur->next) {
2389 int ((*func)(MsgInfo *info, GSList *list, gboolean spam)) = cur->data;
2390 ret |= func(info, list, spam);
2395 static gchar *spam_folder_item = NULL;
2396 static FolderItem * (*procmsg_spam_get_folder_func)(MsgInfo *msginfo) = NULL;
2397 void procmsg_spam_set_folder (const char *item_identifier, FolderItem *(*spam_get_folder_func)(MsgInfo *info))
2399 g_free(spam_folder_item);
2400 if (item_identifier)
2401 spam_folder_item = g_strdup(item_identifier);
2403 spam_folder_item = NULL;
2404 if (spam_get_folder_func != NULL)
2405 procmsg_spam_get_folder_func = spam_get_folder_func;
2407 procmsg_spam_get_folder_func = NULL;
2410 FolderItem *procmsg_spam_get_folder (MsgInfo *msginfo)
2412 FolderItem *item = NULL;
2414 if (procmsg_spam_get_folder_func)
2415 item = procmsg_spam_get_folder_func(msginfo);
2416 if (item == NULL && spam_folder_item)
2417 item = folder_find_item_from_identifier(spam_folder_item);
2419 item = folder_get_default_trash();
2423 static void item_has_queued_mails(FolderItem *item, gpointer data)
2425 gboolean *result = (gboolean *)data;
2426 if (*result == TRUE)
2428 if (folder_has_parent_of_type(item, F_QUEUE)) {
2429 if (item->total_msgs == 0)
2432 GSList *msglist = folder_item_get_msg_list(item);
2434 for (cur = msglist; cur; cur = cur->next) {
2435 MsgInfo *msginfo = (MsgInfo *)cur->data;
2436 if (!MSG_IS_DELETED(msginfo->flags) &&
2437 !MSG_IS_LOCKED(msginfo->flags)) {
2442 procmsg_msg_list_free(msglist);
2447 gboolean procmsg_have_queued_mails_fast (void)
2449 gboolean result = FALSE;
2450 folder_func_to_all_folders(item_has_queued_mails, &result);
2454 static void item_has_trashed_mails(FolderItem *item, gpointer data)
2456 gboolean *result = (gboolean *)data;
2457 if (*result == TRUE)
2459 if (folder_has_parent_of_type(item, F_TRASH) && item->total_msgs > 0)
2463 gboolean procmsg_have_trashed_mails_fast (void)
2465 gboolean result = FALSE;
2466 folder_func_to_all_folders(item_has_trashed_mails, &result);
2470 gchar *procmsg_msginfo_get_tags_str(MsgInfo *msginfo)
2478 if (msginfo->tags == NULL)
2480 for (cur = msginfo->tags; cur; cur = cur->next) {
2481 const gchar *tag = tags_get_tag(GPOINTER_TO_INT(cur->data));
2485 tags = g_strdup(tag);
2487 int olen = strlen(tags);
2488 int nlen = olen + strlen(tag) + 2 /* strlen(", ") */;
2489 tags = g_realloc(tags, nlen+1);
2492 strcpy(tags+olen, ", ");
2493 strcpy(tags+olen+2, tag);
2500 void procmsg_msginfo_update_tags(MsgInfo *msginfo, gboolean set, gint id)
2508 msginfo->tags = g_slist_remove(
2510 GINT_TO_POINTER(id));
2511 changed.data = GINT_TO_POINTER(id);
2512 changed.next = NULL;
2513 folder_item_commit_tags(msginfo->folder, msginfo, NULL, &changed);
2515 if (!g_slist_find(msginfo->tags, GINT_TO_POINTER(id))) {
2516 msginfo->tags = g_slist_append(
2518 GINT_TO_POINTER(id));
2520 changed.data = GINT_TO_POINTER(id);
2521 changed.next = NULL;
2522 folder_item_commit_tags(msginfo->folder, msginfo, &changed, NULL);
2527 void procmsg_msginfo_clear_tags(MsgInfo *msginfo)
2529 GSList *unset = msginfo->tags;
2530 msginfo->tags = NULL;
2531 folder_item_commit_tags(msginfo->folder, msginfo, NULL, unset);
2532 g_slist_free(unset);