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++;
1775 fprintf(stdout, "++ STATS ++ SENT %d %d %d\n", session_stats.sent, session_stats.replied, session_stats.forwarded);
1779 /* save message to outbox */
1780 if (mailval == 0 && newsval == 0 && savecopyfolder) {
1783 debug_print("saving sent message...\n");
1785 outbox = folder_find_item_from_identifier(savecopyfolder);
1787 outbox = folder_get_default_outbox();
1789 if (save_clear_text || tmp_enc_file == NULL) {
1790 gboolean saved = FALSE;
1791 *queued_removed = FALSE;
1792 if (queue && msgnum > 0) {
1793 MsgInfo *queued_mail = folder_item_get_msginfo(queue, msgnum);
1794 if (folder_item_move_msg(outbox, queued_mail) >= 0) {
1795 debug_print("moved queued mail %d to sent folder\n", msgnum);
1797 *queued_removed = TRUE;
1798 } else if (folder_item_copy_msg(outbox, queued_mail) >= 0) {
1799 debug_print("copied queued mail %d to sent folder\n", msgnum);
1802 procmsg_msginfo_free(queued_mail);
1805 debug_print("resaving clear text queued mail to sent folder\n");
1806 procmsg_save_to_outbox(outbox, file, TRUE);
1809 debug_print("saving encrpyted queued mail to sent folder\n");
1810 procmsg_save_to_outbox(outbox, tmp_enc_file, FALSE);
1814 if (tmp_enc_file != NULL) {
1815 claws_unlink(tmp_enc_file);
1817 tmp_enc_file = NULL;
1820 if (replymessageid != NULL || fwdmessageid != NULL) {
1824 if (replymessageid != NULL)
1825 tokens = g_strsplit(replymessageid, "\t", 0);
1827 tokens = g_strsplit(fwdmessageid, "\t", 0);
1828 item = folder_find_item_from_identifier(tokens[0]);
1830 /* check if queued message has valid folder and message id */
1831 if (item != NULL && tokens[2] != NULL) {
1834 msginfo = folder_item_get_msginfo(item, atoi(tokens[1]));
1836 /* check if referring message exists and has a message id */
1837 if ((msginfo != NULL) &&
1838 (msginfo->msgid != NULL) &&
1839 (strcmp(msginfo->msgid, tokens[2]) != 0)) {
1840 procmsg_msginfo_free(msginfo);
1844 if (msginfo == NULL) {
1845 msginfo = folder_item_get_msginfo_by_msgid(item, tokens[2]);
1848 if (msginfo != NULL) {
1849 if (replymessageid != NULL) {
1850 MsgPermFlags to_unset = 0;
1852 if (prefs_common.mark_as_read_on_new_window)
1853 to_unset = (MSG_NEW|MSG_UNREAD);
1855 procmsg_msginfo_unset_flags(msginfo, to_unset, 0);
1856 procmsg_msginfo_set_flags(msginfo, MSG_REPLIED, 0);
1858 procmsg_msginfo_set_flags(msginfo, MSG_FORWARDED, 0);
1860 procmsg_msginfo_free(msginfo);
1868 slist_free_strings(to_list);
1869 g_slist_free(to_list);
1870 slist_free_strings(newsgroup_list);
1871 g_slist_free(newsgroup_list);
1872 g_free(savecopyfolder);
1873 g_free(replymessageid);
1874 g_free(fwdmessageid);
1875 g_free(privacy_system);
1876 g_free(encrypt_data);
1878 return (newsval != 0 ? newsval : mailval);
1881 gint procmsg_send_message_queue(const gchar *file, gchar **errstr, FolderItem *queue, gint msgnum, gboolean *queued_removed)
1883 gint result = procmsg_send_message_queue_full(file, FALSE, errstr, queue, msgnum, queued_removed);
1884 toolbar_main_set_sensitive(mainwindow_get_mainwindow());
1888 gint procmsg_send_message_queue_with_lock(const gchar *file, gchar **errstr, FolderItem *queue, gint msgnum, gboolean *queued_removed)
1891 if (procmsg_queue_lock(errstr)) {
1892 val = procmsg_send_message_queue(file, errstr, queue, msgnum, queued_removed);
1893 procmsg_queue_unlock();
1899 static void update_folder_msg_counts(FolderItem *item, MsgInfo *msginfo, MsgPermFlags old_flags)
1901 MsgPermFlags new_flags = msginfo->flags.perm_flags;
1904 if (!(old_flags & MSG_NEW) && (new_flags & MSG_NEW)) {
1908 if ((old_flags & MSG_NEW) && !(new_flags & MSG_NEW)) {
1913 if (!(old_flags & MSG_UNREAD) && (new_flags & MSG_UNREAD)) {
1914 item->unread_msgs++;
1915 if (procmsg_msg_has_marked_parent(msginfo))
1916 item->unreadmarked_msgs++;
1919 if ((old_flags & MSG_UNREAD) && !(new_flags & MSG_UNREAD)) {
1920 item->unread_msgs--;
1921 if (procmsg_msg_has_marked_parent(msginfo))
1922 item->unreadmarked_msgs--;
1926 if (!(old_flags & MSG_MARKED) && (new_flags & MSG_MARKED)) {
1927 procmsg_update_unread_children(msginfo, TRUE);
1928 item->marked_msgs++;
1931 if ((old_flags & MSG_MARKED) && !(new_flags & MSG_MARKED)) {
1932 procmsg_update_unread_children(msginfo, FALSE);
1933 item->marked_msgs--;
1936 if (!(old_flags & MSG_REPLIED) && (new_flags & MSG_REPLIED)) {
1937 item->replied_msgs++;
1940 if ((old_flags & MSG_REPLIED) && !(new_flags & MSG_REPLIED)) {
1941 item->replied_msgs--;
1944 if (!(old_flags & MSG_FORWARDED) && (new_flags & MSG_FORWARDED)) {
1945 item->forwarded_msgs++;
1948 if ((old_flags & MSG_FORWARDED) && !(new_flags & MSG_FORWARDED)) {
1949 item->forwarded_msgs--;
1952 if (!(old_flags & MSG_LOCKED) && (new_flags & MSG_LOCKED)) {
1953 item->locked_msgs++;
1956 if ((old_flags & MSG_LOCKED) && !(new_flags & MSG_LOCKED)) {
1957 item->locked_msgs--;
1960 if ((old_flags & MSG_IGNORE_THREAD) && !(new_flags & MSG_IGNORE_THREAD)) {
1961 item->ignored_msgs--;
1964 if (!(old_flags & MSG_IGNORE_THREAD) && (new_flags & MSG_IGNORE_THREAD)) {
1965 item->ignored_msgs++;
1968 if ((old_flags & MSG_WATCH_THREAD) && !(new_flags & MSG_WATCH_THREAD)) {
1969 item->watched_msgs--;
1972 if (!(old_flags & MSG_WATCH_THREAD) && (new_flags & MSG_WATCH_THREAD)) {
1973 item->watched_msgs++;
1977 void procmsg_msginfo_set_flags(MsgInfo *msginfo, MsgPermFlags perm_flags, MsgTmpFlags tmp_flags)
1980 MsgInfoUpdate msginfo_update;
1981 MsgPermFlags perm_flags_new, perm_flags_old;
1982 MsgTmpFlags tmp_flags_old;
1984 cm_return_if_fail(msginfo != NULL);
1985 item = msginfo->folder;
1986 cm_return_if_fail(item != NULL);
1988 debug_print("Setting flags for message %d in folder %s\n", msginfo->msgnum, item->path);
1990 /* Perm Flags handling */
1991 perm_flags_old = msginfo->flags.perm_flags;
1992 perm_flags_new = msginfo->flags.perm_flags | perm_flags;
1993 if ((perm_flags & MSG_IGNORE_THREAD) || (perm_flags_old & MSG_IGNORE_THREAD)) {
1994 perm_flags_new &= ~(MSG_NEW | MSG_UNREAD);
1996 if ((perm_flags & MSG_WATCH_THREAD) || (perm_flags_old & MSG_WATCH_THREAD)) {
1997 perm_flags_new &= ~(MSG_IGNORE_THREAD);
2000 if (perm_flags_old != perm_flags_new) {
2001 folder_item_change_msg_flags(msginfo->folder, msginfo, perm_flags_new);
2003 update_folder_msg_counts(item, msginfo, perm_flags_old);
2004 summary_update_unread(mainwindow_get_mainwindow()->summaryview, NULL);
2007 /* Tmp flags handling */
2008 tmp_flags_old = msginfo->flags.tmp_flags;
2009 msginfo->flags.tmp_flags |= tmp_flags;
2011 /* update notification */
2012 if ((perm_flags_old != perm_flags_new) || (tmp_flags_old != msginfo->flags.tmp_flags)) {
2013 msginfo_update.msginfo = msginfo;
2014 msginfo_update.flags = MSGINFO_UPDATE_FLAGS;
2015 hooks_invoke(MSGINFO_UPDATE_HOOKLIST, &msginfo_update);
2016 folder_item_update(msginfo->folder, F_ITEM_UPDATE_MSGCNT);
2020 void procmsg_msginfo_unset_flags(MsgInfo *msginfo, MsgPermFlags perm_flags, MsgTmpFlags tmp_flags)
2023 MsgInfoUpdate msginfo_update;
2024 MsgPermFlags perm_flags_new, perm_flags_old;
2025 MsgTmpFlags tmp_flags_old;
2027 cm_return_if_fail(msginfo != NULL);
2028 item = msginfo->folder;
2029 cm_return_if_fail(item != NULL);
2031 debug_print("Unsetting flags for message %d in folder %s\n", msginfo->msgnum, item->path);
2033 /* Perm Flags handling */
2034 perm_flags_old = msginfo->flags.perm_flags;
2035 perm_flags_new = msginfo->flags.perm_flags & ~perm_flags;
2037 if (perm_flags_old != perm_flags_new) {
2038 folder_item_change_msg_flags(msginfo->folder, msginfo, perm_flags_new);
2040 update_folder_msg_counts(item, msginfo, perm_flags_old);
2043 /* Tmp flags hanlding */
2044 tmp_flags_old = msginfo->flags.tmp_flags;
2045 msginfo->flags.tmp_flags &= ~tmp_flags;
2047 /* update notification */
2048 if ((perm_flags_old != perm_flags_new) || (tmp_flags_old != msginfo->flags.tmp_flags)) {
2049 msginfo_update.msginfo = msginfo;
2050 msginfo_update.flags = MSGINFO_UPDATE_FLAGS;
2051 hooks_invoke(MSGINFO_UPDATE_HOOKLIST, &msginfo_update);
2052 folder_item_update(msginfo->folder, F_ITEM_UPDATE_MSGCNT);
2056 void procmsg_msginfo_change_flags(MsgInfo *msginfo,
2057 MsgPermFlags add_perm_flags, MsgTmpFlags add_tmp_flags,
2058 MsgPermFlags rem_perm_flags, MsgTmpFlags rem_tmp_flags)
2061 MsgInfoUpdate msginfo_update;
2062 MsgPermFlags perm_flags_new, perm_flags_old;
2063 MsgTmpFlags tmp_flags_old;
2065 cm_return_if_fail(msginfo != NULL);
2066 item = msginfo->folder;
2067 cm_return_if_fail(item != NULL);
2069 debug_print("Changing flags for message %d in folder %s\n", msginfo->msgnum, item->path);
2071 /* Perm Flags handling */
2072 perm_flags_old = msginfo->flags.perm_flags;
2073 perm_flags_new = (msginfo->flags.perm_flags & ~rem_perm_flags) | add_perm_flags;
2074 if ((add_perm_flags & MSG_IGNORE_THREAD) || (perm_flags_old & MSG_IGNORE_THREAD)) {
2075 perm_flags_new &= ~(MSG_NEW | MSG_UNREAD);
2077 if ((add_perm_flags & MSG_WATCH_THREAD) || (perm_flags_old & MSG_WATCH_THREAD)) {
2078 perm_flags_new &= ~(MSG_IGNORE_THREAD);
2081 if (perm_flags_old != perm_flags_new) {
2082 folder_item_change_msg_flags(msginfo->folder, msginfo, perm_flags_new);
2084 update_folder_msg_counts(item, msginfo, perm_flags_old);
2088 /* Tmp flags handling */
2089 tmp_flags_old = msginfo->flags.tmp_flags;
2090 msginfo->flags.tmp_flags &= ~rem_tmp_flags;
2091 msginfo->flags.tmp_flags |= add_tmp_flags;
2093 /* update notification */
2094 if ((perm_flags_old != perm_flags_new) || (tmp_flags_old != msginfo->flags.tmp_flags)) {
2095 msginfo_update.msginfo = msginfo;
2096 msginfo_update.flags = MSGINFO_UPDATE_FLAGS;
2097 hooks_invoke(MSGINFO_UPDATE_HOOKLIST, &msginfo_update);
2098 folder_item_update(msginfo->folder, F_ITEM_UPDATE_MSGCNT);
2103 *\brief check for flags (e.g. mark) in prior msgs of current thread
2105 *\param info Current message
2106 *\param perm_flags Flags to be checked
2107 *\param parentmsgs Hash of prior msgs to avoid loops
2109 *\return gboolean TRUE if perm_flags are found
2111 static gboolean procmsg_msg_has_flagged_parent_real(MsgInfo *info,
2112 MsgPermFlags perm_flags, GHashTable *parentmsgs)
2116 cm_return_val_if_fail(info != NULL, FALSE);
2118 if (info != NULL && info->folder != NULL && info->inreplyto != NULL) {
2119 tmp = folder_item_get_msginfo_by_msgid(info->folder,
2121 if (tmp && (tmp->flags.perm_flags & perm_flags)) {
2122 procmsg_msginfo_free(tmp);
2124 } else if (tmp != NULL) {
2127 if (g_hash_table_lookup(parentmsgs, info)) {
2128 debug_print("loop detected: %d\n",
2132 g_hash_table_insert(parentmsgs, info, "1");
2133 result = procmsg_msg_has_flagged_parent_real(
2134 tmp, perm_flags, parentmsgs);
2136 procmsg_msginfo_free(tmp);
2146 *\brief Callback for cleaning up hash of parentmsgs
2148 static gboolean parentmsgs_hash_remove(gpointer key,
2156 *\brief Set up list of parentmsgs
2157 * See procmsg_msg_has_flagged_parent_real()
2159 gboolean procmsg_msg_has_flagged_parent(MsgInfo *info, MsgPermFlags perm_flags)
2162 static GHashTable *parentmsgs = NULL;
2164 if (parentmsgs == NULL)
2165 parentmsgs = g_hash_table_new(NULL, NULL);
2167 result = procmsg_msg_has_flagged_parent_real(info, perm_flags, parentmsgs);
2168 g_hash_table_foreach_remove(parentmsgs, parentmsgs_hash_remove, NULL);
2174 *\brief Check if msgs prior in thread are marked
2175 * See procmsg_msg_has_flagged_parent_real()
2177 gboolean procmsg_msg_has_marked_parent(MsgInfo *info)
2179 return procmsg_msg_has_flagged_parent(info, MSG_MARKED);
2183 static GSList *procmsg_find_children_func(MsgInfo *info,
2184 GSList *children, GSList *all)
2188 cm_return_val_if_fail(info!=NULL, children);
2189 if (info->msgid == NULL)
2192 for (cur = all; cur != NULL; cur = g_slist_next(cur)) {
2193 MsgInfo *tmp = (MsgInfo *)cur->data;
2194 if (tmp->inreplyto && !strcmp(tmp->inreplyto, info->msgid)) {
2195 /* Check if message is already in the list */
2196 if ((children == NULL) ||
2197 (g_slist_index(children, tmp) == -1)) {
2198 children = g_slist_prepend(children,
2199 procmsg_msginfo_new_ref(tmp));
2200 children = procmsg_find_children_func(tmp,
2209 static GSList *procmsg_find_children (MsgInfo *info)
2214 cm_return_val_if_fail(info!=NULL, NULL);
2215 all = folder_item_get_msg_list(info->folder);
2216 children = procmsg_find_children_func(info, NULL, all);
2217 if (children != NULL) {
2218 for (cur = all; cur != NULL; cur = g_slist_next(cur)) {
2219 /* this will not free the used pointers
2220 created with procmsg_msginfo_new_ref */
2221 procmsg_msginfo_free((MsgInfo *)cur->data);
2229 static void procmsg_update_unread_children(MsgInfo *info, gboolean newly_marked)
2231 GSList *children = procmsg_find_children(info);
2233 for (cur = children; cur != NULL; cur = g_slist_next(cur)) {
2234 MsgInfo *tmp = (MsgInfo *)cur->data;
2235 if(MSG_IS_UNREAD(tmp->flags) && !MSG_IS_IGNORE_THREAD(tmp->flags)) {
2237 info->folder->unreadmarked_msgs++;
2239 info->folder->unreadmarked_msgs--;
2240 folder_item_update(info->folder, F_ITEM_UPDATE_MSGCNT);
2242 procmsg_msginfo_free(tmp);
2244 g_slist_free(children);
2248 * Set the destination folder for a copy or move operation
2250 * \param msginfo The message which's destination folder is changed
2251 * \param to_folder The destination folder for the operation
2253 void procmsg_msginfo_set_to_folder(MsgInfo *msginfo, FolderItem *to_folder)
2255 if(msginfo->to_folder != NULL) {
2256 msginfo->to_folder->op_count--;
2257 folder_item_update(msginfo->to_folder, F_ITEM_UPDATE_MSGCNT);
2259 msginfo->to_folder = to_folder;
2260 if(to_folder != NULL) {
2261 to_folder->op_count++;
2262 folder_item_update(msginfo->to_folder, F_ITEM_UPDATE_MSGCNT);
2267 * Apply filtering actions to the msginfo
2269 * \param msginfo The MsgInfo describing the message that should be filtered
2270 * \return TRUE if the message was moved and MsgInfo is now invalid,
2273 static gboolean procmsg_msginfo_filter(MsgInfo *msginfo, PrefsAccount* ac_prefs)
2275 MailFilteringData mail_filtering_data;
2277 mail_filtering_data.msginfo = msginfo;
2278 mail_filtering_data.msglist = NULL;
2279 mail_filtering_data.filtered = NULL;
2280 mail_filtering_data.unfiltered = NULL;
2281 mail_filtering_data.account = ac_prefs;
2283 if (!ac_prefs || ac_prefs->filterhook_on_recv)
2284 if (hooks_invoke(MAIL_FILTERING_HOOKLIST, &mail_filtering_data))
2287 /* filter if enabled in prefs or move to inbox if not */
2288 if((filtering_rules != NULL) &&
2289 filter_message_by_msginfo(filtering_rules, msginfo, ac_prefs,
2290 FILTERING_INCORPORATION, NULL)) {
2297 void procmsg_msglist_filter(GSList *list, PrefsAccount *ac,
2298 GSList **filtered, GSList **unfiltered,
2301 GSList *cur, *to_do = NULL;
2302 gint total = 0, curnum = 0;
2303 MailFilteringData mail_filtering_data;
2305 cm_return_if_fail(filtered != NULL);
2306 cm_return_if_fail(unfiltered != NULL);
2314 total = g_slist_length(list);
2318 *unfiltered = g_slist_copy(list);
2322 statusbar_print_all(_("Filtering messages...\n"));
2324 mail_filtering_data.msginfo = NULL;
2325 mail_filtering_data.msglist = list;
2326 mail_filtering_data.filtered = NULL;
2327 mail_filtering_data.unfiltered = NULL;
2328 mail_filtering_data.account = ac;
2330 if (!ac || ac->filterhook_on_recv)
2331 hooks_invoke(MAIL_LISTFILTERING_HOOKLIST, &mail_filtering_data);
2333 if (mail_filtering_data.filtered == NULL &&
2334 mail_filtering_data.unfiltered == NULL) {
2335 /* nothing happened */
2336 debug_print(MAIL_LISTFILTERING_HOOKLIST " did nothing. filtering whole list normally.\n");
2339 if (mail_filtering_data.filtered != NULL) {
2340 /* keep track of what's been filtered by the hooks */
2341 debug_print(MAIL_LISTFILTERING_HOOKLIST " filtered some stuff. total %d filtered %d unfilt %d.\n",
2342 g_slist_length(list),
2343 g_slist_length(mail_filtering_data.filtered),
2344 g_slist_length(mail_filtering_data.unfiltered));
2346 *filtered = g_slist_copy(mail_filtering_data.filtered);
2348 if (mail_filtering_data.unfiltered != NULL) {
2349 /* what the hooks didn't handle will go in filtered or
2350 * unfiltered in the next loop */
2351 debug_print(MAIL_LISTFILTERING_HOOKLIST " left unfiltered stuff. total %d filtered %d unfilt %d.\n",
2352 g_slist_length(list),
2353 g_slist_length(mail_filtering_data.filtered),
2354 g_slist_length(mail_filtering_data.unfiltered));
2355 to_do = mail_filtering_data.unfiltered;
2358 for (cur = to_do; cur; cur = cur->next) {
2359 MsgInfo *info = (MsgInfo *)cur->data;
2360 if (procmsg_msginfo_filter(info, ac))
2361 *filtered = g_slist_prepend(*filtered, info);
2363 *unfiltered = g_slist_prepend(*unfiltered, info);
2364 statusbar_progress_all(curnum++, total, prefs_common.statusbar_update_step);
2367 g_slist_free(mail_filtering_data.filtered);
2368 g_slist_free(mail_filtering_data.unfiltered);
2370 *filtered = g_slist_reverse(*filtered);
2371 *unfiltered = g_slist_reverse(*unfiltered);
2373 statusbar_progress_all(0,0,0);
2374 statusbar_pop_all();
2377 MsgInfo *procmsg_msginfo_new_from_mimeinfo(MsgInfo *src_msginfo, MimeInfo *mimeinfo)
2379 MsgInfo *tmp_msginfo = NULL;
2380 MsgFlags flags = {0, 0};
2381 gchar *tmpfile = get_tmp_file();
2382 FILE *fp = g_fopen(tmpfile, "wb");
2384 if (!mimeinfo || mimeinfo->type != MIMETYPE_MESSAGE ||
2385 g_ascii_strcasecmp(mimeinfo->subtype, "rfc822")) {
2386 g_warning("procmsg_msginfo_new_from_mimeinfo(): unsuitable mimeinfo");
2393 if (fp && procmime_write_mimeinfo(mimeinfo, fp) >= 0) {
2396 tmp_msginfo = procheader_parse_file(
2403 if (tmp_msginfo != NULL) {
2405 tmp_msginfo->folder = src_msginfo->folder;
2406 tmp_msginfo->plaintext_file = g_strdup(tmpfile);
2408 g_warning("procmsg_msginfo_new_from_mimeinfo(): Can't generate new msginfo");
2416 static GSList *spam_learners = NULL;
2418 void procmsg_register_spam_learner (int (*learn_func)(MsgInfo *info, GSList *list, gboolean spam))
2420 if (!g_slist_find(spam_learners, learn_func))
2421 spam_learners = g_slist_append(spam_learners, learn_func);
2422 if (mainwindow_get_mainwindow()) {
2423 main_window_set_menu_sensitive(mainwindow_get_mainwindow());
2424 summary_set_menu_sensitive(
2425 mainwindow_get_mainwindow()->summaryview);
2426 toolbar_main_set_sensitive(mainwindow_get_mainwindow());
2430 void procmsg_unregister_spam_learner (int (*learn_func)(MsgInfo *info, GSList *list, gboolean spam))
2432 spam_learners = g_slist_remove(spam_learners, learn_func);
2433 if (mainwindow_get_mainwindow()) {
2434 main_window_set_menu_sensitive(mainwindow_get_mainwindow());
2435 summary_set_menu_sensitive(
2436 mainwindow_get_mainwindow()->summaryview);
2437 toolbar_main_set_sensitive(mainwindow_get_mainwindow());
2441 gboolean procmsg_spam_can_learn(void)
2443 return g_slist_length(spam_learners) > 0;
2446 int procmsg_spam_learner_learn (MsgInfo *info, GSList *list, gboolean spam)
2448 GSList *cur = spam_learners;
2450 for (; cur; cur = cur->next) {
2451 int ((*func)(MsgInfo *info, GSList *list, gboolean spam)) = cur->data;
2452 ret |= func(info, list, spam);
2457 static gchar *spam_folder_item = NULL;
2458 static FolderItem * (*procmsg_spam_get_folder_func)(MsgInfo *msginfo) = NULL;
2459 void procmsg_spam_set_folder (const char *item_identifier, FolderItem *(*spam_get_folder_func)(MsgInfo *info))
2461 g_free(spam_folder_item);
2462 if (item_identifier)
2463 spam_folder_item = g_strdup(item_identifier);
2465 spam_folder_item = NULL;
2466 if (spam_get_folder_func != NULL)
2467 procmsg_spam_get_folder_func = spam_get_folder_func;
2469 procmsg_spam_get_folder_func = NULL;
2472 FolderItem *procmsg_spam_get_folder (MsgInfo *msginfo)
2474 FolderItem *item = NULL;
2476 if (procmsg_spam_get_folder_func)
2477 item = procmsg_spam_get_folder_func(msginfo);
2478 if (item == NULL && spam_folder_item)
2479 item = folder_find_item_from_identifier(spam_folder_item);
2481 item = folder_get_default_trash();
2485 static void item_has_queued_mails(FolderItem *item, gpointer data)
2487 gboolean *result = (gboolean *)data;
2488 if (*result == TRUE)
2490 if (folder_has_parent_of_type(item, F_QUEUE)) {
2491 if (item->total_msgs == 0)
2494 GSList *msglist = folder_item_get_msg_list(item);
2496 for (cur = msglist; cur; cur = cur->next) {
2497 MsgInfo *msginfo = (MsgInfo *)cur->data;
2498 if (!MSG_IS_DELETED(msginfo->flags) &&
2499 !MSG_IS_LOCKED(msginfo->flags)) {
2504 procmsg_msg_list_free(msglist);
2509 gboolean procmsg_have_queued_mails_fast (void)
2511 gboolean result = FALSE;
2512 folder_func_to_all_folders(item_has_queued_mails, &result);
2516 static void item_has_trashed_mails(FolderItem *item, gpointer data)
2518 gboolean *result = (gboolean *)data;
2519 if (*result == TRUE)
2521 if (folder_has_parent_of_type(item, F_TRASH) && item->total_msgs > 0)
2525 gboolean procmsg_have_trashed_mails_fast (void)
2527 gboolean result = FALSE;
2528 folder_func_to_all_folders(item_has_trashed_mails, &result);
2532 gchar *procmsg_msginfo_get_tags_str(MsgInfo *msginfo)
2540 if (msginfo->tags == NULL)
2542 for (cur = msginfo->tags; cur; cur = cur->next) {
2543 const gchar *tag = tags_get_tag(GPOINTER_TO_INT(cur->data));
2547 tags = g_strdup(tag);
2549 int olen = strlen(tags);
2550 int nlen = olen + strlen(tag) + 2 /* strlen(", ") */;
2551 tags = g_realloc(tags, nlen+1);
2554 strcpy(tags+olen, ", ");
2555 strcpy(tags+olen+2, tag);
2562 void procmsg_msginfo_update_tags(MsgInfo *msginfo, gboolean set, gint id)
2570 msginfo->tags = g_slist_remove(
2572 GINT_TO_POINTER(id));
2573 changed.data = GINT_TO_POINTER(id);
2574 changed.next = NULL;
2575 folder_item_commit_tags(msginfo->folder, msginfo, NULL, &changed);
2577 if (!g_slist_find(msginfo->tags, GINT_TO_POINTER(id))) {
2578 msginfo->tags = g_slist_append(
2580 GINT_TO_POINTER(id));
2582 changed.data = GINT_TO_POINTER(id);
2583 changed.next = NULL;
2584 folder_item_commit_tags(msginfo->folder, msginfo, &changed, NULL);
2589 void procmsg_msginfo_clear_tags(MsgInfo *msginfo)
2591 GSList *unset = msginfo->tags;
2592 msginfo->tags = NULL;
2593 folder_item_commit_tags(msginfo->folder, msginfo, NULL, unset);
2594 g_slist_free(unset);