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-- */
1142 void procmsg_print_message(MsgInfo *msginfo, const gchar *cmdline)
1144 static const gchar *def_cmd = "lpr %s";
1145 static guint id = 0;
1151 cm_return_if_fail(msginfo);
1153 if (procmime_msginfo_is_encrypted(msginfo))
1154 tmpfp = procmime_get_first_encrypted_text_content(msginfo);
1156 tmpfp = procmime_get_first_text_content(msginfo);
1157 if (tmpfp == NULL) {
1158 g_warning("Can't get text part\n");
1162 prtmp = g_strdup_printf("%s%cprinttmp.%08x",
1163 get_mime_tmp_dir(), G_DIR_SEPARATOR, id++);
1165 if ((prfp = g_fopen(prtmp, "wb")) == NULL) {
1166 FILE_OP_ERROR(prtmp, "fopen");
1172 if (msginfo->date) { r = fprintf(prfp, "Date: %s\n", msginfo->date); if (r < 0) goto fpferr; }
1173 if (msginfo->from) { r = fprintf(prfp, "From: %s\n", msginfo->from); if (r < 0) goto fpferr; }
1174 if (msginfo->to) { r = fprintf(prfp, "To: %s\n", msginfo->to); if (r < 0) goto fpferr; }
1175 if (msginfo->cc) { r = fprintf(prfp, "Cc: %s\n", msginfo->cc); if (r < 0) goto fpferr; }
1176 if (msginfo->newsgroups) {
1177 r = fprintf(prfp, "Newsgroups: %s\n", msginfo->newsgroups); if (r < 0) goto fpferr;
1179 if (msginfo->subject) { r = fprintf(prfp, "Subject: %s\n", msginfo->subject); if (r < 0) goto fpferr; }
1180 if (fputc('\n', prfp) == EOF) goto fpferr;
1182 while (fgets(buf, sizeof(buf), tmpfp) != NULL) {
1183 r = fputs(buf, prfp);
1184 if (r == EOF) goto fpferr;
1190 if (cmdline && (p = strchr(cmdline, '%')) && *(p + 1) == 's' &&
1191 !strchr(p + 2, '%'))
1192 g_snprintf(buf, sizeof(buf) - 1, cmdline, prtmp);
1195 g_warning("Print command-line is invalid: '%s'\n",
1197 g_snprintf(buf, sizeof(buf) - 1, def_cmd, prtmp);
1203 if (buf[strlen(buf) - 1] != '&')
1204 strncat(buf, "&", sizeof(buf) - strlen(buf) - 1);
1205 if (system(buf) == -1)
1206 g_warning("system(%s) failed.", buf);
1209 FILE_OP_ERROR(prtmp, "fprintf/fputc/fputs");
1215 MsgInfo *procmsg_msginfo_new_ref(MsgInfo *msginfo)
1222 MsgInfo *procmsg_msginfo_new(void)
1224 MsgInfo *newmsginfo;
1226 newmsginfo = g_new0(MsgInfo, 1);
1227 newmsginfo->refcnt = 1;
1232 MsgInfo *procmsg_msginfo_copy(MsgInfo *msginfo)
1234 MsgInfo *newmsginfo;
1237 if (msginfo == NULL) return NULL;
1239 newmsginfo = g_new0(MsgInfo, 1);
1241 newmsginfo->refcnt = 1;
1243 #define MEMBCOPY(mmb) newmsginfo->mmb = msginfo->mmb
1244 #define MEMBDUP(mmb) newmsginfo->mmb = msginfo->mmb ? \
1245 g_strdup(msginfo->mmb) : NULL
1260 MEMBDUP(newsgroups);
1267 MEMBCOPY(to_folder);
1269 if (msginfo->extradata) {
1270 newmsginfo->extradata = g_new0(MsgInfoExtraData, 1);
1271 MEMBDUP(extradata->face);
1272 MEMBDUP(extradata->xface);
1273 MEMBDUP(extradata->dispositionnotificationto);
1274 MEMBDUP(extradata->returnreceiptto);
1275 MEMBDUP(extradata->partial_recv);
1276 MEMBDUP(extradata->account_server);
1277 MEMBDUP(extradata->account_login);
1278 MEMBDUP(extradata->list_post);
1279 MEMBDUP(extradata->list_subscribe);
1280 MEMBDUP(extradata->list_unsubscribe);
1281 MEMBDUP(extradata->list_help);
1282 MEMBDUP(extradata->list_archive);
1283 MEMBDUP(extradata->list_owner);
1286 refs = msginfo->references;
1287 for (refs = msginfo->references; refs != NULL; refs = refs->next) {
1288 newmsginfo->references = g_slist_prepend
1289 (newmsginfo->references, g_strdup(refs->data));
1291 newmsginfo->references = g_slist_reverse(newmsginfo->references);
1294 MEMBDUP(plaintext_file);
1299 MsgInfo *procmsg_msginfo_get_full_info_from_file(MsgInfo *msginfo, const gchar *file)
1301 MsgInfo *full_msginfo;
1303 if (msginfo == NULL) return NULL;
1305 if (!file || !is_file_exist(file)) {
1306 g_warning("procmsg_msginfo_get_full_info_from_file(): can't get message file.\n");
1310 full_msginfo = procheader_parse_file(file, msginfo->flags, TRUE, FALSE);
1311 if (!full_msginfo) return NULL;
1313 msginfo->total_size = full_msginfo->total_size;
1314 msginfo->planned_download = full_msginfo->planned_download;
1316 if (full_msginfo->extradata) {
1317 if (!msginfo->extradata)
1318 msginfo->extradata = g_new0(MsgInfoExtraData, 1);
1319 if (!msginfo->extradata->list_post)
1320 msginfo->extradata->list_post = g_strdup(full_msginfo->extradata->list_post);
1321 if (!msginfo->extradata->list_subscribe)
1322 msginfo->extradata->list_subscribe = g_strdup(full_msginfo->extradata->list_subscribe);
1323 if (!msginfo->extradata->list_unsubscribe)
1324 msginfo->extradata->list_unsubscribe = g_strdup(full_msginfo->extradata->list_unsubscribe);
1325 if (!msginfo->extradata->list_help)
1326 msginfo->extradata->list_help = g_strdup(full_msginfo->extradata->list_help);
1327 if (!msginfo->extradata->list_archive)
1328 msginfo->extradata->list_archive= g_strdup(full_msginfo->extradata->list_archive);
1329 if (!msginfo->extradata->list_owner)
1330 msginfo->extradata->list_owner = g_strdup(full_msginfo->extradata->list_owner);
1331 if (!msginfo->extradata->xface)
1332 msginfo->extradata->xface = g_strdup(full_msginfo->extradata->xface);
1333 if (!msginfo->extradata->face)
1334 msginfo->extradata->face = g_strdup(full_msginfo->extradata->face);
1335 if (!msginfo->extradata->dispositionnotificationto)
1336 msginfo->extradata->dispositionnotificationto =
1337 g_strdup(full_msginfo->extradata->dispositionnotificationto);
1338 if (!msginfo->extradata->returnreceiptto)
1339 msginfo->extradata->returnreceiptto = g_strdup
1340 (full_msginfo->extradata->returnreceiptto);
1341 if (!msginfo->extradata->partial_recv && full_msginfo->extradata->partial_recv)
1342 msginfo->extradata->partial_recv = g_strdup
1343 (full_msginfo->extradata->partial_recv);
1344 if (!msginfo->extradata->account_server && full_msginfo->extradata->account_server)
1345 msginfo->extradata->account_server = g_strdup
1346 (full_msginfo->extradata->account_server);
1347 if (!msginfo->extradata->account_login && full_msginfo->extradata->account_login)
1348 msginfo->extradata->account_login = g_strdup
1349 (full_msginfo->extradata->account_login);
1351 procmsg_msginfo_free(full_msginfo);
1353 return procmsg_msginfo_new_ref(msginfo);
1356 MsgInfo *procmsg_msginfo_get_full_info(MsgInfo *msginfo)
1358 MsgInfo *full_msginfo;
1361 if (msginfo == NULL) return NULL;
1363 file = procmsg_get_message_file_path(msginfo);
1364 if (!file || !is_file_exist(file)) {
1366 file = procmsg_get_message_file(msginfo);
1368 if (!file || !is_file_exist(file)) {
1369 g_warning("procmsg_msginfo_get_full_info(): can't get message file.\n");
1373 full_msginfo = procmsg_msginfo_get_full_info_from_file(msginfo, file);
1375 return full_msginfo;
1378 void procmsg_msginfo_free(MsgInfo *msginfo)
1380 if (msginfo == NULL) return;
1383 if (msginfo->refcnt > 0)
1386 if (msginfo->to_folder) {
1387 msginfo->to_folder->op_count--;
1388 folder_item_update(msginfo->to_folder, F_ITEM_UPDATE_MSGCNT);
1391 g_free(msginfo->fromspace);
1393 g_free(msginfo->fromname);
1395 g_free(msginfo->date);
1396 g_free(msginfo->from);
1397 g_free(msginfo->to);
1398 g_free(msginfo->cc);
1399 g_free(msginfo->newsgroups);
1400 g_free(msginfo->subject);
1401 g_free(msginfo->msgid);
1402 g_free(msginfo->inreplyto);
1403 g_free(msginfo->xref);
1405 if (msginfo->extradata) {
1406 g_free(msginfo->extradata->returnreceiptto);
1407 g_free(msginfo->extradata->dispositionnotificationto);
1408 g_free(msginfo->extradata->xface);
1409 g_free(msginfo->extradata->face);
1410 g_free(msginfo->extradata->list_post);
1411 g_free(msginfo->extradata->list_subscribe);
1412 g_free(msginfo->extradata->list_unsubscribe);
1413 g_free(msginfo->extradata->list_help);
1414 g_free(msginfo->extradata->list_archive);
1415 g_free(msginfo->extradata->list_owner);
1416 g_free(msginfo->extradata->partial_recv);
1417 g_free(msginfo->extradata->account_server);
1418 g_free(msginfo->extradata->account_login);
1419 g_free(msginfo->extradata);
1421 slist_free_strings(msginfo->references);
1422 g_slist_free(msginfo->references);
1423 g_slist_free(msginfo->tags);
1425 g_free(msginfo->plaintext_file);
1430 guint procmsg_msginfo_memusage(MsgInfo *msginfo)
1435 memusage += sizeof(MsgInfo);
1436 if (msginfo->fromname)
1437 memusage += strlen(msginfo->fromname);
1439 memusage += strlen(msginfo->date);
1441 memusage += strlen(msginfo->from);
1443 memusage += strlen(msginfo->to);
1445 memusage += strlen(msginfo->cc);
1446 if (msginfo->newsgroups)
1447 memusage += strlen(msginfo->newsgroups);
1448 if (msginfo->subject)
1449 memusage += strlen(msginfo->subject);
1451 memusage += strlen(msginfo->msgid);
1452 if (msginfo->inreplyto)
1453 memusage += strlen(msginfo->inreplyto);
1455 for (tmp = msginfo->references; tmp; tmp=tmp->next) {
1456 gchar *r = (gchar *)tmp->data;
1457 memusage += r?strlen(r):0 + sizeof(GSList);
1459 if (msginfo->fromspace)
1460 memusage += strlen(msginfo->fromspace);
1462 for (tmp = msginfo->tags; tmp; tmp=tmp->next) {
1463 memusage += sizeof(GSList);
1465 if (msginfo->extradata) {
1466 memusage += sizeof(MsgInfoExtraData);
1467 if (msginfo->extradata->xface)
1468 memusage += strlen(msginfo->extradata->xface);
1469 if (msginfo->extradata->face)
1470 memusage += strlen(msginfo->extradata->face);
1471 if (msginfo->extradata->dispositionnotificationto)
1472 memusage += strlen(msginfo->extradata->dispositionnotificationto);
1473 if (msginfo->extradata->returnreceiptto)
1474 memusage += strlen(msginfo->extradata->returnreceiptto);
1476 if (msginfo->extradata->partial_recv)
1477 memusage += strlen(msginfo->extradata->partial_recv);
1478 if (msginfo->extradata->account_server)
1479 memusage += strlen(msginfo->extradata->account_server);
1480 if (msginfo->extradata->account_login)
1481 memusage += strlen(msginfo->extradata->account_login);
1483 if (msginfo->extradata->list_post)
1484 memusage += strlen(msginfo->extradata->list_post);
1485 if (msginfo->extradata->list_subscribe)
1486 memusage += strlen(msginfo->extradata->list_subscribe);
1487 if (msginfo->extradata->list_unsubscribe)
1488 memusage += strlen(msginfo->extradata->list_unsubscribe);
1489 if (msginfo->extradata->list_help)
1490 memusage += strlen(msginfo->extradata->list_help);
1491 if (msginfo->extradata->list_archive)
1492 memusage += strlen(msginfo->extradata->list_archive);
1493 if (msginfo->extradata->list_owner)
1494 memusage += strlen(msginfo->extradata->list_owner);
1499 static gint procmsg_send_message_queue_full(const gchar *file, gboolean keep_session, gchar **errstr,
1500 FolderItem *queue, gint msgnum, gboolean *queued_removed)
1502 static HeaderEntry qentry[] = {{"S:", NULL, FALSE},
1503 {"SSV:", NULL, FALSE},
1504 {"R:", NULL, FALSE},
1505 {"NG:", NULL, FALSE},
1506 {"MAID:", NULL, FALSE},
1507 {"NAID:", NULL, FALSE},
1508 {"SCF:", NULL, FALSE},
1509 {"RMID:", NULL, FALSE},
1510 {"FMID:", NULL, FALSE},
1511 {"X-Claws-Privacy-System:", NULL, FALSE},
1512 {"X-Claws-Encrypt:", NULL, FALSE},
1513 {"X-Claws-Encrypt-Data:", NULL, FALSE},
1514 {"X-Claws-End-Special-Headers:", NULL, FALSE},
1515 {"X-Sylpheed-Privacy-System:", NULL, FALSE},
1516 {"X-Sylpheed-Encrypt:", NULL, FALSE},
1517 {"X-Sylpheed-Encrypt-Data:", NULL, FALSE},
1518 {"X-Sylpheed-End-Special-Headers:", NULL, FALSE},
1519 {NULL, NULL, FALSE}};
1522 gint mailval = 0, newsval = 0;
1524 gchar *smtpserver = NULL;
1525 GSList *to_list = NULL;
1526 GSList *newsgroup_list = NULL;
1527 gchar *savecopyfolder = NULL;
1528 gchar *replymessageid = NULL;
1529 gchar *fwdmessageid = NULL;
1530 gchar *privacy_system = NULL;
1531 gboolean encrypt = FALSE;
1532 gchar *encrypt_data = NULL;
1533 gchar buf[BUFFSIZE];
1535 PrefsAccount *mailac = NULL, *newsac = NULL;
1536 gboolean save_clear_text = TRUE;
1537 gchar *tmp_enc_file = NULL;
1539 cm_return_val_if_fail(file != NULL, -1);
1541 if ((fp = g_fopen(file, "rb")) == NULL) {
1542 FILE_OP_ERROR(file, "fopen");
1544 if (*errstr) g_free(*errstr);
1545 *errstr = g_strdup_printf(_("Couldn't open file %s."), file);
1550 while ((hnum = procheader_get_one_field(buf, sizeof(buf), fp, qentry))
1552 gchar *p = buf + strlen(qentry[hnum].name);
1560 if (smtpserver == NULL)
1561 smtpserver = g_strdup(p);
1564 to_list = address_list_append(to_list, p);
1567 newsgroup_list = newsgroup_list_append(newsgroup_list, p);
1569 case Q_MAIL_ACCOUNT_ID:
1570 mailac = account_find_from_id(atoi(p));
1572 case Q_NEWS_ACCOUNT_ID:
1573 newsac = account_find_from_id(atoi(p));
1575 case Q_SAVE_COPY_FOLDER:
1576 if (savecopyfolder == NULL)
1577 savecopyfolder = g_strdup(p);
1579 case Q_REPLY_MESSAGE_ID:
1580 if (replymessageid == NULL)
1581 replymessageid = g_strdup(p);
1583 case Q_FWD_MESSAGE_ID:
1584 if (fwdmessageid == NULL)
1585 fwdmessageid = g_strdup(p);
1587 case Q_PRIVACY_SYSTEM:
1588 case Q_PRIVACY_SYSTEM_OLD:
1589 if (privacy_system == NULL)
1590 privacy_system = g_strdup(p);
1597 case Q_ENCRYPT_DATA:
1598 case Q_ENCRYPT_DATA_OLD:
1599 if (encrypt_data == NULL)
1600 encrypt_data = g_strdup(p);
1603 case Q_CLAWS_HDRS_OLD:
1604 /* end of special headers reached */
1605 goto send_mail; /* can't "break;break;" */
1609 filepos = ftell(fp);
1614 if (mailac && mailac->save_encrypted_as_clear_text
1615 && !mailac->encrypt_to_self)
1616 save_clear_text = TRUE;
1618 save_clear_text = FALSE;
1623 mimeinfo = procmime_scan_queue_file(file);
1624 if (!privacy_encrypt(privacy_system, mimeinfo, encrypt_data)
1625 || (fp = my_tmpfile()) == NULL
1626 || procmime_write_mimeinfo(mimeinfo, fp) < 0) {
1629 procmime_mimeinfo_free_all(mimeinfo);
1632 slist_free_strings(to_list);
1633 g_slist_free(to_list);
1634 slist_free_strings(newsgroup_list);
1635 g_slist_free(newsgroup_list);
1636 g_free(savecopyfolder);
1637 g_free(replymessageid);
1638 g_free(fwdmessageid);
1639 g_free(privacy_system);
1640 g_free(encrypt_data);
1642 if (*errstr) g_free(*errstr);
1643 *errstr = g_strdup_printf(_("Couldn't encrypt the email: %s"),
1644 privacy_get_error());
1650 if (!save_clear_text) {
1651 gchar *content = NULL;
1652 FILE *tmpfp = get_tmpfile_in_dir(get_mime_tmp_dir(), &tmp_enc_file);
1656 content = file_read_stream_to_str(fp);
1659 str_write_to_file(content, tmp_enc_file);
1662 g_warning("couldn't get tempfile\n");
1666 procmime_mimeinfo_free_all(mimeinfo);
1672 debug_print("Sending message by mail\n");
1675 if (*errstr) g_free(*errstr);
1676 *errstr = g_strdup_printf(_("Queued message header is broken."));
1679 } else if (mailac && mailac->use_mail_command &&
1680 mailac->mail_command && (* mailac->mail_command)) {
1681 mailval = send_message_local(mailac->mail_command, fp);
1684 mailac = account_find_from_smtp_server(from, smtpserver);
1686 g_warning("Account not found. "
1687 "Using current account...\n");
1688 mailac = cur_account;
1693 mailval = send_message_smtp_full(mailac, to_list, fp, keep_session);
1694 if (mailval == -1 && errstr) {
1695 if (*errstr) g_free(*errstr);
1696 *errstr = g_strdup_printf(_("An error happened during SMTP session."));
1699 PrefsAccount tmp_ac;
1701 g_warning("Account not found.\n");
1703 memset(&tmp_ac, 0, sizeof(PrefsAccount));
1704 tmp_ac.address = from;
1705 tmp_ac.smtp_server = smtpserver;
1706 tmp_ac.smtpport = SMTP_PORT;
1707 mailval = send_message_smtp(&tmp_ac, to_list, fp);
1708 if (mailval == -1 && errstr) {
1709 if (*errstr) g_free(*errstr);
1710 *errstr = g_strdup_printf(_("No specific account has been found to "
1711 "send, and an error happened during SMTP session."));
1715 } else if (!to_list && !newsgroup_list) {
1717 if (*errstr) g_free(*errstr);
1718 *errstr = g_strdup(_("Couldn't determine sending informations. "
1719 "Maybe the email hasn't been generated by Claws Mail."));
1724 fseek(fp, filepos, SEEK_SET);
1725 if (newsgroup_list && newsac && (mailval == 0)) {
1730 /* write to temporary file */
1731 tmp = g_strdup_printf("%s%cnntp%p", get_tmp_dir(),
1732 G_DIR_SEPARATOR, file);
1733 if ((tmpfp = g_fopen(tmp, "wb")) == NULL) {
1734 FILE_OP_ERROR(tmp, "fopen");
1736 alertpanel_error(_("Couldn't create temporary file for news sending."));
1738 if (change_file_mode_rw(tmpfp, tmp) < 0) {
1739 FILE_OP_ERROR(tmp, "chmod");
1740 g_warning("can't change file mode\n");
1743 while ((newsval == 0) && fgets(buf, sizeof(buf), fp) != NULL) {
1744 if (fputs(buf, tmpfp) == EOF) {
1745 FILE_OP_ERROR(tmp, "fputs");
1748 if (*errstr) g_free(*errstr);
1749 *errstr = g_strdup_printf(_("Error when writing temporary file for news sending."));
1756 debug_print("Sending message by news\n");
1758 folder = FOLDER(newsac->folder);
1760 newsval = news_post(folder, tmp);
1761 if (newsval < 0 && errstr) {
1762 if (*errstr) g_free(*errstr);
1763 *errstr = g_strdup_printf(_("Error occurred while posting the message to %s."),
1764 newsac->nntp_server);
1774 /* update session statistics */
1775 if (mailval == 0 && newsval == 0) {
1776 /* update session stats */
1778 session_stats.replied++;
1779 else if (fwdmessageid)
1780 session_stats.forwarded++;
1782 session_stats.sent++;
1785 /* save message to outbox */
1786 if (mailval == 0 && newsval == 0 && savecopyfolder) {
1789 debug_print("saving sent message...\n");
1791 outbox = folder_find_item_from_identifier(savecopyfolder);
1793 outbox = folder_get_default_outbox();
1795 if (save_clear_text || tmp_enc_file == NULL) {
1796 gboolean saved = FALSE;
1797 *queued_removed = FALSE;
1798 if (queue && msgnum > 0) {
1799 MsgInfo *queued_mail = folder_item_get_msginfo(queue, msgnum);
1800 if (folder_item_move_msg(outbox, queued_mail) >= 0) {
1801 debug_print("moved queued mail %d to sent folder\n", msgnum);
1803 *queued_removed = TRUE;
1804 } else if (folder_item_copy_msg(outbox, queued_mail) >= 0) {
1805 debug_print("copied queued mail %d to sent folder\n", msgnum);
1808 procmsg_msginfo_free(queued_mail);
1811 debug_print("resaving clear text queued mail to sent folder\n");
1812 procmsg_save_to_outbox(outbox, file, TRUE);
1815 debug_print("saving encrpyted queued mail to sent folder\n");
1816 procmsg_save_to_outbox(outbox, tmp_enc_file, FALSE);
1820 if (tmp_enc_file != NULL) {
1821 claws_unlink(tmp_enc_file);
1823 tmp_enc_file = NULL;
1826 if (replymessageid != NULL || fwdmessageid != NULL) {
1830 if (replymessageid != NULL)
1831 tokens = g_strsplit(replymessageid, "\t", 0);
1833 tokens = g_strsplit(fwdmessageid, "\t", 0);
1834 item = folder_find_item_from_identifier(tokens[0]);
1836 /* check if queued message has valid folder and message id */
1837 if (item != NULL && tokens[2] != NULL) {
1840 msginfo = folder_item_get_msginfo(item, atoi(tokens[1]));
1842 /* check if referring message exists and has a message id */
1843 if ((msginfo != NULL) &&
1844 (msginfo->msgid != NULL) &&
1845 (strcmp(msginfo->msgid, tokens[2]) != 0)) {
1846 procmsg_msginfo_free(msginfo);
1850 if (msginfo == NULL) {
1851 msginfo = folder_item_get_msginfo_by_msgid(item, tokens[2]);
1854 if (msginfo != NULL) {
1855 if (replymessageid != NULL) {
1856 MsgPermFlags to_unset = 0;
1858 if (prefs_common.mark_as_read_on_new_window)
1859 to_unset = (MSG_NEW|MSG_UNREAD);
1861 procmsg_msginfo_unset_flags(msginfo, to_unset, 0);
1862 procmsg_msginfo_set_flags(msginfo, MSG_REPLIED, 0);
1864 procmsg_msginfo_set_flags(msginfo, MSG_FORWARDED, 0);
1866 procmsg_msginfo_free(msginfo);
1874 slist_free_strings(to_list);
1875 g_slist_free(to_list);
1876 slist_free_strings(newsgroup_list);
1877 g_slist_free(newsgroup_list);
1878 g_free(savecopyfolder);
1879 g_free(replymessageid);
1880 g_free(fwdmessageid);
1881 g_free(privacy_system);
1882 g_free(encrypt_data);
1884 return (newsval != 0 ? newsval : mailval);
1887 gint procmsg_send_message_queue(const gchar *file, gchar **errstr, FolderItem *queue, gint msgnum, gboolean *queued_removed)
1889 gint result = procmsg_send_message_queue_full(file, FALSE, errstr, queue, msgnum, queued_removed);
1890 toolbar_main_set_sensitive(mainwindow_get_mainwindow());
1894 gint procmsg_send_message_queue_with_lock(const gchar *file, gchar **errstr, FolderItem *queue, gint msgnum, gboolean *queued_removed)
1897 if (procmsg_queue_lock(errstr)) {
1898 val = procmsg_send_message_queue(file, errstr, queue, msgnum, queued_removed);
1899 procmsg_queue_unlock();
1905 static void update_folder_msg_counts(FolderItem *item, MsgInfo *msginfo, MsgPermFlags old_flags)
1907 MsgPermFlags new_flags = msginfo->flags.perm_flags;
1910 if (!(old_flags & MSG_NEW) && (new_flags & MSG_NEW)) {
1914 if ((old_flags & MSG_NEW) && !(new_flags & MSG_NEW)) {
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++;
1925 if ((old_flags & MSG_UNREAD) && !(new_flags & MSG_UNREAD)) {
1926 item->unread_msgs--;
1927 if (procmsg_msg_has_marked_parent(msginfo))
1928 item->unreadmarked_msgs--;
1932 if (!(old_flags & MSG_MARKED) && (new_flags & MSG_MARKED)) {
1933 procmsg_update_unread_children(msginfo, TRUE);
1934 item->marked_msgs++;
1937 if ((old_flags & MSG_MARKED) && !(new_flags & MSG_MARKED)) {
1938 procmsg_update_unread_children(msginfo, FALSE);
1939 item->marked_msgs--;
1942 if (!(old_flags & MSG_REPLIED) && (new_flags & MSG_REPLIED)) {
1943 item->replied_msgs++;
1946 if ((old_flags & MSG_REPLIED) && !(new_flags & MSG_REPLIED)) {
1947 item->replied_msgs--;
1950 if (!(old_flags & MSG_FORWARDED) && (new_flags & MSG_FORWARDED)) {
1951 item->forwarded_msgs++;
1954 if ((old_flags & MSG_FORWARDED) && !(new_flags & MSG_FORWARDED)) {
1955 item->forwarded_msgs--;
1958 if (!(old_flags & MSG_LOCKED) && (new_flags & MSG_LOCKED)) {
1959 item->locked_msgs++;
1962 if ((old_flags & MSG_LOCKED) && !(new_flags & MSG_LOCKED)) {
1963 item->locked_msgs--;
1966 if ((old_flags & MSG_IGNORE_THREAD) && !(new_flags & MSG_IGNORE_THREAD)) {
1967 item->ignored_msgs--;
1970 if (!(old_flags & MSG_IGNORE_THREAD) && (new_flags & MSG_IGNORE_THREAD)) {
1971 item->ignored_msgs++;
1974 if ((old_flags & MSG_WATCH_THREAD) && !(new_flags & MSG_WATCH_THREAD)) {
1975 item->watched_msgs--;
1978 if (!(old_flags & MSG_WATCH_THREAD) && (new_flags & MSG_WATCH_THREAD)) {
1979 item->watched_msgs++;
1983 void procmsg_msginfo_set_flags(MsgInfo *msginfo, MsgPermFlags perm_flags, MsgTmpFlags tmp_flags)
1986 MsgInfoUpdate msginfo_update;
1987 MsgPermFlags perm_flags_new, perm_flags_old;
1988 MsgTmpFlags tmp_flags_old;
1990 cm_return_if_fail(msginfo != NULL);
1991 item = msginfo->folder;
1992 cm_return_if_fail(item != NULL);
1994 debug_print("Setting flags for message %d in folder %s\n", msginfo->msgnum, item->path);
1996 /* Perm Flags handling */
1997 perm_flags_old = msginfo->flags.perm_flags;
1998 perm_flags_new = msginfo->flags.perm_flags | perm_flags;
1999 if ((perm_flags & MSG_IGNORE_THREAD) || (perm_flags_old & MSG_IGNORE_THREAD)) {
2000 perm_flags_new &= ~(MSG_NEW | MSG_UNREAD);
2002 if ((perm_flags & MSG_WATCH_THREAD) || (perm_flags_old & MSG_WATCH_THREAD)) {
2003 perm_flags_new &= ~(MSG_IGNORE_THREAD);
2006 if (perm_flags_old != perm_flags_new) {
2007 folder_item_change_msg_flags(msginfo->folder, msginfo, perm_flags_new);
2009 update_folder_msg_counts(item, msginfo, perm_flags_old);
2010 summary_update_unread(mainwindow_get_mainwindow()->summaryview, NULL);
2013 /* Tmp flags handling */
2014 tmp_flags_old = msginfo->flags.tmp_flags;
2015 msginfo->flags.tmp_flags |= tmp_flags;
2017 /* update notification */
2018 if ((perm_flags_old != perm_flags_new) || (tmp_flags_old != msginfo->flags.tmp_flags)) {
2019 msginfo_update.msginfo = msginfo;
2020 msginfo_update.flags = MSGINFO_UPDATE_FLAGS;
2021 hooks_invoke(MSGINFO_UPDATE_HOOKLIST, &msginfo_update);
2022 folder_item_update(msginfo->folder, F_ITEM_UPDATE_MSGCNT);
2026 void procmsg_msginfo_unset_flags(MsgInfo *msginfo, MsgPermFlags perm_flags, MsgTmpFlags tmp_flags)
2029 MsgInfoUpdate msginfo_update;
2030 MsgPermFlags perm_flags_new, perm_flags_old;
2031 MsgTmpFlags tmp_flags_old;
2033 cm_return_if_fail(msginfo != NULL);
2034 item = msginfo->folder;
2035 cm_return_if_fail(item != NULL);
2037 debug_print("Unsetting flags for message %d in folder %s\n", msginfo->msgnum, item->path);
2039 /* Perm Flags handling */
2040 perm_flags_old = msginfo->flags.perm_flags;
2041 perm_flags_new = msginfo->flags.perm_flags & ~perm_flags;
2043 if (perm_flags_old != perm_flags_new) {
2044 folder_item_change_msg_flags(msginfo->folder, msginfo, perm_flags_new);
2046 update_folder_msg_counts(item, msginfo, perm_flags_old);
2049 /* Tmp flags hanlding */
2050 tmp_flags_old = msginfo->flags.tmp_flags;
2051 msginfo->flags.tmp_flags &= ~tmp_flags;
2053 /* update notification */
2054 if ((perm_flags_old != perm_flags_new) || (tmp_flags_old != msginfo->flags.tmp_flags)) {
2055 msginfo_update.msginfo = msginfo;
2056 msginfo_update.flags = MSGINFO_UPDATE_FLAGS;
2057 hooks_invoke(MSGINFO_UPDATE_HOOKLIST, &msginfo_update);
2058 folder_item_update(msginfo->folder, F_ITEM_UPDATE_MSGCNT);
2062 void procmsg_msginfo_change_flags(MsgInfo *msginfo,
2063 MsgPermFlags add_perm_flags, MsgTmpFlags add_tmp_flags,
2064 MsgPermFlags rem_perm_flags, MsgTmpFlags rem_tmp_flags)
2067 MsgInfoUpdate msginfo_update;
2068 MsgPermFlags perm_flags_new, perm_flags_old;
2069 MsgTmpFlags tmp_flags_old;
2071 cm_return_if_fail(msginfo != NULL);
2072 item = msginfo->folder;
2073 cm_return_if_fail(item != NULL);
2075 debug_print("Changing flags for message %d in folder %s\n", msginfo->msgnum, item->path);
2077 /* Perm Flags handling */
2078 perm_flags_old = msginfo->flags.perm_flags;
2079 perm_flags_new = (msginfo->flags.perm_flags & ~rem_perm_flags) | add_perm_flags;
2080 if ((add_perm_flags & MSG_IGNORE_THREAD) || (perm_flags_old & MSG_IGNORE_THREAD)) {
2081 perm_flags_new &= ~(MSG_NEW | MSG_UNREAD);
2083 if ((add_perm_flags & MSG_WATCH_THREAD) || (perm_flags_old & MSG_WATCH_THREAD)) {
2084 perm_flags_new &= ~(MSG_IGNORE_THREAD);
2087 if (perm_flags_old != perm_flags_new) {
2088 folder_item_change_msg_flags(msginfo->folder, msginfo, perm_flags_new);
2090 update_folder_msg_counts(item, msginfo, perm_flags_old);
2094 /* Tmp flags handling */
2095 tmp_flags_old = msginfo->flags.tmp_flags;
2096 msginfo->flags.tmp_flags &= ~rem_tmp_flags;
2097 msginfo->flags.tmp_flags |= add_tmp_flags;
2099 /* update notification */
2100 if ((perm_flags_old != perm_flags_new) || (tmp_flags_old != msginfo->flags.tmp_flags)) {
2101 msginfo_update.msginfo = msginfo;
2102 msginfo_update.flags = MSGINFO_UPDATE_FLAGS;
2103 hooks_invoke(MSGINFO_UPDATE_HOOKLIST, &msginfo_update);
2104 folder_item_update(msginfo->folder, F_ITEM_UPDATE_MSGCNT);
2109 *\brief check for flags (e.g. mark) in prior msgs of current thread
2111 *\param info Current message
2112 *\param perm_flags Flags to be checked
2113 *\param parentmsgs Hash of prior msgs to avoid loops
2115 *\return gboolean TRUE if perm_flags are found
2117 static gboolean procmsg_msg_has_flagged_parent_real(MsgInfo *info,
2118 MsgPermFlags perm_flags, GHashTable *parentmsgs)
2122 cm_return_val_if_fail(info != NULL, FALSE);
2124 if (info != NULL && info->folder != NULL && info->inreplyto != NULL) {
2125 tmp = folder_item_get_msginfo_by_msgid(info->folder,
2127 if (tmp && (tmp->flags.perm_flags & perm_flags)) {
2128 procmsg_msginfo_free(tmp);
2130 } else if (tmp != NULL) {
2133 if (g_hash_table_lookup(parentmsgs, info)) {
2134 debug_print("loop detected: %d\n",
2138 g_hash_table_insert(parentmsgs, info, "1");
2139 result = procmsg_msg_has_flagged_parent_real(
2140 tmp, perm_flags, parentmsgs);
2142 procmsg_msginfo_free(tmp);
2152 *\brief Callback for cleaning up hash of parentmsgs
2154 static gboolean parentmsgs_hash_remove(gpointer key,
2162 *\brief Set up list of parentmsgs
2163 * See procmsg_msg_has_flagged_parent_real()
2165 gboolean procmsg_msg_has_flagged_parent(MsgInfo *info, MsgPermFlags perm_flags)
2168 static GHashTable *parentmsgs = NULL;
2170 if (parentmsgs == NULL)
2171 parentmsgs = g_hash_table_new(NULL, NULL);
2173 result = procmsg_msg_has_flagged_parent_real(info, perm_flags, parentmsgs);
2174 g_hash_table_foreach_remove(parentmsgs, parentmsgs_hash_remove, NULL);
2180 *\brief Check if msgs prior in thread are marked
2181 * See procmsg_msg_has_flagged_parent_real()
2183 gboolean procmsg_msg_has_marked_parent(MsgInfo *info)
2185 return procmsg_msg_has_flagged_parent(info, MSG_MARKED);
2189 static GSList *procmsg_find_children_func(MsgInfo *info,
2190 GSList *children, GSList *all)
2194 cm_return_val_if_fail(info!=NULL, children);
2195 if (info->msgid == NULL)
2198 for (cur = all; cur != NULL; cur = g_slist_next(cur)) {
2199 MsgInfo *tmp = (MsgInfo *)cur->data;
2200 if (tmp->inreplyto && !strcmp(tmp->inreplyto, info->msgid)) {
2201 /* Check if message is already in the list */
2202 if ((children == NULL) ||
2203 (g_slist_index(children, tmp) == -1)) {
2204 children = g_slist_prepend(children,
2205 procmsg_msginfo_new_ref(tmp));
2206 children = procmsg_find_children_func(tmp,
2215 static GSList *procmsg_find_children (MsgInfo *info)
2220 cm_return_val_if_fail(info!=NULL, NULL);
2221 all = folder_item_get_msg_list(info->folder);
2222 children = procmsg_find_children_func(info, NULL, all);
2223 if (children != NULL) {
2224 for (cur = all; cur != NULL; cur = g_slist_next(cur)) {
2225 /* this will not free the used pointers
2226 created with procmsg_msginfo_new_ref */
2227 procmsg_msginfo_free((MsgInfo *)cur->data);
2235 static void procmsg_update_unread_children(MsgInfo *info, gboolean newly_marked)
2237 GSList *children = procmsg_find_children(info);
2239 for (cur = children; cur != NULL; cur = g_slist_next(cur)) {
2240 MsgInfo *tmp = (MsgInfo *)cur->data;
2241 if(MSG_IS_UNREAD(tmp->flags) && !MSG_IS_IGNORE_THREAD(tmp->flags)) {
2243 info->folder->unreadmarked_msgs++;
2245 info->folder->unreadmarked_msgs--;
2246 folder_item_update(info->folder, F_ITEM_UPDATE_MSGCNT);
2248 procmsg_msginfo_free(tmp);
2250 g_slist_free(children);
2254 * Set the destination folder for a copy or move operation
2256 * \param msginfo The message which's destination folder is changed
2257 * \param to_folder The destination folder for the operation
2259 void procmsg_msginfo_set_to_folder(MsgInfo *msginfo, FolderItem *to_folder)
2261 if(msginfo->to_folder != NULL) {
2262 msginfo->to_folder->op_count--;
2263 folder_item_update(msginfo->to_folder, F_ITEM_UPDATE_MSGCNT);
2265 msginfo->to_folder = to_folder;
2266 if(to_folder != NULL) {
2267 to_folder->op_count++;
2268 folder_item_update(msginfo->to_folder, F_ITEM_UPDATE_MSGCNT);
2273 * Apply filtering actions to the msginfo
2275 * \param msginfo The MsgInfo describing the message that should be filtered
2276 * \return TRUE if the message was moved and MsgInfo is now invalid,
2279 static gboolean procmsg_msginfo_filter(MsgInfo *msginfo, PrefsAccount* ac_prefs)
2281 MailFilteringData mail_filtering_data;
2283 mail_filtering_data.msginfo = msginfo;
2284 mail_filtering_data.msglist = NULL;
2285 mail_filtering_data.filtered = NULL;
2286 mail_filtering_data.unfiltered = NULL;
2287 mail_filtering_data.account = ac_prefs;
2289 if (!ac_prefs || ac_prefs->filterhook_on_recv)
2290 if (hooks_invoke(MAIL_FILTERING_HOOKLIST, &mail_filtering_data))
2293 /* filter if enabled in prefs or move to inbox if not */
2294 if((filtering_rules != NULL) &&
2295 filter_message_by_msginfo(filtering_rules, msginfo, ac_prefs,
2296 FILTERING_INCORPORATION, NULL)) {
2303 void procmsg_msglist_filter(GSList *list, PrefsAccount *ac,
2304 GSList **filtered, GSList **unfiltered,
2307 GSList *cur, *to_do = NULL;
2308 gint total = 0, curnum = 0;
2309 MailFilteringData mail_filtering_data;
2311 cm_return_if_fail(filtered != NULL);
2312 cm_return_if_fail(unfiltered != NULL);
2320 total = g_slist_length(list);
2324 *unfiltered = g_slist_copy(list);
2328 statusbar_print_all(_("Filtering messages...\n"));
2330 mail_filtering_data.msginfo = NULL;
2331 mail_filtering_data.msglist = list;
2332 mail_filtering_data.filtered = NULL;
2333 mail_filtering_data.unfiltered = NULL;
2334 mail_filtering_data.account = ac;
2336 if (!ac || ac->filterhook_on_recv)
2337 hooks_invoke(MAIL_LISTFILTERING_HOOKLIST, &mail_filtering_data);
2339 if (mail_filtering_data.filtered == NULL &&
2340 mail_filtering_data.unfiltered == NULL) {
2341 /* nothing happened */
2342 debug_print(MAIL_LISTFILTERING_HOOKLIST " did nothing. filtering whole list normally.\n");
2345 if (mail_filtering_data.filtered != NULL) {
2346 /* keep track of what's been filtered by the hooks */
2347 debug_print(MAIL_LISTFILTERING_HOOKLIST " filtered some 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));
2352 *filtered = g_slist_copy(mail_filtering_data.filtered);
2354 if (mail_filtering_data.unfiltered != NULL) {
2355 /* what the hooks didn't handle will go in filtered or
2356 * unfiltered in the next loop */
2357 debug_print(MAIL_LISTFILTERING_HOOKLIST " left unfiltered stuff. total %d filtered %d unfilt %d.\n",
2358 g_slist_length(list),
2359 g_slist_length(mail_filtering_data.filtered),
2360 g_slist_length(mail_filtering_data.unfiltered));
2361 to_do = mail_filtering_data.unfiltered;
2364 for (cur = to_do; cur; cur = cur->next) {
2365 MsgInfo *info = (MsgInfo *)cur->data;
2366 if (procmsg_msginfo_filter(info, ac))
2367 *filtered = g_slist_prepend(*filtered, info);
2369 *unfiltered = g_slist_prepend(*unfiltered, info);
2370 statusbar_progress_all(curnum++, total, prefs_common.statusbar_update_step);
2373 g_slist_free(mail_filtering_data.filtered);
2374 g_slist_free(mail_filtering_data.unfiltered);
2376 *filtered = g_slist_reverse(*filtered);
2377 *unfiltered = g_slist_reverse(*unfiltered);
2379 statusbar_progress_all(0,0,0);
2380 statusbar_pop_all();
2383 MsgInfo *procmsg_msginfo_new_from_mimeinfo(MsgInfo *src_msginfo, MimeInfo *mimeinfo)
2385 MsgInfo *tmp_msginfo = NULL;
2386 MsgFlags flags = {0, 0};
2387 gchar *tmpfile = get_tmp_file();
2388 FILE *fp = g_fopen(tmpfile, "wb");
2390 if (!mimeinfo || mimeinfo->type != MIMETYPE_MESSAGE ||
2391 g_ascii_strcasecmp(mimeinfo->subtype, "rfc822")) {
2392 g_warning("procmsg_msginfo_new_from_mimeinfo(): unsuitable mimeinfo");
2399 if (fp && procmime_write_mimeinfo(mimeinfo, fp) >= 0) {
2402 tmp_msginfo = procheader_parse_file(
2409 if (tmp_msginfo != NULL) {
2411 tmp_msginfo->folder = src_msginfo->folder;
2412 tmp_msginfo->plaintext_file = g_strdup(tmpfile);
2414 g_warning("procmsg_msginfo_new_from_mimeinfo(): Can't generate new msginfo");
2422 static GSList *spam_learners = NULL;
2424 void procmsg_register_spam_learner (int (*learn_func)(MsgInfo *info, GSList *list, gboolean spam))
2426 if (!g_slist_find(spam_learners, learn_func))
2427 spam_learners = g_slist_append(spam_learners, learn_func);
2428 if (mainwindow_get_mainwindow()) {
2429 main_window_set_menu_sensitive(mainwindow_get_mainwindow());
2430 summary_set_menu_sensitive(
2431 mainwindow_get_mainwindow()->summaryview);
2432 toolbar_main_set_sensitive(mainwindow_get_mainwindow());
2436 void procmsg_unregister_spam_learner (int (*learn_func)(MsgInfo *info, GSList *list, gboolean spam))
2438 spam_learners = g_slist_remove(spam_learners, learn_func);
2439 if (mainwindow_get_mainwindow()) {
2440 main_window_set_menu_sensitive(mainwindow_get_mainwindow());
2441 summary_set_menu_sensitive(
2442 mainwindow_get_mainwindow()->summaryview);
2443 toolbar_main_set_sensitive(mainwindow_get_mainwindow());
2447 gboolean procmsg_spam_can_learn(void)
2449 return g_slist_length(spam_learners) > 0;
2452 int procmsg_spam_learner_learn (MsgInfo *info, GSList *list, gboolean spam)
2454 GSList *cur = spam_learners;
2456 for (; cur; cur = cur->next) {
2457 int ((*func)(MsgInfo *info, GSList *list, gboolean spam)) = cur->data;
2458 ret |= func(info, list, spam);
2463 static gchar *spam_folder_item = NULL;
2464 static FolderItem * (*procmsg_spam_get_folder_func)(MsgInfo *msginfo) = NULL;
2465 void procmsg_spam_set_folder (const char *item_identifier, FolderItem *(*spam_get_folder_func)(MsgInfo *info))
2467 g_free(spam_folder_item);
2468 if (item_identifier)
2469 spam_folder_item = g_strdup(item_identifier);
2471 spam_folder_item = NULL;
2472 if (spam_get_folder_func != NULL)
2473 procmsg_spam_get_folder_func = spam_get_folder_func;
2475 procmsg_spam_get_folder_func = NULL;
2478 FolderItem *procmsg_spam_get_folder (MsgInfo *msginfo)
2480 FolderItem *item = NULL;
2482 if (procmsg_spam_get_folder_func)
2483 item = procmsg_spam_get_folder_func(msginfo);
2484 if (item == NULL && spam_folder_item)
2485 item = folder_find_item_from_identifier(spam_folder_item);
2487 item = folder_get_default_trash();
2491 static void item_has_queued_mails(FolderItem *item, gpointer data)
2493 gboolean *result = (gboolean *)data;
2494 if (*result == TRUE)
2496 if (folder_has_parent_of_type(item, F_QUEUE)) {
2497 if (item->total_msgs == 0)
2500 GSList *msglist = folder_item_get_msg_list(item);
2502 for (cur = msglist; cur; cur = cur->next) {
2503 MsgInfo *msginfo = (MsgInfo *)cur->data;
2504 if (!MSG_IS_DELETED(msginfo->flags) &&
2505 !MSG_IS_LOCKED(msginfo->flags)) {
2510 procmsg_msg_list_free(msglist);
2515 gboolean procmsg_have_queued_mails_fast (void)
2517 gboolean result = FALSE;
2518 folder_func_to_all_folders(item_has_queued_mails, &result);
2522 static void item_has_trashed_mails(FolderItem *item, gpointer data)
2524 gboolean *result = (gboolean *)data;
2525 if (*result == TRUE)
2527 if (folder_has_parent_of_type(item, F_TRASH) && item->total_msgs > 0)
2531 gboolean procmsg_have_trashed_mails_fast (void)
2533 gboolean result = FALSE;
2534 folder_func_to_all_folders(item_has_trashed_mails, &result);
2538 gchar *procmsg_msginfo_get_tags_str(MsgInfo *msginfo)
2546 if (msginfo->tags == NULL)
2548 for (cur = msginfo->tags; cur; cur = cur->next) {
2549 const gchar *tag = tags_get_tag(GPOINTER_TO_INT(cur->data));
2553 tags = g_strdup(tag);
2555 int olen = strlen(tags);
2556 int nlen = olen + strlen(tag) + 2 /* strlen(", ") */;
2557 tags = g_realloc(tags, nlen+1);
2560 strcpy(tags+olen, ", ");
2561 strcpy(tags+olen+2, tag);
2568 void procmsg_msginfo_update_tags(MsgInfo *msginfo, gboolean set, gint id)
2576 msginfo->tags = g_slist_remove(
2578 GINT_TO_POINTER(id));
2579 changed.data = GINT_TO_POINTER(id);
2580 changed.next = NULL;
2581 folder_item_commit_tags(msginfo->folder, msginfo, NULL, &changed);
2583 if (!g_slist_find(msginfo->tags, GINT_TO_POINTER(id))) {
2584 msginfo->tags = g_slist_append(
2586 GINT_TO_POINTER(id));
2588 changed.data = GINT_TO_POINTER(id);
2589 changed.next = NULL;
2590 folder_item_commit_tags(msginfo->folder, msginfo, &changed, NULL);
2595 void procmsg_msginfo_clear_tags(MsgInfo *msginfo)
2597 GSList *unset = msginfo->tags;
2598 msginfo->tags = NULL;
2599 folder_item_commit_tags(msginfo->folder, msginfo, NULL, unset);
2600 g_slist_free(unset);