2 * Sylpheed -- a GTK+ based, lightweight, and fast e-mail client
3 * Copyright (C) 1999-2011 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"
52 static gint procmsg_send_message_queue_full(const gchar *file, gboolean keep_session, gchar **errstr,
53 FolderItem *queue, gint msgnum, gboolean *queued_removed);
54 static void procmsg_update_unread_children (MsgInfo *info,
55 gboolean newly_marked);
62 Q_MAIL_ACCOUNT_ID = 4,
63 Q_NEWS_ACCOUNT_ID = 5,
64 Q_SAVE_COPY_FOLDER = 6,
65 Q_REPLY_MESSAGE_ID = 7,
71 Q_PRIVACY_SYSTEM_OLD = 13,
73 Q_ENCRYPT_DATA_OLD = 15,
74 Q_CLAWS_HDRS_OLD = 16,
77 void procmsg_msg_list_free(GSList *mlist)
82 for (cur = mlist; cur != NULL; cur = cur->next) {
83 msginfo = (MsgInfo *)cur->data;
84 procmsg_msginfo_free(msginfo);
98 /* CLAWS subject threading:
100 in the first round it inserts subject lines in a
101 hashtable (subject <-> node)
103 the second round finishes the threads by attaching
104 matching subject lines to the one found in the
105 hashtable. will use the oldest node with the same
106 subject that is not more then thread_by_subject_max_age
107 days old (see subject_hashtable_lookup)
110 static void subject_hashtable_insert(GHashTable *hashtable, GNode *node)
116 cm_return_if_fail(hashtable != NULL);
117 cm_return_if_fail(node != NULL);
118 msginfo = (MsgInfo *) node->data;
119 cm_return_if_fail(msginfo != NULL);
121 subject = msginfo->subject;
125 subject += subject_get_prefix_length(subject);
127 list = g_hash_table_lookup(hashtable, subject);
128 list = g_slist_prepend(list, node);
129 g_hash_table_insert(hashtable, subject, list);
132 static GNode *subject_hashtable_lookup(GHashTable *hashtable, MsgInfo *msginfo)
136 GNode *node = NULL, *hashtable_node = NULL;
138 MsgInfo *hashtable_msginfo = NULL, *best_msginfo = NULL;
141 cm_return_val_if_fail(hashtable != NULL, NULL);
143 subject = msginfo->subject;
146 prefix_length = subject_get_prefix_length(subject);
147 if (prefix_length <= 0)
149 subject += prefix_length;
151 list = g_hash_table_lookup(hashtable, subject);
155 /* check all nodes with the same subject to find the best parent */
156 for (cur = list; cur; cur = cur->next) {
157 hashtable_node = (GNode *)cur->data;
158 hashtable_msginfo = (MsgInfo *) hashtable_node->data;
161 /* best node should be the oldest in the found nodes */
162 /* parent node must not be older then msginfo */
163 if ((hashtable_msginfo->date_t < msginfo->date_t) &&
164 ((best_msginfo == NULL) ||
165 (best_msginfo->date_t > hashtable_msginfo->date_t)))
168 /* parent node must not be more then thread_by_subject_max_age
169 days older then msginfo */
170 if (abs(difftime(msginfo->date_t, hashtable_msginfo->date_t)) >
171 prefs_common.thread_by_subject_max_age * 3600 * 24)
174 /* can add new tests for all matching
175 nodes found by subject */
178 node = hashtable_node;
179 best_msginfo = hashtable_msginfo;
186 static void subject_hashtable_free(gpointer key, gpointer value, gpointer data)
191 /* return the reversed thread tree */
192 GNode *procmsg_get_thread_tree(GSList *mlist)
194 GNode *root, *parent, *node, *next;
195 GHashTable *msgid_table;
196 GHashTable *subject_hashtable = NULL;
201 root = g_node_new(NULL);
202 msgid_table = g_hash_table_new(g_str_hash, g_str_equal);
204 if (prefs_common.thread_by_subject) {
205 subject_hashtable = g_hash_table_new(g_str_hash, g_str_equal);
208 for (; mlist != NULL; mlist = mlist->next) {
209 msginfo = (MsgInfo *)mlist->data;
212 if (msginfo->inreplyto) {
213 parent = g_hash_table_lookup(msgid_table, msginfo->inreplyto);
214 if (parent == NULL) {
218 node = g_node_insert_data_before
219 (parent, parent == root ? parent->children : NULL,
221 if ((msgid = msginfo->msgid) && g_hash_table_lookup(msgid_table, msgid) == NULL)
222 g_hash_table_insert(msgid_table, (gchar *)msgid, node);
224 /* CLAWS: add subject to hashtable (without prefix) */
225 if (prefs_common.thread_by_subject) {
226 subject_hashtable_insert(subject_hashtable, node);
230 /* complete the unfinished threads */
231 for (node = root->children; node != NULL; ) {
233 msginfo = (MsgInfo *)node->data;
236 if (msginfo->inreplyto)
237 parent = g_hash_table_lookup(msgid_table, msginfo->inreplyto);
239 /* try looking for the indirect parent */
240 if (!parent && msginfo->references) {
241 for (reflist = msginfo->references;
242 reflist != NULL; reflist = reflist->next)
243 if ((parent = g_hash_table_lookup
244 (msgid_table, reflist->data)) != NULL)
248 /* node should not be the parent, and node should not
249 be an ancestor of parent (circular reference) */
250 if (parent && parent != node &&
251 !g_node_is_ancestor(node, parent)) {
254 (parent, parent->children, node);
260 if (prefs_common.thread_by_subject) {
261 START_TIMING("thread by subject");
262 for (node = root->children; node && node != NULL;) {
264 msginfo = (MsgInfo *) node->data;
266 parent = subject_hashtable_lookup(subject_hashtable, msginfo);
268 /* the node may already be threaded by IN-REPLY-TO, so go up
270 find the parent node */
271 if (parent != NULL) {
272 if (g_node_is_ancestor(node, parent))
280 g_node_append(parent, node);
288 if (prefs_common.thread_by_subject)
290 g_hash_table_foreach(subject_hashtable, subject_hashtable_free, NULL);
291 g_hash_table_destroy(subject_hashtable);
294 g_hash_table_destroy(msgid_table);
299 gint procmsg_move_messages(GSList *mlist)
301 GSList *cur, *movelist = NULL;
303 FolderItem *dest = NULL;
305 gboolean finished = TRUE;
306 if (!mlist) return 0;
308 folder_item_update_freeze();
311 for (cur = mlist; cur != NULL; cur = cur->next) {
312 msginfo = (MsgInfo *)cur->data;
313 if (!msginfo->to_folder) {
319 dest = msginfo->to_folder;
320 movelist = g_slist_prepend(movelist, msginfo);
321 } else if (dest == msginfo->to_folder) {
322 movelist = g_slist_prepend(movelist, msginfo);
326 procmsg_msginfo_set_to_folder(msginfo, NULL);
329 movelist = g_slist_reverse(movelist);
330 retval |= folder_item_move_msgs(dest, movelist);
331 g_slist_free(movelist);
334 if (finished == FALSE) {
340 folder_item_update_thaw();
344 void procmsg_copy_messages(GSList *mlist)
346 GSList *cur, *copylist = NULL;
348 FolderItem *dest = NULL;
349 gboolean finished = TRUE;
352 folder_item_update_freeze();
355 for (cur = mlist; cur != NULL; cur = cur->next) {
356 msginfo = (MsgInfo *)cur->data;
357 if (!msginfo->to_folder) {
363 dest = msginfo->to_folder;
364 copylist = g_slist_prepend(copylist, msginfo);
365 } else if (dest == msginfo->to_folder) {
366 copylist = g_slist_prepend(copylist, msginfo);
370 procmsg_msginfo_set_to_folder(msginfo, NULL);
373 copylist = g_slist_reverse(copylist);
374 folder_item_copy_msgs(dest, copylist);
375 g_slist_free(copylist);
378 if (finished == FALSE) {
384 folder_item_update_thaw();
387 gchar *procmsg_get_message_file_path(MsgInfo *msginfo)
391 cm_return_val_if_fail(msginfo != NULL, NULL);
393 if (msginfo->plaintext_file)
394 file = g_strdup(msginfo->plaintext_file);
396 file = folder_item_fetch_msg(msginfo->folder, msginfo->msgnum);
402 gchar *procmsg_get_message_file(MsgInfo *msginfo)
404 gchar *filename = NULL;
406 cm_return_val_if_fail(msginfo != NULL, NULL);
408 filename = folder_item_fetch_msg(msginfo->folder, msginfo->msgnum);
410 debug_print("can't fetch message %d\n", msginfo->msgnum);
415 gchar *procmsg_get_message_file_full(MsgInfo *msginfo, gboolean headers, gboolean body)
417 gchar *filename = NULL;
419 cm_return_val_if_fail(msginfo != NULL, NULL);
421 filename = folder_item_fetch_msg_full(msginfo->folder, msginfo->msgnum,
424 debug_print("can't fetch message %d\n", msginfo->msgnum);
429 GSList *procmsg_get_message_file_list(GSList *mlist)
431 GSList *file_list = NULL;
433 MsgFileInfo *fileinfo;
436 while (mlist != NULL) {
437 msginfo = (MsgInfo *)mlist->data;
438 file = procmsg_get_message_file(msginfo);
440 procmsg_message_file_list_free(file_list);
443 fileinfo = g_new(MsgFileInfo, 1);
444 fileinfo->msginfo = procmsg_msginfo_new_ref(msginfo);
445 fileinfo->file = file;
446 fileinfo->flags = g_new(MsgFlags, 1);
447 *fileinfo->flags = msginfo->flags;
448 file_list = g_slist_prepend(file_list, fileinfo);
452 file_list = g_slist_reverse(file_list);
457 void procmsg_message_file_list_free(MsgInfoList *file_list)
460 MsgFileInfo *fileinfo;
462 for (cur = file_list; cur != NULL; cur = cur->next) {
463 fileinfo = (MsgFileInfo *)cur->data;
464 procmsg_msginfo_free(fileinfo->msginfo);
465 g_free(fileinfo->file);
466 g_free(fileinfo->flags);
470 g_slist_free(file_list);
473 FILE *procmsg_open_message(MsgInfo *msginfo)
478 cm_return_val_if_fail(msginfo != NULL, NULL);
480 file = procmsg_get_message_file_path(msginfo);
481 cm_return_val_if_fail(file != NULL, NULL);
483 if (!is_file_exist(file)) {
485 file = procmsg_get_message_file(msginfo);
490 if ((fp = g_fopen(file, "rb")) == NULL) {
491 FILE_OP_ERROR(file, "fopen");
498 if (MSG_IS_QUEUED(msginfo->flags) || MSG_IS_DRAFT(msginfo->flags)) {
501 while (fgets(buf, sizeof(buf), fp) != NULL) {
503 if ((!strncmp(buf, "X-Claws-End-Special-Headers: 1",
504 strlen("X-Claws-End-Special-Headers:"))) ||
505 (!strncmp(buf, "X-Sylpheed-End-Special-Headers: 1",
506 strlen("X-Sylpheed-End-Special-Headers:"))))
509 if (buf[0] == '\r' || buf[0] == '\n') break;
510 /* from other mailers */
511 if (!strncmp(buf, "Date: ", 6)
512 || !strncmp(buf, "To: ", 4)
513 || !strncmp(buf, "From: ", 6)
514 || !strncmp(buf, "Subject: ", 9)) {
524 gboolean procmsg_msg_exist(MsgInfo *msginfo)
529 if (!msginfo) return FALSE;
531 path = folder_item_get_path(msginfo->folder);
533 ret = !folder_item_is_msg_changed(msginfo->folder, msginfo);
539 void procmsg_get_filter_keyword(MsgInfo *msginfo, gchar **header, gchar **key,
540 PrefsFilterType type)
542 static HeaderEntry hentry[] = {{"X-BeenThere:", NULL, TRUE},
543 {"X-ML-Name:", NULL, TRUE},
544 {"X-List:", NULL, TRUE},
545 {"X-Mailing-list:", NULL, TRUE},
546 {"List-Id:", NULL, TRUE},
547 {"X-Sequence:", NULL, TRUE},
548 {"Sender:", NULL, TRUE},
549 {"List-Post:", NULL, TRUE},
550 {NULL, NULL, FALSE}};
556 H_X_MAILING_LIST = 3,
565 cm_return_if_fail(msginfo != NULL);
566 cm_return_if_fail(header != NULL);
567 cm_return_if_fail(key != NULL);
576 if ((fp = procmsg_open_message(msginfo)) == NULL)
578 procheader_get_header_fields(fp, hentry);
581 #define SET_FILTER_KEY(hstr, idx) \
583 *header = g_strdup(hstr); \
584 *key = hentry[idx].body; \
585 hentry[idx].body = NULL; \
588 if (hentry[H_LIST_ID].body != NULL) {
589 SET_FILTER_KEY("header \"List-Id\"", H_LIST_ID);
590 extract_list_id_str(*key);
591 } else if (hentry[H_X_BEENTHERE].body != NULL) {
592 SET_FILTER_KEY("header \"X-BeenThere\"", H_X_BEENTHERE);
593 } else if (hentry[H_X_ML_NAME].body != NULL) {
594 SET_FILTER_KEY("header \"X-ML-Name\"", H_X_ML_NAME);
595 } else if (hentry[H_X_LIST].body != NULL) {
596 SET_FILTER_KEY("header \"X-List\"", H_X_LIST);
597 } else if (hentry[H_X_MAILING_LIST].body != NULL) {
598 SET_FILTER_KEY("header \"X-Mailing-List\"", H_X_MAILING_LIST);
599 } else if (hentry[H_X_SEQUENCE].body != NULL) {
602 SET_FILTER_KEY("X-Sequence", H_X_SEQUENCE);
605 while (*p != '\0' && !g_ascii_isspace(*p)) p++;
606 while (g_ascii_isspace(*p)) p++;
607 if (g_ascii_isdigit(*p)) {
613 } else if (hentry[H_SENDER].body != NULL) {
614 SET_FILTER_KEY("header \"Sender\"", H_SENDER);
615 } else if (hentry[H_LIST_POST].body != NULL) {
616 SET_FILTER_KEY("header \"List-Post\"", H_LIST_POST);
617 } else if (msginfo->to) {
618 *header = g_strdup("to");
619 *key = g_strdup(msginfo->to);
620 } else if (msginfo->subject) {
621 *header = g_strdup("subject");
622 *key = g_strdup(msginfo->subject);
625 #undef SET_FILTER_KEY
627 g_free(hentry[H_X_BEENTHERE].body);
628 hentry[H_X_BEENTHERE].body = NULL;
629 g_free(hentry[H_X_ML_NAME].body);
630 hentry[H_X_ML_NAME].body = NULL;
631 g_free(hentry[H_X_LIST].body);
632 hentry[H_X_LIST].body = NULL;
633 g_free(hentry[H_X_MAILING_LIST].body);
634 hentry[H_X_MAILING_LIST].body = NULL;
635 g_free(hentry[H_LIST_ID].body);
636 hentry[H_LIST_ID].body = NULL;
637 g_free(hentry[H_SENDER].body);
638 hentry[H_SENDER].body = NULL;
639 g_free(hentry[H_LIST_POST].body);
640 hentry[H_LIST_POST].body = NULL;
644 *header = g_strdup("from");
645 *key = g_strdup(msginfo->from);
648 *header = g_strdup("to");
649 *key = g_strdup(msginfo->to);
651 case FILTER_BY_SUBJECT:
652 *header = g_strdup("subject");
653 *key = g_strdup(msginfo->subject);
660 static void procmsg_empty_trash(FolderItem *trash)
665 (trash->stype != F_TRASH &&
666 !folder_has_parent_of_type(trash, F_TRASH)))
669 if (trash && trash->total_msgs > 0) {
670 GSList *mlist = folder_item_get_msg_list(trash);
672 for (cur = mlist ; cur != NULL ; cur = cur->next) {
673 MsgInfo * msginfo = (MsgInfo *) cur->data;
674 if (MSG_IS_LOCKED(msginfo->flags)) {
675 procmsg_msginfo_free(msginfo);
678 if (msginfo->total_size != 0 &&
679 msginfo->size != (off_t)msginfo->total_size)
680 partial_mark_for_delete(msginfo);
682 procmsg_msginfo_free(msginfo);
685 folder_item_remove_all_msg(trash);
688 if (!trash->node || !trash->node->children)
691 node = trash->node->children;
692 while (node != NULL) {
694 procmsg_empty_trash(FOLDER_ITEM(node->data));
699 void procmsg_empty_all_trash(void)
704 for (cur = folder_get_list(); cur != NULL; cur = cur->next) {
705 Folder *folder = FOLDER(cur->data);
706 trash = folder->trash;
707 procmsg_empty_trash(trash);
708 if (folder->account && folder->account->set_trash_folder &&
709 folder_find_item_from_identifier(folder->account->trash_folder))
711 folder_find_item_from_identifier(folder->account->trash_folder));
715 static PrefsAccount *procmsg_get_account_from_file(const gchar *file)
717 PrefsAccount *mailac = NULL;
721 static HeaderEntry qentry[] = {{"S:", NULL, FALSE},
722 {"SSV:", NULL, FALSE},
724 {"NG:", NULL, FALSE},
725 {"MAID:", NULL, FALSE},
726 {"NAID:", NULL, FALSE},
727 {"SCF:", NULL, FALSE},
728 {"RMID:", NULL, FALSE},
729 {"FMID:", NULL, FALSE},
730 {"X-Claws-Privacy-System:", NULL, FALSE},
731 {"X-Claws-Encrypt:", NULL, FALSE},
732 {"X-Claws-Encrypt-Data:", NULL, FALSE},
733 {"X-Claws-End-Special-Headers", NULL, FALSE},
734 {"X-Sylpheed-Privacy-System:", NULL, FALSE},
735 {"X-Sylpheed-Encrypt:", NULL, FALSE},
736 {"X-Sylpheed-Encrypt-Data:", NULL, FALSE},
737 {NULL, NULL, FALSE}};
739 cm_return_val_if_fail(file != NULL, NULL);
741 if ((fp = g_fopen(file, "rb")) == NULL) {
742 FILE_OP_ERROR(file, "fopen");
746 while ((hnum = procheader_get_one_field(buf, sizeof(buf), fp, qentry))
748 gchar *p = buf + strlen(qentry[hnum].name);
750 if (hnum == Q_MAIL_ACCOUNT_ID) {
751 mailac = account_find_from_id(atoi(p));
759 static GSList *procmsg_list_sort_by_account(FolderItem *queue, GSList *list)
761 GSList *result = NULL;
763 PrefsAccount *last_account = NULL;
766 gboolean nothing_to_sort = TRUE;
771 orig = g_slist_copy(list);
773 msg = (MsgInfo *)orig->data;
775 for (cur = orig; cur; cur = cur->next)
776 debug_print("sort before %s\n", ((MsgInfo *)cur->data)->from);
781 nothing_to_sort = TRUE;
785 PrefsAccount *ac = NULL;
786 msg = (MsgInfo *)cur->data;
787 file = folder_item_fetch_msg(queue, msg->msgnum);
788 ac = procmsg_get_account_from_file(file);
791 if (last_account == NULL || (ac != NULL && ac == last_account)) {
792 result = g_slist_append(result, msg);
793 orig = g_slist_remove(orig, msg);
795 nothing_to_sort = FALSE;
801 if (orig || g_slist_length(orig)) {
802 if (!last_account && nothing_to_sort) {
803 /* can't find an account for the rest of the list */
806 result = g_slist_append(result, cur->data);
817 for (cur = result; cur; cur = cur->next)
818 debug_print("sort after %s\n", ((MsgInfo *)cur->data)->from);
825 static gboolean procmsg_is_last_for_account(FolderItem *queue, MsgInfo *msginfo, GSList *elem)
827 gchar *file = folder_item_fetch_msg(queue, msginfo->msgnum);
828 PrefsAccount *ac = procmsg_get_account_from_file(file);
831 for (cur = elem; cur; cur = cur->next) {
832 MsgInfo *cur_msginfo = (MsgInfo *)cur->data;
833 file = folder_item_fetch_msg(queue, cur_msginfo->msgnum);
835 if (cur_msginfo != msginfo && !MSG_IS_LOCKED(cur_msginfo->flags)) {
836 if (procmsg_get_account_from_file(file) == ac) {
847 static gboolean send_queue_lock = FALSE;
849 *\brief Send messages in queue
851 *\param queue Queue folder to process
852 *\param save_msgs Unused
854 *\return Number of messages sent, negative if an error occurred
855 * positive if no error occurred
857 gint procmsg_send_queue(FolderItem *queue, gboolean save_msgs, gchar **errstr)
859 gint sent = 0, err = 0;
861 GSList *sorted_list = NULL;
864 if (send_queue_lock) {
865 /* Avoid having to translate two similar strings */
866 log_warning(LOG_PROTOCOL, "%s\n", _("Already trying to send."));
868 if (*errstr) g_free(*errstr);
869 *errstr = g_strdup_printf(_("Already trying to send."));
871 toolbar_main_set_sensitive(mainwindow_get_mainwindow());
874 send_queue_lock = TRUE;
877 queue = folder_get_default_queue();
880 send_queue_lock = FALSE;
885 toolbar_main_set_sensitive(mainwindow_get_mainwindow());
887 folder_item_scan(queue);
888 list = folder_item_get_msg_list(queue);
890 /* sort the list per sender account; this helps reusing the same SMTP server */
891 sorted_list = procmsg_list_sort_by_account(queue, list);
893 for (elem = sorted_list; elem != NULL; elem = elem->next) {
897 msginfo = (MsgInfo *)(elem->data);
898 if (!MSG_IS_LOCKED(msginfo->flags) && !MSG_IS_DELETED(msginfo->flags)) {
899 file = folder_item_fetch_msg(queue, msginfo->msgnum);
901 gboolean queued_removed = FALSE;
902 if (procmsg_send_message_queue_full(file,
903 !procmsg_is_last_for_account(queue, msginfo, elem),
904 errstr, queue, msginfo->msgnum, &queued_removed) < 0) {
905 g_warning("Sending queued message %d failed.\n",
911 folder_item_remove_msg(queue, msginfo->msgnum);
916 /* FIXME: supposedly if only one message is locked, and queue
917 * is being flushed, the following free says something like
918 * "freeing msg ## in folder (nil)". */
919 procmsg_msginfo_free(msginfo);
922 g_slist_free(sorted_list);
923 folder_item_scan(queue);
925 if (queue->node && queue->node->children) {
926 node = queue->node->children;
927 while (node != NULL) {
930 send_queue_lock = FALSE;
931 res = procmsg_send_queue(FOLDER_ITEM(node->data), save_msgs, errstr);
932 send_queue_lock = TRUE;
940 send_queue_lock = FALSE;
942 toolbar_main_set_sensitive(mainwindow_get_mainwindow());
944 return (err != 0 ? -err : sent);
947 gboolean procmsg_is_sending(void)
949 return send_queue_lock;
953 *\brief Determine if a queue folder is empty
955 *\param queue Queue folder to process
957 *\return TRUE if the queue folder is empty, otherwise return FALSE
959 gboolean procmsg_queue_is_empty(FolderItem *queue)
962 gboolean res = FALSE;
964 queue = folder_get_default_queue();
965 cm_return_val_if_fail(queue != NULL, TRUE);
967 folder_item_scan(queue);
968 list = folder_item_get_msg_list(queue);
969 res = (list == NULL);
970 procmsg_msg_list_free(list);
974 if (queue->node && queue->node->children) {
975 node = queue->node->children;
976 while (node != NULL) {
978 if (!procmsg_queue_is_empty(FOLDER_ITEM(node->data)))
987 gint procmsg_remove_special_headers(const gchar *in, const gchar *out)
992 if ((fp = g_fopen(in, "rb")) == NULL) {
993 FILE_OP_ERROR(in, "fopen");
996 if ((outfp = g_fopen(out, "wb")) == NULL) {
997 FILE_OP_ERROR(out, "fopen");
1001 while (fgets(buf, sizeof(buf), fp) != NULL) {
1003 if ((!strncmp(buf, "X-Claws-End-Special-Headers: 1",
1004 strlen("X-Claws-End-Special-Headers:"))) ||
1005 (!strncmp(buf, "X-Sylpheed-End-Special-Headers: 1",
1006 strlen("X-Sylpheed-End-Special-Headers:"))))
1009 if (buf[0] == '\r' || buf[0] == '\n') break;
1010 /* from other mailers */
1011 if (!strncmp(buf, "Date: ", 6)
1012 || !strncmp(buf, "To: ", 4)
1013 || !strncmp(buf, "From: ", 6)
1014 || !strncmp(buf, "Subject: ", 9)) {
1019 while (fgets(buf, sizeof(buf), fp) != NULL)
1026 static gint procmsg_save_to_outbox(FolderItem *outbox, const gchar *file,
1030 MsgInfo *msginfo, *tmp_msginfo;
1031 MsgFlags flag = {0, 0};
1033 debug_print("saving sent message...\n");
1036 outbox = folder_get_default_outbox();
1037 cm_return_val_if_fail(outbox != NULL, -1);
1039 /* remove queueing headers */
1041 gchar tmp[MAXPATHLEN + 1];
1043 g_snprintf(tmp, sizeof(tmp), "%s%ctmpmsg.out.%08x",
1044 get_rc_dir(), G_DIR_SEPARATOR, (guint) rand());
1046 if (procmsg_remove_special_headers(file, tmp) !=0)
1049 folder_item_scan(outbox);
1050 if ((num = folder_item_add_msg(outbox, tmp, &flag, TRUE)) < 0) {
1051 g_warning("can't save message\n");
1056 folder_item_scan(outbox);
1057 if ((num = folder_item_add_msg
1058 (outbox, file, &flag, FALSE)) < 0) {
1059 g_warning("can't save message\n");
1063 msginfo = folder_item_get_msginfo(outbox, num); /* refcnt++ */
1064 tmp_msginfo = procmsg_msginfo_get_full_info(msginfo); /* refcnt++ */
1065 if (msginfo != NULL) {
1066 procmsg_msginfo_unset_flags(msginfo, ~0, 0);
1067 procmsg_msginfo_free(msginfo); /* refcnt-- */
1068 /* tmp_msginfo == msginfo */
1069 if (tmp_msginfo && msginfo->extradata &&
1070 (msginfo->extradata->dispositionnotificationto ||
1071 msginfo->extradata->returnreceiptto)) {
1072 procmsg_msginfo_set_flags(msginfo, MSG_RETRCPT_SENT, 0);
1074 procmsg_msginfo_free(tmp_msginfo); /* refcnt-- */
1080 void procmsg_print_message(MsgInfo *msginfo, const gchar *cmdline)
1082 static const gchar *def_cmd = "lpr %s";
1083 static guint id = 0;
1089 cm_return_if_fail(msginfo);
1091 if (procmime_msginfo_is_encrypted(msginfo))
1092 tmpfp = procmime_get_first_encrypted_text_content(msginfo);
1094 tmpfp = procmime_get_first_text_content(msginfo);
1095 if (tmpfp == NULL) {
1096 g_warning("Can't get text part\n");
1100 prtmp = g_strdup_printf("%s%cprinttmp.%08x",
1101 get_mime_tmp_dir(), G_DIR_SEPARATOR, id++);
1103 if ((prfp = g_fopen(prtmp, "wb")) == NULL) {
1104 FILE_OP_ERROR(prtmp, "fopen");
1110 if (msginfo->date) r = fprintf(prfp, "Date: %s\n", msginfo->date);
1111 if (msginfo->from) r = fprintf(prfp, "From: %s\n", msginfo->from);
1112 if (msginfo->to) r = fprintf(prfp, "To: %s\n", msginfo->to);
1113 if (msginfo->cc) r = fprintf(prfp, "Cc: %s\n", msginfo->cc);
1114 if (msginfo->newsgroups)
1115 r = fprintf(prfp, "Newsgroups: %s\n", msginfo->newsgroups);
1116 if (msginfo->subject) r = fprintf(prfp, "Subject: %s\n", msginfo->subject);
1119 while (fgets(buf, sizeof(buf), tmpfp) != NULL)
1120 r = fputs(buf, prfp);
1125 if (cmdline && (p = strchr(cmdline, '%')) && *(p + 1) == 's' &&
1126 !strchr(p + 2, '%'))
1127 g_snprintf(buf, sizeof(buf) - 1, cmdline, prtmp);
1130 g_warning("Print command-line is invalid: '%s'\n",
1132 g_snprintf(buf, sizeof(buf) - 1, def_cmd, prtmp);
1138 if (buf[strlen(buf) - 1] != '&') strcat(buf, "&");
1139 if (system(buf) == -1)
1140 g_warning("system(%s) failed.", buf);
1143 MsgInfo *procmsg_msginfo_new_ref(MsgInfo *msginfo)
1150 MsgInfo *procmsg_msginfo_new(void)
1152 MsgInfo *newmsginfo;
1154 newmsginfo = g_new0(MsgInfo, 1);
1155 newmsginfo->refcnt = 1;
1160 MsgInfo *procmsg_msginfo_copy(MsgInfo *msginfo)
1162 MsgInfo *newmsginfo;
1165 if (msginfo == NULL) return NULL;
1167 newmsginfo = g_new0(MsgInfo, 1);
1169 newmsginfo->refcnt = 1;
1171 #define MEMBCOPY(mmb) newmsginfo->mmb = msginfo->mmb
1172 #define MEMBDUP(mmb) newmsginfo->mmb = msginfo->mmb ? \
1173 g_strdup(msginfo->mmb) : NULL
1188 MEMBDUP(newsgroups);
1195 MEMBCOPY(to_folder);
1197 if (msginfo->extradata) {
1198 newmsginfo->extradata = g_new0(MsgInfoExtraData, 1);
1199 MEMBDUP(extradata->face);
1200 MEMBDUP(extradata->xface);
1201 MEMBDUP(extradata->dispositionnotificationto);
1202 MEMBDUP(extradata->returnreceiptto);
1203 MEMBDUP(extradata->partial_recv);
1204 MEMBDUP(extradata->account_server);
1205 MEMBDUP(extradata->account_login);
1206 MEMBDUP(extradata->list_post);
1207 MEMBDUP(extradata->list_subscribe);
1208 MEMBDUP(extradata->list_unsubscribe);
1209 MEMBDUP(extradata->list_help);
1210 MEMBDUP(extradata->list_archive);
1211 MEMBDUP(extradata->list_owner);
1214 refs = msginfo->references;
1215 for (refs = msginfo->references; refs != NULL; refs = refs->next) {
1216 newmsginfo->references = g_slist_prepend
1217 (newmsginfo->references, g_strdup(refs->data));
1219 newmsginfo->references = g_slist_reverse(newmsginfo->references);
1222 MEMBDUP(plaintext_file);
1227 MsgInfo *procmsg_msginfo_get_full_info_from_file(MsgInfo *msginfo, const gchar *file)
1229 MsgInfo *full_msginfo;
1231 if (msginfo == NULL) return NULL;
1233 if (!file || !is_file_exist(file)) {
1234 g_warning("procmsg_msginfo_get_full_info_from_file(): can't get message file.\n");
1238 full_msginfo = procheader_parse_file(file, msginfo->flags, TRUE, FALSE);
1239 if (!full_msginfo) return NULL;
1241 msginfo->total_size = full_msginfo->total_size;
1242 msginfo->planned_download = full_msginfo->planned_download;
1244 if (full_msginfo->extradata) {
1245 if (!msginfo->extradata)
1246 msginfo->extradata = g_new0(MsgInfoExtraData, 1);
1247 if (!msginfo->extradata->list_post)
1248 msginfo->extradata->list_post = g_strdup(full_msginfo->extradata->list_post);
1249 if (!msginfo->extradata->list_subscribe)
1250 msginfo->extradata->list_subscribe = g_strdup(full_msginfo->extradata->list_subscribe);
1251 if (!msginfo->extradata->list_unsubscribe)
1252 msginfo->extradata->list_unsubscribe = g_strdup(full_msginfo->extradata->list_unsubscribe);
1253 if (!msginfo->extradata->list_help)
1254 msginfo->extradata->list_help = g_strdup(full_msginfo->extradata->list_help);
1255 if (!msginfo->extradata->list_archive)
1256 msginfo->extradata->list_archive= g_strdup(full_msginfo->extradata->list_archive);
1257 if (!msginfo->extradata->list_owner)
1258 msginfo->extradata->list_owner = g_strdup(full_msginfo->extradata->list_owner);
1259 if (!msginfo->extradata->xface)
1260 msginfo->extradata->xface = g_strdup(full_msginfo->extradata->xface);
1261 if (!msginfo->extradata->face)
1262 msginfo->extradata->face = g_strdup(full_msginfo->extradata->face);
1263 if (!msginfo->extradata->dispositionnotificationto)
1264 msginfo->extradata->dispositionnotificationto =
1265 g_strdup(full_msginfo->extradata->dispositionnotificationto);
1266 if (!msginfo->extradata->returnreceiptto)
1267 msginfo->extradata->returnreceiptto = g_strdup
1268 (full_msginfo->extradata->returnreceiptto);
1269 if (!msginfo->extradata->partial_recv && full_msginfo->extradata->partial_recv)
1270 msginfo->extradata->partial_recv = g_strdup
1271 (full_msginfo->extradata->partial_recv);
1272 if (!msginfo->extradata->account_server && full_msginfo->extradata->account_server)
1273 msginfo->extradata->account_server = g_strdup
1274 (full_msginfo->extradata->account_server);
1275 if (!msginfo->extradata->account_login && full_msginfo->extradata->account_login)
1276 msginfo->extradata->account_login = g_strdup
1277 (full_msginfo->extradata->account_login);
1279 procmsg_msginfo_free(full_msginfo);
1281 return procmsg_msginfo_new_ref(msginfo);
1284 MsgInfo *procmsg_msginfo_get_full_info(MsgInfo *msginfo)
1286 MsgInfo *full_msginfo;
1289 if (msginfo == NULL) return NULL;
1291 file = procmsg_get_message_file_path(msginfo);
1292 if (!file || !is_file_exist(file)) {
1294 file = procmsg_get_message_file(msginfo);
1296 if (!file || !is_file_exist(file)) {
1297 g_warning("procmsg_msginfo_get_full_info(): can't get message file.\n");
1301 full_msginfo = procmsg_msginfo_get_full_info_from_file(msginfo, file);
1303 return full_msginfo;
1306 void procmsg_msginfo_free(MsgInfo *msginfo)
1308 if (msginfo == NULL) return;
1311 if (msginfo->refcnt > 0)
1314 if (msginfo->to_folder) {
1315 msginfo->to_folder->op_count--;
1316 folder_item_update(msginfo->to_folder, F_ITEM_UPDATE_MSGCNT);
1319 g_free(msginfo->fromspace);
1321 g_free(msginfo->fromname);
1323 g_free(msginfo->date);
1324 g_free(msginfo->from);
1325 g_free(msginfo->to);
1326 g_free(msginfo->cc);
1327 g_free(msginfo->newsgroups);
1328 g_free(msginfo->subject);
1329 g_free(msginfo->msgid);
1330 g_free(msginfo->inreplyto);
1331 g_free(msginfo->xref);
1333 if (msginfo->extradata) {
1334 g_free(msginfo->extradata->returnreceiptto);
1335 g_free(msginfo->extradata->dispositionnotificationto);
1336 g_free(msginfo->extradata->xface);
1337 g_free(msginfo->extradata->face);
1338 g_free(msginfo->extradata->list_post);
1339 g_free(msginfo->extradata->list_subscribe);
1340 g_free(msginfo->extradata->list_unsubscribe);
1341 g_free(msginfo->extradata->list_help);
1342 g_free(msginfo->extradata->list_archive);
1343 g_free(msginfo->extradata->list_owner);
1344 g_free(msginfo->extradata->partial_recv);
1345 g_free(msginfo->extradata->account_server);
1346 g_free(msginfo->extradata->account_login);
1347 g_free(msginfo->extradata);
1349 slist_free_strings(msginfo->references);
1350 g_slist_free(msginfo->references);
1351 g_slist_free(msginfo->tags);
1353 g_free(msginfo->plaintext_file);
1358 guint procmsg_msginfo_memusage(MsgInfo *msginfo)
1363 memusage += sizeof(MsgInfo);
1364 if (msginfo->fromname)
1365 memusage += strlen(msginfo->fromname);
1367 memusage += strlen(msginfo->date);
1369 memusage += strlen(msginfo->from);
1371 memusage += strlen(msginfo->to);
1373 memusage += strlen(msginfo->cc);
1374 if (msginfo->newsgroups)
1375 memusage += strlen(msginfo->newsgroups);
1376 if (msginfo->subject)
1377 memusage += strlen(msginfo->subject);
1379 memusage += strlen(msginfo->msgid);
1380 if (msginfo->inreplyto)
1381 memusage += strlen(msginfo->inreplyto);
1383 for (tmp = msginfo->references; tmp; tmp=tmp->next) {
1384 gchar *r = (gchar *)tmp->data;
1385 memusage += r?strlen(r):0 + sizeof(GSList);
1387 if (msginfo->fromspace)
1388 memusage += strlen(msginfo->fromspace);
1390 for (tmp = msginfo->tags; tmp; tmp=tmp->next) {
1391 memusage += sizeof(GSList);
1393 if (msginfo->extradata) {
1394 memusage += sizeof(MsgInfoExtraData);
1395 if (msginfo->extradata->xface)
1396 memusage += strlen(msginfo->extradata->xface);
1397 if (msginfo->extradata->face)
1398 memusage += strlen(msginfo->extradata->face);
1399 if (msginfo->extradata->dispositionnotificationto)
1400 memusage += strlen(msginfo->extradata->dispositionnotificationto);
1401 if (msginfo->extradata->returnreceiptto)
1402 memusage += strlen(msginfo->extradata->returnreceiptto);
1404 if (msginfo->extradata->partial_recv)
1405 memusage += strlen(msginfo->extradata->partial_recv);
1406 if (msginfo->extradata->account_server)
1407 memusage += strlen(msginfo->extradata->account_server);
1408 if (msginfo->extradata->account_login)
1409 memusage += strlen(msginfo->extradata->account_login);
1411 if (msginfo->extradata->list_post)
1412 memusage += strlen(msginfo->extradata->list_post);
1413 if (msginfo->extradata->list_subscribe)
1414 memusage += strlen(msginfo->extradata->list_subscribe);
1415 if (msginfo->extradata->list_unsubscribe)
1416 memusage += strlen(msginfo->extradata->list_unsubscribe);
1417 if (msginfo->extradata->list_help)
1418 memusage += strlen(msginfo->extradata->list_help);
1419 if (msginfo->extradata->list_archive)
1420 memusage += strlen(msginfo->extradata->list_archive);
1421 if (msginfo->extradata->list_owner)
1422 memusage += strlen(msginfo->extradata->list_owner);
1427 static gint procmsg_send_message_queue_full(const gchar *file, gboolean keep_session, gchar **errstr,
1428 FolderItem *queue, gint msgnum, gboolean *queued_removed)
1430 static HeaderEntry qentry[] = {{"S:", NULL, FALSE},
1431 {"SSV:", NULL, FALSE},
1432 {"R:", NULL, FALSE},
1433 {"NG:", NULL, FALSE},
1434 {"MAID:", NULL, FALSE},
1435 {"NAID:", NULL, FALSE},
1436 {"SCF:", NULL, FALSE},
1437 {"RMID:", NULL, FALSE},
1438 {"FMID:", NULL, FALSE},
1439 {"X-Claws-Privacy-System:", NULL, FALSE},
1440 {"X-Claws-Encrypt:", NULL, FALSE},
1441 {"X-Claws-Encrypt-Data:", NULL, FALSE},
1442 {"X-Claws-End-Special-Headers:", NULL, FALSE},
1443 {"X-Sylpheed-Privacy-System:", NULL, FALSE},
1444 {"X-Sylpheed-Encrypt:", NULL, FALSE},
1445 {"X-Sylpheed-Encrypt-Data:", NULL, FALSE},
1446 {"X-Sylpheed-End-Special-Headers:", NULL, FALSE},
1447 {NULL, NULL, FALSE}};
1450 gint mailval = 0, newsval = 0;
1452 gchar *smtpserver = NULL;
1453 GSList *to_list = NULL;
1454 GSList *newsgroup_list = NULL;
1455 gchar *savecopyfolder = NULL;
1456 gchar *replymessageid = NULL;
1457 gchar *fwdmessageid = NULL;
1458 gchar *privacy_system = NULL;
1459 gboolean encrypt = FALSE;
1460 gchar *encrypt_data = NULL;
1461 gchar buf[BUFFSIZE];
1463 PrefsAccount *mailac = NULL, *newsac = NULL;
1464 gboolean save_clear_text = TRUE;
1465 gchar *tmp_enc_file = NULL;
1469 cm_return_val_if_fail(file != NULL, -1);
1471 if ((fp = g_fopen(file, "rb")) == NULL) {
1472 FILE_OP_ERROR(file, "fopen");
1474 if (*errstr) g_free(*errstr);
1475 *errstr = g_strdup_printf(_("Couldn't open file %s."), file);
1480 while ((hnum = procheader_get_one_field(buf, sizeof(buf), fp, qentry))
1482 gchar *p = buf + strlen(qentry[hnum].name);
1490 if (smtpserver == NULL)
1491 smtpserver = g_strdup(p);
1494 to_list = address_list_append(to_list, p);
1497 newsgroup_list = newsgroup_list_append(newsgroup_list, p);
1499 case Q_MAIL_ACCOUNT_ID:
1500 mailac = account_find_from_id(atoi(p));
1502 case Q_NEWS_ACCOUNT_ID:
1503 newsac = account_find_from_id(atoi(p));
1505 case Q_SAVE_COPY_FOLDER:
1506 if (savecopyfolder == NULL)
1507 savecopyfolder = g_strdup(p);
1509 case Q_REPLY_MESSAGE_ID:
1510 if (replymessageid == NULL)
1511 replymessageid = g_strdup(p);
1513 case Q_FWD_MESSAGE_ID:
1514 if (fwdmessageid == NULL)
1515 fwdmessageid = g_strdup(p);
1517 case Q_PRIVACY_SYSTEM:
1518 case Q_PRIVACY_SYSTEM_OLD:
1519 if (privacy_system == NULL)
1520 privacy_system = g_strdup(p);
1527 case Q_ENCRYPT_DATA:
1528 case Q_ENCRYPT_DATA_OLD:
1529 if (encrypt_data == NULL)
1530 encrypt_data = g_strdup(p);
1533 case Q_CLAWS_HDRS_OLD:
1534 /* end of special headers reached */
1535 goto send_mail; /* can't "break;break;" */
1539 filepos = ftell(fp);
1544 if (mailac && mailac->save_encrypted_as_clear_text
1545 && !mailac->encrypt_to_self)
1546 save_clear_text = TRUE;
1548 save_clear_text = FALSE;
1553 mimeinfo = procmime_scan_queue_file(file);
1554 if (!privacy_encrypt(privacy_system, mimeinfo, encrypt_data)
1555 || (fp = my_tmpfile()) == NULL
1556 || procmime_write_mimeinfo(mimeinfo, fp) < 0) {
1559 procmime_mimeinfo_free_all(mimeinfo);
1562 slist_free_strings(to_list);
1563 g_slist_free(to_list);
1564 slist_free_strings(newsgroup_list);
1565 g_slist_free(newsgroup_list);
1566 g_free(savecopyfolder);
1567 g_free(replymessageid);
1568 g_free(fwdmessageid);
1569 g_free(privacy_system);
1570 g_free(encrypt_data);
1572 if (*errstr) g_free(*errstr);
1573 *errstr = g_strdup_printf(_("Couldn't encrypt the email: %s"),
1574 privacy_get_error());
1580 if (!save_clear_text) {
1581 gchar *content = NULL;
1582 FILE *tmpfp = get_tmpfile_in_dir(get_mime_tmp_dir(), &tmp_enc_file);
1586 content = file_read_stream_to_str(fp);
1589 str_write_to_file(content, tmp_enc_file);
1592 g_warning("couldn't get tempfile\n");
1596 procmime_mimeinfo_free_all(mimeinfo);
1602 debug_print("Sending message by mail\n");
1605 if (*errstr) g_free(*errstr);
1606 *errstr = g_strdup_printf(_("Queued message header is broken."));
1609 } else if (mailac && mailac->use_mail_command &&
1610 mailac->mail_command && (* mailac->mail_command)) {
1611 mailval = send_message_local(mailac->mail_command, fp);
1615 mailac = account_find_from_smtp_server(from, smtpserver);
1617 g_warning("Account not found. "
1618 "Using current account...\n");
1619 mailac = cur_account;
1624 mailval = send_message_smtp_full(mailac, to_list, fp, keep_session);
1625 if (mailval == -1 && errstr) {
1626 if (*errstr) g_free(*errstr);
1627 *errstr = g_strdup_printf(_("An error happened during SMTP session."));
1630 PrefsAccount tmp_ac;
1632 g_warning("Account not found.\n");
1634 memset(&tmp_ac, 0, sizeof(PrefsAccount));
1635 tmp_ac.address = from;
1636 tmp_ac.smtp_server = smtpserver;
1637 tmp_ac.smtpport = SMTP_PORT;
1638 mailval = send_message_smtp(&tmp_ac, to_list, fp);
1639 if (mailval == -1 && errstr) {
1640 if (*errstr) g_free(*errstr);
1641 *errstr = g_strdup_printf(_("No specific account has been found to "
1642 "send, and an error happened during SMTP session."));
1646 } else if (!to_list && !newsgroup_list) {
1648 if (*errstr) g_free(*errstr);
1649 *errstr = g_strdup(_("Couldn't determine sending informations. "
1650 "Maybe the email hasn't been generated by Claws Mail."));
1655 fseek(fp, filepos, SEEK_SET);
1656 if (newsgroup_list && newsac && (mailval == 0)) {
1661 /* write to temporary file */
1662 tmp = g_strdup_printf("%s%cnntp%p", get_tmp_dir(),
1663 G_DIR_SEPARATOR, file);
1664 if ((tmpfp = g_fopen(tmp, "wb")) == NULL) {
1665 FILE_OP_ERROR(tmp, "fopen");
1667 alertpanel_error(_("Couldn't create temporary file for news sending."));
1669 if (change_file_mode_rw(tmpfp, tmp) < 0) {
1670 FILE_OP_ERROR(tmp, "chmod");
1671 g_warning("can't change file mode\n");
1674 while ((newsval == 0) && fgets(buf, sizeof(buf), fp) != NULL) {
1675 if (fputs(buf, tmpfp) == EOF) {
1676 FILE_OP_ERROR(tmp, "fputs");
1679 if (*errstr) g_free(*errstr);
1680 *errstr = g_strdup_printf(_("Error when writing temporary file for news sending."));
1687 debug_print("Sending message by news\n");
1689 folder = FOLDER(newsac->folder);
1691 newsval = news_post(folder, tmp);
1692 if (newsval < 0 && errstr) {
1693 if (*errstr) g_free(*errstr);
1694 *errstr = g_strdup_printf(_("Error occurred while posting the message to %s."),
1695 newsac->nntp_server);
1705 /* save message to outbox */
1706 if (mailval == 0 && newsval == 0 && savecopyfolder) {
1709 debug_print("saving sent message...\n");
1711 outbox = folder_find_item_from_identifier(savecopyfolder);
1713 outbox = folder_get_default_outbox();
1715 if (save_clear_text || tmp_enc_file == NULL) {
1716 gboolean saved = FALSE;
1717 *queued_removed = FALSE;
1718 if (queue && msgnum > 0) {
1719 MsgInfo *queued_mail = folder_item_get_msginfo(queue, msgnum);
1720 if (folder_item_move_msg(outbox, queued_mail) >= 0) {
1721 debug_print("moved queued mail %d to sent folder\n", msgnum);
1723 *queued_removed = TRUE;
1724 } else if (folder_item_copy_msg(outbox, queued_mail) >= 0) {
1725 debug_print("copied queued mail %d to sent folder\n", msgnum);
1728 procmsg_msginfo_free(queued_mail);
1731 debug_print("resaving clear text queued mail to sent folder\n");
1732 procmsg_save_to_outbox(outbox, file, TRUE);
1735 debug_print("saving encrpyted queued mail to sent folder\n");
1736 procmsg_save_to_outbox(outbox, tmp_enc_file, FALSE);
1740 if (tmp_enc_file != NULL) {
1741 claws_unlink(tmp_enc_file);
1743 tmp_enc_file = NULL;
1746 if (replymessageid != NULL || fwdmessageid != NULL) {
1750 if (replymessageid != NULL)
1751 tokens = g_strsplit(replymessageid, "\t", 0);
1753 tokens = g_strsplit(fwdmessageid, "\t", 0);
1754 item = folder_find_item_from_identifier(tokens[0]);
1756 /* check if queued message has valid folder and message id */
1757 if (item != NULL && tokens[2] != NULL) {
1760 msginfo = folder_item_get_msginfo(item, atoi(tokens[1]));
1762 /* check if referring message exists and has a message id */
1763 if ((msginfo != NULL) &&
1764 (msginfo->msgid != NULL) &&
1765 (strcmp(msginfo->msgid, tokens[2]) != 0)) {
1766 procmsg_msginfo_free(msginfo);
1770 if (msginfo == NULL) {
1771 msginfo = folder_item_get_msginfo_by_msgid(item, tokens[2]);
1774 if (msginfo != NULL) {
1775 MsgPermFlags to_unset = 0;
1777 if (prefs_common.mark_as_read_on_new_window)
1778 to_unset = (MSG_NEW|MSG_UNREAD);
1780 if (replymessageid != NULL) {
1781 procmsg_msginfo_unset_flags(msginfo, to_unset|MSG_FORWARDED, 0);
1782 procmsg_msginfo_set_flags(msginfo, MSG_REPLIED, 0);
1784 procmsg_msginfo_unset_flags(msginfo, MSG_REPLIED, 0);
1785 procmsg_msginfo_set_flags(msginfo, MSG_FORWARDED, 0);
1787 procmsg_msginfo_free(msginfo);
1795 slist_free_strings(to_list);
1796 g_slist_free(to_list);
1797 slist_free_strings(newsgroup_list);
1798 g_slist_free(newsgroup_list);
1799 g_free(savecopyfolder);
1800 g_free(replymessageid);
1801 g_free(fwdmessageid);
1802 g_free(privacy_system);
1803 g_free(encrypt_data);
1805 return (newsval != 0 ? newsval : mailval);
1808 gint procmsg_send_message_queue(const gchar *file, gchar **errstr, FolderItem *queue, gint msgnum, gboolean *queued_removed)
1810 gint result = procmsg_send_message_queue_full(file, FALSE, errstr, queue, msgnum, queued_removed);
1811 toolbar_main_set_sensitive(mainwindow_get_mainwindow());
1815 static void update_folder_msg_counts(FolderItem *item, MsgInfo *msginfo, MsgPermFlags old_flags)
1817 MsgPermFlags new_flags = msginfo->flags.perm_flags;
1820 if (!(old_flags & MSG_NEW) && (new_flags & MSG_NEW)) {
1824 if ((old_flags & MSG_NEW) && !(new_flags & MSG_NEW)) {
1829 if (!(old_flags & MSG_UNREAD) && (new_flags & MSG_UNREAD)) {
1830 item->unread_msgs++;
1831 if (procmsg_msg_has_marked_parent(msginfo))
1832 item->unreadmarked_msgs++;
1835 if ((old_flags & MSG_UNREAD) && !(new_flags & MSG_UNREAD)) {
1836 item->unread_msgs--;
1837 if (procmsg_msg_has_marked_parent(msginfo))
1838 item->unreadmarked_msgs--;
1842 if (!(old_flags & MSG_MARKED) && (new_flags & MSG_MARKED)) {
1843 procmsg_update_unread_children(msginfo, TRUE);
1844 item->marked_msgs++;
1847 if ((old_flags & MSG_MARKED) && !(new_flags & MSG_MARKED)) {
1848 procmsg_update_unread_children(msginfo, FALSE);
1849 item->marked_msgs--;
1852 if (!(old_flags & MSG_REPLIED) && (new_flags & MSG_REPLIED)) {
1853 item->replied_msgs++;
1856 if ((old_flags & MSG_REPLIED) && !(new_flags & MSG_REPLIED)) {
1857 item->replied_msgs--;
1860 if (!(old_flags & MSG_FORWARDED) && (new_flags & MSG_FORWARDED)) {
1861 item->forwarded_msgs++;
1864 if ((old_flags & MSG_FORWARDED) && !(new_flags & MSG_FORWARDED)) {
1865 item->forwarded_msgs--;
1868 if (!(old_flags & MSG_LOCKED) && (new_flags & MSG_LOCKED)) {
1869 item->locked_msgs++;
1872 if ((old_flags & MSG_LOCKED) && !(new_flags & MSG_LOCKED)) {
1873 item->locked_msgs--;
1876 if ((old_flags & MSG_IGNORE_THREAD) && !(new_flags & MSG_IGNORE_THREAD)) {
1877 item->ignored_msgs--;
1880 if (!(old_flags & MSG_IGNORE_THREAD) && (new_flags & MSG_IGNORE_THREAD)) {
1881 item->ignored_msgs++;
1884 if ((old_flags & MSG_WATCH_THREAD) && !(new_flags & MSG_WATCH_THREAD)) {
1885 item->watched_msgs--;
1888 if (!(old_flags & MSG_WATCH_THREAD) && (new_flags & MSG_WATCH_THREAD)) {
1889 item->watched_msgs++;
1893 void procmsg_msginfo_set_flags(MsgInfo *msginfo, MsgPermFlags perm_flags, MsgTmpFlags tmp_flags)
1896 MsgInfoUpdate msginfo_update;
1897 MsgPermFlags perm_flags_new, perm_flags_old;
1898 MsgTmpFlags tmp_flags_old;
1900 cm_return_if_fail(msginfo != NULL);
1901 item = msginfo->folder;
1902 cm_return_if_fail(item != NULL);
1904 debug_print("Setting flags for message %d in folder %s\n", msginfo->msgnum, item->path);
1906 /* Perm Flags handling */
1907 perm_flags_old = msginfo->flags.perm_flags;
1908 perm_flags_new = msginfo->flags.perm_flags | perm_flags;
1909 if ((perm_flags & MSG_IGNORE_THREAD) || (perm_flags_old & MSG_IGNORE_THREAD)) {
1910 perm_flags_new &= ~(MSG_NEW | MSG_UNREAD);
1912 if ((perm_flags & MSG_WATCH_THREAD) || (perm_flags_old & MSG_WATCH_THREAD)) {
1913 perm_flags_new &= ~(MSG_IGNORE_THREAD);
1916 if (perm_flags_old != perm_flags_new) {
1917 folder_item_change_msg_flags(msginfo->folder, msginfo, perm_flags_new);
1919 update_folder_msg_counts(item, msginfo, perm_flags_old);
1920 summary_update_unread(mainwindow_get_mainwindow()->summaryview, NULL);
1923 /* Tmp flags handling */
1924 tmp_flags_old = msginfo->flags.tmp_flags;
1925 msginfo->flags.tmp_flags |= tmp_flags;
1927 /* update notification */
1928 if ((perm_flags_old != perm_flags_new) || (tmp_flags_old != msginfo->flags.tmp_flags)) {
1929 msginfo_update.msginfo = msginfo;
1930 msginfo_update.flags = MSGINFO_UPDATE_FLAGS;
1931 hooks_invoke(MSGINFO_UPDATE_HOOKLIST, &msginfo_update);
1932 folder_item_update(msginfo->folder, F_ITEM_UPDATE_MSGCNT);
1936 void procmsg_msginfo_unset_flags(MsgInfo *msginfo, MsgPermFlags perm_flags, MsgTmpFlags tmp_flags)
1939 MsgInfoUpdate msginfo_update;
1940 MsgPermFlags perm_flags_new, perm_flags_old;
1941 MsgTmpFlags tmp_flags_old;
1943 cm_return_if_fail(msginfo != NULL);
1944 item = msginfo->folder;
1945 cm_return_if_fail(item != NULL);
1947 debug_print("Unsetting flags for message %d in folder %s\n", msginfo->msgnum, item->path);
1949 /* Perm Flags handling */
1950 perm_flags_old = msginfo->flags.perm_flags;
1951 perm_flags_new = msginfo->flags.perm_flags & ~perm_flags;
1953 if (perm_flags_old != perm_flags_new) {
1954 folder_item_change_msg_flags(msginfo->folder, msginfo, perm_flags_new);
1956 update_folder_msg_counts(item, msginfo, perm_flags_old);
1959 /* Tmp flags hanlding */
1960 tmp_flags_old = msginfo->flags.tmp_flags;
1961 msginfo->flags.tmp_flags &= ~tmp_flags;
1963 /* update notification */
1964 if ((perm_flags_old != perm_flags_new) || (tmp_flags_old != msginfo->flags.tmp_flags)) {
1965 msginfo_update.msginfo = msginfo;
1966 msginfo_update.flags = MSGINFO_UPDATE_FLAGS;
1967 hooks_invoke(MSGINFO_UPDATE_HOOKLIST, &msginfo_update);
1968 folder_item_update(msginfo->folder, F_ITEM_UPDATE_MSGCNT);
1972 void procmsg_msginfo_change_flags(MsgInfo *msginfo,
1973 MsgPermFlags add_perm_flags, MsgTmpFlags add_tmp_flags,
1974 MsgPermFlags rem_perm_flags, MsgTmpFlags rem_tmp_flags)
1977 MsgInfoUpdate msginfo_update;
1978 MsgPermFlags perm_flags_new, perm_flags_old;
1979 MsgTmpFlags tmp_flags_old;
1981 cm_return_if_fail(msginfo != NULL);
1982 item = msginfo->folder;
1983 cm_return_if_fail(item != NULL);
1985 debug_print("Changing flags for message %d in folder %s\n", msginfo->msgnum, item->path);
1987 /* Perm Flags handling */
1988 perm_flags_old = msginfo->flags.perm_flags;
1989 perm_flags_new = (msginfo->flags.perm_flags & ~rem_perm_flags) | add_perm_flags;
1990 if ((add_perm_flags & MSG_IGNORE_THREAD) || (perm_flags_old & MSG_IGNORE_THREAD)) {
1991 perm_flags_new &= ~(MSG_NEW | MSG_UNREAD);
1993 if ((add_perm_flags & MSG_WATCH_THREAD) || (perm_flags_old & MSG_WATCH_THREAD)) {
1994 perm_flags_new &= ~(MSG_IGNORE_THREAD);
1997 if (perm_flags_old != perm_flags_new) {
1998 folder_item_change_msg_flags(msginfo->folder, msginfo, perm_flags_new);
2000 update_folder_msg_counts(item, msginfo, perm_flags_old);
2004 /* Tmp flags handling */
2005 tmp_flags_old = msginfo->flags.tmp_flags;
2006 msginfo->flags.tmp_flags &= ~rem_tmp_flags;
2007 msginfo->flags.tmp_flags |= add_tmp_flags;
2009 /* update notification */
2010 if ((perm_flags_old != perm_flags_new) || (tmp_flags_old != msginfo->flags.tmp_flags)) {
2011 msginfo_update.msginfo = msginfo;
2012 msginfo_update.flags = MSGINFO_UPDATE_FLAGS;
2013 hooks_invoke(MSGINFO_UPDATE_HOOKLIST, &msginfo_update);
2014 folder_item_update(msginfo->folder, F_ITEM_UPDATE_MSGCNT);
2019 *\brief check for flags (e.g. mark) in prior msgs of current thread
2021 *\param info Current message
2022 *\param perm_flags Flags to be checked
2023 *\param parentmsgs Hash of prior msgs to avoid loops
2025 *\return gboolean TRUE if perm_flags are found
2027 static gboolean procmsg_msg_has_flagged_parent_real(MsgInfo *info,
2028 MsgPermFlags perm_flags, GHashTable *parentmsgs)
2032 cm_return_val_if_fail(info != NULL, FALSE);
2034 if (info != NULL && info->folder != NULL && info->inreplyto != NULL) {
2035 tmp = folder_item_get_msginfo_by_msgid(info->folder,
2037 if (tmp && (tmp->flags.perm_flags & perm_flags)) {
2038 procmsg_msginfo_free(tmp);
2040 } else if (tmp != NULL) {
2043 if (g_hash_table_lookup(parentmsgs, info)) {
2044 debug_print("loop detected: %d\n",
2048 g_hash_table_insert(parentmsgs, info, "1");
2049 result = procmsg_msg_has_flagged_parent_real(
2050 tmp, perm_flags, parentmsgs);
2052 procmsg_msginfo_free(tmp);
2062 *\brief Callback for cleaning up hash of parentmsgs
2064 static gboolean parentmsgs_hash_remove(gpointer key,
2072 *\brief Set up list of parentmsgs
2073 * See procmsg_msg_has_flagged_parent_real()
2075 gboolean procmsg_msg_has_flagged_parent(MsgInfo *info, MsgPermFlags perm_flags)
2078 static GHashTable *parentmsgs = NULL;
2080 if (parentmsgs == NULL)
2081 parentmsgs = g_hash_table_new(NULL, NULL);
2083 result = procmsg_msg_has_flagged_parent_real(info, perm_flags, parentmsgs);
2084 g_hash_table_foreach_remove(parentmsgs, parentmsgs_hash_remove, NULL);
2090 *\brief Check if msgs prior in thread are marked
2091 * See procmsg_msg_has_flagged_parent_real()
2093 gboolean procmsg_msg_has_marked_parent(MsgInfo *info)
2095 return procmsg_msg_has_flagged_parent(info, MSG_MARKED);
2099 static GSList *procmsg_find_children_func(MsgInfo *info,
2100 GSList *children, GSList *all)
2104 cm_return_val_if_fail(info!=NULL, children);
2105 if (info->msgid == NULL)
2108 for (cur = all; cur != NULL; cur = g_slist_next(cur)) {
2109 MsgInfo *tmp = (MsgInfo *)cur->data;
2110 if (tmp->inreplyto && !strcmp(tmp->inreplyto, info->msgid)) {
2111 /* Check if message is already in the list */
2112 if ((children == NULL) ||
2113 (g_slist_index(children, tmp) == -1)) {
2114 children = g_slist_prepend(children,
2115 procmsg_msginfo_new_ref(tmp));
2116 children = procmsg_find_children_func(tmp,
2125 static GSList *procmsg_find_children (MsgInfo *info)
2130 cm_return_val_if_fail(info!=NULL, NULL);
2131 all = folder_item_get_msg_list(info->folder);
2132 children = procmsg_find_children_func(info, NULL, all);
2133 if (children != NULL) {
2134 for (cur = all; cur != NULL; cur = g_slist_next(cur)) {
2135 /* this will not free the used pointers
2136 created with procmsg_msginfo_new_ref */
2137 procmsg_msginfo_free((MsgInfo *)cur->data);
2145 static void procmsg_update_unread_children(MsgInfo *info, gboolean newly_marked)
2147 GSList *children = procmsg_find_children(info);
2149 for (cur = children; cur != NULL; cur = g_slist_next(cur)) {
2150 MsgInfo *tmp = (MsgInfo *)cur->data;
2151 if(MSG_IS_UNREAD(tmp->flags) && !MSG_IS_IGNORE_THREAD(tmp->flags)) {
2153 info->folder->unreadmarked_msgs++;
2155 info->folder->unreadmarked_msgs--;
2156 folder_item_update(info->folder, F_ITEM_UPDATE_MSGCNT);
2158 procmsg_msginfo_free(tmp);
2160 g_slist_free(children);
2164 * Set the destination folder for a copy or move operation
2166 * \param msginfo The message which's destination folder is changed
2167 * \param to_folder The destination folder for the operation
2169 void procmsg_msginfo_set_to_folder(MsgInfo *msginfo, FolderItem *to_folder)
2171 if(msginfo->to_folder != NULL) {
2172 msginfo->to_folder->op_count--;
2173 folder_item_update(msginfo->to_folder, F_ITEM_UPDATE_MSGCNT);
2175 msginfo->to_folder = to_folder;
2176 if(to_folder != NULL) {
2177 to_folder->op_count++;
2178 folder_item_update(msginfo->to_folder, F_ITEM_UPDATE_MSGCNT);
2183 * Apply filtering actions to the msginfo
2185 * \param msginfo The MsgInfo describing the message that should be filtered
2186 * \return TRUE if the message was moved and MsgInfo is now invalid,
2189 static gboolean procmsg_msginfo_filter(MsgInfo *msginfo, PrefsAccount* ac_prefs)
2191 MailFilteringData mail_filtering_data;
2193 mail_filtering_data.msginfo = msginfo;
2194 mail_filtering_data.msglist = NULL;
2195 mail_filtering_data.filtered = NULL;
2196 mail_filtering_data.unfiltered = NULL;
2197 mail_filtering_data.account = ac_prefs;
2199 if (!ac_prefs || ac_prefs->filterhook_on_recv)
2200 if (hooks_invoke(MAIL_FILTERING_HOOKLIST, &mail_filtering_data))
2203 /* filter if enabled in prefs or move to inbox if not */
2204 if((filtering_rules != NULL) &&
2205 filter_message_by_msginfo(filtering_rules, msginfo, ac_prefs,
2206 FILTERING_INCORPORATION, NULL)) {
2213 void procmsg_msglist_filter(GSList *list, PrefsAccount *ac,
2214 GSList **filtered, GSList **unfiltered,
2217 GSList *cur, *to_do = NULL;
2218 gint total = 0, curnum = 0;
2219 MailFilteringData mail_filtering_data;
2221 cm_return_if_fail(filtered != NULL);
2222 cm_return_if_fail(unfiltered != NULL);
2230 total = g_slist_length(list);
2234 *unfiltered = g_slist_copy(list);
2238 statusbar_print_all(_("Filtering messages...\n"));
2240 mail_filtering_data.msginfo = NULL;
2241 mail_filtering_data.msglist = list;
2242 mail_filtering_data.filtered = NULL;
2243 mail_filtering_data.unfiltered = NULL;
2244 mail_filtering_data.account = ac;
2246 if (!ac || ac->filterhook_on_recv)
2247 hooks_invoke(MAIL_LISTFILTERING_HOOKLIST, &mail_filtering_data);
2249 if (mail_filtering_data.filtered == NULL &&
2250 mail_filtering_data.unfiltered == NULL) {
2251 /* nothing happened */
2252 debug_print(MAIL_LISTFILTERING_HOOKLIST " did nothing. filtering whole list normally.\n");
2255 if (mail_filtering_data.filtered != NULL) {
2256 /* keep track of what's been filtered by the hooks */
2257 debug_print(MAIL_LISTFILTERING_HOOKLIST " filtered some stuff. total %d filtered %d unfilt %d.\n",
2258 g_slist_length(list),
2259 g_slist_length(mail_filtering_data.filtered),
2260 g_slist_length(mail_filtering_data.unfiltered));
2262 *filtered = g_slist_copy(mail_filtering_data.filtered);
2264 if (mail_filtering_data.unfiltered != NULL) {
2265 /* what the hooks didn't handle will go in filtered or
2266 * unfiltered in the next loop */
2267 debug_print(MAIL_LISTFILTERING_HOOKLIST " left unfiltered stuff. total %d filtered %d unfilt %d.\n",
2268 g_slist_length(list),
2269 g_slist_length(mail_filtering_data.filtered),
2270 g_slist_length(mail_filtering_data.unfiltered));
2271 to_do = mail_filtering_data.unfiltered;
2274 for (cur = to_do; cur; cur = cur->next) {
2275 MsgInfo *info = (MsgInfo *)cur->data;
2276 if (procmsg_msginfo_filter(info, ac))
2277 *filtered = g_slist_prepend(*filtered, info);
2279 *unfiltered = g_slist_prepend(*unfiltered, info);
2280 statusbar_progress_all(curnum++, total, prefs_common.statusbar_update_step);
2283 g_slist_free(mail_filtering_data.filtered);
2284 g_slist_free(mail_filtering_data.unfiltered);
2286 *filtered = g_slist_reverse(*filtered);
2287 *unfiltered = g_slist_reverse(*unfiltered);
2289 statusbar_progress_all(0,0,0);
2290 statusbar_pop_all();
2293 MsgInfo *procmsg_msginfo_new_from_mimeinfo(MsgInfo *src_msginfo, MimeInfo *mimeinfo)
2295 MsgInfo *tmp_msginfo = NULL;
2296 MsgFlags flags = {0, 0};
2297 gchar *tmpfile = get_tmp_file();
2298 FILE *fp = g_fopen(tmpfile, "wb");
2300 if (!mimeinfo || mimeinfo->type != MIMETYPE_MESSAGE ||
2301 g_ascii_strcasecmp(mimeinfo->subtype, "rfc822")) {
2302 g_warning("procmsg_msginfo_new_from_mimeinfo(): unsuitable mimeinfo");
2309 if (fp && procmime_write_mimeinfo(mimeinfo, fp) >= 0) {
2312 tmp_msginfo = procheader_parse_file(
2319 if (tmp_msginfo != NULL) {
2321 tmp_msginfo->folder = src_msginfo->folder;
2322 tmp_msginfo->plaintext_file = g_strdup(tmpfile);
2324 g_warning("procmsg_msginfo_new_from_mimeinfo(): Can't generate new msginfo");
2332 static GSList *spam_learners = NULL;
2334 void procmsg_register_spam_learner (int (*learn_func)(MsgInfo *info, GSList *list, gboolean spam))
2336 if (!g_slist_find(spam_learners, learn_func))
2337 spam_learners = g_slist_append(spam_learners, learn_func);
2338 if (mainwindow_get_mainwindow()) {
2339 main_window_set_menu_sensitive(mainwindow_get_mainwindow());
2340 summary_set_menu_sensitive(
2341 mainwindow_get_mainwindow()->summaryview);
2342 toolbar_main_set_sensitive(mainwindow_get_mainwindow());
2346 void procmsg_unregister_spam_learner (int (*learn_func)(MsgInfo *info, GSList *list, gboolean spam))
2348 spam_learners = g_slist_remove(spam_learners, learn_func);
2349 if (mainwindow_get_mainwindow()) {
2350 main_window_set_menu_sensitive(mainwindow_get_mainwindow());
2351 summary_set_menu_sensitive(
2352 mainwindow_get_mainwindow()->summaryview);
2353 toolbar_main_set_sensitive(mainwindow_get_mainwindow());
2357 gboolean procmsg_spam_can_learn(void)
2359 return g_slist_length(spam_learners) > 0;
2362 int procmsg_spam_learner_learn (MsgInfo *info, GSList *list, gboolean spam)
2364 GSList *cur = spam_learners;
2366 for (; cur; cur = cur->next) {
2367 int ((*func)(MsgInfo *info, GSList *list, gboolean spam)) = cur->data;
2368 ret |= func(info, list, spam);
2373 static gchar *spam_folder_item = NULL;
2374 static FolderItem * (*procmsg_spam_get_folder_func)(MsgInfo *msginfo) = NULL;
2375 void procmsg_spam_set_folder (const char *item_identifier, FolderItem *(*spam_get_folder_func)(MsgInfo *info))
2377 g_free(spam_folder_item);
2378 if (item_identifier)
2379 spam_folder_item = g_strdup(item_identifier);
2381 spam_folder_item = NULL;
2382 if (spam_get_folder_func != NULL)
2383 procmsg_spam_get_folder_func = spam_get_folder_func;
2385 procmsg_spam_get_folder_func = NULL;
2388 FolderItem *procmsg_spam_get_folder (MsgInfo *msginfo)
2390 FolderItem *item = NULL;
2392 if (procmsg_spam_get_folder_func)
2393 item = procmsg_spam_get_folder_func(msginfo);
2394 if (item == NULL && spam_folder_item)
2395 item = folder_find_item_from_identifier(spam_folder_item);
2397 item = folder_get_default_trash();
2401 static void item_has_queued_mails(FolderItem *item, gpointer data)
2403 gboolean *result = (gboolean *)data;
2404 if (*result == TRUE)
2406 if (folder_has_parent_of_type(item, F_QUEUE)) {
2407 if (item->total_msgs == 0)
2410 GSList *msglist = folder_item_get_msg_list(item);
2412 for (cur = msglist; cur; cur = cur->next) {
2413 MsgInfo *msginfo = (MsgInfo *)cur->data;
2414 if (!MSG_IS_DELETED(msginfo->flags) &&
2415 !MSG_IS_LOCKED(msginfo->flags)) {
2420 procmsg_msg_list_free(msglist);
2425 gboolean procmsg_have_queued_mails_fast (void)
2427 gboolean result = FALSE;
2428 folder_func_to_all_folders(item_has_queued_mails, &result);
2432 static void item_has_trashed_mails(FolderItem *item, gpointer data)
2434 gboolean *result = (gboolean *)data;
2435 if (*result == TRUE)
2437 if (folder_has_parent_of_type(item, F_TRASH) && item->total_msgs > 0)
2441 gboolean procmsg_have_trashed_mails_fast (void)
2443 gboolean result = FALSE;
2444 folder_func_to_all_folders(item_has_trashed_mails, &result);
2448 gchar *procmsg_msginfo_get_tags_str(MsgInfo *msginfo)
2456 if (msginfo->tags == NULL)
2458 for (cur = msginfo->tags; cur; cur = cur->next) {
2459 const gchar *tag = tags_get_tag(GPOINTER_TO_INT(cur->data));
2463 tags = g_strdup(tag);
2465 int olen = strlen(tags);
2466 int nlen = olen + strlen(tag) + 2 /* strlen(", ") */;
2467 tags = g_realloc(tags, nlen+1);
2470 strcpy(tags+olen, ", ");
2471 strcpy(tags+olen+2, tag);
2478 void procmsg_msginfo_update_tags(MsgInfo *msginfo, gboolean set, gint id)
2486 msginfo->tags = g_slist_remove(
2488 GINT_TO_POINTER(id));
2489 changed.data = GINT_TO_POINTER(id);
2490 changed.next = NULL;
2491 folder_item_commit_tags(msginfo->folder, msginfo, NULL, &changed);
2493 if (!g_slist_find(msginfo->tags, GINT_TO_POINTER(id))) {
2494 msginfo->tags = g_slist_append(
2496 GINT_TO_POINTER(id));
2498 changed.data = GINT_TO_POINTER(id);
2499 changed.next = NULL;
2500 folder_item_commit_tags(msginfo->folder, msginfo, &changed, NULL);
2505 void procmsg_msginfo_clear_tags(MsgInfo *msginfo)
2507 GSList *unset = msginfo->tags;
2508 msginfo->tags = NULL;
2509 folder_item_commit_tags(msginfo->folder, msginfo, NULL, unset);
2510 g_slist_free(unset);