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 toolbar_main_set_sensitive(mainwindow_get_mainwindow());
951 queue = folder_get_default_queue();
954 procmsg_queue_unlock();
959 toolbar_main_set_sensitive(mainwindow_get_mainwindow());
961 folder_item_scan(queue);
962 list = folder_item_get_msg_list(queue);
964 /* sort the list per sender account; this helps reusing the same SMTP server */
965 sorted_list = procmsg_list_sort_by_account(queue, list);
967 for (elem = sorted_list; elem != NULL; elem = elem->next) {
971 msginfo = (MsgInfo *)(elem->data);
972 if (!MSG_IS_LOCKED(msginfo->flags) && !MSG_IS_DELETED(msginfo->flags)) {
973 file = folder_item_fetch_msg(queue, msginfo->msgnum);
975 gboolean queued_removed = FALSE;
976 if (procmsg_send_message_queue_full(file,
977 !procmsg_is_last_for_account(queue, msginfo, elem),
978 errstr, queue, msginfo->msgnum, &queued_removed) < 0) {
979 g_warning("Sending queued message %d failed.\n",
985 folder_item_remove_msg(queue, msginfo->msgnum);
990 /* FIXME: supposedly if only one message is locked, and queue
991 * is being flushed, the following free says something like
992 * "freeing msg ## in folder (nil)". */
993 procmsg_msginfo_free(msginfo);
996 g_slist_free(sorted_list);
997 folder_item_scan(queue);
999 if (queue->node && queue->node->children) {
1000 node = queue->node->children;
1001 while (node != NULL) {
1004 send_queue_lock = FALSE;
1005 res = procmsg_send_queue(FOLDER_ITEM(node->data), save_msgs, errstr);
1006 send_queue_lock = TRUE;
1014 procmsg_queue_unlock();
1016 toolbar_main_set_sensitive(mainwindow_get_mainwindow());
1018 return (err != 0 ? -err : sent);
1021 gboolean procmsg_is_sending(void)
1023 return send_queue_lock;
1027 *\brief Determine if a queue folder is empty
1029 *\param queue Queue folder to process
1031 *\return TRUE if the queue folder is empty, otherwise return FALSE
1033 gboolean procmsg_queue_is_empty(FolderItem *queue)
1036 gboolean res = FALSE;
1038 queue = folder_get_default_queue();
1039 cm_return_val_if_fail(queue != NULL, TRUE);
1041 folder_item_scan(queue);
1042 list = folder_item_get_msg_list(queue);
1043 res = (list == NULL);
1044 procmsg_msg_list_free(list);
1048 if (queue->node && queue->node->children) {
1049 node = queue->node->children;
1050 while (node != NULL) {
1052 if (!procmsg_queue_is_empty(FOLDER_ITEM(node->data)))
1061 gint procmsg_remove_special_headers(const gchar *in, const gchar *out)
1064 gchar buf[BUFFSIZE];
1066 if ((fp = g_fopen(in, "rb")) == NULL) {
1067 FILE_OP_ERROR(in, "fopen");
1070 if ((outfp = g_fopen(out, "wb")) == NULL) {
1071 FILE_OP_ERROR(out, "fopen");
1075 while (fgets(buf, sizeof(buf), fp) != NULL) {
1077 if ((!strncmp(buf, "X-Claws-End-Special-Headers: 1",
1078 strlen("X-Claws-End-Special-Headers:"))) ||
1079 (!strncmp(buf, "X-Sylpheed-End-Special-Headers: 1",
1080 strlen("X-Sylpheed-End-Special-Headers:"))))
1083 if (buf[0] == '\r' || buf[0] == '\n') break;
1084 /* from other mailers */
1085 if (!strncmp(buf, "Date: ", 6)
1086 || !strncmp(buf, "To: ", 4)
1087 || !strncmp(buf, "From: ", 6)
1088 || !strncmp(buf, "Subject: ", 9)) {
1093 while (fgets(buf, sizeof(buf), fp) != NULL)
1100 static gint procmsg_save_to_outbox(FolderItem *outbox, const gchar *file,
1104 MsgInfo *msginfo, *tmp_msginfo;
1105 MsgFlags flag = {0, 0};
1107 debug_print("saving sent message...\n");
1110 outbox = folder_get_default_outbox();
1111 cm_return_val_if_fail(outbox != NULL, -1);
1113 /* remove queueing headers */
1115 gchar tmp[MAXPATHLEN + 1];
1117 g_snprintf(tmp, sizeof(tmp), "%s%ctmpmsg.out.%08x",
1118 get_rc_dir(), G_DIR_SEPARATOR, (guint) rand());
1120 if (procmsg_remove_special_headers(file, tmp) !=0)
1123 folder_item_scan(outbox);
1124 if ((num = folder_item_add_msg(outbox, tmp, &flag, TRUE)) < 0) {
1125 g_warning("can't save message\n");
1130 folder_item_scan(outbox);
1131 if ((num = folder_item_add_msg
1132 (outbox, file, &flag, FALSE)) < 0) {
1133 g_warning("can't save message\n");
1137 msginfo = folder_item_get_msginfo(outbox, num); /* refcnt++ */
1138 tmp_msginfo = procmsg_msginfo_get_full_info(msginfo); /* refcnt++ */
1139 if (msginfo != NULL) {
1140 procmsg_msginfo_unset_flags(msginfo, ~0, 0);
1141 procmsg_msginfo_free(msginfo); /* refcnt-- */
1142 /* tmp_msginfo == msginfo */
1143 if (tmp_msginfo && msginfo->extradata &&
1144 (msginfo->extradata->dispositionnotificationto ||
1145 msginfo->extradata->returnreceiptto)) {
1146 procmsg_msginfo_set_flags(msginfo, MSG_RETRCPT_SENT, 0);
1148 procmsg_msginfo_free(tmp_msginfo); /* refcnt-- */
1155 void procmsg_print_message(MsgInfo *msginfo, const gchar *cmdline)
1157 static const gchar *def_cmd = "lpr %s";
1158 static guint id = 0;
1164 cm_return_if_fail(msginfo);
1166 if (procmime_msginfo_is_encrypted(msginfo))
1167 tmpfp = procmime_get_first_encrypted_text_content(msginfo);
1169 tmpfp = procmime_get_first_text_content(msginfo);
1170 if (tmpfp == NULL) {
1171 g_warning("Can't get text part\n");
1175 prtmp = g_strdup_printf("%s%cprinttmp.%08x",
1176 get_mime_tmp_dir(), G_DIR_SEPARATOR, id++);
1178 if ((prfp = g_fopen(prtmp, "wb")) == NULL) {
1179 FILE_OP_ERROR(prtmp, "fopen");
1185 if (msginfo->date) { r = fprintf(prfp, "Date: %s\n", msginfo->date); if (r < 0) goto fpferr; }
1186 if (msginfo->from) { r = fprintf(prfp, "From: %s\n", msginfo->from); if (r < 0) goto fpferr; }
1187 if (msginfo->to) { r = fprintf(prfp, "To: %s\n", msginfo->to); if (r < 0) goto fpferr; }
1188 if (msginfo->cc) { r = fprintf(prfp, "Cc: %s\n", msginfo->cc); if (r < 0) goto fpferr; }
1189 if (msginfo->newsgroups) {
1190 r = fprintf(prfp, "Newsgroups: %s\n", msginfo->newsgroups); if (r < 0) goto fpferr;
1192 if (msginfo->subject) { r = fprintf(prfp, "Subject: %s\n", msginfo->subject); if (r < 0) goto fpferr; }
1193 if (fputc('\n', prfp) == EOF) goto fpferr;
1195 while (fgets(buf, sizeof(buf), tmpfp) != NULL) {
1196 r = fputs(buf, prfp);
1197 if (r == EOF) goto fpferr;
1203 if (cmdline && (p = strchr(cmdline, '%')) && *(p + 1) == 's' &&
1204 !strchr(p + 2, '%'))
1205 g_snprintf(buf, sizeof(buf) - 1, cmdline, prtmp);
1208 g_warning("Print command-line is invalid: '%s'\n",
1210 g_snprintf(buf, sizeof(buf) - 1, def_cmd, prtmp);
1216 if (buf[strlen(buf) - 1] != '&')
1217 strncat(buf, "&", sizeof(buf) - strlen(buf) - 1);
1218 if (system(buf) == -1)
1219 g_warning("system(%s) failed.", buf);
1222 FILE_OP_ERROR(prtmp, "fprintf/fputc/fputs");
1228 MsgInfo *procmsg_msginfo_new_ref(MsgInfo *msginfo)
1235 MsgInfo *procmsg_msginfo_new(void)
1237 MsgInfo *newmsginfo;
1239 newmsginfo = g_new0(MsgInfo, 1);
1240 newmsginfo->refcnt = 1;
1245 MsgInfo *procmsg_msginfo_copy(MsgInfo *msginfo)
1247 MsgInfo *newmsginfo;
1250 if (msginfo == NULL) return NULL;
1252 newmsginfo = g_new0(MsgInfo, 1);
1254 newmsginfo->refcnt = 1;
1256 #define MEMBCOPY(mmb) newmsginfo->mmb = msginfo->mmb
1257 #define MEMBDUP(mmb) newmsginfo->mmb = msginfo->mmb ? \
1258 g_strdup(msginfo->mmb) : NULL
1273 MEMBDUP(newsgroups);
1280 MEMBCOPY(to_folder);
1282 if (msginfo->extradata) {
1283 newmsginfo->extradata = g_new0(MsgInfoExtraData, 1);
1284 MEMBDUP(extradata->face);
1285 MEMBDUP(extradata->xface);
1286 MEMBDUP(extradata->dispositionnotificationto);
1287 MEMBDUP(extradata->returnreceiptto);
1288 MEMBDUP(extradata->partial_recv);
1289 MEMBDUP(extradata->account_server);
1290 MEMBDUP(extradata->account_login);
1291 MEMBDUP(extradata->list_post);
1292 MEMBDUP(extradata->list_subscribe);
1293 MEMBDUP(extradata->list_unsubscribe);
1294 MEMBDUP(extradata->list_help);
1295 MEMBDUP(extradata->list_archive);
1296 MEMBDUP(extradata->list_owner);
1299 refs = msginfo->references;
1300 for (refs = msginfo->references; refs != NULL; refs = refs->next) {
1301 newmsginfo->references = g_slist_prepend
1302 (newmsginfo->references, g_strdup(refs->data));
1304 newmsginfo->references = g_slist_reverse(newmsginfo->references);
1307 MEMBDUP(plaintext_file);
1312 MsgInfo *procmsg_msginfo_get_full_info_from_file(MsgInfo *msginfo, const gchar *file)
1314 MsgInfo *full_msginfo;
1316 if (msginfo == NULL) return NULL;
1318 if (!file || !is_file_exist(file)) {
1319 g_warning("procmsg_msginfo_get_full_info_from_file(): can't get message file.\n");
1323 full_msginfo = procheader_parse_file(file, msginfo->flags, TRUE, FALSE);
1324 if (!full_msginfo) return NULL;
1326 msginfo->total_size = full_msginfo->total_size;
1327 msginfo->planned_download = full_msginfo->planned_download;
1329 if (full_msginfo->extradata) {
1330 if (!msginfo->extradata)
1331 msginfo->extradata = g_new0(MsgInfoExtraData, 1);
1332 if (!msginfo->extradata->list_post)
1333 msginfo->extradata->list_post = g_strdup(full_msginfo->extradata->list_post);
1334 if (!msginfo->extradata->list_subscribe)
1335 msginfo->extradata->list_subscribe = g_strdup(full_msginfo->extradata->list_subscribe);
1336 if (!msginfo->extradata->list_unsubscribe)
1337 msginfo->extradata->list_unsubscribe = g_strdup(full_msginfo->extradata->list_unsubscribe);
1338 if (!msginfo->extradata->list_help)
1339 msginfo->extradata->list_help = g_strdup(full_msginfo->extradata->list_help);
1340 if (!msginfo->extradata->list_archive)
1341 msginfo->extradata->list_archive= g_strdup(full_msginfo->extradata->list_archive);
1342 if (!msginfo->extradata->list_owner)
1343 msginfo->extradata->list_owner = g_strdup(full_msginfo->extradata->list_owner);
1344 if (!msginfo->extradata->xface)
1345 msginfo->extradata->xface = g_strdup(full_msginfo->extradata->xface);
1346 if (!msginfo->extradata->face)
1347 msginfo->extradata->face = g_strdup(full_msginfo->extradata->face);
1348 if (!msginfo->extradata->dispositionnotificationto)
1349 msginfo->extradata->dispositionnotificationto =
1350 g_strdup(full_msginfo->extradata->dispositionnotificationto);
1351 if (!msginfo->extradata->returnreceiptto)
1352 msginfo->extradata->returnreceiptto = g_strdup
1353 (full_msginfo->extradata->returnreceiptto);
1354 if (!msginfo->extradata->partial_recv && full_msginfo->extradata->partial_recv)
1355 msginfo->extradata->partial_recv = g_strdup
1356 (full_msginfo->extradata->partial_recv);
1357 if (!msginfo->extradata->account_server && full_msginfo->extradata->account_server)
1358 msginfo->extradata->account_server = g_strdup
1359 (full_msginfo->extradata->account_server);
1360 if (!msginfo->extradata->account_login && full_msginfo->extradata->account_login)
1361 msginfo->extradata->account_login = g_strdup
1362 (full_msginfo->extradata->account_login);
1364 procmsg_msginfo_free(full_msginfo);
1366 return procmsg_msginfo_new_ref(msginfo);
1369 MsgInfo *procmsg_msginfo_get_full_info(MsgInfo *msginfo)
1371 MsgInfo *full_msginfo;
1374 if (msginfo == NULL) return NULL;
1376 file = procmsg_get_message_file_path(msginfo);
1377 if (!file || !is_file_exist(file)) {
1379 file = procmsg_get_message_file(msginfo);
1381 if (!file || !is_file_exist(file)) {
1382 g_warning("procmsg_msginfo_get_full_info(): can't get message file.\n");
1386 full_msginfo = procmsg_msginfo_get_full_info_from_file(msginfo, file);
1388 return full_msginfo;
1391 void procmsg_msginfo_free(MsgInfo *msginfo)
1393 if (msginfo == NULL) return;
1396 if (msginfo->refcnt > 0)
1399 if (msginfo->to_folder) {
1400 msginfo->to_folder->op_count--;
1401 folder_item_update(msginfo->to_folder, F_ITEM_UPDATE_MSGCNT);
1404 g_free(msginfo->fromspace);
1406 g_free(msginfo->fromname);
1408 g_free(msginfo->date);
1409 g_free(msginfo->from);
1410 g_free(msginfo->to);
1411 g_free(msginfo->cc);
1412 g_free(msginfo->newsgroups);
1413 g_free(msginfo->subject);
1414 g_free(msginfo->msgid);
1415 g_free(msginfo->inreplyto);
1416 g_free(msginfo->xref);
1418 if (msginfo->extradata) {
1419 g_free(msginfo->extradata->returnreceiptto);
1420 g_free(msginfo->extradata->dispositionnotificationto);
1421 g_free(msginfo->extradata->xface);
1422 g_free(msginfo->extradata->face);
1423 g_free(msginfo->extradata->list_post);
1424 g_free(msginfo->extradata->list_subscribe);
1425 g_free(msginfo->extradata->list_unsubscribe);
1426 g_free(msginfo->extradata->list_help);
1427 g_free(msginfo->extradata->list_archive);
1428 g_free(msginfo->extradata->list_owner);
1429 g_free(msginfo->extradata->partial_recv);
1430 g_free(msginfo->extradata->account_server);
1431 g_free(msginfo->extradata->account_login);
1432 g_free(msginfo->extradata);
1434 slist_free_strings_full(msginfo->references);
1435 g_slist_free(msginfo->tags);
1437 g_free(msginfo->plaintext_file);
1442 guint procmsg_msginfo_memusage(MsgInfo *msginfo)
1447 memusage += sizeof(MsgInfo);
1448 if (msginfo->fromname)
1449 memusage += strlen(msginfo->fromname);
1451 memusage += strlen(msginfo->date);
1453 memusage += strlen(msginfo->from);
1455 memusage += strlen(msginfo->to);
1457 memusage += strlen(msginfo->cc);
1458 if (msginfo->newsgroups)
1459 memusage += strlen(msginfo->newsgroups);
1460 if (msginfo->subject)
1461 memusage += strlen(msginfo->subject);
1463 memusage += strlen(msginfo->msgid);
1464 if (msginfo->inreplyto)
1465 memusage += strlen(msginfo->inreplyto);
1467 for (tmp = msginfo->references; tmp; tmp=tmp->next) {
1468 gchar *r = (gchar *)tmp->data;
1469 memusage += r?strlen(r):0 + sizeof(GSList);
1471 if (msginfo->fromspace)
1472 memusage += strlen(msginfo->fromspace);
1474 for (tmp = msginfo->tags; tmp; tmp=tmp->next) {
1475 memusage += sizeof(GSList);
1477 if (msginfo->extradata) {
1478 memusage += sizeof(MsgInfoExtraData);
1479 if (msginfo->extradata->xface)
1480 memusage += strlen(msginfo->extradata->xface);
1481 if (msginfo->extradata->face)
1482 memusage += strlen(msginfo->extradata->face);
1483 if (msginfo->extradata->dispositionnotificationto)
1484 memusage += strlen(msginfo->extradata->dispositionnotificationto);
1485 if (msginfo->extradata->returnreceiptto)
1486 memusage += strlen(msginfo->extradata->returnreceiptto);
1488 if (msginfo->extradata->partial_recv)
1489 memusage += strlen(msginfo->extradata->partial_recv);
1490 if (msginfo->extradata->account_server)
1491 memusage += strlen(msginfo->extradata->account_server);
1492 if (msginfo->extradata->account_login)
1493 memusage += strlen(msginfo->extradata->account_login);
1495 if (msginfo->extradata->list_post)
1496 memusage += strlen(msginfo->extradata->list_post);
1497 if (msginfo->extradata->list_subscribe)
1498 memusage += strlen(msginfo->extradata->list_subscribe);
1499 if (msginfo->extradata->list_unsubscribe)
1500 memusage += strlen(msginfo->extradata->list_unsubscribe);
1501 if (msginfo->extradata->list_help)
1502 memusage += strlen(msginfo->extradata->list_help);
1503 if (msginfo->extradata->list_archive)
1504 memusage += strlen(msginfo->extradata->list_archive);
1505 if (msginfo->extradata->list_owner)
1506 memusage += strlen(msginfo->extradata->list_owner);
1511 static gint procmsg_send_message_queue_full(const gchar *file, gboolean keep_session, gchar **errstr,
1512 FolderItem *queue, gint msgnum, gboolean *queued_removed)
1514 static HeaderEntry qentry[] = {{"S:", NULL, FALSE},
1515 {"SSV:", NULL, FALSE},
1516 {"R:", NULL, FALSE},
1517 {"NG:", NULL, FALSE},
1518 {"MAID:", NULL, FALSE},
1519 {"NAID:", NULL, FALSE},
1520 {"SCF:", NULL, FALSE},
1521 {"RMID:", NULL, FALSE},
1522 {"FMID:", NULL, FALSE},
1523 {"X-Claws-Privacy-System:", NULL, FALSE},
1524 {"X-Claws-Encrypt:", NULL, FALSE},
1525 {"X-Claws-Encrypt-Data:", NULL, FALSE},
1526 {"X-Claws-End-Special-Headers:", NULL, FALSE},
1527 {"X-Sylpheed-Privacy-System:", NULL, FALSE},
1528 {"X-Sylpheed-Encrypt:", NULL, FALSE},
1529 {"X-Sylpheed-Encrypt-Data:", NULL, FALSE},
1530 {"X-Sylpheed-End-Special-Headers:", NULL, FALSE},
1531 {NULL, NULL, FALSE}};
1534 gint mailval = 0, newsval = 0;
1536 gchar *smtpserver = NULL;
1537 GSList *to_list = NULL;
1538 GSList *newsgroup_list = NULL;
1539 gchar *savecopyfolder = NULL;
1540 gchar *replymessageid = NULL;
1541 gchar *fwdmessageid = NULL;
1542 gchar *privacy_system = NULL;
1543 gboolean encrypt = FALSE;
1544 gchar *encrypt_data = NULL;
1545 gchar buf[BUFFSIZE];
1547 PrefsAccount *mailac = NULL, *newsac = NULL;
1548 gboolean save_clear_text = TRUE;
1549 gchar *tmp_enc_file = NULL;
1551 cm_return_val_if_fail(file != NULL, -1);
1553 if ((fp = g_fopen(file, "rb")) == NULL) {
1554 FILE_OP_ERROR(file, "fopen");
1556 if (*errstr) g_free(*errstr);
1557 *errstr = g_strdup_printf(_("Couldn't open file %s."), file);
1562 while ((hnum = procheader_get_one_field(buf, sizeof(buf), fp, qentry))
1564 gchar *p = buf + strlen(qentry[hnum].name);
1572 if (smtpserver == NULL)
1573 smtpserver = g_strdup(p);
1576 to_list = address_list_append(to_list, p);
1579 newsgroup_list = newsgroup_list_append(newsgroup_list, p);
1581 case Q_MAIL_ACCOUNT_ID:
1582 mailac = account_find_from_id(atoi(p));
1584 case Q_NEWS_ACCOUNT_ID:
1585 newsac = account_find_from_id(atoi(p));
1587 case Q_SAVE_COPY_FOLDER:
1588 if (savecopyfolder == NULL)
1589 savecopyfolder = g_strdup(p);
1591 case Q_REPLY_MESSAGE_ID:
1592 if (replymessageid == NULL)
1593 replymessageid = g_strdup(p);
1595 case Q_FWD_MESSAGE_ID:
1596 if (fwdmessageid == NULL)
1597 fwdmessageid = g_strdup(p);
1599 case Q_PRIVACY_SYSTEM:
1600 case Q_PRIVACY_SYSTEM_OLD:
1601 if (privacy_system == NULL)
1602 privacy_system = g_strdup(p);
1609 case Q_ENCRYPT_DATA:
1610 case Q_ENCRYPT_DATA_OLD:
1611 if (encrypt_data == NULL)
1612 encrypt_data = g_strdup(p);
1615 case Q_CLAWS_HDRS_OLD:
1616 /* end of special headers reached */
1617 goto send_mail; /* can't "break;break;" */
1621 filepos = ftell(fp);
1626 if (mailac && mailac->save_encrypted_as_clear_text
1627 && !mailac->encrypt_to_self)
1628 save_clear_text = TRUE;
1630 save_clear_text = FALSE;
1635 mimeinfo = procmime_scan_queue_file(file);
1636 if (!privacy_encrypt(privacy_system, mimeinfo, encrypt_data)
1637 || (fp = my_tmpfile()) == NULL
1638 || procmime_write_mimeinfo(mimeinfo, fp) < 0) {
1641 procmime_mimeinfo_free_all(mimeinfo);
1644 slist_free_strings_full(to_list);
1645 slist_free_strings_full(newsgroup_list);
1646 g_free(savecopyfolder);
1647 g_free(replymessageid);
1648 g_free(fwdmessageid);
1649 g_free(privacy_system);
1650 g_free(encrypt_data);
1652 if (*errstr) g_free(*errstr);
1653 *errstr = g_strdup_printf(_("Couldn't encrypt the email: %s"),
1654 privacy_get_error());
1660 if (!save_clear_text) {
1661 gchar *content = NULL;
1662 FILE *tmpfp = get_tmpfile_in_dir(get_mime_tmp_dir(), &tmp_enc_file);
1666 content = file_read_stream_to_str(fp);
1669 str_write_to_file(content, tmp_enc_file);
1672 g_warning("couldn't get tempfile\n");
1676 procmime_mimeinfo_free_all(mimeinfo);
1682 debug_print("Sending message by mail\n");
1685 if (*errstr) g_free(*errstr);
1686 *errstr = g_strdup_printf(_("Queued message header is broken."));
1689 } else if (mailac && mailac->use_mail_command &&
1690 mailac->mail_command && (* mailac->mail_command)) {
1691 mailval = send_message_local(mailac->mail_command, fp);
1694 mailac = account_find_from_smtp_server(from, smtpserver);
1696 g_warning("Account not found. "
1697 "Using current account...\n");
1698 mailac = cur_account;
1703 mailval = send_message_smtp_full(mailac, to_list, fp, keep_session);
1704 if (mailval == -1 && errstr) {
1705 if (*errstr) g_free(*errstr);
1706 *errstr = g_strdup_printf(_("An error happened during SMTP session."));
1709 PrefsAccount tmp_ac;
1711 g_warning("Account not found.\n");
1713 memset(&tmp_ac, 0, sizeof(PrefsAccount));
1714 tmp_ac.address = from;
1715 tmp_ac.smtp_server = smtpserver;
1716 tmp_ac.smtpport = SMTP_PORT;
1717 mailval = send_message_smtp(&tmp_ac, to_list, fp);
1718 if (mailval == -1 && errstr) {
1719 if (*errstr) g_free(*errstr);
1720 *errstr = g_strdup_printf(_("No specific account has been found to "
1721 "send, and an error happened during SMTP session."));
1725 } else if (!to_list && !newsgroup_list) {
1727 if (*errstr) g_free(*errstr);
1728 *errstr = g_strdup(_("Couldn't determine sending informations. "
1729 "Maybe the email hasn't been generated by Claws Mail."));
1734 fseek(fp, filepos, SEEK_SET);
1735 if (newsgroup_list && newsac && (mailval == 0)) {
1740 /* write to temporary file */
1741 tmp = g_strdup_printf("%s%cnntp%p", get_tmp_dir(),
1742 G_DIR_SEPARATOR, file);
1743 if ((tmpfp = g_fopen(tmp, "wb")) == NULL) {
1744 FILE_OP_ERROR(tmp, "fopen");
1746 alertpanel_error(_("Couldn't create temporary file for news sending."));
1748 if (change_file_mode_rw(tmpfp, tmp) < 0) {
1749 FILE_OP_ERROR(tmp, "chmod");
1750 g_warning("can't change file mode\n");
1753 while ((newsval == 0) && fgets(buf, sizeof(buf), fp) != NULL) {
1754 if (fputs(buf, tmpfp) == EOF) {
1755 FILE_OP_ERROR(tmp, "fputs");
1758 if (*errstr) g_free(*errstr);
1759 *errstr = g_strdup_printf(_("Error when writing temporary file for news sending."));
1766 debug_print("Sending message by news\n");
1768 folder = FOLDER(newsac->folder);
1770 newsval = news_post(folder, tmp);
1771 if (newsval < 0 && errstr) {
1772 if (*errstr) g_free(*errstr);
1773 *errstr = g_strdup_printf(_("Error occurred while posting the message to %s."),
1774 newsac->nntp_server);
1784 /* update session statistics */
1785 if (mailval == 0 && newsval == 0) {
1786 /* update session stats */
1788 session_stats.replied++;
1789 else if (fwdmessageid)
1790 session_stats.forwarded++;
1792 session_stats.sent++;
1795 /* save message to outbox */
1796 if (mailval == 0 && newsval == 0 && savecopyfolder) {
1799 debug_print("saving sent message...\n");
1801 outbox = folder_find_item_from_identifier(savecopyfolder);
1803 outbox = folder_get_default_outbox();
1805 if (save_clear_text || tmp_enc_file == NULL) {
1806 gboolean saved = FALSE;
1807 *queued_removed = FALSE;
1808 if (queue && msgnum > 0) {
1809 MsgInfo *queued_mail = folder_item_get_msginfo(queue, msgnum);
1810 if (folder_item_move_msg(outbox, queued_mail) >= 0) {
1811 debug_print("moved queued mail %d to sent folder\n", msgnum);
1813 *queued_removed = TRUE;
1814 } else if (folder_item_copy_msg(outbox, queued_mail) >= 0) {
1815 debug_print("copied queued mail %d to sent folder\n", msgnum);
1818 procmsg_msginfo_free(queued_mail);
1821 debug_print("resaving clear text queued mail to sent folder\n");
1822 procmsg_save_to_outbox(outbox, file, TRUE);
1825 debug_print("saving encrpyted queued mail to sent folder\n");
1826 procmsg_save_to_outbox(outbox, tmp_enc_file, FALSE);
1830 if (tmp_enc_file != NULL) {
1831 claws_unlink(tmp_enc_file);
1833 tmp_enc_file = NULL;
1836 if (replymessageid != NULL || fwdmessageid != NULL) {
1840 if (replymessageid != NULL)
1841 tokens = g_strsplit(replymessageid, "\t", 0);
1843 tokens = g_strsplit(fwdmessageid, "\t", 0);
1844 item = folder_find_item_from_identifier(tokens[0]);
1846 /* check if queued message has valid folder and message id */
1847 if (item != NULL && tokens[2] != NULL) {
1850 msginfo = folder_item_get_msginfo(item, atoi(tokens[1]));
1852 /* check if referring message exists and has a message id */
1853 if ((msginfo != NULL) &&
1854 (msginfo->msgid != NULL) &&
1855 (strcmp(msginfo->msgid, tokens[2]) != 0)) {
1856 procmsg_msginfo_free(msginfo);
1860 if (msginfo == NULL) {
1861 msginfo = folder_item_get_msginfo_by_msgid(item, tokens[2]);
1864 if (msginfo != NULL) {
1865 if (replymessageid != NULL) {
1866 MsgPermFlags to_unset = 0;
1868 if (prefs_common.mark_as_read_on_new_window)
1869 to_unset = (MSG_NEW|MSG_UNREAD);
1871 procmsg_msginfo_unset_flags(msginfo, to_unset, 0);
1872 procmsg_msginfo_set_flags(msginfo, MSG_REPLIED, 0);
1874 procmsg_msginfo_set_flags(msginfo, MSG_FORWARDED, 0);
1876 procmsg_msginfo_free(msginfo);
1884 slist_free_strings_full(to_list);
1885 slist_free_strings_full(newsgroup_list);
1886 g_free(savecopyfolder);
1887 g_free(replymessageid);
1888 g_free(fwdmessageid);
1889 g_free(privacy_system);
1890 g_free(encrypt_data);
1892 return (newsval != 0 ? newsval : mailval);
1895 gint procmsg_send_message_queue(const gchar *file, gchar **errstr, FolderItem *queue, gint msgnum, gboolean *queued_removed)
1897 gint result = procmsg_send_message_queue_full(file, FALSE, errstr, queue, msgnum, queued_removed);
1898 toolbar_main_set_sensitive(mainwindow_get_mainwindow());
1902 gint procmsg_send_message_queue_with_lock(const gchar *file, gchar **errstr, FolderItem *queue, gint msgnum, gboolean *queued_removed)
1905 if (procmsg_queue_lock(errstr)) {
1906 val = procmsg_send_message_queue(file, errstr, queue, msgnum, queued_removed);
1907 procmsg_queue_unlock();
1913 static void update_folder_msg_counts(FolderItem *item, MsgInfo *msginfo, MsgPermFlags old_flags)
1915 MsgPermFlags new_flags = msginfo->flags.perm_flags;
1918 if (!(old_flags & MSG_NEW) && (new_flags & MSG_NEW)) {
1922 if ((old_flags & MSG_NEW) && !(new_flags & MSG_NEW)) {
1927 if (!(old_flags & MSG_UNREAD) && (new_flags & MSG_UNREAD)) {
1928 item->unread_msgs++;
1929 if (procmsg_msg_has_marked_parent(msginfo))
1930 item->unreadmarked_msgs++;
1933 if ((old_flags & MSG_UNREAD) && !(new_flags & MSG_UNREAD)) {
1934 item->unread_msgs--;
1935 if (procmsg_msg_has_marked_parent(msginfo))
1936 item->unreadmarked_msgs--;
1940 if (!(old_flags & MSG_MARKED) && (new_flags & MSG_MARKED)) {
1941 procmsg_update_unread_children(msginfo, TRUE);
1942 item->marked_msgs++;
1945 if ((old_flags & MSG_MARKED) && !(new_flags & MSG_MARKED)) {
1946 procmsg_update_unread_children(msginfo, FALSE);
1947 item->marked_msgs--;
1950 if (!(old_flags & MSG_REPLIED) && (new_flags & MSG_REPLIED)) {
1951 item->replied_msgs++;
1954 if ((old_flags & MSG_REPLIED) && !(new_flags & MSG_REPLIED)) {
1955 item->replied_msgs--;
1958 if (!(old_flags & MSG_FORWARDED) && (new_flags & MSG_FORWARDED)) {
1959 item->forwarded_msgs++;
1962 if ((old_flags & MSG_FORWARDED) && !(new_flags & MSG_FORWARDED)) {
1963 item->forwarded_msgs--;
1966 if (!(old_flags & MSG_LOCKED) && (new_flags & MSG_LOCKED)) {
1967 item->locked_msgs++;
1970 if ((old_flags & MSG_LOCKED) && !(new_flags & MSG_LOCKED)) {
1971 item->locked_msgs--;
1974 if ((old_flags & MSG_IGNORE_THREAD) && !(new_flags & MSG_IGNORE_THREAD)) {
1975 item->ignored_msgs--;
1978 if (!(old_flags & MSG_IGNORE_THREAD) && (new_flags & MSG_IGNORE_THREAD)) {
1979 item->ignored_msgs++;
1982 if ((old_flags & MSG_WATCH_THREAD) && !(new_flags & MSG_WATCH_THREAD)) {
1983 item->watched_msgs--;
1986 if (!(old_flags & MSG_WATCH_THREAD) && (new_flags & MSG_WATCH_THREAD)) {
1987 item->watched_msgs++;
1991 void procmsg_msginfo_set_flags(MsgInfo *msginfo, MsgPermFlags perm_flags, MsgTmpFlags tmp_flags)
1994 MsgInfoUpdate msginfo_update;
1995 MsgPermFlags perm_flags_new, perm_flags_old;
1996 MsgTmpFlags tmp_flags_old;
1998 cm_return_if_fail(msginfo != NULL);
1999 item = msginfo->folder;
2000 cm_return_if_fail(item != NULL);
2002 debug_print("Setting flags for message %d in folder %s\n", msginfo->msgnum, item->path);
2004 /* Perm Flags handling */
2005 perm_flags_old = msginfo->flags.perm_flags;
2006 perm_flags_new = msginfo->flags.perm_flags | perm_flags;
2007 if ((perm_flags & MSG_IGNORE_THREAD) || (perm_flags_old & MSG_IGNORE_THREAD)) {
2008 perm_flags_new &= ~(MSG_NEW | MSG_UNREAD);
2010 if ((perm_flags & MSG_WATCH_THREAD) || (perm_flags_old & MSG_WATCH_THREAD)) {
2011 perm_flags_new &= ~(MSG_IGNORE_THREAD);
2014 if (perm_flags_old != perm_flags_new) {
2015 folder_item_change_msg_flags(msginfo->folder, msginfo, perm_flags_new);
2017 update_folder_msg_counts(item, msginfo, perm_flags_old);
2018 summary_update_unread(mainwindow_get_mainwindow()->summaryview, NULL);
2021 /* Tmp flags handling */
2022 tmp_flags_old = msginfo->flags.tmp_flags;
2023 msginfo->flags.tmp_flags |= tmp_flags;
2025 /* update notification */
2026 if ((perm_flags_old != perm_flags_new) || (tmp_flags_old != msginfo->flags.tmp_flags)) {
2027 msginfo_update.msginfo = msginfo;
2028 msginfo_update.flags = MSGINFO_UPDATE_FLAGS;
2029 hooks_invoke(MSGINFO_UPDATE_HOOKLIST, &msginfo_update);
2030 folder_item_update(msginfo->folder, F_ITEM_UPDATE_MSGCNT);
2034 void procmsg_msginfo_unset_flags(MsgInfo *msginfo, MsgPermFlags perm_flags, MsgTmpFlags tmp_flags)
2037 MsgInfoUpdate msginfo_update;
2038 MsgPermFlags perm_flags_new, perm_flags_old;
2039 MsgTmpFlags tmp_flags_old;
2041 cm_return_if_fail(msginfo != NULL);
2042 item = msginfo->folder;
2043 cm_return_if_fail(item != NULL);
2045 debug_print("Unsetting flags for message %d in folder %s\n", msginfo->msgnum, item->path);
2047 /* Perm Flags handling */
2048 perm_flags_old = msginfo->flags.perm_flags;
2049 perm_flags_new = msginfo->flags.perm_flags & ~perm_flags;
2051 if (perm_flags_old != perm_flags_new) {
2052 folder_item_change_msg_flags(msginfo->folder, msginfo, perm_flags_new);
2054 update_folder_msg_counts(item, msginfo, perm_flags_old);
2057 /* Tmp flags hanlding */
2058 tmp_flags_old = msginfo->flags.tmp_flags;
2059 msginfo->flags.tmp_flags &= ~tmp_flags;
2061 /* update notification */
2062 if ((perm_flags_old != perm_flags_new) || (tmp_flags_old != msginfo->flags.tmp_flags)) {
2063 msginfo_update.msginfo = msginfo;
2064 msginfo_update.flags = MSGINFO_UPDATE_FLAGS;
2065 hooks_invoke(MSGINFO_UPDATE_HOOKLIST, &msginfo_update);
2066 folder_item_update(msginfo->folder, F_ITEM_UPDATE_MSGCNT);
2070 void procmsg_msginfo_change_flags(MsgInfo *msginfo,
2071 MsgPermFlags add_perm_flags, MsgTmpFlags add_tmp_flags,
2072 MsgPermFlags rem_perm_flags, MsgTmpFlags rem_tmp_flags)
2075 MsgInfoUpdate msginfo_update;
2076 MsgPermFlags perm_flags_new, perm_flags_old;
2077 MsgTmpFlags tmp_flags_old;
2079 cm_return_if_fail(msginfo != NULL);
2080 item = msginfo->folder;
2081 cm_return_if_fail(item != NULL);
2083 debug_print("Changing flags for message %d in folder %s\n", msginfo->msgnum, item->path);
2085 /* Perm Flags handling */
2086 perm_flags_old = msginfo->flags.perm_flags;
2087 perm_flags_new = (msginfo->flags.perm_flags & ~rem_perm_flags) | add_perm_flags;
2088 if ((add_perm_flags & MSG_IGNORE_THREAD) || (perm_flags_old & MSG_IGNORE_THREAD)) {
2089 perm_flags_new &= ~(MSG_NEW | MSG_UNREAD);
2091 if ((add_perm_flags & MSG_WATCH_THREAD) || (perm_flags_old & MSG_WATCH_THREAD)) {
2092 perm_flags_new &= ~(MSG_IGNORE_THREAD);
2095 if (perm_flags_old != perm_flags_new) {
2096 folder_item_change_msg_flags(msginfo->folder, msginfo, perm_flags_new);
2098 update_folder_msg_counts(item, msginfo, perm_flags_old);
2102 /* Tmp flags handling */
2103 tmp_flags_old = msginfo->flags.tmp_flags;
2104 msginfo->flags.tmp_flags &= ~rem_tmp_flags;
2105 msginfo->flags.tmp_flags |= add_tmp_flags;
2107 /* update notification */
2108 if ((perm_flags_old != perm_flags_new) || (tmp_flags_old != msginfo->flags.tmp_flags)) {
2109 msginfo_update.msginfo = msginfo;
2110 msginfo_update.flags = MSGINFO_UPDATE_FLAGS;
2111 hooks_invoke(MSGINFO_UPDATE_HOOKLIST, &msginfo_update);
2112 folder_item_update(msginfo->folder, F_ITEM_UPDATE_MSGCNT);
2117 *\brief check for flags (e.g. mark) in prior msgs of current thread
2119 *\param info Current message
2120 *\param perm_flags Flags to be checked
2121 *\param parentmsgs Hash of prior msgs to avoid loops
2123 *\return gboolean TRUE if perm_flags are found
2125 static gboolean procmsg_msg_has_flagged_parent_real(MsgInfo *info,
2126 MsgPermFlags perm_flags, GHashTable *parentmsgs)
2130 cm_return_val_if_fail(info != NULL, FALSE);
2132 if (info != NULL && info->folder != NULL && info->inreplyto != NULL) {
2133 tmp = folder_item_get_msginfo_by_msgid(info->folder,
2135 if (tmp && (tmp->flags.perm_flags & perm_flags)) {
2136 procmsg_msginfo_free(tmp);
2138 } else if (tmp != NULL) {
2141 if (g_hash_table_lookup(parentmsgs, info)) {
2142 debug_print("loop detected: %d\n",
2146 g_hash_table_insert(parentmsgs, info, "1");
2147 result = procmsg_msg_has_flagged_parent_real(
2148 tmp, perm_flags, parentmsgs);
2150 procmsg_msginfo_free(tmp);
2160 *\brief Callback for cleaning up hash of parentmsgs
2162 static gboolean parentmsgs_hash_remove(gpointer key,
2170 *\brief Set up list of parentmsgs
2171 * See procmsg_msg_has_flagged_parent_real()
2173 gboolean procmsg_msg_has_flagged_parent(MsgInfo *info, MsgPermFlags perm_flags)
2176 static GHashTable *parentmsgs = NULL;
2178 if (parentmsgs == NULL)
2179 parentmsgs = g_hash_table_new(NULL, NULL);
2181 result = procmsg_msg_has_flagged_parent_real(info, perm_flags, parentmsgs);
2182 g_hash_table_foreach_remove(parentmsgs, parentmsgs_hash_remove, NULL);
2188 *\brief Check if msgs prior in thread are marked
2189 * See procmsg_msg_has_flagged_parent_real()
2191 gboolean procmsg_msg_has_marked_parent(MsgInfo *info)
2193 return procmsg_msg_has_flagged_parent(info, MSG_MARKED);
2197 static GSList *procmsg_find_children_func(MsgInfo *info,
2198 GSList *children, GSList *all)
2202 cm_return_val_if_fail(info!=NULL, children);
2203 if (info->msgid == NULL)
2206 for (cur = all; cur != NULL; cur = g_slist_next(cur)) {
2207 MsgInfo *tmp = (MsgInfo *)cur->data;
2208 if (tmp->inreplyto && !strcmp(tmp->inreplyto, info->msgid)) {
2209 /* Check if message is already in the list */
2210 if ((children == NULL) ||
2211 (g_slist_index(children, tmp) == -1)) {
2212 children = g_slist_prepend(children,
2213 procmsg_msginfo_new_ref(tmp));
2214 children = procmsg_find_children_func(tmp,
2223 static GSList *procmsg_find_children (MsgInfo *info)
2228 cm_return_val_if_fail(info!=NULL, NULL);
2229 all = folder_item_get_msg_list(info->folder);
2230 children = procmsg_find_children_func(info, NULL, all);
2231 if (children != NULL) {
2232 for (cur = all; cur != NULL; cur = g_slist_next(cur)) {
2233 /* this will not free the used pointers
2234 created with procmsg_msginfo_new_ref */
2235 procmsg_msginfo_free((MsgInfo *)cur->data);
2243 static void procmsg_update_unread_children(MsgInfo *info, gboolean newly_marked)
2245 GSList *children = procmsg_find_children(info);
2247 for (cur = children; cur != NULL; cur = g_slist_next(cur)) {
2248 MsgInfo *tmp = (MsgInfo *)cur->data;
2249 if(MSG_IS_UNREAD(tmp->flags) && !MSG_IS_IGNORE_THREAD(tmp->flags)) {
2251 info->folder->unreadmarked_msgs++;
2253 info->folder->unreadmarked_msgs--;
2254 folder_item_update(info->folder, F_ITEM_UPDATE_MSGCNT);
2256 procmsg_msginfo_free(tmp);
2258 g_slist_free(children);
2262 * Set the destination folder for a copy or move operation
2264 * \param msginfo The message which's destination folder is changed
2265 * \param to_folder The destination folder for the operation
2267 void procmsg_msginfo_set_to_folder(MsgInfo *msginfo, FolderItem *to_folder)
2269 if(msginfo->to_folder != NULL) {
2270 msginfo->to_folder->op_count--;
2271 folder_item_update(msginfo->to_folder, F_ITEM_UPDATE_MSGCNT);
2273 msginfo->to_folder = to_folder;
2274 if(to_folder != NULL) {
2275 to_folder->op_count++;
2276 folder_item_update(msginfo->to_folder, F_ITEM_UPDATE_MSGCNT);
2281 * Apply filtering actions to the msginfo
2283 * \param msginfo The MsgInfo describing the message that should be filtered
2284 * \return TRUE if the message was moved and MsgInfo is now invalid,
2287 static gboolean procmsg_msginfo_filter(MsgInfo *msginfo, PrefsAccount* ac_prefs)
2289 MailFilteringData mail_filtering_data;
2291 mail_filtering_data.msginfo = msginfo;
2292 mail_filtering_data.msglist = NULL;
2293 mail_filtering_data.filtered = NULL;
2294 mail_filtering_data.unfiltered = NULL;
2295 mail_filtering_data.account = ac_prefs;
2297 if (!ac_prefs || ac_prefs->filterhook_on_recv)
2298 if (hooks_invoke(MAIL_FILTERING_HOOKLIST, &mail_filtering_data))
2301 /* filter if enabled in prefs or move to inbox if not */
2302 if((filtering_rules != NULL) &&
2303 filter_message_by_msginfo(filtering_rules, msginfo, ac_prefs,
2304 FILTERING_INCORPORATION, NULL)) {
2311 void procmsg_msglist_filter(GSList *list, PrefsAccount *ac,
2312 GSList **filtered, GSList **unfiltered,
2315 GSList *cur, *to_do = NULL;
2316 gint total = 0, curnum = 0;
2317 MailFilteringData mail_filtering_data;
2319 cm_return_if_fail(filtered != NULL);
2320 cm_return_if_fail(unfiltered != NULL);
2328 total = g_slist_length(list);
2332 *unfiltered = g_slist_copy(list);
2336 statusbar_print_all(_("Filtering messages...\n"));
2338 mail_filtering_data.msginfo = NULL;
2339 mail_filtering_data.msglist = list;
2340 mail_filtering_data.filtered = NULL;
2341 mail_filtering_data.unfiltered = NULL;
2342 mail_filtering_data.account = ac;
2344 if (!ac || ac->filterhook_on_recv)
2345 hooks_invoke(MAIL_LISTFILTERING_HOOKLIST, &mail_filtering_data);
2347 if (mail_filtering_data.filtered == NULL &&
2348 mail_filtering_data.unfiltered == NULL) {
2349 /* nothing happened */
2350 debug_print(MAIL_LISTFILTERING_HOOKLIST " did nothing. filtering whole list normally.\n");
2353 if (mail_filtering_data.filtered != NULL) {
2354 /* keep track of what's been filtered by the hooks */
2355 debug_print(MAIL_LISTFILTERING_HOOKLIST " filtered some stuff. total %d filtered %d unfilt %d.\n",
2356 g_slist_length(list),
2357 g_slist_length(mail_filtering_data.filtered),
2358 g_slist_length(mail_filtering_data.unfiltered));
2360 *filtered = g_slist_copy(mail_filtering_data.filtered);
2362 if (mail_filtering_data.unfiltered != NULL) {
2363 /* what the hooks didn't handle will go in filtered or
2364 * unfiltered in the next loop */
2365 debug_print(MAIL_LISTFILTERING_HOOKLIST " left unfiltered stuff. total %d filtered %d unfilt %d.\n",
2366 g_slist_length(list),
2367 g_slist_length(mail_filtering_data.filtered),
2368 g_slist_length(mail_filtering_data.unfiltered));
2369 to_do = mail_filtering_data.unfiltered;
2372 for (cur = to_do; cur; cur = cur->next) {
2373 MsgInfo *info = (MsgInfo *)cur->data;
2374 if (procmsg_msginfo_filter(info, ac))
2375 *filtered = g_slist_prepend(*filtered, info);
2377 *unfiltered = g_slist_prepend(*unfiltered, info);
2378 statusbar_progress_all(curnum++, total, prefs_common.statusbar_update_step);
2381 g_slist_free(mail_filtering_data.filtered);
2382 g_slist_free(mail_filtering_data.unfiltered);
2384 *filtered = g_slist_reverse(*filtered);
2385 *unfiltered = g_slist_reverse(*unfiltered);
2387 statusbar_progress_all(0,0,0);
2388 statusbar_pop_all();
2391 MsgInfo *procmsg_msginfo_new_from_mimeinfo(MsgInfo *src_msginfo, MimeInfo *mimeinfo)
2393 MsgInfo *tmp_msginfo = NULL;
2394 MsgFlags flags = {0, 0};
2395 gchar *tmpfile = get_tmp_file();
2396 FILE *fp = g_fopen(tmpfile, "wb");
2398 if (!mimeinfo || mimeinfo->type != MIMETYPE_MESSAGE ||
2399 g_ascii_strcasecmp(mimeinfo->subtype, "rfc822")) {
2400 g_warning("procmsg_msginfo_new_from_mimeinfo(): unsuitable mimeinfo");
2407 if (fp && procmime_write_mimeinfo(mimeinfo, fp) >= 0) {
2410 tmp_msginfo = procheader_parse_file(
2417 if (tmp_msginfo != NULL) {
2419 tmp_msginfo->folder = src_msginfo->folder;
2420 tmp_msginfo->plaintext_file = g_strdup(tmpfile);
2422 g_warning("procmsg_msginfo_new_from_mimeinfo(): Can't generate new msginfo");
2430 static GSList *spam_learners = NULL;
2432 void procmsg_register_spam_learner (int (*learn_func)(MsgInfo *info, GSList *list, gboolean spam))
2434 if (!g_slist_find(spam_learners, learn_func))
2435 spam_learners = g_slist_append(spam_learners, learn_func);
2436 if (mainwindow_get_mainwindow()) {
2437 main_window_set_menu_sensitive(mainwindow_get_mainwindow());
2438 summary_set_menu_sensitive(
2439 mainwindow_get_mainwindow()->summaryview);
2440 toolbar_main_set_sensitive(mainwindow_get_mainwindow());
2444 void procmsg_unregister_spam_learner (int (*learn_func)(MsgInfo *info, GSList *list, gboolean spam))
2446 spam_learners = g_slist_remove(spam_learners, learn_func);
2447 if (mainwindow_get_mainwindow()) {
2448 main_window_set_menu_sensitive(mainwindow_get_mainwindow());
2449 summary_set_menu_sensitive(
2450 mainwindow_get_mainwindow()->summaryview);
2451 toolbar_main_set_sensitive(mainwindow_get_mainwindow());
2455 gboolean procmsg_spam_can_learn(void)
2457 return g_slist_length(spam_learners) > 0;
2460 int procmsg_spam_learner_learn (MsgInfo *info, GSList *list, gboolean spam)
2462 GSList *cur = spam_learners;
2464 for (; cur; cur = cur->next) {
2465 int ((*func)(MsgInfo *info, GSList *list, gboolean spam)) = cur->data;
2466 ret |= func(info, list, spam);
2471 static gchar *spam_folder_item = NULL;
2472 static FolderItem * (*procmsg_spam_get_folder_func)(MsgInfo *msginfo) = NULL;
2473 void procmsg_spam_set_folder (const char *item_identifier, FolderItem *(*spam_get_folder_func)(MsgInfo *info))
2475 g_free(spam_folder_item);
2476 if (item_identifier)
2477 spam_folder_item = g_strdup(item_identifier);
2479 spam_folder_item = NULL;
2480 if (spam_get_folder_func != NULL)
2481 procmsg_spam_get_folder_func = spam_get_folder_func;
2483 procmsg_spam_get_folder_func = NULL;
2486 FolderItem *procmsg_spam_get_folder (MsgInfo *msginfo)
2488 FolderItem *item = NULL;
2490 if (procmsg_spam_get_folder_func)
2491 item = procmsg_spam_get_folder_func(msginfo);
2492 if (item == NULL && spam_folder_item)
2493 item = folder_find_item_from_identifier(spam_folder_item);
2495 item = folder_get_default_trash();
2499 static void item_has_queued_mails(FolderItem *item, gpointer data)
2501 gboolean *result = (gboolean *)data;
2502 if (*result == TRUE)
2504 if (folder_has_parent_of_type(item, F_QUEUE)) {
2505 if (item->total_msgs == 0)
2508 GSList *msglist = folder_item_get_msg_list(item);
2510 for (cur = msglist; cur; cur = cur->next) {
2511 MsgInfo *msginfo = (MsgInfo *)cur->data;
2512 if (!MSG_IS_DELETED(msginfo->flags) &&
2513 !MSG_IS_LOCKED(msginfo->flags)) {
2518 procmsg_msg_list_free(msglist);
2523 gboolean procmsg_have_queued_mails_fast (void)
2525 gboolean result = FALSE;
2526 folder_func_to_all_folders(item_has_queued_mails, &result);
2530 static void item_has_trashed_mails(FolderItem *item, gpointer data)
2532 gboolean *result = (gboolean *)data;
2533 if (*result == TRUE)
2535 if (folder_has_parent_of_type(item, F_TRASH) && item->total_msgs > 0)
2539 gboolean procmsg_have_trashed_mails_fast (void)
2541 gboolean result = FALSE;
2542 folder_func_to_all_folders(item_has_trashed_mails, &result);
2546 gchar *procmsg_msginfo_get_tags_str(MsgInfo *msginfo)
2554 if (msginfo->tags == NULL)
2556 for (cur = msginfo->tags; cur; cur = cur->next) {
2557 const gchar *tag = tags_get_tag(GPOINTER_TO_INT(cur->data));
2561 tags = g_strdup(tag);
2563 int olen = strlen(tags);
2564 int nlen = olen + strlen(tag) + 2 /* strlen(", ") */;
2565 tags = g_realloc(tags, nlen+1);
2568 strcpy(tags+olen, ", ");
2569 strcpy(tags+olen+2, tag);
2576 void procmsg_msginfo_update_tags(MsgInfo *msginfo, gboolean set, gint id)
2584 msginfo->tags = g_slist_remove(
2586 GINT_TO_POINTER(id));
2587 changed.data = GINT_TO_POINTER(id);
2588 changed.next = NULL;
2589 folder_item_commit_tags(msginfo->folder, msginfo, NULL, &changed);
2591 if (!g_slist_find(msginfo->tags, GINT_TO_POINTER(id))) {
2592 msginfo->tags = g_slist_append(
2594 GINT_TO_POINTER(id));
2596 changed.data = GINT_TO_POINTER(id);
2597 changed.next = NULL;
2598 folder_item_commit_tags(msginfo->folder, msginfo, &changed, NULL);
2603 void procmsg_msginfo_clear_tags(MsgInfo *msginfo)
2605 GSList *unset = msginfo->tags;
2606 msginfo->tags = NULL;
2607 folder_item_commit_tags(msginfo->folder, msginfo, NULL, unset);
2608 g_slist_free(unset);