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;
1165 cm_return_if_fail(msginfo);
1167 if (procmime_msginfo_is_encrypted(msginfo))
1168 tmpfp = procmime_get_first_encrypted_text_content(msginfo);
1170 tmpfp = procmime_get_first_text_content(msginfo);
1171 if (tmpfp == NULL) {
1172 g_warning("Can't get text part\n");
1176 prtmp = g_strdup_printf("%s%cprinttmp.%08x",
1177 get_mime_tmp_dir(), G_DIR_SEPARATOR, id++);
1179 if ((prfp = g_fopen(prtmp, "wb")) == NULL) {
1180 FILE_OP_ERROR(prtmp, "fopen");
1186 if (msginfo->date) { r = fprintf(prfp, "Date: %s\n", msginfo->date); if (r < 0) goto fpferr; }
1187 if (msginfo->from) { r = fprintf(prfp, "From: %s\n", msginfo->from); if (r < 0) goto fpferr; }
1188 if (msginfo->to) { r = fprintf(prfp, "To: %s\n", msginfo->to); if (r < 0) goto fpferr; }
1189 if (msginfo->cc) { r = fprintf(prfp, "Cc: %s\n", msginfo->cc); if (r < 0) goto fpferr; }
1190 if (msginfo->newsgroups) {
1191 r = fprintf(prfp, "Newsgroups: %s\n", msginfo->newsgroups); if (r < 0) goto fpferr;
1193 if (msginfo->subject) { r = fprintf(prfp, "Subject: %s\n", msginfo->subject); if (r < 0) goto fpferr; }
1194 if (fputc('\n', prfp) == EOF) goto fpferr;
1196 while (fgets(buf, sizeof(buf), tmpfp) != NULL) {
1197 r = fputs(buf, prfp);
1198 if (r == EOF) goto fpferr;
1204 if (cmdline && (p = strchr(cmdline, '%')) && *(p + 1) == 's' &&
1205 !strchr(p + 2, '%'))
1206 g_snprintf(buf, sizeof(buf) - 1, cmdline, prtmp);
1209 g_warning("Print command-line is invalid: '%s'\n",
1211 g_snprintf(buf, sizeof(buf) - 1, def_cmd, prtmp);
1217 if (buf[strlen(buf) - 1] != '&')
1218 strncat(buf, "&", sizeof(buf) - strlen(buf) - 1);
1219 if (system(buf) == -1)
1220 g_warning("system(%s) failed.", buf);
1223 FILE_OP_ERROR(prtmp, "fprintf/fputc/fputs");
1229 MsgInfo *procmsg_msginfo_new_ref(MsgInfo *msginfo)
1236 MsgInfo *procmsg_msginfo_new(void)
1238 MsgInfo *newmsginfo;
1240 newmsginfo = g_new0(MsgInfo, 1);
1241 newmsginfo->refcnt = 1;
1246 MsgInfo *procmsg_msginfo_copy(MsgInfo *msginfo)
1248 MsgInfo *newmsginfo;
1251 if (msginfo == NULL) return NULL;
1253 newmsginfo = g_new0(MsgInfo, 1);
1255 newmsginfo->refcnt = 1;
1257 #define MEMBCOPY(mmb) newmsginfo->mmb = msginfo->mmb
1258 #define MEMBDUP(mmb) newmsginfo->mmb = msginfo->mmb ? \
1259 g_strdup(msginfo->mmb) : NULL
1274 MEMBDUP(newsgroups);
1281 MEMBCOPY(to_folder);
1283 if (msginfo->extradata) {
1284 newmsginfo->extradata = g_new0(MsgInfoExtraData, 1);
1285 MEMBDUP(extradata->face);
1286 MEMBDUP(extradata->xface);
1287 MEMBDUP(extradata->dispositionnotificationto);
1288 MEMBDUP(extradata->returnreceiptto);
1289 MEMBDUP(extradata->partial_recv);
1290 MEMBDUP(extradata->account_server);
1291 MEMBDUP(extradata->account_login);
1292 MEMBDUP(extradata->list_post);
1293 MEMBDUP(extradata->list_subscribe);
1294 MEMBDUP(extradata->list_unsubscribe);
1295 MEMBDUP(extradata->list_help);
1296 MEMBDUP(extradata->list_archive);
1297 MEMBDUP(extradata->list_owner);
1300 refs = msginfo->references;
1301 for (refs = msginfo->references; refs != NULL; refs = refs->next) {
1302 newmsginfo->references = g_slist_prepend
1303 (newmsginfo->references, g_strdup(refs->data));
1305 newmsginfo->references = g_slist_reverse(newmsginfo->references);
1308 MEMBDUP(plaintext_file);
1313 MsgInfo *procmsg_msginfo_get_full_info_from_file(MsgInfo *msginfo, const gchar *file)
1315 MsgInfo *full_msginfo;
1317 if (msginfo == NULL) return NULL;
1319 if (!file || !is_file_exist(file)) {
1320 g_warning("procmsg_msginfo_get_full_info_from_file(): can't get message file.\n");
1324 full_msginfo = procheader_parse_file(file, msginfo->flags, TRUE, FALSE);
1325 if (!full_msginfo) return NULL;
1327 msginfo->total_size = full_msginfo->total_size;
1328 msginfo->planned_download = full_msginfo->planned_download;
1330 if (full_msginfo->extradata) {
1331 if (!msginfo->extradata)
1332 msginfo->extradata = g_new0(MsgInfoExtraData, 1);
1333 if (!msginfo->extradata->list_post)
1334 msginfo->extradata->list_post = g_strdup(full_msginfo->extradata->list_post);
1335 if (!msginfo->extradata->list_subscribe)
1336 msginfo->extradata->list_subscribe = g_strdup(full_msginfo->extradata->list_subscribe);
1337 if (!msginfo->extradata->list_unsubscribe)
1338 msginfo->extradata->list_unsubscribe = g_strdup(full_msginfo->extradata->list_unsubscribe);
1339 if (!msginfo->extradata->list_help)
1340 msginfo->extradata->list_help = g_strdup(full_msginfo->extradata->list_help);
1341 if (!msginfo->extradata->list_archive)
1342 msginfo->extradata->list_archive= g_strdup(full_msginfo->extradata->list_archive);
1343 if (!msginfo->extradata->list_owner)
1344 msginfo->extradata->list_owner = g_strdup(full_msginfo->extradata->list_owner);
1345 if (!msginfo->extradata->xface)
1346 msginfo->extradata->xface = g_strdup(full_msginfo->extradata->xface);
1347 if (!msginfo->extradata->face)
1348 msginfo->extradata->face = g_strdup(full_msginfo->extradata->face);
1349 if (!msginfo->extradata->dispositionnotificationto)
1350 msginfo->extradata->dispositionnotificationto =
1351 g_strdup(full_msginfo->extradata->dispositionnotificationto);
1352 if (!msginfo->extradata->returnreceiptto)
1353 msginfo->extradata->returnreceiptto = g_strdup
1354 (full_msginfo->extradata->returnreceiptto);
1355 if (!msginfo->extradata->partial_recv && full_msginfo->extradata->partial_recv)
1356 msginfo->extradata->partial_recv = g_strdup
1357 (full_msginfo->extradata->partial_recv);
1358 if (!msginfo->extradata->account_server && full_msginfo->extradata->account_server)
1359 msginfo->extradata->account_server = g_strdup
1360 (full_msginfo->extradata->account_server);
1361 if (!msginfo->extradata->account_login && full_msginfo->extradata->account_login)
1362 msginfo->extradata->account_login = g_strdup
1363 (full_msginfo->extradata->account_login);
1365 procmsg_msginfo_free(full_msginfo);
1367 return procmsg_msginfo_new_ref(msginfo);
1370 MsgInfo *procmsg_msginfo_get_full_info(MsgInfo *msginfo)
1372 MsgInfo *full_msginfo;
1375 if (msginfo == NULL) return NULL;
1377 file = procmsg_get_message_file_path(msginfo);
1378 if (!file || !is_file_exist(file)) {
1380 file = procmsg_get_message_file(msginfo);
1382 if (!file || !is_file_exist(file)) {
1383 g_warning("procmsg_msginfo_get_full_info(): can't get message file.\n");
1387 full_msginfo = procmsg_msginfo_get_full_info_from_file(msginfo, file);
1389 return full_msginfo;
1392 void procmsg_msginfo_free(MsgInfo *msginfo)
1394 if (msginfo == NULL) return;
1397 if (msginfo->refcnt > 0)
1400 if (msginfo->to_folder) {
1401 msginfo->to_folder->op_count--;
1402 folder_item_update(msginfo->to_folder, F_ITEM_UPDATE_MSGCNT);
1405 g_free(msginfo->fromspace);
1407 g_free(msginfo->fromname);
1409 g_free(msginfo->date);
1410 g_free(msginfo->from);
1411 g_free(msginfo->to);
1412 g_free(msginfo->cc);
1413 g_free(msginfo->newsgroups);
1414 g_free(msginfo->subject);
1415 g_free(msginfo->msgid);
1416 g_free(msginfo->inreplyto);
1417 g_free(msginfo->xref);
1419 if (msginfo->extradata) {
1420 g_free(msginfo->extradata->returnreceiptto);
1421 g_free(msginfo->extradata->dispositionnotificationto);
1422 g_free(msginfo->extradata->xface);
1423 g_free(msginfo->extradata->face);
1424 g_free(msginfo->extradata->list_post);
1425 g_free(msginfo->extradata->list_subscribe);
1426 g_free(msginfo->extradata->list_unsubscribe);
1427 g_free(msginfo->extradata->list_help);
1428 g_free(msginfo->extradata->list_archive);
1429 g_free(msginfo->extradata->list_owner);
1430 g_free(msginfo->extradata->partial_recv);
1431 g_free(msginfo->extradata->account_server);
1432 g_free(msginfo->extradata->account_login);
1433 g_free(msginfo->extradata);
1435 slist_free_strings_full(msginfo->references);
1436 g_slist_free(msginfo->tags);
1438 g_free(msginfo->plaintext_file);
1443 guint procmsg_msginfo_memusage(MsgInfo *msginfo)
1448 memusage += sizeof(MsgInfo);
1449 if (msginfo->fromname)
1450 memusage += strlen(msginfo->fromname);
1452 memusage += strlen(msginfo->date);
1454 memusage += strlen(msginfo->from);
1456 memusage += strlen(msginfo->to);
1458 memusage += strlen(msginfo->cc);
1459 if (msginfo->newsgroups)
1460 memusage += strlen(msginfo->newsgroups);
1461 if (msginfo->subject)
1462 memusage += strlen(msginfo->subject);
1464 memusage += strlen(msginfo->msgid);
1465 if (msginfo->inreplyto)
1466 memusage += strlen(msginfo->inreplyto);
1468 for (tmp = msginfo->references; tmp; tmp=tmp->next) {
1469 gchar *r = (gchar *)tmp->data;
1470 memusage += r?strlen(r):0 + sizeof(GSList);
1472 if (msginfo->fromspace)
1473 memusage += strlen(msginfo->fromspace);
1475 for (tmp = msginfo->tags; tmp; tmp=tmp->next) {
1476 memusage += sizeof(GSList);
1478 if (msginfo->extradata) {
1479 memusage += sizeof(MsgInfoExtraData);
1480 if (msginfo->extradata->xface)
1481 memusage += strlen(msginfo->extradata->xface);
1482 if (msginfo->extradata->face)
1483 memusage += strlen(msginfo->extradata->face);
1484 if (msginfo->extradata->dispositionnotificationto)
1485 memusage += strlen(msginfo->extradata->dispositionnotificationto);
1486 if (msginfo->extradata->returnreceiptto)
1487 memusage += strlen(msginfo->extradata->returnreceiptto);
1489 if (msginfo->extradata->partial_recv)
1490 memusage += strlen(msginfo->extradata->partial_recv);
1491 if (msginfo->extradata->account_server)
1492 memusage += strlen(msginfo->extradata->account_server);
1493 if (msginfo->extradata->account_login)
1494 memusage += strlen(msginfo->extradata->account_login);
1496 if (msginfo->extradata->list_post)
1497 memusage += strlen(msginfo->extradata->list_post);
1498 if (msginfo->extradata->list_subscribe)
1499 memusage += strlen(msginfo->extradata->list_subscribe);
1500 if (msginfo->extradata->list_unsubscribe)
1501 memusage += strlen(msginfo->extradata->list_unsubscribe);
1502 if (msginfo->extradata->list_help)
1503 memusage += strlen(msginfo->extradata->list_help);
1504 if (msginfo->extradata->list_archive)
1505 memusage += strlen(msginfo->extradata->list_archive);
1506 if (msginfo->extradata->list_owner)
1507 memusage += strlen(msginfo->extradata->list_owner);
1512 static gint procmsg_send_message_queue_full(const gchar *file, gboolean keep_session, gchar **errstr,
1513 FolderItem *queue, gint msgnum, gboolean *queued_removed)
1515 static HeaderEntry qentry[] = {{"S:", NULL, FALSE},
1516 {"SSV:", NULL, FALSE},
1517 {"R:", NULL, FALSE},
1518 {"NG:", NULL, FALSE},
1519 {"MAID:", NULL, FALSE},
1520 {"NAID:", NULL, FALSE},
1521 {"SCF:", NULL, FALSE},
1522 {"RMID:", NULL, FALSE},
1523 {"FMID:", NULL, FALSE},
1524 {"X-Claws-Privacy-System:", NULL, FALSE},
1525 {"X-Claws-Encrypt:", NULL, FALSE},
1526 {"X-Claws-Encrypt-Data:", NULL, FALSE},
1527 {"X-Claws-End-Special-Headers:", NULL, FALSE},
1528 {"X-Sylpheed-Privacy-System:", NULL, FALSE},
1529 {"X-Sylpheed-Encrypt:", NULL, FALSE},
1530 {"X-Sylpheed-Encrypt-Data:", NULL, FALSE},
1531 {"X-Sylpheed-End-Special-Headers:", NULL, FALSE},
1532 {NULL, NULL, FALSE}};
1535 gint mailval = 0, newsval = 0;
1537 gchar *smtpserver = NULL;
1538 GSList *to_list = NULL;
1539 GSList *newsgroup_list = NULL;
1540 gchar *savecopyfolder = NULL;
1541 gchar *replymessageid = NULL;
1542 gchar *fwdmessageid = NULL;
1543 gchar *privacy_system = NULL;
1544 gboolean encrypt = FALSE;
1545 gchar *encrypt_data = NULL;
1546 gchar buf[BUFFSIZE];
1548 PrefsAccount *mailac = NULL, *newsac = NULL;
1549 gboolean save_clear_text = TRUE;
1550 gchar *tmp_enc_file = NULL;
1552 cm_return_val_if_fail(file != NULL, -1);
1554 if ((fp = g_fopen(file, "rb")) == NULL) {
1555 FILE_OP_ERROR(file, "fopen");
1557 if (*errstr) g_free(*errstr);
1558 *errstr = g_strdup_printf(_("Couldn't open file %s."), file);
1563 while ((hnum = procheader_get_one_field(buf, sizeof(buf), fp, qentry))
1565 gchar *p = buf + strlen(qentry[hnum].name);
1573 if (smtpserver == NULL)
1574 smtpserver = g_strdup(p);
1577 to_list = address_list_append(to_list, p);
1580 newsgroup_list = newsgroup_list_append(newsgroup_list, p);
1582 case Q_MAIL_ACCOUNT_ID:
1583 mailac = account_find_from_id(atoi(p));
1585 case Q_NEWS_ACCOUNT_ID:
1586 newsac = account_find_from_id(atoi(p));
1588 case Q_SAVE_COPY_FOLDER:
1589 if (savecopyfolder == NULL)
1590 savecopyfolder = g_strdup(p);
1592 case Q_REPLY_MESSAGE_ID:
1593 if (replymessageid == NULL)
1594 replymessageid = g_strdup(p);
1596 case Q_FWD_MESSAGE_ID:
1597 if (fwdmessageid == NULL)
1598 fwdmessageid = g_strdup(p);
1600 case Q_PRIVACY_SYSTEM:
1601 case Q_PRIVACY_SYSTEM_OLD:
1602 if (privacy_system == NULL)
1603 privacy_system = g_strdup(p);
1610 case Q_ENCRYPT_DATA:
1611 case Q_ENCRYPT_DATA_OLD:
1612 if (encrypt_data == NULL)
1613 encrypt_data = g_strdup(p);
1616 case Q_CLAWS_HDRS_OLD:
1617 /* end of special headers reached */
1618 goto send_mail; /* can't "break;break;" */
1622 filepos = ftell(fp);
1627 if (mailac && mailac->save_encrypted_as_clear_text
1628 && !mailac->encrypt_to_self)
1629 save_clear_text = TRUE;
1631 save_clear_text = FALSE;
1636 mimeinfo = procmime_scan_queue_file(file);
1637 if (!privacy_encrypt(privacy_system, mimeinfo, encrypt_data)
1638 || (fp = my_tmpfile()) == NULL
1639 || procmime_write_mimeinfo(mimeinfo, fp) < 0) {
1642 procmime_mimeinfo_free_all(mimeinfo);
1645 slist_free_strings_full(to_list);
1646 slist_free_strings_full(newsgroup_list);
1647 g_free(savecopyfolder);
1648 g_free(replymessageid);
1649 g_free(fwdmessageid);
1650 g_free(privacy_system);
1651 g_free(encrypt_data);
1653 if (*errstr) g_free(*errstr);
1654 *errstr = g_strdup_printf(_("Couldn't encrypt the email: %s"),
1655 privacy_get_error());
1661 if (!save_clear_text) {
1662 gchar *content = NULL;
1663 FILE *tmpfp = get_tmpfile_in_dir(get_mime_tmp_dir(), &tmp_enc_file);
1667 content = file_read_stream_to_str(fp);
1670 str_write_to_file(content, tmp_enc_file);
1673 g_warning("couldn't get tempfile\n");
1677 procmime_mimeinfo_free_all(mimeinfo);
1683 debug_print("Sending message by mail\n");
1686 if (*errstr) g_free(*errstr);
1687 *errstr = g_strdup_printf(_("Queued message header is broken."));
1690 } else if (mailac && mailac->use_mail_command &&
1691 mailac->mail_command && (* mailac->mail_command)) {
1692 mailval = send_message_local(mailac->mail_command, fp);
1695 mailac = account_find_from_smtp_server(from, smtpserver);
1697 g_warning("Account not found. "
1698 "Using current account...\n");
1699 mailac = cur_account;
1704 mailval = send_message_smtp_full(mailac, to_list, fp, keep_session);
1705 if (mailval == -1 && errstr) {
1706 if (*errstr) g_free(*errstr);
1707 *errstr = g_strdup_printf(_("An error happened during SMTP session."));
1710 PrefsAccount tmp_ac;
1712 g_warning("Account not found.\n");
1714 memset(&tmp_ac, 0, sizeof(PrefsAccount));
1715 tmp_ac.address = from;
1716 tmp_ac.smtp_server = smtpserver;
1717 tmp_ac.smtpport = SMTP_PORT;
1718 mailval = send_message_smtp(&tmp_ac, to_list, fp);
1719 if (mailval == -1 && errstr) {
1720 if (*errstr) g_free(*errstr);
1721 *errstr = g_strdup_printf(_("No specific account has been found to "
1722 "send, and an error happened during SMTP session."));
1726 } else if (!to_list && !newsgroup_list) {
1728 if (*errstr) g_free(*errstr);
1729 *errstr = g_strdup(_("Couldn't determine sending informations. "
1730 "Maybe the email hasn't been generated by Claws Mail."));
1735 fseek(fp, filepos, SEEK_SET);
1736 if (newsgroup_list && newsac && (mailval == 0)) {
1741 /* write to temporary file */
1742 tmp = g_strdup_printf("%s%cnntp%p", get_tmp_dir(),
1743 G_DIR_SEPARATOR, file);
1744 if ((tmpfp = g_fopen(tmp, "wb")) == NULL) {
1745 FILE_OP_ERROR(tmp, "fopen");
1747 alertpanel_error(_("Couldn't create temporary file for news sending."));
1749 if (change_file_mode_rw(tmpfp, tmp) < 0) {
1750 FILE_OP_ERROR(tmp, "chmod");
1751 g_warning("can't change file mode\n");
1754 while ((newsval == 0) && fgets(buf, sizeof(buf), fp) != NULL) {
1755 if (fputs(buf, tmpfp) == EOF) {
1756 FILE_OP_ERROR(tmp, "fputs");
1759 if (*errstr) g_free(*errstr);
1760 *errstr = g_strdup_printf(_("Error when writing temporary file for news sending."));
1767 debug_print("Sending message by news\n");
1769 folder = FOLDER(newsac->folder);
1771 newsval = news_post(folder, tmp);
1772 if (newsval < 0 && errstr) {
1773 if (*errstr) g_free(*errstr);
1774 *errstr = g_strdup_printf(_("Error occurred while posting the message to %s."),
1775 newsac->nntp_server);
1785 /* update session statistics */
1786 if (mailval == 0 && newsval == 0) {
1787 /* update session stats */
1789 session_stats.replied++;
1790 else if (fwdmessageid)
1791 session_stats.forwarded++;
1793 session_stats.sent++;
1796 /* save message to outbox */
1797 if (mailval == 0 && newsval == 0 && savecopyfolder) {
1800 debug_print("saving sent message...\n");
1802 outbox = folder_find_item_from_identifier(savecopyfolder);
1804 outbox = folder_get_default_outbox();
1806 if (save_clear_text || tmp_enc_file == NULL) {
1807 gboolean saved = FALSE;
1808 *queued_removed = FALSE;
1809 if (queue && msgnum > 0) {
1810 MsgInfo *queued_mail = folder_item_get_msginfo(queue, msgnum);
1811 if (folder_item_move_msg(outbox, queued_mail) >= 0) {
1812 debug_print("moved queued mail %d to sent folder\n", msgnum);
1814 *queued_removed = TRUE;
1815 } else if (folder_item_copy_msg(outbox, queued_mail) >= 0) {
1816 debug_print("copied queued mail %d to sent folder\n", msgnum);
1819 procmsg_msginfo_free(queued_mail);
1822 debug_print("resaving clear text queued mail to sent folder\n");
1823 procmsg_save_to_outbox(outbox, file, TRUE);
1826 debug_print("saving encrpyted queued mail to sent folder\n");
1827 procmsg_save_to_outbox(outbox, tmp_enc_file, FALSE);
1831 if (tmp_enc_file != NULL) {
1832 claws_unlink(tmp_enc_file);
1834 tmp_enc_file = NULL;
1837 if (replymessageid != NULL || fwdmessageid != NULL) {
1841 if (replymessageid != NULL)
1842 tokens = g_strsplit(replymessageid, "\t", 0);
1844 tokens = g_strsplit(fwdmessageid, "\t", 0);
1845 item = folder_find_item_from_identifier(tokens[0]);
1847 /* check if queued message has valid folder and message id */
1848 if (item != NULL && tokens[2] != NULL) {
1851 msginfo = folder_item_get_msginfo(item, atoi(tokens[1]));
1853 /* check if referring message exists and has a message id */
1854 if ((msginfo != NULL) &&
1855 (msginfo->msgid != NULL) &&
1856 (strcmp(msginfo->msgid, tokens[2]) != 0)) {
1857 procmsg_msginfo_free(msginfo);
1861 if (msginfo == NULL) {
1862 msginfo = folder_item_get_msginfo_by_msgid(item, tokens[2]);
1865 if (msginfo != NULL) {
1866 if (replymessageid != NULL) {
1867 MsgPermFlags to_unset = 0;
1869 if (prefs_common.mark_as_read_on_new_window)
1870 to_unset = (MSG_NEW|MSG_UNREAD);
1872 procmsg_msginfo_unset_flags(msginfo, to_unset, 0);
1873 procmsg_msginfo_set_flags(msginfo, MSG_REPLIED, 0);
1875 procmsg_msginfo_set_flags(msginfo, MSG_FORWARDED, 0);
1877 procmsg_msginfo_free(msginfo);
1885 slist_free_strings_full(to_list);
1886 slist_free_strings_full(newsgroup_list);
1887 g_free(savecopyfolder);
1888 g_free(replymessageid);
1889 g_free(fwdmessageid);
1890 g_free(privacy_system);
1891 g_free(encrypt_data);
1893 return (newsval != 0 ? newsval : mailval);
1896 gint procmsg_send_message_queue(const gchar *file, gchar **errstr, FolderItem *queue, gint msgnum, gboolean *queued_removed)
1898 gint result = procmsg_send_message_queue_full(file, FALSE, errstr, queue, msgnum, queued_removed);
1899 toolbar_main_set_sensitive(mainwindow_get_mainwindow());
1903 gint procmsg_send_message_queue_with_lock(const gchar *file, gchar **errstr, FolderItem *queue, gint msgnum, gboolean *queued_removed)
1906 if (procmsg_queue_lock(errstr)) {
1907 val = procmsg_send_message_queue(file, errstr, queue, msgnum, queued_removed);
1908 procmsg_queue_unlock();
1914 static void update_folder_msg_counts(FolderItem *item, MsgInfo *msginfo, MsgPermFlags old_flags)
1916 MsgPermFlags new_flags = msginfo->flags.perm_flags;
1919 if (!(old_flags & MSG_NEW) && (new_flags & MSG_NEW)) {
1923 if ((old_flags & MSG_NEW) && !(new_flags & MSG_NEW)) {
1928 if (!(old_flags & MSG_UNREAD) && (new_flags & MSG_UNREAD)) {
1929 item->unread_msgs++;
1930 if (procmsg_msg_has_marked_parent(msginfo))
1931 item->unreadmarked_msgs++;
1934 if ((old_flags & MSG_UNREAD) && !(new_flags & MSG_UNREAD)) {
1935 item->unread_msgs--;
1936 if (procmsg_msg_has_marked_parent(msginfo))
1937 item->unreadmarked_msgs--;
1941 if (!(old_flags & MSG_MARKED) && (new_flags & MSG_MARKED)) {
1942 procmsg_update_unread_children(msginfo, TRUE);
1943 item->marked_msgs++;
1946 if ((old_flags & MSG_MARKED) && !(new_flags & MSG_MARKED)) {
1947 procmsg_update_unread_children(msginfo, FALSE);
1948 item->marked_msgs--;
1951 if (!(old_flags & MSG_REPLIED) && (new_flags & MSG_REPLIED)) {
1952 item->replied_msgs++;
1955 if ((old_flags & MSG_REPLIED) && !(new_flags & MSG_REPLIED)) {
1956 item->replied_msgs--;
1959 if (!(old_flags & MSG_FORWARDED) && (new_flags & MSG_FORWARDED)) {
1960 item->forwarded_msgs++;
1963 if ((old_flags & MSG_FORWARDED) && !(new_flags & MSG_FORWARDED)) {
1964 item->forwarded_msgs--;
1967 if (!(old_flags & MSG_LOCKED) && (new_flags & MSG_LOCKED)) {
1968 item->locked_msgs++;
1971 if ((old_flags & MSG_LOCKED) && !(new_flags & MSG_LOCKED)) {
1972 item->locked_msgs--;
1975 if ((old_flags & MSG_IGNORE_THREAD) && !(new_flags & MSG_IGNORE_THREAD)) {
1976 item->ignored_msgs--;
1979 if (!(old_flags & MSG_IGNORE_THREAD) && (new_flags & MSG_IGNORE_THREAD)) {
1980 item->ignored_msgs++;
1983 if ((old_flags & MSG_WATCH_THREAD) && !(new_flags & MSG_WATCH_THREAD)) {
1984 item->watched_msgs--;
1987 if (!(old_flags & MSG_WATCH_THREAD) && (new_flags & MSG_WATCH_THREAD)) {
1988 item->watched_msgs++;
1992 void procmsg_msginfo_set_flags(MsgInfo *msginfo, MsgPermFlags perm_flags, MsgTmpFlags tmp_flags)
1995 MsgInfoUpdate msginfo_update;
1996 MsgPermFlags perm_flags_new, perm_flags_old;
1997 MsgTmpFlags tmp_flags_old;
1999 cm_return_if_fail(msginfo != NULL);
2000 item = msginfo->folder;
2001 cm_return_if_fail(item != NULL);
2003 debug_print("Setting flags for message %d in folder %s\n", msginfo->msgnum, item->path);
2005 /* Perm Flags handling */
2006 perm_flags_old = msginfo->flags.perm_flags;
2007 perm_flags_new = msginfo->flags.perm_flags | perm_flags;
2008 if ((perm_flags & MSG_IGNORE_THREAD) || (perm_flags_old & MSG_IGNORE_THREAD)) {
2009 perm_flags_new &= ~(MSG_NEW | MSG_UNREAD);
2011 if ((perm_flags & MSG_WATCH_THREAD) || (perm_flags_old & MSG_WATCH_THREAD)) {
2012 perm_flags_new &= ~(MSG_IGNORE_THREAD);
2015 if (perm_flags_old != perm_flags_new) {
2016 folder_item_change_msg_flags(msginfo->folder, msginfo, perm_flags_new);
2018 update_folder_msg_counts(item, msginfo, perm_flags_old);
2019 summary_update_unread(mainwindow_get_mainwindow()->summaryview, NULL);
2022 /* Tmp flags handling */
2023 tmp_flags_old = msginfo->flags.tmp_flags;
2024 msginfo->flags.tmp_flags |= tmp_flags;
2026 /* update notification */
2027 if ((perm_flags_old != perm_flags_new) || (tmp_flags_old != msginfo->flags.tmp_flags)) {
2028 msginfo_update.msginfo = msginfo;
2029 msginfo_update.flags = MSGINFO_UPDATE_FLAGS;
2030 hooks_invoke(MSGINFO_UPDATE_HOOKLIST, &msginfo_update);
2031 folder_item_update(msginfo->folder, F_ITEM_UPDATE_MSGCNT);
2035 void procmsg_msginfo_unset_flags(MsgInfo *msginfo, MsgPermFlags perm_flags, MsgTmpFlags tmp_flags)
2038 MsgInfoUpdate msginfo_update;
2039 MsgPermFlags perm_flags_new, perm_flags_old;
2040 MsgTmpFlags tmp_flags_old;
2042 cm_return_if_fail(msginfo != NULL);
2043 item = msginfo->folder;
2044 cm_return_if_fail(item != NULL);
2046 debug_print("Unsetting flags for message %d in folder %s\n", msginfo->msgnum, item->path);
2048 /* Perm Flags handling */
2049 perm_flags_old = msginfo->flags.perm_flags;
2050 perm_flags_new = msginfo->flags.perm_flags & ~perm_flags;
2052 if (perm_flags_old != perm_flags_new) {
2053 folder_item_change_msg_flags(msginfo->folder, msginfo, perm_flags_new);
2055 update_folder_msg_counts(item, msginfo, perm_flags_old);
2058 /* Tmp flags hanlding */
2059 tmp_flags_old = msginfo->flags.tmp_flags;
2060 msginfo->flags.tmp_flags &= ~tmp_flags;
2062 /* update notification */
2063 if ((perm_flags_old != perm_flags_new) || (tmp_flags_old != msginfo->flags.tmp_flags)) {
2064 msginfo_update.msginfo = msginfo;
2065 msginfo_update.flags = MSGINFO_UPDATE_FLAGS;
2066 hooks_invoke(MSGINFO_UPDATE_HOOKLIST, &msginfo_update);
2067 folder_item_update(msginfo->folder, F_ITEM_UPDATE_MSGCNT);
2071 void procmsg_msginfo_change_flags(MsgInfo *msginfo,
2072 MsgPermFlags add_perm_flags, MsgTmpFlags add_tmp_flags,
2073 MsgPermFlags rem_perm_flags, MsgTmpFlags rem_tmp_flags)
2076 MsgInfoUpdate msginfo_update;
2077 MsgPermFlags perm_flags_new, perm_flags_old;
2078 MsgTmpFlags tmp_flags_old;
2080 cm_return_if_fail(msginfo != NULL);
2081 item = msginfo->folder;
2082 cm_return_if_fail(item != NULL);
2084 debug_print("Changing flags for message %d in folder %s\n", msginfo->msgnum, item->path);
2086 /* Perm Flags handling */
2087 perm_flags_old = msginfo->flags.perm_flags;
2088 perm_flags_new = (msginfo->flags.perm_flags & ~rem_perm_flags) | add_perm_flags;
2089 if ((add_perm_flags & MSG_IGNORE_THREAD) || (perm_flags_old & MSG_IGNORE_THREAD)) {
2090 perm_flags_new &= ~(MSG_NEW | MSG_UNREAD);
2092 if ((add_perm_flags & MSG_WATCH_THREAD) || (perm_flags_old & MSG_WATCH_THREAD)) {
2093 perm_flags_new &= ~(MSG_IGNORE_THREAD);
2096 if (perm_flags_old != perm_flags_new) {
2097 folder_item_change_msg_flags(msginfo->folder, msginfo, perm_flags_new);
2099 update_folder_msg_counts(item, msginfo, perm_flags_old);
2103 /* Tmp flags handling */
2104 tmp_flags_old = msginfo->flags.tmp_flags;
2105 msginfo->flags.tmp_flags &= ~rem_tmp_flags;
2106 msginfo->flags.tmp_flags |= add_tmp_flags;
2108 /* update notification */
2109 if ((perm_flags_old != perm_flags_new) || (tmp_flags_old != msginfo->flags.tmp_flags)) {
2110 msginfo_update.msginfo = msginfo;
2111 msginfo_update.flags = MSGINFO_UPDATE_FLAGS;
2112 hooks_invoke(MSGINFO_UPDATE_HOOKLIST, &msginfo_update);
2113 folder_item_update(msginfo->folder, F_ITEM_UPDATE_MSGCNT);
2118 *\brief check for flags (e.g. mark) in prior msgs of current thread
2120 *\param info Current message
2121 *\param perm_flags Flags to be checked
2122 *\param parentmsgs Hash of prior msgs to avoid loops
2124 *\return gboolean TRUE if perm_flags are found
2126 static gboolean procmsg_msg_has_flagged_parent_real(MsgInfo *info,
2127 MsgPermFlags perm_flags, GHashTable *parentmsgs)
2131 cm_return_val_if_fail(info != NULL, FALSE);
2133 if (info != NULL && info->folder != NULL && info->inreplyto != NULL) {
2134 tmp = folder_item_get_msginfo_by_msgid(info->folder,
2136 if (tmp && (tmp->flags.perm_flags & perm_flags)) {
2137 procmsg_msginfo_free(tmp);
2139 } else if (tmp != NULL) {
2142 if (g_hash_table_lookup(parentmsgs, info)) {
2143 debug_print("loop detected: %d\n",
2147 g_hash_table_insert(parentmsgs, info, "1");
2148 result = procmsg_msg_has_flagged_parent_real(
2149 tmp, perm_flags, parentmsgs);
2151 procmsg_msginfo_free(tmp);
2161 *\brief Callback for cleaning up hash of parentmsgs
2163 static gboolean parentmsgs_hash_remove(gpointer key,
2171 *\brief Set up list of parentmsgs
2172 * See procmsg_msg_has_flagged_parent_real()
2174 gboolean procmsg_msg_has_flagged_parent(MsgInfo *info, MsgPermFlags perm_flags)
2177 static GHashTable *parentmsgs = NULL;
2179 if (parentmsgs == NULL)
2180 parentmsgs = g_hash_table_new(NULL, NULL);
2182 result = procmsg_msg_has_flagged_parent_real(info, perm_flags, parentmsgs);
2183 g_hash_table_foreach_remove(parentmsgs, parentmsgs_hash_remove, NULL);
2189 *\brief Check if msgs prior in thread are marked
2190 * See procmsg_msg_has_flagged_parent_real()
2192 gboolean procmsg_msg_has_marked_parent(MsgInfo *info)
2194 return procmsg_msg_has_flagged_parent(info, MSG_MARKED);
2198 static GSList *procmsg_find_children_func(MsgInfo *info,
2199 GSList *children, GSList *all)
2203 cm_return_val_if_fail(info!=NULL, children);
2204 if (info->msgid == NULL)
2207 for (cur = all; cur != NULL; cur = g_slist_next(cur)) {
2208 MsgInfo *tmp = (MsgInfo *)cur->data;
2209 if (tmp->inreplyto && !strcmp(tmp->inreplyto, info->msgid)) {
2210 /* Check if message is already in the list */
2211 if ((children == NULL) ||
2212 (g_slist_index(children, tmp) == -1)) {
2213 children = g_slist_prepend(children,
2214 procmsg_msginfo_new_ref(tmp));
2215 children = procmsg_find_children_func(tmp,
2224 static GSList *procmsg_find_children (MsgInfo *info)
2229 cm_return_val_if_fail(info!=NULL, NULL);
2230 all = folder_item_get_msg_list(info->folder);
2231 children = procmsg_find_children_func(info, NULL, all);
2232 if (children != NULL) {
2233 for (cur = all; cur != NULL; cur = g_slist_next(cur)) {
2234 /* this will not free the used pointers
2235 created with procmsg_msginfo_new_ref */
2236 procmsg_msginfo_free((MsgInfo *)cur->data);
2244 static void procmsg_update_unread_children(MsgInfo *info, gboolean newly_marked)
2246 GSList *children = procmsg_find_children(info);
2248 for (cur = children; cur != NULL; cur = g_slist_next(cur)) {
2249 MsgInfo *tmp = (MsgInfo *)cur->data;
2250 if(MSG_IS_UNREAD(tmp->flags) && !MSG_IS_IGNORE_THREAD(tmp->flags)) {
2252 info->folder->unreadmarked_msgs++;
2254 info->folder->unreadmarked_msgs--;
2255 folder_item_update(info->folder, F_ITEM_UPDATE_MSGCNT);
2257 procmsg_msginfo_free(tmp);
2259 g_slist_free(children);
2263 * Set the destination folder for a copy or move operation
2265 * \param msginfo The message which's destination folder is changed
2266 * \param to_folder The destination folder for the operation
2268 void procmsg_msginfo_set_to_folder(MsgInfo *msginfo, FolderItem *to_folder)
2270 if(msginfo->to_folder != NULL) {
2271 msginfo->to_folder->op_count--;
2272 folder_item_update(msginfo->to_folder, F_ITEM_UPDATE_MSGCNT);
2274 msginfo->to_folder = to_folder;
2275 if(to_folder != NULL) {
2276 to_folder->op_count++;
2277 folder_item_update(msginfo->to_folder, F_ITEM_UPDATE_MSGCNT);
2282 * Apply filtering actions to the msginfo
2284 * \param msginfo The MsgInfo describing the message that should be filtered
2285 * \return TRUE if the message was moved and MsgInfo is now invalid,
2288 static gboolean procmsg_msginfo_filter(MsgInfo *msginfo, PrefsAccount* ac_prefs)
2290 MailFilteringData mail_filtering_data;
2292 mail_filtering_data.msginfo = msginfo;
2293 mail_filtering_data.msglist = NULL;
2294 mail_filtering_data.filtered = NULL;
2295 mail_filtering_data.unfiltered = NULL;
2296 mail_filtering_data.account = ac_prefs;
2298 if (!ac_prefs || ac_prefs->filterhook_on_recv)
2299 if (hooks_invoke(MAIL_FILTERING_HOOKLIST, &mail_filtering_data))
2302 /* filter if enabled in prefs or move to inbox if not */
2303 if((filtering_rules != NULL) &&
2304 filter_message_by_msginfo(filtering_rules, msginfo, ac_prefs,
2305 FILTERING_INCORPORATION, NULL)) {
2312 void procmsg_msglist_filter(GSList *list, PrefsAccount *ac,
2313 GSList **filtered, GSList **unfiltered,
2316 GSList *cur, *to_do = NULL;
2317 gint total = 0, curnum = 0;
2318 MailFilteringData mail_filtering_data;
2320 cm_return_if_fail(filtered != NULL);
2321 cm_return_if_fail(unfiltered != NULL);
2329 total = g_slist_length(list);
2333 *unfiltered = g_slist_copy(list);
2337 statusbar_print_all(_("Filtering messages...\n"));
2339 mail_filtering_data.msginfo = NULL;
2340 mail_filtering_data.msglist = list;
2341 mail_filtering_data.filtered = NULL;
2342 mail_filtering_data.unfiltered = NULL;
2343 mail_filtering_data.account = ac;
2345 if (!ac || ac->filterhook_on_recv)
2346 hooks_invoke(MAIL_LISTFILTERING_HOOKLIST, &mail_filtering_data);
2348 if (mail_filtering_data.filtered == NULL &&
2349 mail_filtering_data.unfiltered == NULL) {
2350 /* nothing happened */
2351 debug_print(MAIL_LISTFILTERING_HOOKLIST " did nothing. filtering whole list normally.\n");
2354 if (mail_filtering_data.filtered != NULL) {
2355 /* keep track of what's been filtered by the hooks */
2356 debug_print(MAIL_LISTFILTERING_HOOKLIST " filtered some stuff. total %d filtered %d unfilt %d.\n",
2357 g_slist_length(list),
2358 g_slist_length(mail_filtering_data.filtered),
2359 g_slist_length(mail_filtering_data.unfiltered));
2361 *filtered = g_slist_copy(mail_filtering_data.filtered);
2363 if (mail_filtering_data.unfiltered != NULL) {
2364 /* what the hooks didn't handle will go in filtered or
2365 * unfiltered in the next loop */
2366 debug_print(MAIL_LISTFILTERING_HOOKLIST " left unfiltered stuff. total %d filtered %d unfilt %d.\n",
2367 g_slist_length(list),
2368 g_slist_length(mail_filtering_data.filtered),
2369 g_slist_length(mail_filtering_data.unfiltered));
2370 to_do = mail_filtering_data.unfiltered;
2373 for (cur = to_do; cur; cur = cur->next) {
2374 MsgInfo *info = (MsgInfo *)cur->data;
2375 if (procmsg_msginfo_filter(info, ac))
2376 *filtered = g_slist_prepend(*filtered, info);
2378 *unfiltered = g_slist_prepend(*unfiltered, info);
2379 statusbar_progress_all(curnum++, total, prefs_common.statusbar_update_step);
2382 g_slist_free(mail_filtering_data.filtered);
2383 g_slist_free(mail_filtering_data.unfiltered);
2385 *filtered = g_slist_reverse(*filtered);
2386 *unfiltered = g_slist_reverse(*unfiltered);
2388 statusbar_progress_all(0,0,0);
2389 statusbar_pop_all();
2392 MsgInfo *procmsg_msginfo_new_from_mimeinfo(MsgInfo *src_msginfo, MimeInfo *mimeinfo)
2394 MsgInfo *tmp_msginfo = NULL;
2395 MsgFlags flags = {0, 0};
2396 gchar *tmpfile = get_tmp_file();
2397 FILE *fp = g_fopen(tmpfile, "wb");
2399 if (!mimeinfo || mimeinfo->type != MIMETYPE_MESSAGE ||
2400 g_ascii_strcasecmp(mimeinfo->subtype, "rfc822")) {
2401 g_warning("procmsg_msginfo_new_from_mimeinfo(): unsuitable mimeinfo");
2408 if (fp && procmime_write_mimeinfo(mimeinfo, fp) >= 0) {
2411 tmp_msginfo = procheader_parse_file(
2418 if (tmp_msginfo != NULL) {
2420 tmp_msginfo->folder = src_msginfo->folder;
2421 tmp_msginfo->plaintext_file = g_strdup(tmpfile);
2423 g_warning("procmsg_msginfo_new_from_mimeinfo(): Can't generate new msginfo");
2431 static GSList *spam_learners = NULL;
2433 void procmsg_register_spam_learner (int (*learn_func)(MsgInfo *info, GSList *list, gboolean spam))
2435 if (!g_slist_find(spam_learners, learn_func))
2436 spam_learners = g_slist_append(spam_learners, learn_func);
2437 if (mainwindow_get_mainwindow()) {
2438 main_window_set_menu_sensitive(mainwindow_get_mainwindow());
2439 summary_set_menu_sensitive(
2440 mainwindow_get_mainwindow()->summaryview);
2441 toolbar_main_set_sensitive(mainwindow_get_mainwindow());
2445 void procmsg_unregister_spam_learner (int (*learn_func)(MsgInfo *info, GSList *list, gboolean spam))
2447 spam_learners = g_slist_remove(spam_learners, learn_func);
2448 if (mainwindow_get_mainwindow()) {
2449 main_window_set_menu_sensitive(mainwindow_get_mainwindow());
2450 summary_set_menu_sensitive(
2451 mainwindow_get_mainwindow()->summaryview);
2452 toolbar_main_set_sensitive(mainwindow_get_mainwindow());
2456 gboolean procmsg_spam_can_learn(void)
2458 return g_slist_length(spam_learners) > 0;
2461 int procmsg_spam_learner_learn (MsgInfo *info, GSList *list, gboolean spam)
2463 GSList *cur = spam_learners;
2465 for (; cur; cur = cur->next) {
2466 int ((*func)(MsgInfo *info, GSList *list, gboolean spam)) = cur->data;
2467 ret |= func(info, list, spam);
2472 static gchar *spam_folder_item = NULL;
2473 static FolderItem * (*procmsg_spam_get_folder_func)(MsgInfo *msginfo) = NULL;
2474 void procmsg_spam_set_folder (const char *item_identifier, FolderItem *(*spam_get_folder_func)(MsgInfo *info))
2476 g_free(spam_folder_item);
2477 if (item_identifier)
2478 spam_folder_item = g_strdup(item_identifier);
2480 spam_folder_item = NULL;
2481 if (spam_get_folder_func != NULL)
2482 procmsg_spam_get_folder_func = spam_get_folder_func;
2484 procmsg_spam_get_folder_func = NULL;
2487 FolderItem *procmsg_spam_get_folder (MsgInfo *msginfo)
2489 FolderItem *item = NULL;
2491 if (procmsg_spam_get_folder_func)
2492 item = procmsg_spam_get_folder_func(msginfo);
2493 if (item == NULL && spam_folder_item)
2494 item = folder_find_item_from_identifier(spam_folder_item);
2496 item = folder_get_default_trash();
2500 static void item_has_queued_mails(FolderItem *item, gpointer data)
2502 gboolean *result = (gboolean *)data;
2503 if (*result == TRUE)
2505 if (folder_has_parent_of_type(item, F_QUEUE)) {
2506 if (item->total_msgs == 0)
2509 GSList *msglist = folder_item_get_msg_list(item);
2511 for (cur = msglist; cur; cur = cur->next) {
2512 MsgInfo *msginfo = (MsgInfo *)cur->data;
2513 if (!MSG_IS_DELETED(msginfo->flags) &&
2514 !MSG_IS_LOCKED(msginfo->flags)) {
2519 procmsg_msg_list_free(msglist);
2524 gboolean procmsg_have_queued_mails_fast (void)
2526 gboolean result = FALSE;
2527 folder_func_to_all_folders(item_has_queued_mails, &result);
2531 static void item_has_trashed_mails(FolderItem *item, gpointer data)
2533 gboolean *result = (gboolean *)data;
2534 if (*result == TRUE)
2536 if (folder_has_parent_of_type(item, F_TRASH) && item->total_msgs > 0)
2540 gboolean procmsg_have_trashed_mails_fast (void)
2542 gboolean result = FALSE;
2543 folder_func_to_all_folders(item_has_trashed_mails, &result);
2547 gchar *procmsg_msginfo_get_tags_str(MsgInfo *msginfo)
2555 if (msginfo->tags == NULL)
2557 for (cur = msginfo->tags; cur; cur = cur->next) {
2558 const gchar *tag = tags_get_tag(GPOINTER_TO_INT(cur->data));
2562 tags = g_strdup(tag);
2564 int olen = strlen(tags);
2565 int nlen = olen + strlen(tag) + 2 /* strlen(", ") */;
2566 tags = g_realloc(tags, nlen+1);
2569 strcpy(tags+olen, ", ");
2570 strcpy(tags+olen+2, tag);
2577 void procmsg_msginfo_update_tags(MsgInfo *msginfo, gboolean set, gint id)
2585 msginfo->tags = g_slist_remove(
2587 GINT_TO_POINTER(id));
2588 changed.data = GINT_TO_POINTER(id);
2589 changed.next = NULL;
2590 folder_item_commit_tags(msginfo->folder, msginfo, NULL, &changed);
2592 if (!g_slist_find(msginfo->tags, GINT_TO_POINTER(id))) {
2593 msginfo->tags = g_slist_append(
2595 GINT_TO_POINTER(id));
2597 changed.data = GINT_TO_POINTER(id);
2598 changed.next = NULL;
2599 folder_item_commit_tags(msginfo->folder, msginfo, &changed, NULL);
2604 void procmsg_msginfo_clear_tags(MsgInfo *msginfo)
2606 GSList *unset = msginfo->tags;
2607 msginfo->tags = NULL;
2608 folder_item_commit_tags(msginfo->folder, msginfo, NULL, unset);
2609 g_slist_free(unset);