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);
92 MsgNumberList *procmsg_get_number_list_for_msgs(MsgInfoList *msglist)
97 for (cur = msglist; cur; cur = cur->next) {
98 MsgInfo *msg = (MsgInfo *)cur->data;
99 nums = g_slist_prepend(nums, GUINT_TO_POINTER(msg->msgnum));
102 return g_slist_reverse(nums);
114 /* CLAWS subject threading:
116 in the first round it inserts subject lines in a
117 hashtable (subject <-> node)
119 the second round finishes the threads by attaching
120 matching subject lines to the one found in the
121 hashtable. will use the oldest node with the same
122 subject that is not more then thread_by_subject_max_age
123 days old (see subject_hashtable_lookup)
126 static void subject_hashtable_insert(GHashTable *hashtable, GNode *node)
132 cm_return_if_fail(hashtable != NULL);
133 cm_return_if_fail(node != NULL);
134 msginfo = (MsgInfo *) node->data;
135 cm_return_if_fail(msginfo != NULL);
137 subject = msginfo->subject;
141 subject += subject_get_prefix_length(subject);
143 list = g_hash_table_lookup(hashtable, subject);
144 list = g_slist_prepend(list, node);
145 g_hash_table_insert(hashtable, subject, list);
148 static GNode *subject_hashtable_lookup(GHashTable *hashtable, MsgInfo *msginfo)
152 GNode *node = NULL, *hashtable_node = NULL;
154 MsgInfo *hashtable_msginfo = NULL, *best_msginfo = NULL;
157 cm_return_val_if_fail(hashtable != NULL, NULL);
159 subject = msginfo->subject;
162 prefix_length = subject_get_prefix_length(subject);
163 if (prefix_length <= 0)
165 subject += prefix_length;
167 list = g_hash_table_lookup(hashtable, subject);
171 /* check all nodes with the same subject to find the best parent */
172 for (cur = list; cur; cur = cur->next) {
173 hashtable_node = (GNode *)cur->data;
174 hashtable_msginfo = (MsgInfo *) hashtable_node->data;
177 /* best node should be the oldest in the found nodes */
178 /* parent node must not be older then msginfo */
179 if ((hashtable_msginfo->date_t < msginfo->date_t) &&
180 ((best_msginfo == NULL) ||
181 (best_msginfo->date_t > hashtable_msginfo->date_t)))
184 /* parent node must not be more then thread_by_subject_max_age
185 days older then msginfo */
186 if (abs(difftime(msginfo->date_t, hashtable_msginfo->date_t)) >
187 prefs_common.thread_by_subject_max_age * 3600 * 24)
190 /* can add new tests for all matching
191 nodes found by subject */
194 node = hashtable_node;
195 best_msginfo = hashtable_msginfo;
202 static void subject_hashtable_free(gpointer key, gpointer value, gpointer data)
207 /* return the reversed thread tree */
208 GNode *procmsg_get_thread_tree(GSList *mlist)
210 GNode *root, *parent, *node, *next;
211 GHashTable *msgid_table;
212 GHashTable *subject_hashtable = NULL;
217 root = g_node_new(NULL);
218 msgid_table = g_hash_table_new(g_str_hash, g_str_equal);
220 if (prefs_common.thread_by_subject) {
221 subject_hashtable = g_hash_table_new(g_str_hash, g_str_equal);
224 for (; mlist != NULL; mlist = mlist->next) {
225 msginfo = (MsgInfo *)mlist->data;
228 if (msginfo->inreplyto) {
229 parent = g_hash_table_lookup(msgid_table, msginfo->inreplyto);
230 if (parent == NULL) {
234 node = g_node_insert_data_before
235 (parent, parent == root ? parent->children : NULL,
237 if ((msgid = msginfo->msgid) && g_hash_table_lookup(msgid_table, msgid) == NULL)
238 g_hash_table_insert(msgid_table, (gchar *)msgid, node);
240 /* CLAWS: add subject to hashtable (without prefix) */
241 if (prefs_common.thread_by_subject) {
242 subject_hashtable_insert(subject_hashtable, node);
246 /* complete the unfinished threads */
247 for (node = root->children; node != NULL; ) {
249 msginfo = (MsgInfo *)node->data;
252 if (msginfo->inreplyto)
253 parent = g_hash_table_lookup(msgid_table, msginfo->inreplyto);
255 /* try looking for the indirect parent */
256 if (!parent && msginfo->references) {
257 for (reflist = msginfo->references;
258 reflist != NULL; reflist = reflist->next)
259 if ((parent = g_hash_table_lookup
260 (msgid_table, reflist->data)) != NULL)
264 /* node should not be the parent, and node should not
265 be an ancestor of parent (circular reference) */
266 if (parent && parent != node &&
267 !g_node_is_ancestor(node, parent)) {
270 (parent, parent->children, node);
276 if (prefs_common.thread_by_subject) {
277 START_TIMING("thread by subject");
278 for (node = root->children; node && node != NULL;) {
280 msginfo = (MsgInfo *) node->data;
282 parent = subject_hashtable_lookup(subject_hashtable, msginfo);
284 /* the node may already be threaded by IN-REPLY-TO, so go up
286 find the parent node */
287 if (parent != NULL) {
288 if (g_node_is_ancestor(node, parent))
296 g_node_append(parent, node);
304 if (prefs_common.thread_by_subject)
306 g_hash_table_foreach(subject_hashtable, subject_hashtable_free, NULL);
307 g_hash_table_destroy(subject_hashtable);
310 g_hash_table_destroy(msgid_table);
315 gint procmsg_move_messages(GSList *mlist)
317 GSList *cur, *movelist = NULL;
319 FolderItem *dest = NULL;
321 gboolean finished = TRUE;
322 if (!mlist) return 0;
324 folder_item_update_freeze();
327 for (cur = mlist; cur != NULL; cur = cur->next) {
328 msginfo = (MsgInfo *)cur->data;
329 if (!msginfo->to_folder) {
335 dest = msginfo->to_folder;
336 movelist = g_slist_prepend(movelist, msginfo);
337 } else if (dest == msginfo->to_folder) {
338 movelist = g_slist_prepend(movelist, msginfo);
342 procmsg_msginfo_set_to_folder(msginfo, NULL);
345 movelist = g_slist_reverse(movelist);
346 retval |= folder_item_move_msgs(dest, movelist);
347 g_slist_free(movelist);
350 if (finished == FALSE) {
356 folder_item_update_thaw();
360 void procmsg_copy_messages(GSList *mlist)
362 GSList *cur, *copylist = NULL;
364 FolderItem *dest = NULL;
365 gboolean finished = TRUE;
368 folder_item_update_freeze();
371 for (cur = mlist; cur != NULL; cur = cur->next) {
372 msginfo = (MsgInfo *)cur->data;
373 if (!msginfo->to_folder) {
379 dest = msginfo->to_folder;
380 copylist = g_slist_prepend(copylist, msginfo);
381 } else if (dest == msginfo->to_folder) {
382 copylist = g_slist_prepend(copylist, msginfo);
386 procmsg_msginfo_set_to_folder(msginfo, NULL);
389 copylist = g_slist_reverse(copylist);
390 folder_item_copy_msgs(dest, copylist);
391 g_slist_free(copylist);
394 if (finished == FALSE) {
400 folder_item_update_thaw();
403 gchar *procmsg_get_message_file_path(MsgInfo *msginfo)
407 cm_return_val_if_fail(msginfo != NULL, NULL);
409 if (msginfo->plaintext_file)
410 file = g_strdup(msginfo->plaintext_file);
412 file = folder_item_fetch_msg(msginfo->folder, msginfo->msgnum);
418 gchar *procmsg_get_message_file(MsgInfo *msginfo)
420 gchar *filename = NULL;
422 cm_return_val_if_fail(msginfo != NULL, NULL);
424 filename = folder_item_fetch_msg(msginfo->folder, msginfo->msgnum);
426 debug_print("can't fetch message %d\n", msginfo->msgnum);
431 gchar *procmsg_get_message_file_full(MsgInfo *msginfo, gboolean headers, gboolean body)
433 gchar *filename = NULL;
435 cm_return_val_if_fail(msginfo != NULL, NULL);
437 filename = folder_item_fetch_msg_full(msginfo->folder, msginfo->msgnum,
440 debug_print("can't fetch message %d\n", msginfo->msgnum);
445 GSList *procmsg_get_message_file_list(GSList *mlist)
447 GSList *file_list = NULL;
449 MsgFileInfo *fileinfo;
452 while (mlist != NULL) {
453 msginfo = (MsgInfo *)mlist->data;
454 file = procmsg_get_message_file(msginfo);
456 procmsg_message_file_list_free(file_list);
459 fileinfo = g_new(MsgFileInfo, 1);
460 fileinfo->msginfo = procmsg_msginfo_new_ref(msginfo);
461 fileinfo->file = file;
462 fileinfo->flags = g_new(MsgFlags, 1);
463 *fileinfo->flags = msginfo->flags;
464 file_list = g_slist_prepend(file_list, fileinfo);
468 file_list = g_slist_reverse(file_list);
473 void procmsg_message_file_list_free(MsgInfoList *file_list)
476 MsgFileInfo *fileinfo;
478 for (cur = file_list; cur != NULL; cur = cur->next) {
479 fileinfo = (MsgFileInfo *)cur->data;
480 procmsg_msginfo_free(fileinfo->msginfo);
481 g_free(fileinfo->file);
482 g_free(fileinfo->flags);
486 g_slist_free(file_list);
489 FILE *procmsg_open_message(MsgInfo *msginfo)
494 cm_return_val_if_fail(msginfo != NULL, NULL);
496 file = procmsg_get_message_file_path(msginfo);
497 cm_return_val_if_fail(file != NULL, NULL);
499 if (!is_file_exist(file)) {
501 file = procmsg_get_message_file(msginfo);
506 if ((fp = g_fopen(file, "rb")) == NULL) {
507 FILE_OP_ERROR(file, "fopen");
514 if (MSG_IS_QUEUED(msginfo->flags) || MSG_IS_DRAFT(msginfo->flags)) {
517 while (fgets(buf, sizeof(buf), fp) != NULL) {
519 if ((!strncmp(buf, "X-Claws-End-Special-Headers: 1",
520 strlen("X-Claws-End-Special-Headers:"))) ||
521 (!strncmp(buf, "X-Sylpheed-End-Special-Headers: 1",
522 strlen("X-Sylpheed-End-Special-Headers:"))))
525 if (buf[0] == '\r' || buf[0] == '\n') break;
526 /* from other mailers */
527 if (!strncmp(buf, "Date: ", 6)
528 || !strncmp(buf, "To: ", 4)
529 || !strncmp(buf, "From: ", 6)
530 || !strncmp(buf, "Subject: ", 9)) {
540 gboolean procmsg_msg_exist(MsgInfo *msginfo)
545 if (!msginfo) return FALSE;
547 path = folder_item_get_path(msginfo->folder);
549 ret = !folder_item_is_msg_changed(msginfo->folder, msginfo);
555 void procmsg_get_filter_keyword(MsgInfo *msginfo, gchar **header, gchar **key,
556 PrefsFilterType type)
558 static HeaderEntry hentry[] = {{"X-BeenThere:", NULL, TRUE},
559 {"X-ML-Name:", NULL, TRUE},
560 {"X-List:", NULL, TRUE},
561 {"X-Mailing-list:", NULL, TRUE},
562 {"List-Id:", NULL, TRUE},
563 {"X-Sequence:", NULL, TRUE},
564 {"Sender:", NULL, TRUE},
565 {"List-Post:", NULL, TRUE},
566 {NULL, NULL, FALSE}};
572 H_X_MAILING_LIST = 3,
581 cm_return_if_fail(msginfo != NULL);
582 cm_return_if_fail(header != NULL);
583 cm_return_if_fail(key != NULL);
592 if ((fp = procmsg_open_message(msginfo)) == NULL)
594 procheader_get_header_fields(fp, hentry);
597 #define SET_FILTER_KEY(hstr, idx) \
599 *header = g_strdup(hstr); \
600 *key = hentry[idx].body; \
601 hentry[idx].body = NULL; \
604 if (hentry[H_LIST_ID].body != NULL) {
605 SET_FILTER_KEY("header \"List-Id\"", H_LIST_ID);
606 extract_list_id_str(*key);
607 } else if (hentry[H_X_BEENTHERE].body != NULL) {
608 SET_FILTER_KEY("header \"X-BeenThere\"", H_X_BEENTHERE);
609 } else if (hentry[H_X_ML_NAME].body != NULL) {
610 SET_FILTER_KEY("header \"X-ML-Name\"", H_X_ML_NAME);
611 } else if (hentry[H_X_LIST].body != NULL) {
612 SET_FILTER_KEY("header \"X-List\"", H_X_LIST);
613 } else if (hentry[H_X_MAILING_LIST].body != NULL) {
614 SET_FILTER_KEY("header \"X-Mailing-List\"", H_X_MAILING_LIST);
615 } else if (hentry[H_X_SEQUENCE].body != NULL) {
618 SET_FILTER_KEY("X-Sequence", H_X_SEQUENCE);
621 while (*p != '\0' && !g_ascii_isspace(*p)) p++;
622 while (g_ascii_isspace(*p)) p++;
623 if (g_ascii_isdigit(*p)) {
629 } else if (hentry[H_SENDER].body != NULL) {
630 SET_FILTER_KEY("header \"Sender\"", H_SENDER);
631 } else if (hentry[H_LIST_POST].body != NULL) {
632 SET_FILTER_KEY("header \"List-Post\"", H_LIST_POST);
633 } else if (msginfo->to) {
634 *header = g_strdup("to");
635 *key = g_strdup(msginfo->to);
636 } else if (msginfo->subject) {
637 *header = g_strdup("subject");
638 *key = g_strdup(msginfo->subject);
641 #undef SET_FILTER_KEY
643 g_free(hentry[H_X_BEENTHERE].body);
644 hentry[H_X_BEENTHERE].body = NULL;
645 g_free(hentry[H_X_ML_NAME].body);
646 hentry[H_X_ML_NAME].body = NULL;
647 g_free(hentry[H_X_LIST].body);
648 hentry[H_X_LIST].body = NULL;
649 g_free(hentry[H_X_MAILING_LIST].body);
650 hentry[H_X_MAILING_LIST].body = NULL;
651 g_free(hentry[H_LIST_ID].body);
652 hentry[H_LIST_ID].body = NULL;
653 g_free(hentry[H_SENDER].body);
654 hentry[H_SENDER].body = NULL;
655 g_free(hentry[H_LIST_POST].body);
656 hentry[H_LIST_POST].body = NULL;
660 *header = g_strdup("from");
661 *key = g_strdup(msginfo->from);
664 *header = g_strdup("to");
665 *key = g_strdup(msginfo->to);
667 case FILTER_BY_SUBJECT:
668 *header = g_strdup("subject");
669 *key = g_strdup(msginfo->subject);
676 static void procmsg_empty_trash(FolderItem *trash)
681 (trash->stype != F_TRASH &&
682 !folder_has_parent_of_type(trash, F_TRASH)))
685 if (trash && trash->total_msgs > 0) {
686 GSList *mlist = folder_item_get_msg_list(trash);
688 for (cur = mlist ; cur != NULL ; cur = cur->next) {
689 MsgInfo * msginfo = (MsgInfo *) cur->data;
690 if (MSG_IS_LOCKED(msginfo->flags)) {
691 procmsg_msginfo_free(msginfo);
694 if (msginfo->total_size != 0 &&
695 msginfo->size != (off_t)msginfo->total_size)
696 partial_mark_for_delete(msginfo);
698 procmsg_msginfo_free(msginfo);
701 folder_item_remove_all_msg(trash);
704 if (!trash->node || !trash->node->children)
707 node = trash->node->children;
708 while (node != NULL) {
710 procmsg_empty_trash(FOLDER_ITEM(node->data));
715 void procmsg_empty_all_trash(void)
720 for (cur = folder_get_list(); cur != NULL; cur = cur->next) {
721 Folder *folder = FOLDER(cur->data);
722 trash = folder->trash;
723 procmsg_empty_trash(trash);
724 if (folder->account && folder->account->set_trash_folder &&
725 folder_find_item_from_identifier(folder->account->trash_folder))
727 folder_find_item_from_identifier(folder->account->trash_folder));
731 static PrefsAccount *procmsg_get_account_from_file(const gchar *file)
733 PrefsAccount *mailac = NULL;
737 static HeaderEntry qentry[] = {{"S:", NULL, FALSE},
738 {"SSV:", NULL, FALSE},
740 {"NG:", NULL, FALSE},
741 {"MAID:", NULL, FALSE},
742 {"NAID:", NULL, FALSE},
743 {"SCF:", NULL, FALSE},
744 {"RMID:", NULL, FALSE},
745 {"FMID:", NULL, FALSE},
746 {"X-Claws-Privacy-System:", NULL, FALSE},
747 {"X-Claws-Encrypt:", NULL, FALSE},
748 {"X-Claws-Encrypt-Data:", NULL, FALSE},
749 {"X-Claws-End-Special-Headers", NULL, FALSE},
750 {"X-Sylpheed-Privacy-System:", NULL, FALSE},
751 {"X-Sylpheed-Encrypt:", NULL, FALSE},
752 {"X-Sylpheed-Encrypt-Data:", NULL, FALSE},
753 {NULL, NULL, FALSE}};
755 cm_return_val_if_fail(file != NULL, NULL);
757 if ((fp = g_fopen(file, "rb")) == NULL) {
758 FILE_OP_ERROR(file, "fopen");
762 while ((hnum = procheader_get_one_field(buf, sizeof(buf), fp, qentry))
764 gchar *p = buf + strlen(qentry[hnum].name);
766 if (hnum == Q_MAIL_ACCOUNT_ID) {
767 mailac = account_find_from_id(atoi(p));
775 gchar *procmsg_msginfo_get_identifier(MsgInfo *msginfo)
781 cm_return_val_if_fail(msginfo != NULL, NULL);
782 folder_id = folder_item_get_identifier(msginfo->folder);
783 msgid = msginfo->msgid;
785 id = g_strconcat(folder_id, G_DIR_SEPARATOR_S, msgid, NULL);
792 MsgInfo *procmsg_get_msginfo_from_identifier(const gchar *id)
794 gchar *folder_id = g_strdup(id);
795 gchar *separator = strrchr(folder_id, G_DIR_SEPARATOR);
800 if (separator == NULL) {
806 msgid = separator + 1;
808 item = folder_find_item_from_identifier(folder_id);
815 msginfo = folder_item_get_msginfo_by_msgid(item, msgid);
821 static GSList *procmsg_list_sort_by_account(FolderItem *queue, GSList *list)
823 GSList *result = NULL;
825 PrefsAccount *last_account = NULL;
828 gboolean nothing_to_sort = TRUE;
833 orig = g_slist_copy(list);
835 msg = (MsgInfo *)orig->data;
837 for (cur = orig; cur; cur = cur->next)
838 debug_print("sort before %s\n", ((MsgInfo *)cur->data)->from);
843 nothing_to_sort = TRUE;
847 PrefsAccount *ac = NULL;
848 msg = (MsgInfo *)cur->data;
849 file = folder_item_fetch_msg(queue, msg->msgnum);
850 ac = procmsg_get_account_from_file(file);
853 if (last_account == NULL || (ac != NULL && ac == last_account)) {
854 result = g_slist_append(result, msg);
855 orig = g_slist_remove(orig, msg);
857 nothing_to_sort = FALSE;
863 if (orig || g_slist_length(orig)) {
864 if (!last_account && nothing_to_sort) {
865 /* can't find an account for the rest of the list */
868 result = g_slist_append(result, cur->data);
879 for (cur = result; cur; cur = cur->next)
880 debug_print("sort after %s\n", ((MsgInfo *)cur->data)->from);
887 static gboolean procmsg_is_last_for_account(FolderItem *queue, MsgInfo *msginfo, GSList *elem)
889 gchar *file = folder_item_fetch_msg(queue, msginfo->msgnum);
890 PrefsAccount *ac = procmsg_get_account_from_file(file);
893 for (cur = elem; cur; cur = cur->next) {
894 MsgInfo *cur_msginfo = (MsgInfo *)cur->data;
895 file = folder_item_fetch_msg(queue, cur_msginfo->msgnum);
897 if (cur_msginfo != msginfo && !MSG_IS_LOCKED(cur_msginfo->flags)) {
898 if (procmsg_get_account_from_file(file) == ac) {
909 static gboolean send_queue_lock = FALSE;
911 gboolean procmsg_queue_lock(char **errstr)
913 if (send_queue_lock) {
914 /* Avoid having to translate two similar strings */
915 log_warning(LOG_PROTOCOL, "%s\n", _("Already trying to send."));
917 if (*errstr) g_free(*errstr);
918 *errstr = g_strdup_printf(_("Already trying to send."));
922 send_queue_lock = TRUE;
925 void procmsg_queue_unlock(void)
927 send_queue_lock = FALSE;
930 *\brief Send messages in queue
932 *\param queue Queue folder to process
933 *\param save_msgs Unused
935 *\return Number of messages sent, negative if an error occurred
936 * positive if no error occurred
938 gint procmsg_send_queue(FolderItem *queue, gboolean save_msgs, gchar **errstr)
940 gint sent = 0, err = 0;
942 GSList *sorted_list = NULL;
945 if (!procmsg_queue_lock(errstr)) {
946 main_window_set_menu_sensitive(mainwindow_get_mainwindow());
947 toolbar_main_set_sensitive(mainwindow_get_mainwindow());
952 queue = folder_get_default_queue();
955 procmsg_queue_unlock();
960 main_window_set_menu_sensitive(mainwindow_get_mainwindow());
961 toolbar_main_set_sensitive(mainwindow_get_mainwindow());
963 folder_item_scan(queue);
964 list = folder_item_get_msg_list(queue);
966 /* sort the list per sender account; this helps reusing the same SMTP server */
967 sorted_list = procmsg_list_sort_by_account(queue, list);
969 for (elem = sorted_list; elem != NULL; elem = elem->next) {
973 msginfo = (MsgInfo *)(elem->data);
974 if (!MSG_IS_LOCKED(msginfo->flags) && !MSG_IS_DELETED(msginfo->flags)) {
975 file = folder_item_fetch_msg(queue, msginfo->msgnum);
977 gboolean queued_removed = FALSE;
978 if (procmsg_send_message_queue_full(file,
979 !procmsg_is_last_for_account(queue, msginfo, elem),
980 errstr, queue, msginfo->msgnum, &queued_removed) < 0) {
981 g_warning("Sending queued message %d failed.\n",
987 folder_item_remove_msg(queue, msginfo->msgnum);
992 /* FIXME: supposedly if only one message is locked, and queue
993 * is being flushed, the following free says something like
994 * "freeing msg ## in folder (nil)". */
995 procmsg_msginfo_free(msginfo);
998 g_slist_free(sorted_list);
999 folder_item_scan(queue);
1001 if (queue->node && queue->node->children) {
1002 node = queue->node->children;
1003 while (node != NULL) {
1006 send_queue_lock = FALSE;
1007 res = procmsg_send_queue(FOLDER_ITEM(node->data), save_msgs, errstr);
1008 send_queue_lock = TRUE;
1016 procmsg_queue_unlock();
1018 main_window_set_menu_sensitive(mainwindow_get_mainwindow());
1019 toolbar_main_set_sensitive(mainwindow_get_mainwindow());
1021 return (err != 0 ? -err : sent);
1024 gboolean procmsg_is_sending(void)
1026 return send_queue_lock;
1030 *\brief Determine if a queue folder is empty
1032 *\param queue Queue folder to process
1034 *\return TRUE if the queue folder is empty, otherwise return FALSE
1036 gboolean procmsg_queue_is_empty(FolderItem *queue)
1039 gboolean res = FALSE;
1041 queue = folder_get_default_queue();
1042 cm_return_val_if_fail(queue != NULL, TRUE);
1044 folder_item_scan(queue);
1045 list = folder_item_get_msg_list(queue);
1046 res = (list == NULL);
1047 procmsg_msg_list_free(list);
1051 if (queue->node && queue->node->children) {
1052 node = queue->node->children;
1053 while (node != NULL) {
1055 if (!procmsg_queue_is_empty(FOLDER_ITEM(node->data)))
1064 gint procmsg_remove_special_headers(const gchar *in, const gchar *out)
1067 gchar buf[BUFFSIZE];
1069 if ((fp = g_fopen(in, "rb")) == NULL) {
1070 FILE_OP_ERROR(in, "fopen");
1073 if ((outfp = g_fopen(out, "wb")) == NULL) {
1074 FILE_OP_ERROR(out, "fopen");
1078 while (fgets(buf, sizeof(buf), fp) != NULL) {
1080 if ((!strncmp(buf, "X-Claws-End-Special-Headers: 1",
1081 strlen("X-Claws-End-Special-Headers:"))) ||
1082 (!strncmp(buf, "X-Sylpheed-End-Special-Headers: 1",
1083 strlen("X-Sylpheed-End-Special-Headers:"))))
1086 if (buf[0] == '\r' || buf[0] == '\n') break;
1087 /* from other mailers */
1088 if (!strncmp(buf, "Date: ", 6)
1089 || !strncmp(buf, "To: ", 4)
1090 || !strncmp(buf, "From: ", 6)
1091 || !strncmp(buf, "Subject: ", 9)) {
1096 while (fgets(buf, sizeof(buf), fp) != NULL)
1103 static gint procmsg_save_to_outbox(FolderItem *outbox, const gchar *file,
1107 MsgInfo *msginfo, *tmp_msginfo;
1108 MsgFlags flag = {0, 0};
1110 debug_print("saving sent message...\n");
1113 outbox = folder_get_default_outbox();
1114 cm_return_val_if_fail(outbox != NULL, -1);
1116 /* remove queueing headers */
1118 gchar tmp[MAXPATHLEN + 1];
1120 g_snprintf(tmp, sizeof(tmp), "%s%ctmpmsg.out.%08x",
1121 get_rc_dir(), G_DIR_SEPARATOR, (guint) rand());
1123 if (procmsg_remove_special_headers(file, tmp) !=0)
1126 folder_item_scan(outbox);
1127 if ((num = folder_item_add_msg(outbox, tmp, &flag, TRUE)) < 0) {
1128 g_warning("can't save message\n");
1133 folder_item_scan(outbox);
1134 if ((num = folder_item_add_msg
1135 (outbox, file, &flag, FALSE)) < 0) {
1136 g_warning("can't save message\n");
1140 msginfo = folder_item_get_msginfo(outbox, num); /* refcnt++ */
1141 tmp_msginfo = procmsg_msginfo_get_full_info(msginfo); /* refcnt++ */
1142 if (msginfo != NULL) {
1143 procmsg_msginfo_unset_flags(msginfo, ~0, 0);
1144 procmsg_msginfo_free(msginfo); /* refcnt-- */
1145 /* tmp_msginfo == msginfo */
1146 if (tmp_msginfo && msginfo->extradata &&
1147 (msginfo->extradata->dispositionnotificationto ||
1148 msginfo->extradata->returnreceiptto)) {
1149 procmsg_msginfo_set_flags(msginfo, MSG_RETRCPT_SENT, 0);
1151 procmsg_msginfo_free(tmp_msginfo); /* refcnt-- */
1158 void procmsg_print_message(MsgInfo *msginfo, const gchar *cmdline)
1160 static const gchar *def_cmd = "lpr %s";
1161 static guint id = 0;
1168 cm_return_if_fail(msginfo);
1170 if (procmime_msginfo_is_encrypted(msginfo))
1171 tmpfp = procmime_get_first_encrypted_text_content(msginfo);
1173 tmpfp = procmime_get_first_text_content(msginfo);
1174 if (tmpfp == NULL) {
1175 g_warning("Can't get text part\n");
1179 prtmp = g_strdup_printf("%s%cprinttmp.%08x",
1180 get_mime_tmp_dir(), G_DIR_SEPARATOR, id++);
1182 if ((prfp = g_fopen(prtmp, "wb")) == NULL) {
1183 FILE_OP_ERROR(prtmp, "fopen");
1189 if (msginfo->date) { r = fprintf(prfp, "Date: %s\n", msginfo->date); if (r < 0) goto fpferr; }
1190 if (msginfo->from) { r = fprintf(prfp, "From: %s\n", msginfo->from); if (r < 0) goto fpferr; }
1191 if (msginfo->to) { r = fprintf(prfp, "To: %s\n", msginfo->to); if (r < 0) goto fpferr; }
1192 if (msginfo->cc) { r = fprintf(prfp, "Cc: %s\n", msginfo->cc); if (r < 0) goto fpferr; }
1193 if (msginfo->newsgroups) {
1194 r = fprintf(prfp, "Newsgroups: %s\n", msginfo->newsgroups); if (r < 0) goto fpferr;
1196 if (msginfo->subject) { r = fprintf(prfp, "Subject: %s\n", msginfo->subject); if (r < 0) goto fpferr; }
1197 if (fputc('\n', prfp) == EOF) goto fpferr;
1199 while (fgets(buf, sizeof(buf), tmpfp) != NULL) {
1200 r = fputs(buf, prfp);
1201 if (r == EOF) goto fpferr;
1207 if (cmdline && (p = strchr(cmdline, '%')) && *(p + 1) == 's' &&
1208 !strchr(p + 2, '%'))
1209 g_snprintf(buf, sizeof(buf) - 1, cmdline, prtmp);
1212 g_warning("Print command-line is invalid: '%s'\n",
1214 g_snprintf(buf, sizeof(buf) - 1, def_cmd, prtmp);
1220 if (buf[strlen(buf) - 1] != '&')
1221 strncat(buf, "&", sizeof(buf) - strlen(buf) - 1);
1222 if (system(buf) == -1)
1223 g_warning("system(%s) failed.", buf);
1226 FILE_OP_ERROR(prtmp, "fprintf/fputc/fputs");
1232 MsgInfo *procmsg_msginfo_new_ref(MsgInfo *msginfo)
1239 MsgInfo *procmsg_msginfo_new(void)
1241 MsgInfo *newmsginfo;
1243 newmsginfo = g_new0(MsgInfo, 1);
1244 newmsginfo->refcnt = 1;
1249 MsgInfo *procmsg_msginfo_copy(MsgInfo *msginfo)
1251 MsgInfo *newmsginfo;
1254 if (msginfo == NULL) return NULL;
1256 newmsginfo = g_new0(MsgInfo, 1);
1258 newmsginfo->refcnt = 1;
1260 #define MEMBCOPY(mmb) newmsginfo->mmb = msginfo->mmb
1261 #define MEMBDUP(mmb) newmsginfo->mmb = msginfo->mmb ? \
1262 g_strdup(msginfo->mmb) : NULL
1277 MEMBDUP(newsgroups);
1284 MEMBCOPY(to_folder);
1286 if (msginfo->extradata) {
1287 newmsginfo->extradata = g_new0(MsgInfoExtraData, 1);
1288 MEMBDUP(extradata->face);
1289 MEMBDUP(extradata->xface);
1290 MEMBDUP(extradata->dispositionnotificationto);
1291 MEMBDUP(extradata->returnreceiptto);
1292 MEMBDUP(extradata->partial_recv);
1293 MEMBDUP(extradata->account_server);
1294 MEMBDUP(extradata->account_login);
1295 MEMBDUP(extradata->list_post);
1296 MEMBDUP(extradata->list_subscribe);
1297 MEMBDUP(extradata->list_unsubscribe);
1298 MEMBDUP(extradata->list_help);
1299 MEMBDUP(extradata->list_archive);
1300 MEMBDUP(extradata->list_owner);
1303 refs = msginfo->references;
1304 for (refs = msginfo->references; refs != NULL; refs = refs->next) {
1305 newmsginfo->references = g_slist_prepend
1306 (newmsginfo->references, g_strdup(refs->data));
1308 newmsginfo->references = g_slist_reverse(newmsginfo->references);
1311 MEMBDUP(plaintext_file);
1316 MsgInfo *procmsg_msginfo_get_full_info_from_file(MsgInfo *msginfo, const gchar *file)
1318 MsgInfo *full_msginfo;
1320 if (msginfo == NULL) return NULL;
1322 if (!file || !is_file_exist(file)) {
1323 g_warning("procmsg_msginfo_get_full_info_from_file(): can't get message file.\n");
1327 full_msginfo = procheader_parse_file(file, msginfo->flags, TRUE, FALSE);
1328 if (!full_msginfo) return NULL;
1330 msginfo->total_size = full_msginfo->total_size;
1331 msginfo->planned_download = full_msginfo->planned_download;
1333 if (full_msginfo->extradata) {
1334 if (!msginfo->extradata)
1335 msginfo->extradata = g_new0(MsgInfoExtraData, 1);
1336 if (!msginfo->extradata->list_post)
1337 msginfo->extradata->list_post = g_strdup(full_msginfo->extradata->list_post);
1338 if (!msginfo->extradata->list_subscribe)
1339 msginfo->extradata->list_subscribe = g_strdup(full_msginfo->extradata->list_subscribe);
1340 if (!msginfo->extradata->list_unsubscribe)
1341 msginfo->extradata->list_unsubscribe = g_strdup(full_msginfo->extradata->list_unsubscribe);
1342 if (!msginfo->extradata->list_help)
1343 msginfo->extradata->list_help = g_strdup(full_msginfo->extradata->list_help);
1344 if (!msginfo->extradata->list_archive)
1345 msginfo->extradata->list_archive= g_strdup(full_msginfo->extradata->list_archive);
1346 if (!msginfo->extradata->list_owner)
1347 msginfo->extradata->list_owner = g_strdup(full_msginfo->extradata->list_owner);
1348 if (!msginfo->extradata->xface)
1349 msginfo->extradata->xface = g_strdup(full_msginfo->extradata->xface);
1350 if (!msginfo->extradata->face)
1351 msginfo->extradata->face = g_strdup(full_msginfo->extradata->face);
1352 if (!msginfo->extradata->dispositionnotificationto)
1353 msginfo->extradata->dispositionnotificationto =
1354 g_strdup(full_msginfo->extradata->dispositionnotificationto);
1355 if (!msginfo->extradata->returnreceiptto)
1356 msginfo->extradata->returnreceiptto = g_strdup
1357 (full_msginfo->extradata->returnreceiptto);
1358 if (!msginfo->extradata->partial_recv && full_msginfo->extradata->partial_recv)
1359 msginfo->extradata->partial_recv = g_strdup
1360 (full_msginfo->extradata->partial_recv);
1361 if (!msginfo->extradata->account_server && full_msginfo->extradata->account_server)
1362 msginfo->extradata->account_server = g_strdup
1363 (full_msginfo->extradata->account_server);
1364 if (!msginfo->extradata->account_login && full_msginfo->extradata->account_login)
1365 msginfo->extradata->account_login = g_strdup
1366 (full_msginfo->extradata->account_login);
1368 procmsg_msginfo_free(full_msginfo);
1370 return procmsg_msginfo_new_ref(msginfo);
1373 MsgInfo *procmsg_msginfo_get_full_info(MsgInfo *msginfo)
1375 MsgInfo *full_msginfo;
1378 if (msginfo == NULL) return NULL;
1380 file = procmsg_get_message_file_path(msginfo);
1381 if (!file || !is_file_exist(file)) {
1383 file = procmsg_get_message_file(msginfo);
1385 if (!file || !is_file_exist(file)) {
1386 g_warning("procmsg_msginfo_get_full_info(): can't get message file.\n");
1390 full_msginfo = procmsg_msginfo_get_full_info_from_file(msginfo, file);
1392 return full_msginfo;
1395 void procmsg_msginfo_free(MsgInfo *msginfo)
1397 if (msginfo == NULL) return;
1400 if (msginfo->refcnt > 0)
1403 if (msginfo->to_folder) {
1404 msginfo->to_folder->op_count--;
1405 folder_item_update(msginfo->to_folder, F_ITEM_UPDATE_MSGCNT);
1408 g_free(msginfo->fromspace);
1410 g_free(msginfo->fromname);
1412 g_free(msginfo->date);
1413 g_free(msginfo->from);
1414 g_free(msginfo->to);
1415 g_free(msginfo->cc);
1416 g_free(msginfo->newsgroups);
1417 g_free(msginfo->subject);
1418 g_free(msginfo->msgid);
1419 g_free(msginfo->inreplyto);
1420 g_free(msginfo->xref);
1422 if (msginfo->extradata) {
1423 g_free(msginfo->extradata->returnreceiptto);
1424 g_free(msginfo->extradata->dispositionnotificationto);
1425 g_free(msginfo->extradata->xface);
1426 g_free(msginfo->extradata->face);
1427 g_free(msginfo->extradata->list_post);
1428 g_free(msginfo->extradata->list_subscribe);
1429 g_free(msginfo->extradata->list_unsubscribe);
1430 g_free(msginfo->extradata->list_help);
1431 g_free(msginfo->extradata->list_archive);
1432 g_free(msginfo->extradata->list_owner);
1433 g_free(msginfo->extradata->partial_recv);
1434 g_free(msginfo->extradata->account_server);
1435 g_free(msginfo->extradata->account_login);
1436 g_free(msginfo->extradata);
1438 slist_free_strings_full(msginfo->references);
1439 g_slist_free(msginfo->tags);
1441 g_free(msginfo->plaintext_file);
1446 guint procmsg_msginfo_memusage(MsgInfo *msginfo)
1451 memusage += sizeof(MsgInfo);
1452 if (msginfo->fromname)
1453 memusage += strlen(msginfo->fromname);
1455 memusage += strlen(msginfo->date);
1457 memusage += strlen(msginfo->from);
1459 memusage += strlen(msginfo->to);
1461 memusage += strlen(msginfo->cc);
1462 if (msginfo->newsgroups)
1463 memusage += strlen(msginfo->newsgroups);
1464 if (msginfo->subject)
1465 memusage += strlen(msginfo->subject);
1467 memusage += strlen(msginfo->msgid);
1468 if (msginfo->inreplyto)
1469 memusage += strlen(msginfo->inreplyto);
1471 for (tmp = msginfo->references; tmp; tmp=tmp->next) {
1472 gchar *r = (gchar *)tmp->data;
1473 memusage += r?strlen(r):0 + sizeof(GSList);
1475 if (msginfo->fromspace)
1476 memusage += strlen(msginfo->fromspace);
1478 for (tmp = msginfo->tags; tmp; tmp=tmp->next) {
1479 memusage += sizeof(GSList);
1481 if (msginfo->extradata) {
1482 memusage += sizeof(MsgInfoExtraData);
1483 if (msginfo->extradata->xface)
1484 memusage += strlen(msginfo->extradata->xface);
1485 if (msginfo->extradata->face)
1486 memusage += strlen(msginfo->extradata->face);
1487 if (msginfo->extradata->dispositionnotificationto)
1488 memusage += strlen(msginfo->extradata->dispositionnotificationto);
1489 if (msginfo->extradata->returnreceiptto)
1490 memusage += strlen(msginfo->extradata->returnreceiptto);
1492 if (msginfo->extradata->partial_recv)
1493 memusage += strlen(msginfo->extradata->partial_recv);
1494 if (msginfo->extradata->account_server)
1495 memusage += strlen(msginfo->extradata->account_server);
1496 if (msginfo->extradata->account_login)
1497 memusage += strlen(msginfo->extradata->account_login);
1499 if (msginfo->extradata->list_post)
1500 memusage += strlen(msginfo->extradata->list_post);
1501 if (msginfo->extradata->list_subscribe)
1502 memusage += strlen(msginfo->extradata->list_subscribe);
1503 if (msginfo->extradata->list_unsubscribe)
1504 memusage += strlen(msginfo->extradata->list_unsubscribe);
1505 if (msginfo->extradata->list_help)
1506 memusage += strlen(msginfo->extradata->list_help);
1507 if (msginfo->extradata->list_archive)
1508 memusage += strlen(msginfo->extradata->list_archive);
1509 if (msginfo->extradata->list_owner)
1510 memusage += strlen(msginfo->extradata->list_owner);
1515 static gint procmsg_send_message_queue_full(const gchar *file, gboolean keep_session, gchar **errstr,
1516 FolderItem *queue, gint msgnum, gboolean *queued_removed)
1518 static HeaderEntry qentry[] = {{"S:", NULL, FALSE},
1519 {"SSV:", NULL, FALSE},
1520 {"R:", NULL, FALSE},
1521 {"NG:", NULL, FALSE},
1522 {"MAID:", NULL, FALSE},
1523 {"NAID:", NULL, FALSE},
1524 {"SCF:", NULL, FALSE},
1525 {"RMID:", NULL, FALSE},
1526 {"FMID:", NULL, FALSE},
1527 {"X-Claws-Privacy-System:", NULL, FALSE},
1528 {"X-Claws-Encrypt:", NULL, FALSE},
1529 {"X-Claws-Encrypt-Data:", NULL, FALSE},
1530 {"X-Claws-End-Special-Headers:", NULL, FALSE},
1531 {"X-Sylpheed-Privacy-System:", NULL, FALSE},
1532 {"X-Sylpheed-Encrypt:", NULL, FALSE},
1533 {"X-Sylpheed-Encrypt-Data:", NULL, FALSE},
1534 {"X-Sylpheed-End-Special-Headers:", NULL, FALSE},
1535 {NULL, NULL, FALSE}};
1538 gint mailval = 0, newsval = 0;
1540 gchar *smtpserver = NULL;
1541 GSList *to_list = NULL;
1542 GSList *newsgroup_list = NULL;
1543 gchar *savecopyfolder = NULL;
1544 gchar *replymessageid = NULL;
1545 gchar *fwdmessageid = NULL;
1546 gchar *privacy_system = NULL;
1547 gboolean encrypt = FALSE;
1548 gchar *encrypt_data = NULL;
1549 gchar buf[BUFFSIZE];
1551 PrefsAccount *mailac = NULL, *newsac = NULL;
1552 gboolean save_clear_text = TRUE;
1553 gchar *tmp_enc_file = NULL;
1555 cm_return_val_if_fail(file != NULL, -1);
1557 if ((fp = g_fopen(file, "rb")) == NULL) {
1558 FILE_OP_ERROR(file, "fopen");
1560 if (*errstr) g_free(*errstr);
1561 *errstr = g_strdup_printf(_("Couldn't open file %s."), file);
1566 while ((hnum = procheader_get_one_field(buf, sizeof(buf), fp, qentry))
1568 gchar *p = buf + strlen(qentry[hnum].name);
1576 if (smtpserver == NULL)
1577 smtpserver = g_strdup(p);
1580 to_list = address_list_append(to_list, p);
1583 newsgroup_list = newsgroup_list_append(newsgroup_list, p);
1585 case Q_MAIL_ACCOUNT_ID:
1586 mailac = account_find_from_id(atoi(p));
1588 case Q_NEWS_ACCOUNT_ID:
1589 newsac = account_find_from_id(atoi(p));
1591 case Q_SAVE_COPY_FOLDER:
1592 if (savecopyfolder == NULL)
1593 savecopyfolder = g_strdup(p);
1595 case Q_REPLY_MESSAGE_ID:
1596 if (replymessageid == NULL)
1597 replymessageid = g_strdup(p);
1599 case Q_FWD_MESSAGE_ID:
1600 if (fwdmessageid == NULL)
1601 fwdmessageid = g_strdup(p);
1603 case Q_PRIVACY_SYSTEM:
1604 case Q_PRIVACY_SYSTEM_OLD:
1605 if (privacy_system == NULL)
1606 privacy_system = g_strdup(p);
1613 case Q_ENCRYPT_DATA:
1614 case Q_ENCRYPT_DATA_OLD:
1615 if (encrypt_data == NULL)
1616 encrypt_data = g_strdup(p);
1619 case Q_CLAWS_HDRS_OLD:
1620 /* end of special headers reached */
1621 goto send_mail; /* can't "break;break;" */
1625 filepos = ftell(fp);
1630 if (mailac && mailac->save_encrypted_as_clear_text
1631 && !mailac->encrypt_to_self)
1632 save_clear_text = TRUE;
1634 save_clear_text = FALSE;
1639 mimeinfo = procmime_scan_queue_file(file);
1640 if (!privacy_encrypt(privacy_system, mimeinfo, encrypt_data)
1641 || (fp = my_tmpfile()) == NULL
1642 || procmime_write_mimeinfo(mimeinfo, fp) < 0) {
1645 procmime_mimeinfo_free_all(mimeinfo);
1648 slist_free_strings_full(to_list);
1649 slist_free_strings_full(newsgroup_list);
1650 g_free(savecopyfolder);
1651 g_free(replymessageid);
1652 g_free(fwdmessageid);
1653 g_free(privacy_system);
1654 g_free(encrypt_data);
1656 if (*errstr) g_free(*errstr);
1657 *errstr = g_strdup_printf(_("Couldn't encrypt the email: %s"),
1658 privacy_get_error());
1664 if (!save_clear_text) {
1665 gchar *content = NULL;
1666 FILE *tmpfp = get_tmpfile_in_dir(get_mime_tmp_dir(), &tmp_enc_file);
1670 content = file_read_stream_to_str(fp);
1673 str_write_to_file(content, tmp_enc_file);
1676 g_warning("couldn't get tempfile\n");
1680 procmime_mimeinfo_free_all(mimeinfo);
1686 debug_print("Sending message by mail\n");
1689 if (*errstr) g_free(*errstr);
1690 *errstr = g_strdup_printf(_("Queued message header is broken."));
1693 } else if (mailac && mailac->use_mail_command &&
1694 mailac->mail_command && (* mailac->mail_command)) {
1695 mailval = send_message_local(mailac->mail_command, fp);
1698 mailac = account_find_from_smtp_server(from, smtpserver);
1700 g_warning("Account not found. "
1701 "Using current account...\n");
1702 mailac = cur_account;
1707 mailval = send_message_smtp_full(mailac, to_list, fp, keep_session);
1708 if (mailval == -1 && errstr) {
1709 if (*errstr) g_free(*errstr);
1710 *errstr = g_strdup_printf(_("An error happened during SMTP session."));
1713 PrefsAccount tmp_ac;
1715 g_warning("Account not found.\n");
1717 memset(&tmp_ac, 0, sizeof(PrefsAccount));
1718 tmp_ac.address = from;
1719 tmp_ac.smtp_server = smtpserver;
1720 tmp_ac.smtpport = SMTP_PORT;
1721 mailval = send_message_smtp(&tmp_ac, to_list, fp);
1722 if (mailval == -1 && errstr) {
1723 if (*errstr) g_free(*errstr);
1724 *errstr = g_strdup_printf(_("No specific account has been found to "
1725 "send, and an error happened during SMTP session."));
1729 } else if (!to_list && !newsgroup_list) {
1731 if (*errstr) g_free(*errstr);
1732 *errstr = g_strdup(_("Couldn't determine sending informations. "
1733 "Maybe the email hasn't been generated by Claws Mail."));
1738 fseek(fp, filepos, SEEK_SET);
1739 if (newsgroup_list && newsac && (mailval == 0)) {
1744 /* write to temporary file */
1745 tmp = g_strdup_printf("%s%cnntp%p", get_tmp_dir(),
1746 G_DIR_SEPARATOR, file);
1747 if ((tmpfp = g_fopen(tmp, "wb")) == NULL) {
1748 FILE_OP_ERROR(tmp, "fopen");
1750 alertpanel_error(_("Couldn't create temporary file for news sending."));
1752 if (change_file_mode_rw(tmpfp, tmp) < 0) {
1753 FILE_OP_ERROR(tmp, "chmod");
1754 g_warning("can't change file mode\n");
1757 while ((newsval == 0) && fgets(buf, sizeof(buf), fp) != NULL) {
1758 if (fputs(buf, tmpfp) == EOF) {
1759 FILE_OP_ERROR(tmp, "fputs");
1762 if (*errstr) g_free(*errstr);
1763 *errstr = g_strdup_printf(_("Error when writing temporary file for news sending."));
1770 debug_print("Sending message by news\n");
1772 folder = FOLDER(newsac->folder);
1774 newsval = news_post(folder, tmp);
1775 if (newsval < 0 && errstr) {
1776 if (*errstr) g_free(*errstr);
1777 *errstr = g_strdup_printf(_("Error occurred while posting the message to %s."),
1778 newsac->nntp_server);
1788 /* update session statistics */
1789 if (mailval == 0 && newsval == 0) {
1790 /* update session stats */
1792 session_stats.replied++;
1793 else if (fwdmessageid)
1794 session_stats.forwarded++;
1796 session_stats.sent++;
1799 /* save message to outbox */
1800 if (mailval == 0 && newsval == 0 && savecopyfolder) {
1803 debug_print("saving sent message...\n");
1805 outbox = folder_find_item_from_identifier(savecopyfolder);
1807 outbox = folder_get_default_outbox();
1809 if (save_clear_text || tmp_enc_file == NULL) {
1810 gboolean saved = FALSE;
1811 *queued_removed = FALSE;
1812 if (queue && msgnum > 0) {
1813 MsgInfo *queued_mail = folder_item_get_msginfo(queue, msgnum);
1814 if (folder_item_move_msg(outbox, queued_mail) >= 0) {
1815 debug_print("moved queued mail %d to sent folder\n", msgnum);
1817 *queued_removed = TRUE;
1818 } else if (folder_item_copy_msg(outbox, queued_mail) >= 0) {
1819 debug_print("copied queued mail %d to sent folder\n", msgnum);
1822 procmsg_msginfo_free(queued_mail);
1825 debug_print("resaving clear text queued mail to sent folder\n");
1826 procmsg_save_to_outbox(outbox, file, TRUE);
1829 debug_print("saving encrpyted queued mail to sent folder\n");
1830 procmsg_save_to_outbox(outbox, tmp_enc_file, FALSE);
1834 if (tmp_enc_file != NULL) {
1835 claws_unlink(tmp_enc_file);
1837 tmp_enc_file = NULL;
1840 if (replymessageid != NULL || fwdmessageid != NULL) {
1844 if (replymessageid != NULL)
1845 tokens = g_strsplit(replymessageid, "\t", 0);
1847 tokens = g_strsplit(fwdmessageid, "\t", 0);
1848 item = folder_find_item_from_identifier(tokens[0]);
1850 /* check if queued message has valid folder and message id */
1851 if (item != NULL && tokens[2] != NULL) {
1854 msginfo = folder_item_get_msginfo(item, atoi(tokens[1]));
1856 /* check if referring message exists and has a message id */
1857 if ((msginfo != NULL) &&
1858 (msginfo->msgid != NULL) &&
1859 (strcmp(msginfo->msgid, tokens[2]) != 0)) {
1860 procmsg_msginfo_free(msginfo);
1864 if (msginfo == NULL) {
1865 msginfo = folder_item_get_msginfo_by_msgid(item, tokens[2]);
1868 if (msginfo != NULL) {
1869 if (replymessageid != NULL) {
1870 MsgPermFlags to_unset = 0;
1872 if (prefs_common.mark_as_read_on_new_window)
1873 to_unset = (MSG_NEW|MSG_UNREAD);
1875 procmsg_msginfo_unset_flags(msginfo, to_unset, 0);
1876 procmsg_msginfo_set_flags(msginfo, MSG_REPLIED, 0);
1878 procmsg_msginfo_set_flags(msginfo, MSG_FORWARDED, 0);
1880 procmsg_msginfo_free(msginfo);
1888 slist_free_strings_full(to_list);
1889 slist_free_strings_full(newsgroup_list);
1890 g_free(savecopyfolder);
1891 g_free(replymessageid);
1892 g_free(fwdmessageid);
1893 g_free(privacy_system);
1894 g_free(encrypt_data);
1896 return (newsval != 0 ? newsval : mailval);
1899 gint procmsg_send_message_queue(const gchar *file, gchar **errstr, FolderItem *queue, gint msgnum, gboolean *queued_removed)
1901 gint result = procmsg_send_message_queue_full(file, FALSE, errstr, queue, msgnum, queued_removed);
1902 main_window_set_menu_sensitive(mainwindow_get_mainwindow());
1903 toolbar_main_set_sensitive(mainwindow_get_mainwindow());
1907 gint procmsg_send_message_queue_with_lock(const gchar *file, gchar **errstr, FolderItem *queue, gint msgnum, gboolean *queued_removed)
1910 if (procmsg_queue_lock(errstr)) {
1911 val = procmsg_send_message_queue(file, errstr, queue, msgnum, queued_removed);
1912 procmsg_queue_unlock();
1918 static void update_folder_msg_counts(FolderItem *item, MsgInfo *msginfo, MsgPermFlags old_flags)
1920 MsgPermFlags new_flags = msginfo->flags.perm_flags;
1923 if (!(old_flags & MSG_NEW) && (new_flags & MSG_NEW)) {
1927 if ((old_flags & MSG_NEW) && !(new_flags & MSG_NEW)) {
1932 if (!(old_flags & MSG_UNREAD) && (new_flags & MSG_UNREAD)) {
1933 item->unread_msgs++;
1934 if (procmsg_msg_has_marked_parent(msginfo))
1935 item->unreadmarked_msgs++;
1938 if ((old_flags & MSG_UNREAD) && !(new_flags & MSG_UNREAD)) {
1939 item->unread_msgs--;
1940 if (procmsg_msg_has_marked_parent(msginfo))
1941 item->unreadmarked_msgs--;
1945 if (!(old_flags & MSG_MARKED) && (new_flags & MSG_MARKED)) {
1946 procmsg_update_unread_children(msginfo, TRUE);
1947 item->marked_msgs++;
1950 if ((old_flags & MSG_MARKED) && !(new_flags & MSG_MARKED)) {
1951 procmsg_update_unread_children(msginfo, FALSE);
1952 item->marked_msgs--;
1955 if (!(old_flags & MSG_REPLIED) && (new_flags & MSG_REPLIED)) {
1956 item->replied_msgs++;
1959 if ((old_flags & MSG_REPLIED) && !(new_flags & MSG_REPLIED)) {
1960 item->replied_msgs--;
1963 if (!(old_flags & MSG_FORWARDED) && (new_flags & MSG_FORWARDED)) {
1964 item->forwarded_msgs++;
1967 if ((old_flags & MSG_FORWARDED) && !(new_flags & MSG_FORWARDED)) {
1968 item->forwarded_msgs--;
1971 if (!(old_flags & MSG_LOCKED) && (new_flags & MSG_LOCKED)) {
1972 item->locked_msgs++;
1975 if ((old_flags & MSG_LOCKED) && !(new_flags & MSG_LOCKED)) {
1976 item->locked_msgs--;
1979 if ((old_flags & MSG_IGNORE_THREAD) && !(new_flags & MSG_IGNORE_THREAD)) {
1980 item->ignored_msgs--;
1983 if (!(old_flags & MSG_IGNORE_THREAD) && (new_flags & MSG_IGNORE_THREAD)) {
1984 item->ignored_msgs++;
1987 if ((old_flags & MSG_WATCH_THREAD) && !(new_flags & MSG_WATCH_THREAD)) {
1988 item->watched_msgs--;
1991 if (!(old_flags & MSG_WATCH_THREAD) && (new_flags & MSG_WATCH_THREAD)) {
1992 item->watched_msgs++;
1996 void procmsg_msginfo_set_flags(MsgInfo *msginfo, MsgPermFlags perm_flags, MsgTmpFlags tmp_flags)
1999 MsgInfoUpdate msginfo_update;
2000 MsgPermFlags perm_flags_new, perm_flags_old;
2001 MsgTmpFlags tmp_flags_old;
2003 cm_return_if_fail(msginfo != NULL);
2004 item = msginfo->folder;
2005 cm_return_if_fail(item != NULL);
2007 debug_print("Setting flags for message %d in folder %s\n", msginfo->msgnum, item->path);
2009 /* Perm Flags handling */
2010 perm_flags_old = msginfo->flags.perm_flags;
2011 perm_flags_new = msginfo->flags.perm_flags | perm_flags;
2012 if ((perm_flags & MSG_IGNORE_THREAD) || (perm_flags_old & MSG_IGNORE_THREAD)) {
2013 perm_flags_new &= ~(MSG_NEW | MSG_UNREAD);
2015 if ((perm_flags & MSG_WATCH_THREAD) || (perm_flags_old & MSG_WATCH_THREAD)) {
2016 perm_flags_new &= ~(MSG_IGNORE_THREAD);
2019 if (perm_flags_old != perm_flags_new) {
2020 folder_item_change_msg_flags(msginfo->folder, msginfo, perm_flags_new);
2022 update_folder_msg_counts(item, msginfo, perm_flags_old);
2023 summary_update_unread(mainwindow_get_mainwindow()->summaryview, NULL);
2026 /* Tmp flags handling */
2027 tmp_flags_old = msginfo->flags.tmp_flags;
2028 msginfo->flags.tmp_flags |= tmp_flags;
2030 /* update notification */
2031 if ((perm_flags_old != perm_flags_new) || (tmp_flags_old != msginfo->flags.tmp_flags)) {
2032 msginfo_update.msginfo = msginfo;
2033 msginfo_update.flags = MSGINFO_UPDATE_FLAGS;
2034 hooks_invoke(MSGINFO_UPDATE_HOOKLIST, &msginfo_update);
2035 folder_item_update(msginfo->folder, F_ITEM_UPDATE_MSGCNT);
2039 void procmsg_msginfo_unset_flags(MsgInfo *msginfo, MsgPermFlags perm_flags, MsgTmpFlags tmp_flags)
2042 MsgInfoUpdate msginfo_update;
2043 MsgPermFlags perm_flags_new, perm_flags_old;
2044 MsgTmpFlags tmp_flags_old;
2046 cm_return_if_fail(msginfo != NULL);
2047 item = msginfo->folder;
2048 cm_return_if_fail(item != NULL);
2050 debug_print("Unsetting flags for message %d in folder %s\n", msginfo->msgnum, item->path);
2052 /* Perm Flags handling */
2053 perm_flags_old = msginfo->flags.perm_flags;
2054 perm_flags_new = msginfo->flags.perm_flags & ~perm_flags;
2056 if (perm_flags_old != perm_flags_new) {
2057 folder_item_change_msg_flags(msginfo->folder, msginfo, perm_flags_new);
2059 update_folder_msg_counts(item, msginfo, perm_flags_old);
2062 /* Tmp flags hanlding */
2063 tmp_flags_old = msginfo->flags.tmp_flags;
2064 msginfo->flags.tmp_flags &= ~tmp_flags;
2066 /* update notification */
2067 if ((perm_flags_old != perm_flags_new) || (tmp_flags_old != msginfo->flags.tmp_flags)) {
2068 msginfo_update.msginfo = msginfo;
2069 msginfo_update.flags = MSGINFO_UPDATE_FLAGS;
2070 hooks_invoke(MSGINFO_UPDATE_HOOKLIST, &msginfo_update);
2071 folder_item_update(msginfo->folder, F_ITEM_UPDATE_MSGCNT);
2075 void procmsg_msginfo_change_flags(MsgInfo *msginfo,
2076 MsgPermFlags add_perm_flags, MsgTmpFlags add_tmp_flags,
2077 MsgPermFlags rem_perm_flags, MsgTmpFlags rem_tmp_flags)
2080 MsgInfoUpdate msginfo_update;
2081 MsgPermFlags perm_flags_new, perm_flags_old;
2082 MsgTmpFlags tmp_flags_old;
2084 cm_return_if_fail(msginfo != NULL);
2085 item = msginfo->folder;
2086 cm_return_if_fail(item != NULL);
2088 debug_print("Changing flags for message %d in folder %s\n", msginfo->msgnum, item->path);
2090 /* Perm Flags handling */
2091 perm_flags_old = msginfo->flags.perm_flags;
2092 perm_flags_new = (msginfo->flags.perm_flags & ~rem_perm_flags) | add_perm_flags;
2093 if ((add_perm_flags & MSG_IGNORE_THREAD) || (perm_flags_old & MSG_IGNORE_THREAD)) {
2094 perm_flags_new &= ~(MSG_NEW | MSG_UNREAD);
2096 if ((add_perm_flags & MSG_WATCH_THREAD) || (perm_flags_old & MSG_WATCH_THREAD)) {
2097 perm_flags_new &= ~(MSG_IGNORE_THREAD);
2100 if (perm_flags_old != perm_flags_new) {
2101 folder_item_change_msg_flags(msginfo->folder, msginfo, perm_flags_new);
2103 update_folder_msg_counts(item, msginfo, perm_flags_old);
2107 /* Tmp flags handling */
2108 tmp_flags_old = msginfo->flags.tmp_flags;
2109 msginfo->flags.tmp_flags &= ~rem_tmp_flags;
2110 msginfo->flags.tmp_flags |= add_tmp_flags;
2112 /* update notification */
2113 if ((perm_flags_old != perm_flags_new) || (tmp_flags_old != msginfo->flags.tmp_flags)) {
2114 msginfo_update.msginfo = msginfo;
2115 msginfo_update.flags = MSGINFO_UPDATE_FLAGS;
2116 hooks_invoke(MSGINFO_UPDATE_HOOKLIST, &msginfo_update);
2117 folder_item_update(msginfo->folder, F_ITEM_UPDATE_MSGCNT);
2122 *\brief check for flags (e.g. mark) in prior msgs of current thread
2124 *\param info Current message
2125 *\param perm_flags Flags to be checked
2126 *\param parentmsgs Hash of prior msgs to avoid loops
2128 *\return gboolean TRUE if perm_flags are found
2130 static gboolean procmsg_msg_has_flagged_parent_real(MsgInfo *info,
2131 MsgPermFlags perm_flags, GHashTable *parentmsgs)
2135 cm_return_val_if_fail(info != NULL, FALSE);
2137 if (info != NULL && info->folder != NULL && info->inreplyto != NULL) {
2138 tmp = folder_item_get_msginfo_by_msgid(info->folder,
2140 if (tmp && (tmp->flags.perm_flags & perm_flags)) {
2141 procmsg_msginfo_free(tmp);
2143 } else if (tmp != NULL) {
2146 if (g_hash_table_lookup(parentmsgs, info)) {
2147 debug_print("loop detected: %d\n",
2151 g_hash_table_insert(parentmsgs, info, "1");
2152 result = procmsg_msg_has_flagged_parent_real(
2153 tmp, perm_flags, parentmsgs);
2155 procmsg_msginfo_free(tmp);
2165 *\brief Callback for cleaning up hash of parentmsgs
2167 static gboolean parentmsgs_hash_remove(gpointer key,
2175 *\brief Set up list of parentmsgs
2176 * See procmsg_msg_has_flagged_parent_real()
2178 gboolean procmsg_msg_has_flagged_parent(MsgInfo *info, MsgPermFlags perm_flags)
2181 static GHashTable *parentmsgs = NULL;
2183 if (parentmsgs == NULL)
2184 parentmsgs = g_hash_table_new(NULL, NULL);
2186 result = procmsg_msg_has_flagged_parent_real(info, perm_flags, parentmsgs);
2187 g_hash_table_foreach_remove(parentmsgs, parentmsgs_hash_remove, NULL);
2193 *\brief Check if msgs prior in thread are marked
2194 * See procmsg_msg_has_flagged_parent_real()
2196 gboolean procmsg_msg_has_marked_parent(MsgInfo *info)
2198 return procmsg_msg_has_flagged_parent(info, MSG_MARKED);
2202 static GSList *procmsg_find_children_func(MsgInfo *info,
2203 GSList *children, GSList *all)
2207 cm_return_val_if_fail(info!=NULL, children);
2208 if (info->msgid == NULL)
2211 for (cur = all; cur != NULL; cur = g_slist_next(cur)) {
2212 MsgInfo *tmp = (MsgInfo *)cur->data;
2213 if (tmp->inreplyto && !strcmp(tmp->inreplyto, info->msgid)) {
2214 /* Check if message is already in the list */
2215 if ((children == NULL) ||
2216 (g_slist_index(children, tmp) == -1)) {
2217 children = g_slist_prepend(children,
2218 procmsg_msginfo_new_ref(tmp));
2219 children = procmsg_find_children_func(tmp,
2228 static GSList *procmsg_find_children (MsgInfo *info)
2233 cm_return_val_if_fail(info!=NULL, NULL);
2234 all = folder_item_get_msg_list(info->folder);
2235 children = procmsg_find_children_func(info, NULL, all);
2236 if (children != NULL) {
2237 for (cur = all; cur != NULL; cur = g_slist_next(cur)) {
2238 /* this will not free the used pointers
2239 created with procmsg_msginfo_new_ref */
2240 procmsg_msginfo_free((MsgInfo *)cur->data);
2248 static void procmsg_update_unread_children(MsgInfo *info, gboolean newly_marked)
2250 GSList *children = procmsg_find_children(info);
2252 for (cur = children; cur != NULL; cur = g_slist_next(cur)) {
2253 MsgInfo *tmp = (MsgInfo *)cur->data;
2254 if(MSG_IS_UNREAD(tmp->flags) && !MSG_IS_IGNORE_THREAD(tmp->flags)) {
2256 info->folder->unreadmarked_msgs++;
2258 info->folder->unreadmarked_msgs--;
2259 folder_item_update(info->folder, F_ITEM_UPDATE_MSGCNT);
2261 procmsg_msginfo_free(tmp);
2263 g_slist_free(children);
2267 * Set the destination folder for a copy or move operation
2269 * \param msginfo The message which's destination folder is changed
2270 * \param to_folder The destination folder for the operation
2272 void procmsg_msginfo_set_to_folder(MsgInfo *msginfo, FolderItem *to_folder)
2274 if(msginfo->to_folder != NULL) {
2275 msginfo->to_folder->op_count--;
2276 folder_item_update(msginfo->to_folder, F_ITEM_UPDATE_MSGCNT);
2278 msginfo->to_folder = to_folder;
2279 if(to_folder != NULL) {
2280 to_folder->op_count++;
2281 folder_item_update(msginfo->to_folder, F_ITEM_UPDATE_MSGCNT);
2286 * Apply filtering actions to the msginfo
2288 * \param msginfo The MsgInfo describing the message that should be filtered
2289 * \return TRUE if the message was moved and MsgInfo is now invalid,
2292 static gboolean procmsg_msginfo_filter(MsgInfo *msginfo, PrefsAccount* ac_prefs)
2294 MailFilteringData mail_filtering_data;
2296 mail_filtering_data.msginfo = msginfo;
2297 mail_filtering_data.msglist = NULL;
2298 mail_filtering_data.filtered = NULL;
2299 mail_filtering_data.unfiltered = NULL;
2300 mail_filtering_data.account = ac_prefs;
2302 if (!ac_prefs || ac_prefs->filterhook_on_recv)
2303 if (hooks_invoke(MAIL_FILTERING_HOOKLIST, &mail_filtering_data))
2306 /* filter if enabled in prefs or move to inbox if not */
2307 if((filtering_rules != NULL) &&
2308 filter_message_by_msginfo(filtering_rules, msginfo, ac_prefs,
2309 FILTERING_INCORPORATION, NULL)) {
2316 void procmsg_msglist_filter(GSList *list, PrefsAccount *ac,
2317 GSList **filtered, GSList **unfiltered,
2320 GSList *cur, *to_do = NULL;
2321 gint total = 0, curnum = 0;
2322 MailFilteringData mail_filtering_data;
2324 cm_return_if_fail(filtered != NULL);
2325 cm_return_if_fail(unfiltered != NULL);
2333 total = g_slist_length(list);
2337 *unfiltered = g_slist_copy(list);
2341 statusbar_print_all(_("Filtering messages...\n"));
2343 mail_filtering_data.msginfo = NULL;
2344 mail_filtering_data.msglist = list;
2345 mail_filtering_data.filtered = NULL;
2346 mail_filtering_data.unfiltered = NULL;
2347 mail_filtering_data.account = ac;
2349 if (!ac || ac->filterhook_on_recv)
2350 hooks_invoke(MAIL_LISTFILTERING_HOOKLIST, &mail_filtering_data);
2352 if (mail_filtering_data.filtered == NULL &&
2353 mail_filtering_data.unfiltered == NULL) {
2354 /* nothing happened */
2355 debug_print(MAIL_LISTFILTERING_HOOKLIST " did nothing. filtering whole list normally.\n");
2358 if (mail_filtering_data.filtered != NULL) {
2359 /* keep track of what's been filtered by the hooks */
2360 debug_print(MAIL_LISTFILTERING_HOOKLIST " filtered some stuff. total %d filtered %d unfilt %d.\n",
2361 g_slist_length(list),
2362 g_slist_length(mail_filtering_data.filtered),
2363 g_slist_length(mail_filtering_data.unfiltered));
2365 *filtered = g_slist_copy(mail_filtering_data.filtered);
2367 if (mail_filtering_data.unfiltered != NULL) {
2368 /* what the hooks didn't handle will go in filtered or
2369 * unfiltered in the next loop */
2370 debug_print(MAIL_LISTFILTERING_HOOKLIST " left unfiltered stuff. total %d filtered %d unfilt %d.\n",
2371 g_slist_length(list),
2372 g_slist_length(mail_filtering_data.filtered),
2373 g_slist_length(mail_filtering_data.unfiltered));
2374 to_do = mail_filtering_data.unfiltered;
2377 for (cur = to_do; cur; cur = cur->next) {
2378 MsgInfo *info = (MsgInfo *)cur->data;
2379 if (procmsg_msginfo_filter(info, ac))
2380 *filtered = g_slist_prepend(*filtered, info);
2382 *unfiltered = g_slist_prepend(*unfiltered, info);
2383 statusbar_progress_all(curnum++, total, prefs_common.statusbar_update_step);
2386 g_slist_free(mail_filtering_data.filtered);
2387 g_slist_free(mail_filtering_data.unfiltered);
2389 *filtered = g_slist_reverse(*filtered);
2390 *unfiltered = g_slist_reverse(*unfiltered);
2392 statusbar_progress_all(0,0,0);
2393 statusbar_pop_all();
2396 MsgInfo *procmsg_msginfo_new_from_mimeinfo(MsgInfo *src_msginfo, MimeInfo *mimeinfo)
2398 MsgInfo *tmp_msginfo = NULL;
2399 MsgFlags flags = {0, 0};
2400 gchar *tmpfile = get_tmp_file();
2401 FILE *fp = g_fopen(tmpfile, "wb");
2403 if (!mimeinfo || mimeinfo->type != MIMETYPE_MESSAGE ||
2404 g_ascii_strcasecmp(mimeinfo->subtype, "rfc822")) {
2405 g_warning("procmsg_msginfo_new_from_mimeinfo(): unsuitable mimeinfo");
2412 if (fp && procmime_write_mimeinfo(mimeinfo, fp) >= 0) {
2415 tmp_msginfo = procheader_parse_file(
2422 if (tmp_msginfo != NULL) {
2424 tmp_msginfo->folder = src_msginfo->folder;
2425 tmp_msginfo->plaintext_file = g_strdup(tmpfile);
2427 g_warning("procmsg_msginfo_new_from_mimeinfo(): Can't generate new msginfo");
2435 static GSList *spam_learners = NULL;
2437 void procmsg_register_spam_learner (int (*learn_func)(MsgInfo *info, GSList *list, gboolean spam))
2439 if (!g_slist_find(spam_learners, learn_func))
2440 spam_learners = g_slist_append(spam_learners, learn_func);
2441 if (mainwindow_get_mainwindow()) {
2442 main_window_set_menu_sensitive(mainwindow_get_mainwindow());
2443 summary_set_menu_sensitive(
2444 mainwindow_get_mainwindow()->summaryview);
2445 toolbar_main_set_sensitive(mainwindow_get_mainwindow());
2449 void procmsg_unregister_spam_learner (int (*learn_func)(MsgInfo *info, GSList *list, gboolean spam))
2451 spam_learners = g_slist_remove(spam_learners, learn_func);
2452 if (mainwindow_get_mainwindow()) {
2453 main_window_set_menu_sensitive(mainwindow_get_mainwindow());
2454 summary_set_menu_sensitive(
2455 mainwindow_get_mainwindow()->summaryview);
2456 toolbar_main_set_sensitive(mainwindow_get_mainwindow());
2460 gboolean procmsg_spam_can_learn(void)
2462 return g_slist_length(spam_learners) > 0;
2465 int procmsg_spam_learner_learn (MsgInfo *info, GSList *list, gboolean spam)
2467 GSList *cur = spam_learners;
2469 for (; cur; cur = cur->next) {
2470 int ((*func)(MsgInfo *info, GSList *list, gboolean spam)) = cur->data;
2471 ret |= func(info, list, spam);
2476 static gchar *spam_folder_item = NULL;
2477 static FolderItem * (*procmsg_spam_get_folder_func)(MsgInfo *msginfo) = NULL;
2478 void procmsg_spam_set_folder (const char *item_identifier, FolderItem *(*spam_get_folder_func)(MsgInfo *info))
2480 g_free(spam_folder_item);
2481 if (item_identifier)
2482 spam_folder_item = g_strdup(item_identifier);
2484 spam_folder_item = NULL;
2485 if (spam_get_folder_func != NULL)
2486 procmsg_spam_get_folder_func = spam_get_folder_func;
2488 procmsg_spam_get_folder_func = NULL;
2491 FolderItem *procmsg_spam_get_folder (MsgInfo *msginfo)
2493 FolderItem *item = NULL;
2495 if (procmsg_spam_get_folder_func)
2496 item = procmsg_spam_get_folder_func(msginfo);
2497 if (item == NULL && spam_folder_item)
2498 item = folder_find_item_from_identifier(spam_folder_item);
2500 item = folder_get_default_trash();
2504 static void item_has_queued_mails(FolderItem *item, gpointer data)
2506 gboolean *result = (gboolean *)data;
2507 if (*result == TRUE)
2509 if (folder_has_parent_of_type(item, F_QUEUE)) {
2510 if (item->total_msgs == 0)
2513 GSList *msglist = folder_item_get_msg_list(item);
2515 for (cur = msglist; cur; cur = cur->next) {
2516 MsgInfo *msginfo = (MsgInfo *)cur->data;
2517 if (!MSG_IS_DELETED(msginfo->flags) &&
2518 !MSG_IS_LOCKED(msginfo->flags)) {
2523 procmsg_msg_list_free(msglist);
2528 gboolean procmsg_have_queued_mails_fast (void)
2530 gboolean result = FALSE;
2531 folder_func_to_all_folders(item_has_queued_mails, &result);
2535 static void item_has_trashed_mails(FolderItem *item, gpointer data)
2537 gboolean *result = (gboolean *)data;
2538 if (*result == TRUE)
2540 if (folder_has_parent_of_type(item, F_TRASH) && item->total_msgs > 0)
2544 gboolean procmsg_have_trashed_mails_fast (void)
2546 gboolean result = FALSE;
2547 folder_func_to_all_folders(item_has_trashed_mails, &result);
2551 gchar *procmsg_msginfo_get_tags_str(MsgInfo *msginfo)
2559 if (msginfo->tags == NULL)
2561 for (cur = msginfo->tags; cur; cur = cur->next) {
2562 const gchar *tag = tags_get_tag(GPOINTER_TO_INT(cur->data));
2566 tags = g_strdup(tag);
2568 int olen = strlen(tags);
2569 int nlen = olen + strlen(tag) + 2 /* strlen(", ") */;
2570 tags = g_realloc(tags, nlen+1);
2573 strcpy(tags+olen, ", ");
2574 strcpy(tags+olen+2, tag);
2581 void procmsg_msginfo_update_tags(MsgInfo *msginfo, gboolean set, gint id)
2589 msginfo->tags = g_slist_remove(
2591 GINT_TO_POINTER(id));
2592 changed.data = GINT_TO_POINTER(id);
2593 changed.next = NULL;
2594 folder_item_commit_tags(msginfo->folder, msginfo, NULL, &changed);
2596 if (!g_slist_find(msginfo->tags, GINT_TO_POINTER(id))) {
2597 msginfo->tags = g_slist_append(
2599 GINT_TO_POINTER(id));
2601 changed.data = GINT_TO_POINTER(id);
2602 changed.next = NULL;
2603 folder_item_commit_tags(msginfo->folder, msginfo, &changed, NULL);
2608 void procmsg_msginfo_clear_tags(MsgInfo *msginfo)
2610 GSList *unset = msginfo->tags;
2611 msginfo->tags = NULL;
2612 folder_item_commit_tags(msginfo->folder, msginfo, NULL, unset);
2613 g_slist_free(unset);