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)
405 cm_return_val_if_fail(msginfo != NULL, NULL);
407 return folder_item_fetch_msg(msginfo->folder, msginfo->msgnum);
410 gchar *procmsg_get_message_file(MsgInfo *msginfo)
412 gchar *filename = NULL;
414 cm_return_val_if_fail(msginfo != NULL, NULL);
416 filename = folder_item_fetch_msg(msginfo->folder, msginfo->msgnum);
418 debug_print("can't fetch message %d\n", msginfo->msgnum);
423 gchar *procmsg_get_message_file_full(MsgInfo *msginfo, gboolean headers, gboolean body)
425 gchar *filename = NULL;
427 cm_return_val_if_fail(msginfo != NULL, NULL);
429 filename = folder_item_fetch_msg_full(msginfo->folder, msginfo->msgnum,
432 debug_print("can't fetch message %d\n", msginfo->msgnum);
437 GSList *procmsg_get_message_file_list(GSList *mlist)
439 GSList *file_list = NULL;
441 MsgFileInfo *fileinfo;
444 while (mlist != NULL) {
445 msginfo = (MsgInfo *)mlist->data;
446 file = procmsg_get_message_file(msginfo);
448 procmsg_message_file_list_free(file_list);
451 fileinfo = g_new(MsgFileInfo, 1);
452 fileinfo->msginfo = procmsg_msginfo_new_ref(msginfo);
453 fileinfo->file = file;
454 fileinfo->flags = g_new(MsgFlags, 1);
455 *fileinfo->flags = msginfo->flags;
456 file_list = g_slist_prepend(file_list, fileinfo);
460 file_list = g_slist_reverse(file_list);
465 void procmsg_message_file_list_free(MsgInfoList *file_list)
468 MsgFileInfo *fileinfo;
470 for (cur = file_list; cur != NULL; cur = cur->next) {
471 fileinfo = (MsgFileInfo *)cur->data;
472 procmsg_msginfo_free(fileinfo->msginfo);
473 g_free(fileinfo->file);
474 g_free(fileinfo->flags);
478 g_slist_free(file_list);
481 FILE *procmsg_open_message(MsgInfo *msginfo)
486 cm_return_val_if_fail(msginfo != NULL, NULL);
488 file = procmsg_get_message_file_path(msginfo);
489 cm_return_val_if_fail(file != NULL, NULL);
491 if (!is_file_exist(file)) {
493 file = procmsg_get_message_file(msginfo);
498 if ((fp = g_fopen(file, "rb")) == NULL) {
499 FILE_OP_ERROR(file, "fopen");
506 if (MSG_IS_QUEUED(msginfo->flags) || MSG_IS_DRAFT(msginfo->flags)) {
509 while (fgets(buf, sizeof(buf), fp) != NULL) {
511 if ((!strncmp(buf, "X-Claws-End-Special-Headers: 1",
512 strlen("X-Claws-End-Special-Headers:"))) ||
513 (!strncmp(buf, "X-Sylpheed-End-Special-Headers: 1",
514 strlen("X-Sylpheed-End-Special-Headers:"))))
517 if (buf[0] == '\r' || buf[0] == '\n') break;
518 /* from other mailers */
519 if (!strncmp(buf, "Date: ", 6)
520 || !strncmp(buf, "To: ", 4)
521 || !strncmp(buf, "From: ", 6)
522 || !strncmp(buf, "Subject: ", 9)) {
532 gboolean procmsg_msg_exist(MsgInfo *msginfo)
537 if (!msginfo) return FALSE;
539 path = folder_item_get_path(msginfo->folder);
541 ret = !folder_item_is_msg_changed(msginfo->folder, msginfo);
547 void procmsg_get_filter_keyword(MsgInfo *msginfo, gchar **header, gchar **key,
548 PrefsFilterType type)
550 static HeaderEntry hentry[] = {{"X-BeenThere:", NULL, TRUE},
551 {"X-ML-Name:", NULL, TRUE},
552 {"X-List:", NULL, TRUE},
553 {"X-Mailing-list:", NULL, TRUE},
554 {"List-Id:", NULL, TRUE},
555 {"X-Sequence:", NULL, TRUE},
556 {"Sender:", NULL, TRUE},
557 {"List-Post:", NULL, TRUE},
558 {NULL, NULL, FALSE}};
564 H_X_MAILING_LIST = 3,
573 cm_return_if_fail(msginfo != NULL);
574 cm_return_if_fail(header != NULL);
575 cm_return_if_fail(key != NULL);
584 if ((fp = procmsg_open_message(msginfo)) == NULL)
586 procheader_get_header_fields(fp, hentry);
589 #define SET_FILTER_KEY(hstr, idx) \
591 *header = g_strdup(hstr); \
592 *key = hentry[idx].body; \
593 hentry[idx].body = NULL; \
596 if (hentry[H_LIST_ID].body != NULL) {
597 SET_FILTER_KEY("header \"List-Id\"", H_LIST_ID);
598 extract_list_id_str(*key);
599 } else if (hentry[H_X_BEENTHERE].body != NULL) {
600 SET_FILTER_KEY("header \"X-BeenThere\"", H_X_BEENTHERE);
601 } else if (hentry[H_X_ML_NAME].body != NULL) {
602 SET_FILTER_KEY("header \"X-ML-Name\"", H_X_ML_NAME);
603 } else if (hentry[H_X_LIST].body != NULL) {
604 SET_FILTER_KEY("header \"X-List\"", H_X_LIST);
605 } else if (hentry[H_X_MAILING_LIST].body != NULL) {
606 SET_FILTER_KEY("header \"X-Mailing-List\"", H_X_MAILING_LIST);
607 } else if (hentry[H_X_SEQUENCE].body != NULL) {
610 SET_FILTER_KEY("X-Sequence", H_X_SEQUENCE);
613 while (*p != '\0' && !g_ascii_isspace(*p)) p++;
614 while (g_ascii_isspace(*p)) p++;
615 if (g_ascii_isdigit(*p)) {
621 } else if (hentry[H_SENDER].body != NULL) {
622 SET_FILTER_KEY("header \"Sender\"", H_SENDER);
623 } else if (hentry[H_LIST_POST].body != NULL) {
624 SET_FILTER_KEY("header \"List-Post\"", H_LIST_POST);
625 } else if (msginfo->to) {
626 *header = g_strdup("to");
627 *key = g_strdup(msginfo->to);
628 } else if (msginfo->subject) {
629 *header = g_strdup("subject");
630 *key = g_strdup(msginfo->subject);
633 #undef SET_FILTER_KEY
635 g_free(hentry[H_X_BEENTHERE].body);
636 hentry[H_X_BEENTHERE].body = NULL;
637 g_free(hentry[H_X_ML_NAME].body);
638 hentry[H_X_ML_NAME].body = NULL;
639 g_free(hentry[H_X_LIST].body);
640 hentry[H_X_LIST].body = NULL;
641 g_free(hentry[H_X_MAILING_LIST].body);
642 hentry[H_X_MAILING_LIST].body = NULL;
643 g_free(hentry[H_LIST_ID].body);
644 hentry[H_LIST_ID].body = NULL;
645 g_free(hentry[H_SENDER].body);
646 hentry[H_SENDER].body = NULL;
647 g_free(hentry[H_LIST_POST].body);
648 hentry[H_LIST_POST].body = NULL;
652 *header = g_strdup("from");
653 *key = g_strdup(msginfo->from);
656 *header = g_strdup("to");
657 *key = g_strdup(msginfo->to);
659 case FILTER_BY_SUBJECT:
660 *header = g_strdup("subject");
661 *key = g_strdup(msginfo->subject);
668 static void procmsg_empty_trash(FolderItem *trash)
673 (trash->stype != F_TRASH &&
674 !folder_has_parent_of_type(trash, F_TRASH)))
677 if (trash && trash->total_msgs > 0) {
678 GSList *mlist = folder_item_get_msg_list(trash);
680 for (cur = mlist ; cur != NULL ; cur = cur->next) {
681 MsgInfo * msginfo = (MsgInfo *) cur->data;
682 if (MSG_IS_LOCKED(msginfo->flags)) {
683 procmsg_msginfo_free(msginfo);
686 if (msginfo->total_size != 0 &&
687 msginfo->size != (off_t)msginfo->total_size)
688 partial_mark_for_delete(msginfo);
690 procmsg_msginfo_free(msginfo);
693 folder_item_remove_all_msg(trash);
696 if (!trash->node || !trash->node->children)
699 node = trash->node->children;
700 while (node != NULL) {
702 procmsg_empty_trash(FOLDER_ITEM(node->data));
707 void procmsg_empty_all_trash(void)
712 for (cur = folder_get_list(); cur != NULL; cur = cur->next) {
713 Folder *folder = FOLDER(cur->data);
714 trash = folder->trash;
715 procmsg_empty_trash(trash);
716 if (folder->account && folder->account->set_trash_folder &&
717 folder_find_item_from_identifier(folder->account->trash_folder))
719 folder_find_item_from_identifier(folder->account->trash_folder));
723 static PrefsAccount *procmsg_get_account_from_file(const gchar *file)
725 PrefsAccount *mailac = NULL;
729 static HeaderEntry qentry[] = {{"S:", NULL, FALSE},
730 {"SSV:", NULL, FALSE},
732 {"NG:", NULL, FALSE},
733 {"MAID:", NULL, FALSE},
734 {"NAID:", NULL, FALSE},
735 {"SCF:", NULL, FALSE},
736 {"RMID:", NULL, FALSE},
737 {"FMID:", NULL, FALSE},
738 {"X-Claws-Privacy-System:", NULL, FALSE},
739 {"X-Claws-Encrypt:", NULL, FALSE},
740 {"X-Claws-Encrypt-Data:", NULL, FALSE},
741 {"X-Claws-End-Special-Headers", NULL, FALSE},
742 {"X-Sylpheed-Privacy-System:", NULL, FALSE},
743 {"X-Sylpheed-Encrypt:", NULL, FALSE},
744 {"X-Sylpheed-Encrypt-Data:", NULL, FALSE},
745 {NULL, NULL, FALSE}};
747 cm_return_val_if_fail(file != NULL, NULL);
749 if ((fp = g_fopen(file, "rb")) == NULL) {
750 FILE_OP_ERROR(file, "fopen");
754 while ((hnum = procheader_get_one_field(buf, sizeof(buf), fp, qentry))
756 gchar *p = buf + strlen(qentry[hnum].name);
758 if (hnum == Q_MAIL_ACCOUNT_ID) {
759 mailac = account_find_from_id(atoi(p));
767 gchar *procmsg_msginfo_get_avatar(MsgInfo *msginfo, gint type)
771 if (!msginfo || !msginfo->extradata || !msginfo->extradata->avatars)
774 for (mia = msginfo->extradata->avatars; mia; mia = mia->next) {
775 MsgInfoAvatar *avatar = (MsgInfoAvatar *)mia->data;
776 if (avatar->avatar_id == type)
777 return avatar->avatar_src;
783 void procmsg_msginfo_add_avatar(MsgInfo *msginfo, gint type, const gchar *data)
787 if (!msginfo->extradata)
788 msginfo->extradata = g_new0(MsgInfoExtraData, 1);
790 av = g_new0(MsgInfoAvatar, 1);
791 av->avatar_id = type;
792 av->avatar_src = g_strdup(data);
794 msginfo->extradata->avatars = g_slist_append(msginfo->extradata->avatars, av);
797 gchar *procmsg_msginfo_get_identifier(MsgInfo *msginfo)
803 cm_return_val_if_fail(msginfo != NULL, NULL);
804 folder_id = folder_item_get_identifier(msginfo->folder);
805 msgid = msginfo->msgid;
807 id = g_strconcat(folder_id, G_DIR_SEPARATOR_S, msgid, NULL);
814 MsgInfo *procmsg_get_msginfo_from_identifier(const gchar *id)
816 gchar *folder_id = g_strdup(id);
817 gchar *separator = strrchr(folder_id, G_DIR_SEPARATOR);
822 if (separator == NULL) {
828 msgid = separator + 1;
830 item = folder_find_item_from_identifier(folder_id);
837 msginfo = folder_item_get_msginfo_by_msgid(item, msgid);
843 static GSList *procmsg_list_sort_by_account(FolderItem *queue, GSList *list)
845 GSList *result = NULL;
847 PrefsAccount *last_account = NULL;
850 gboolean nothing_to_sort = TRUE;
855 orig = g_slist_copy(list);
857 msg = (MsgInfo *)orig->data;
859 for (cur = orig; cur; cur = cur->next)
860 debug_print("sort before %s\n", ((MsgInfo *)cur->data)->from);
865 nothing_to_sort = TRUE;
869 PrefsAccount *ac = NULL;
870 msg = (MsgInfo *)cur->data;
871 file = folder_item_fetch_msg(queue, msg->msgnum);
872 ac = procmsg_get_account_from_file(file);
875 if (last_account == NULL || (ac != NULL && ac == last_account)) {
876 result = g_slist_append(result, msg);
877 orig = g_slist_remove(orig, msg);
879 nothing_to_sort = FALSE;
885 if (orig && g_slist_length(orig)) {
886 if (!last_account && nothing_to_sort) {
887 /* can't find an account for the rest of the list */
890 result = g_slist_append(result, cur->data);
901 for (cur = result; cur; cur = cur->next)
902 debug_print("sort after %s\n", ((MsgInfo *)cur->data)->from);
909 static gboolean procmsg_is_last_for_account(FolderItem *queue, MsgInfo *msginfo, GSList *elem)
911 gchar *file = folder_item_fetch_msg(queue, msginfo->msgnum);
912 PrefsAccount *ac = procmsg_get_account_from_file(file);
915 for (cur = elem; cur; cur = cur->next) {
916 MsgInfo *cur_msginfo = (MsgInfo *)cur->data;
917 file = folder_item_fetch_msg(queue, cur_msginfo->msgnum);
919 if (cur_msginfo != msginfo && !MSG_IS_LOCKED(cur_msginfo->flags)) {
920 if (procmsg_get_account_from_file(file) == ac) {
931 static gboolean send_queue_lock = FALSE;
933 gboolean procmsg_queue_lock(char **errstr)
935 if (send_queue_lock) {
936 /* Avoid having to translate two similar strings */
937 log_warning(LOG_PROTOCOL, "%s\n", _("Already trying to send."));
939 if (*errstr) g_free(*errstr);
940 *errstr = g_strdup_printf(_("Already trying to send."));
944 send_queue_lock = TRUE;
947 void procmsg_queue_unlock(void)
949 send_queue_lock = FALSE;
952 *\brief Send messages in queue
954 *\param queue Queue folder to process
955 *\param save_msgs Unused
957 *\return Number of messages sent, negative if an error occurred
958 * positive if no error occurred
960 gint procmsg_send_queue(FolderItem *queue, gboolean save_msgs, gchar **errstr)
962 gint sent = 0, err = 0;
964 GSList *sorted_list = NULL;
967 if (!procmsg_queue_lock(errstr)) {
968 main_window_set_menu_sensitive(mainwindow_get_mainwindow());
969 toolbar_main_set_sensitive(mainwindow_get_mainwindow());
974 queue = folder_get_default_queue();
977 procmsg_queue_unlock();
982 main_window_set_menu_sensitive(mainwindow_get_mainwindow());
983 toolbar_main_set_sensitive(mainwindow_get_mainwindow());
985 folder_item_scan(queue);
986 list = folder_item_get_msg_list(queue);
988 /* sort the list per sender account; this helps reusing the same SMTP server */
989 sorted_list = procmsg_list_sort_by_account(queue, list);
991 for (elem = sorted_list; elem != NULL; elem = elem->next) {
995 msginfo = (MsgInfo *)(elem->data);
996 if (!MSG_IS_LOCKED(msginfo->flags) && !MSG_IS_DELETED(msginfo->flags)) {
997 file = folder_item_fetch_msg(queue, msginfo->msgnum);
999 gboolean queued_removed = FALSE;
1000 if (procmsg_send_message_queue_full(file,
1001 !procmsg_is_last_for_account(queue, msginfo, elem),
1002 errstr, queue, msginfo->msgnum, &queued_removed) < 0) {
1003 g_warning("Sending queued message %d failed.",
1008 if (!queued_removed)
1009 folder_item_remove_msg(queue, msginfo->msgnum);
1014 /* FIXME: supposedly if only one message is locked, and queue
1015 * is being flushed, the following free says something like
1016 * "freeing msg ## in folder (nil)". */
1017 procmsg_msginfo_free(msginfo);
1020 g_slist_free(sorted_list);
1021 folder_item_scan(queue);
1023 if (queue->node && queue->node->children) {
1024 node = queue->node->children;
1025 while (node != NULL) {
1028 send_queue_lock = FALSE;
1029 res = procmsg_send_queue(FOLDER_ITEM(node->data), save_msgs, errstr);
1030 send_queue_lock = TRUE;
1038 procmsg_queue_unlock();
1040 main_window_set_menu_sensitive(mainwindow_get_mainwindow());
1041 toolbar_main_set_sensitive(mainwindow_get_mainwindow());
1043 return (err != 0 ? -err : sent);
1046 gboolean procmsg_is_sending(void)
1048 return send_queue_lock;
1052 *\brief Determine if a queue folder is empty
1054 *\param queue Queue folder to process
1056 *\return TRUE if the queue folder is empty, otherwise return FALSE
1058 gboolean procmsg_queue_is_empty(FolderItem *queue)
1061 gboolean res = FALSE;
1063 queue = folder_get_default_queue();
1064 cm_return_val_if_fail(queue != NULL, TRUE);
1066 folder_item_scan(queue);
1067 list = folder_item_get_msg_list(queue);
1068 res = (list == NULL);
1069 procmsg_msg_list_free(list);
1073 if (queue->node && queue->node->children) {
1074 node = queue->node->children;
1075 while (node != NULL) {
1077 if (!procmsg_queue_is_empty(FOLDER_ITEM(node->data)))
1086 gint procmsg_remove_special_headers(const gchar *in, const gchar *out)
1089 gchar buf[BUFFSIZE];
1091 if ((fp = g_fopen(in, "rb")) == NULL) {
1092 FILE_OP_ERROR(in, "fopen");
1095 if ((outfp = g_fopen(out, "wb")) == NULL) {
1096 FILE_OP_ERROR(out, "fopen");
1100 while (fgets(buf, sizeof(buf), fp) != NULL) {
1102 if ((!strncmp(buf, "X-Claws-End-Special-Headers: 1",
1103 strlen("X-Claws-End-Special-Headers:"))) ||
1104 (!strncmp(buf, "X-Sylpheed-End-Special-Headers: 1",
1105 strlen("X-Sylpheed-End-Special-Headers:"))))
1108 if (buf[0] == '\r' || buf[0] == '\n') break;
1109 /* from other mailers */
1110 if (!strncmp(buf, "Date: ", 6)
1111 || !strncmp(buf, "To: ", 4)
1112 || !strncmp(buf, "From: ", 6)
1113 || !strncmp(buf, "Subject: ", 9)) {
1118 while (fgets(buf, sizeof(buf), fp) != NULL) {
1119 if (fputs(buf, outfp) == EOF) {
1120 FILE_OP_ERROR(out, "fputs");
1131 gint procmsg_save_to_outbox(FolderItem *outbox, const gchar *file,
1135 MsgInfo *msginfo, *tmp_msginfo;
1136 MsgFlags flag = {0, 0};
1138 debug_print("saving sent message...\n");
1141 outbox = folder_get_default_outbox();
1142 cm_return_val_if_fail(outbox != NULL, -1);
1144 /* remove queueing headers */
1146 gchar tmp[MAXPATHLEN + 1];
1148 g_snprintf(tmp, sizeof(tmp), "%s%ctmpmsg.out.%08x",
1149 get_rc_dir(), G_DIR_SEPARATOR, (guint) rand());
1151 if (procmsg_remove_special_headers(file, tmp) !=0)
1154 folder_item_scan(outbox);
1155 if ((num = folder_item_add_msg(outbox, tmp, &flag, TRUE)) < 0) {
1156 g_warning("can't save message");
1161 folder_item_scan(outbox);
1162 if ((num = folder_item_add_msg
1163 (outbox, file, &flag, FALSE)) < 0) {
1164 g_warning("can't save message");
1168 msginfo = folder_item_get_msginfo(outbox, num); /* refcnt++ */
1169 tmp_msginfo = procmsg_msginfo_get_full_info(msginfo); /* refcnt++ */
1170 if (msginfo != NULL) {
1171 procmsg_msginfo_unset_flags(msginfo, ~0, 0);
1172 procmsg_msginfo_free(msginfo); /* refcnt-- */
1173 /* tmp_msginfo == msginfo */
1174 if (tmp_msginfo && msginfo->extradata &&
1175 (msginfo->extradata->dispositionnotificationto ||
1176 msginfo->extradata->returnreceiptto)) {
1177 procmsg_msginfo_set_flags(msginfo, MSG_RETRCPT_SENT, 0);
1179 procmsg_msginfo_free(tmp_msginfo); /* refcnt-- */
1186 MsgInfo *procmsg_msginfo_new_ref(MsgInfo *msginfo)
1193 MsgInfo *procmsg_msginfo_new(void)
1195 MsgInfo *newmsginfo;
1197 newmsginfo = g_new0(MsgInfo, 1);
1198 newmsginfo->refcnt = 1;
1203 static MsgInfoAvatar *procmsg_msginfoavatar_copy(MsgInfoAvatar *avatar)
1205 MsgInfoAvatar *newavatar;
1207 if (avatar == NULL) return NULL;
1209 newavatar = g_new0(MsgInfoAvatar, 1);
1210 newavatar->avatar_id = avatar->avatar_id;
1211 newavatar->avatar_src = g_strdup(avatar->avatar_src);
1216 static void procmsg_msginfoavatar_free(MsgInfoAvatar *avatar)
1218 if (avatar != NULL) {
1219 if (avatar->avatar_src != NULL)
1220 g_free(avatar->avatar_src);
1225 MsgInfo *procmsg_msginfo_copy(MsgInfo *msginfo)
1227 MsgInfo *newmsginfo;
1230 if (msginfo == NULL) return NULL;
1232 newmsginfo = g_new0(MsgInfo, 1);
1234 newmsginfo->refcnt = 1;
1236 #define MEMBCOPY(mmb) newmsginfo->mmb = msginfo->mmb
1237 #define MEMBDUP(mmb) newmsginfo->mmb = msginfo->mmb ? \
1238 g_strdup(msginfo->mmb) : NULL
1253 MEMBDUP(newsgroups);
1260 MEMBCOPY(to_folder);
1262 if (msginfo->extradata) {
1263 newmsginfo->extradata = g_new0(MsgInfoExtraData, 1);
1264 if (msginfo->extradata->avatars) {
1265 newmsginfo->extradata->avatars = slist_copy_deep(msginfo->extradata->avatars,
1266 (GCopyFunc) procmsg_msginfoavatar_copy);
1268 MEMBDUP(extradata->dispositionnotificationto);
1269 MEMBDUP(extradata->returnreceiptto);
1270 MEMBDUP(extradata->partial_recv);
1271 MEMBDUP(extradata->account_server);
1272 MEMBDUP(extradata->account_login);
1273 MEMBDUP(extradata->list_post);
1274 MEMBDUP(extradata->list_subscribe);
1275 MEMBDUP(extradata->list_unsubscribe);
1276 MEMBDUP(extradata->list_help);
1277 MEMBDUP(extradata->list_archive);
1278 MEMBDUP(extradata->list_owner);
1279 MEMBDUP(extradata->resent_from);
1282 refs = msginfo->references;
1283 for (refs = msginfo->references; refs != NULL; refs = refs->next) {
1284 newmsginfo->references = g_slist_prepend
1285 (newmsginfo->references, g_strdup(refs->data));
1287 newmsginfo->references = g_slist_reverse(newmsginfo->references);
1290 MEMBDUP(plaintext_file);
1295 MsgInfo *procmsg_msginfo_get_full_info_from_file(MsgInfo *msginfo, const gchar *file)
1297 MsgInfo *full_msginfo;
1299 if (msginfo == NULL) return NULL;
1301 if (!file || !is_file_exist(file)) {
1302 g_warning("procmsg_msginfo_get_full_info_from_file(): can't get message file.");
1306 full_msginfo = procheader_parse_file(file, msginfo->flags, TRUE, FALSE);
1307 if (!full_msginfo) return NULL;
1309 msginfo->total_size = full_msginfo->total_size;
1310 msginfo->planned_download = full_msginfo->planned_download;
1312 if (full_msginfo->extradata) {
1313 if (!msginfo->extradata)
1314 msginfo->extradata = g_new0(MsgInfoExtraData, 1);
1315 if (!msginfo->extradata->list_post)
1316 msginfo->extradata->list_post = g_strdup(full_msginfo->extradata->list_post);
1317 if (!msginfo->extradata->list_subscribe)
1318 msginfo->extradata->list_subscribe = g_strdup(full_msginfo->extradata->list_subscribe);
1319 if (!msginfo->extradata->list_unsubscribe)
1320 msginfo->extradata->list_unsubscribe = g_strdup(full_msginfo->extradata->list_unsubscribe);
1321 if (!msginfo->extradata->list_help)
1322 msginfo->extradata->list_help = g_strdup(full_msginfo->extradata->list_help);
1323 if (!msginfo->extradata->list_archive)
1324 msginfo->extradata->list_archive= g_strdup(full_msginfo->extradata->list_archive);
1325 if (!msginfo->extradata->list_owner)
1326 msginfo->extradata->list_owner = g_strdup(full_msginfo->extradata->list_owner);
1327 if (!msginfo->extradata->avatars)
1328 msginfo->extradata->avatars = slist_copy_deep(full_msginfo->extradata->avatars,
1329 (GCopyFunc) procmsg_msginfoavatar_copy);
1330 if (!msginfo->extradata->dispositionnotificationto)
1331 msginfo->extradata->dispositionnotificationto =
1332 g_strdup(full_msginfo->extradata->dispositionnotificationto);
1333 if (!msginfo->extradata->returnreceiptto)
1334 msginfo->extradata->returnreceiptto = g_strdup
1335 (full_msginfo->extradata->returnreceiptto);
1336 if (!msginfo->extradata->partial_recv && full_msginfo->extradata->partial_recv)
1337 msginfo->extradata->partial_recv = g_strdup
1338 (full_msginfo->extradata->partial_recv);
1339 if (!msginfo->extradata->account_server && full_msginfo->extradata->account_server)
1340 msginfo->extradata->account_server = g_strdup
1341 (full_msginfo->extradata->account_server);
1342 if (!msginfo->extradata->account_login && full_msginfo->extradata->account_login)
1343 msginfo->extradata->account_login = g_strdup
1344 (full_msginfo->extradata->account_login);
1345 if (!msginfo->extradata->resent_from && full_msginfo->extradata->resent_from)
1346 msginfo->extradata->resent_from = g_strdup
1347 (full_msginfo->extradata->resent_from);
1349 procmsg_msginfo_free(full_msginfo);
1351 return procmsg_msginfo_new_ref(msginfo);
1354 MsgInfo *procmsg_msginfo_get_full_info(MsgInfo *msginfo)
1356 MsgInfo *full_msginfo;
1359 if (msginfo == NULL) return NULL;
1361 file = procmsg_get_message_file_path(msginfo);
1362 if (!file || !is_file_exist(file)) {
1364 file = procmsg_get_message_file(msginfo);
1366 if (!file || !is_file_exist(file)) {
1367 g_warning("procmsg_msginfo_get_full_info(): can't get message file.");
1371 full_msginfo = procmsg_msginfo_get_full_info_from_file(msginfo, file);
1373 return full_msginfo;
1376 void procmsg_msginfo_free(MsgInfo *msginfo)
1378 if (msginfo == NULL) return;
1381 if (msginfo->refcnt > 0)
1384 if (msginfo->to_folder) {
1385 msginfo->to_folder->op_count--;
1386 folder_item_update(msginfo->to_folder, F_ITEM_UPDATE_MSGCNT);
1389 g_free(msginfo->fromspace);
1391 g_free(msginfo->fromname);
1393 g_free(msginfo->date);
1394 g_free(msginfo->from);
1395 g_free(msginfo->to);
1396 g_free(msginfo->cc);
1397 g_free(msginfo->newsgroups);
1398 g_free(msginfo->subject);
1399 g_free(msginfo->msgid);
1400 g_free(msginfo->inreplyto);
1401 g_free(msginfo->xref);
1403 if (msginfo->extradata) {
1404 if (msginfo->extradata->avatars) {
1405 g_slist_foreach(msginfo->extradata->avatars,
1406 (GFunc)procmsg_msginfoavatar_free,
1408 g_slist_free(msginfo->extradata->avatars);
1410 g_free(msginfo->extradata->returnreceiptto);
1411 g_free(msginfo->extradata->dispositionnotificationto);
1412 g_free(msginfo->extradata->list_post);
1413 g_free(msginfo->extradata->list_subscribe);
1414 g_free(msginfo->extradata->list_unsubscribe);
1415 g_free(msginfo->extradata->list_help);
1416 g_free(msginfo->extradata->list_archive);
1417 g_free(msginfo->extradata->list_owner);
1418 g_free(msginfo->extradata->partial_recv);
1419 g_free(msginfo->extradata->account_server);
1420 g_free(msginfo->extradata->account_login);
1421 g_free(msginfo->extradata->resent_from);
1422 g_free(msginfo->extradata);
1424 slist_free_strings_full(msginfo->references);
1425 g_slist_free(msginfo->tags);
1427 g_free(msginfo->plaintext_file);
1432 guint procmsg_msginfo_memusage(MsgInfo *msginfo)
1437 memusage += sizeof(MsgInfo);
1438 if (msginfo->fromname)
1439 memusage += strlen(msginfo->fromname);
1441 memusage += strlen(msginfo->date);
1443 memusage += strlen(msginfo->from);
1445 memusage += strlen(msginfo->to);
1447 memusage += strlen(msginfo->cc);
1448 if (msginfo->newsgroups)
1449 memusage += strlen(msginfo->newsgroups);
1450 if (msginfo->subject)
1451 memusage += strlen(msginfo->subject);
1453 memusage += strlen(msginfo->msgid);
1454 if (msginfo->inreplyto)
1455 memusage += strlen(msginfo->inreplyto);
1457 for (tmp = msginfo->references; tmp; tmp=tmp->next) {
1458 gchar *r = (gchar *)tmp->data;
1459 memusage += r?strlen(r):0 + sizeof(GSList);
1461 if (msginfo->fromspace)
1462 memusage += strlen(msginfo->fromspace);
1464 for (tmp = msginfo->tags; tmp; tmp=tmp->next) {
1465 memusage += sizeof(GSList);
1467 if (msginfo->extradata) {
1468 memusage += sizeof(MsgInfoExtraData);
1469 if (msginfo->extradata->avatars) {
1470 for (tmp = msginfo->extradata->avatars; tmp; tmp = tmp->next) {
1471 MsgInfoAvatar *avt = (MsgInfoAvatar *)tmp->data;
1472 memusage += (avt->avatar_src)? strlen(avt->avatar_src): 0;
1473 memusage += sizeof(MsgInfoAvatar) + sizeof(GSList);
1476 if (msginfo->extradata->dispositionnotificationto)
1477 memusage += strlen(msginfo->extradata->dispositionnotificationto);
1478 if (msginfo->extradata->returnreceiptto)
1479 memusage += strlen(msginfo->extradata->returnreceiptto);
1481 if (msginfo->extradata->partial_recv)
1482 memusage += strlen(msginfo->extradata->partial_recv);
1483 if (msginfo->extradata->account_server)
1484 memusage += strlen(msginfo->extradata->account_server);
1485 if (msginfo->extradata->account_login)
1486 memusage += strlen(msginfo->extradata->account_login);
1487 if (msginfo->extradata->resent_from)
1488 memusage += strlen(msginfo->extradata->resent_from);
1490 if (msginfo->extradata->list_post)
1491 memusage += strlen(msginfo->extradata->list_post);
1492 if (msginfo->extradata->list_subscribe)
1493 memusage += strlen(msginfo->extradata->list_subscribe);
1494 if (msginfo->extradata->list_unsubscribe)
1495 memusage += strlen(msginfo->extradata->list_unsubscribe);
1496 if (msginfo->extradata->list_help)
1497 memusage += strlen(msginfo->extradata->list_help);
1498 if (msginfo->extradata->list_archive)
1499 memusage += strlen(msginfo->extradata->list_archive);
1500 if (msginfo->extradata->list_owner)
1501 memusage += strlen(msginfo->extradata->list_owner);
1506 static gint procmsg_send_message_queue_full(const gchar *file, gboolean keep_session, gchar **errstr,
1507 FolderItem *queue, gint msgnum, gboolean *queued_removed)
1509 static HeaderEntry qentry[] = {
1510 {"S:", NULL, FALSE}, /* 0 */
1511 {"SSV:", NULL, FALSE},
1512 {"R:", NULL, FALSE},
1513 {"NG:", NULL, FALSE},
1514 {"MAID:", NULL, FALSE},
1515 {"NAID:", NULL, FALSE}, /* 5 */
1516 {"SCF:", NULL, FALSE},
1517 {"RMID:", NULL, FALSE},
1518 {"FMID:", NULL, FALSE},
1519 {"X-Claws-Privacy-System:", NULL, FALSE},
1520 {"X-Claws-Encrypt:", NULL, FALSE}, /* 10 */
1521 {"X-Claws-Encrypt-Data:", NULL, FALSE},
1522 {"X-Claws-End-Special-Headers:", NULL, FALSE},
1523 {"X-Sylpheed-Privacy-System:", NULL, FALSE},
1524 {"X-Sylpheed-Encrypt:", NULL, FALSE},
1525 {"X-Sylpheed-Encrypt-Data:", NULL, FALSE}, /* 15 */
1526 {"X-Sylpheed-End-Special-Headers:", NULL, FALSE},
1527 {NULL, NULL, FALSE}};
1530 gint mailval = 0, newsval = 0;
1532 gchar *smtpserver = NULL;
1533 GSList *to_list = NULL;
1534 GSList *newsgroup_list = NULL;
1535 gchar *savecopyfolder = NULL;
1536 gchar *replymessageid = NULL;
1537 gchar *fwdmessageid = NULL;
1538 gchar buf[BUFFSIZE];
1540 PrefsAccount *mailac = NULL, *newsac = NULL;
1541 gboolean encrypt = FALSE;
1544 cm_return_val_if_fail(file != NULL, -1);
1546 if ((fp = g_fopen(file, "rb")) == NULL) {
1547 FILE_OP_ERROR(file, "fopen");
1549 if (*errstr) g_free(*errstr);
1550 *errstr = g_strdup_printf(_("Couldn't open file %s."), file);
1555 while ((hnum = procheader_get_one_field(buf, sizeof(buf), fp, qentry))
1557 gchar *p = buf + strlen(qentry[hnum].name);
1565 if (smtpserver == NULL)
1566 smtpserver = g_strdup(p);
1569 to_list = address_list_append(to_list, p);
1572 newsgroup_list = newsgroup_list_append(newsgroup_list, p);
1574 case Q_MAIL_ACCOUNT_ID:
1575 mailac = account_find_from_id(atoi(p));
1577 case Q_NEWS_ACCOUNT_ID:
1578 newsac = account_find_from_id(atoi(p));
1580 case Q_SAVE_COPY_FOLDER:
1581 if (savecopyfolder == NULL)
1582 savecopyfolder = g_strdup(p);
1584 case Q_REPLY_MESSAGE_ID:
1585 if (replymessageid == NULL)
1586 replymessageid = g_strdup(p);
1588 case Q_FWD_MESSAGE_ID:
1589 if (fwdmessageid == NULL)
1590 fwdmessageid = g_strdup(p);
1598 case Q_CLAWS_HDRS_OLD:
1599 /* end of special headers reached */
1600 goto send_mail; /* can't "break;break;" */
1604 filepos = ftell(fp);
1606 FILE_OP_ERROR(file, "ftell");
1608 if (*errstr) g_free(*errstr);
1609 *errstr = g_strdup_printf(_("Couldn't open file %s."), file);
1615 debug_print("Sending message by mail\n");
1618 if (*errstr) g_free(*errstr);
1619 *errstr = g_strdup_printf(_("Queued message header is broken."));
1622 } else if (mailac && mailac->use_mail_command &&
1623 mailac->mail_command && (* mailac->mail_command)) {
1624 mailval = send_message_local(mailac->mail_command, fp);
1627 mailac = account_find_from_smtp_server(from, smtpserver);
1629 g_warning("Account not found. "
1630 "Using current account...");
1631 mailac = cur_account;
1636 mailval = send_message_smtp_full(mailac, to_list, fp, keep_session);
1637 if (mailval == -1 && errstr) {
1638 if (*errstr) g_free(*errstr);
1639 *errstr = g_strdup_printf(_("An error happened during SMTP session."));
1642 PrefsAccount tmp_ac;
1644 g_warning("Account not found.");
1646 memset(&tmp_ac, 0, sizeof(PrefsAccount));
1647 tmp_ac.address = from;
1648 tmp_ac.smtp_server = smtpserver;
1649 tmp_ac.smtpport = SMTP_PORT;
1650 mailval = send_message_smtp(&tmp_ac, to_list, fp);
1651 if (mailval == -1 && errstr) {
1652 if (*errstr) g_free(*errstr);
1653 *errstr = g_strdup_printf(_("No specific account has been found to "
1654 "send, and an error happened during SMTP session."));
1658 } else if (!to_list && !newsgroup_list) {
1660 if (*errstr) g_free(*errstr);
1661 *errstr = g_strdup(_("Couldn't determine sending information. "
1662 "Maybe the email hasn't been generated by Claws Mail."));
1667 if (fseek(fp, filepos, SEEK_SET) < 0) {
1668 FILE_OP_ERROR(file, "fseek");
1672 if (newsgroup_list && newsac && (mailval == 0)) {
1677 /* write to temporary file */
1678 tmp = g_strdup_printf("%s%cnntp%p", get_tmp_dir(),
1679 G_DIR_SEPARATOR, file);
1680 if ((tmpfp = g_fopen(tmp, "wb")) == NULL) {
1681 FILE_OP_ERROR(tmp, "fopen");
1683 alertpanel_error(_("Couldn't create temporary file for news sending."));
1685 if (change_file_mode_rw(tmpfp, tmp) < 0) {
1686 FILE_OP_ERROR(tmp, "chmod");
1687 g_warning("can't change file mode");
1690 while ((newsval == 0) && fgets(buf, sizeof(buf), fp) != NULL) {
1691 if (fputs(buf, tmpfp) == EOF) {
1692 FILE_OP_ERROR(tmp, "fputs");
1695 if (*errstr) g_free(*errstr);
1696 *errstr = g_strdup_printf(_("Error when writing temporary file for news sending."));
1703 debug_print("Sending message by news\n");
1705 folder = FOLDER(newsac->folder);
1707 newsval = news_post(folder, tmp);
1708 if (newsval < 0 && errstr) {
1709 if (*errstr) g_free(*errstr);
1710 *errstr = g_strdup_printf(_("Error occurred while posting the message to %s."),
1711 newsac->nntp_server);
1721 /* update session statistics */
1722 if (mailval == 0 && newsval == 0) {
1723 /* update session stats */
1725 session_stats.replied++;
1726 else if (fwdmessageid)
1727 session_stats.forwarded++;
1729 session_stats.sent++;
1732 /* save message to outbox */
1733 if (mailval == 0 && newsval == 0 && savecopyfolder) {
1734 debug_print("saving sent message...\n");
1736 if (!encrypt || !mailac->save_encrypted_as_clear_text) {
1737 outbox = folder_find_item_from_identifier(savecopyfolder);
1739 outbox = folder_get_default_outbox();
1741 /* Mail was not saved to outbox before encrypting, save it now. */
1742 gboolean saved = FALSE;
1743 *queued_removed = FALSE;
1744 if (queue && msgnum > 0) {
1745 MsgInfo *queued_mail = folder_item_get_msginfo(queue, msgnum);
1746 if (folder_item_move_msg(outbox, queued_mail) >= 0) {
1747 debug_print("moved queued mail %d to sent folder\n", msgnum);
1749 *queued_removed = TRUE;
1750 } else if (folder_item_copy_msg(outbox, queued_mail) >= 0) {
1751 debug_print("copied queued mail %d to sent folder\n", msgnum);
1754 procmsg_msginfo_free(queued_mail);
1757 debug_print("resaving queued mail to sent folder\n");
1758 procmsg_save_to_outbox(outbox, file, TRUE);
1763 if (replymessageid != NULL || fwdmessageid != NULL) {
1767 if (replymessageid != NULL)
1768 tokens = g_strsplit(replymessageid, "\t", 0);
1770 tokens = g_strsplit(fwdmessageid, "\t", 0);
1771 item = folder_find_item_from_identifier(tokens[0]);
1773 /* check if queued message has valid folder and message id */
1774 if (item != NULL && tokens[2] != NULL) {
1777 msginfo = folder_item_get_msginfo(item, atoi(tokens[1]));
1779 /* check if referring message exists and has a message id */
1780 if ((msginfo != NULL) &&
1781 (msginfo->msgid != NULL) &&
1782 (strcmp(msginfo->msgid, tokens[2]) != 0)) {
1783 procmsg_msginfo_free(msginfo);
1787 if (msginfo == NULL) {
1788 msginfo = folder_item_get_msginfo_by_msgid(item, tokens[2]);
1791 if (msginfo != NULL) {
1792 if (replymessageid != NULL) {
1793 MsgPermFlags to_unset = 0;
1795 if (prefs_common.mark_as_read_on_new_window)
1796 to_unset = (MSG_NEW|MSG_UNREAD);
1798 procmsg_msginfo_unset_flags(msginfo, to_unset, 0);
1799 procmsg_msginfo_set_flags(msginfo, MSG_REPLIED, 0);
1801 procmsg_msginfo_set_flags(msginfo, MSG_FORWARDED, 0);
1803 procmsg_msginfo_free(msginfo);
1811 slist_free_strings_full(to_list);
1812 slist_free_strings_full(newsgroup_list);
1813 g_free(savecopyfolder);
1814 g_free(replymessageid);
1815 g_free(fwdmessageid);
1817 return (newsval != 0 ? newsval : mailval);
1820 gint procmsg_send_message_queue(const gchar *file, gchar **errstr, FolderItem *queue, gint msgnum, gboolean *queued_removed)
1822 gint result = procmsg_send_message_queue_full(file, FALSE, errstr, queue, msgnum, queued_removed);
1823 main_window_set_menu_sensitive(mainwindow_get_mainwindow());
1824 toolbar_main_set_sensitive(mainwindow_get_mainwindow());
1828 gint procmsg_send_message_queue_with_lock(const gchar *file, gchar **errstr, FolderItem *queue, gint msgnum, gboolean *queued_removed)
1831 if (procmsg_queue_lock(errstr)) {
1832 val = procmsg_send_message_queue(file, errstr, queue, msgnum, queued_removed);
1833 procmsg_queue_unlock();
1839 static void update_folder_msg_counts(FolderItem *item, MsgInfo *msginfo, MsgPermFlags old_flags)
1841 MsgPermFlags new_flags = msginfo->flags.perm_flags;
1844 if (!(old_flags & MSG_NEW) && (new_flags & MSG_NEW)) {
1848 if ((old_flags & MSG_NEW) && !(new_flags & MSG_NEW)) {
1853 if (!(old_flags & MSG_UNREAD) && (new_flags & MSG_UNREAD)) {
1854 item->unread_msgs++;
1855 if (procmsg_msg_has_marked_parent(msginfo))
1856 item->unreadmarked_msgs++;
1859 if ((old_flags & MSG_UNREAD) && !(new_flags & MSG_UNREAD)) {
1860 item->unread_msgs--;
1861 if (procmsg_msg_has_marked_parent(msginfo))
1862 item->unreadmarked_msgs--;
1866 if (!(old_flags & MSG_MARKED) && (new_flags & MSG_MARKED)) {
1867 procmsg_update_unread_children(msginfo, TRUE);
1868 item->marked_msgs++;
1871 if ((old_flags & MSG_MARKED) && !(new_flags & MSG_MARKED)) {
1872 procmsg_update_unread_children(msginfo, FALSE);
1873 item->marked_msgs--;
1876 if (!(old_flags & MSG_REPLIED) && (new_flags & MSG_REPLIED)) {
1877 item->replied_msgs++;
1880 if ((old_flags & MSG_REPLIED) && !(new_flags & MSG_REPLIED)) {
1881 item->replied_msgs--;
1884 if (!(old_flags & MSG_FORWARDED) && (new_flags & MSG_FORWARDED)) {
1885 item->forwarded_msgs++;
1888 if ((old_flags & MSG_FORWARDED) && !(new_flags & MSG_FORWARDED)) {
1889 item->forwarded_msgs--;
1892 if (!(old_flags & MSG_LOCKED) && (new_flags & MSG_LOCKED)) {
1893 item->locked_msgs++;
1896 if ((old_flags & MSG_LOCKED) && !(new_flags & MSG_LOCKED)) {
1897 item->locked_msgs--;
1900 if ((old_flags & MSG_IGNORE_THREAD) && !(new_flags & MSG_IGNORE_THREAD)) {
1901 item->ignored_msgs--;
1904 if (!(old_flags & MSG_IGNORE_THREAD) && (new_flags & MSG_IGNORE_THREAD)) {
1905 item->ignored_msgs++;
1908 if ((old_flags & MSG_WATCH_THREAD) && !(new_flags & MSG_WATCH_THREAD)) {
1909 item->watched_msgs--;
1912 if (!(old_flags & MSG_WATCH_THREAD) && (new_flags & MSG_WATCH_THREAD)) {
1913 item->watched_msgs++;
1917 void procmsg_msginfo_set_flags(MsgInfo *msginfo, MsgPermFlags perm_flags, MsgTmpFlags tmp_flags)
1920 MsgInfoUpdate msginfo_update;
1921 MsgPermFlags perm_flags_new, perm_flags_old;
1922 MsgTmpFlags tmp_flags_old;
1924 cm_return_if_fail(msginfo != NULL);
1925 item = msginfo->folder;
1926 cm_return_if_fail(item != NULL);
1928 debug_print("Setting flags for message %d in folder %s\n", msginfo->msgnum, item->path);
1930 /* Perm Flags handling */
1931 perm_flags_old = msginfo->flags.perm_flags;
1932 perm_flags_new = msginfo->flags.perm_flags | perm_flags;
1933 if ((perm_flags & MSG_IGNORE_THREAD) || (perm_flags_old & MSG_IGNORE_THREAD)) {
1934 perm_flags_new &= ~(MSG_NEW | MSG_UNREAD);
1936 if ((perm_flags & MSG_WATCH_THREAD) || (perm_flags_old & MSG_WATCH_THREAD)) {
1937 perm_flags_new &= ~(MSG_IGNORE_THREAD);
1940 if (perm_flags_old != perm_flags_new) {
1941 folder_item_change_msg_flags(msginfo->folder, msginfo, perm_flags_new);
1943 update_folder_msg_counts(item, msginfo, perm_flags_old);
1944 summary_update_unread(mainwindow_get_mainwindow()->summaryview, NULL);
1947 /* Tmp flags handling */
1948 tmp_flags_old = msginfo->flags.tmp_flags;
1949 msginfo->flags.tmp_flags |= tmp_flags;
1951 /* update notification */
1952 if ((perm_flags_old != perm_flags_new) || (tmp_flags_old != msginfo->flags.tmp_flags)) {
1953 msginfo_update.msginfo = msginfo;
1954 msginfo_update.flags = MSGINFO_UPDATE_FLAGS;
1955 hooks_invoke(MSGINFO_UPDATE_HOOKLIST, &msginfo_update);
1956 folder_item_update(msginfo->folder, F_ITEM_UPDATE_MSGCNT);
1960 void procmsg_msginfo_unset_flags(MsgInfo *msginfo, MsgPermFlags perm_flags, MsgTmpFlags tmp_flags)
1963 MsgInfoUpdate msginfo_update;
1964 MsgPermFlags perm_flags_new, perm_flags_old;
1965 MsgTmpFlags tmp_flags_old;
1967 cm_return_if_fail(msginfo != NULL);
1968 item = msginfo->folder;
1969 cm_return_if_fail(item != NULL);
1971 debug_print("Unsetting flags for message %d in folder %s\n", msginfo->msgnum, item->path);
1973 /* Perm Flags handling */
1974 perm_flags_old = msginfo->flags.perm_flags;
1975 perm_flags_new = msginfo->flags.perm_flags & ~perm_flags;
1977 if (perm_flags_old != perm_flags_new) {
1978 folder_item_change_msg_flags(msginfo->folder, msginfo, perm_flags_new);
1980 update_folder_msg_counts(item, msginfo, perm_flags_old);
1983 /* Tmp flags hanlding */
1984 tmp_flags_old = msginfo->flags.tmp_flags;
1985 msginfo->flags.tmp_flags &= ~tmp_flags;
1987 /* update notification */
1988 if ((perm_flags_old != perm_flags_new) || (tmp_flags_old != msginfo->flags.tmp_flags)) {
1989 msginfo_update.msginfo = msginfo;
1990 msginfo_update.flags = MSGINFO_UPDATE_FLAGS;
1991 hooks_invoke(MSGINFO_UPDATE_HOOKLIST, &msginfo_update);
1992 folder_item_update(msginfo->folder, F_ITEM_UPDATE_MSGCNT);
1996 void procmsg_msginfo_change_flags(MsgInfo *msginfo,
1997 MsgPermFlags add_perm_flags, MsgTmpFlags add_tmp_flags,
1998 MsgPermFlags rem_perm_flags, MsgTmpFlags rem_tmp_flags)
2001 MsgInfoUpdate msginfo_update;
2002 MsgPermFlags perm_flags_new, perm_flags_old;
2003 MsgTmpFlags tmp_flags_old;
2005 cm_return_if_fail(msginfo != NULL);
2006 item = msginfo->folder;
2007 cm_return_if_fail(item != NULL);
2009 debug_print("Changing flags for message %d in folder %s\n", msginfo->msgnum, item->path);
2011 /* Perm Flags handling */
2012 perm_flags_old = msginfo->flags.perm_flags;
2013 perm_flags_new = (msginfo->flags.perm_flags & ~rem_perm_flags) | add_perm_flags;
2014 if ((add_perm_flags & MSG_IGNORE_THREAD) || (perm_flags_old & MSG_IGNORE_THREAD)) {
2015 perm_flags_new &= ~(MSG_NEW | MSG_UNREAD);
2017 if ((add_perm_flags & MSG_WATCH_THREAD) || (perm_flags_old & MSG_WATCH_THREAD)) {
2018 perm_flags_new &= ~(MSG_IGNORE_THREAD);
2021 if (perm_flags_old != perm_flags_new) {
2022 folder_item_change_msg_flags(msginfo->folder, msginfo, perm_flags_new);
2024 update_folder_msg_counts(item, msginfo, perm_flags_old);
2028 /* Tmp flags handling */
2029 tmp_flags_old = msginfo->flags.tmp_flags;
2030 msginfo->flags.tmp_flags &= ~rem_tmp_flags;
2031 msginfo->flags.tmp_flags |= add_tmp_flags;
2033 /* update notification */
2034 if ((perm_flags_old != perm_flags_new) || (tmp_flags_old != msginfo->flags.tmp_flags)) {
2035 msginfo_update.msginfo = msginfo;
2036 msginfo_update.flags = MSGINFO_UPDATE_FLAGS;
2037 hooks_invoke(MSGINFO_UPDATE_HOOKLIST, &msginfo_update);
2038 folder_item_update(msginfo->folder, F_ITEM_UPDATE_MSGCNT);
2043 *\brief check for flags (e.g. mark) in prior msgs of current thread
2045 *\param info Current message
2046 *\param perm_flags Flags to be checked
2047 *\param parentmsgs Hash of prior msgs to avoid loops
2049 *\return gboolean TRUE if perm_flags are found
2051 static gboolean procmsg_msg_has_flagged_parent_real(MsgInfo *info,
2052 MsgPermFlags perm_flags, GHashTable *parentmsgs)
2056 cm_return_val_if_fail(info != NULL, FALSE);
2058 if (info != NULL && info->folder != NULL && info->inreplyto != NULL) {
2059 tmp = folder_item_get_msginfo_by_msgid(info->folder,
2061 if (tmp && (tmp->flags.perm_flags & perm_flags)) {
2062 procmsg_msginfo_free(tmp);
2064 } else if (tmp != NULL) {
2067 if (g_hash_table_lookup(parentmsgs, info)) {
2068 debug_print("loop detected: %d\n",
2072 g_hash_table_insert(parentmsgs, info, "1");
2073 result = procmsg_msg_has_flagged_parent_real(
2074 tmp, perm_flags, parentmsgs);
2076 procmsg_msginfo_free(tmp);
2086 *\brief Callback for cleaning up hash of parentmsgs
2088 static gboolean parentmsgs_hash_remove(gpointer key,
2096 *\brief Set up list of parentmsgs
2097 * See procmsg_msg_has_flagged_parent_real()
2099 gboolean procmsg_msg_has_flagged_parent(MsgInfo *info, MsgPermFlags perm_flags)
2102 static GHashTable *parentmsgs = NULL;
2104 if (parentmsgs == NULL)
2105 parentmsgs = g_hash_table_new(NULL, NULL);
2107 result = procmsg_msg_has_flagged_parent_real(info, perm_flags, parentmsgs);
2108 g_hash_table_foreach_remove(parentmsgs, parentmsgs_hash_remove, NULL);
2114 *\brief Check if msgs prior in thread are marked
2115 * See procmsg_msg_has_flagged_parent_real()
2117 gboolean procmsg_msg_has_marked_parent(MsgInfo *info)
2119 return procmsg_msg_has_flagged_parent(info, MSG_MARKED);
2123 static GSList *procmsg_find_children_func(MsgInfo *info,
2124 GSList *children, GSList *all)
2128 cm_return_val_if_fail(info!=NULL, children);
2129 if (info->msgid == NULL)
2132 for (cur = all; cur != NULL; cur = g_slist_next(cur)) {
2133 MsgInfo *tmp = (MsgInfo *)cur->data;
2134 if (tmp->inreplyto && !strcmp(tmp->inreplyto, info->msgid)) {
2135 /* Check if message is already in the list */
2136 if ((children == NULL) ||
2137 (g_slist_index(children, tmp) == -1)) {
2138 children = g_slist_prepend(children,
2139 procmsg_msginfo_new_ref(tmp));
2140 children = procmsg_find_children_func(tmp,
2149 static GSList *procmsg_find_children (MsgInfo *info)
2154 cm_return_val_if_fail(info!=NULL, NULL);
2155 all = folder_item_get_msg_list(info->folder);
2156 children = procmsg_find_children_func(info, NULL, all);
2157 if (children != NULL) {
2158 for (cur = all; cur != NULL; cur = g_slist_next(cur)) {
2159 /* this will not free the used pointers
2160 created with procmsg_msginfo_new_ref */
2161 procmsg_msginfo_free((MsgInfo *)cur->data);
2169 static void procmsg_update_unread_children(MsgInfo *info, gboolean newly_marked)
2171 GSList *children = procmsg_find_children(info);
2173 for (cur = children; cur != NULL; cur = g_slist_next(cur)) {
2174 MsgInfo *tmp = (MsgInfo *)cur->data;
2175 if(MSG_IS_UNREAD(tmp->flags) && !MSG_IS_IGNORE_THREAD(tmp->flags)) {
2177 info->folder->unreadmarked_msgs++;
2179 info->folder->unreadmarked_msgs--;
2180 folder_item_update(info->folder, F_ITEM_UPDATE_MSGCNT);
2182 procmsg_msginfo_free(tmp);
2184 g_slist_free(children);
2188 * Set the destination folder for a copy or move operation
2190 * \param msginfo The message which's destination folder is changed
2191 * \param to_folder The destination folder for the operation
2193 void procmsg_msginfo_set_to_folder(MsgInfo *msginfo, FolderItem *to_folder)
2195 if(msginfo->to_folder != NULL) {
2196 msginfo->to_folder->op_count--;
2197 folder_item_update(msginfo->to_folder, F_ITEM_UPDATE_MSGCNT);
2199 msginfo->to_folder = to_folder;
2200 if(to_folder != NULL) {
2201 to_folder->op_count++;
2202 folder_item_update(msginfo->to_folder, F_ITEM_UPDATE_MSGCNT);
2207 * Apply filtering actions to the msginfo
2209 * \param msginfo The MsgInfo describing the message that should be filtered
2210 * \return TRUE if the message was moved and MsgInfo is now invalid,
2213 static gboolean procmsg_msginfo_filter(MsgInfo *msginfo, PrefsAccount* ac_prefs)
2215 MailFilteringData mail_filtering_data;
2217 mail_filtering_data.msginfo = msginfo;
2218 mail_filtering_data.msglist = NULL;
2219 mail_filtering_data.filtered = NULL;
2220 mail_filtering_data.unfiltered = NULL;
2221 mail_filtering_data.account = ac_prefs;
2223 if (!ac_prefs || ac_prefs->filterhook_on_recv)
2224 if (hooks_invoke(MAIL_FILTERING_HOOKLIST, &mail_filtering_data))
2227 /* filter if enabled in prefs or move to inbox if not */
2228 if((filtering_rules != NULL) &&
2229 filter_message_by_msginfo(filtering_rules, msginfo, ac_prefs,
2230 FILTERING_INCORPORATION, NULL)) {
2237 void procmsg_msglist_filter(GSList *list, PrefsAccount *ac,
2238 GSList **filtered, GSList **unfiltered,
2241 GSList *cur, *to_do = NULL;
2242 gint total = 0, curnum = 0;
2243 MailFilteringData mail_filtering_data;
2245 cm_return_if_fail(filtered != NULL);
2246 cm_return_if_fail(unfiltered != NULL);
2254 total = g_slist_length(list);
2258 *unfiltered = g_slist_copy(list);
2262 statusbar_print_all(_("Filtering messages...\n"));
2264 mail_filtering_data.msginfo = NULL;
2265 mail_filtering_data.msglist = list;
2266 mail_filtering_data.filtered = NULL;
2267 mail_filtering_data.unfiltered = NULL;
2268 mail_filtering_data.account = ac;
2270 if (!ac || ac->filterhook_on_recv)
2271 hooks_invoke(MAIL_LISTFILTERING_HOOKLIST, &mail_filtering_data);
2273 if (mail_filtering_data.filtered == NULL &&
2274 mail_filtering_data.unfiltered == NULL) {
2275 /* nothing happened */
2276 debug_print(MAIL_LISTFILTERING_HOOKLIST " did nothing. filtering whole list normally.\n");
2279 if (mail_filtering_data.filtered != NULL) {
2280 /* keep track of what's been filtered by the hooks */
2281 debug_print(MAIL_LISTFILTERING_HOOKLIST " filtered some stuff. total %d filtered %d unfilt %d.\n",
2282 g_slist_length(list),
2283 g_slist_length(mail_filtering_data.filtered),
2284 g_slist_length(mail_filtering_data.unfiltered));
2286 *filtered = g_slist_copy(mail_filtering_data.filtered);
2288 if (mail_filtering_data.unfiltered != NULL) {
2289 /* what the hooks didn't handle will go in filtered or
2290 * unfiltered in the next loop */
2291 debug_print(MAIL_LISTFILTERING_HOOKLIST " left unfiltered stuff. total %d filtered %d unfilt %d.\n",
2292 g_slist_length(list),
2293 g_slist_length(mail_filtering_data.filtered),
2294 g_slist_length(mail_filtering_data.unfiltered));
2295 to_do = mail_filtering_data.unfiltered;
2298 for (cur = to_do; cur; cur = cur->next) {
2299 MsgInfo *info = (MsgInfo *)cur->data;
2300 if (procmsg_msginfo_filter(info, ac))
2301 *filtered = g_slist_prepend(*filtered, info);
2303 *unfiltered = g_slist_prepend(*unfiltered, info);
2304 statusbar_progress_all(curnum++, total, prefs_common.statusbar_update_step);
2307 g_slist_free(mail_filtering_data.filtered);
2308 g_slist_free(mail_filtering_data.unfiltered);
2310 *filtered = g_slist_reverse(*filtered);
2311 *unfiltered = g_slist_reverse(*unfiltered);
2313 statusbar_progress_all(0,0,0);
2314 statusbar_pop_all();
2317 MsgInfo *procmsg_msginfo_new_from_mimeinfo(MsgInfo *src_msginfo, MimeInfo *mimeinfo)
2319 MsgInfo *tmp_msginfo = NULL;
2320 MsgFlags flags = {0, 0};
2321 gchar *tmpfile = get_tmp_file();
2322 FILE *fp = g_fopen(tmpfile, "wb");
2324 if (!mimeinfo || mimeinfo->type != MIMETYPE_MESSAGE ||
2325 g_ascii_strcasecmp(mimeinfo->subtype, "rfc822")) {
2326 g_warning("procmsg_msginfo_new_from_mimeinfo(): unsuitable mimeinfo");
2333 if (fp && procmime_write_mimeinfo(mimeinfo, fp) >= 0) {
2336 tmp_msginfo = procheader_parse_file(
2343 if (tmp_msginfo != NULL) {
2345 tmp_msginfo->folder = src_msginfo->folder;
2346 tmp_msginfo->plaintext_file = g_strdup(tmpfile);
2348 g_warning("procmsg_msginfo_new_from_mimeinfo(): Can't generate new msginfo");
2356 static GSList *spam_learners = NULL;
2358 void procmsg_register_spam_learner (int (*learn_func)(MsgInfo *info, GSList *list, gboolean spam))
2360 if (!g_slist_find(spam_learners, learn_func))
2361 spam_learners = g_slist_append(spam_learners, learn_func);
2362 if (mainwindow_get_mainwindow()) {
2363 main_window_set_menu_sensitive(mainwindow_get_mainwindow());
2364 summary_set_menu_sensitive(
2365 mainwindow_get_mainwindow()->summaryview);
2366 toolbar_main_set_sensitive(mainwindow_get_mainwindow());
2370 void procmsg_unregister_spam_learner (int (*learn_func)(MsgInfo *info, GSList *list, gboolean spam))
2372 spam_learners = g_slist_remove(spam_learners, learn_func);
2373 if (mainwindow_get_mainwindow()) {
2374 main_window_set_menu_sensitive(mainwindow_get_mainwindow());
2375 summary_set_menu_sensitive(
2376 mainwindow_get_mainwindow()->summaryview);
2377 toolbar_main_set_sensitive(mainwindow_get_mainwindow());
2381 gboolean procmsg_spam_can_learn(void)
2383 return g_slist_length(spam_learners) > 0;
2386 int procmsg_spam_learner_learn (MsgInfo *info, GSList *list, gboolean spam)
2388 GSList *cur = spam_learners;
2390 for (; cur; cur = cur->next) {
2391 int ((*func)(MsgInfo *info, GSList *list, gboolean spam)) = cur->data;
2392 ret |= func(info, list, spam);
2397 static gchar *spam_folder_item = NULL;
2398 static FolderItem * (*procmsg_spam_get_folder_func)(MsgInfo *msginfo) = NULL;
2399 void procmsg_spam_set_folder (const char *item_identifier, FolderItem *(*spam_get_folder_func)(MsgInfo *info))
2401 g_free(spam_folder_item);
2402 if (item_identifier)
2403 spam_folder_item = g_strdup(item_identifier);
2405 spam_folder_item = NULL;
2406 if (spam_get_folder_func != NULL)
2407 procmsg_spam_get_folder_func = spam_get_folder_func;
2409 procmsg_spam_get_folder_func = NULL;
2412 FolderItem *procmsg_spam_get_folder (MsgInfo *msginfo)
2414 FolderItem *item = NULL;
2416 if (procmsg_spam_get_folder_func)
2417 item = procmsg_spam_get_folder_func(msginfo);
2418 if (item == NULL && spam_folder_item)
2419 item = folder_find_item_from_identifier(spam_folder_item);
2421 item = folder_get_default_trash();
2425 static void item_has_queued_mails(FolderItem *item, gpointer data)
2427 gboolean *result = (gboolean *)data;
2428 if (*result == TRUE)
2430 if (folder_has_parent_of_type(item, F_QUEUE)) {
2431 if (item->total_msgs == 0)
2434 GSList *msglist = folder_item_get_msg_list(item);
2436 for (cur = msglist; cur; cur = cur->next) {
2437 MsgInfo *msginfo = (MsgInfo *)cur->data;
2438 if (!MSG_IS_DELETED(msginfo->flags) &&
2439 !MSG_IS_LOCKED(msginfo->flags)) {
2444 procmsg_msg_list_free(msglist);
2449 gboolean procmsg_have_queued_mails_fast (void)
2451 gboolean result = FALSE;
2452 folder_func_to_all_folders(item_has_queued_mails, &result);
2456 static void item_has_trashed_mails(FolderItem *item, gpointer data)
2458 gboolean *result = (gboolean *)data;
2459 if (*result == TRUE)
2461 if (folder_has_parent_of_type(item, F_TRASH) && item->total_msgs > 0)
2465 gboolean procmsg_have_trashed_mails_fast (void)
2467 gboolean result = FALSE;
2468 folder_func_to_all_folders(item_has_trashed_mails, &result);
2472 gchar *procmsg_msginfo_get_tags_str(MsgInfo *msginfo)
2480 if (msginfo->tags == NULL)
2482 for (cur = msginfo->tags; cur; cur = cur->next) {
2483 const gchar *tag = tags_get_tag(GPOINTER_TO_INT(cur->data));
2487 tags = g_strdup(tag);
2489 int olen = strlen(tags);
2490 int nlen = olen + strlen(tag) + 2 /* strlen(", ") */;
2491 tags = g_realloc(tags, nlen+1);
2494 strcpy(tags+olen, ", ");
2495 strcpy(tags+olen+2, tag);
2502 void procmsg_msginfo_update_tags(MsgInfo *msginfo, gboolean set, gint id)
2510 msginfo->tags = g_slist_remove(
2512 GINT_TO_POINTER(id));
2513 changed.data = GINT_TO_POINTER(id);
2514 changed.next = NULL;
2515 folder_item_commit_tags(msginfo->folder, msginfo, NULL, &changed);
2517 if (!g_slist_find(msginfo->tags, GINT_TO_POINTER(id))) {
2518 msginfo->tags = g_slist_append(
2520 GINT_TO_POINTER(id));
2522 changed.data = GINT_TO_POINTER(id);
2523 changed.next = NULL;
2524 folder_item_commit_tags(msginfo->folder, msginfo, &changed, NULL);
2529 void procmsg_msginfo_clear_tags(MsgInfo *msginfo)
2531 GSList *unset = msginfo->tags;
2532 msginfo->tags = NULL;
2533 folder_item_commit_tags(msginfo->folder, msginfo, NULL, unset);
2534 g_slist_free(unset);