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"
53 extern SessionStats session_stats;
55 static gint procmsg_send_message_queue_full(const gchar *file, gboolean keep_session, gchar **errstr,
56 FolderItem *queue, gint msgnum, gboolean *queued_removed);
57 static void procmsg_update_unread_children (MsgInfo *info,
58 gboolean newly_marked);
65 Q_MAIL_ACCOUNT_ID = 4,
66 Q_NEWS_ACCOUNT_ID = 5,
67 Q_SAVE_COPY_FOLDER = 6,
68 Q_REPLY_MESSAGE_ID = 7,
74 Q_PRIVACY_SYSTEM_OLD = 13,
76 Q_ENCRYPT_DATA_OLD = 15,
77 Q_CLAWS_HDRS_OLD = 16,
80 void procmsg_msg_list_free(GSList *mlist)
85 for (cur = mlist; cur != NULL; cur = cur->next) {
86 msginfo = (MsgInfo *)cur->data;
87 procmsg_msginfo_free(msginfo);
101 /* CLAWS subject threading:
103 in the first round it inserts subject lines in a
104 hashtable (subject <-> node)
106 the second round finishes the threads by attaching
107 matching subject lines to the one found in the
108 hashtable. will use the oldest node with the same
109 subject that is not more then thread_by_subject_max_age
110 days old (see subject_hashtable_lookup)
113 static void subject_hashtable_insert(GHashTable *hashtable, GNode *node)
119 cm_return_if_fail(hashtable != NULL);
120 cm_return_if_fail(node != NULL);
121 msginfo = (MsgInfo *) node->data;
122 cm_return_if_fail(msginfo != NULL);
124 subject = msginfo->subject;
128 subject += subject_get_prefix_length(subject);
130 list = g_hash_table_lookup(hashtable, subject);
131 list = g_slist_prepend(list, node);
132 g_hash_table_insert(hashtable, subject, list);
135 static GNode *subject_hashtable_lookup(GHashTable *hashtable, MsgInfo *msginfo)
139 GNode *node = NULL, *hashtable_node = NULL;
141 MsgInfo *hashtable_msginfo = NULL, *best_msginfo = NULL;
144 cm_return_val_if_fail(hashtable != NULL, NULL);
146 subject = msginfo->subject;
149 prefix_length = subject_get_prefix_length(subject);
150 if (prefix_length <= 0)
152 subject += prefix_length;
154 list = g_hash_table_lookup(hashtable, subject);
158 /* check all nodes with the same subject to find the best parent */
159 for (cur = list; cur; cur = cur->next) {
160 hashtable_node = (GNode *)cur->data;
161 hashtable_msginfo = (MsgInfo *) hashtable_node->data;
164 /* best node should be the oldest in the found nodes */
165 /* parent node must not be older then msginfo */
166 if ((hashtable_msginfo->date_t < msginfo->date_t) &&
167 ((best_msginfo == NULL) ||
168 (best_msginfo->date_t > hashtable_msginfo->date_t)))
171 /* parent node must not be more then thread_by_subject_max_age
172 days older then msginfo */
173 if (abs(difftime(msginfo->date_t, hashtable_msginfo->date_t)) >
174 prefs_common.thread_by_subject_max_age * 3600 * 24)
177 /* can add new tests for all matching
178 nodes found by subject */
181 node = hashtable_node;
182 best_msginfo = hashtable_msginfo;
189 static void subject_hashtable_free(gpointer key, gpointer value, gpointer data)
194 /* return the reversed thread tree */
195 GNode *procmsg_get_thread_tree(GSList *mlist)
197 GNode *root, *parent, *node, *next;
198 GHashTable *msgid_table;
199 GHashTable *subject_hashtable = NULL;
204 root = g_node_new(NULL);
205 msgid_table = g_hash_table_new(g_str_hash, g_str_equal);
207 if (prefs_common.thread_by_subject) {
208 subject_hashtable = g_hash_table_new(g_str_hash, g_str_equal);
211 for (; mlist != NULL; mlist = mlist->next) {
212 msginfo = (MsgInfo *)mlist->data;
215 if (msginfo->inreplyto) {
216 parent = g_hash_table_lookup(msgid_table, msginfo->inreplyto);
217 if (parent == NULL) {
221 node = g_node_insert_data_before
222 (parent, parent == root ? parent->children : NULL,
224 if ((msgid = msginfo->msgid) && g_hash_table_lookup(msgid_table, msgid) == NULL)
225 g_hash_table_insert(msgid_table, (gchar *)msgid, node);
227 /* CLAWS: add subject to hashtable (without prefix) */
228 if (prefs_common.thread_by_subject) {
229 subject_hashtable_insert(subject_hashtable, node);
233 /* complete the unfinished threads */
234 for (node = root->children; node != NULL; ) {
236 msginfo = (MsgInfo *)node->data;
239 if (msginfo->inreplyto)
240 parent = g_hash_table_lookup(msgid_table, msginfo->inreplyto);
242 /* try looking for the indirect parent */
243 if (!parent && msginfo->references) {
244 for (reflist = msginfo->references;
245 reflist != NULL; reflist = reflist->next)
246 if ((parent = g_hash_table_lookup
247 (msgid_table, reflist->data)) != NULL)
251 /* node should not be the parent, and node should not
252 be an ancestor of parent (circular reference) */
253 if (parent && parent != node &&
254 !g_node_is_ancestor(node, parent)) {
257 (parent, parent->children, node);
263 if (prefs_common.thread_by_subject) {
264 START_TIMING("thread by subject");
265 for (node = root->children; node && node != NULL;) {
267 msginfo = (MsgInfo *) node->data;
269 parent = subject_hashtable_lookup(subject_hashtable, msginfo);
271 /* the node may already be threaded by IN-REPLY-TO, so go up
273 find the parent node */
274 if (parent != NULL) {
275 if (g_node_is_ancestor(node, parent))
283 g_node_append(parent, node);
291 if (prefs_common.thread_by_subject)
293 g_hash_table_foreach(subject_hashtable, subject_hashtable_free, NULL);
294 g_hash_table_destroy(subject_hashtable);
297 g_hash_table_destroy(msgid_table);
302 gint procmsg_move_messages(GSList *mlist)
304 GSList *cur, *movelist = NULL;
306 FolderItem *dest = NULL;
308 gboolean finished = TRUE;
309 if (!mlist) return 0;
311 folder_item_update_freeze();
314 for (cur = mlist; cur != NULL; cur = cur->next) {
315 msginfo = (MsgInfo *)cur->data;
316 if (!msginfo->to_folder) {
322 dest = msginfo->to_folder;
323 movelist = g_slist_prepend(movelist, msginfo);
324 } else if (dest == msginfo->to_folder) {
325 movelist = g_slist_prepend(movelist, msginfo);
329 procmsg_msginfo_set_to_folder(msginfo, NULL);
332 movelist = g_slist_reverse(movelist);
333 retval |= folder_item_move_msgs(dest, movelist);
334 g_slist_free(movelist);
337 if (finished == FALSE) {
343 folder_item_update_thaw();
347 void procmsg_copy_messages(GSList *mlist)
349 GSList *cur, *copylist = NULL;
351 FolderItem *dest = NULL;
352 gboolean finished = TRUE;
355 folder_item_update_freeze();
358 for (cur = mlist; cur != NULL; cur = cur->next) {
359 msginfo = (MsgInfo *)cur->data;
360 if (!msginfo->to_folder) {
366 dest = msginfo->to_folder;
367 copylist = g_slist_prepend(copylist, msginfo);
368 } else if (dest == msginfo->to_folder) {
369 copylist = g_slist_prepend(copylist, msginfo);
373 procmsg_msginfo_set_to_folder(msginfo, NULL);
376 copylist = g_slist_reverse(copylist);
377 folder_item_copy_msgs(dest, copylist);
378 g_slist_free(copylist);
381 if (finished == FALSE) {
387 folder_item_update_thaw();
390 gchar *procmsg_get_message_file_path(MsgInfo *msginfo)
394 cm_return_val_if_fail(msginfo != NULL, NULL);
396 if (msginfo->plaintext_file)
397 file = g_strdup(msginfo->plaintext_file);
399 file = folder_item_fetch_msg(msginfo->folder, msginfo->msgnum);
405 gchar *procmsg_get_message_file(MsgInfo *msginfo)
407 gchar *filename = NULL;
409 cm_return_val_if_fail(msginfo != NULL, NULL);
411 filename = folder_item_fetch_msg(msginfo->folder, msginfo->msgnum);
413 debug_print("can't fetch message %d\n", msginfo->msgnum);
418 gchar *procmsg_get_message_file_full(MsgInfo *msginfo, gboolean headers, gboolean body)
420 gchar *filename = NULL;
422 cm_return_val_if_fail(msginfo != NULL, NULL);
424 filename = folder_item_fetch_msg_full(msginfo->folder, msginfo->msgnum,
427 debug_print("can't fetch message %d\n", msginfo->msgnum);
432 GSList *procmsg_get_message_file_list(GSList *mlist)
434 GSList *file_list = NULL;
436 MsgFileInfo *fileinfo;
439 while (mlist != NULL) {
440 msginfo = (MsgInfo *)mlist->data;
441 file = procmsg_get_message_file(msginfo);
443 procmsg_message_file_list_free(file_list);
446 fileinfo = g_new(MsgFileInfo, 1);
447 fileinfo->msginfo = procmsg_msginfo_new_ref(msginfo);
448 fileinfo->file = file;
449 fileinfo->flags = g_new(MsgFlags, 1);
450 *fileinfo->flags = msginfo->flags;
451 file_list = g_slist_prepend(file_list, fileinfo);
455 file_list = g_slist_reverse(file_list);
460 void procmsg_message_file_list_free(MsgInfoList *file_list)
463 MsgFileInfo *fileinfo;
465 for (cur = file_list; cur != NULL; cur = cur->next) {
466 fileinfo = (MsgFileInfo *)cur->data;
467 procmsg_msginfo_free(fileinfo->msginfo);
468 g_free(fileinfo->file);
469 g_free(fileinfo->flags);
473 g_slist_free(file_list);
476 FILE *procmsg_open_message(MsgInfo *msginfo)
481 cm_return_val_if_fail(msginfo != NULL, NULL);
483 file = procmsg_get_message_file_path(msginfo);
484 cm_return_val_if_fail(file != NULL, NULL);
486 if (!is_file_exist(file)) {
488 file = procmsg_get_message_file(msginfo);
493 if ((fp = g_fopen(file, "rb")) == NULL) {
494 FILE_OP_ERROR(file, "fopen");
501 if (MSG_IS_QUEUED(msginfo->flags) || MSG_IS_DRAFT(msginfo->flags)) {
504 while (fgets(buf, sizeof(buf), fp) != NULL) {
506 if ((!strncmp(buf, "X-Claws-End-Special-Headers: 1",
507 strlen("X-Claws-End-Special-Headers:"))) ||
508 (!strncmp(buf, "X-Sylpheed-End-Special-Headers: 1",
509 strlen("X-Sylpheed-End-Special-Headers:"))))
512 if (buf[0] == '\r' || buf[0] == '\n') break;
513 /* from other mailers */
514 if (!strncmp(buf, "Date: ", 6)
515 || !strncmp(buf, "To: ", 4)
516 || !strncmp(buf, "From: ", 6)
517 || !strncmp(buf, "Subject: ", 9)) {
527 gboolean procmsg_msg_exist(MsgInfo *msginfo)
532 if (!msginfo) return FALSE;
534 path = folder_item_get_path(msginfo->folder);
536 ret = !folder_item_is_msg_changed(msginfo->folder, msginfo);
542 void procmsg_get_filter_keyword(MsgInfo *msginfo, gchar **header, gchar **key,
543 PrefsFilterType type)
545 static HeaderEntry hentry[] = {{"X-BeenThere:", NULL, TRUE},
546 {"X-ML-Name:", NULL, TRUE},
547 {"X-List:", NULL, TRUE},
548 {"X-Mailing-list:", NULL, TRUE},
549 {"List-Id:", NULL, TRUE},
550 {"X-Sequence:", NULL, TRUE},
551 {"Sender:", NULL, TRUE},
552 {"List-Post:", NULL, TRUE},
553 {NULL, NULL, FALSE}};
559 H_X_MAILING_LIST = 3,
568 cm_return_if_fail(msginfo != NULL);
569 cm_return_if_fail(header != NULL);
570 cm_return_if_fail(key != NULL);
579 if ((fp = procmsg_open_message(msginfo)) == NULL)
581 procheader_get_header_fields(fp, hentry);
584 #define SET_FILTER_KEY(hstr, idx) \
586 *header = g_strdup(hstr); \
587 *key = hentry[idx].body; \
588 hentry[idx].body = NULL; \
591 if (hentry[H_LIST_ID].body != NULL) {
592 SET_FILTER_KEY("header \"List-Id\"", H_LIST_ID);
593 extract_list_id_str(*key);
594 } else if (hentry[H_X_BEENTHERE].body != NULL) {
595 SET_FILTER_KEY("header \"X-BeenThere\"", H_X_BEENTHERE);
596 } else if (hentry[H_X_ML_NAME].body != NULL) {
597 SET_FILTER_KEY("header \"X-ML-Name\"", H_X_ML_NAME);
598 } else if (hentry[H_X_LIST].body != NULL) {
599 SET_FILTER_KEY("header \"X-List\"", H_X_LIST);
600 } else if (hentry[H_X_MAILING_LIST].body != NULL) {
601 SET_FILTER_KEY("header \"X-Mailing-List\"", H_X_MAILING_LIST);
602 } else if (hentry[H_X_SEQUENCE].body != NULL) {
605 SET_FILTER_KEY("X-Sequence", H_X_SEQUENCE);
608 while (*p != '\0' && !g_ascii_isspace(*p)) p++;
609 while (g_ascii_isspace(*p)) p++;
610 if (g_ascii_isdigit(*p)) {
616 } else if (hentry[H_SENDER].body != NULL) {
617 SET_FILTER_KEY("header \"Sender\"", H_SENDER);
618 } else if (hentry[H_LIST_POST].body != NULL) {
619 SET_FILTER_KEY("header \"List-Post\"", H_LIST_POST);
620 } else if (msginfo->to) {
621 *header = g_strdup("to");
622 *key = g_strdup(msginfo->to);
623 } else if (msginfo->subject) {
624 *header = g_strdup("subject");
625 *key = g_strdup(msginfo->subject);
628 #undef SET_FILTER_KEY
630 g_free(hentry[H_X_BEENTHERE].body);
631 hentry[H_X_BEENTHERE].body = NULL;
632 g_free(hentry[H_X_ML_NAME].body);
633 hentry[H_X_ML_NAME].body = NULL;
634 g_free(hentry[H_X_LIST].body);
635 hentry[H_X_LIST].body = NULL;
636 g_free(hentry[H_X_MAILING_LIST].body);
637 hentry[H_X_MAILING_LIST].body = NULL;
638 g_free(hentry[H_LIST_ID].body);
639 hentry[H_LIST_ID].body = NULL;
640 g_free(hentry[H_SENDER].body);
641 hentry[H_SENDER].body = NULL;
642 g_free(hentry[H_LIST_POST].body);
643 hentry[H_LIST_POST].body = NULL;
647 *header = g_strdup("from");
648 *key = g_strdup(msginfo->from);
651 *header = g_strdup("to");
652 *key = g_strdup(msginfo->to);
654 case FILTER_BY_SUBJECT:
655 *header = g_strdup("subject");
656 *key = g_strdup(msginfo->subject);
663 static void procmsg_empty_trash(FolderItem *trash)
668 (trash->stype != F_TRASH &&
669 !folder_has_parent_of_type(trash, F_TRASH)))
672 if (trash && trash->total_msgs > 0) {
673 GSList *mlist = folder_item_get_msg_list(trash);
675 for (cur = mlist ; cur != NULL ; cur = cur->next) {
676 MsgInfo * msginfo = (MsgInfo *) cur->data;
677 if (MSG_IS_LOCKED(msginfo->flags)) {
678 procmsg_msginfo_free(msginfo);
681 if (msginfo->total_size != 0 &&
682 msginfo->size != (off_t)msginfo->total_size)
683 partial_mark_for_delete(msginfo);
685 procmsg_msginfo_free(msginfo);
688 folder_item_remove_all_msg(trash);
691 if (!trash->node || !trash->node->children)
694 node = trash->node->children;
695 while (node != NULL) {
697 procmsg_empty_trash(FOLDER_ITEM(node->data));
702 void procmsg_empty_all_trash(void)
707 for (cur = folder_get_list(); cur != NULL; cur = cur->next) {
708 Folder *folder = FOLDER(cur->data);
709 trash = folder->trash;
710 procmsg_empty_trash(trash);
711 if (folder->account && folder->account->set_trash_folder &&
712 folder_find_item_from_identifier(folder->account->trash_folder))
714 folder_find_item_from_identifier(folder->account->trash_folder));
718 static PrefsAccount *procmsg_get_account_from_file(const gchar *file)
720 PrefsAccount *mailac = NULL;
724 static HeaderEntry qentry[] = {{"S:", NULL, FALSE},
725 {"SSV:", NULL, FALSE},
727 {"NG:", NULL, FALSE},
728 {"MAID:", NULL, FALSE},
729 {"NAID:", NULL, FALSE},
730 {"SCF:", NULL, FALSE},
731 {"RMID:", NULL, FALSE},
732 {"FMID:", NULL, FALSE},
733 {"X-Claws-Privacy-System:", NULL, FALSE},
734 {"X-Claws-Encrypt:", NULL, FALSE},
735 {"X-Claws-Encrypt-Data:", NULL, FALSE},
736 {"X-Claws-End-Special-Headers", NULL, FALSE},
737 {"X-Sylpheed-Privacy-System:", NULL, FALSE},
738 {"X-Sylpheed-Encrypt:", NULL, FALSE},
739 {"X-Sylpheed-Encrypt-Data:", NULL, FALSE},
740 {NULL, NULL, FALSE}};
742 cm_return_val_if_fail(file != NULL, NULL);
744 if ((fp = g_fopen(file, "rb")) == NULL) {
745 FILE_OP_ERROR(file, "fopen");
749 while ((hnum = procheader_get_one_field(buf, sizeof(buf), fp, qentry))
751 gchar *p = buf + strlen(qentry[hnum].name);
753 if (hnum == Q_MAIL_ACCOUNT_ID) {
754 mailac = account_find_from_id(atoi(p));
762 gchar *procmsg_msginfo_get_identifier(MsgInfo *msginfo)
768 cm_return_val_if_fail(msginfo != NULL, NULL);
769 folder_id = folder_item_get_identifier(msginfo->folder);
770 msgid = msginfo->msgid;
772 id = g_strconcat(folder_id, G_DIR_SEPARATOR_S, msgid, NULL);
779 MsgInfo *procmsg_get_msginfo_from_identifier(const gchar *id)
781 gchar *folder_id = g_strdup(id);
782 gchar *separator = strrchr(folder_id, G_DIR_SEPARATOR);
787 if (separator == NULL) {
793 msgid = separator + 1;
795 item = folder_find_item_from_identifier(folder_id);
802 msginfo = folder_item_get_msginfo_by_msgid(item, msgid);
808 static GSList *procmsg_list_sort_by_account(FolderItem *queue, GSList *list)
810 GSList *result = NULL;
812 PrefsAccount *last_account = NULL;
815 gboolean nothing_to_sort = TRUE;
820 orig = g_slist_copy(list);
822 msg = (MsgInfo *)orig->data;
824 for (cur = orig; cur; cur = cur->next)
825 debug_print("sort before %s\n", ((MsgInfo *)cur->data)->from);
830 nothing_to_sort = TRUE;
834 PrefsAccount *ac = NULL;
835 msg = (MsgInfo *)cur->data;
836 file = folder_item_fetch_msg(queue, msg->msgnum);
837 ac = procmsg_get_account_from_file(file);
840 if (last_account == NULL || (ac != NULL && ac == last_account)) {
841 result = g_slist_append(result, msg);
842 orig = g_slist_remove(orig, msg);
844 nothing_to_sort = FALSE;
850 if (orig || g_slist_length(orig)) {
851 if (!last_account && nothing_to_sort) {
852 /* can't find an account for the rest of the list */
855 result = g_slist_append(result, cur->data);
866 for (cur = result; cur; cur = cur->next)
867 debug_print("sort after %s\n", ((MsgInfo *)cur->data)->from);
874 static gboolean procmsg_is_last_for_account(FolderItem *queue, MsgInfo *msginfo, GSList *elem)
876 gchar *file = folder_item_fetch_msg(queue, msginfo->msgnum);
877 PrefsAccount *ac = procmsg_get_account_from_file(file);
880 for (cur = elem; cur; cur = cur->next) {
881 MsgInfo *cur_msginfo = (MsgInfo *)cur->data;
882 file = folder_item_fetch_msg(queue, cur_msginfo->msgnum);
884 if (cur_msginfo != msginfo && !MSG_IS_LOCKED(cur_msginfo->flags)) {
885 if (procmsg_get_account_from_file(file) == ac) {
896 static gboolean send_queue_lock = FALSE;
898 gboolean procmsg_queue_lock(char **errstr)
900 if (send_queue_lock) {
901 /* Avoid having to translate two similar strings */
902 log_warning(LOG_PROTOCOL, "%s\n", _("Already trying to send."));
904 if (*errstr) g_free(*errstr);
905 *errstr = g_strdup_printf(_("Already trying to send."));
909 send_queue_lock = TRUE;
912 void procmsg_queue_unlock(void)
914 send_queue_lock = FALSE;
917 *\brief Send messages in queue
919 *\param queue Queue folder to process
920 *\param save_msgs Unused
922 *\return Number of messages sent, negative if an error occurred
923 * positive if no error occurred
925 gint procmsg_send_queue(FolderItem *queue, gboolean save_msgs, gchar **errstr)
927 gint sent = 0, err = 0;
929 GSList *sorted_list = NULL;
932 if (!procmsg_queue_lock(errstr)) {
933 toolbar_main_set_sensitive(mainwindow_get_mainwindow());
938 queue = folder_get_default_queue();
941 procmsg_queue_unlock();
946 toolbar_main_set_sensitive(mainwindow_get_mainwindow());
948 folder_item_scan(queue);
949 list = folder_item_get_msg_list(queue);
951 /* sort the list per sender account; this helps reusing the same SMTP server */
952 sorted_list = procmsg_list_sort_by_account(queue, list);
954 for (elem = sorted_list; elem != NULL; elem = elem->next) {
958 msginfo = (MsgInfo *)(elem->data);
959 if (!MSG_IS_LOCKED(msginfo->flags) && !MSG_IS_DELETED(msginfo->flags)) {
960 file = folder_item_fetch_msg(queue, msginfo->msgnum);
962 gboolean queued_removed = FALSE;
963 if (procmsg_send_message_queue_full(file,
964 !procmsg_is_last_for_account(queue, msginfo, elem),
965 errstr, queue, msginfo->msgnum, &queued_removed) < 0) {
966 g_warning("Sending queued message %d failed.\n",
972 folder_item_remove_msg(queue, msginfo->msgnum);
977 /* FIXME: supposedly if only one message is locked, and queue
978 * is being flushed, the following free says something like
979 * "freeing msg ## in folder (nil)". */
980 procmsg_msginfo_free(msginfo);
983 g_slist_free(sorted_list);
984 folder_item_scan(queue);
986 if (queue->node && queue->node->children) {
987 node = queue->node->children;
988 while (node != NULL) {
991 send_queue_lock = FALSE;
992 res = procmsg_send_queue(FOLDER_ITEM(node->data), save_msgs, errstr);
993 send_queue_lock = TRUE;
1001 procmsg_queue_unlock();
1003 toolbar_main_set_sensitive(mainwindow_get_mainwindow());
1005 return (err != 0 ? -err : sent);
1008 gboolean procmsg_is_sending(void)
1010 return send_queue_lock;
1014 *\brief Determine if a queue folder is empty
1016 *\param queue Queue folder to process
1018 *\return TRUE if the queue folder is empty, otherwise return FALSE
1020 gboolean procmsg_queue_is_empty(FolderItem *queue)
1023 gboolean res = FALSE;
1025 queue = folder_get_default_queue();
1026 cm_return_val_if_fail(queue != NULL, TRUE);
1028 folder_item_scan(queue);
1029 list = folder_item_get_msg_list(queue);
1030 res = (list == NULL);
1031 procmsg_msg_list_free(list);
1035 if (queue->node && queue->node->children) {
1036 node = queue->node->children;
1037 while (node != NULL) {
1039 if (!procmsg_queue_is_empty(FOLDER_ITEM(node->data)))
1048 gint procmsg_remove_special_headers(const gchar *in, const gchar *out)
1051 gchar buf[BUFFSIZE];
1053 if ((fp = g_fopen(in, "rb")) == NULL) {
1054 FILE_OP_ERROR(in, "fopen");
1057 if ((outfp = g_fopen(out, "wb")) == NULL) {
1058 FILE_OP_ERROR(out, "fopen");
1062 while (fgets(buf, sizeof(buf), fp) != NULL) {
1064 if ((!strncmp(buf, "X-Claws-End-Special-Headers: 1",
1065 strlen("X-Claws-End-Special-Headers:"))) ||
1066 (!strncmp(buf, "X-Sylpheed-End-Special-Headers: 1",
1067 strlen("X-Sylpheed-End-Special-Headers:"))))
1070 if (buf[0] == '\r' || buf[0] == '\n') break;
1071 /* from other mailers */
1072 if (!strncmp(buf, "Date: ", 6)
1073 || !strncmp(buf, "To: ", 4)
1074 || !strncmp(buf, "From: ", 6)
1075 || !strncmp(buf, "Subject: ", 9)) {
1080 while (fgets(buf, sizeof(buf), fp) != NULL)
1087 static gint procmsg_save_to_outbox(FolderItem *outbox, const gchar *file,
1091 MsgInfo *msginfo, *tmp_msginfo;
1092 MsgFlags flag = {0, 0};
1094 debug_print("saving sent message...\n");
1097 outbox = folder_get_default_outbox();
1098 cm_return_val_if_fail(outbox != NULL, -1);
1100 /* remove queueing headers */
1102 gchar tmp[MAXPATHLEN + 1];
1104 g_snprintf(tmp, sizeof(tmp), "%s%ctmpmsg.out.%08x",
1105 get_rc_dir(), G_DIR_SEPARATOR, (guint) rand());
1107 if (procmsg_remove_special_headers(file, tmp) !=0)
1110 folder_item_scan(outbox);
1111 if ((num = folder_item_add_msg(outbox, tmp, &flag, TRUE)) < 0) {
1112 g_warning("can't save message\n");
1117 folder_item_scan(outbox);
1118 if ((num = folder_item_add_msg
1119 (outbox, file, &flag, FALSE)) < 0) {
1120 g_warning("can't save message\n");
1124 msginfo = folder_item_get_msginfo(outbox, num); /* refcnt++ */
1125 tmp_msginfo = procmsg_msginfo_get_full_info(msginfo); /* refcnt++ */
1126 if (msginfo != NULL) {
1127 procmsg_msginfo_unset_flags(msginfo, ~0, 0);
1128 procmsg_msginfo_free(msginfo); /* refcnt-- */
1129 /* tmp_msginfo == msginfo */
1130 if (tmp_msginfo && msginfo->extradata &&
1131 (msginfo->extradata->dispositionnotificationto ||
1132 msginfo->extradata->returnreceiptto)) {
1133 procmsg_msginfo_set_flags(msginfo, MSG_RETRCPT_SENT, 0);
1135 procmsg_msginfo_free(tmp_msginfo); /* refcnt-- */
1141 void procmsg_print_message(MsgInfo *msginfo, const gchar *cmdline)
1143 static const gchar *def_cmd = "lpr %s";
1144 static guint id = 0;
1150 cm_return_if_fail(msginfo);
1152 if (procmime_msginfo_is_encrypted(msginfo))
1153 tmpfp = procmime_get_first_encrypted_text_content(msginfo);
1155 tmpfp = procmime_get_first_text_content(msginfo);
1156 if (tmpfp == NULL) {
1157 g_warning("Can't get text part\n");
1161 prtmp = g_strdup_printf("%s%cprinttmp.%08x",
1162 get_mime_tmp_dir(), G_DIR_SEPARATOR, id++);
1164 if ((prfp = g_fopen(prtmp, "wb")) == NULL) {
1165 FILE_OP_ERROR(prtmp, "fopen");
1171 if (msginfo->date) r = fprintf(prfp, "Date: %s\n", msginfo->date);
1172 if (msginfo->from) r = fprintf(prfp, "From: %s\n", msginfo->from);
1173 if (msginfo->to) r = fprintf(prfp, "To: %s\n", msginfo->to);
1174 if (msginfo->cc) r = fprintf(prfp, "Cc: %s\n", msginfo->cc);
1175 if (msginfo->newsgroups)
1176 r = fprintf(prfp, "Newsgroups: %s\n", msginfo->newsgroups);
1177 if (msginfo->subject) r = fprintf(prfp, "Subject: %s\n", msginfo->subject);
1180 while (fgets(buf, sizeof(buf), tmpfp) != NULL)
1181 r = fputs(buf, prfp);
1186 if (cmdline && (p = strchr(cmdline, '%')) && *(p + 1) == 's' &&
1187 !strchr(p + 2, '%'))
1188 g_snprintf(buf, sizeof(buf) - 1, cmdline, prtmp);
1191 g_warning("Print command-line is invalid: '%s'\n",
1193 g_snprintf(buf, sizeof(buf) - 1, def_cmd, prtmp);
1199 if (buf[strlen(buf) - 1] != '&')
1200 strncat(buf, "&", sizeof(buf) - strlen(buf) - 1);
1201 if (system(buf) == -1)
1202 g_warning("system(%s) failed.", buf);
1205 MsgInfo *procmsg_msginfo_new_ref(MsgInfo *msginfo)
1212 MsgInfo *procmsg_msginfo_new(void)
1214 MsgInfo *newmsginfo;
1216 newmsginfo = g_new0(MsgInfo, 1);
1217 newmsginfo->refcnt = 1;
1222 MsgInfo *procmsg_msginfo_copy(MsgInfo *msginfo)
1224 MsgInfo *newmsginfo;
1227 if (msginfo == NULL) return NULL;
1229 newmsginfo = g_new0(MsgInfo, 1);
1231 newmsginfo->refcnt = 1;
1233 #define MEMBCOPY(mmb) newmsginfo->mmb = msginfo->mmb
1234 #define MEMBDUP(mmb) newmsginfo->mmb = msginfo->mmb ? \
1235 g_strdup(msginfo->mmb) : NULL
1250 MEMBDUP(newsgroups);
1257 MEMBCOPY(to_folder);
1259 if (msginfo->extradata) {
1260 newmsginfo->extradata = g_new0(MsgInfoExtraData, 1);
1261 MEMBDUP(extradata->face);
1262 MEMBDUP(extradata->xface);
1263 MEMBDUP(extradata->dispositionnotificationto);
1264 MEMBDUP(extradata->returnreceiptto);
1265 MEMBDUP(extradata->partial_recv);
1266 MEMBDUP(extradata->account_server);
1267 MEMBDUP(extradata->account_login);
1268 MEMBDUP(extradata->list_post);
1269 MEMBDUP(extradata->list_subscribe);
1270 MEMBDUP(extradata->list_unsubscribe);
1271 MEMBDUP(extradata->list_help);
1272 MEMBDUP(extradata->list_archive);
1273 MEMBDUP(extradata->list_owner);
1276 refs = msginfo->references;
1277 for (refs = msginfo->references; refs != NULL; refs = refs->next) {
1278 newmsginfo->references = g_slist_prepend
1279 (newmsginfo->references, g_strdup(refs->data));
1281 newmsginfo->references = g_slist_reverse(newmsginfo->references);
1284 MEMBDUP(plaintext_file);
1289 MsgInfo *procmsg_msginfo_get_full_info_from_file(MsgInfo *msginfo, const gchar *file)
1291 MsgInfo *full_msginfo;
1293 if (msginfo == NULL) return NULL;
1295 if (!file || !is_file_exist(file)) {
1296 g_warning("procmsg_msginfo_get_full_info_from_file(): can't get message file.\n");
1300 full_msginfo = procheader_parse_file(file, msginfo->flags, TRUE, FALSE);
1301 if (!full_msginfo) return NULL;
1303 msginfo->total_size = full_msginfo->total_size;
1304 msginfo->planned_download = full_msginfo->planned_download;
1306 if (full_msginfo->extradata) {
1307 if (!msginfo->extradata)
1308 msginfo->extradata = g_new0(MsgInfoExtraData, 1);
1309 if (!msginfo->extradata->list_post)
1310 msginfo->extradata->list_post = g_strdup(full_msginfo->extradata->list_post);
1311 if (!msginfo->extradata->list_subscribe)
1312 msginfo->extradata->list_subscribe = g_strdup(full_msginfo->extradata->list_subscribe);
1313 if (!msginfo->extradata->list_unsubscribe)
1314 msginfo->extradata->list_unsubscribe = g_strdup(full_msginfo->extradata->list_unsubscribe);
1315 if (!msginfo->extradata->list_help)
1316 msginfo->extradata->list_help = g_strdup(full_msginfo->extradata->list_help);
1317 if (!msginfo->extradata->list_archive)
1318 msginfo->extradata->list_archive= g_strdup(full_msginfo->extradata->list_archive);
1319 if (!msginfo->extradata->list_owner)
1320 msginfo->extradata->list_owner = g_strdup(full_msginfo->extradata->list_owner);
1321 if (!msginfo->extradata->xface)
1322 msginfo->extradata->xface = g_strdup(full_msginfo->extradata->xface);
1323 if (!msginfo->extradata->face)
1324 msginfo->extradata->face = g_strdup(full_msginfo->extradata->face);
1325 if (!msginfo->extradata->dispositionnotificationto)
1326 msginfo->extradata->dispositionnotificationto =
1327 g_strdup(full_msginfo->extradata->dispositionnotificationto);
1328 if (!msginfo->extradata->returnreceiptto)
1329 msginfo->extradata->returnreceiptto = g_strdup
1330 (full_msginfo->extradata->returnreceiptto);
1331 if (!msginfo->extradata->partial_recv && full_msginfo->extradata->partial_recv)
1332 msginfo->extradata->partial_recv = g_strdup
1333 (full_msginfo->extradata->partial_recv);
1334 if (!msginfo->extradata->account_server && full_msginfo->extradata->account_server)
1335 msginfo->extradata->account_server = g_strdup
1336 (full_msginfo->extradata->account_server);
1337 if (!msginfo->extradata->account_login && full_msginfo->extradata->account_login)
1338 msginfo->extradata->account_login = g_strdup
1339 (full_msginfo->extradata->account_login);
1341 procmsg_msginfo_free(full_msginfo);
1343 return procmsg_msginfo_new_ref(msginfo);
1346 MsgInfo *procmsg_msginfo_get_full_info(MsgInfo *msginfo)
1348 MsgInfo *full_msginfo;
1351 if (msginfo == NULL) return NULL;
1353 file = procmsg_get_message_file_path(msginfo);
1354 if (!file || !is_file_exist(file)) {
1356 file = procmsg_get_message_file(msginfo);
1358 if (!file || !is_file_exist(file)) {
1359 g_warning("procmsg_msginfo_get_full_info(): can't get message file.\n");
1363 full_msginfo = procmsg_msginfo_get_full_info_from_file(msginfo, file);
1365 return full_msginfo;
1368 void procmsg_msginfo_free(MsgInfo *msginfo)
1370 if (msginfo == NULL) return;
1373 if (msginfo->refcnt > 0)
1376 if (msginfo->to_folder) {
1377 msginfo->to_folder->op_count--;
1378 folder_item_update(msginfo->to_folder, F_ITEM_UPDATE_MSGCNT);
1381 g_free(msginfo->fromspace);
1383 g_free(msginfo->fromname);
1385 g_free(msginfo->date);
1386 g_free(msginfo->from);
1387 g_free(msginfo->to);
1388 g_free(msginfo->cc);
1389 g_free(msginfo->newsgroups);
1390 g_free(msginfo->subject);
1391 g_free(msginfo->msgid);
1392 g_free(msginfo->inreplyto);
1393 g_free(msginfo->xref);
1395 if (msginfo->extradata) {
1396 g_free(msginfo->extradata->returnreceiptto);
1397 g_free(msginfo->extradata->dispositionnotificationto);
1398 g_free(msginfo->extradata->xface);
1399 g_free(msginfo->extradata->face);
1400 g_free(msginfo->extradata->list_post);
1401 g_free(msginfo->extradata->list_subscribe);
1402 g_free(msginfo->extradata->list_unsubscribe);
1403 g_free(msginfo->extradata->list_help);
1404 g_free(msginfo->extradata->list_archive);
1405 g_free(msginfo->extradata->list_owner);
1406 g_free(msginfo->extradata->partial_recv);
1407 g_free(msginfo->extradata->account_server);
1408 g_free(msginfo->extradata->account_login);
1409 g_free(msginfo->extradata);
1411 slist_free_strings(msginfo->references);
1412 g_slist_free(msginfo->references);
1413 g_slist_free(msginfo->tags);
1415 g_free(msginfo->plaintext_file);
1420 guint procmsg_msginfo_memusage(MsgInfo *msginfo)
1425 memusage += sizeof(MsgInfo);
1426 if (msginfo->fromname)
1427 memusage += strlen(msginfo->fromname);
1429 memusage += strlen(msginfo->date);
1431 memusage += strlen(msginfo->from);
1433 memusage += strlen(msginfo->to);
1435 memusage += strlen(msginfo->cc);
1436 if (msginfo->newsgroups)
1437 memusage += strlen(msginfo->newsgroups);
1438 if (msginfo->subject)
1439 memusage += strlen(msginfo->subject);
1441 memusage += strlen(msginfo->msgid);
1442 if (msginfo->inreplyto)
1443 memusage += strlen(msginfo->inreplyto);
1445 for (tmp = msginfo->references; tmp; tmp=tmp->next) {
1446 gchar *r = (gchar *)tmp->data;
1447 memusage += r?strlen(r):0 + sizeof(GSList);
1449 if (msginfo->fromspace)
1450 memusage += strlen(msginfo->fromspace);
1452 for (tmp = msginfo->tags; tmp; tmp=tmp->next) {
1453 memusage += sizeof(GSList);
1455 if (msginfo->extradata) {
1456 memusage += sizeof(MsgInfoExtraData);
1457 if (msginfo->extradata->xface)
1458 memusage += strlen(msginfo->extradata->xface);
1459 if (msginfo->extradata->face)
1460 memusage += strlen(msginfo->extradata->face);
1461 if (msginfo->extradata->dispositionnotificationto)
1462 memusage += strlen(msginfo->extradata->dispositionnotificationto);
1463 if (msginfo->extradata->returnreceiptto)
1464 memusage += strlen(msginfo->extradata->returnreceiptto);
1466 if (msginfo->extradata->partial_recv)
1467 memusage += strlen(msginfo->extradata->partial_recv);
1468 if (msginfo->extradata->account_server)
1469 memusage += strlen(msginfo->extradata->account_server);
1470 if (msginfo->extradata->account_login)
1471 memusage += strlen(msginfo->extradata->account_login);
1473 if (msginfo->extradata->list_post)
1474 memusage += strlen(msginfo->extradata->list_post);
1475 if (msginfo->extradata->list_subscribe)
1476 memusage += strlen(msginfo->extradata->list_subscribe);
1477 if (msginfo->extradata->list_unsubscribe)
1478 memusage += strlen(msginfo->extradata->list_unsubscribe);
1479 if (msginfo->extradata->list_help)
1480 memusage += strlen(msginfo->extradata->list_help);
1481 if (msginfo->extradata->list_archive)
1482 memusage += strlen(msginfo->extradata->list_archive);
1483 if (msginfo->extradata->list_owner)
1484 memusage += strlen(msginfo->extradata->list_owner);
1489 static gint procmsg_send_message_queue_full(const gchar *file, gboolean keep_session, gchar **errstr,
1490 FolderItem *queue, gint msgnum, gboolean *queued_removed)
1492 static HeaderEntry qentry[] = {{"S:", NULL, FALSE},
1493 {"SSV:", NULL, FALSE},
1494 {"R:", NULL, FALSE},
1495 {"NG:", NULL, FALSE},
1496 {"MAID:", NULL, FALSE},
1497 {"NAID:", NULL, FALSE},
1498 {"SCF:", NULL, FALSE},
1499 {"RMID:", NULL, FALSE},
1500 {"FMID:", NULL, FALSE},
1501 {"X-Claws-Privacy-System:", NULL, FALSE},
1502 {"X-Claws-Encrypt:", NULL, FALSE},
1503 {"X-Claws-Encrypt-Data:", NULL, FALSE},
1504 {"X-Claws-End-Special-Headers:", NULL, FALSE},
1505 {"X-Sylpheed-Privacy-System:", NULL, FALSE},
1506 {"X-Sylpheed-Encrypt:", NULL, FALSE},
1507 {"X-Sylpheed-Encrypt-Data:", NULL, FALSE},
1508 {"X-Sylpheed-End-Special-Headers:", NULL, FALSE},
1509 {NULL, NULL, FALSE}};
1512 gint mailval = 0, newsval = 0;
1514 gchar *smtpserver = NULL;
1515 GSList *to_list = NULL;
1516 GSList *newsgroup_list = NULL;
1517 gchar *savecopyfolder = NULL;
1518 gchar *replymessageid = NULL;
1519 gchar *fwdmessageid = NULL;
1520 gchar *privacy_system = NULL;
1521 gboolean encrypt = FALSE;
1522 gchar *encrypt_data = NULL;
1523 gchar buf[BUFFSIZE];
1525 PrefsAccount *mailac = NULL, *newsac = NULL;
1526 gboolean save_clear_text = TRUE;
1527 gchar *tmp_enc_file = NULL;
1529 cm_return_val_if_fail(file != NULL, -1);
1531 if ((fp = g_fopen(file, "rb")) == NULL) {
1532 FILE_OP_ERROR(file, "fopen");
1534 if (*errstr) g_free(*errstr);
1535 *errstr = g_strdup_printf(_("Couldn't open file %s."), file);
1540 while ((hnum = procheader_get_one_field(buf, sizeof(buf), fp, qentry))
1542 gchar *p = buf + strlen(qentry[hnum].name);
1550 if (smtpserver == NULL)
1551 smtpserver = g_strdup(p);
1554 to_list = address_list_append(to_list, p);
1557 newsgroup_list = newsgroup_list_append(newsgroup_list, p);
1559 case Q_MAIL_ACCOUNT_ID:
1560 mailac = account_find_from_id(atoi(p));
1562 case Q_NEWS_ACCOUNT_ID:
1563 newsac = account_find_from_id(atoi(p));
1565 case Q_SAVE_COPY_FOLDER:
1566 if (savecopyfolder == NULL)
1567 savecopyfolder = g_strdup(p);
1569 case Q_REPLY_MESSAGE_ID:
1570 if (replymessageid == NULL)
1571 replymessageid = g_strdup(p);
1573 case Q_FWD_MESSAGE_ID:
1574 if (fwdmessageid == NULL)
1575 fwdmessageid = g_strdup(p);
1577 case Q_PRIVACY_SYSTEM:
1578 case Q_PRIVACY_SYSTEM_OLD:
1579 if (privacy_system == NULL)
1580 privacy_system = g_strdup(p);
1587 case Q_ENCRYPT_DATA:
1588 case Q_ENCRYPT_DATA_OLD:
1589 if (encrypt_data == NULL)
1590 encrypt_data = g_strdup(p);
1593 case Q_CLAWS_HDRS_OLD:
1594 /* end of special headers reached */
1595 goto send_mail; /* can't "break;break;" */
1599 filepos = ftell(fp);
1604 if (mailac && mailac->save_encrypted_as_clear_text
1605 && !mailac->encrypt_to_self)
1606 save_clear_text = TRUE;
1608 save_clear_text = FALSE;
1613 mimeinfo = procmime_scan_queue_file(file);
1614 if (!privacy_encrypt(privacy_system, mimeinfo, encrypt_data)
1615 || (fp = my_tmpfile()) == NULL
1616 || procmime_write_mimeinfo(mimeinfo, fp) < 0) {
1619 procmime_mimeinfo_free_all(mimeinfo);
1622 slist_free_strings(to_list);
1623 g_slist_free(to_list);
1624 slist_free_strings(newsgroup_list);
1625 g_slist_free(newsgroup_list);
1626 g_free(savecopyfolder);
1627 g_free(replymessageid);
1628 g_free(fwdmessageid);
1629 g_free(privacy_system);
1630 g_free(encrypt_data);
1632 if (*errstr) g_free(*errstr);
1633 *errstr = g_strdup_printf(_("Couldn't encrypt the email: %s"),
1634 privacy_get_error());
1640 if (!save_clear_text) {
1641 gchar *content = NULL;
1642 FILE *tmpfp = get_tmpfile_in_dir(get_mime_tmp_dir(), &tmp_enc_file);
1646 content = file_read_stream_to_str(fp);
1649 str_write_to_file(content, tmp_enc_file);
1652 g_warning("couldn't get tempfile\n");
1656 procmime_mimeinfo_free_all(mimeinfo);
1662 debug_print("Sending message by mail\n");
1665 if (*errstr) g_free(*errstr);
1666 *errstr = g_strdup_printf(_("Queued message header is broken."));
1669 } else if (mailac && mailac->use_mail_command &&
1670 mailac->mail_command && (* mailac->mail_command)) {
1671 mailval = send_message_local(mailac->mail_command, fp);
1674 mailac = account_find_from_smtp_server(from, smtpserver);
1676 g_warning("Account not found. "
1677 "Using current account...\n");
1678 mailac = cur_account;
1683 mailval = send_message_smtp_full(mailac, to_list, fp, keep_session);
1684 if (mailval == -1 && errstr) {
1685 if (*errstr) g_free(*errstr);
1686 *errstr = g_strdup_printf(_("An error happened during SMTP session."));
1689 PrefsAccount tmp_ac;
1691 g_warning("Account not found.\n");
1693 memset(&tmp_ac, 0, sizeof(PrefsAccount));
1694 tmp_ac.address = from;
1695 tmp_ac.smtp_server = smtpserver;
1696 tmp_ac.smtpport = SMTP_PORT;
1697 mailval = send_message_smtp(&tmp_ac, to_list, fp);
1698 if (mailval == -1 && errstr) {
1699 if (*errstr) g_free(*errstr);
1700 *errstr = g_strdup_printf(_("No specific account has been found to "
1701 "send, and an error happened during SMTP session."));
1705 } else if (!to_list && !newsgroup_list) {
1707 if (*errstr) g_free(*errstr);
1708 *errstr = g_strdup(_("Couldn't determine sending informations. "
1709 "Maybe the email hasn't been generated by Claws Mail."));
1714 fseek(fp, filepos, SEEK_SET);
1715 if (newsgroup_list && newsac && (mailval == 0)) {
1720 /* write to temporary file */
1721 tmp = g_strdup_printf("%s%cnntp%p", get_tmp_dir(),
1722 G_DIR_SEPARATOR, file);
1723 if ((tmpfp = g_fopen(tmp, "wb")) == NULL) {
1724 FILE_OP_ERROR(tmp, "fopen");
1726 alertpanel_error(_("Couldn't create temporary file for news sending."));
1728 if (change_file_mode_rw(tmpfp, tmp) < 0) {
1729 FILE_OP_ERROR(tmp, "chmod");
1730 g_warning("can't change file mode\n");
1733 while ((newsval == 0) && fgets(buf, sizeof(buf), fp) != NULL) {
1734 if (fputs(buf, tmpfp) == EOF) {
1735 FILE_OP_ERROR(tmp, "fputs");
1738 if (*errstr) g_free(*errstr);
1739 *errstr = g_strdup_printf(_("Error when writing temporary file for news sending."));
1746 debug_print("Sending message by news\n");
1748 folder = FOLDER(newsac->folder);
1750 newsval = news_post(folder, tmp);
1751 if (newsval < 0 && errstr) {
1752 if (*errstr) g_free(*errstr);
1753 *errstr = g_strdup_printf(_("Error occurred while posting the message to %s."),
1754 newsac->nntp_server);
1764 /* update session statistics */
1765 if (mailval == 0 && newsval == 0) {
1766 /* update session stats */
1768 session_stats.replied++;
1769 else if (fwdmessageid)
1770 session_stats.forwarded++;
1772 session_stats.sent++;
1775 /* save message to outbox */
1776 if (mailval == 0 && newsval == 0 && savecopyfolder) {
1779 debug_print("saving sent message...\n");
1781 outbox = folder_find_item_from_identifier(savecopyfolder);
1783 outbox = folder_get_default_outbox();
1785 if (save_clear_text || tmp_enc_file == NULL) {
1786 gboolean saved = FALSE;
1787 *queued_removed = FALSE;
1788 if (queue && msgnum > 0) {
1789 MsgInfo *queued_mail = folder_item_get_msginfo(queue, msgnum);
1790 if (folder_item_move_msg(outbox, queued_mail) >= 0) {
1791 debug_print("moved queued mail %d to sent folder\n", msgnum);
1793 *queued_removed = TRUE;
1794 } else if (folder_item_copy_msg(outbox, queued_mail) >= 0) {
1795 debug_print("copied queued mail %d to sent folder\n", msgnum);
1798 procmsg_msginfo_free(queued_mail);
1801 debug_print("resaving clear text queued mail to sent folder\n");
1802 procmsg_save_to_outbox(outbox, file, TRUE);
1805 debug_print("saving encrpyted queued mail to sent folder\n");
1806 procmsg_save_to_outbox(outbox, tmp_enc_file, FALSE);
1810 if (tmp_enc_file != NULL) {
1811 claws_unlink(tmp_enc_file);
1813 tmp_enc_file = NULL;
1816 if (replymessageid != NULL || fwdmessageid != NULL) {
1820 if (replymessageid != NULL)
1821 tokens = g_strsplit(replymessageid, "\t", 0);
1823 tokens = g_strsplit(fwdmessageid, "\t", 0);
1824 item = folder_find_item_from_identifier(tokens[0]);
1826 /* check if queued message has valid folder and message id */
1827 if (item != NULL && tokens[2] != NULL) {
1830 msginfo = folder_item_get_msginfo(item, atoi(tokens[1]));
1832 /* check if referring message exists and has a message id */
1833 if ((msginfo != NULL) &&
1834 (msginfo->msgid != NULL) &&
1835 (strcmp(msginfo->msgid, tokens[2]) != 0)) {
1836 procmsg_msginfo_free(msginfo);
1840 if (msginfo == NULL) {
1841 msginfo = folder_item_get_msginfo_by_msgid(item, tokens[2]);
1844 if (msginfo != NULL) {
1845 if (replymessageid != NULL) {
1846 MsgPermFlags to_unset = 0;
1848 if (prefs_common.mark_as_read_on_new_window)
1849 to_unset = (MSG_NEW|MSG_UNREAD);
1851 procmsg_msginfo_unset_flags(msginfo, to_unset, 0);
1852 procmsg_msginfo_set_flags(msginfo, MSG_REPLIED, 0);
1854 procmsg_msginfo_set_flags(msginfo, MSG_FORWARDED, 0);
1856 procmsg_msginfo_free(msginfo);
1864 slist_free_strings(to_list);
1865 g_slist_free(to_list);
1866 slist_free_strings(newsgroup_list);
1867 g_slist_free(newsgroup_list);
1868 g_free(savecopyfolder);
1869 g_free(replymessageid);
1870 g_free(fwdmessageid);
1871 g_free(privacy_system);
1872 g_free(encrypt_data);
1874 return (newsval != 0 ? newsval : mailval);
1877 gint procmsg_send_message_queue(const gchar *file, gchar **errstr, FolderItem *queue, gint msgnum, gboolean *queued_removed)
1879 gint result = procmsg_send_message_queue_full(file, FALSE, errstr, queue, msgnum, queued_removed);
1880 toolbar_main_set_sensitive(mainwindow_get_mainwindow());
1884 gint procmsg_send_message_queue_with_lock(const gchar *file, gchar **errstr, FolderItem *queue, gint msgnum, gboolean *queued_removed)
1887 if (procmsg_queue_lock(errstr)) {
1888 val = procmsg_send_message_queue(file, errstr, queue, msgnum, queued_removed);
1889 procmsg_queue_unlock();
1895 static void update_folder_msg_counts(FolderItem *item, MsgInfo *msginfo, MsgPermFlags old_flags)
1897 MsgPermFlags new_flags = msginfo->flags.perm_flags;
1900 if (!(old_flags & MSG_NEW) && (new_flags & MSG_NEW)) {
1904 if ((old_flags & MSG_NEW) && !(new_flags & MSG_NEW)) {
1909 if (!(old_flags & MSG_UNREAD) && (new_flags & MSG_UNREAD)) {
1910 item->unread_msgs++;
1911 if (procmsg_msg_has_marked_parent(msginfo))
1912 item->unreadmarked_msgs++;
1915 if ((old_flags & MSG_UNREAD) && !(new_flags & MSG_UNREAD)) {
1916 item->unread_msgs--;
1917 if (procmsg_msg_has_marked_parent(msginfo))
1918 item->unreadmarked_msgs--;
1922 if (!(old_flags & MSG_MARKED) && (new_flags & MSG_MARKED)) {
1923 procmsg_update_unread_children(msginfo, TRUE);
1924 item->marked_msgs++;
1927 if ((old_flags & MSG_MARKED) && !(new_flags & MSG_MARKED)) {
1928 procmsg_update_unread_children(msginfo, FALSE);
1929 item->marked_msgs--;
1932 if (!(old_flags & MSG_REPLIED) && (new_flags & MSG_REPLIED)) {
1933 item->replied_msgs++;
1936 if ((old_flags & MSG_REPLIED) && !(new_flags & MSG_REPLIED)) {
1937 item->replied_msgs--;
1940 if (!(old_flags & MSG_FORWARDED) && (new_flags & MSG_FORWARDED)) {
1941 item->forwarded_msgs++;
1944 if ((old_flags & MSG_FORWARDED) && !(new_flags & MSG_FORWARDED)) {
1945 item->forwarded_msgs--;
1948 if (!(old_flags & MSG_LOCKED) && (new_flags & MSG_LOCKED)) {
1949 item->locked_msgs++;
1952 if ((old_flags & MSG_LOCKED) && !(new_flags & MSG_LOCKED)) {
1953 item->locked_msgs--;
1956 if ((old_flags & MSG_IGNORE_THREAD) && !(new_flags & MSG_IGNORE_THREAD)) {
1957 item->ignored_msgs--;
1960 if (!(old_flags & MSG_IGNORE_THREAD) && (new_flags & MSG_IGNORE_THREAD)) {
1961 item->ignored_msgs++;
1964 if ((old_flags & MSG_WATCH_THREAD) && !(new_flags & MSG_WATCH_THREAD)) {
1965 item->watched_msgs--;
1968 if (!(old_flags & MSG_WATCH_THREAD) && (new_flags & MSG_WATCH_THREAD)) {
1969 item->watched_msgs++;
1973 void procmsg_msginfo_set_flags(MsgInfo *msginfo, MsgPermFlags perm_flags, MsgTmpFlags tmp_flags)
1976 MsgInfoUpdate msginfo_update;
1977 MsgPermFlags perm_flags_new, perm_flags_old;
1978 MsgTmpFlags tmp_flags_old;
1980 cm_return_if_fail(msginfo != NULL);
1981 item = msginfo->folder;
1982 cm_return_if_fail(item != NULL);
1984 debug_print("Setting flags for message %d in folder %s\n", msginfo->msgnum, item->path);
1986 /* Perm Flags handling */
1987 perm_flags_old = msginfo->flags.perm_flags;
1988 perm_flags_new = msginfo->flags.perm_flags | perm_flags;
1989 if ((perm_flags & MSG_IGNORE_THREAD) || (perm_flags_old & MSG_IGNORE_THREAD)) {
1990 perm_flags_new &= ~(MSG_NEW | MSG_UNREAD);
1992 if ((perm_flags & MSG_WATCH_THREAD) || (perm_flags_old & MSG_WATCH_THREAD)) {
1993 perm_flags_new &= ~(MSG_IGNORE_THREAD);
1996 if (perm_flags_old != perm_flags_new) {
1997 folder_item_change_msg_flags(msginfo->folder, msginfo, perm_flags_new);
1999 update_folder_msg_counts(item, msginfo, perm_flags_old);
2000 summary_update_unread(mainwindow_get_mainwindow()->summaryview, NULL);
2003 /* Tmp flags handling */
2004 tmp_flags_old = msginfo->flags.tmp_flags;
2005 msginfo->flags.tmp_flags |= tmp_flags;
2007 /* update notification */
2008 if ((perm_flags_old != perm_flags_new) || (tmp_flags_old != msginfo->flags.tmp_flags)) {
2009 msginfo_update.msginfo = msginfo;
2010 msginfo_update.flags = MSGINFO_UPDATE_FLAGS;
2011 hooks_invoke(MSGINFO_UPDATE_HOOKLIST, &msginfo_update);
2012 folder_item_update(msginfo->folder, F_ITEM_UPDATE_MSGCNT);
2016 void procmsg_msginfo_unset_flags(MsgInfo *msginfo, MsgPermFlags perm_flags, MsgTmpFlags tmp_flags)
2019 MsgInfoUpdate msginfo_update;
2020 MsgPermFlags perm_flags_new, perm_flags_old;
2021 MsgTmpFlags tmp_flags_old;
2023 cm_return_if_fail(msginfo != NULL);
2024 item = msginfo->folder;
2025 cm_return_if_fail(item != NULL);
2027 debug_print("Unsetting flags for message %d in folder %s\n", msginfo->msgnum, item->path);
2029 /* Perm Flags handling */
2030 perm_flags_old = msginfo->flags.perm_flags;
2031 perm_flags_new = msginfo->flags.perm_flags & ~perm_flags;
2033 if (perm_flags_old != perm_flags_new) {
2034 folder_item_change_msg_flags(msginfo->folder, msginfo, perm_flags_new);
2036 update_folder_msg_counts(item, msginfo, perm_flags_old);
2039 /* Tmp flags hanlding */
2040 tmp_flags_old = msginfo->flags.tmp_flags;
2041 msginfo->flags.tmp_flags &= ~tmp_flags;
2043 /* update notification */
2044 if ((perm_flags_old != perm_flags_new) || (tmp_flags_old != msginfo->flags.tmp_flags)) {
2045 msginfo_update.msginfo = msginfo;
2046 msginfo_update.flags = MSGINFO_UPDATE_FLAGS;
2047 hooks_invoke(MSGINFO_UPDATE_HOOKLIST, &msginfo_update);
2048 folder_item_update(msginfo->folder, F_ITEM_UPDATE_MSGCNT);
2052 void procmsg_msginfo_change_flags(MsgInfo *msginfo,
2053 MsgPermFlags add_perm_flags, MsgTmpFlags add_tmp_flags,
2054 MsgPermFlags rem_perm_flags, MsgTmpFlags rem_tmp_flags)
2057 MsgInfoUpdate msginfo_update;
2058 MsgPermFlags perm_flags_new, perm_flags_old;
2059 MsgTmpFlags tmp_flags_old;
2061 cm_return_if_fail(msginfo != NULL);
2062 item = msginfo->folder;
2063 cm_return_if_fail(item != NULL);
2065 debug_print("Changing flags for message %d in folder %s\n", msginfo->msgnum, item->path);
2067 /* Perm Flags handling */
2068 perm_flags_old = msginfo->flags.perm_flags;
2069 perm_flags_new = (msginfo->flags.perm_flags & ~rem_perm_flags) | add_perm_flags;
2070 if ((add_perm_flags & MSG_IGNORE_THREAD) || (perm_flags_old & MSG_IGNORE_THREAD)) {
2071 perm_flags_new &= ~(MSG_NEW | MSG_UNREAD);
2073 if ((add_perm_flags & MSG_WATCH_THREAD) || (perm_flags_old & MSG_WATCH_THREAD)) {
2074 perm_flags_new &= ~(MSG_IGNORE_THREAD);
2077 if (perm_flags_old != perm_flags_new) {
2078 folder_item_change_msg_flags(msginfo->folder, msginfo, perm_flags_new);
2080 update_folder_msg_counts(item, msginfo, perm_flags_old);
2084 /* Tmp flags handling */
2085 tmp_flags_old = msginfo->flags.tmp_flags;
2086 msginfo->flags.tmp_flags &= ~rem_tmp_flags;
2087 msginfo->flags.tmp_flags |= add_tmp_flags;
2089 /* update notification */
2090 if ((perm_flags_old != perm_flags_new) || (tmp_flags_old != msginfo->flags.tmp_flags)) {
2091 msginfo_update.msginfo = msginfo;
2092 msginfo_update.flags = MSGINFO_UPDATE_FLAGS;
2093 hooks_invoke(MSGINFO_UPDATE_HOOKLIST, &msginfo_update);
2094 folder_item_update(msginfo->folder, F_ITEM_UPDATE_MSGCNT);
2099 *\brief check for flags (e.g. mark) in prior msgs of current thread
2101 *\param info Current message
2102 *\param perm_flags Flags to be checked
2103 *\param parentmsgs Hash of prior msgs to avoid loops
2105 *\return gboolean TRUE if perm_flags are found
2107 static gboolean procmsg_msg_has_flagged_parent_real(MsgInfo *info,
2108 MsgPermFlags perm_flags, GHashTable *parentmsgs)
2112 cm_return_val_if_fail(info != NULL, FALSE);
2114 if (info != NULL && info->folder != NULL && info->inreplyto != NULL) {
2115 tmp = folder_item_get_msginfo_by_msgid(info->folder,
2117 if (tmp && (tmp->flags.perm_flags & perm_flags)) {
2118 procmsg_msginfo_free(tmp);
2120 } else if (tmp != NULL) {
2123 if (g_hash_table_lookup(parentmsgs, info)) {
2124 debug_print("loop detected: %d\n",
2128 g_hash_table_insert(parentmsgs, info, "1");
2129 result = procmsg_msg_has_flagged_parent_real(
2130 tmp, perm_flags, parentmsgs);
2132 procmsg_msginfo_free(tmp);
2142 *\brief Callback for cleaning up hash of parentmsgs
2144 static gboolean parentmsgs_hash_remove(gpointer key,
2152 *\brief Set up list of parentmsgs
2153 * See procmsg_msg_has_flagged_parent_real()
2155 gboolean procmsg_msg_has_flagged_parent(MsgInfo *info, MsgPermFlags perm_flags)
2158 static GHashTable *parentmsgs = NULL;
2160 if (parentmsgs == NULL)
2161 parentmsgs = g_hash_table_new(NULL, NULL);
2163 result = procmsg_msg_has_flagged_parent_real(info, perm_flags, parentmsgs);
2164 g_hash_table_foreach_remove(parentmsgs, parentmsgs_hash_remove, NULL);
2170 *\brief Check if msgs prior in thread are marked
2171 * See procmsg_msg_has_flagged_parent_real()
2173 gboolean procmsg_msg_has_marked_parent(MsgInfo *info)
2175 return procmsg_msg_has_flagged_parent(info, MSG_MARKED);
2179 static GSList *procmsg_find_children_func(MsgInfo *info,
2180 GSList *children, GSList *all)
2184 cm_return_val_if_fail(info!=NULL, children);
2185 if (info->msgid == NULL)
2188 for (cur = all; cur != NULL; cur = g_slist_next(cur)) {
2189 MsgInfo *tmp = (MsgInfo *)cur->data;
2190 if (tmp->inreplyto && !strcmp(tmp->inreplyto, info->msgid)) {
2191 /* Check if message is already in the list */
2192 if ((children == NULL) ||
2193 (g_slist_index(children, tmp) == -1)) {
2194 children = g_slist_prepend(children,
2195 procmsg_msginfo_new_ref(tmp));
2196 children = procmsg_find_children_func(tmp,
2205 static GSList *procmsg_find_children (MsgInfo *info)
2210 cm_return_val_if_fail(info!=NULL, NULL);
2211 all = folder_item_get_msg_list(info->folder);
2212 children = procmsg_find_children_func(info, NULL, all);
2213 if (children != NULL) {
2214 for (cur = all; cur != NULL; cur = g_slist_next(cur)) {
2215 /* this will not free the used pointers
2216 created with procmsg_msginfo_new_ref */
2217 procmsg_msginfo_free((MsgInfo *)cur->data);
2225 static void procmsg_update_unread_children(MsgInfo *info, gboolean newly_marked)
2227 GSList *children = procmsg_find_children(info);
2229 for (cur = children; cur != NULL; cur = g_slist_next(cur)) {
2230 MsgInfo *tmp = (MsgInfo *)cur->data;
2231 if(MSG_IS_UNREAD(tmp->flags) && !MSG_IS_IGNORE_THREAD(tmp->flags)) {
2233 info->folder->unreadmarked_msgs++;
2235 info->folder->unreadmarked_msgs--;
2236 folder_item_update(info->folder, F_ITEM_UPDATE_MSGCNT);
2238 procmsg_msginfo_free(tmp);
2240 g_slist_free(children);
2244 * Set the destination folder for a copy or move operation
2246 * \param msginfo The message which's destination folder is changed
2247 * \param to_folder The destination folder for the operation
2249 void procmsg_msginfo_set_to_folder(MsgInfo *msginfo, FolderItem *to_folder)
2251 if(msginfo->to_folder != NULL) {
2252 msginfo->to_folder->op_count--;
2253 folder_item_update(msginfo->to_folder, F_ITEM_UPDATE_MSGCNT);
2255 msginfo->to_folder = to_folder;
2256 if(to_folder != NULL) {
2257 to_folder->op_count++;
2258 folder_item_update(msginfo->to_folder, F_ITEM_UPDATE_MSGCNT);
2263 * Apply filtering actions to the msginfo
2265 * \param msginfo The MsgInfo describing the message that should be filtered
2266 * \return TRUE if the message was moved and MsgInfo is now invalid,
2269 static gboolean procmsg_msginfo_filter(MsgInfo *msginfo, PrefsAccount* ac_prefs)
2271 MailFilteringData mail_filtering_data;
2273 mail_filtering_data.msginfo = msginfo;
2274 mail_filtering_data.msglist = NULL;
2275 mail_filtering_data.filtered = NULL;
2276 mail_filtering_data.unfiltered = NULL;
2277 mail_filtering_data.account = ac_prefs;
2279 if (!ac_prefs || ac_prefs->filterhook_on_recv)
2280 if (hooks_invoke(MAIL_FILTERING_HOOKLIST, &mail_filtering_data))
2283 /* filter if enabled in prefs or move to inbox if not */
2284 if((filtering_rules != NULL) &&
2285 filter_message_by_msginfo(filtering_rules, msginfo, ac_prefs,
2286 FILTERING_INCORPORATION, NULL)) {
2293 void procmsg_msglist_filter(GSList *list, PrefsAccount *ac,
2294 GSList **filtered, GSList **unfiltered,
2297 GSList *cur, *to_do = NULL;
2298 gint total = 0, curnum = 0;
2299 MailFilteringData mail_filtering_data;
2301 cm_return_if_fail(filtered != NULL);
2302 cm_return_if_fail(unfiltered != NULL);
2310 total = g_slist_length(list);
2314 *unfiltered = g_slist_copy(list);
2318 statusbar_print_all(_("Filtering messages...\n"));
2320 mail_filtering_data.msginfo = NULL;
2321 mail_filtering_data.msglist = list;
2322 mail_filtering_data.filtered = NULL;
2323 mail_filtering_data.unfiltered = NULL;
2324 mail_filtering_data.account = ac;
2326 if (!ac || ac->filterhook_on_recv)
2327 hooks_invoke(MAIL_LISTFILTERING_HOOKLIST, &mail_filtering_data);
2329 if (mail_filtering_data.filtered == NULL &&
2330 mail_filtering_data.unfiltered == NULL) {
2331 /* nothing happened */
2332 debug_print(MAIL_LISTFILTERING_HOOKLIST " did nothing. filtering whole list normally.\n");
2335 if (mail_filtering_data.filtered != NULL) {
2336 /* keep track of what's been filtered by the hooks */
2337 debug_print(MAIL_LISTFILTERING_HOOKLIST " filtered some stuff. total %d filtered %d unfilt %d.\n",
2338 g_slist_length(list),
2339 g_slist_length(mail_filtering_data.filtered),
2340 g_slist_length(mail_filtering_data.unfiltered));
2342 *filtered = g_slist_copy(mail_filtering_data.filtered);
2344 if (mail_filtering_data.unfiltered != NULL) {
2345 /* what the hooks didn't handle will go in filtered or
2346 * unfiltered in the next loop */
2347 debug_print(MAIL_LISTFILTERING_HOOKLIST " left unfiltered stuff. total %d filtered %d unfilt %d.\n",
2348 g_slist_length(list),
2349 g_slist_length(mail_filtering_data.filtered),
2350 g_slist_length(mail_filtering_data.unfiltered));
2351 to_do = mail_filtering_data.unfiltered;
2354 for (cur = to_do; cur; cur = cur->next) {
2355 MsgInfo *info = (MsgInfo *)cur->data;
2356 if (procmsg_msginfo_filter(info, ac))
2357 *filtered = g_slist_prepend(*filtered, info);
2359 *unfiltered = g_slist_prepend(*unfiltered, info);
2360 statusbar_progress_all(curnum++, total, prefs_common.statusbar_update_step);
2363 g_slist_free(mail_filtering_data.filtered);
2364 g_slist_free(mail_filtering_data.unfiltered);
2366 *filtered = g_slist_reverse(*filtered);
2367 *unfiltered = g_slist_reverse(*unfiltered);
2369 statusbar_progress_all(0,0,0);
2370 statusbar_pop_all();
2373 MsgInfo *procmsg_msginfo_new_from_mimeinfo(MsgInfo *src_msginfo, MimeInfo *mimeinfo)
2375 MsgInfo *tmp_msginfo = NULL;
2376 MsgFlags flags = {0, 0};
2377 gchar *tmpfile = get_tmp_file();
2378 FILE *fp = g_fopen(tmpfile, "wb");
2380 if (!mimeinfo || mimeinfo->type != MIMETYPE_MESSAGE ||
2381 g_ascii_strcasecmp(mimeinfo->subtype, "rfc822")) {
2382 g_warning("procmsg_msginfo_new_from_mimeinfo(): unsuitable mimeinfo");
2389 if (fp && procmime_write_mimeinfo(mimeinfo, fp) >= 0) {
2392 tmp_msginfo = procheader_parse_file(
2399 if (tmp_msginfo != NULL) {
2401 tmp_msginfo->folder = src_msginfo->folder;
2402 tmp_msginfo->plaintext_file = g_strdup(tmpfile);
2404 g_warning("procmsg_msginfo_new_from_mimeinfo(): Can't generate new msginfo");
2412 static GSList *spam_learners = NULL;
2414 void procmsg_register_spam_learner (int (*learn_func)(MsgInfo *info, GSList *list, gboolean spam))
2416 if (!g_slist_find(spam_learners, learn_func))
2417 spam_learners = g_slist_append(spam_learners, learn_func);
2418 if (mainwindow_get_mainwindow()) {
2419 main_window_set_menu_sensitive(mainwindow_get_mainwindow());
2420 summary_set_menu_sensitive(
2421 mainwindow_get_mainwindow()->summaryview);
2422 toolbar_main_set_sensitive(mainwindow_get_mainwindow());
2426 void procmsg_unregister_spam_learner (int (*learn_func)(MsgInfo *info, GSList *list, gboolean spam))
2428 spam_learners = g_slist_remove(spam_learners, learn_func);
2429 if (mainwindow_get_mainwindow()) {
2430 main_window_set_menu_sensitive(mainwindow_get_mainwindow());
2431 summary_set_menu_sensitive(
2432 mainwindow_get_mainwindow()->summaryview);
2433 toolbar_main_set_sensitive(mainwindow_get_mainwindow());
2437 gboolean procmsg_spam_can_learn(void)
2439 return g_slist_length(spam_learners) > 0;
2442 int procmsg_spam_learner_learn (MsgInfo *info, GSList *list, gboolean spam)
2444 GSList *cur = spam_learners;
2446 for (; cur; cur = cur->next) {
2447 int ((*func)(MsgInfo *info, GSList *list, gboolean spam)) = cur->data;
2448 ret |= func(info, list, spam);
2453 static gchar *spam_folder_item = NULL;
2454 static FolderItem * (*procmsg_spam_get_folder_func)(MsgInfo *msginfo) = NULL;
2455 void procmsg_spam_set_folder (const char *item_identifier, FolderItem *(*spam_get_folder_func)(MsgInfo *info))
2457 g_free(spam_folder_item);
2458 if (item_identifier)
2459 spam_folder_item = g_strdup(item_identifier);
2461 spam_folder_item = NULL;
2462 if (spam_get_folder_func != NULL)
2463 procmsg_spam_get_folder_func = spam_get_folder_func;
2465 procmsg_spam_get_folder_func = NULL;
2468 FolderItem *procmsg_spam_get_folder (MsgInfo *msginfo)
2470 FolderItem *item = NULL;
2472 if (procmsg_spam_get_folder_func)
2473 item = procmsg_spam_get_folder_func(msginfo);
2474 if (item == NULL && spam_folder_item)
2475 item = folder_find_item_from_identifier(spam_folder_item);
2477 item = folder_get_default_trash();
2481 static void item_has_queued_mails(FolderItem *item, gpointer data)
2483 gboolean *result = (gboolean *)data;
2484 if (*result == TRUE)
2486 if (folder_has_parent_of_type(item, F_QUEUE)) {
2487 if (item->total_msgs == 0)
2490 GSList *msglist = folder_item_get_msg_list(item);
2492 for (cur = msglist; cur; cur = cur->next) {
2493 MsgInfo *msginfo = (MsgInfo *)cur->data;
2494 if (!MSG_IS_DELETED(msginfo->flags) &&
2495 !MSG_IS_LOCKED(msginfo->flags)) {
2500 procmsg_msg_list_free(msglist);
2505 gboolean procmsg_have_queued_mails_fast (void)
2507 gboolean result = FALSE;
2508 folder_func_to_all_folders(item_has_queued_mails, &result);
2512 static void item_has_trashed_mails(FolderItem *item, gpointer data)
2514 gboolean *result = (gboolean *)data;
2515 if (*result == TRUE)
2517 if (folder_has_parent_of_type(item, F_TRASH) && item->total_msgs > 0)
2521 gboolean procmsg_have_trashed_mails_fast (void)
2523 gboolean result = FALSE;
2524 folder_func_to_all_folders(item_has_trashed_mails, &result);
2528 gchar *procmsg_msginfo_get_tags_str(MsgInfo *msginfo)
2536 if (msginfo->tags == NULL)
2538 for (cur = msginfo->tags; cur; cur = cur->next) {
2539 const gchar *tag = tags_get_tag(GPOINTER_TO_INT(cur->data));
2543 tags = g_strdup(tag);
2545 int olen = strlen(tags);
2546 int nlen = olen + strlen(tag) + 2 /* strlen(", ") */;
2547 tags = g_realloc(tags, nlen+1);
2550 strcpy(tags+olen, ", ");
2551 strcpy(tags+olen+2, tag);
2558 void procmsg_msginfo_update_tags(MsgInfo *msginfo, gboolean set, gint id)
2566 msginfo->tags = g_slist_remove(
2568 GINT_TO_POINTER(id));
2569 changed.data = GINT_TO_POINTER(id);
2570 changed.next = NULL;
2571 folder_item_commit_tags(msginfo->folder, msginfo, NULL, &changed);
2573 if (!g_slist_find(msginfo->tags, GINT_TO_POINTER(id))) {
2574 msginfo->tags = g_slist_append(
2576 GINT_TO_POINTER(id));
2578 changed.data = GINT_TO_POINTER(id);
2579 changed.next = NULL;
2580 folder_item_commit_tags(msginfo->folder, msginfo, &changed, NULL);
2585 void procmsg_msginfo_clear_tags(MsgInfo *msginfo)
2587 GSList *unset = msginfo->tags;
2588 msginfo->tags = NULL;
2589 folder_item_commit_tags(msginfo->folder, msginfo, NULL, unset);
2590 g_slist_free(unset);