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);
406 return folder_item_fetch_msg(msginfo->folder, msginfo->msgnum);
409 gchar *procmsg_get_message_file(MsgInfo *msginfo)
411 gchar *filename = NULL;
413 cm_return_val_if_fail(msginfo != NULL, NULL);
415 filename = folder_item_fetch_msg(msginfo->folder, msginfo->msgnum);
417 debug_print("can't fetch message %d\n", msginfo->msgnum);
422 gchar *procmsg_get_message_file_full(MsgInfo *msginfo, gboolean headers, gboolean body)
424 gchar *filename = NULL;
426 cm_return_val_if_fail(msginfo != NULL, NULL);
428 filename = folder_item_fetch_msg_full(msginfo->folder, msginfo->msgnum,
431 debug_print("can't fetch message %d\n", msginfo->msgnum);
436 GSList *procmsg_get_message_file_list(GSList *mlist)
438 GSList *file_list = NULL;
440 MsgFileInfo *fileinfo;
443 while (mlist != NULL) {
444 msginfo = (MsgInfo *)mlist->data;
445 file = procmsg_get_message_file(msginfo);
447 procmsg_message_file_list_free(file_list);
450 fileinfo = g_new(MsgFileInfo, 1);
451 fileinfo->msginfo = procmsg_msginfo_new_ref(msginfo);
452 fileinfo->file = file;
453 fileinfo->flags = g_new(MsgFlags, 1);
454 *fileinfo->flags = msginfo->flags;
455 file_list = g_slist_prepend(file_list, fileinfo);
459 file_list = g_slist_reverse(file_list);
464 void procmsg_message_file_list_free(MsgInfoList *file_list)
467 MsgFileInfo *fileinfo;
469 for (cur = file_list; cur != NULL; cur = cur->next) {
470 fileinfo = (MsgFileInfo *)cur->data;
471 procmsg_msginfo_free(fileinfo->msginfo);
472 g_free(fileinfo->file);
473 g_free(fileinfo->flags);
477 g_slist_free(file_list);
480 FILE *procmsg_open_message(MsgInfo *msginfo)
485 cm_return_val_if_fail(msginfo != NULL, NULL);
487 file = procmsg_get_message_file_path(msginfo);
488 cm_return_val_if_fail(file != NULL, NULL);
490 if (!is_file_exist(file)) {
492 file = procmsg_get_message_file(msginfo);
497 if ((fp = g_fopen(file, "rb")) == NULL) {
498 FILE_OP_ERROR(file, "fopen");
505 if (MSG_IS_QUEUED(msginfo->flags) || MSG_IS_DRAFT(msginfo->flags)) {
508 while (fgets(buf, sizeof(buf), fp) != NULL) {
510 if ((!strncmp(buf, "X-Claws-End-Special-Headers: 1",
511 strlen("X-Claws-End-Special-Headers:"))) ||
512 (!strncmp(buf, "X-Sylpheed-End-Special-Headers: 1",
513 strlen("X-Sylpheed-End-Special-Headers:"))))
516 if (buf[0] == '\r' || buf[0] == '\n') break;
517 /* from other mailers */
518 if (!strncmp(buf, "Date: ", 6)
519 || !strncmp(buf, "To: ", 4)
520 || !strncmp(buf, "From: ", 6)
521 || !strncmp(buf, "Subject: ", 9)) {
531 gboolean procmsg_msg_exist(MsgInfo *msginfo)
536 if (!msginfo) return FALSE;
538 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, sizeof(buf), fp, qentry))
755 gchar *p = buf + strlen(qentry[hnum].name);
757 if (hnum == Q_MAIL_ACCOUNT_ID) {
758 mailac = account_find_from_id(atoi(p));
766 gchar *procmsg_msginfo_get_avatar(MsgInfo *msginfo, gint type)
770 if (!msginfo || !msginfo->extradata || !msginfo->extradata->avatars)
773 for (mia = msginfo->extradata->avatars; mia; mia = mia->next) {
774 MsgInfoAvatar *avatar = (MsgInfoAvatar *)mia->data;
775 if (avatar->avatar_id == type)
776 return avatar->avatar_src;
782 void procmsg_msginfo_add_avatar(MsgInfo *msginfo, gint type, const gchar *data)
786 if (!msginfo->extradata)
787 msginfo->extradata = g_new0(MsgInfoExtraData, 1);
789 av = g_new0(MsgInfoAvatar, 1);
790 av->avatar_id = type;
791 av->avatar_src = g_strdup(data);
793 msginfo->extradata->avatars = g_slist_append(msginfo->extradata->avatars, av);
796 gchar *procmsg_msginfo_get_identifier(MsgInfo *msginfo)
802 cm_return_val_if_fail(msginfo != NULL, NULL);
803 folder_id = folder_item_get_identifier(msginfo->folder);
804 msgid = msginfo->msgid;
806 id = g_strconcat(folder_id, G_DIR_SEPARATOR_S, msgid, NULL);
813 MsgInfo *procmsg_get_msginfo_from_identifier(const gchar *id)
815 gchar *folder_id = g_strdup(id);
816 gchar *separator = strrchr(folder_id, G_DIR_SEPARATOR);
821 if (separator == NULL) {
827 msgid = separator + 1;
829 item = folder_find_item_from_identifier(folder_id);
836 msginfo = folder_item_get_msginfo_by_msgid(item, msgid);
842 static GSList *procmsg_list_sort_by_account(FolderItem *queue, GSList *list)
844 GSList *result = NULL;
846 PrefsAccount *last_account = NULL;
849 gboolean nothing_to_sort = TRUE;
854 orig = g_slist_copy(list);
856 msg = (MsgInfo *)orig->data;
858 for (cur = orig; cur; cur = cur->next)
859 debug_print("sort before %s\n", ((MsgInfo *)cur->data)->from);
864 nothing_to_sort = TRUE;
868 PrefsAccount *ac = NULL;
869 msg = (MsgInfo *)cur->data;
870 file = folder_item_fetch_msg(queue, msg->msgnum);
871 ac = procmsg_get_account_from_file(file);
874 if (last_account == NULL || (ac != NULL && ac == last_account)) {
875 result = g_slist_append(result, msg);
876 orig = g_slist_remove(orig, msg);
878 nothing_to_sort = FALSE;
884 if (orig && g_slist_length(orig)) {
885 if (!last_account && nothing_to_sort) {
886 /* can't find an account for the rest of the list */
889 result = g_slist_append(result, cur->data);
900 for (cur = result; cur; cur = cur->next)
901 debug_print("sort after %s\n", ((MsgInfo *)cur->data)->from);
908 static gboolean procmsg_is_last_for_account(FolderItem *queue, MsgInfo *msginfo, GSList *elem)
910 gchar *file = folder_item_fetch_msg(queue, msginfo->msgnum);
911 PrefsAccount *ac = procmsg_get_account_from_file(file);
914 for (cur = elem; cur; cur = cur->next) {
915 MsgInfo *cur_msginfo = (MsgInfo *)cur->data;
916 file = folder_item_fetch_msg(queue, cur_msginfo->msgnum);
918 if (cur_msginfo != msginfo && !MSG_IS_LOCKED(cur_msginfo->flags)) {
919 if (procmsg_get_account_from_file(file) == ac) {
930 static gboolean send_queue_lock = FALSE;
932 gboolean procmsg_queue_lock(char **errstr)
934 if (send_queue_lock) {
935 /* Avoid having to translate two similar strings */
936 log_warning(LOG_PROTOCOL, "%s\n", _("Already trying to send."));
938 if (*errstr) g_free(*errstr);
939 *errstr = g_strdup_printf(_("Already trying to send."));
943 send_queue_lock = TRUE;
946 void procmsg_queue_unlock(void)
948 send_queue_lock = FALSE;
951 *\brief Send messages in queue
953 *\param queue Queue folder to process
954 *\param save_msgs Unused
956 *\return Number of messages sent, negative if an error occurred
957 * positive if no error occurred
959 gint procmsg_send_queue(FolderItem *queue, gboolean save_msgs, gchar **errstr)
961 gint sent = 0, err = 0;
963 GSList *sorted_list = NULL;
966 if (!procmsg_queue_lock(errstr)) {
967 main_window_set_menu_sensitive(mainwindow_get_mainwindow());
968 toolbar_main_set_sensitive(mainwindow_get_mainwindow());
973 queue = folder_get_default_queue();
976 procmsg_queue_unlock();
981 main_window_set_menu_sensitive(mainwindow_get_mainwindow());
982 toolbar_main_set_sensitive(mainwindow_get_mainwindow());
984 folder_item_scan(queue);
985 list = folder_item_get_msg_list(queue);
987 /* sort the list per sender account; this helps reusing the same SMTP server */
988 sorted_list = procmsg_list_sort_by_account(queue, list);
990 for (elem = sorted_list; elem != NULL; elem = elem->next) {
994 msginfo = (MsgInfo *)(elem->data);
995 if (!MSG_IS_LOCKED(msginfo->flags) && !MSG_IS_DELETED(msginfo->flags)) {
996 file = folder_item_fetch_msg(queue, msginfo->msgnum);
998 gboolean queued_removed = FALSE;
999 if (procmsg_send_message_queue_full(file,
1000 !procmsg_is_last_for_account(queue, msginfo, elem),
1001 errstr, queue, msginfo->msgnum, &queued_removed) < 0) {
1002 g_warning("Sending queued message %d failed.\n",
1007 if (!queued_removed)
1008 folder_item_remove_msg(queue, msginfo->msgnum);
1013 /* FIXME: supposedly if only one message is locked, and queue
1014 * is being flushed, the following free says something like
1015 * "freeing msg ## in folder (nil)". */
1016 procmsg_msginfo_free(msginfo);
1019 g_slist_free(sorted_list);
1020 folder_item_scan(queue);
1022 if (queue->node && queue->node->children) {
1023 node = queue->node->children;
1024 while (node != NULL) {
1027 send_queue_lock = FALSE;
1028 res = procmsg_send_queue(FOLDER_ITEM(node->data), save_msgs, errstr);
1029 send_queue_lock = TRUE;
1037 procmsg_queue_unlock();
1039 main_window_set_menu_sensitive(mainwindow_get_mainwindow());
1040 toolbar_main_set_sensitive(mainwindow_get_mainwindow());
1042 return (err != 0 ? -err : sent);
1045 gboolean procmsg_is_sending(void)
1047 return send_queue_lock;
1051 *\brief Determine if a queue folder is empty
1053 *\param queue Queue folder to process
1055 *\return TRUE if the queue folder is empty, otherwise return FALSE
1057 gboolean procmsg_queue_is_empty(FolderItem *queue)
1060 gboolean res = FALSE;
1062 queue = folder_get_default_queue();
1063 cm_return_val_if_fail(queue != NULL, TRUE);
1065 folder_item_scan(queue);
1066 list = folder_item_get_msg_list(queue);
1067 res = (list == NULL);
1068 procmsg_msg_list_free(list);
1072 if (queue->node && queue->node->children) {
1073 node = queue->node->children;
1074 while (node != NULL) {
1076 if (!procmsg_queue_is_empty(FOLDER_ITEM(node->data)))
1085 gint procmsg_remove_special_headers(const gchar *in, const gchar *out)
1088 gchar buf[BUFFSIZE];
1090 if ((fp = g_fopen(in, "rb")) == NULL) {
1091 FILE_OP_ERROR(in, "fopen");
1094 if ((outfp = g_fopen(out, "wb")) == NULL) {
1095 FILE_OP_ERROR(out, "fopen");
1099 while (fgets(buf, sizeof(buf), fp) != NULL) {
1101 if ((!strncmp(buf, "X-Claws-End-Special-Headers: 1",
1102 strlen("X-Claws-End-Special-Headers:"))) ||
1103 (!strncmp(buf, "X-Sylpheed-End-Special-Headers: 1",
1104 strlen("X-Sylpheed-End-Special-Headers:"))))
1107 if (buf[0] == '\r' || buf[0] == '\n') break;
1108 /* from other mailers */
1109 if (!strncmp(buf, "Date: ", 6)
1110 || !strncmp(buf, "To: ", 4)
1111 || !strncmp(buf, "From: ", 6)
1112 || !strncmp(buf, "Subject: ", 9)) {
1117 while (fgets(buf, sizeof(buf), fp) != NULL) {
1118 if (fputs(buf, outfp) == EOF) {
1119 FILE_OP_ERROR(out, "fputs");
1130 gint procmsg_save_to_outbox(FolderItem *outbox, const gchar *file,
1134 MsgInfo *msginfo, *tmp_msginfo;
1135 MsgFlags flag = {0, 0};
1137 debug_print("saving sent message...\n");
1140 outbox = folder_get_default_outbox();
1141 cm_return_val_if_fail(outbox != NULL, -1);
1143 /* remove queueing headers */
1145 gchar tmp[MAXPATHLEN + 1];
1147 g_snprintf(tmp, sizeof(tmp), "%s%ctmpmsg.out.%08x",
1148 get_rc_dir(), G_DIR_SEPARATOR, (guint) rand());
1150 if (procmsg_remove_special_headers(file, tmp) !=0)
1153 folder_item_scan(outbox);
1154 if ((num = folder_item_add_msg(outbox, tmp, &flag, TRUE)) < 0) {
1155 g_warning("can't save message\n");
1160 folder_item_scan(outbox);
1161 if ((num = folder_item_add_msg
1162 (outbox, file, &flag, FALSE)) < 0) {
1163 g_warning("can't save message\n");
1167 msginfo = folder_item_get_msginfo(outbox, num); /* refcnt++ */
1168 tmp_msginfo = procmsg_msginfo_get_full_info(msginfo); /* refcnt++ */
1169 if (msginfo != NULL) {
1170 procmsg_msginfo_unset_flags(msginfo, ~0, 0);
1171 procmsg_msginfo_free(msginfo); /* refcnt-- */
1172 /* tmp_msginfo == msginfo */
1173 if (tmp_msginfo && msginfo->extradata &&
1174 (msginfo->extradata->dispositionnotificationto ||
1175 msginfo->extradata->returnreceiptto)) {
1176 procmsg_msginfo_set_flags(msginfo, MSG_RETRCPT_SENT, 0);
1178 procmsg_msginfo_free(tmp_msginfo); /* refcnt-- */
1185 MsgInfo *procmsg_msginfo_new_ref(MsgInfo *msginfo)
1192 MsgInfo *procmsg_msginfo_new(void)
1194 MsgInfo *newmsginfo;
1196 newmsginfo = g_new0(MsgInfo, 1);
1197 newmsginfo->refcnt = 1;
1202 static MsgInfoAvatar *procmsg_msginfoavatar_copy(MsgInfoAvatar *avatar)
1204 MsgInfoAvatar *newavatar;
1206 if (avatar == NULL) return NULL;
1208 newavatar = g_new0(MsgInfoAvatar, 1);
1209 newavatar->avatar_id = avatar->avatar_id;
1210 newavatar->avatar_src = g_strdup(avatar->avatar_src);
1215 static void procmsg_msginfoavatar_free(MsgInfoAvatar *avatar)
1217 if (avatar != NULL) {
1218 if (avatar->avatar_src != NULL)
1219 g_free(avatar->avatar_src);
1224 MsgInfo *procmsg_msginfo_copy(MsgInfo *msginfo)
1226 MsgInfo *newmsginfo;
1229 if (msginfo == NULL) return NULL;
1231 newmsginfo = g_new0(MsgInfo, 1);
1233 newmsginfo->refcnt = 1;
1235 #define MEMBCOPY(mmb) newmsginfo->mmb = msginfo->mmb
1236 #define MEMBDUP(mmb) newmsginfo->mmb = msginfo->mmb ? \
1237 g_strdup(msginfo->mmb) : NULL
1252 MEMBDUP(newsgroups);
1259 MEMBCOPY(to_folder);
1261 if (msginfo->extradata) {
1262 newmsginfo->extradata = g_new0(MsgInfoExtraData, 1);
1263 if (msginfo->extradata->avatars) {
1264 newmsginfo->extradata->avatars = slist_copy_deep(msginfo->extradata->avatars,
1265 (GCopyFunc) procmsg_msginfoavatar_copy);
1267 MEMBDUP(extradata->dispositionnotificationto);
1268 MEMBDUP(extradata->returnreceiptto);
1269 MEMBDUP(extradata->partial_recv);
1270 MEMBDUP(extradata->account_server);
1271 MEMBDUP(extradata->account_login);
1272 MEMBDUP(extradata->list_post);
1273 MEMBDUP(extradata->list_subscribe);
1274 MEMBDUP(extradata->list_unsubscribe);
1275 MEMBDUP(extradata->list_help);
1276 MEMBDUP(extradata->list_archive);
1277 MEMBDUP(extradata->list_owner);
1278 MEMBDUP(extradata->resent_from);
1281 refs = msginfo->references;
1282 for (refs = msginfo->references; refs != NULL; refs = refs->next) {
1283 newmsginfo->references = g_slist_prepend
1284 (newmsginfo->references, g_strdup(refs->data));
1286 newmsginfo->references = g_slist_reverse(newmsginfo->references);
1293 MsgInfo *procmsg_msginfo_get_full_info_from_file(MsgInfo *msginfo, const gchar *file)
1295 MsgInfo *full_msginfo;
1297 if (msginfo == NULL) return NULL;
1299 if (!file || !is_file_exist(file)) {
1300 g_warning("procmsg_msginfo_get_full_info_from_file(): can't get message file.\n");
1304 full_msginfo = procheader_parse_file(file, msginfo->flags, TRUE, FALSE);
1305 if (!full_msginfo) return NULL;
1307 msginfo->total_size = full_msginfo->total_size;
1308 msginfo->planned_download = full_msginfo->planned_download;
1310 if (full_msginfo->extradata) {
1311 if (!msginfo->extradata)
1312 msginfo->extradata = g_new0(MsgInfoExtraData, 1);
1313 if (!msginfo->extradata->list_post)
1314 msginfo->extradata->list_post = g_strdup(full_msginfo->extradata->list_post);
1315 if (!msginfo->extradata->list_subscribe)
1316 msginfo->extradata->list_subscribe = g_strdup(full_msginfo->extradata->list_subscribe);
1317 if (!msginfo->extradata->list_unsubscribe)
1318 msginfo->extradata->list_unsubscribe = g_strdup(full_msginfo->extradata->list_unsubscribe);
1319 if (!msginfo->extradata->list_help)
1320 msginfo->extradata->list_help = g_strdup(full_msginfo->extradata->list_help);
1321 if (!msginfo->extradata->list_archive)
1322 msginfo->extradata->list_archive= g_strdup(full_msginfo->extradata->list_archive);
1323 if (!msginfo->extradata->list_owner)
1324 msginfo->extradata->list_owner = g_strdup(full_msginfo->extradata->list_owner);
1325 if (!msginfo->extradata->avatars)
1326 msginfo->extradata->avatars = slist_copy_deep(full_msginfo->extradata->avatars,
1327 (GCopyFunc) procmsg_msginfoavatar_copy);
1328 if (!msginfo->extradata->dispositionnotificationto)
1329 msginfo->extradata->dispositionnotificationto =
1330 g_strdup(full_msginfo->extradata->dispositionnotificationto);
1331 if (!msginfo->extradata->returnreceiptto)
1332 msginfo->extradata->returnreceiptto = g_strdup
1333 (full_msginfo->extradata->returnreceiptto);
1334 if (!msginfo->extradata->partial_recv && full_msginfo->extradata->partial_recv)
1335 msginfo->extradata->partial_recv = g_strdup
1336 (full_msginfo->extradata->partial_recv);
1337 if (!msginfo->extradata->account_server && full_msginfo->extradata->account_server)
1338 msginfo->extradata->account_server = g_strdup
1339 (full_msginfo->extradata->account_server);
1340 if (!msginfo->extradata->account_login && full_msginfo->extradata->account_login)
1341 msginfo->extradata->account_login = g_strdup
1342 (full_msginfo->extradata->account_login);
1343 if (!msginfo->extradata->resent_from && full_msginfo->extradata->resent_from)
1344 msginfo->extradata->resent_from = g_strdup
1345 (full_msginfo->extradata->resent_from);
1347 procmsg_msginfo_free(full_msginfo);
1349 return procmsg_msginfo_new_ref(msginfo);
1352 MsgInfo *procmsg_msginfo_get_full_info(MsgInfo *msginfo)
1354 MsgInfo *full_msginfo;
1357 if (msginfo == NULL) return NULL;
1359 file = procmsg_get_message_file_path(msginfo);
1360 if (!file || !is_file_exist(file)) {
1362 file = procmsg_get_message_file(msginfo);
1364 if (!file || !is_file_exist(file)) {
1365 g_warning("procmsg_msginfo_get_full_info(): can't get message file.\n");
1369 full_msginfo = procmsg_msginfo_get_full_info_from_file(msginfo, file);
1371 return full_msginfo;
1374 void procmsg_msginfo_free(MsgInfo *msginfo)
1376 if (msginfo == NULL) return;
1379 if (msginfo->refcnt > 0)
1382 if (msginfo->to_folder) {
1383 msginfo->to_folder->op_count--;
1384 folder_item_update(msginfo->to_folder, F_ITEM_UPDATE_MSGCNT);
1387 g_free(msginfo->fromspace);
1389 g_free(msginfo->fromname);
1391 g_free(msginfo->date);
1392 g_free(msginfo->from);
1393 g_free(msginfo->to);
1394 g_free(msginfo->cc);
1395 g_free(msginfo->newsgroups);
1396 g_free(msginfo->subject);
1397 g_free(msginfo->msgid);
1398 g_free(msginfo->inreplyto);
1399 g_free(msginfo->xref);
1401 if (msginfo->extradata) {
1402 if (msginfo->extradata->avatars) {
1403 g_slist_foreach(msginfo->extradata->avatars,
1404 (GFunc)procmsg_msginfoavatar_free,
1406 g_slist_free(msginfo->extradata->avatars);
1408 g_free(msginfo->extradata->returnreceiptto);
1409 g_free(msginfo->extradata->dispositionnotificationto);
1410 g_free(msginfo->extradata->list_post);
1411 g_free(msginfo->extradata->list_subscribe);
1412 g_free(msginfo->extradata->list_unsubscribe);
1413 g_free(msginfo->extradata->list_help);
1414 g_free(msginfo->extradata->list_archive);
1415 g_free(msginfo->extradata->list_owner);
1416 g_free(msginfo->extradata->partial_recv);
1417 g_free(msginfo->extradata->account_server);
1418 g_free(msginfo->extradata->account_login);
1419 g_free(msginfo->extradata->resent_from);
1420 g_free(msginfo->extradata);
1422 slist_free_strings_full(msginfo->references);
1423 g_slist_free(msginfo->tags);
1428 guint procmsg_msginfo_memusage(MsgInfo *msginfo)
1433 memusage += sizeof(MsgInfo);
1434 if (msginfo->fromname)
1435 memusage += strlen(msginfo->fromname);
1437 memusage += strlen(msginfo->date);
1439 memusage += strlen(msginfo->from);
1441 memusage += strlen(msginfo->to);
1443 memusage += strlen(msginfo->cc);
1444 if (msginfo->newsgroups)
1445 memusage += strlen(msginfo->newsgroups);
1446 if (msginfo->subject)
1447 memusage += strlen(msginfo->subject);
1449 memusage += strlen(msginfo->msgid);
1450 if (msginfo->inreplyto)
1451 memusage += strlen(msginfo->inreplyto);
1453 for (tmp = msginfo->references; tmp; tmp=tmp->next) {
1454 gchar *r = (gchar *)tmp->data;
1455 memusage += r?strlen(r):0 + sizeof(GSList);
1457 if (msginfo->fromspace)
1458 memusage += strlen(msginfo->fromspace);
1460 for (tmp = msginfo->tags; tmp; tmp=tmp->next) {
1461 memusage += sizeof(GSList);
1463 if (msginfo->extradata) {
1464 memusage += sizeof(MsgInfoExtraData);
1465 if (msginfo->extradata->avatars) {
1466 for (tmp = msginfo->extradata->avatars; tmp; tmp = tmp->next) {
1467 MsgInfoAvatar *avt = (MsgInfoAvatar *)tmp->data;
1468 memusage += (avt->avatar_src)? strlen(avt->avatar_src): 0;
1469 memusage += sizeof(MsgInfoAvatar) + sizeof(GSList);
1472 if (msginfo->extradata->dispositionnotificationto)
1473 memusage += strlen(msginfo->extradata->dispositionnotificationto);
1474 if (msginfo->extradata->returnreceiptto)
1475 memusage += strlen(msginfo->extradata->returnreceiptto);
1477 if (msginfo->extradata->partial_recv)
1478 memusage += strlen(msginfo->extradata->partial_recv);
1479 if (msginfo->extradata->account_server)
1480 memusage += strlen(msginfo->extradata->account_server);
1481 if (msginfo->extradata->account_login)
1482 memusage += strlen(msginfo->extradata->account_login);
1483 if (msginfo->extradata->resent_from)
1484 memusage += strlen(msginfo->extradata->resent_from);
1486 if (msginfo->extradata->list_post)
1487 memusage += strlen(msginfo->extradata->list_post);
1488 if (msginfo->extradata->list_subscribe)
1489 memusage += strlen(msginfo->extradata->list_subscribe);
1490 if (msginfo->extradata->list_unsubscribe)
1491 memusage += strlen(msginfo->extradata->list_unsubscribe);
1492 if (msginfo->extradata->list_help)
1493 memusage += strlen(msginfo->extradata->list_help);
1494 if (msginfo->extradata->list_archive)
1495 memusage += strlen(msginfo->extradata->list_archive);
1496 if (msginfo->extradata->list_owner)
1497 memusage += strlen(msginfo->extradata->list_owner);
1502 static gint procmsg_send_message_queue_full(const gchar *file, gboolean keep_session, gchar **errstr,
1503 FolderItem *queue, gint msgnum, gboolean *queued_removed)
1505 static HeaderEntry qentry[] = {
1506 {"S:", NULL, FALSE}, /* 0 */
1507 {"SSV:", NULL, FALSE},
1508 {"R:", NULL, FALSE},
1509 {"NG:", NULL, FALSE},
1510 {"MAID:", NULL, FALSE},
1511 {"NAID:", NULL, FALSE}, /* 5 */
1512 {"SCF:", NULL, FALSE},
1513 {"RMID:", NULL, FALSE},
1514 {"FMID:", NULL, FALSE},
1515 {"X-Claws-Privacy-System:", NULL, FALSE},
1516 {"X-Claws-Encrypt:", NULL, FALSE}, /* 10 */
1517 {"X-Claws-Encrypt-Data:", NULL, FALSE},
1518 {"X-Claws-End-Special-Headers:", NULL, FALSE},
1519 {"X-Sylpheed-Privacy-System:", NULL, FALSE},
1520 {"X-Sylpheed-Encrypt:", NULL, FALSE},
1521 {"X-Sylpheed-Encrypt-Data:", NULL, FALSE}, /* 15 */
1522 {"X-Sylpheed-End-Special-Headers:", NULL, FALSE},
1523 {NULL, NULL, FALSE}};
1526 gint mailval = 0, newsval = 0;
1528 gchar *smtpserver = NULL;
1529 GSList *to_list = NULL;
1530 GSList *newsgroup_list = NULL;
1531 gchar *savecopyfolder = NULL;
1532 gchar *replymessageid = NULL;
1533 gchar *fwdmessageid = NULL;
1534 gchar buf[BUFFSIZE];
1536 PrefsAccount *mailac = NULL, *newsac = NULL;
1537 gboolean encrypt = FALSE;
1540 cm_return_val_if_fail(file != NULL, -1);
1542 if ((fp = g_fopen(file, "rb")) == NULL) {
1543 FILE_OP_ERROR(file, "fopen");
1545 if (*errstr) g_free(*errstr);
1546 *errstr = g_strdup_printf(_("Couldn't open file %s."), file);
1551 while ((hnum = procheader_get_one_field(buf, sizeof(buf), fp, qentry))
1553 gchar *p = buf + strlen(qentry[hnum].name);
1561 if (smtpserver == NULL)
1562 smtpserver = g_strdup(p);
1565 to_list = address_list_append(to_list, p);
1568 newsgroup_list = newsgroup_list_append(newsgroup_list, p);
1570 case Q_MAIL_ACCOUNT_ID:
1571 mailac = account_find_from_id(atoi(p));
1573 case Q_NEWS_ACCOUNT_ID:
1574 newsac = account_find_from_id(atoi(p));
1576 case Q_SAVE_COPY_FOLDER:
1577 if (savecopyfolder == NULL)
1578 savecopyfolder = g_strdup(p);
1580 case Q_REPLY_MESSAGE_ID:
1581 if (replymessageid == NULL)
1582 replymessageid = g_strdup(p);
1584 case Q_FWD_MESSAGE_ID:
1585 if (fwdmessageid == NULL)
1586 fwdmessageid = g_strdup(p);
1594 case Q_CLAWS_HDRS_OLD:
1595 /* end of special headers reached */
1596 goto send_mail; /* can't "break;break;" */
1600 filepos = ftell(fp);
1602 FILE_OP_ERROR(file, "ftell");
1604 if (*errstr) g_free(*errstr);
1605 *errstr = g_strdup_printf(_("Couldn't open file %s."), file);
1611 debug_print("Sending message by mail\n");
1614 if (*errstr) g_free(*errstr);
1615 *errstr = g_strdup_printf(_("Queued message header is broken."));
1618 } else if (mailac && mailac->use_mail_command &&
1619 mailac->mail_command && (* mailac->mail_command)) {
1620 mailval = send_message_local(mailac->mail_command, fp);
1623 mailac = account_find_from_smtp_server(from, smtpserver);
1625 g_warning("Account not found. "
1626 "Using current account...\n");
1627 mailac = cur_account;
1632 mailval = send_message_smtp_full(mailac, to_list, fp, keep_session);
1633 if (mailval == -1 && errstr) {
1634 if (*errstr) g_free(*errstr);
1635 *errstr = g_strdup_printf(_("An error happened during SMTP session."));
1638 PrefsAccount tmp_ac;
1640 g_warning("Account not found.\n");
1642 memset(&tmp_ac, 0, sizeof(PrefsAccount));
1643 tmp_ac.address = from;
1644 tmp_ac.smtp_server = smtpserver;
1645 tmp_ac.smtpport = SMTP_PORT;
1646 mailval = send_message_smtp(&tmp_ac, to_list, fp);
1647 if (mailval == -1 && errstr) {
1648 if (*errstr) g_free(*errstr);
1649 *errstr = g_strdup_printf(_("No specific account has been found to "
1650 "send, and an error happened during SMTP session."));
1654 } else if (!to_list && !newsgroup_list) {
1656 if (*errstr) g_free(*errstr);
1657 *errstr = g_strdup(_("Couldn't determine sending information. "
1658 "Maybe the email hasn't been generated by Claws Mail."));
1663 if (fseek(fp, filepos, SEEK_SET) < 0) {
1664 FILE_OP_ERROR(file, "fseek");
1668 if (newsgroup_list && newsac && (mailval == 0)) {
1673 /* write to temporary file */
1674 tmp = g_strdup_printf("%s%cnntp%p", get_tmp_dir(),
1675 G_DIR_SEPARATOR, file);
1676 if ((tmpfp = g_fopen(tmp, "wb")) == NULL) {
1677 FILE_OP_ERROR(tmp, "fopen");
1679 alertpanel_error(_("Couldn't create temporary file for news sending."));
1681 if (change_file_mode_rw(tmpfp, tmp) < 0) {
1682 FILE_OP_ERROR(tmp, "chmod");
1683 g_warning("can't change file mode\n");
1686 while ((newsval == 0) && fgets(buf, sizeof(buf), fp) != NULL) {
1687 if (fputs(buf, tmpfp) == EOF) {
1688 FILE_OP_ERROR(tmp, "fputs");
1691 if (*errstr) g_free(*errstr);
1692 *errstr = g_strdup_printf(_("Error when writing temporary file for news sending."));
1699 debug_print("Sending message by news\n");
1701 folder = FOLDER(newsac->folder);
1703 newsval = news_post(folder, tmp);
1704 if (newsval < 0 && errstr) {
1705 if (*errstr) g_free(*errstr);
1706 *errstr = g_strdup_printf(_("Error occurred while posting the message to %s."),
1707 newsac->nntp_server);
1717 /* update session statistics */
1718 if (mailval == 0 && newsval == 0) {
1719 /* update session stats */
1721 session_stats.replied++;
1722 else if (fwdmessageid)
1723 session_stats.forwarded++;
1725 session_stats.sent++;
1728 /* save message to outbox */
1729 if (mailval == 0 && newsval == 0 && savecopyfolder) {
1730 debug_print("saving sent message...\n");
1732 if (!encrypt || !mailac->save_encrypted_as_clear_text) {
1733 outbox = folder_find_item_from_identifier(savecopyfolder);
1735 outbox = folder_get_default_outbox();
1737 /* Mail was not saved to outbox before encrypting, save it now. */
1738 gboolean saved = FALSE;
1739 *queued_removed = FALSE;
1740 if (queue && msgnum > 0) {
1741 MsgInfo *queued_mail = folder_item_get_msginfo(queue, msgnum);
1742 if (folder_item_move_msg(outbox, queued_mail) >= 0) {
1743 debug_print("moved queued mail %d to sent folder\n", msgnum);
1745 *queued_removed = TRUE;
1746 } else if (folder_item_copy_msg(outbox, queued_mail) >= 0) {
1747 debug_print("copied queued mail %d to sent folder\n", msgnum);
1750 procmsg_msginfo_free(queued_mail);
1753 debug_print("resaving queued mail to sent folder\n");
1754 procmsg_save_to_outbox(outbox, file, TRUE);
1759 if (replymessageid != NULL || fwdmessageid != NULL) {
1763 if (replymessageid != NULL)
1764 tokens = g_strsplit(replymessageid, "\t", 0);
1766 tokens = g_strsplit(fwdmessageid, "\t", 0);
1767 item = folder_find_item_from_identifier(tokens[0]);
1769 /* check if queued message has valid folder and message id */
1770 if (item != NULL && tokens[2] != NULL) {
1773 msginfo = folder_item_get_msginfo(item, atoi(tokens[1]));
1775 /* check if referring message exists and has a message id */
1776 if ((msginfo != NULL) &&
1777 (msginfo->msgid != NULL) &&
1778 (strcmp(msginfo->msgid, tokens[2]) != 0)) {
1779 procmsg_msginfo_free(msginfo);
1783 if (msginfo == NULL) {
1784 msginfo = folder_item_get_msginfo_by_msgid(item, tokens[2]);
1787 if (msginfo != NULL) {
1788 if (replymessageid != NULL) {
1789 MsgPermFlags to_unset = 0;
1791 if (prefs_common.mark_as_read_on_new_window)
1792 to_unset = (MSG_NEW|MSG_UNREAD);
1794 procmsg_msginfo_unset_flags(msginfo, to_unset, 0);
1795 procmsg_msginfo_set_flags(msginfo, MSG_REPLIED, 0);
1797 procmsg_msginfo_set_flags(msginfo, MSG_FORWARDED, 0);
1799 procmsg_msginfo_free(msginfo);
1807 slist_free_strings_full(to_list);
1808 slist_free_strings_full(newsgroup_list);
1809 g_free(savecopyfolder);
1810 g_free(replymessageid);
1811 g_free(fwdmessageid);
1813 return (newsval != 0 ? newsval : mailval);
1816 gint procmsg_send_message_queue(const gchar *file, gchar **errstr, FolderItem *queue, gint msgnum, gboolean *queued_removed)
1818 gint result = procmsg_send_message_queue_full(file, FALSE, errstr, queue, msgnum, queued_removed);
1819 main_window_set_menu_sensitive(mainwindow_get_mainwindow());
1820 toolbar_main_set_sensitive(mainwindow_get_mainwindow());
1824 gint procmsg_send_message_queue_with_lock(const gchar *file, gchar **errstr, FolderItem *queue, gint msgnum, gboolean *queued_removed)
1827 if (procmsg_queue_lock(errstr)) {
1828 val = procmsg_send_message_queue(file, errstr, queue, msgnum, queued_removed);
1829 procmsg_queue_unlock();
1835 static void update_folder_msg_counts(FolderItem *item, MsgInfo *msginfo, MsgPermFlags old_flags)
1837 MsgPermFlags new_flags = msginfo->flags.perm_flags;
1840 if (!(old_flags & MSG_NEW) && (new_flags & MSG_NEW)) {
1844 if ((old_flags & MSG_NEW) && !(new_flags & MSG_NEW)) {
1849 if (!(old_flags & MSG_UNREAD) && (new_flags & MSG_UNREAD)) {
1850 item->unread_msgs++;
1851 if (procmsg_msg_has_marked_parent(msginfo))
1852 item->unreadmarked_msgs++;
1855 if ((old_flags & MSG_UNREAD) && !(new_flags & MSG_UNREAD)) {
1856 item->unread_msgs--;
1857 if (procmsg_msg_has_marked_parent(msginfo))
1858 item->unreadmarked_msgs--;
1862 if (!(old_flags & MSG_MARKED) && (new_flags & MSG_MARKED)) {
1863 procmsg_update_unread_children(msginfo, TRUE);
1864 item->marked_msgs++;
1867 if ((old_flags & MSG_MARKED) && !(new_flags & MSG_MARKED)) {
1868 procmsg_update_unread_children(msginfo, FALSE);
1869 item->marked_msgs--;
1872 if (!(old_flags & MSG_REPLIED) && (new_flags & MSG_REPLIED)) {
1873 item->replied_msgs++;
1876 if ((old_flags & MSG_REPLIED) && !(new_flags & MSG_REPLIED)) {
1877 item->replied_msgs--;
1880 if (!(old_flags & MSG_FORWARDED) && (new_flags & MSG_FORWARDED)) {
1881 item->forwarded_msgs++;
1884 if ((old_flags & MSG_FORWARDED) && !(new_flags & MSG_FORWARDED)) {
1885 item->forwarded_msgs--;
1888 if (!(old_flags & MSG_LOCKED) && (new_flags & MSG_LOCKED)) {
1889 item->locked_msgs++;
1892 if ((old_flags & MSG_LOCKED) && !(new_flags & MSG_LOCKED)) {
1893 item->locked_msgs--;
1896 if ((old_flags & MSG_IGNORE_THREAD) && !(new_flags & MSG_IGNORE_THREAD)) {
1897 item->ignored_msgs--;
1900 if (!(old_flags & MSG_IGNORE_THREAD) && (new_flags & MSG_IGNORE_THREAD)) {
1901 item->ignored_msgs++;
1904 if ((old_flags & MSG_WATCH_THREAD) && !(new_flags & MSG_WATCH_THREAD)) {
1905 item->watched_msgs--;
1908 if (!(old_flags & MSG_WATCH_THREAD) && (new_flags & MSG_WATCH_THREAD)) {
1909 item->watched_msgs++;
1913 void procmsg_msginfo_set_flags(MsgInfo *msginfo, MsgPermFlags perm_flags, MsgTmpFlags tmp_flags)
1916 MsgInfoUpdate msginfo_update;
1917 MsgPermFlags perm_flags_new, perm_flags_old;
1918 MsgTmpFlags tmp_flags_old;
1920 cm_return_if_fail(msginfo != NULL);
1921 item = msginfo->folder;
1922 cm_return_if_fail(item != NULL);
1924 debug_print("Setting flags for message %d in folder %s\n", msginfo->msgnum, item->path);
1926 /* Perm Flags handling */
1927 perm_flags_old = msginfo->flags.perm_flags;
1928 perm_flags_new = msginfo->flags.perm_flags | perm_flags;
1929 if ((perm_flags & MSG_IGNORE_THREAD) || (perm_flags_old & MSG_IGNORE_THREAD)) {
1930 perm_flags_new &= ~(MSG_NEW | MSG_UNREAD);
1932 if ((perm_flags & MSG_WATCH_THREAD) || (perm_flags_old & MSG_WATCH_THREAD)) {
1933 perm_flags_new &= ~(MSG_IGNORE_THREAD);
1936 if (perm_flags_old != perm_flags_new) {
1937 folder_item_change_msg_flags(msginfo->folder, msginfo, perm_flags_new);
1939 update_folder_msg_counts(item, msginfo, perm_flags_old);
1940 summary_update_unread(mainwindow_get_mainwindow()->summaryview, NULL);
1943 /* Tmp flags handling */
1944 tmp_flags_old = msginfo->flags.tmp_flags;
1945 msginfo->flags.tmp_flags |= tmp_flags;
1947 /* update notification */
1948 if ((perm_flags_old != perm_flags_new) || (tmp_flags_old != msginfo->flags.tmp_flags)) {
1949 msginfo_update.msginfo = msginfo;
1950 msginfo_update.flags = MSGINFO_UPDATE_FLAGS;
1951 hooks_invoke(MSGINFO_UPDATE_HOOKLIST, &msginfo_update);
1952 folder_item_update(msginfo->folder, F_ITEM_UPDATE_MSGCNT);
1956 void procmsg_msginfo_unset_flags(MsgInfo *msginfo, MsgPermFlags perm_flags, MsgTmpFlags tmp_flags)
1959 MsgInfoUpdate msginfo_update;
1960 MsgPermFlags perm_flags_new, perm_flags_old;
1961 MsgTmpFlags tmp_flags_old;
1963 cm_return_if_fail(msginfo != NULL);
1964 item = msginfo->folder;
1965 cm_return_if_fail(item != NULL);
1967 debug_print("Unsetting flags for message %d in folder %s\n", msginfo->msgnum, item->path);
1969 /* Perm Flags handling */
1970 perm_flags_old = msginfo->flags.perm_flags;
1971 perm_flags_new = msginfo->flags.perm_flags & ~perm_flags;
1973 if (perm_flags_old != perm_flags_new) {
1974 folder_item_change_msg_flags(msginfo->folder, msginfo, perm_flags_new);
1976 update_folder_msg_counts(item, msginfo, perm_flags_old);
1979 /* Tmp flags hanlding */
1980 tmp_flags_old = msginfo->flags.tmp_flags;
1981 msginfo->flags.tmp_flags &= ~tmp_flags;
1983 /* update notification */
1984 if ((perm_flags_old != perm_flags_new) || (tmp_flags_old != msginfo->flags.tmp_flags)) {
1985 msginfo_update.msginfo = msginfo;
1986 msginfo_update.flags = MSGINFO_UPDATE_FLAGS;
1987 hooks_invoke(MSGINFO_UPDATE_HOOKLIST, &msginfo_update);
1988 folder_item_update(msginfo->folder, F_ITEM_UPDATE_MSGCNT);
1992 void procmsg_msginfo_change_flags(MsgInfo *msginfo,
1993 MsgPermFlags add_perm_flags, MsgTmpFlags add_tmp_flags,
1994 MsgPermFlags rem_perm_flags, MsgTmpFlags rem_tmp_flags)
1997 MsgInfoUpdate msginfo_update;
1998 MsgPermFlags perm_flags_new, perm_flags_old;
1999 MsgTmpFlags tmp_flags_old;
2001 cm_return_if_fail(msginfo != NULL);
2002 item = msginfo->folder;
2003 cm_return_if_fail(item != NULL);
2005 debug_print("Changing flags for message %d in folder %s\n", msginfo->msgnum, item->path);
2007 /* Perm Flags handling */
2008 perm_flags_old = msginfo->flags.perm_flags;
2009 perm_flags_new = (msginfo->flags.perm_flags & ~rem_perm_flags) | add_perm_flags;
2010 if ((add_perm_flags & MSG_IGNORE_THREAD) || (perm_flags_old & MSG_IGNORE_THREAD)) {
2011 perm_flags_new &= ~(MSG_NEW | MSG_UNREAD);
2013 if ((add_perm_flags & MSG_WATCH_THREAD) || (perm_flags_old & MSG_WATCH_THREAD)) {
2014 perm_flags_new &= ~(MSG_IGNORE_THREAD);
2017 if (perm_flags_old != perm_flags_new) {
2018 folder_item_change_msg_flags(msginfo->folder, msginfo, perm_flags_new);
2020 update_folder_msg_counts(item, msginfo, perm_flags_old);
2024 /* Tmp flags handling */
2025 tmp_flags_old = msginfo->flags.tmp_flags;
2026 msginfo->flags.tmp_flags &= ~rem_tmp_flags;
2027 msginfo->flags.tmp_flags |= add_tmp_flags;
2029 /* update notification */
2030 if ((perm_flags_old != perm_flags_new) || (tmp_flags_old != msginfo->flags.tmp_flags)) {
2031 msginfo_update.msginfo = msginfo;
2032 msginfo_update.flags = MSGINFO_UPDATE_FLAGS;
2033 hooks_invoke(MSGINFO_UPDATE_HOOKLIST, &msginfo_update);
2034 folder_item_update(msginfo->folder, F_ITEM_UPDATE_MSGCNT);
2039 *\brief check for flags (e.g. mark) in prior msgs of current thread
2041 *\param info Current message
2042 *\param perm_flags Flags to be checked
2043 *\param parentmsgs Hash of prior msgs to avoid loops
2045 *\return gboolean TRUE if perm_flags are found
2047 static gboolean procmsg_msg_has_flagged_parent_real(MsgInfo *info,
2048 MsgPermFlags perm_flags, GHashTable *parentmsgs)
2052 cm_return_val_if_fail(info != NULL, FALSE);
2054 if (info != NULL && info->folder != NULL && info->inreplyto != NULL) {
2055 tmp = folder_item_get_msginfo_by_msgid(info->folder,
2057 if (tmp && (tmp->flags.perm_flags & perm_flags)) {
2058 procmsg_msginfo_free(tmp);
2060 } else if (tmp != NULL) {
2063 if (g_hash_table_lookup(parentmsgs, info)) {
2064 debug_print("loop detected: %d\n",
2068 g_hash_table_insert(parentmsgs, info, "1");
2069 result = procmsg_msg_has_flagged_parent_real(
2070 tmp, perm_flags, parentmsgs);
2072 procmsg_msginfo_free(tmp);
2082 *\brief Callback for cleaning up hash of parentmsgs
2084 static gboolean parentmsgs_hash_remove(gpointer key,
2092 *\brief Set up list of parentmsgs
2093 * See procmsg_msg_has_flagged_parent_real()
2095 gboolean procmsg_msg_has_flagged_parent(MsgInfo *info, MsgPermFlags perm_flags)
2098 static GHashTable *parentmsgs = NULL;
2100 if (parentmsgs == NULL)
2101 parentmsgs = g_hash_table_new(NULL, NULL);
2103 result = procmsg_msg_has_flagged_parent_real(info, perm_flags, parentmsgs);
2104 g_hash_table_foreach_remove(parentmsgs, parentmsgs_hash_remove, NULL);
2110 *\brief Check if msgs prior in thread are marked
2111 * See procmsg_msg_has_flagged_parent_real()
2113 gboolean procmsg_msg_has_marked_parent(MsgInfo *info)
2115 return procmsg_msg_has_flagged_parent(info, MSG_MARKED);
2119 static GSList *procmsg_find_children_func(MsgInfo *info,
2120 GSList *children, GSList *all)
2124 cm_return_val_if_fail(info!=NULL, children);
2125 if (info->msgid == NULL)
2128 for (cur = all; cur != NULL; cur = g_slist_next(cur)) {
2129 MsgInfo *tmp = (MsgInfo *)cur->data;
2130 if (tmp->inreplyto && !strcmp(tmp->inreplyto, info->msgid)) {
2131 /* Check if message is already in the list */
2132 if ((children == NULL) ||
2133 (g_slist_index(children, tmp) == -1)) {
2134 children = g_slist_prepend(children,
2135 procmsg_msginfo_new_ref(tmp));
2136 children = procmsg_find_children_func(tmp,
2145 static GSList *procmsg_find_children (MsgInfo *info)
2150 cm_return_val_if_fail(info!=NULL, NULL);
2151 all = folder_item_get_msg_list(info->folder);
2152 children = procmsg_find_children_func(info, NULL, all);
2153 if (children != NULL) {
2154 for (cur = all; cur != NULL; cur = g_slist_next(cur)) {
2155 /* this will not free the used pointers
2156 created with procmsg_msginfo_new_ref */
2157 procmsg_msginfo_free((MsgInfo *)cur->data);
2165 static void procmsg_update_unread_children(MsgInfo *info, gboolean newly_marked)
2167 GSList *children = procmsg_find_children(info);
2169 for (cur = children; cur != NULL; cur = g_slist_next(cur)) {
2170 MsgInfo *tmp = (MsgInfo *)cur->data;
2171 if(MSG_IS_UNREAD(tmp->flags) && !MSG_IS_IGNORE_THREAD(tmp->flags)) {
2173 info->folder->unreadmarked_msgs++;
2175 info->folder->unreadmarked_msgs--;
2176 folder_item_update(info->folder, F_ITEM_UPDATE_MSGCNT);
2178 procmsg_msginfo_free(tmp);
2180 g_slist_free(children);
2184 * Set the destination folder for a copy or move operation
2186 * \param msginfo The message which's destination folder is changed
2187 * \param to_folder The destination folder for the operation
2189 void procmsg_msginfo_set_to_folder(MsgInfo *msginfo, FolderItem *to_folder)
2191 if(msginfo->to_folder != NULL) {
2192 msginfo->to_folder->op_count--;
2193 folder_item_update(msginfo->to_folder, F_ITEM_UPDATE_MSGCNT);
2195 msginfo->to_folder = to_folder;
2196 if(to_folder != NULL) {
2197 to_folder->op_count++;
2198 folder_item_update(msginfo->to_folder, F_ITEM_UPDATE_MSGCNT);
2203 * Apply filtering actions to the msginfo
2205 * \param msginfo The MsgInfo describing the message that should be filtered
2206 * \return TRUE if the message was moved and MsgInfo is now invalid,
2209 static gboolean procmsg_msginfo_filter(MsgInfo *msginfo, PrefsAccount* ac_prefs)
2211 MailFilteringData mail_filtering_data;
2213 mail_filtering_data.msginfo = msginfo;
2214 mail_filtering_data.msglist = NULL;
2215 mail_filtering_data.filtered = NULL;
2216 mail_filtering_data.unfiltered = NULL;
2217 mail_filtering_data.account = ac_prefs;
2219 if (!ac_prefs || ac_prefs->filterhook_on_recv)
2220 if (hooks_invoke(MAIL_FILTERING_HOOKLIST, &mail_filtering_data))
2223 /* filter if enabled in prefs or move to inbox if not */
2224 if((filtering_rules != NULL) &&
2225 filter_message_by_msginfo(filtering_rules, msginfo, ac_prefs,
2226 FILTERING_INCORPORATION, NULL)) {
2233 void procmsg_msglist_filter(GSList *list, PrefsAccount *ac,
2234 GSList **filtered, GSList **unfiltered,
2237 GSList *cur, *to_do = NULL;
2238 gint total = 0, curnum = 0;
2239 MailFilteringData mail_filtering_data;
2241 cm_return_if_fail(filtered != NULL);
2242 cm_return_if_fail(unfiltered != NULL);
2250 total = g_slist_length(list);
2254 *unfiltered = g_slist_copy(list);
2258 statusbar_print_all(_("Filtering messages...\n"));
2260 mail_filtering_data.msginfo = NULL;
2261 mail_filtering_data.msglist = list;
2262 mail_filtering_data.filtered = NULL;
2263 mail_filtering_data.unfiltered = NULL;
2264 mail_filtering_data.account = ac;
2266 if (!ac || ac->filterhook_on_recv)
2267 hooks_invoke(MAIL_LISTFILTERING_HOOKLIST, &mail_filtering_data);
2269 if (mail_filtering_data.filtered == NULL &&
2270 mail_filtering_data.unfiltered == NULL) {
2271 /* nothing happened */
2272 debug_print(MAIL_LISTFILTERING_HOOKLIST " did nothing. filtering whole list normally.\n");
2275 if (mail_filtering_data.filtered != NULL) {
2276 /* keep track of what's been filtered by the hooks */
2277 debug_print(MAIL_LISTFILTERING_HOOKLIST " filtered some stuff. total %d filtered %d unfilt %d.\n",
2278 g_slist_length(list),
2279 g_slist_length(mail_filtering_data.filtered),
2280 g_slist_length(mail_filtering_data.unfiltered));
2282 *filtered = g_slist_copy(mail_filtering_data.filtered);
2284 if (mail_filtering_data.unfiltered != NULL) {
2285 /* what the hooks didn't handle will go in filtered or
2286 * unfiltered in the next loop */
2287 debug_print(MAIL_LISTFILTERING_HOOKLIST " left unfiltered stuff. total %d filtered %d unfilt %d.\n",
2288 g_slist_length(list),
2289 g_slist_length(mail_filtering_data.filtered),
2290 g_slist_length(mail_filtering_data.unfiltered));
2291 to_do = mail_filtering_data.unfiltered;
2294 for (cur = to_do; cur; cur = cur->next) {
2295 MsgInfo *info = (MsgInfo *)cur->data;
2296 if (procmsg_msginfo_filter(info, ac))
2297 *filtered = g_slist_prepend(*filtered, info);
2299 *unfiltered = g_slist_prepend(*unfiltered, info);
2300 statusbar_progress_all(curnum++, total, prefs_common.statusbar_update_step);
2303 g_slist_free(mail_filtering_data.filtered);
2304 g_slist_free(mail_filtering_data.unfiltered);
2306 *filtered = g_slist_reverse(*filtered);
2307 *unfiltered = g_slist_reverse(*unfiltered);
2309 statusbar_progress_all(0,0,0);
2310 statusbar_pop_all();
2313 MsgInfo *procmsg_msginfo_new_from_mimeinfo(MsgInfo *src_msginfo, MimeInfo *mimeinfo)
2315 MsgInfo *tmp_msginfo = NULL;
2316 MsgFlags flags = {0, 0};
2317 gchar *tmpfile = get_tmp_file();
2318 FILE *fp = g_fopen(tmpfile, "wb");
2320 if (!mimeinfo || mimeinfo->type != MIMETYPE_MESSAGE ||
2321 g_ascii_strcasecmp(mimeinfo->subtype, "rfc822")) {
2322 g_warning("procmsg_msginfo_new_from_mimeinfo(): unsuitable mimeinfo");
2329 if (fp && procmime_write_mimeinfo(mimeinfo, fp) >= 0) {
2332 tmp_msginfo = procheader_parse_file(
2339 if (tmp_msginfo != NULL) {
2341 tmp_msginfo->folder = src_msginfo->folder;
2343 g_warning("procmsg_msginfo_new_from_mimeinfo(): Can't generate new msginfo");
2351 static GSList *spam_learners = NULL;
2353 void procmsg_register_spam_learner (int (*learn_func)(MsgInfo *info, GSList *list, gboolean spam))
2355 if (!g_slist_find(spam_learners, learn_func))
2356 spam_learners = g_slist_append(spam_learners, learn_func);
2357 if (mainwindow_get_mainwindow()) {
2358 main_window_set_menu_sensitive(mainwindow_get_mainwindow());
2359 summary_set_menu_sensitive(
2360 mainwindow_get_mainwindow()->summaryview);
2361 toolbar_main_set_sensitive(mainwindow_get_mainwindow());
2365 void procmsg_unregister_spam_learner (int (*learn_func)(MsgInfo *info, GSList *list, gboolean spam))
2367 spam_learners = g_slist_remove(spam_learners, learn_func);
2368 if (mainwindow_get_mainwindow()) {
2369 main_window_set_menu_sensitive(mainwindow_get_mainwindow());
2370 summary_set_menu_sensitive(
2371 mainwindow_get_mainwindow()->summaryview);
2372 toolbar_main_set_sensitive(mainwindow_get_mainwindow());
2376 gboolean procmsg_spam_can_learn(void)
2378 return g_slist_length(spam_learners) > 0;
2381 int procmsg_spam_learner_learn (MsgInfo *info, GSList *list, gboolean spam)
2383 GSList *cur = spam_learners;
2385 for (; cur; cur = cur->next) {
2386 int ((*func)(MsgInfo *info, GSList *list, gboolean spam)) = cur->data;
2387 ret |= func(info, list, spam);
2392 static gchar *spam_folder_item = NULL;
2393 static FolderItem * (*procmsg_spam_get_folder_func)(MsgInfo *msginfo) = NULL;
2394 void procmsg_spam_set_folder (const char *item_identifier, FolderItem *(*spam_get_folder_func)(MsgInfo *info))
2396 g_free(spam_folder_item);
2397 if (item_identifier)
2398 spam_folder_item = g_strdup(item_identifier);
2400 spam_folder_item = NULL;
2401 if (spam_get_folder_func != NULL)
2402 procmsg_spam_get_folder_func = spam_get_folder_func;
2404 procmsg_spam_get_folder_func = NULL;
2407 FolderItem *procmsg_spam_get_folder (MsgInfo *msginfo)
2409 FolderItem *item = NULL;
2411 if (procmsg_spam_get_folder_func)
2412 item = procmsg_spam_get_folder_func(msginfo);
2413 if (item == NULL && spam_folder_item)
2414 item = folder_find_item_from_identifier(spam_folder_item);
2416 item = folder_get_default_trash();
2420 static void item_has_queued_mails(FolderItem *item, gpointer data)
2422 gboolean *result = (gboolean *)data;
2423 if (*result == TRUE)
2425 if (folder_has_parent_of_type(item, F_QUEUE)) {
2426 if (item->total_msgs == 0)
2429 GSList *msglist = folder_item_get_msg_list(item);
2431 for (cur = msglist; cur; cur = cur->next) {
2432 MsgInfo *msginfo = (MsgInfo *)cur->data;
2433 if (!MSG_IS_DELETED(msginfo->flags) &&
2434 !MSG_IS_LOCKED(msginfo->flags)) {
2439 procmsg_msg_list_free(msglist);
2444 gboolean procmsg_have_queued_mails_fast (void)
2446 gboolean result = FALSE;
2447 folder_func_to_all_folders(item_has_queued_mails, &result);
2451 static void item_has_trashed_mails(FolderItem *item, gpointer data)
2453 gboolean *result = (gboolean *)data;
2454 if (*result == TRUE)
2456 if (folder_has_parent_of_type(item, F_TRASH) && item->total_msgs > 0)
2460 gboolean procmsg_have_trashed_mails_fast (void)
2462 gboolean result = FALSE;
2463 folder_func_to_all_folders(item_has_trashed_mails, &result);
2467 gchar *procmsg_msginfo_get_tags_str(MsgInfo *msginfo)
2475 if (msginfo->tags == NULL)
2477 for (cur = msginfo->tags; cur; cur = cur->next) {
2478 const gchar *tag = tags_get_tag(GPOINTER_TO_INT(cur->data));
2482 tags = g_strdup(tag);
2484 int olen = strlen(tags);
2485 int nlen = olen + strlen(tag) + 2 /* strlen(", ") */;
2486 tags = g_realloc(tags, nlen+1);
2489 strcpy(tags+olen, ", ");
2490 strcpy(tags+olen+2, tag);
2497 void procmsg_msginfo_update_tags(MsgInfo *msginfo, gboolean set, gint id)
2505 msginfo->tags = g_slist_remove(
2507 GINT_TO_POINTER(id));
2508 changed.data = GINT_TO_POINTER(id);
2509 changed.next = NULL;
2510 folder_item_commit_tags(msginfo->folder, msginfo, NULL, &changed);
2512 if (!g_slist_find(msginfo->tags, GINT_TO_POINTER(id))) {
2513 msginfo->tags = g_slist_append(
2515 GINT_TO_POINTER(id));
2517 changed.data = GINT_TO_POINTER(id);
2518 changed.next = NULL;
2519 folder_item_commit_tags(msginfo->folder, msginfo, &changed, NULL);
2524 void procmsg_msginfo_clear_tags(MsgInfo *msginfo)
2526 GSList *unset = msginfo->tags;
2527 msginfo->tags = NULL;
2528 folder_item_commit_tags(msginfo->folder, msginfo, NULL, unset);
2529 g_slist_free(unset);