2 * Sylpheed -- a GTK+ based, lightweight, and fast e-mail client
3 * Copyright (C) 1999-2012 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 extern SessionStats session_stats;
54 static gint procmsg_send_message_queue_full(const gchar *file, gboolean keep_session, gchar **errstr,
55 FolderItem *queue, gint msgnum, gboolean *queued_removed);
56 static void procmsg_update_unread_children (MsgInfo *info,
57 gboolean newly_marked);
64 Q_MAIL_ACCOUNT_ID = 4,
65 Q_NEWS_ACCOUNT_ID = 5,
66 Q_SAVE_COPY_FOLDER = 6,
67 Q_REPLY_MESSAGE_ID = 7,
73 Q_PRIVACY_SYSTEM_OLD = 13,
75 Q_ENCRYPT_DATA_OLD = 15,
76 Q_CLAWS_HDRS_OLD = 16,
79 void procmsg_msg_list_free(GSList *mlist)
84 for (cur = mlist; cur != NULL; cur = cur->next) {
85 msginfo = (MsgInfo *)cur->data;
86 procmsg_msginfo_free(msginfo);
100 /* CLAWS subject threading:
102 in the first round it inserts subject lines in a
103 hashtable (subject <-> node)
105 the second round finishes the threads by attaching
106 matching subject lines to the one found in the
107 hashtable. will use the oldest node with the same
108 subject that is not more then thread_by_subject_max_age
109 days old (see subject_hashtable_lookup)
112 static void subject_hashtable_insert(GHashTable *hashtable, GNode *node)
118 cm_return_if_fail(hashtable != NULL);
119 cm_return_if_fail(node != NULL);
120 msginfo = (MsgInfo *) node->data;
121 cm_return_if_fail(msginfo != NULL);
123 subject = msginfo->subject;
127 subject += subject_get_prefix_length(subject);
129 list = g_hash_table_lookup(hashtable, subject);
130 list = g_slist_prepend(list, node);
131 g_hash_table_insert(hashtable, subject, list);
134 static GNode *subject_hashtable_lookup(GHashTable *hashtable, MsgInfo *msginfo)
138 GNode *node = NULL, *hashtable_node = NULL;
140 MsgInfo *hashtable_msginfo = NULL, *best_msginfo = NULL;
143 cm_return_val_if_fail(hashtable != NULL, NULL);
145 subject = msginfo->subject;
148 prefix_length = subject_get_prefix_length(subject);
149 if (prefix_length <= 0)
151 subject += prefix_length;
153 list = g_hash_table_lookup(hashtable, subject);
157 /* check all nodes with the same subject to find the best parent */
158 for (cur = list; cur; cur = cur->next) {
159 hashtable_node = (GNode *)cur->data;
160 hashtable_msginfo = (MsgInfo *) hashtable_node->data;
163 /* best node should be the oldest in the found nodes */
164 /* parent node must not be older then msginfo */
165 if ((hashtable_msginfo->date_t < msginfo->date_t) &&
166 ((best_msginfo == NULL) ||
167 (best_msginfo->date_t > hashtable_msginfo->date_t)))
170 /* parent node must not be more then thread_by_subject_max_age
171 days older then msginfo */
172 if (abs(difftime(msginfo->date_t, hashtable_msginfo->date_t)) >
173 prefs_common.thread_by_subject_max_age * 3600 * 24)
176 /* can add new tests for all matching
177 nodes found by subject */
180 node = hashtable_node;
181 best_msginfo = hashtable_msginfo;
188 static void subject_hashtable_free(gpointer key, gpointer value, gpointer data)
193 /* return the reversed thread tree */
194 GNode *procmsg_get_thread_tree(GSList *mlist)
196 GNode *root, *parent, *node, *next;
197 GHashTable *msgid_table;
198 GHashTable *subject_hashtable = NULL;
203 root = g_node_new(NULL);
204 msgid_table = g_hash_table_new(g_str_hash, g_str_equal);
206 if (prefs_common.thread_by_subject) {
207 subject_hashtable = g_hash_table_new(g_str_hash, g_str_equal);
210 for (; mlist != NULL; mlist = mlist->next) {
211 msginfo = (MsgInfo *)mlist->data;
214 if (msginfo->inreplyto) {
215 parent = g_hash_table_lookup(msgid_table, msginfo->inreplyto);
216 if (parent == NULL) {
220 node = g_node_insert_data_before
221 (parent, parent == root ? parent->children : NULL,
223 if ((msgid = msginfo->msgid) && g_hash_table_lookup(msgid_table, msgid) == NULL)
224 g_hash_table_insert(msgid_table, (gchar *)msgid, node);
226 /* CLAWS: add subject to hashtable (without prefix) */
227 if (prefs_common.thread_by_subject) {
228 subject_hashtable_insert(subject_hashtable, node);
232 /* complete the unfinished threads */
233 for (node = root->children; node != NULL; ) {
235 msginfo = (MsgInfo *)node->data;
238 if (msginfo->inreplyto)
239 parent = g_hash_table_lookup(msgid_table, msginfo->inreplyto);
241 /* try looking for the indirect parent */
242 if (!parent && msginfo->references) {
243 for (reflist = msginfo->references;
244 reflist != NULL; reflist = reflist->next)
245 if ((parent = g_hash_table_lookup
246 (msgid_table, reflist->data)) != NULL)
250 /* node should not be the parent, and node should not
251 be an ancestor of parent (circular reference) */
252 if (parent && parent != node &&
253 !g_node_is_ancestor(node, parent)) {
256 (parent, parent->children, node);
262 if (prefs_common.thread_by_subject) {
263 START_TIMING("thread by subject");
264 for (node = root->children; node && node != NULL;) {
266 msginfo = (MsgInfo *) node->data;
268 parent = subject_hashtable_lookup(subject_hashtable, msginfo);
270 /* the node may already be threaded by IN-REPLY-TO, so go up
272 find the parent node */
273 if (parent != NULL) {
274 if (g_node_is_ancestor(node, parent))
282 g_node_append(parent, node);
290 if (prefs_common.thread_by_subject)
292 g_hash_table_foreach(subject_hashtable, subject_hashtable_free, NULL);
293 g_hash_table_destroy(subject_hashtable);
296 g_hash_table_destroy(msgid_table);
301 gint procmsg_move_messages(GSList *mlist)
303 GSList *cur, *movelist = NULL;
305 FolderItem *dest = NULL;
307 gboolean finished = TRUE;
308 if (!mlist) return 0;
310 folder_item_update_freeze();
313 for (cur = mlist; cur != NULL; cur = cur->next) {
314 msginfo = (MsgInfo *)cur->data;
315 if (!msginfo->to_folder) {
321 dest = msginfo->to_folder;
322 movelist = g_slist_prepend(movelist, msginfo);
323 } else if (dest == msginfo->to_folder) {
324 movelist = g_slist_prepend(movelist, msginfo);
328 procmsg_msginfo_set_to_folder(msginfo, NULL);
331 movelist = g_slist_reverse(movelist);
332 retval |= folder_item_move_msgs(dest, movelist);
333 g_slist_free(movelist);
336 if (finished == FALSE) {
342 folder_item_update_thaw();
346 void procmsg_copy_messages(GSList *mlist)
348 GSList *cur, *copylist = NULL;
350 FolderItem *dest = NULL;
351 gboolean finished = TRUE;
354 folder_item_update_freeze();
357 for (cur = mlist; cur != NULL; cur = cur->next) {
358 msginfo = (MsgInfo *)cur->data;
359 if (!msginfo->to_folder) {
365 dest = msginfo->to_folder;
366 copylist = g_slist_prepend(copylist, msginfo);
367 } else if (dest == msginfo->to_folder) {
368 copylist = g_slist_prepend(copylist, msginfo);
372 procmsg_msginfo_set_to_folder(msginfo, NULL);
375 copylist = g_slist_reverse(copylist);
376 folder_item_copy_msgs(dest, copylist);
377 g_slist_free(copylist);
380 if (finished == FALSE) {
386 folder_item_update_thaw();
389 gchar *procmsg_get_message_file_path(MsgInfo *msginfo)
393 cm_return_val_if_fail(msginfo != NULL, NULL);
395 if (msginfo->plaintext_file)
396 file = g_strdup(msginfo->plaintext_file);
398 file = folder_item_fetch_msg(msginfo->folder, msginfo->msgnum);
404 gchar *procmsg_get_message_file(MsgInfo *msginfo)
406 gchar *filename = NULL;
408 cm_return_val_if_fail(msginfo != NULL, NULL);
410 filename = folder_item_fetch_msg(msginfo->folder, msginfo->msgnum);
412 debug_print("can't fetch message %d\n", msginfo->msgnum);
417 gchar *procmsg_get_message_file_full(MsgInfo *msginfo, gboolean headers, gboolean body)
419 gchar *filename = NULL;
421 cm_return_val_if_fail(msginfo != NULL, NULL);
423 filename = folder_item_fetch_msg_full(msginfo->folder, msginfo->msgnum,
426 debug_print("can't fetch message %d\n", msginfo->msgnum);
431 GSList *procmsg_get_message_file_list(GSList *mlist)
433 GSList *file_list = NULL;
435 MsgFileInfo *fileinfo;
438 while (mlist != NULL) {
439 msginfo = (MsgInfo *)mlist->data;
440 file = procmsg_get_message_file(msginfo);
442 procmsg_message_file_list_free(file_list);
445 fileinfo = g_new(MsgFileInfo, 1);
446 fileinfo->msginfo = procmsg_msginfo_new_ref(msginfo);
447 fileinfo->file = file;
448 fileinfo->flags = g_new(MsgFlags, 1);
449 *fileinfo->flags = msginfo->flags;
450 file_list = g_slist_prepend(file_list, fileinfo);
454 file_list = g_slist_reverse(file_list);
459 void procmsg_message_file_list_free(MsgInfoList *file_list)
462 MsgFileInfo *fileinfo;
464 for (cur = file_list; cur != NULL; cur = cur->next) {
465 fileinfo = (MsgFileInfo *)cur->data;
466 procmsg_msginfo_free(fileinfo->msginfo);
467 g_free(fileinfo->file);
468 g_free(fileinfo->flags);
472 g_slist_free(file_list);
475 FILE *procmsg_open_message(MsgInfo *msginfo)
480 cm_return_val_if_fail(msginfo != NULL, NULL);
482 file = procmsg_get_message_file_path(msginfo);
483 cm_return_val_if_fail(file != NULL, NULL);
485 if (!is_file_exist(file)) {
487 file = procmsg_get_message_file(msginfo);
492 if ((fp = g_fopen(file, "rb")) == NULL) {
493 FILE_OP_ERROR(file, "fopen");
500 if (MSG_IS_QUEUED(msginfo->flags) || MSG_IS_DRAFT(msginfo->flags)) {
503 while (fgets(buf, sizeof(buf), fp) != NULL) {
505 if ((!strncmp(buf, "X-Claws-End-Special-Headers: 1",
506 strlen("X-Claws-End-Special-Headers:"))) ||
507 (!strncmp(buf, "X-Sylpheed-End-Special-Headers: 1",
508 strlen("X-Sylpheed-End-Special-Headers:"))))
511 if (buf[0] == '\r' || buf[0] == '\n') break;
512 /* from other mailers */
513 if (!strncmp(buf, "Date: ", 6)
514 || !strncmp(buf, "To: ", 4)
515 || !strncmp(buf, "From: ", 6)
516 || !strncmp(buf, "Subject: ", 9)) {
526 gboolean procmsg_msg_exist(MsgInfo *msginfo)
531 if (!msginfo) return FALSE;
533 path = folder_item_get_path(msginfo->folder);
535 ret = !folder_item_is_msg_changed(msginfo->folder, msginfo);
541 void procmsg_get_filter_keyword(MsgInfo *msginfo, gchar **header, gchar **key,
542 PrefsFilterType type)
544 static HeaderEntry hentry[] = {{"X-BeenThere:", NULL, TRUE},
545 {"X-ML-Name:", NULL, TRUE},
546 {"X-List:", NULL, TRUE},
547 {"X-Mailing-list:", NULL, TRUE},
548 {"List-Id:", NULL, TRUE},
549 {"X-Sequence:", NULL, TRUE},
550 {"Sender:", NULL, TRUE},
551 {"List-Post:", NULL, TRUE},
552 {NULL, NULL, FALSE}};
558 H_X_MAILING_LIST = 3,
567 cm_return_if_fail(msginfo != NULL);
568 cm_return_if_fail(header != NULL);
569 cm_return_if_fail(key != NULL);
578 if ((fp = procmsg_open_message(msginfo)) == NULL)
580 procheader_get_header_fields(fp, hentry);
583 #define SET_FILTER_KEY(hstr, idx) \
585 *header = g_strdup(hstr); \
586 *key = hentry[idx].body; \
587 hentry[idx].body = NULL; \
590 if (hentry[H_LIST_ID].body != NULL) {
591 SET_FILTER_KEY("header \"List-Id\"", H_LIST_ID);
592 extract_list_id_str(*key);
593 } else if (hentry[H_X_BEENTHERE].body != NULL) {
594 SET_FILTER_KEY("header \"X-BeenThere\"", H_X_BEENTHERE);
595 } else if (hentry[H_X_ML_NAME].body != NULL) {
596 SET_FILTER_KEY("header \"X-ML-Name\"", H_X_ML_NAME);
597 } else if (hentry[H_X_LIST].body != NULL) {
598 SET_FILTER_KEY("header \"X-List\"", H_X_LIST);
599 } else if (hentry[H_X_MAILING_LIST].body != NULL) {
600 SET_FILTER_KEY("header \"X-Mailing-List\"", H_X_MAILING_LIST);
601 } else if (hentry[H_X_SEQUENCE].body != NULL) {
604 SET_FILTER_KEY("X-Sequence", H_X_SEQUENCE);
607 while (*p != '\0' && !g_ascii_isspace(*p)) p++;
608 while (g_ascii_isspace(*p)) p++;
609 if (g_ascii_isdigit(*p)) {
615 } else if (hentry[H_SENDER].body != NULL) {
616 SET_FILTER_KEY("header \"Sender\"", H_SENDER);
617 } else if (hentry[H_LIST_POST].body != NULL) {
618 SET_FILTER_KEY("header \"List-Post\"", H_LIST_POST);
619 } else if (msginfo->to) {
620 *header = g_strdup("to");
621 *key = g_strdup(msginfo->to);
622 } else if (msginfo->subject) {
623 *header = g_strdup("subject");
624 *key = g_strdup(msginfo->subject);
627 #undef SET_FILTER_KEY
629 g_free(hentry[H_X_BEENTHERE].body);
630 hentry[H_X_BEENTHERE].body = NULL;
631 g_free(hentry[H_X_ML_NAME].body);
632 hentry[H_X_ML_NAME].body = NULL;
633 g_free(hentry[H_X_LIST].body);
634 hentry[H_X_LIST].body = NULL;
635 g_free(hentry[H_X_MAILING_LIST].body);
636 hentry[H_X_MAILING_LIST].body = NULL;
637 g_free(hentry[H_LIST_ID].body);
638 hentry[H_LIST_ID].body = NULL;
639 g_free(hentry[H_SENDER].body);
640 hentry[H_SENDER].body = NULL;
641 g_free(hentry[H_LIST_POST].body);
642 hentry[H_LIST_POST].body = NULL;
646 *header = g_strdup("from");
647 *key = g_strdup(msginfo->from);
650 *header = g_strdup("to");
651 *key = g_strdup(msginfo->to);
653 case FILTER_BY_SUBJECT:
654 *header = g_strdup("subject");
655 *key = g_strdup(msginfo->subject);
662 static void procmsg_empty_trash(FolderItem *trash)
667 (trash->stype != F_TRASH &&
668 !folder_has_parent_of_type(trash, F_TRASH)))
671 if (trash && trash->total_msgs > 0) {
672 GSList *mlist = folder_item_get_msg_list(trash);
674 for (cur = mlist ; cur != NULL ; cur = cur->next) {
675 MsgInfo * msginfo = (MsgInfo *) cur->data;
676 if (MSG_IS_LOCKED(msginfo->flags)) {
677 procmsg_msginfo_free(msginfo);
680 if (msginfo->total_size != 0 &&
681 msginfo->size != (off_t)msginfo->total_size)
682 partial_mark_for_delete(msginfo);
684 procmsg_msginfo_free(msginfo);
687 folder_item_remove_all_msg(trash);
690 if (!trash->node || !trash->node->children)
693 node = trash->node->children;
694 while (node != NULL) {
696 procmsg_empty_trash(FOLDER_ITEM(node->data));
701 void procmsg_empty_all_trash(void)
706 for (cur = folder_get_list(); cur != NULL; cur = cur->next) {
707 Folder *folder = FOLDER(cur->data);
708 trash = folder->trash;
709 procmsg_empty_trash(trash);
710 if (folder->account && folder->account->set_trash_folder &&
711 folder_find_item_from_identifier(folder->account->trash_folder))
713 folder_find_item_from_identifier(folder->account->trash_folder));
717 static PrefsAccount *procmsg_get_account_from_file(const gchar *file)
719 PrefsAccount *mailac = NULL;
723 static HeaderEntry qentry[] = {{"S:", NULL, FALSE},
724 {"SSV:", NULL, FALSE},
726 {"NG:", NULL, FALSE},
727 {"MAID:", NULL, FALSE},
728 {"NAID:", NULL, FALSE},
729 {"SCF:", NULL, FALSE},
730 {"RMID:", NULL, FALSE},
731 {"FMID:", NULL, FALSE},
732 {"X-Claws-Privacy-System:", NULL, FALSE},
733 {"X-Claws-Encrypt:", NULL, FALSE},
734 {"X-Claws-Encrypt-Data:", NULL, FALSE},
735 {"X-Claws-End-Special-Headers", NULL, FALSE},
736 {"X-Sylpheed-Privacy-System:", NULL, FALSE},
737 {"X-Sylpheed-Encrypt:", NULL, FALSE},
738 {"X-Sylpheed-Encrypt-Data:", NULL, FALSE},
739 {NULL, NULL, FALSE}};
741 cm_return_val_if_fail(file != NULL, NULL);
743 if ((fp = g_fopen(file, "rb")) == NULL) {
744 FILE_OP_ERROR(file, "fopen");
748 while ((hnum = procheader_get_one_field(buf, sizeof(buf), fp, qentry))
750 gchar *p = buf + strlen(qentry[hnum].name);
752 if (hnum == Q_MAIL_ACCOUNT_ID) {
753 mailac = account_find_from_id(atoi(p));
761 gchar *procmsg_msginfo_get_identifier(MsgInfo *msginfo)
767 cm_return_val_if_fail(msginfo != NULL, NULL);
768 folder_id = folder_item_get_identifier(msginfo->folder);
769 msgid = msginfo->msgid;
771 id = g_strconcat(folder_id, G_DIR_SEPARATOR_S, msgid, NULL);
778 MsgInfo *procmsg_get_msginfo_from_identifier(const gchar *id)
780 gchar *folder_id = g_strdup(id);
781 gchar *separator = strrchr(folder_id, G_DIR_SEPARATOR);
786 if (separator == NULL) {
792 msgid = separator + 1;
794 item = folder_find_item_from_identifier(folder_id);
801 msginfo = folder_item_get_msginfo_by_msgid(item, msgid);
807 static GSList *procmsg_list_sort_by_account(FolderItem *queue, GSList *list)
809 GSList *result = NULL;
811 PrefsAccount *last_account = NULL;
814 gboolean nothing_to_sort = TRUE;
819 orig = g_slist_copy(list);
821 msg = (MsgInfo *)orig->data;
823 for (cur = orig; cur; cur = cur->next)
824 debug_print("sort before %s\n", ((MsgInfo *)cur->data)->from);
829 nothing_to_sort = TRUE;
833 PrefsAccount *ac = NULL;
834 msg = (MsgInfo *)cur->data;
835 file = folder_item_fetch_msg(queue, msg->msgnum);
836 ac = procmsg_get_account_from_file(file);
839 if (last_account == NULL || (ac != NULL && ac == last_account)) {
840 result = g_slist_append(result, msg);
841 orig = g_slist_remove(orig, msg);
843 nothing_to_sort = FALSE;
849 if (orig || g_slist_length(orig)) {
850 if (!last_account && nothing_to_sort) {
851 /* can't find an account for the rest of the list */
854 result = g_slist_append(result, cur->data);
865 for (cur = result; cur; cur = cur->next)
866 debug_print("sort after %s\n", ((MsgInfo *)cur->data)->from);
873 static gboolean procmsg_is_last_for_account(FolderItem *queue, MsgInfo *msginfo, GSList *elem)
875 gchar *file = folder_item_fetch_msg(queue, msginfo->msgnum);
876 PrefsAccount *ac = procmsg_get_account_from_file(file);
879 for (cur = elem; cur; cur = cur->next) {
880 MsgInfo *cur_msginfo = (MsgInfo *)cur->data;
881 file = folder_item_fetch_msg(queue, cur_msginfo->msgnum);
883 if (cur_msginfo != msginfo && !MSG_IS_LOCKED(cur_msginfo->flags)) {
884 if (procmsg_get_account_from_file(file) == ac) {
895 static gboolean send_queue_lock = FALSE;
897 gboolean procmsg_queue_lock(char **errstr)
899 if (send_queue_lock) {
900 /* Avoid having to translate two similar strings */
901 log_warning(LOG_PROTOCOL, "%s\n", _("Already trying to send."));
903 if (*errstr) g_free(*errstr);
904 *errstr = g_strdup_printf(_("Already trying to send."));
908 send_queue_lock = TRUE;
911 void procmsg_queue_unlock(void)
913 send_queue_lock = FALSE;
916 *\brief Send messages in queue
918 *\param queue Queue folder to process
919 *\param save_msgs Unused
921 *\return Number of messages sent, negative if an error occurred
922 * positive if no error occurred
924 gint procmsg_send_queue(FolderItem *queue, gboolean save_msgs, gchar **errstr)
926 gint sent = 0, err = 0;
928 GSList *sorted_list = NULL;
931 if (!procmsg_queue_lock(errstr)) {
932 toolbar_main_set_sensitive(mainwindow_get_mainwindow());
937 queue = folder_get_default_queue();
940 procmsg_queue_unlock();
945 toolbar_main_set_sensitive(mainwindow_get_mainwindow());
947 folder_item_scan(queue);
948 list = folder_item_get_msg_list(queue);
950 /* sort the list per sender account; this helps reusing the same SMTP server */
951 sorted_list = procmsg_list_sort_by_account(queue, list);
953 for (elem = sorted_list; elem != NULL; elem = elem->next) {
957 msginfo = (MsgInfo *)(elem->data);
958 if (!MSG_IS_LOCKED(msginfo->flags) && !MSG_IS_DELETED(msginfo->flags)) {
959 file = folder_item_fetch_msg(queue, msginfo->msgnum);
961 gboolean queued_removed = FALSE;
962 if (procmsg_send_message_queue_full(file,
963 !procmsg_is_last_for_account(queue, msginfo, elem),
964 errstr, queue, msginfo->msgnum, &queued_removed) < 0) {
965 g_warning("Sending queued message %d failed.\n",
971 folder_item_remove_msg(queue, msginfo->msgnum);
976 /* FIXME: supposedly if only one message is locked, and queue
977 * is being flushed, the following free says something like
978 * "freeing msg ## in folder (nil)". */
979 procmsg_msginfo_free(msginfo);
982 g_slist_free(sorted_list);
983 folder_item_scan(queue);
985 if (queue->node && queue->node->children) {
986 node = queue->node->children;
987 while (node != NULL) {
990 send_queue_lock = FALSE;
991 res = procmsg_send_queue(FOLDER_ITEM(node->data), save_msgs, errstr);
992 send_queue_lock = TRUE;
1000 procmsg_queue_unlock();
1002 toolbar_main_set_sensitive(mainwindow_get_mainwindow());
1004 return (err != 0 ? -err : sent);
1007 gboolean procmsg_is_sending(void)
1009 return send_queue_lock;
1013 *\brief Determine if a queue folder is empty
1015 *\param queue Queue folder to process
1017 *\return TRUE if the queue folder is empty, otherwise return FALSE
1019 gboolean procmsg_queue_is_empty(FolderItem *queue)
1022 gboolean res = FALSE;
1024 queue = folder_get_default_queue();
1025 cm_return_val_if_fail(queue != NULL, TRUE);
1027 folder_item_scan(queue);
1028 list = folder_item_get_msg_list(queue);
1029 res = (list == NULL);
1030 procmsg_msg_list_free(list);
1034 if (queue->node && queue->node->children) {
1035 node = queue->node->children;
1036 while (node != NULL) {
1038 if (!procmsg_queue_is_empty(FOLDER_ITEM(node->data)))
1047 gint procmsg_remove_special_headers(const gchar *in, const gchar *out)
1050 gchar buf[BUFFSIZE];
1052 if ((fp = g_fopen(in, "rb")) == NULL) {
1053 FILE_OP_ERROR(in, "fopen");
1056 if ((outfp = g_fopen(out, "wb")) == NULL) {
1057 FILE_OP_ERROR(out, "fopen");
1061 while (fgets(buf, sizeof(buf), fp) != NULL) {
1063 if ((!strncmp(buf, "X-Claws-End-Special-Headers: 1",
1064 strlen("X-Claws-End-Special-Headers:"))) ||
1065 (!strncmp(buf, "X-Sylpheed-End-Special-Headers: 1",
1066 strlen("X-Sylpheed-End-Special-Headers:"))))
1069 if (buf[0] == '\r' || buf[0] == '\n') break;
1070 /* from other mailers */
1071 if (!strncmp(buf, "Date: ", 6)
1072 || !strncmp(buf, "To: ", 4)
1073 || !strncmp(buf, "From: ", 6)
1074 || !strncmp(buf, "Subject: ", 9)) {
1079 while (fgets(buf, sizeof(buf), fp) != NULL)
1086 static gint procmsg_save_to_outbox(FolderItem *outbox, const gchar *file,
1090 MsgInfo *msginfo, *tmp_msginfo;
1091 MsgFlags flag = {0, 0};
1093 debug_print("saving sent message...\n");
1096 outbox = folder_get_default_outbox();
1097 cm_return_val_if_fail(outbox != NULL, -1);
1099 /* remove queueing headers */
1101 gchar tmp[MAXPATHLEN + 1];
1103 g_snprintf(tmp, sizeof(tmp), "%s%ctmpmsg.out.%08x",
1104 get_rc_dir(), G_DIR_SEPARATOR, (guint) rand());
1106 if (procmsg_remove_special_headers(file, tmp) !=0)
1109 folder_item_scan(outbox);
1110 if ((num = folder_item_add_msg(outbox, tmp, &flag, TRUE)) < 0) {
1111 g_warning("can't save message\n");
1116 folder_item_scan(outbox);
1117 if ((num = folder_item_add_msg
1118 (outbox, file, &flag, FALSE)) < 0) {
1119 g_warning("can't save message\n");
1123 msginfo = folder_item_get_msginfo(outbox, num); /* refcnt++ */
1124 tmp_msginfo = procmsg_msginfo_get_full_info(msginfo); /* refcnt++ */
1125 if (msginfo != NULL) {
1126 procmsg_msginfo_unset_flags(msginfo, ~0, 0);
1127 procmsg_msginfo_free(msginfo); /* refcnt-- */
1128 /* tmp_msginfo == msginfo */
1129 if (tmp_msginfo && msginfo->extradata &&
1130 (msginfo->extradata->dispositionnotificationto ||
1131 msginfo->extradata->returnreceiptto)) {
1132 procmsg_msginfo_set_flags(msginfo, MSG_RETRCPT_SENT, 0);
1134 procmsg_msginfo_free(tmp_msginfo); /* refcnt-- */
1140 void procmsg_print_message(MsgInfo *msginfo, const gchar *cmdline)
1142 static const gchar *def_cmd = "lpr %s";
1143 static guint id = 0;
1149 cm_return_if_fail(msginfo);
1151 if (procmime_msginfo_is_encrypted(msginfo))
1152 tmpfp = procmime_get_first_encrypted_text_content(msginfo);
1154 tmpfp = procmime_get_first_text_content(msginfo);
1155 if (tmpfp == NULL) {
1156 g_warning("Can't get text part\n");
1160 prtmp = g_strdup_printf("%s%cprinttmp.%08x",
1161 get_mime_tmp_dir(), G_DIR_SEPARATOR, id++);
1163 if ((prfp = g_fopen(prtmp, "wb")) == NULL) {
1164 FILE_OP_ERROR(prtmp, "fopen");
1170 if (msginfo->date) r = fprintf(prfp, "Date: %s\n", msginfo->date);
1171 if (msginfo->from) r = fprintf(prfp, "From: %s\n", msginfo->from);
1172 if (msginfo->to) r = fprintf(prfp, "To: %s\n", msginfo->to);
1173 if (msginfo->cc) r = fprintf(prfp, "Cc: %s\n", msginfo->cc);
1174 if (msginfo->newsgroups)
1175 r = fprintf(prfp, "Newsgroups: %s\n", msginfo->newsgroups);
1176 if (msginfo->subject) r = fprintf(prfp, "Subject: %s\n", msginfo->subject);
1179 while (fgets(buf, sizeof(buf), tmpfp) != NULL)
1180 r = fputs(buf, prfp);
1185 if (cmdline && (p = strchr(cmdline, '%')) && *(p + 1) == 's' &&
1186 !strchr(p + 2, '%'))
1187 g_snprintf(buf, sizeof(buf) - 1, cmdline, prtmp);
1190 g_warning("Print command-line is invalid: '%s'\n",
1192 g_snprintf(buf, sizeof(buf) - 1, def_cmd, prtmp);
1198 if (buf[strlen(buf) - 1] != '&')
1199 strncat(buf, "&", sizeof(buf) - strlen(buf) - 1);
1200 if (system(buf) == -1)
1201 g_warning("system(%s) failed.", buf);
1204 MsgInfo *procmsg_msginfo_new_ref(MsgInfo *msginfo)
1211 MsgInfo *procmsg_msginfo_new(void)
1213 MsgInfo *newmsginfo;
1215 newmsginfo = g_new0(MsgInfo, 1);
1216 newmsginfo->refcnt = 1;
1221 MsgInfo *procmsg_msginfo_copy(MsgInfo *msginfo)
1223 MsgInfo *newmsginfo;
1226 if (msginfo == NULL) return NULL;
1228 newmsginfo = g_new0(MsgInfo, 1);
1230 newmsginfo->refcnt = 1;
1232 #define MEMBCOPY(mmb) newmsginfo->mmb = msginfo->mmb
1233 #define MEMBDUP(mmb) newmsginfo->mmb = msginfo->mmb ? \
1234 g_strdup(msginfo->mmb) : NULL
1249 MEMBDUP(newsgroups);
1256 MEMBCOPY(to_folder);
1258 if (msginfo->extradata) {
1259 newmsginfo->extradata = g_new0(MsgInfoExtraData, 1);
1260 MEMBDUP(extradata->face);
1261 MEMBDUP(extradata->xface);
1262 MEMBDUP(extradata->dispositionnotificationto);
1263 MEMBDUP(extradata->returnreceiptto);
1264 MEMBDUP(extradata->partial_recv);
1265 MEMBDUP(extradata->account_server);
1266 MEMBDUP(extradata->account_login);
1267 MEMBDUP(extradata->list_post);
1268 MEMBDUP(extradata->list_subscribe);
1269 MEMBDUP(extradata->list_unsubscribe);
1270 MEMBDUP(extradata->list_help);
1271 MEMBDUP(extradata->list_archive);
1272 MEMBDUP(extradata->list_owner);
1275 refs = msginfo->references;
1276 for (refs = msginfo->references; refs != NULL; refs = refs->next) {
1277 newmsginfo->references = g_slist_prepend
1278 (newmsginfo->references, g_strdup(refs->data));
1280 newmsginfo->references = g_slist_reverse(newmsginfo->references);
1283 MEMBDUP(plaintext_file);
1288 MsgInfo *procmsg_msginfo_get_full_info_from_file(MsgInfo *msginfo, const gchar *file)
1290 MsgInfo *full_msginfo;
1292 if (msginfo == NULL) return NULL;
1294 if (!file || !is_file_exist(file)) {
1295 g_warning("procmsg_msginfo_get_full_info_from_file(): can't get message file.\n");
1299 full_msginfo = procheader_parse_file(file, msginfo->flags, TRUE, FALSE);
1300 if (!full_msginfo) return NULL;
1302 msginfo->total_size = full_msginfo->total_size;
1303 msginfo->planned_download = full_msginfo->planned_download;
1305 if (full_msginfo->extradata) {
1306 if (!msginfo->extradata)
1307 msginfo->extradata = g_new0(MsgInfoExtraData, 1);
1308 if (!msginfo->extradata->list_post)
1309 msginfo->extradata->list_post = g_strdup(full_msginfo->extradata->list_post);
1310 if (!msginfo->extradata->list_subscribe)
1311 msginfo->extradata->list_subscribe = g_strdup(full_msginfo->extradata->list_subscribe);
1312 if (!msginfo->extradata->list_unsubscribe)
1313 msginfo->extradata->list_unsubscribe = g_strdup(full_msginfo->extradata->list_unsubscribe);
1314 if (!msginfo->extradata->list_help)
1315 msginfo->extradata->list_help = g_strdup(full_msginfo->extradata->list_help);
1316 if (!msginfo->extradata->list_archive)
1317 msginfo->extradata->list_archive= g_strdup(full_msginfo->extradata->list_archive);
1318 if (!msginfo->extradata->list_owner)
1319 msginfo->extradata->list_owner = g_strdup(full_msginfo->extradata->list_owner);
1320 if (!msginfo->extradata->xface)
1321 msginfo->extradata->xface = g_strdup(full_msginfo->extradata->xface);
1322 if (!msginfo->extradata->face)
1323 msginfo->extradata->face = g_strdup(full_msginfo->extradata->face);
1324 if (!msginfo->extradata->dispositionnotificationto)
1325 msginfo->extradata->dispositionnotificationto =
1326 g_strdup(full_msginfo->extradata->dispositionnotificationto);
1327 if (!msginfo->extradata->returnreceiptto)
1328 msginfo->extradata->returnreceiptto = g_strdup
1329 (full_msginfo->extradata->returnreceiptto);
1330 if (!msginfo->extradata->partial_recv && full_msginfo->extradata->partial_recv)
1331 msginfo->extradata->partial_recv = g_strdup
1332 (full_msginfo->extradata->partial_recv);
1333 if (!msginfo->extradata->account_server && full_msginfo->extradata->account_server)
1334 msginfo->extradata->account_server = g_strdup
1335 (full_msginfo->extradata->account_server);
1336 if (!msginfo->extradata->account_login && full_msginfo->extradata->account_login)
1337 msginfo->extradata->account_login = g_strdup
1338 (full_msginfo->extradata->account_login);
1340 procmsg_msginfo_free(full_msginfo);
1342 return procmsg_msginfo_new_ref(msginfo);
1345 MsgInfo *procmsg_msginfo_get_full_info(MsgInfo *msginfo)
1347 MsgInfo *full_msginfo;
1350 if (msginfo == NULL) return NULL;
1352 file = procmsg_get_message_file_path(msginfo);
1353 if (!file || !is_file_exist(file)) {
1355 file = procmsg_get_message_file(msginfo);
1357 if (!file || !is_file_exist(file)) {
1358 g_warning("procmsg_msginfo_get_full_info(): can't get message file.\n");
1362 full_msginfo = procmsg_msginfo_get_full_info_from_file(msginfo, file);
1364 return full_msginfo;
1367 void procmsg_msginfo_free(MsgInfo *msginfo)
1369 if (msginfo == NULL) return;
1372 if (msginfo->refcnt > 0)
1375 if (msginfo->to_folder) {
1376 msginfo->to_folder->op_count--;
1377 folder_item_update(msginfo->to_folder, F_ITEM_UPDATE_MSGCNT);
1380 g_free(msginfo->fromspace);
1382 g_free(msginfo->fromname);
1384 g_free(msginfo->date);
1385 g_free(msginfo->from);
1386 g_free(msginfo->to);
1387 g_free(msginfo->cc);
1388 g_free(msginfo->newsgroups);
1389 g_free(msginfo->subject);
1390 g_free(msginfo->msgid);
1391 g_free(msginfo->inreplyto);
1392 g_free(msginfo->xref);
1394 if (msginfo->extradata) {
1395 g_free(msginfo->extradata->returnreceiptto);
1396 g_free(msginfo->extradata->dispositionnotificationto);
1397 g_free(msginfo->extradata->xface);
1398 g_free(msginfo->extradata->face);
1399 g_free(msginfo->extradata->list_post);
1400 g_free(msginfo->extradata->list_subscribe);
1401 g_free(msginfo->extradata->list_unsubscribe);
1402 g_free(msginfo->extradata->list_help);
1403 g_free(msginfo->extradata->list_archive);
1404 g_free(msginfo->extradata->list_owner);
1405 g_free(msginfo->extradata->partial_recv);
1406 g_free(msginfo->extradata->account_server);
1407 g_free(msginfo->extradata->account_login);
1408 g_free(msginfo->extradata);
1410 slist_free_strings(msginfo->references);
1411 g_slist_free(msginfo->references);
1412 g_slist_free(msginfo->tags);
1414 g_free(msginfo->plaintext_file);
1419 guint procmsg_msginfo_memusage(MsgInfo *msginfo)
1424 memusage += sizeof(MsgInfo);
1425 if (msginfo->fromname)
1426 memusage += strlen(msginfo->fromname);
1428 memusage += strlen(msginfo->date);
1430 memusage += strlen(msginfo->from);
1432 memusage += strlen(msginfo->to);
1434 memusage += strlen(msginfo->cc);
1435 if (msginfo->newsgroups)
1436 memusage += strlen(msginfo->newsgroups);
1437 if (msginfo->subject)
1438 memusage += strlen(msginfo->subject);
1440 memusage += strlen(msginfo->msgid);
1441 if (msginfo->inreplyto)
1442 memusage += strlen(msginfo->inreplyto);
1444 for (tmp = msginfo->references; tmp; tmp=tmp->next) {
1445 gchar *r = (gchar *)tmp->data;
1446 memusage += r?strlen(r):0 + sizeof(GSList);
1448 if (msginfo->fromspace)
1449 memusage += strlen(msginfo->fromspace);
1451 for (tmp = msginfo->tags; tmp; tmp=tmp->next) {
1452 memusage += sizeof(GSList);
1454 if (msginfo->extradata) {
1455 memusage += sizeof(MsgInfoExtraData);
1456 if (msginfo->extradata->xface)
1457 memusage += strlen(msginfo->extradata->xface);
1458 if (msginfo->extradata->face)
1459 memusage += strlen(msginfo->extradata->face);
1460 if (msginfo->extradata->dispositionnotificationto)
1461 memusage += strlen(msginfo->extradata->dispositionnotificationto);
1462 if (msginfo->extradata->returnreceiptto)
1463 memusage += strlen(msginfo->extradata->returnreceiptto);
1465 if (msginfo->extradata->partial_recv)
1466 memusage += strlen(msginfo->extradata->partial_recv);
1467 if (msginfo->extradata->account_server)
1468 memusage += strlen(msginfo->extradata->account_server);
1469 if (msginfo->extradata->account_login)
1470 memusage += strlen(msginfo->extradata->account_login);
1472 if (msginfo->extradata->list_post)
1473 memusage += strlen(msginfo->extradata->list_post);
1474 if (msginfo->extradata->list_subscribe)
1475 memusage += strlen(msginfo->extradata->list_subscribe);
1476 if (msginfo->extradata->list_unsubscribe)
1477 memusage += strlen(msginfo->extradata->list_unsubscribe);
1478 if (msginfo->extradata->list_help)
1479 memusage += strlen(msginfo->extradata->list_help);
1480 if (msginfo->extradata->list_archive)
1481 memusage += strlen(msginfo->extradata->list_archive);
1482 if (msginfo->extradata->list_owner)
1483 memusage += strlen(msginfo->extradata->list_owner);
1488 static gint procmsg_send_message_queue_full(const gchar *file, gboolean keep_session, gchar **errstr,
1489 FolderItem *queue, gint msgnum, gboolean *queued_removed)
1491 static HeaderEntry qentry[] = {{"S:", NULL, FALSE},
1492 {"SSV:", NULL, FALSE},
1493 {"R:", NULL, FALSE},
1494 {"NG:", NULL, FALSE},
1495 {"MAID:", NULL, FALSE},
1496 {"NAID:", NULL, FALSE},
1497 {"SCF:", NULL, FALSE},
1498 {"RMID:", NULL, FALSE},
1499 {"FMID:", NULL, FALSE},
1500 {"X-Claws-Privacy-System:", NULL, FALSE},
1501 {"X-Claws-Encrypt:", NULL, FALSE},
1502 {"X-Claws-Encrypt-Data:", NULL, FALSE},
1503 {"X-Claws-End-Special-Headers:", NULL, FALSE},
1504 {"X-Sylpheed-Privacy-System:", NULL, FALSE},
1505 {"X-Sylpheed-Encrypt:", NULL, FALSE},
1506 {"X-Sylpheed-Encrypt-Data:", NULL, FALSE},
1507 {"X-Sylpheed-End-Special-Headers:", NULL, FALSE},
1508 {NULL, NULL, FALSE}};
1511 gint mailval = 0, newsval = 0;
1513 gchar *smtpserver = NULL;
1514 GSList *to_list = NULL;
1515 GSList *newsgroup_list = NULL;
1516 gchar *savecopyfolder = NULL;
1517 gchar *replymessageid = NULL;
1518 gchar *fwdmessageid = NULL;
1519 gchar *privacy_system = NULL;
1520 gboolean encrypt = FALSE;
1521 gchar *encrypt_data = NULL;
1522 gchar buf[BUFFSIZE];
1524 PrefsAccount *mailac = NULL, *newsac = NULL;
1525 gboolean save_clear_text = TRUE;
1526 gchar *tmp_enc_file = NULL;
1530 cm_return_val_if_fail(file != NULL, -1);
1532 if ((fp = g_fopen(file, "rb")) == NULL) {
1533 FILE_OP_ERROR(file, "fopen");
1535 if (*errstr) g_free(*errstr);
1536 *errstr = g_strdup_printf(_("Couldn't open file %s."), file);
1541 while ((hnum = procheader_get_one_field(buf, sizeof(buf), fp, qentry))
1543 gchar *p = buf + strlen(qentry[hnum].name);
1551 if (smtpserver == NULL)
1552 smtpserver = g_strdup(p);
1555 to_list = address_list_append(to_list, p);
1558 newsgroup_list = newsgroup_list_append(newsgroup_list, p);
1560 case Q_MAIL_ACCOUNT_ID:
1561 mailac = account_find_from_id(atoi(p));
1563 case Q_NEWS_ACCOUNT_ID:
1564 newsac = account_find_from_id(atoi(p));
1566 case Q_SAVE_COPY_FOLDER:
1567 if (savecopyfolder == NULL)
1568 savecopyfolder = g_strdup(p);
1570 case Q_REPLY_MESSAGE_ID:
1571 if (replymessageid == NULL)
1572 replymessageid = g_strdup(p);
1574 case Q_FWD_MESSAGE_ID:
1575 if (fwdmessageid == NULL)
1576 fwdmessageid = g_strdup(p);
1578 case Q_PRIVACY_SYSTEM:
1579 case Q_PRIVACY_SYSTEM_OLD:
1580 if (privacy_system == NULL)
1581 privacy_system = g_strdup(p);
1588 case Q_ENCRYPT_DATA:
1589 case Q_ENCRYPT_DATA_OLD:
1590 if (encrypt_data == NULL)
1591 encrypt_data = g_strdup(p);
1594 case Q_CLAWS_HDRS_OLD:
1595 /* end of special headers reached */
1596 goto send_mail; /* can't "break;break;" */
1600 filepos = ftell(fp);
1605 if (mailac && mailac->save_encrypted_as_clear_text
1606 && !mailac->encrypt_to_self)
1607 save_clear_text = TRUE;
1609 save_clear_text = FALSE;
1614 mimeinfo = procmime_scan_queue_file(file);
1615 if (!privacy_encrypt(privacy_system, mimeinfo, encrypt_data)
1616 || (fp = my_tmpfile()) == NULL
1617 || procmime_write_mimeinfo(mimeinfo, fp) < 0) {
1620 procmime_mimeinfo_free_all(mimeinfo);
1623 slist_free_strings(to_list);
1624 g_slist_free(to_list);
1625 slist_free_strings(newsgroup_list);
1626 g_slist_free(newsgroup_list);
1627 g_free(savecopyfolder);
1628 g_free(replymessageid);
1629 g_free(fwdmessageid);
1630 g_free(privacy_system);
1631 g_free(encrypt_data);
1633 if (*errstr) g_free(*errstr);
1634 *errstr = g_strdup_printf(_("Couldn't encrypt the email: %s"),
1635 privacy_get_error());
1641 if (!save_clear_text) {
1642 gchar *content = NULL;
1643 FILE *tmpfp = get_tmpfile_in_dir(get_mime_tmp_dir(), &tmp_enc_file);
1647 content = file_read_stream_to_str(fp);
1650 str_write_to_file(content, tmp_enc_file);
1653 g_warning("couldn't get tempfile\n");
1657 procmime_mimeinfo_free_all(mimeinfo);
1663 debug_print("Sending message by mail\n");
1666 if (*errstr) g_free(*errstr);
1667 *errstr = g_strdup_printf(_("Queued message header is broken."));
1670 } else if (mailac && mailac->use_mail_command &&
1671 mailac->mail_command && (* mailac->mail_command)) {
1672 mailval = send_message_local(mailac->mail_command, fp);
1676 mailac = account_find_from_smtp_server(from, smtpserver);
1678 g_warning("Account not found. "
1679 "Using current account...\n");
1680 mailac = cur_account;
1685 mailval = send_message_smtp_full(mailac, to_list, fp, keep_session);
1686 if (mailval == -1 && errstr) {
1687 if (*errstr) g_free(*errstr);
1688 *errstr = g_strdup_printf(_("An error happened during SMTP session."));
1691 PrefsAccount tmp_ac;
1693 g_warning("Account not found.\n");
1695 memset(&tmp_ac, 0, sizeof(PrefsAccount));
1696 tmp_ac.address = from;
1697 tmp_ac.smtp_server = smtpserver;
1698 tmp_ac.smtpport = SMTP_PORT;
1699 mailval = send_message_smtp(&tmp_ac, to_list, fp);
1700 if (mailval == -1 && errstr) {
1701 if (*errstr) g_free(*errstr);
1702 *errstr = g_strdup_printf(_("No specific account has been found to "
1703 "send, and an error happened during SMTP session."));
1707 } else if (!to_list && !newsgroup_list) {
1709 if (*errstr) g_free(*errstr);
1710 *errstr = g_strdup(_("Couldn't determine sending informations. "
1711 "Maybe the email hasn't been generated by Claws Mail."));
1716 fseek(fp, filepos, SEEK_SET);
1717 if (newsgroup_list && newsac && (mailval == 0)) {
1722 /* write to temporary file */
1723 tmp = g_strdup_printf("%s%cnntp%p", get_tmp_dir(),
1724 G_DIR_SEPARATOR, file);
1725 if ((tmpfp = g_fopen(tmp, "wb")) == NULL) {
1726 FILE_OP_ERROR(tmp, "fopen");
1728 alertpanel_error(_("Couldn't create temporary file for news sending."));
1730 if (change_file_mode_rw(tmpfp, tmp) < 0) {
1731 FILE_OP_ERROR(tmp, "chmod");
1732 g_warning("can't change file mode\n");
1735 while ((newsval == 0) && fgets(buf, sizeof(buf), fp) != NULL) {
1736 if (fputs(buf, tmpfp) == EOF) {
1737 FILE_OP_ERROR(tmp, "fputs");
1740 if (*errstr) g_free(*errstr);
1741 *errstr = g_strdup_printf(_("Error when writing temporary file for news sending."));
1748 debug_print("Sending message by news\n");
1750 folder = FOLDER(newsac->folder);
1752 newsval = news_post(folder, tmp);
1753 if (newsval < 0 && errstr) {
1754 if (*errstr) g_free(*errstr);
1755 *errstr = g_strdup_printf(_("Error occurred while posting the message to %s."),
1756 newsac->nntp_server);
1766 /* update session statistics */
1767 if (mailval == 0 && newsval == 0) {
1768 /* update session stats */
1770 session_stats.replied++;
1771 else if (fwdmessageid)
1772 session_stats.forwarded++;
1774 session_stats.sent++;
1777 /* save message to outbox */
1778 if (mailval == 0 && newsval == 0 && savecopyfolder) {
1781 debug_print("saving sent message...\n");
1783 outbox = folder_find_item_from_identifier(savecopyfolder);
1785 outbox = folder_get_default_outbox();
1787 if (save_clear_text || tmp_enc_file == NULL) {
1788 gboolean saved = FALSE;
1789 *queued_removed = FALSE;
1790 if (queue && msgnum > 0) {
1791 MsgInfo *queued_mail = folder_item_get_msginfo(queue, msgnum);
1792 if (folder_item_move_msg(outbox, queued_mail) >= 0) {
1793 debug_print("moved queued mail %d to sent folder\n", msgnum);
1795 *queued_removed = TRUE;
1796 } else if (folder_item_copy_msg(outbox, queued_mail) >= 0) {
1797 debug_print("copied queued mail %d to sent folder\n", msgnum);
1800 procmsg_msginfo_free(queued_mail);
1803 debug_print("resaving clear text queued mail to sent folder\n");
1804 procmsg_save_to_outbox(outbox, file, TRUE);
1807 debug_print("saving encrpyted queued mail to sent folder\n");
1808 procmsg_save_to_outbox(outbox, tmp_enc_file, FALSE);
1812 if (tmp_enc_file != NULL) {
1813 claws_unlink(tmp_enc_file);
1815 tmp_enc_file = NULL;
1818 if (replymessageid != NULL || fwdmessageid != NULL) {
1822 if (replymessageid != NULL)
1823 tokens = g_strsplit(replymessageid, "\t", 0);
1825 tokens = g_strsplit(fwdmessageid, "\t", 0);
1826 item = folder_find_item_from_identifier(tokens[0]);
1828 /* check if queued message has valid folder and message id */
1829 if (item != NULL && tokens[2] != NULL) {
1832 msginfo = folder_item_get_msginfo(item, atoi(tokens[1]));
1834 /* check if referring message exists and has a message id */
1835 if ((msginfo != NULL) &&
1836 (msginfo->msgid != NULL) &&
1837 (strcmp(msginfo->msgid, tokens[2]) != 0)) {
1838 procmsg_msginfo_free(msginfo);
1842 if (msginfo == NULL) {
1843 msginfo = folder_item_get_msginfo_by_msgid(item, tokens[2]);
1846 if (msginfo != NULL) {
1847 if (replymessageid != NULL) {
1848 MsgPermFlags to_unset = 0;
1850 if (prefs_common.mark_as_read_on_new_window)
1851 to_unset = (MSG_NEW|MSG_UNREAD);
1853 procmsg_msginfo_unset_flags(msginfo, to_unset, 0);
1854 procmsg_msginfo_set_flags(msginfo, MSG_REPLIED, 0);
1856 procmsg_msginfo_set_flags(msginfo, MSG_FORWARDED, 0);
1858 procmsg_msginfo_free(msginfo);
1866 slist_free_strings(to_list);
1867 g_slist_free(to_list);
1868 slist_free_strings(newsgroup_list);
1869 g_slist_free(newsgroup_list);
1870 g_free(savecopyfolder);
1871 g_free(replymessageid);
1872 g_free(fwdmessageid);
1873 g_free(privacy_system);
1874 g_free(encrypt_data);
1876 return (newsval != 0 ? newsval : mailval);
1879 gint procmsg_send_message_queue(const gchar *file, gchar **errstr, FolderItem *queue, gint msgnum, gboolean *queued_removed)
1881 gint result = procmsg_send_message_queue_full(file, FALSE, errstr, queue, msgnum, queued_removed);
1882 toolbar_main_set_sensitive(mainwindow_get_mainwindow());
1886 gint procmsg_send_message_queue_with_lock(const gchar *file, gchar **errstr, FolderItem *queue, gint msgnum, gboolean *queued_removed)
1889 if (procmsg_queue_lock(errstr)) {
1890 val = procmsg_send_message_queue(file, errstr, queue, msgnum, queued_removed);
1891 procmsg_queue_unlock();
1897 static void update_folder_msg_counts(FolderItem *item, MsgInfo *msginfo, MsgPermFlags old_flags)
1899 MsgPermFlags new_flags = msginfo->flags.perm_flags;
1902 if (!(old_flags & MSG_NEW) && (new_flags & MSG_NEW)) {
1906 if ((old_flags & MSG_NEW) && !(new_flags & MSG_NEW)) {
1911 if (!(old_flags & MSG_UNREAD) && (new_flags & MSG_UNREAD)) {
1912 item->unread_msgs++;
1913 if (procmsg_msg_has_marked_parent(msginfo))
1914 item->unreadmarked_msgs++;
1917 if ((old_flags & MSG_UNREAD) && !(new_flags & MSG_UNREAD)) {
1918 item->unread_msgs--;
1919 if (procmsg_msg_has_marked_parent(msginfo))
1920 item->unreadmarked_msgs--;
1924 if (!(old_flags & MSG_MARKED) && (new_flags & MSG_MARKED)) {
1925 procmsg_update_unread_children(msginfo, TRUE);
1926 item->marked_msgs++;
1929 if ((old_flags & MSG_MARKED) && !(new_flags & MSG_MARKED)) {
1930 procmsg_update_unread_children(msginfo, FALSE);
1931 item->marked_msgs--;
1934 if (!(old_flags & MSG_REPLIED) && (new_flags & MSG_REPLIED)) {
1935 item->replied_msgs++;
1938 if ((old_flags & MSG_REPLIED) && !(new_flags & MSG_REPLIED)) {
1939 item->replied_msgs--;
1942 if (!(old_flags & MSG_FORWARDED) && (new_flags & MSG_FORWARDED)) {
1943 item->forwarded_msgs++;
1946 if ((old_flags & MSG_FORWARDED) && !(new_flags & MSG_FORWARDED)) {
1947 item->forwarded_msgs--;
1950 if (!(old_flags & MSG_LOCKED) && (new_flags & MSG_LOCKED)) {
1951 item->locked_msgs++;
1954 if ((old_flags & MSG_LOCKED) && !(new_flags & MSG_LOCKED)) {
1955 item->locked_msgs--;
1958 if ((old_flags & MSG_IGNORE_THREAD) && !(new_flags & MSG_IGNORE_THREAD)) {
1959 item->ignored_msgs--;
1962 if (!(old_flags & MSG_IGNORE_THREAD) && (new_flags & MSG_IGNORE_THREAD)) {
1963 item->ignored_msgs++;
1966 if ((old_flags & MSG_WATCH_THREAD) && !(new_flags & MSG_WATCH_THREAD)) {
1967 item->watched_msgs--;
1970 if (!(old_flags & MSG_WATCH_THREAD) && (new_flags & MSG_WATCH_THREAD)) {
1971 item->watched_msgs++;
1975 void procmsg_msginfo_set_flags(MsgInfo *msginfo, MsgPermFlags perm_flags, MsgTmpFlags tmp_flags)
1978 MsgInfoUpdate msginfo_update;
1979 MsgPermFlags perm_flags_new, perm_flags_old;
1980 MsgTmpFlags tmp_flags_old;
1982 cm_return_if_fail(msginfo != NULL);
1983 item = msginfo->folder;
1984 cm_return_if_fail(item != NULL);
1986 debug_print("Setting flags for message %d in folder %s\n", msginfo->msgnum, item->path);
1988 /* Perm Flags handling */
1989 perm_flags_old = msginfo->flags.perm_flags;
1990 perm_flags_new = msginfo->flags.perm_flags | perm_flags;
1991 if ((perm_flags & MSG_IGNORE_THREAD) || (perm_flags_old & MSG_IGNORE_THREAD)) {
1992 perm_flags_new &= ~(MSG_NEW | MSG_UNREAD);
1994 if ((perm_flags & MSG_WATCH_THREAD) || (perm_flags_old & MSG_WATCH_THREAD)) {
1995 perm_flags_new &= ~(MSG_IGNORE_THREAD);
1998 if (perm_flags_old != perm_flags_new) {
1999 folder_item_change_msg_flags(msginfo->folder, msginfo, perm_flags_new);
2001 update_folder_msg_counts(item, msginfo, perm_flags_old);
2002 summary_update_unread(mainwindow_get_mainwindow()->summaryview, NULL);
2005 /* Tmp flags handling */
2006 tmp_flags_old = msginfo->flags.tmp_flags;
2007 msginfo->flags.tmp_flags |= tmp_flags;
2009 /* update notification */
2010 if ((perm_flags_old != perm_flags_new) || (tmp_flags_old != msginfo->flags.tmp_flags)) {
2011 msginfo_update.msginfo = msginfo;
2012 msginfo_update.flags = MSGINFO_UPDATE_FLAGS;
2013 hooks_invoke(MSGINFO_UPDATE_HOOKLIST, &msginfo_update);
2014 folder_item_update(msginfo->folder, F_ITEM_UPDATE_MSGCNT);
2018 void procmsg_msginfo_unset_flags(MsgInfo *msginfo, MsgPermFlags perm_flags, MsgTmpFlags tmp_flags)
2021 MsgInfoUpdate msginfo_update;
2022 MsgPermFlags perm_flags_new, perm_flags_old;
2023 MsgTmpFlags tmp_flags_old;
2025 cm_return_if_fail(msginfo != NULL);
2026 item = msginfo->folder;
2027 cm_return_if_fail(item != NULL);
2029 debug_print("Unsetting flags for message %d in folder %s\n", msginfo->msgnum, item->path);
2031 /* Perm Flags handling */
2032 perm_flags_old = msginfo->flags.perm_flags;
2033 perm_flags_new = msginfo->flags.perm_flags & ~perm_flags;
2035 if (perm_flags_old != perm_flags_new) {
2036 folder_item_change_msg_flags(msginfo->folder, msginfo, perm_flags_new);
2038 update_folder_msg_counts(item, msginfo, perm_flags_old);
2041 /* Tmp flags hanlding */
2042 tmp_flags_old = msginfo->flags.tmp_flags;
2043 msginfo->flags.tmp_flags &= ~tmp_flags;
2045 /* update notification */
2046 if ((perm_flags_old != perm_flags_new) || (tmp_flags_old != msginfo->flags.tmp_flags)) {
2047 msginfo_update.msginfo = msginfo;
2048 msginfo_update.flags = MSGINFO_UPDATE_FLAGS;
2049 hooks_invoke(MSGINFO_UPDATE_HOOKLIST, &msginfo_update);
2050 folder_item_update(msginfo->folder, F_ITEM_UPDATE_MSGCNT);
2054 void procmsg_msginfo_change_flags(MsgInfo *msginfo,
2055 MsgPermFlags add_perm_flags, MsgTmpFlags add_tmp_flags,
2056 MsgPermFlags rem_perm_flags, MsgTmpFlags rem_tmp_flags)
2059 MsgInfoUpdate msginfo_update;
2060 MsgPermFlags perm_flags_new, perm_flags_old;
2061 MsgTmpFlags tmp_flags_old;
2063 cm_return_if_fail(msginfo != NULL);
2064 item = msginfo->folder;
2065 cm_return_if_fail(item != NULL);
2067 debug_print("Changing flags for message %d in folder %s\n", msginfo->msgnum, item->path);
2069 /* Perm Flags handling */
2070 perm_flags_old = msginfo->flags.perm_flags;
2071 perm_flags_new = (msginfo->flags.perm_flags & ~rem_perm_flags) | add_perm_flags;
2072 if ((add_perm_flags & MSG_IGNORE_THREAD) || (perm_flags_old & MSG_IGNORE_THREAD)) {
2073 perm_flags_new &= ~(MSG_NEW | MSG_UNREAD);
2075 if ((add_perm_flags & MSG_WATCH_THREAD) || (perm_flags_old & MSG_WATCH_THREAD)) {
2076 perm_flags_new &= ~(MSG_IGNORE_THREAD);
2079 if (perm_flags_old != perm_flags_new) {
2080 folder_item_change_msg_flags(msginfo->folder, msginfo, perm_flags_new);
2082 update_folder_msg_counts(item, msginfo, perm_flags_old);
2086 /* Tmp flags handling */
2087 tmp_flags_old = msginfo->flags.tmp_flags;
2088 msginfo->flags.tmp_flags &= ~rem_tmp_flags;
2089 msginfo->flags.tmp_flags |= add_tmp_flags;
2091 /* update notification */
2092 if ((perm_flags_old != perm_flags_new) || (tmp_flags_old != msginfo->flags.tmp_flags)) {
2093 msginfo_update.msginfo = msginfo;
2094 msginfo_update.flags = MSGINFO_UPDATE_FLAGS;
2095 hooks_invoke(MSGINFO_UPDATE_HOOKLIST, &msginfo_update);
2096 folder_item_update(msginfo->folder, F_ITEM_UPDATE_MSGCNT);
2101 *\brief check for flags (e.g. mark) in prior msgs of current thread
2103 *\param info Current message
2104 *\param perm_flags Flags to be checked
2105 *\param parentmsgs Hash of prior msgs to avoid loops
2107 *\return gboolean TRUE if perm_flags are found
2109 static gboolean procmsg_msg_has_flagged_parent_real(MsgInfo *info,
2110 MsgPermFlags perm_flags, GHashTable *parentmsgs)
2114 cm_return_val_if_fail(info != NULL, FALSE);
2116 if (info != NULL && info->folder != NULL && info->inreplyto != NULL) {
2117 tmp = folder_item_get_msginfo_by_msgid(info->folder,
2119 if (tmp && (tmp->flags.perm_flags & perm_flags)) {
2120 procmsg_msginfo_free(tmp);
2122 } else if (tmp != NULL) {
2125 if (g_hash_table_lookup(parentmsgs, info)) {
2126 debug_print("loop detected: %d\n",
2130 g_hash_table_insert(parentmsgs, info, "1");
2131 result = procmsg_msg_has_flagged_parent_real(
2132 tmp, perm_flags, parentmsgs);
2134 procmsg_msginfo_free(tmp);
2144 *\brief Callback for cleaning up hash of parentmsgs
2146 static gboolean parentmsgs_hash_remove(gpointer key,
2154 *\brief Set up list of parentmsgs
2155 * See procmsg_msg_has_flagged_parent_real()
2157 gboolean procmsg_msg_has_flagged_parent(MsgInfo *info, MsgPermFlags perm_flags)
2160 static GHashTable *parentmsgs = NULL;
2162 if (parentmsgs == NULL)
2163 parentmsgs = g_hash_table_new(NULL, NULL);
2165 result = procmsg_msg_has_flagged_parent_real(info, perm_flags, parentmsgs);
2166 g_hash_table_foreach_remove(parentmsgs, parentmsgs_hash_remove, NULL);
2172 *\brief Check if msgs prior in thread are marked
2173 * See procmsg_msg_has_flagged_parent_real()
2175 gboolean procmsg_msg_has_marked_parent(MsgInfo *info)
2177 return procmsg_msg_has_flagged_parent(info, MSG_MARKED);
2181 static GSList *procmsg_find_children_func(MsgInfo *info,
2182 GSList *children, GSList *all)
2186 cm_return_val_if_fail(info!=NULL, children);
2187 if (info->msgid == NULL)
2190 for (cur = all; cur != NULL; cur = g_slist_next(cur)) {
2191 MsgInfo *tmp = (MsgInfo *)cur->data;
2192 if (tmp->inreplyto && !strcmp(tmp->inreplyto, info->msgid)) {
2193 /* Check if message is already in the list */
2194 if ((children == NULL) ||
2195 (g_slist_index(children, tmp) == -1)) {
2196 children = g_slist_prepend(children,
2197 procmsg_msginfo_new_ref(tmp));
2198 children = procmsg_find_children_func(tmp,
2207 static GSList *procmsg_find_children (MsgInfo *info)
2212 cm_return_val_if_fail(info!=NULL, NULL);
2213 all = folder_item_get_msg_list(info->folder);
2214 children = procmsg_find_children_func(info, NULL, all);
2215 if (children != NULL) {
2216 for (cur = all; cur != NULL; cur = g_slist_next(cur)) {
2217 /* this will not free the used pointers
2218 created with procmsg_msginfo_new_ref */
2219 procmsg_msginfo_free((MsgInfo *)cur->data);
2227 static void procmsg_update_unread_children(MsgInfo *info, gboolean newly_marked)
2229 GSList *children = procmsg_find_children(info);
2231 for (cur = children; cur != NULL; cur = g_slist_next(cur)) {
2232 MsgInfo *tmp = (MsgInfo *)cur->data;
2233 if(MSG_IS_UNREAD(tmp->flags) && !MSG_IS_IGNORE_THREAD(tmp->flags)) {
2235 info->folder->unreadmarked_msgs++;
2237 info->folder->unreadmarked_msgs--;
2238 folder_item_update(info->folder, F_ITEM_UPDATE_MSGCNT);
2240 procmsg_msginfo_free(tmp);
2242 g_slist_free(children);
2246 * Set the destination folder for a copy or move operation
2248 * \param msginfo The message which's destination folder is changed
2249 * \param to_folder The destination folder for the operation
2251 void procmsg_msginfo_set_to_folder(MsgInfo *msginfo, FolderItem *to_folder)
2253 if(msginfo->to_folder != NULL) {
2254 msginfo->to_folder->op_count--;
2255 folder_item_update(msginfo->to_folder, F_ITEM_UPDATE_MSGCNT);
2257 msginfo->to_folder = to_folder;
2258 if(to_folder != NULL) {
2259 to_folder->op_count++;
2260 folder_item_update(msginfo->to_folder, F_ITEM_UPDATE_MSGCNT);
2265 * Apply filtering actions to the msginfo
2267 * \param msginfo The MsgInfo describing the message that should be filtered
2268 * \return TRUE if the message was moved and MsgInfo is now invalid,
2271 static gboolean procmsg_msginfo_filter(MsgInfo *msginfo, PrefsAccount* ac_prefs)
2273 MailFilteringData mail_filtering_data;
2275 mail_filtering_data.msginfo = msginfo;
2276 mail_filtering_data.msglist = NULL;
2277 mail_filtering_data.filtered = NULL;
2278 mail_filtering_data.unfiltered = NULL;
2279 mail_filtering_data.account = ac_prefs;
2281 if (!ac_prefs || ac_prefs->filterhook_on_recv)
2282 if (hooks_invoke(MAIL_FILTERING_HOOKLIST, &mail_filtering_data))
2285 /* filter if enabled in prefs or move to inbox if not */
2286 if((filtering_rules != NULL) &&
2287 filter_message_by_msginfo(filtering_rules, msginfo, ac_prefs,
2288 FILTERING_INCORPORATION, NULL)) {
2295 void procmsg_msglist_filter(GSList *list, PrefsAccount *ac,
2296 GSList **filtered, GSList **unfiltered,
2299 GSList *cur, *to_do = NULL;
2300 gint total = 0, curnum = 0;
2301 MailFilteringData mail_filtering_data;
2303 cm_return_if_fail(filtered != NULL);
2304 cm_return_if_fail(unfiltered != NULL);
2312 total = g_slist_length(list);
2316 *unfiltered = g_slist_copy(list);
2320 statusbar_print_all(_("Filtering messages...\n"));
2322 mail_filtering_data.msginfo = NULL;
2323 mail_filtering_data.msglist = list;
2324 mail_filtering_data.filtered = NULL;
2325 mail_filtering_data.unfiltered = NULL;
2326 mail_filtering_data.account = ac;
2328 if (!ac || ac->filterhook_on_recv)
2329 hooks_invoke(MAIL_LISTFILTERING_HOOKLIST, &mail_filtering_data);
2331 if (mail_filtering_data.filtered == NULL &&
2332 mail_filtering_data.unfiltered == NULL) {
2333 /* nothing happened */
2334 debug_print(MAIL_LISTFILTERING_HOOKLIST " did nothing. filtering whole list normally.\n");
2337 if (mail_filtering_data.filtered != NULL) {
2338 /* keep track of what's been filtered by the hooks */
2339 debug_print(MAIL_LISTFILTERING_HOOKLIST " filtered some stuff. total %d filtered %d unfilt %d.\n",
2340 g_slist_length(list),
2341 g_slist_length(mail_filtering_data.filtered),
2342 g_slist_length(mail_filtering_data.unfiltered));
2344 *filtered = g_slist_copy(mail_filtering_data.filtered);
2346 if (mail_filtering_data.unfiltered != NULL) {
2347 /* what the hooks didn't handle will go in filtered or
2348 * unfiltered in the next loop */
2349 debug_print(MAIL_LISTFILTERING_HOOKLIST " left unfiltered stuff. total %d filtered %d unfilt %d.\n",
2350 g_slist_length(list),
2351 g_slist_length(mail_filtering_data.filtered),
2352 g_slist_length(mail_filtering_data.unfiltered));
2353 to_do = mail_filtering_data.unfiltered;
2356 for (cur = to_do; cur; cur = cur->next) {
2357 MsgInfo *info = (MsgInfo *)cur->data;
2358 if (procmsg_msginfo_filter(info, ac))
2359 *filtered = g_slist_prepend(*filtered, info);
2361 *unfiltered = g_slist_prepend(*unfiltered, info);
2362 statusbar_progress_all(curnum++, total, prefs_common.statusbar_update_step);
2365 g_slist_free(mail_filtering_data.filtered);
2366 g_slist_free(mail_filtering_data.unfiltered);
2368 *filtered = g_slist_reverse(*filtered);
2369 *unfiltered = g_slist_reverse(*unfiltered);
2371 statusbar_progress_all(0,0,0);
2372 statusbar_pop_all();
2375 MsgInfo *procmsg_msginfo_new_from_mimeinfo(MsgInfo *src_msginfo, MimeInfo *mimeinfo)
2377 MsgInfo *tmp_msginfo = NULL;
2378 MsgFlags flags = {0, 0};
2379 gchar *tmpfile = get_tmp_file();
2380 FILE *fp = g_fopen(tmpfile, "wb");
2382 if (!mimeinfo || mimeinfo->type != MIMETYPE_MESSAGE ||
2383 g_ascii_strcasecmp(mimeinfo->subtype, "rfc822")) {
2384 g_warning("procmsg_msginfo_new_from_mimeinfo(): unsuitable mimeinfo");
2391 if (fp && procmime_write_mimeinfo(mimeinfo, fp) >= 0) {
2394 tmp_msginfo = procheader_parse_file(
2401 if (tmp_msginfo != NULL) {
2403 tmp_msginfo->folder = src_msginfo->folder;
2404 tmp_msginfo->plaintext_file = g_strdup(tmpfile);
2406 g_warning("procmsg_msginfo_new_from_mimeinfo(): Can't generate new msginfo");
2414 static GSList *spam_learners = NULL;
2416 void procmsg_register_spam_learner (int (*learn_func)(MsgInfo *info, GSList *list, gboolean spam))
2418 if (!g_slist_find(spam_learners, learn_func))
2419 spam_learners = g_slist_append(spam_learners, learn_func);
2420 if (mainwindow_get_mainwindow()) {
2421 main_window_set_menu_sensitive(mainwindow_get_mainwindow());
2422 summary_set_menu_sensitive(
2423 mainwindow_get_mainwindow()->summaryview);
2424 toolbar_main_set_sensitive(mainwindow_get_mainwindow());
2428 void procmsg_unregister_spam_learner (int (*learn_func)(MsgInfo *info, GSList *list, gboolean spam))
2430 spam_learners = g_slist_remove(spam_learners, learn_func);
2431 if (mainwindow_get_mainwindow()) {
2432 main_window_set_menu_sensitive(mainwindow_get_mainwindow());
2433 summary_set_menu_sensitive(
2434 mainwindow_get_mainwindow()->summaryview);
2435 toolbar_main_set_sensitive(mainwindow_get_mainwindow());
2439 gboolean procmsg_spam_can_learn(void)
2441 return g_slist_length(spam_learners) > 0;
2444 int procmsg_spam_learner_learn (MsgInfo *info, GSList *list, gboolean spam)
2446 GSList *cur = spam_learners;
2448 for (; cur; cur = cur->next) {
2449 int ((*func)(MsgInfo *info, GSList *list, gboolean spam)) = cur->data;
2450 ret |= func(info, list, spam);
2455 static gchar *spam_folder_item = NULL;
2456 static FolderItem * (*procmsg_spam_get_folder_func)(MsgInfo *msginfo) = NULL;
2457 void procmsg_spam_set_folder (const char *item_identifier, FolderItem *(*spam_get_folder_func)(MsgInfo *info))
2459 g_free(spam_folder_item);
2460 if (item_identifier)
2461 spam_folder_item = g_strdup(item_identifier);
2463 spam_folder_item = NULL;
2464 if (spam_get_folder_func != NULL)
2465 procmsg_spam_get_folder_func = spam_get_folder_func;
2467 procmsg_spam_get_folder_func = NULL;
2470 FolderItem *procmsg_spam_get_folder (MsgInfo *msginfo)
2472 FolderItem *item = NULL;
2474 if (procmsg_spam_get_folder_func)
2475 item = procmsg_spam_get_folder_func(msginfo);
2476 if (item == NULL && spam_folder_item)
2477 item = folder_find_item_from_identifier(spam_folder_item);
2479 item = folder_get_default_trash();
2483 static void item_has_queued_mails(FolderItem *item, gpointer data)
2485 gboolean *result = (gboolean *)data;
2486 if (*result == TRUE)
2488 if (folder_has_parent_of_type(item, F_QUEUE)) {
2489 if (item->total_msgs == 0)
2492 GSList *msglist = folder_item_get_msg_list(item);
2494 for (cur = msglist; cur; cur = cur->next) {
2495 MsgInfo *msginfo = (MsgInfo *)cur->data;
2496 if (!MSG_IS_DELETED(msginfo->flags) &&
2497 !MSG_IS_LOCKED(msginfo->flags)) {
2502 procmsg_msg_list_free(msglist);
2507 gboolean procmsg_have_queued_mails_fast (void)
2509 gboolean result = FALSE;
2510 folder_func_to_all_folders(item_has_queued_mails, &result);
2514 static void item_has_trashed_mails(FolderItem *item, gpointer data)
2516 gboolean *result = (gboolean *)data;
2517 if (*result == TRUE)
2519 if (folder_has_parent_of_type(item, F_TRASH) && item->total_msgs > 0)
2523 gboolean procmsg_have_trashed_mails_fast (void)
2525 gboolean result = FALSE;
2526 folder_func_to_all_folders(item_has_trashed_mails, &result);
2530 gchar *procmsg_msginfo_get_tags_str(MsgInfo *msginfo)
2538 if (msginfo->tags == NULL)
2540 for (cur = msginfo->tags; cur; cur = cur->next) {
2541 const gchar *tag = tags_get_tag(GPOINTER_TO_INT(cur->data));
2545 tags = g_strdup(tag);
2547 int olen = strlen(tags);
2548 int nlen = olen + strlen(tag) + 2 /* strlen(", ") */;
2549 tags = g_realloc(tags, nlen+1);
2552 strcpy(tags+olen, ", ");
2553 strcpy(tags+olen+2, tag);
2560 void procmsg_msginfo_update_tags(MsgInfo *msginfo, gboolean set, gint id)
2568 msginfo->tags = g_slist_remove(
2570 GINT_TO_POINTER(id));
2571 changed.data = GINT_TO_POINTER(id);
2572 changed.next = NULL;
2573 folder_item_commit_tags(msginfo->folder, msginfo, NULL, &changed);
2575 if (!g_slist_find(msginfo->tags, GINT_TO_POINTER(id))) {
2576 msginfo->tags = g_slist_append(
2578 GINT_TO_POINTER(id));
2580 changed.data = GINT_TO_POINTER(id);
2581 changed.next = NULL;
2582 folder_item_commit_tags(msginfo->folder, msginfo, &changed, NULL);
2587 void procmsg_msginfo_clear_tags(MsgInfo *msginfo)
2589 GSList *unset = msginfo->tags;
2590 msginfo->tags = NULL;
2591 folder_item_commit_tags(msginfo->folder, msginfo, NULL, unset);
2592 g_slist_free(unset);