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);
540 ret = !folder_item_is_msg_changed(msginfo->folder, msginfo);
546 void procmsg_get_filter_keyword(MsgInfo *msginfo, gchar **header, gchar **key,
547 PrefsFilterType type)
549 static HeaderEntry hentry[] = {{"X-BeenThere:", NULL, TRUE},
550 {"X-ML-Name:", NULL, TRUE},
551 {"X-List:", NULL, TRUE},
552 {"X-Mailing-list:", NULL, TRUE},
553 {"List-Id:", NULL, TRUE},
554 {"X-Sequence:", NULL, TRUE},
555 {"Sender:", NULL, TRUE},
556 {"List-Post:", NULL, TRUE},
557 {NULL, NULL, FALSE}};
563 H_X_MAILING_LIST = 3,
572 cm_return_if_fail(msginfo != NULL);
573 cm_return_if_fail(header != NULL);
574 cm_return_if_fail(key != NULL);
583 if ((fp = procmsg_open_message(msginfo)) == NULL)
585 procheader_get_header_fields(fp, hentry);
588 #define SET_FILTER_KEY(hstr, idx) \
590 *header = g_strdup(hstr); \
591 *key = hentry[idx].body; \
592 hentry[idx].body = NULL; \
595 if (hentry[H_LIST_ID].body != NULL) {
596 SET_FILTER_KEY("header \"List-Id\"", H_LIST_ID);
597 extract_list_id_str(*key);
598 } else if (hentry[H_X_BEENTHERE].body != NULL) {
599 SET_FILTER_KEY("header \"X-BeenThere\"", H_X_BEENTHERE);
600 } else if (hentry[H_X_ML_NAME].body != NULL) {
601 SET_FILTER_KEY("header \"X-ML-Name\"", H_X_ML_NAME);
602 } else if (hentry[H_X_LIST].body != NULL) {
603 SET_FILTER_KEY("header \"X-List\"", H_X_LIST);
604 } else if (hentry[H_X_MAILING_LIST].body != NULL) {
605 SET_FILTER_KEY("header \"X-Mailing-List\"", H_X_MAILING_LIST);
606 } else if (hentry[H_X_SEQUENCE].body != NULL) {
609 SET_FILTER_KEY("X-Sequence", H_X_SEQUENCE);
612 while (*p != '\0' && !g_ascii_isspace(*p)) p++;
613 while (g_ascii_isspace(*p)) p++;
614 if (g_ascii_isdigit(*p)) {
620 } else if (hentry[H_SENDER].body != NULL) {
621 SET_FILTER_KEY("header \"Sender\"", H_SENDER);
622 } else if (hentry[H_LIST_POST].body != NULL) {
623 SET_FILTER_KEY("header \"List-Post\"", H_LIST_POST);
624 } else if (msginfo->to) {
625 *header = g_strdup("to");
626 *key = g_strdup(msginfo->to);
627 } else if (msginfo->subject) {
628 *header = g_strdup("subject");
629 *key = g_strdup(msginfo->subject);
632 #undef SET_FILTER_KEY
634 g_free(hentry[H_X_BEENTHERE].body);
635 hentry[H_X_BEENTHERE].body = NULL;
636 g_free(hentry[H_X_ML_NAME].body);
637 hentry[H_X_ML_NAME].body = NULL;
638 g_free(hentry[H_X_LIST].body);
639 hentry[H_X_LIST].body = NULL;
640 g_free(hentry[H_X_MAILING_LIST].body);
641 hentry[H_X_MAILING_LIST].body = NULL;
642 g_free(hentry[H_LIST_ID].body);
643 hentry[H_LIST_ID].body = NULL;
644 g_free(hentry[H_SENDER].body);
645 hentry[H_SENDER].body = NULL;
646 g_free(hentry[H_LIST_POST].body);
647 hentry[H_LIST_POST].body = NULL;
651 *header = g_strdup("from");
652 *key = g_strdup(msginfo->from);
655 *header = g_strdup("to");
656 *key = g_strdup(msginfo->to);
658 case FILTER_BY_SUBJECT:
659 *header = g_strdup("subject");
660 *key = g_strdup(msginfo->subject);
667 static void procmsg_empty_trash(FolderItem *trash)
672 (trash->stype != F_TRASH &&
673 !folder_has_parent_of_type(trash, F_TRASH)))
676 if (trash && trash->total_msgs > 0) {
677 GSList *mlist = folder_item_get_msg_list(trash);
679 for (cur = mlist ; cur != NULL ; cur = cur->next) {
680 MsgInfo * msginfo = (MsgInfo *) cur->data;
681 if (MSG_IS_LOCKED(msginfo->flags)) {
682 procmsg_msginfo_free(&msginfo);
685 if (msginfo->total_size != 0 &&
686 msginfo->size != (off_t)msginfo->total_size)
687 partial_mark_for_delete(msginfo);
689 procmsg_msginfo_free(&msginfo);
692 folder_item_remove_all_msg(trash);
695 if (!trash->node || !trash->node->children)
698 node = trash->node->children;
699 while (node != NULL) {
701 procmsg_empty_trash(FOLDER_ITEM(node->data));
706 void procmsg_empty_all_trash(void)
711 for (cur = folder_get_list(); cur != NULL; cur = cur->next) {
712 Folder *folder = FOLDER(cur->data);
713 trash = folder->trash;
714 procmsg_empty_trash(trash);
715 if (folder->account && folder->account->set_trash_folder &&
716 folder_find_item_from_identifier(folder->account->trash_folder))
718 folder_find_item_from_identifier(folder->account->trash_folder));
722 static PrefsAccount *procmsg_get_account_from_file(const gchar *file)
724 PrefsAccount *mailac = NULL;
728 static HeaderEntry qentry[] = {{"S:", NULL, FALSE},
729 {"SSV:", NULL, FALSE},
731 {"NG:", NULL, FALSE},
732 {"MAID:", NULL, FALSE},
733 {"NAID:", NULL, FALSE},
734 {"SCF:", NULL, FALSE},
735 {"RMID:", NULL, FALSE},
736 {"FMID:", NULL, FALSE},
737 {"X-Claws-Privacy-System:", NULL, FALSE},
738 {"X-Claws-Encrypt:", NULL, FALSE},
739 {"X-Claws-Encrypt-Data:", NULL, FALSE},
740 {"X-Claws-End-Special-Headers", NULL, FALSE},
741 {"X-Sylpheed-Privacy-System:", NULL, FALSE},
742 {"X-Sylpheed-Encrypt:", NULL, FALSE},
743 {"X-Sylpheed-Encrypt-Data:", NULL, FALSE},
744 {NULL, NULL, FALSE}};
746 cm_return_val_if_fail(file != NULL, NULL);
748 if ((fp = g_fopen(file, "rb")) == NULL) {
749 FILE_OP_ERROR(file, "fopen");
753 while ((hnum = procheader_get_one_field(&buf, fp, qentry)) != -1 && buf != NULL) {
754 gchar *p = buf + strlen(qentry[hnum].name);
756 if (hnum == Q_MAIL_ACCOUNT_ID) {
757 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 #define FREENULL(n) { g_free(n); n = NULL; }
1377 void procmsg_msginfo_free(MsgInfo **msginfo_ptr)
1379 MsgInfo *msginfo = *msginfo_ptr;
1381 if (msginfo == NULL) return;
1384 if (msginfo->refcnt > 0)
1387 if (msginfo->to_folder) {
1388 msginfo->to_folder->op_count--;
1389 folder_item_update(msginfo->to_folder, F_ITEM_UPDATE_MSGCNT);
1392 FREENULL(msginfo->fromspace);
1394 FREENULL(msginfo->fromname);
1396 FREENULL(msginfo->date);
1397 FREENULL(msginfo->from);
1398 FREENULL(msginfo->to);
1399 FREENULL(msginfo->cc);
1400 FREENULL(msginfo->newsgroups);
1401 FREENULL(msginfo->subject);
1402 FREENULL(msginfo->msgid);
1403 FREENULL(msginfo->inreplyto);
1404 FREENULL(msginfo->xref);
1406 if (msginfo->extradata) {
1407 if (msginfo->extradata->avatars) {
1408 g_slist_foreach(msginfo->extradata->avatars,
1409 (GFunc)procmsg_msginfoavatar_free,
1411 g_slist_free(msginfo->extradata->avatars);
1412 msginfo->extradata->avatars = NULL;
1414 FREENULL(msginfo->extradata->returnreceiptto);
1415 FREENULL(msginfo->extradata->dispositionnotificationto);
1416 FREENULL(msginfo->extradata->list_post);
1417 FREENULL(msginfo->extradata->list_subscribe);
1418 FREENULL(msginfo->extradata->list_unsubscribe);
1419 FREENULL(msginfo->extradata->list_help);
1420 FREENULL(msginfo->extradata->list_archive);
1421 FREENULL(msginfo->extradata->list_owner);
1422 FREENULL(msginfo->extradata->partial_recv);
1423 FREENULL(msginfo->extradata->account_server);
1424 FREENULL(msginfo->extradata->account_login);
1425 FREENULL(msginfo->extradata->resent_from);
1426 FREENULL(msginfo->extradata);
1428 slist_free_strings_full(msginfo->references);
1429 msginfo->references = NULL;
1430 g_slist_free(msginfo->tags);
1431 msginfo->tags = NULL;
1433 FREENULL(msginfo->plaintext_file);
1436 *msginfo_ptr = NULL;
1440 guint procmsg_msginfo_memusage(MsgInfo *msginfo)
1445 memusage += sizeof(MsgInfo);
1446 if (msginfo->fromname)
1447 memusage += strlen(msginfo->fromname);
1449 memusage += strlen(msginfo->date);
1451 memusage += strlen(msginfo->from);
1453 memusage += strlen(msginfo->to);
1455 memusage += strlen(msginfo->cc);
1456 if (msginfo->newsgroups)
1457 memusage += strlen(msginfo->newsgroups);
1458 if (msginfo->subject)
1459 memusage += strlen(msginfo->subject);
1461 memusage += strlen(msginfo->msgid);
1462 if (msginfo->inreplyto)
1463 memusage += strlen(msginfo->inreplyto);
1465 for (tmp = msginfo->references; tmp; tmp=tmp->next) {
1466 gchar *r = (gchar *)tmp->data;
1467 memusage += r?strlen(r):0 + sizeof(GSList);
1469 if (msginfo->fromspace)
1470 memusage += strlen(msginfo->fromspace);
1472 for (tmp = msginfo->tags; tmp; tmp=tmp->next) {
1473 memusage += sizeof(GSList);
1475 if (msginfo->extradata) {
1476 memusage += sizeof(MsgInfoExtraData);
1477 if (msginfo->extradata->avatars) {
1478 for (tmp = msginfo->extradata->avatars; tmp; tmp = tmp->next) {
1479 MsgInfoAvatar *avt = (MsgInfoAvatar *)tmp->data;
1480 memusage += (avt->avatar_src)? strlen(avt->avatar_src): 0;
1481 memusage += sizeof(MsgInfoAvatar) + sizeof(GSList);
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);
1495 if (msginfo->extradata->resent_from)
1496 memusage += strlen(msginfo->extradata->resent_from);
1498 if (msginfo->extradata->list_post)
1499 memusage += strlen(msginfo->extradata->list_post);
1500 if (msginfo->extradata->list_subscribe)
1501 memusage += strlen(msginfo->extradata->list_subscribe);
1502 if (msginfo->extradata->list_unsubscribe)
1503 memusage += strlen(msginfo->extradata->list_unsubscribe);
1504 if (msginfo->extradata->list_help)
1505 memusage += strlen(msginfo->extradata->list_help);
1506 if (msginfo->extradata->list_archive)
1507 memusage += strlen(msginfo->extradata->list_archive);
1508 if (msginfo->extradata->list_owner)
1509 memusage += strlen(msginfo->extradata->list_owner);
1514 static gint procmsg_send_message_queue_full(const gchar *file, gboolean keep_session, gchar **errstr,
1515 FolderItem *queue, gint msgnum, gboolean *queued_removed)
1517 static HeaderEntry qentry[] = {
1518 {"S:", NULL, FALSE}, /* 0 */
1519 {"SSV:", NULL, FALSE},
1520 {"R:", NULL, FALSE},
1521 {"NG:", NULL, FALSE},
1522 {"MAID:", NULL, FALSE},
1523 {"NAID:", NULL, FALSE}, /* 5 */
1524 {"SCF:", NULL, FALSE},
1525 {"RMID:", NULL, FALSE},
1526 {"FMID:", NULL, FALSE},
1527 {"X-Claws-Privacy-System:", NULL, FALSE},
1528 {"X-Claws-Encrypt:", NULL, FALSE}, /* 10 */
1529 {"X-Claws-Encrypt-Data:", NULL, FALSE},
1530 {"X-Claws-End-Special-Headers:", NULL, FALSE},
1531 {"X-Sylpheed-Privacy-System:", NULL, FALSE},
1532 {"X-Sylpheed-Encrypt:", NULL, FALSE},
1533 {"X-Sylpheed-Encrypt-Data:", NULL, FALSE}, /* 15 */
1534 {"X-Sylpheed-End-Special-Headers:", NULL, FALSE},
1535 {NULL, NULL, FALSE}};
1538 gint mailval = 0, newsval = 0;
1540 gchar *smtpserver = NULL;
1541 GSList *to_list = NULL;
1542 GSList *newsgroup_list = NULL;
1543 gchar *savecopyfolder = NULL;
1544 gchar *replymessageid = NULL;
1545 gchar *fwdmessageid = NULL;
1548 PrefsAccount *mailac = NULL, *newsac = NULL;
1549 gboolean encrypt = FALSE;
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, fp, qentry)) != -1 && buf != NULL) {
1564 gchar *p = buf + strlen(qentry[hnum].name);
1572 if (smtpserver == NULL)
1573 smtpserver = g_strdup(p);
1576 to_list = address_list_append(to_list, p);
1579 newsgroup_list = newsgroup_list_append(newsgroup_list, p);
1581 case Q_MAIL_ACCOUNT_ID:
1582 mailac = account_find_from_id(atoi(p));
1584 case Q_NEWS_ACCOUNT_ID:
1585 newsac = account_find_from_id(atoi(p));
1587 case Q_SAVE_COPY_FOLDER:
1588 if (savecopyfolder == NULL)
1589 savecopyfolder = g_strdup(p);
1591 case Q_REPLY_MESSAGE_ID:
1592 if (replymessageid == NULL)
1593 replymessageid = g_strdup(p);
1595 case Q_FWD_MESSAGE_ID:
1596 if (fwdmessageid == NULL)
1597 fwdmessageid = g_strdup(p);
1605 case Q_CLAWS_HDRS_OLD:
1606 /* end of special headers reached */
1608 goto send_mail; /* can't "break;break;" */
1614 filepos = ftell(fp);
1616 FILE_OP_ERROR(file, "ftell");
1618 if (*errstr) g_free(*errstr);
1619 *errstr = g_strdup_printf(_("Couldn't open file %s."), file);
1625 debug_print("Sending message by mail\n");
1628 if (*errstr) g_free(*errstr);
1629 *errstr = g_strdup_printf(_("Queued message header is broken."));
1632 } else if (mailac && mailac->use_mail_command &&
1633 mailac->mail_command && (* mailac->mail_command)) {
1634 mailval = send_message_local(mailac->mail_command, fp);
1637 mailac = account_find_from_smtp_server(from, smtpserver);
1639 g_warning("Account not found. "
1640 "Using current account...");
1641 mailac = cur_account;
1646 mailval = send_message_smtp_full(mailac, to_list, fp, keep_session);
1647 if (mailval == -1 && errstr) {
1648 if (*errstr) g_free(*errstr);
1649 *errstr = g_strdup_printf(_("An error happened during SMTP session."));
1652 PrefsAccount tmp_ac;
1654 g_warning("Account not found.");
1656 memset(&tmp_ac, 0, sizeof(PrefsAccount));
1657 tmp_ac.address = from;
1658 tmp_ac.smtp_server = smtpserver;
1659 tmp_ac.smtpport = SMTP_PORT;
1660 mailval = send_message_smtp(&tmp_ac, to_list, fp);
1661 if (mailval == -1 && errstr) {
1662 if (*errstr) g_free(*errstr);
1663 *errstr = g_strdup_printf(_("No specific account has been found to "
1664 "send, and an error happened during SMTP session."));
1668 } else if (!to_list && !newsgroup_list) {
1670 if (*errstr) g_free(*errstr);
1671 *errstr = g_strdup(_("Couldn't determine sending information. "
1672 "Maybe the email hasn't been generated by Claws Mail."));
1677 if (fseek(fp, filepos, SEEK_SET) < 0) {
1678 FILE_OP_ERROR(file, "fseek");
1682 if (newsgroup_list && newsac && (mailval == 0)) {
1685 gchar buf[BUFFSIZE];
1688 /* write to temporary file */
1689 tmp = g_strdup_printf("%s%cnntp%p", get_tmp_dir(),
1690 G_DIR_SEPARATOR, file);
1691 if ((tmpfp = g_fopen(tmp, "wb")) == NULL) {
1692 FILE_OP_ERROR(tmp, "fopen");
1694 alertpanel_error(_("Couldn't create temporary file for news sending."));
1696 if (change_file_mode_rw(tmpfp, tmp) < 0) {
1697 FILE_OP_ERROR(tmp, "chmod");
1698 g_warning("can't change file mode");
1701 while ((newsval == 0) && fgets(buf, sizeof(buf), fp) != NULL) {
1702 if (fputs(buf, tmpfp) == EOF) {
1703 FILE_OP_ERROR(tmp, "fputs");
1706 if (*errstr) g_free(*errstr);
1707 *errstr = g_strdup_printf(_("Error when writing temporary file for news sending."));
1714 debug_print("Sending message by news\n");
1716 folder = FOLDER(newsac->folder);
1718 newsval = news_post(folder, tmp);
1719 if (newsval < 0 && errstr) {
1720 if (*errstr) g_free(*errstr);
1721 *errstr = g_strdup_printf(_("Error occurred while posting the message to %s."),
1722 newsac->nntp_server);
1732 /* update session statistics */
1733 if (mailval == 0 && newsval == 0) {
1734 /* update session stats */
1736 session_stats.replied++;
1737 else if (fwdmessageid)
1738 session_stats.forwarded++;
1740 session_stats.sent++;
1743 /* save message to outbox */
1744 if (mailval == 0 && newsval == 0 && savecopyfolder) {
1745 debug_print("saving sent message...\n");
1747 if (!encrypt || !mailac->save_encrypted_as_clear_text) {
1748 outbox = folder_find_item_from_identifier(savecopyfolder);
1750 outbox = folder_get_default_outbox();
1752 /* Mail was not saved to outbox before encrypting, save it now. */
1753 gboolean saved = FALSE;
1754 *queued_removed = FALSE;
1755 if (queue && msgnum > 0) {
1756 MsgInfo *queued_mail = folder_item_get_msginfo(queue, msgnum);
1757 if (folder_item_move_msg(outbox, queued_mail) >= 0) {
1758 debug_print("moved queued mail %d to sent folder\n", msgnum);
1760 *queued_removed = TRUE;
1761 } else if (folder_item_copy_msg(outbox, queued_mail) >= 0) {
1762 debug_print("copied queued mail %d to sent folder\n", msgnum);
1765 procmsg_msginfo_free(&queued_mail);
1768 debug_print("resaving queued mail to sent folder\n");
1769 procmsg_save_to_outbox(outbox, file, TRUE);
1774 if (replymessageid != NULL || fwdmessageid != NULL) {
1778 if (replymessageid != NULL)
1779 tokens = g_strsplit(replymessageid, "\t", 0);
1781 tokens = g_strsplit(fwdmessageid, "\t", 0);
1782 item = folder_find_item_from_identifier(tokens[0]);
1784 /* check if queued message has valid folder and message id */
1785 if (item != NULL && tokens[2] != NULL) {
1788 msginfo = folder_item_get_msginfo(item, atoi(tokens[1]));
1790 /* check if referring message exists and has a message id */
1791 if ((msginfo != NULL) &&
1792 (msginfo->msgid != NULL) &&
1793 (strcmp(msginfo->msgid, tokens[2]) != 0)) {
1794 procmsg_msginfo_free(&msginfo);
1798 if (msginfo == NULL) {
1799 msginfo = folder_item_get_msginfo_by_msgid(item, tokens[2]);
1802 if (msginfo != NULL) {
1803 if (replymessageid != NULL) {
1804 MsgPermFlags to_unset = 0;
1806 if (prefs_common.mark_as_read_on_new_window)
1807 to_unset = (MSG_NEW|MSG_UNREAD);
1809 procmsg_msginfo_unset_flags(msginfo, to_unset, 0);
1810 procmsg_msginfo_set_flags(msginfo, MSG_REPLIED, 0);
1812 procmsg_msginfo_set_flags(msginfo, MSG_FORWARDED, 0);
1814 procmsg_msginfo_free(&msginfo);
1822 slist_free_strings_full(to_list);
1823 slist_free_strings_full(newsgroup_list);
1824 g_free(savecopyfolder);
1825 g_free(replymessageid);
1826 g_free(fwdmessageid);
1828 return (newsval != 0 ? newsval : mailval);
1831 gint procmsg_send_message_queue(const gchar *file, gchar **errstr, FolderItem *queue, gint msgnum, gboolean *queued_removed)
1833 gint result = procmsg_send_message_queue_full(file, FALSE, errstr, queue, msgnum, queued_removed);
1834 main_window_set_menu_sensitive(mainwindow_get_mainwindow());
1835 toolbar_main_set_sensitive(mainwindow_get_mainwindow());
1839 gint procmsg_send_message_queue_with_lock(const gchar *file, gchar **errstr, FolderItem *queue, gint msgnum, gboolean *queued_removed)
1842 if (procmsg_queue_lock(errstr)) {
1843 val = procmsg_send_message_queue(file, errstr, queue, msgnum, queued_removed);
1844 procmsg_queue_unlock();
1850 static void update_folder_msg_counts(FolderItem *item, MsgInfo *msginfo, MsgPermFlags old_flags)
1852 MsgPermFlags new_flags = msginfo->flags.perm_flags;
1855 if (!(old_flags & MSG_NEW) && (new_flags & MSG_NEW)) {
1859 if ((old_flags & MSG_NEW) && !(new_flags & MSG_NEW)) {
1864 if (!(old_flags & MSG_UNREAD) && (new_flags & MSG_UNREAD)) {
1865 item->unread_msgs++;
1866 if (procmsg_msg_has_marked_parent(msginfo))
1867 item->unreadmarked_msgs++;
1870 if ((old_flags & MSG_UNREAD) && !(new_flags & MSG_UNREAD)) {
1871 item->unread_msgs--;
1872 if (procmsg_msg_has_marked_parent(msginfo))
1873 item->unreadmarked_msgs--;
1877 if (!(old_flags & MSG_MARKED) && (new_flags & MSG_MARKED)) {
1878 procmsg_update_unread_children(msginfo, TRUE);
1879 item->marked_msgs++;
1882 if ((old_flags & MSG_MARKED) && !(new_flags & MSG_MARKED)) {
1883 procmsg_update_unread_children(msginfo, FALSE);
1884 item->marked_msgs--;
1887 if (!(old_flags & MSG_REPLIED) && (new_flags & MSG_REPLIED)) {
1888 item->replied_msgs++;
1891 if ((old_flags & MSG_REPLIED) && !(new_flags & MSG_REPLIED)) {
1892 item->replied_msgs--;
1895 if (!(old_flags & MSG_FORWARDED) && (new_flags & MSG_FORWARDED)) {
1896 item->forwarded_msgs++;
1899 if ((old_flags & MSG_FORWARDED) && !(new_flags & MSG_FORWARDED)) {
1900 item->forwarded_msgs--;
1903 if (!(old_flags & MSG_LOCKED) && (new_flags & MSG_LOCKED)) {
1904 item->locked_msgs++;
1907 if ((old_flags & MSG_LOCKED) && !(new_flags & MSG_LOCKED)) {
1908 item->locked_msgs--;
1911 if ((old_flags & MSG_IGNORE_THREAD) && !(new_flags & MSG_IGNORE_THREAD)) {
1912 item->ignored_msgs--;
1915 if (!(old_flags & MSG_IGNORE_THREAD) && (new_flags & MSG_IGNORE_THREAD)) {
1916 item->ignored_msgs++;
1919 if ((old_flags & MSG_WATCH_THREAD) && !(new_flags & MSG_WATCH_THREAD)) {
1920 item->watched_msgs--;
1923 if (!(old_flags & MSG_WATCH_THREAD) && (new_flags & MSG_WATCH_THREAD)) {
1924 item->watched_msgs++;
1928 void procmsg_msginfo_set_flags(MsgInfo *msginfo, MsgPermFlags perm_flags, MsgTmpFlags tmp_flags)
1931 MsgInfoUpdate msginfo_update;
1932 MsgPermFlags perm_flags_new, perm_flags_old;
1933 MsgTmpFlags tmp_flags_old;
1935 cm_return_if_fail(msginfo != NULL);
1936 item = msginfo->folder;
1937 cm_return_if_fail(item != NULL);
1939 debug_print("Setting flags for message %d in folder %s\n", msginfo->msgnum, item->path);
1941 /* Perm Flags handling */
1942 perm_flags_old = msginfo->flags.perm_flags;
1943 perm_flags_new = msginfo->flags.perm_flags | perm_flags;
1944 if ((perm_flags & MSG_IGNORE_THREAD) || (perm_flags_old & MSG_IGNORE_THREAD)) {
1945 perm_flags_new &= ~(MSG_NEW | MSG_UNREAD);
1947 if ((perm_flags & MSG_WATCH_THREAD) || (perm_flags_old & MSG_WATCH_THREAD)) {
1948 perm_flags_new &= ~(MSG_IGNORE_THREAD);
1951 if (perm_flags_old != perm_flags_new) {
1952 folder_item_change_msg_flags(msginfo->folder, msginfo, perm_flags_new);
1954 update_folder_msg_counts(item, msginfo, perm_flags_old);
1955 summary_update_unread(mainwindow_get_mainwindow()->summaryview, NULL);
1958 /* Tmp flags handling */
1959 tmp_flags_old = msginfo->flags.tmp_flags;
1960 msginfo->flags.tmp_flags |= tmp_flags;
1962 /* update notification */
1963 if ((perm_flags_old != perm_flags_new) || (tmp_flags_old != msginfo->flags.tmp_flags)) {
1964 msginfo_update.msginfo = msginfo;
1965 msginfo_update.flags = MSGINFO_UPDATE_FLAGS;
1966 hooks_invoke(MSGINFO_UPDATE_HOOKLIST, &msginfo_update);
1967 folder_item_update(msginfo->folder, F_ITEM_UPDATE_MSGCNT);
1971 void procmsg_msginfo_unset_flags(MsgInfo *msginfo, MsgPermFlags perm_flags, MsgTmpFlags tmp_flags)
1974 MsgInfoUpdate msginfo_update;
1975 MsgPermFlags perm_flags_new, perm_flags_old;
1976 MsgTmpFlags tmp_flags_old;
1978 cm_return_if_fail(msginfo != NULL);
1979 item = msginfo->folder;
1980 cm_return_if_fail(item != NULL);
1982 debug_print("Unsetting flags for message %d in folder %s\n", msginfo->msgnum, item->path);
1984 /* Perm Flags handling */
1985 perm_flags_old = msginfo->flags.perm_flags;
1986 perm_flags_new = msginfo->flags.perm_flags & ~perm_flags;
1988 if (perm_flags_old != perm_flags_new) {
1989 folder_item_change_msg_flags(msginfo->folder, msginfo, perm_flags_new);
1991 update_folder_msg_counts(item, msginfo, perm_flags_old);
1994 /* Tmp flags hanlding */
1995 tmp_flags_old = msginfo->flags.tmp_flags;
1996 msginfo->flags.tmp_flags &= ~tmp_flags;
1998 /* update notification */
1999 if ((perm_flags_old != perm_flags_new) || (tmp_flags_old != msginfo->flags.tmp_flags)) {
2000 msginfo_update.msginfo = msginfo;
2001 msginfo_update.flags = MSGINFO_UPDATE_FLAGS;
2002 hooks_invoke(MSGINFO_UPDATE_HOOKLIST, &msginfo_update);
2003 folder_item_update(msginfo->folder, F_ITEM_UPDATE_MSGCNT);
2007 void procmsg_msginfo_change_flags(MsgInfo *msginfo,
2008 MsgPermFlags add_perm_flags, MsgTmpFlags add_tmp_flags,
2009 MsgPermFlags rem_perm_flags, MsgTmpFlags rem_tmp_flags)
2012 MsgInfoUpdate msginfo_update;
2013 MsgPermFlags perm_flags_new, perm_flags_old;
2014 MsgTmpFlags tmp_flags_old;
2016 cm_return_if_fail(msginfo != NULL);
2017 item = msginfo->folder;
2018 cm_return_if_fail(item != NULL);
2020 debug_print("Changing flags for message %d in folder %s\n", msginfo->msgnum, item->path);
2022 /* Perm Flags handling */
2023 perm_flags_old = msginfo->flags.perm_flags;
2024 perm_flags_new = (msginfo->flags.perm_flags & ~rem_perm_flags) | add_perm_flags;
2025 if ((add_perm_flags & MSG_IGNORE_THREAD) || (perm_flags_old & MSG_IGNORE_THREAD)) {
2026 perm_flags_new &= ~(MSG_NEW | MSG_UNREAD);
2028 if ((add_perm_flags & MSG_WATCH_THREAD) || (perm_flags_old & MSG_WATCH_THREAD)) {
2029 perm_flags_new &= ~(MSG_IGNORE_THREAD);
2032 if (perm_flags_old != perm_flags_new) {
2033 folder_item_change_msg_flags(msginfo->folder, msginfo, perm_flags_new);
2035 update_folder_msg_counts(item, msginfo, perm_flags_old);
2039 /* Tmp flags handling */
2040 tmp_flags_old = msginfo->flags.tmp_flags;
2041 msginfo->flags.tmp_flags &= ~rem_tmp_flags;
2042 msginfo->flags.tmp_flags |= add_tmp_flags;
2044 /* update notification */
2045 if ((perm_flags_old != perm_flags_new) || (tmp_flags_old != msginfo->flags.tmp_flags)) {
2046 msginfo_update.msginfo = msginfo;
2047 msginfo_update.flags = MSGINFO_UPDATE_FLAGS;
2048 hooks_invoke(MSGINFO_UPDATE_HOOKLIST, &msginfo_update);
2049 folder_item_update(msginfo->folder, F_ITEM_UPDATE_MSGCNT);
2054 *\brief check for flags (e.g. mark) in prior msgs of current thread
2056 *\param info Current message
2057 *\param perm_flags Flags to be checked
2058 *\param parentmsgs Hash of prior msgs to avoid loops
2060 *\return gboolean TRUE if perm_flags are found
2062 static gboolean procmsg_msg_has_flagged_parent_real(MsgInfo *info,
2063 MsgPermFlags perm_flags, GHashTable *parentmsgs)
2067 cm_return_val_if_fail(info != NULL, FALSE);
2069 if (info != NULL && info->folder != NULL && info->inreplyto != NULL) {
2070 tmp = folder_item_get_msginfo_by_msgid(info->folder,
2072 if (tmp && (tmp->flags.perm_flags & perm_flags)) {
2073 procmsg_msginfo_free(&tmp);
2075 } else if (tmp != NULL) {
2078 if (g_hash_table_lookup(parentmsgs, info)) {
2079 debug_print("loop detected: %d\n",
2083 g_hash_table_insert(parentmsgs, info, "1");
2084 result = procmsg_msg_has_flagged_parent_real(
2085 tmp, perm_flags, parentmsgs);
2087 procmsg_msginfo_free(&tmp);
2097 *\brief Callback for cleaning up hash of parentmsgs
2099 static gboolean parentmsgs_hash_remove(gpointer key,
2107 *\brief Set up list of parentmsgs
2108 * See procmsg_msg_has_flagged_parent_real()
2110 gboolean procmsg_msg_has_flagged_parent(MsgInfo *info, MsgPermFlags perm_flags)
2113 static GHashTable *parentmsgs = NULL;
2115 if (parentmsgs == NULL)
2116 parentmsgs = g_hash_table_new(NULL, NULL);
2118 result = procmsg_msg_has_flagged_parent_real(info, perm_flags, parentmsgs);
2119 g_hash_table_foreach_remove(parentmsgs, parentmsgs_hash_remove, NULL);
2125 *\brief Check if msgs prior in thread are marked
2126 * See procmsg_msg_has_flagged_parent_real()
2128 gboolean procmsg_msg_has_marked_parent(MsgInfo *info)
2130 return procmsg_msg_has_flagged_parent(info, MSG_MARKED);
2134 static GSList *procmsg_find_children_func(MsgInfo *info,
2135 GSList *children, GSList *all)
2139 cm_return_val_if_fail(info!=NULL, children);
2140 if (info->msgid == NULL)
2143 for (cur = all; cur != NULL; cur = g_slist_next(cur)) {
2144 MsgInfo *tmp = (MsgInfo *)cur->data;
2145 if (tmp->inreplyto && !strcmp(tmp->inreplyto, info->msgid)) {
2146 /* Check if message is already in the list */
2147 if ((children == NULL) ||
2148 (g_slist_index(children, tmp) == -1)) {
2149 children = g_slist_prepend(children,
2150 procmsg_msginfo_new_ref(tmp));
2151 children = procmsg_find_children_func(tmp,
2160 static GSList *procmsg_find_children (MsgInfo *info)
2165 cm_return_val_if_fail(info!=NULL, NULL);
2166 all = folder_item_get_msg_list(info->folder);
2167 children = procmsg_find_children_func(info, NULL, all);
2168 if (children != NULL) {
2169 for (cur = all; cur != NULL; cur = g_slist_next(cur)) {
2170 /* this will not free the used pointers
2171 created with procmsg_msginfo_new_ref */
2172 procmsg_msginfo_free((MsgInfo **)&(cur->data));
2180 static void procmsg_update_unread_children(MsgInfo *info, gboolean newly_marked)
2182 GSList *children = procmsg_find_children(info);
2184 for (cur = children; cur != NULL; cur = g_slist_next(cur)) {
2185 MsgInfo *tmp = (MsgInfo *)cur->data;
2186 if(MSG_IS_UNREAD(tmp->flags) && !MSG_IS_IGNORE_THREAD(tmp->flags)) {
2188 info->folder->unreadmarked_msgs++;
2190 info->folder->unreadmarked_msgs--;
2191 folder_item_update(info->folder, F_ITEM_UPDATE_MSGCNT);
2193 procmsg_msginfo_free(&tmp);
2195 g_slist_free(children);
2199 * Set the destination folder for a copy or move operation
2201 * \param msginfo The message which's destination folder is changed
2202 * \param to_folder The destination folder for the operation
2204 void procmsg_msginfo_set_to_folder(MsgInfo *msginfo, FolderItem *to_folder)
2206 if(msginfo->to_folder != NULL) {
2207 msginfo->to_folder->op_count--;
2208 folder_item_update(msginfo->to_folder, F_ITEM_UPDATE_MSGCNT);
2210 msginfo->to_folder = to_folder;
2211 if(to_folder != NULL) {
2212 to_folder->op_count++;
2213 folder_item_update(msginfo->to_folder, F_ITEM_UPDATE_MSGCNT);
2218 * Apply filtering actions to the msginfo
2220 * \param msginfo The MsgInfo describing the message that should be filtered
2221 * \return TRUE if the message was moved and MsgInfo is now invalid,
2224 static gboolean procmsg_msginfo_filter(MsgInfo *msginfo, PrefsAccount* ac_prefs)
2226 MailFilteringData mail_filtering_data;
2228 mail_filtering_data.msginfo = msginfo;
2229 mail_filtering_data.msglist = NULL;
2230 mail_filtering_data.filtered = NULL;
2231 mail_filtering_data.unfiltered = NULL;
2232 mail_filtering_data.account = ac_prefs;
2234 if (!ac_prefs || ac_prefs->filterhook_on_recv)
2235 if (hooks_invoke(MAIL_FILTERING_HOOKLIST, &mail_filtering_data))
2238 /* filter if enabled in prefs or move to inbox if not */
2239 if((filtering_rules != NULL) &&
2240 filter_message_by_msginfo(filtering_rules, msginfo, ac_prefs,
2241 FILTERING_INCORPORATION, NULL)) {
2248 void procmsg_msglist_filter(GSList *list, PrefsAccount *ac,
2249 GSList **filtered, GSList **unfiltered,
2252 GSList *cur, *to_do = NULL;
2253 gint total = 0, curnum = 0;
2254 MailFilteringData mail_filtering_data;
2256 cm_return_if_fail(filtered != NULL);
2257 cm_return_if_fail(unfiltered != NULL);
2265 total = g_slist_length(list);
2269 *unfiltered = g_slist_copy(list);
2273 statusbar_print_all(_("Filtering messages...\n"));
2275 mail_filtering_data.msginfo = NULL;
2276 mail_filtering_data.msglist = list;
2277 mail_filtering_data.filtered = NULL;
2278 mail_filtering_data.unfiltered = NULL;
2279 mail_filtering_data.account = ac;
2281 if (!ac || ac->filterhook_on_recv)
2282 hooks_invoke(MAIL_LISTFILTERING_HOOKLIST, &mail_filtering_data);
2284 if (mail_filtering_data.filtered == NULL &&
2285 mail_filtering_data.unfiltered == NULL) {
2286 /* nothing happened */
2287 debug_print(MAIL_LISTFILTERING_HOOKLIST " did nothing. filtering whole list normally.\n");
2290 if (mail_filtering_data.filtered != NULL) {
2291 /* keep track of what's been filtered by the hooks */
2292 debug_print(MAIL_LISTFILTERING_HOOKLIST " filtered some stuff. total %d filtered %d unfilt %d.\n",
2293 g_slist_length(list),
2294 g_slist_length(mail_filtering_data.filtered),
2295 g_slist_length(mail_filtering_data.unfiltered));
2297 *filtered = g_slist_copy(mail_filtering_data.filtered);
2299 if (mail_filtering_data.unfiltered != NULL) {
2300 /* what the hooks didn't handle will go in filtered or
2301 * unfiltered in the next loop */
2302 debug_print(MAIL_LISTFILTERING_HOOKLIST " left unfiltered stuff. total %d filtered %d unfilt %d.\n",
2303 g_slist_length(list),
2304 g_slist_length(mail_filtering_data.filtered),
2305 g_slist_length(mail_filtering_data.unfiltered));
2306 to_do = mail_filtering_data.unfiltered;
2309 for (cur = to_do; cur; cur = cur->next) {
2310 MsgInfo *info = (MsgInfo *)cur->data;
2311 if (procmsg_msginfo_filter(info, ac))
2312 *filtered = g_slist_prepend(*filtered, info);
2314 *unfiltered = g_slist_prepend(*unfiltered, info);
2315 statusbar_progress_all(curnum++, total, prefs_common.statusbar_update_step);
2318 g_slist_free(mail_filtering_data.filtered);
2319 g_slist_free(mail_filtering_data.unfiltered);
2321 *filtered = g_slist_reverse(*filtered);
2322 *unfiltered = g_slist_reverse(*unfiltered);
2324 statusbar_progress_all(0,0,0);
2325 statusbar_pop_all();
2328 MsgInfo *procmsg_msginfo_new_from_mimeinfo(MsgInfo *src_msginfo, MimeInfo *mimeinfo)
2330 MsgInfo *tmp_msginfo = NULL;
2331 MsgFlags flags = {0, 0};
2332 gchar *tmpfile = get_tmp_file();
2333 FILE *fp = g_fopen(tmpfile, "wb");
2335 if (!mimeinfo || mimeinfo->type != MIMETYPE_MESSAGE ||
2336 g_ascii_strcasecmp(mimeinfo->subtype, "rfc822")) {
2337 g_warning("procmsg_msginfo_new_from_mimeinfo(): unsuitable mimeinfo");
2344 if (fp && procmime_write_mimeinfo(mimeinfo, fp) >= 0) {
2347 tmp_msginfo = procheader_parse_file(
2354 if (tmp_msginfo != NULL) {
2356 tmp_msginfo->folder = src_msginfo->folder;
2357 tmp_msginfo->plaintext_file = g_strdup(tmpfile);
2359 g_warning("procmsg_msginfo_new_from_mimeinfo(): Can't generate new msginfo");
2367 static GSList *spam_learners = NULL;
2369 void procmsg_register_spam_learner (int (*learn_func)(MsgInfo *info, GSList *list, gboolean spam))
2371 if (!g_slist_find(spam_learners, learn_func))
2372 spam_learners = g_slist_append(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 void procmsg_unregister_spam_learner (int (*learn_func)(MsgInfo *info, GSList *list, gboolean spam))
2383 spam_learners = g_slist_remove(spam_learners, learn_func);
2384 if (mainwindow_get_mainwindow()) {
2385 main_window_set_menu_sensitive(mainwindow_get_mainwindow());
2386 summary_set_menu_sensitive(
2387 mainwindow_get_mainwindow()->summaryview);
2388 toolbar_main_set_sensitive(mainwindow_get_mainwindow());
2392 gboolean procmsg_spam_can_learn(void)
2394 return g_slist_length(spam_learners) > 0;
2397 int procmsg_spam_learner_learn (MsgInfo *info, GSList *list, gboolean spam)
2399 GSList *cur = spam_learners;
2401 for (; cur; cur = cur->next) {
2402 int ((*func)(MsgInfo *info, GSList *list, gboolean spam)) = cur->data;
2403 ret |= func(info, list, spam);
2408 static gchar *spam_folder_item = NULL;
2409 static FolderItem * (*procmsg_spam_get_folder_func)(MsgInfo *msginfo) = NULL;
2410 void procmsg_spam_set_folder (const char *item_identifier, FolderItem *(*spam_get_folder_func)(MsgInfo *info))
2412 g_free(spam_folder_item);
2413 if (item_identifier)
2414 spam_folder_item = g_strdup(item_identifier);
2416 spam_folder_item = NULL;
2417 if (spam_get_folder_func != NULL)
2418 procmsg_spam_get_folder_func = spam_get_folder_func;
2420 procmsg_spam_get_folder_func = NULL;
2423 FolderItem *procmsg_spam_get_folder (MsgInfo *msginfo)
2425 FolderItem *item = NULL;
2427 if (procmsg_spam_get_folder_func)
2428 item = procmsg_spam_get_folder_func(msginfo);
2429 if (item == NULL && spam_folder_item)
2430 item = folder_find_item_from_identifier(spam_folder_item);
2432 item = folder_get_default_trash();
2436 static void item_has_queued_mails(FolderItem *item, gpointer data)
2438 gboolean *result = (gboolean *)data;
2439 if (*result == TRUE)
2441 if (folder_has_parent_of_type(item, F_QUEUE)) {
2442 if (item->total_msgs == 0)
2445 GSList *msglist = folder_item_get_msg_list(item);
2447 for (cur = msglist; cur; cur = cur->next) {
2448 MsgInfo *msginfo = (MsgInfo *)cur->data;
2449 if (!MSG_IS_DELETED(msginfo->flags) &&
2450 !MSG_IS_LOCKED(msginfo->flags)) {
2455 procmsg_msg_list_free(msglist);
2460 gboolean procmsg_have_queued_mails_fast (void)
2462 gboolean result = FALSE;
2463 folder_func_to_all_folders(item_has_queued_mails, &result);
2467 static void item_has_trashed_mails(FolderItem *item, gpointer data)
2469 gboolean *result = (gboolean *)data;
2470 if (*result == TRUE)
2472 if (folder_has_parent_of_type(item, F_TRASH) && item->total_msgs > 0)
2476 gboolean procmsg_have_trashed_mails_fast (void)
2478 gboolean result = FALSE;
2479 folder_func_to_all_folders(item_has_trashed_mails, &result);
2483 gchar *procmsg_msginfo_get_tags_str(MsgInfo *msginfo)
2491 if (msginfo->tags == NULL)
2493 for (cur = msginfo->tags; cur; cur = cur->next) {
2494 const gchar *tag = tags_get_tag(GPOINTER_TO_INT(cur->data));
2498 tags = g_strdup(tag);
2500 int olen = strlen(tags);
2501 int nlen = olen + strlen(tag) + 2 /* strlen(", ") */;
2502 tags = g_realloc(tags, nlen+1);
2505 strcpy(tags+olen, ", ");
2506 strcpy(tags+olen+2, tag);
2513 void procmsg_msginfo_update_tags(MsgInfo *msginfo, gboolean set, gint id)
2521 msginfo->tags = g_slist_remove(
2523 GINT_TO_POINTER(id));
2524 changed.data = GINT_TO_POINTER(id);
2525 changed.next = NULL;
2526 folder_item_commit_tags(msginfo->folder, msginfo, NULL, &changed);
2528 if (!g_slist_find(msginfo->tags, GINT_TO_POINTER(id))) {
2529 msginfo->tags = g_slist_append(
2531 GINT_TO_POINTER(id));
2533 changed.data = GINT_TO_POINTER(id);
2534 changed.next = NULL;
2535 folder_item_commit_tags(msginfo->folder, msginfo, &changed, NULL);
2540 void procmsg_msginfo_clear_tags(MsgInfo *msginfo)
2542 GSList *unset = msginfo->tags;
2543 msginfo->tags = NULL;
2544 folder_item_commit_tags(msginfo->folder, msginfo, NULL, unset);
2545 g_slist_free(unset);