2 * Sylpheed -- a GTK+ based, lightweight, and fast e-mail client
3 * Copyright (C) 1999-2012 Hiroyuki Yamamoto and the Claws Mail team
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation; either version 3 of the License, or
8 * (at your option) any later version.
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
15 * You should have received a copy of the GNU General Public License
16 * along with this program. If not, see <http://www.gnu.org/licenses/>.
23 #include <glib/gi18n.h>
31 #include "procheader.h"
32 #include "send_message.h"
34 #include "statusbar.h"
35 #include "prefs_filtering.h"
36 #include "filtering.h"
38 #include "prefs_common.h"
40 #include "alertpanel.h"
44 #include "partial_download.h"
45 #include "mainwindow.h"
46 #include "summaryview.h"
53 extern SessionStats session_stats;
55 static gint procmsg_send_message_queue_full(const gchar *file, gboolean keep_session, gchar **errstr,
56 FolderItem *queue, gint msgnum, gboolean *queued_removed);
57 static void procmsg_update_unread_children (MsgInfo *info,
58 gboolean newly_marked);
65 Q_MAIL_ACCOUNT_ID = 4,
66 Q_NEWS_ACCOUNT_ID = 5,
67 Q_SAVE_COPY_FOLDER = 6,
68 Q_REPLY_MESSAGE_ID = 7,
74 Q_PRIVACY_SYSTEM_OLD = 13,
76 Q_ENCRYPT_DATA_OLD = 15,
77 Q_CLAWS_HDRS_OLD = 16,
80 void procmsg_msg_list_free(GSList *mlist)
85 for (cur = mlist; cur != NULL; cur = cur->next) {
86 msginfo = (MsgInfo *)cur->data;
87 procmsg_msginfo_free(msginfo);
92 MsgNumberList *procmsg_get_number_list_for_msgs(MsgInfoList *msglist)
97 for (cur = msglist; cur; cur = cur->next) {
98 MsgInfo *msg = (MsgInfo *)cur->data;
99 nums = g_slist_prepend(nums, GUINT_TO_POINTER(msg->msgnum));
102 return g_slist_reverse(nums);
114 /* CLAWS subject threading:
116 in the first round it inserts subject lines in a
117 hashtable (subject <-> node)
119 the second round finishes the threads by attaching
120 matching subject lines to the one found in the
121 hashtable. will use the oldest node with the same
122 subject that is not more then thread_by_subject_max_age
123 days old (see subject_hashtable_lookup)
126 static void subject_hashtable_insert(GHashTable *hashtable, GNode *node)
132 cm_return_if_fail(hashtable != NULL);
133 cm_return_if_fail(node != NULL);
134 msginfo = (MsgInfo *) node->data;
135 cm_return_if_fail(msginfo != NULL);
137 subject = msginfo->subject;
141 subject += subject_get_prefix_length(subject);
143 list = g_hash_table_lookup(hashtable, subject);
144 list = g_slist_prepend(list, node);
145 g_hash_table_insert(hashtable, subject, list);
148 static GNode *subject_hashtable_lookup(GHashTable *hashtable, MsgInfo *msginfo)
152 GNode *node = NULL, *hashtable_node = NULL;
154 MsgInfo *hashtable_msginfo = NULL, *best_msginfo = NULL;
157 cm_return_val_if_fail(hashtable != NULL, NULL);
159 subject = msginfo->subject;
162 prefix_length = subject_get_prefix_length(subject);
163 if (prefix_length <= 0)
165 subject += prefix_length;
167 list = g_hash_table_lookup(hashtable, subject);
171 /* check all nodes with the same subject to find the best parent */
172 for (cur = list; cur; cur = cur->next) {
173 hashtable_node = (GNode *)cur->data;
174 hashtable_msginfo = (MsgInfo *) hashtable_node->data;
177 /* best node should be the oldest in the found nodes */
178 /* parent node must not be older then msginfo */
179 if ((hashtable_msginfo->date_t < msginfo->date_t) &&
180 ((best_msginfo == NULL) ||
181 (best_msginfo->date_t > hashtable_msginfo->date_t)))
184 /* parent node must not be more then thread_by_subject_max_age
185 days older then msginfo */
186 if (abs(difftime(msginfo->date_t, hashtable_msginfo->date_t)) >
187 prefs_common.thread_by_subject_max_age * 3600 * 24)
190 /* can add new tests for all matching
191 nodes found by subject */
194 node = hashtable_node;
195 best_msginfo = hashtable_msginfo;
202 static void subject_hashtable_free(gpointer key, gpointer value, gpointer data)
207 /* return the reversed thread tree */
208 GNode *procmsg_get_thread_tree(GSList *mlist)
210 GNode *root, *parent, *node, *next;
211 GHashTable *msgid_table;
212 GHashTable *subject_hashtable = NULL;
217 root = g_node_new(NULL);
218 msgid_table = g_hash_table_new(g_str_hash, g_str_equal);
220 if (prefs_common.thread_by_subject) {
221 subject_hashtable = g_hash_table_new(g_str_hash, g_str_equal);
224 for (; mlist != NULL; mlist = mlist->next) {
225 msginfo = (MsgInfo *)mlist->data;
228 if (msginfo->inreplyto) {
229 parent = g_hash_table_lookup(msgid_table, msginfo->inreplyto);
230 if (parent == NULL) {
234 node = g_node_insert_data_before
235 (parent, parent == root ? parent->children : NULL,
237 if ((msgid = msginfo->msgid) && g_hash_table_lookup(msgid_table, msgid) == NULL)
238 g_hash_table_insert(msgid_table, (gchar *)msgid, node);
240 /* CLAWS: add subject to hashtable (without prefix) */
241 if (prefs_common.thread_by_subject) {
242 subject_hashtable_insert(subject_hashtable, node);
246 /* complete the unfinished threads */
247 for (node = root->children; node != NULL; ) {
249 msginfo = (MsgInfo *)node->data;
252 if (msginfo->inreplyto)
253 parent = g_hash_table_lookup(msgid_table, msginfo->inreplyto);
255 /* try looking for the indirect parent */
256 if (!parent && msginfo->references) {
257 for (reflist = msginfo->references;
258 reflist != NULL; reflist = reflist->next)
259 if ((parent = g_hash_table_lookup
260 (msgid_table, reflist->data)) != NULL)
264 /* node should not be the parent, and node should not
265 be an ancestor of parent (circular reference) */
266 if (parent && parent != node &&
267 !g_node_is_ancestor(node, parent)) {
270 (parent, parent->children, node);
276 if (prefs_common.thread_by_subject) {
277 START_TIMING("thread by subject");
278 for (node = root->children; node && node != NULL;) {
280 msginfo = (MsgInfo *) node->data;
282 parent = subject_hashtable_lookup(subject_hashtable, msginfo);
284 /* the node may already be threaded by IN-REPLY-TO, so go up
286 find the parent node */
287 if (parent != NULL) {
288 if (g_node_is_ancestor(node, parent))
296 g_node_append(parent, node);
304 if (prefs_common.thread_by_subject)
306 g_hash_table_foreach(subject_hashtable, subject_hashtable_free, NULL);
307 g_hash_table_destroy(subject_hashtable);
310 g_hash_table_destroy(msgid_table);
315 gint procmsg_move_messages(GSList *mlist)
317 GSList *cur, *movelist = NULL;
319 FolderItem *dest = NULL;
321 gboolean finished = TRUE;
322 if (!mlist) return 0;
324 folder_item_update_freeze();
327 for (cur = mlist; cur != NULL; cur = cur->next) {
328 msginfo = (MsgInfo *)cur->data;
329 if (!msginfo->to_folder) {
335 dest = msginfo->to_folder;
336 movelist = g_slist_prepend(movelist, msginfo);
337 } else if (dest == msginfo->to_folder) {
338 movelist = g_slist_prepend(movelist, msginfo);
342 procmsg_msginfo_set_to_folder(msginfo, NULL);
345 movelist = g_slist_reverse(movelist);
346 retval |= folder_item_move_msgs(dest, movelist);
347 g_slist_free(movelist);
350 if (finished == FALSE) {
356 folder_item_update_thaw();
360 void procmsg_copy_messages(GSList *mlist)
362 GSList *cur, *copylist = NULL;
364 FolderItem *dest = NULL;
365 gboolean finished = TRUE;
368 folder_item_update_freeze();
371 for (cur = mlist; cur != NULL; cur = cur->next) {
372 msginfo = (MsgInfo *)cur->data;
373 if (!msginfo->to_folder) {
379 dest = msginfo->to_folder;
380 copylist = g_slist_prepend(copylist, msginfo);
381 } else if (dest == msginfo->to_folder) {
382 copylist = g_slist_prepend(copylist, msginfo);
386 procmsg_msginfo_set_to_folder(msginfo, NULL);
389 copylist = g_slist_reverse(copylist);
390 folder_item_copy_msgs(dest, copylist);
391 g_slist_free(copylist);
394 if (finished == FALSE) {
400 folder_item_update_thaw();
403 gchar *procmsg_get_message_file_path(MsgInfo *msginfo)
407 cm_return_val_if_fail(msginfo != NULL, NULL);
409 if (msginfo->plaintext_file)
410 file = g_strdup(msginfo->plaintext_file);
412 file = folder_item_fetch_msg(msginfo->folder, msginfo->msgnum);
418 gchar *procmsg_get_message_file(MsgInfo *msginfo)
420 gchar *filename = NULL;
422 cm_return_val_if_fail(msginfo != NULL, NULL);
424 filename = folder_item_fetch_msg(msginfo->folder, msginfo->msgnum);
426 debug_print("can't fetch message %d\n", msginfo->msgnum);
431 gchar *procmsg_get_message_file_full(MsgInfo *msginfo, gboolean headers, gboolean body)
433 gchar *filename = NULL;
435 cm_return_val_if_fail(msginfo != NULL, NULL);
437 filename = folder_item_fetch_msg_full(msginfo->folder, msginfo->msgnum,
440 debug_print("can't fetch message %d\n", msginfo->msgnum);
445 GSList *procmsg_get_message_file_list(GSList *mlist)
447 GSList *file_list = NULL;
449 MsgFileInfo *fileinfo;
452 while (mlist != NULL) {
453 msginfo = (MsgInfo *)mlist->data;
454 file = procmsg_get_message_file(msginfo);
456 procmsg_message_file_list_free(file_list);
459 fileinfo = g_new(MsgFileInfo, 1);
460 fileinfo->msginfo = procmsg_msginfo_new_ref(msginfo);
461 fileinfo->file = file;
462 fileinfo->flags = g_new(MsgFlags, 1);
463 *fileinfo->flags = msginfo->flags;
464 file_list = g_slist_prepend(file_list, fileinfo);
468 file_list = g_slist_reverse(file_list);
473 void procmsg_message_file_list_free(MsgInfoList *file_list)
476 MsgFileInfo *fileinfo;
478 for (cur = file_list; cur != NULL; cur = cur->next) {
479 fileinfo = (MsgFileInfo *)cur->data;
480 procmsg_msginfo_free(fileinfo->msginfo);
481 g_free(fileinfo->file);
482 g_free(fileinfo->flags);
486 g_slist_free(file_list);
489 FILE *procmsg_open_message(MsgInfo *msginfo)
494 cm_return_val_if_fail(msginfo != NULL, NULL);
496 file = procmsg_get_message_file_path(msginfo);
497 cm_return_val_if_fail(file != NULL, NULL);
499 if (!is_file_exist(file)) {
501 file = procmsg_get_message_file(msginfo);
506 if ((fp = g_fopen(file, "rb")) == NULL) {
507 FILE_OP_ERROR(file, "fopen");
514 if (MSG_IS_QUEUED(msginfo->flags) || MSG_IS_DRAFT(msginfo->flags)) {
517 while (fgets(buf, sizeof(buf), fp) != NULL) {
519 if ((!strncmp(buf, "X-Claws-End-Special-Headers: 1",
520 strlen("X-Claws-End-Special-Headers:"))) ||
521 (!strncmp(buf, "X-Sylpheed-End-Special-Headers: 1",
522 strlen("X-Sylpheed-End-Special-Headers:"))))
525 if (buf[0] == '\r' || buf[0] == '\n') break;
526 /* from other mailers */
527 if (!strncmp(buf, "Date: ", 6)
528 || !strncmp(buf, "To: ", 4)
529 || !strncmp(buf, "From: ", 6)
530 || !strncmp(buf, "Subject: ", 9)) {
540 gboolean procmsg_msg_exist(MsgInfo *msginfo)
545 if (!msginfo) return FALSE;
547 path = folder_item_get_path(msginfo->folder);
549 ret = !folder_item_is_msg_changed(msginfo->folder, msginfo);
555 void procmsg_get_filter_keyword(MsgInfo *msginfo, gchar **header, gchar **key,
556 PrefsFilterType type)
558 static HeaderEntry hentry[] = {{"X-BeenThere:", NULL, TRUE},
559 {"X-ML-Name:", NULL, TRUE},
560 {"X-List:", NULL, TRUE},
561 {"X-Mailing-list:", NULL, TRUE},
562 {"List-Id:", NULL, TRUE},
563 {"X-Sequence:", NULL, TRUE},
564 {"Sender:", NULL, TRUE},
565 {"List-Post:", NULL, TRUE},
566 {NULL, NULL, FALSE}};
572 H_X_MAILING_LIST = 3,
581 cm_return_if_fail(msginfo != NULL);
582 cm_return_if_fail(header != NULL);
583 cm_return_if_fail(key != NULL);
592 if ((fp = procmsg_open_message(msginfo)) == NULL)
594 procheader_get_header_fields(fp, hentry);
597 #define SET_FILTER_KEY(hstr, idx) \
599 *header = g_strdup(hstr); \
600 *key = hentry[idx].body; \
601 hentry[idx].body = NULL; \
604 if (hentry[H_LIST_ID].body != NULL) {
605 SET_FILTER_KEY("header \"List-Id\"", H_LIST_ID);
606 extract_list_id_str(*key);
607 } else if (hentry[H_X_BEENTHERE].body != NULL) {
608 SET_FILTER_KEY("header \"X-BeenThere\"", H_X_BEENTHERE);
609 } else if (hentry[H_X_ML_NAME].body != NULL) {
610 SET_FILTER_KEY("header \"X-ML-Name\"", H_X_ML_NAME);
611 } else if (hentry[H_X_LIST].body != NULL) {
612 SET_FILTER_KEY("header \"X-List\"", H_X_LIST);
613 } else if (hentry[H_X_MAILING_LIST].body != NULL) {
614 SET_FILTER_KEY("header \"X-Mailing-List\"", H_X_MAILING_LIST);
615 } else if (hentry[H_X_SEQUENCE].body != NULL) {
618 SET_FILTER_KEY("X-Sequence", H_X_SEQUENCE);
621 while (*p != '\0' && !g_ascii_isspace(*p)) p++;
622 while (g_ascii_isspace(*p)) p++;
623 if (g_ascii_isdigit(*p)) {
629 } else if (hentry[H_SENDER].body != NULL) {
630 SET_FILTER_KEY("header \"Sender\"", H_SENDER);
631 } else if (hentry[H_LIST_POST].body != NULL) {
632 SET_FILTER_KEY("header \"List-Post\"", H_LIST_POST);
633 } else if (msginfo->to) {
634 *header = g_strdup("to");
635 *key = g_strdup(msginfo->to);
636 } else if (msginfo->subject) {
637 *header = g_strdup("subject");
638 *key = g_strdup(msginfo->subject);
641 #undef SET_FILTER_KEY
643 g_free(hentry[H_X_BEENTHERE].body);
644 hentry[H_X_BEENTHERE].body = NULL;
645 g_free(hentry[H_X_ML_NAME].body);
646 hentry[H_X_ML_NAME].body = NULL;
647 g_free(hentry[H_X_LIST].body);
648 hentry[H_X_LIST].body = NULL;
649 g_free(hentry[H_X_MAILING_LIST].body);
650 hentry[H_X_MAILING_LIST].body = NULL;
651 g_free(hentry[H_LIST_ID].body);
652 hentry[H_LIST_ID].body = NULL;
653 g_free(hentry[H_SENDER].body);
654 hentry[H_SENDER].body = NULL;
655 g_free(hentry[H_LIST_POST].body);
656 hentry[H_LIST_POST].body = NULL;
660 *header = g_strdup("from");
661 *key = g_strdup(msginfo->from);
664 *header = g_strdup("to");
665 *key = g_strdup(msginfo->to);
667 case FILTER_BY_SUBJECT:
668 *header = g_strdup("subject");
669 *key = g_strdup(msginfo->subject);
676 static void procmsg_empty_trash(FolderItem *trash)
681 (trash->stype != F_TRASH &&
682 !folder_has_parent_of_type(trash, F_TRASH)))
685 if (trash && trash->total_msgs > 0) {
686 GSList *mlist = folder_item_get_msg_list(trash);
688 for (cur = mlist ; cur != NULL ; cur = cur->next) {
689 MsgInfo * msginfo = (MsgInfo *) cur->data;
690 if (MSG_IS_LOCKED(msginfo->flags)) {
691 procmsg_msginfo_free(msginfo);
694 if (msginfo->total_size != 0 &&
695 msginfo->size != (off_t)msginfo->total_size)
696 partial_mark_for_delete(msginfo);
698 procmsg_msginfo_free(msginfo);
701 folder_item_remove_all_msg(trash);
704 if (!trash->node || !trash->node->children)
707 node = trash->node->children;
708 while (node != NULL) {
710 procmsg_empty_trash(FOLDER_ITEM(node->data));
715 void procmsg_empty_all_trash(void)
720 for (cur = folder_get_list(); cur != NULL; cur = cur->next) {
721 Folder *folder = FOLDER(cur->data);
722 trash = folder->trash;
723 procmsg_empty_trash(trash);
724 if (folder->account && folder->account->set_trash_folder &&
725 folder_find_item_from_identifier(folder->account->trash_folder))
727 folder_find_item_from_identifier(folder->account->trash_folder));
731 static PrefsAccount *procmsg_get_account_from_file(const gchar *file)
733 PrefsAccount *mailac = NULL;
737 static HeaderEntry qentry[] = {{"S:", NULL, FALSE},
738 {"SSV:", NULL, FALSE},
740 {"NG:", NULL, FALSE},
741 {"MAID:", NULL, FALSE},
742 {"NAID:", NULL, FALSE},
743 {"SCF:", NULL, FALSE},
744 {"RMID:", NULL, FALSE},
745 {"FMID:", NULL, FALSE},
746 {"X-Claws-Privacy-System:", NULL, FALSE},
747 {"X-Claws-Encrypt:", NULL, FALSE},
748 {"X-Claws-Encrypt-Data:", NULL, FALSE},
749 {"X-Claws-End-Special-Headers", NULL, FALSE},
750 {"X-Sylpheed-Privacy-System:", NULL, FALSE},
751 {"X-Sylpheed-Encrypt:", NULL, FALSE},
752 {"X-Sylpheed-Encrypt-Data:", NULL, FALSE},
753 {NULL, NULL, FALSE}};
755 cm_return_val_if_fail(file != NULL, NULL);
757 if ((fp = g_fopen(file, "rb")) == NULL) {
758 FILE_OP_ERROR(file, "fopen");
762 while ((hnum = procheader_get_one_field(buf, sizeof(buf), fp, qentry))
764 gchar *p = buf + strlen(qentry[hnum].name);
766 if (hnum == Q_MAIL_ACCOUNT_ID) {
767 mailac = account_find_from_id(atoi(p));
775 gchar *procmsg_msginfo_get_avatar(MsgInfo *msginfo, gint type)
779 if (!msginfo || !msginfo->extradata || !msginfo->extradata->avatars)
782 for (mia = msginfo->extradata->avatars; mia; mia = mia->next) {
783 MsgInfoAvatar *avatar = (MsgInfoAvatar *)mia->data;
784 if (avatar->avatar_id == type)
785 return avatar->avatar_src;
791 gchar *procmsg_msginfo_get_identifier(MsgInfo *msginfo)
797 cm_return_val_if_fail(msginfo != NULL, NULL);
798 folder_id = folder_item_get_identifier(msginfo->folder);
799 msgid = msginfo->msgid;
801 id = g_strconcat(folder_id, G_DIR_SEPARATOR_S, msgid, NULL);
808 MsgInfo *procmsg_get_msginfo_from_identifier(const gchar *id)
810 gchar *folder_id = g_strdup(id);
811 gchar *separator = strrchr(folder_id, G_DIR_SEPARATOR);
816 if (separator == NULL) {
822 msgid = separator + 1;
824 item = folder_find_item_from_identifier(folder_id);
831 msginfo = folder_item_get_msginfo_by_msgid(item, msgid);
837 static GSList *procmsg_list_sort_by_account(FolderItem *queue, GSList *list)
839 GSList *result = NULL;
841 PrefsAccount *last_account = NULL;
844 gboolean nothing_to_sort = TRUE;
849 orig = g_slist_copy(list);
851 msg = (MsgInfo *)orig->data;
853 for (cur = orig; cur; cur = cur->next)
854 debug_print("sort before %s\n", ((MsgInfo *)cur->data)->from);
859 nothing_to_sort = TRUE;
863 PrefsAccount *ac = NULL;
864 msg = (MsgInfo *)cur->data;
865 file = folder_item_fetch_msg(queue, msg->msgnum);
866 ac = procmsg_get_account_from_file(file);
869 if (last_account == NULL || (ac != NULL && ac == last_account)) {
870 result = g_slist_append(result, msg);
871 orig = g_slist_remove(orig, msg);
873 nothing_to_sort = FALSE;
879 if (orig || g_slist_length(orig)) {
880 if (!last_account && nothing_to_sort) {
881 /* can't find an account for the rest of the list */
884 result = g_slist_append(result, cur->data);
895 for (cur = result; cur; cur = cur->next)
896 debug_print("sort after %s\n", ((MsgInfo *)cur->data)->from);
903 static gboolean procmsg_is_last_for_account(FolderItem *queue, MsgInfo *msginfo, GSList *elem)
905 gchar *file = folder_item_fetch_msg(queue, msginfo->msgnum);
906 PrefsAccount *ac = procmsg_get_account_from_file(file);
909 for (cur = elem; cur; cur = cur->next) {
910 MsgInfo *cur_msginfo = (MsgInfo *)cur->data;
911 file = folder_item_fetch_msg(queue, cur_msginfo->msgnum);
913 if (cur_msginfo != msginfo && !MSG_IS_LOCKED(cur_msginfo->flags)) {
914 if (procmsg_get_account_from_file(file) == ac) {
925 static gboolean send_queue_lock = FALSE;
927 gboolean procmsg_queue_lock(char **errstr)
929 if (send_queue_lock) {
930 /* Avoid having to translate two similar strings */
931 log_warning(LOG_PROTOCOL, "%s\n", _("Already trying to send."));
933 if (*errstr) g_free(*errstr);
934 *errstr = g_strdup_printf(_("Already trying to send."));
938 send_queue_lock = TRUE;
941 void procmsg_queue_unlock(void)
943 send_queue_lock = FALSE;
946 *\brief Send messages in queue
948 *\param queue Queue folder to process
949 *\param save_msgs Unused
951 *\return Number of messages sent, negative if an error occurred
952 * positive if no error occurred
954 gint procmsg_send_queue(FolderItem *queue, gboolean save_msgs, gchar **errstr)
956 gint sent = 0, err = 0;
958 GSList *sorted_list = NULL;
961 if (!procmsg_queue_lock(errstr)) {
962 main_window_set_menu_sensitive(mainwindow_get_mainwindow());
963 toolbar_main_set_sensitive(mainwindow_get_mainwindow());
968 queue = folder_get_default_queue();
971 procmsg_queue_unlock();
976 main_window_set_menu_sensitive(mainwindow_get_mainwindow());
977 toolbar_main_set_sensitive(mainwindow_get_mainwindow());
979 folder_item_scan(queue);
980 list = folder_item_get_msg_list(queue);
982 /* sort the list per sender account; this helps reusing the same SMTP server */
983 sorted_list = procmsg_list_sort_by_account(queue, list);
985 for (elem = sorted_list; elem != NULL; elem = elem->next) {
989 msginfo = (MsgInfo *)(elem->data);
990 if (!MSG_IS_LOCKED(msginfo->flags) && !MSG_IS_DELETED(msginfo->flags)) {
991 file = folder_item_fetch_msg(queue, msginfo->msgnum);
993 gboolean queued_removed = FALSE;
994 if (procmsg_send_message_queue_full(file,
995 !procmsg_is_last_for_account(queue, msginfo, elem),
996 errstr, queue, msginfo->msgnum, &queued_removed) < 0) {
997 g_warning("Sending queued message %d failed.\n",
1002 if (!queued_removed)
1003 folder_item_remove_msg(queue, msginfo->msgnum);
1008 /* FIXME: supposedly if only one message is locked, and queue
1009 * is being flushed, the following free says something like
1010 * "freeing msg ## in folder (nil)". */
1011 procmsg_msginfo_free(msginfo);
1014 g_slist_free(sorted_list);
1015 folder_item_scan(queue);
1017 if (queue->node && queue->node->children) {
1018 node = queue->node->children;
1019 while (node != NULL) {
1022 send_queue_lock = FALSE;
1023 res = procmsg_send_queue(FOLDER_ITEM(node->data), save_msgs, errstr);
1024 send_queue_lock = TRUE;
1032 procmsg_queue_unlock();
1034 main_window_set_menu_sensitive(mainwindow_get_mainwindow());
1035 toolbar_main_set_sensitive(mainwindow_get_mainwindow());
1037 return (err != 0 ? -err : sent);
1040 gboolean procmsg_is_sending(void)
1042 return send_queue_lock;
1046 *\brief Determine if a queue folder is empty
1048 *\param queue Queue folder to process
1050 *\return TRUE if the queue folder is empty, otherwise return FALSE
1052 gboolean procmsg_queue_is_empty(FolderItem *queue)
1055 gboolean res = FALSE;
1057 queue = folder_get_default_queue();
1058 cm_return_val_if_fail(queue != NULL, TRUE);
1060 folder_item_scan(queue);
1061 list = folder_item_get_msg_list(queue);
1062 res = (list == NULL);
1063 procmsg_msg_list_free(list);
1067 if (queue->node && queue->node->children) {
1068 node = queue->node->children;
1069 while (node != NULL) {
1071 if (!procmsg_queue_is_empty(FOLDER_ITEM(node->data)))
1080 gint procmsg_remove_special_headers(const gchar *in, const gchar *out)
1083 gchar buf[BUFFSIZE];
1085 if ((fp = g_fopen(in, "rb")) == NULL) {
1086 FILE_OP_ERROR(in, "fopen");
1089 if ((outfp = g_fopen(out, "wb")) == NULL) {
1090 FILE_OP_ERROR(out, "fopen");
1094 while (fgets(buf, sizeof(buf), fp) != NULL) {
1096 if ((!strncmp(buf, "X-Claws-End-Special-Headers: 1",
1097 strlen("X-Claws-End-Special-Headers:"))) ||
1098 (!strncmp(buf, "X-Sylpheed-End-Special-Headers: 1",
1099 strlen("X-Sylpheed-End-Special-Headers:"))))
1102 if (buf[0] == '\r' || buf[0] == '\n') break;
1103 /* from other mailers */
1104 if (!strncmp(buf, "Date: ", 6)
1105 || !strncmp(buf, "To: ", 4)
1106 || !strncmp(buf, "From: ", 6)
1107 || !strncmp(buf, "Subject: ", 9)) {
1112 while (fgets(buf, sizeof(buf), fp) != NULL)
1119 static gint procmsg_save_to_outbox(FolderItem *outbox, const gchar *file,
1123 MsgInfo *msginfo, *tmp_msginfo;
1124 MsgFlags flag = {0, 0};
1126 debug_print("saving sent message...\n");
1129 outbox = folder_get_default_outbox();
1130 cm_return_val_if_fail(outbox != NULL, -1);
1132 /* remove queueing headers */
1134 gchar tmp[MAXPATHLEN + 1];
1136 g_snprintf(tmp, sizeof(tmp), "%s%ctmpmsg.out.%08x",
1137 get_rc_dir(), G_DIR_SEPARATOR, (guint) rand());
1139 if (procmsg_remove_special_headers(file, tmp) !=0)
1142 folder_item_scan(outbox);
1143 if ((num = folder_item_add_msg(outbox, tmp, &flag, TRUE)) < 0) {
1144 g_warning("can't save message\n");
1149 folder_item_scan(outbox);
1150 if ((num = folder_item_add_msg
1151 (outbox, file, &flag, FALSE)) < 0) {
1152 g_warning("can't save message\n");
1156 msginfo = folder_item_get_msginfo(outbox, num); /* refcnt++ */
1157 tmp_msginfo = procmsg_msginfo_get_full_info(msginfo); /* refcnt++ */
1158 if (msginfo != NULL) {
1159 procmsg_msginfo_unset_flags(msginfo, ~0, 0);
1160 procmsg_msginfo_free(msginfo); /* refcnt-- */
1161 /* tmp_msginfo == msginfo */
1162 if (tmp_msginfo && msginfo->extradata &&
1163 (msginfo->extradata->dispositionnotificationto ||
1164 msginfo->extradata->returnreceiptto)) {
1165 procmsg_msginfo_set_flags(msginfo, MSG_RETRCPT_SENT, 0);
1167 procmsg_msginfo_free(tmp_msginfo); /* refcnt-- */
1174 void procmsg_print_message(MsgInfo *msginfo, const gchar *cmdline)
1176 static const gchar *def_cmd = "lpr %s";
1177 static guint id = 0;
1184 cm_return_if_fail(msginfo);
1186 if (procmime_msginfo_is_encrypted(msginfo))
1187 tmpfp = procmime_get_first_encrypted_text_content(msginfo);
1189 tmpfp = procmime_get_first_text_content(msginfo);
1190 if (tmpfp == NULL) {
1191 g_warning("Can't get text part\n");
1195 prtmp = g_strdup_printf("%s%cprinttmp.%08x",
1196 get_mime_tmp_dir(), G_DIR_SEPARATOR, id++);
1198 if ((prfp = g_fopen(prtmp, "wb")) == NULL) {
1199 FILE_OP_ERROR(prtmp, "fopen");
1205 if (msginfo->date) { r = fprintf(prfp, "Date: %s\n", msginfo->date); if (r < 0) goto fpferr; }
1206 if (msginfo->from) { r = fprintf(prfp, "From: %s\n", msginfo->from); if (r < 0) goto fpferr; }
1207 if (msginfo->to) { r = fprintf(prfp, "To: %s\n", msginfo->to); if (r < 0) goto fpferr; }
1208 if (msginfo->cc) { r = fprintf(prfp, "Cc: %s\n", msginfo->cc); if (r < 0) goto fpferr; }
1209 if (msginfo->newsgroups) {
1210 r = fprintf(prfp, "Newsgroups: %s\n", msginfo->newsgroups); if (r < 0) goto fpferr;
1212 if (msginfo->subject) { r = fprintf(prfp, "Subject: %s\n", msginfo->subject); if (r < 0) goto fpferr; }
1213 if (fputc('\n', prfp) == EOF) goto fpferr;
1215 while (fgets(buf, sizeof(buf), tmpfp) != NULL) {
1216 r = fputs(buf, prfp);
1217 if (r == EOF) goto fpferr;
1223 if (cmdline && (p = strchr(cmdline, '%')) && *(p + 1) == 's' &&
1224 !strchr(p + 2, '%'))
1225 g_snprintf(buf, sizeof(buf) - 1, cmdline, prtmp);
1228 g_warning("Print command-line is invalid: '%s'\n",
1230 g_snprintf(buf, sizeof(buf) - 1, def_cmd, prtmp);
1236 if (buf[strlen(buf) - 1] != '&')
1237 strncat(buf, "&", sizeof(buf) - strlen(buf) - 1);
1238 if (system(buf) == -1)
1239 g_warning("system(%s) failed.", buf);
1242 FILE_OP_ERROR(prtmp, "fprintf/fputc/fputs");
1248 MsgInfo *procmsg_msginfo_new_ref(MsgInfo *msginfo)
1255 MsgInfo *procmsg_msginfo_new(void)
1257 MsgInfo *newmsginfo;
1259 newmsginfo = g_new0(MsgInfo, 1);
1260 newmsginfo->refcnt = 1;
1265 static MsgInfoAvatar *procmsg_msginfoavatar_copy(MsgInfoAvatar *avatar)
1267 MsgInfoAvatar *newavatar;
1269 if (avatar == NULL) return NULL;
1271 newavatar = g_new0(MsgInfoAvatar, 1);
1272 newavatar->avatar_id = avatar->avatar_id;
1273 newavatar->avatar_src = g_strdup(avatar->avatar_src);
1278 static void procmsg_msginfoavatar_free(MsgInfoAvatar *avatar)
1280 if (avatar != NULL) {
1281 if (avatar->avatar_src != NULL)
1282 g_free(avatar->avatar_src);
1287 MsgInfo *procmsg_msginfo_copy(MsgInfo *msginfo)
1289 MsgInfo *newmsginfo;
1292 if (msginfo == NULL) return NULL;
1294 newmsginfo = g_new0(MsgInfo, 1);
1296 newmsginfo->refcnt = 1;
1298 #define MEMBCOPY(mmb) newmsginfo->mmb = msginfo->mmb
1299 #define MEMBDUP(mmb) newmsginfo->mmb = msginfo->mmb ? \
1300 g_strdup(msginfo->mmb) : NULL
1315 MEMBDUP(newsgroups);
1322 MEMBCOPY(to_folder);
1324 if (msginfo->extradata) {
1325 newmsginfo->extradata = g_new0(MsgInfoExtraData, 1);
1326 if (msginfo->extradata->avatars) {
1327 newmsginfo->extradata->avatars = slist_copy_deep(msginfo->extradata->avatars,
1328 (GCopyFunc) procmsg_msginfoavatar_copy);
1330 MEMBDUP(extradata->dispositionnotificationto);
1331 MEMBDUP(extradata->returnreceiptto);
1332 MEMBDUP(extradata->partial_recv);
1333 MEMBDUP(extradata->account_server);
1334 MEMBDUP(extradata->account_login);
1335 MEMBDUP(extradata->list_post);
1336 MEMBDUP(extradata->list_subscribe);
1337 MEMBDUP(extradata->list_unsubscribe);
1338 MEMBDUP(extradata->list_help);
1339 MEMBDUP(extradata->list_archive);
1340 MEMBDUP(extradata->list_owner);
1343 refs = msginfo->references;
1344 for (refs = msginfo->references; refs != NULL; refs = refs->next) {
1345 newmsginfo->references = g_slist_prepend
1346 (newmsginfo->references, g_strdup(refs->data));
1348 newmsginfo->references = g_slist_reverse(newmsginfo->references);
1351 MEMBDUP(plaintext_file);
1356 MsgInfo *procmsg_msginfo_get_full_info_from_file(MsgInfo *msginfo, const gchar *file)
1358 MsgInfo *full_msginfo;
1360 if (msginfo == NULL) return NULL;
1362 if (!file || !is_file_exist(file)) {
1363 g_warning("procmsg_msginfo_get_full_info_from_file(): can't get message file.\n");
1367 full_msginfo = procheader_parse_file(file, msginfo->flags, TRUE, FALSE);
1368 if (!full_msginfo) return NULL;
1370 msginfo->total_size = full_msginfo->total_size;
1371 msginfo->planned_download = full_msginfo->planned_download;
1373 if (full_msginfo->extradata) {
1374 if (!msginfo->extradata)
1375 msginfo->extradata = g_new0(MsgInfoExtraData, 1);
1376 if (!msginfo->extradata->list_post)
1377 msginfo->extradata->list_post = g_strdup(full_msginfo->extradata->list_post);
1378 if (!msginfo->extradata->list_subscribe)
1379 msginfo->extradata->list_subscribe = g_strdup(full_msginfo->extradata->list_subscribe);
1380 if (!msginfo->extradata->list_unsubscribe)
1381 msginfo->extradata->list_unsubscribe = g_strdup(full_msginfo->extradata->list_unsubscribe);
1382 if (!msginfo->extradata->list_help)
1383 msginfo->extradata->list_help = g_strdup(full_msginfo->extradata->list_help);
1384 if (!msginfo->extradata->list_archive)
1385 msginfo->extradata->list_archive= g_strdup(full_msginfo->extradata->list_archive);
1386 if (!msginfo->extradata->list_owner)
1387 msginfo->extradata->list_owner = g_strdup(full_msginfo->extradata->list_owner);
1388 if (!msginfo->extradata->avatars)
1389 msginfo->extradata->avatars = slist_copy_deep(full_msginfo->extradata->avatars,
1390 (GCopyFunc) procmsg_msginfoavatar_copy);
1391 if (!msginfo->extradata->dispositionnotificationto)
1392 msginfo->extradata->dispositionnotificationto =
1393 g_strdup(full_msginfo->extradata->dispositionnotificationto);
1394 if (!msginfo->extradata->returnreceiptto)
1395 msginfo->extradata->returnreceiptto = g_strdup
1396 (full_msginfo->extradata->returnreceiptto);
1397 if (!msginfo->extradata->partial_recv && full_msginfo->extradata->partial_recv)
1398 msginfo->extradata->partial_recv = g_strdup
1399 (full_msginfo->extradata->partial_recv);
1400 if (!msginfo->extradata->account_server && full_msginfo->extradata->account_server)
1401 msginfo->extradata->account_server = g_strdup
1402 (full_msginfo->extradata->account_server);
1403 if (!msginfo->extradata->account_login && full_msginfo->extradata->account_login)
1404 msginfo->extradata->account_login = g_strdup
1405 (full_msginfo->extradata->account_login);
1407 procmsg_msginfo_free(full_msginfo);
1409 return procmsg_msginfo_new_ref(msginfo);
1412 MsgInfo *procmsg_msginfo_get_full_info(MsgInfo *msginfo)
1414 MsgInfo *full_msginfo;
1417 if (msginfo == NULL) return NULL;
1419 file = procmsg_get_message_file_path(msginfo);
1420 if (!file || !is_file_exist(file)) {
1422 file = procmsg_get_message_file(msginfo);
1424 if (!file || !is_file_exist(file)) {
1425 g_warning("procmsg_msginfo_get_full_info(): can't get message file.\n");
1429 full_msginfo = procmsg_msginfo_get_full_info_from_file(msginfo, file);
1431 return full_msginfo;
1434 void procmsg_msginfo_free(MsgInfo *msginfo)
1436 if (msginfo == NULL) return;
1439 if (msginfo->refcnt > 0)
1442 if (msginfo->to_folder) {
1443 msginfo->to_folder->op_count--;
1444 folder_item_update(msginfo->to_folder, F_ITEM_UPDATE_MSGCNT);
1447 g_free(msginfo->fromspace);
1449 g_free(msginfo->fromname);
1451 g_free(msginfo->date);
1452 g_free(msginfo->from);
1453 g_free(msginfo->to);
1454 g_free(msginfo->cc);
1455 g_free(msginfo->newsgroups);
1456 g_free(msginfo->subject);
1457 g_free(msginfo->msgid);
1458 g_free(msginfo->inreplyto);
1459 g_free(msginfo->xref);
1461 if (msginfo->extradata) {
1462 if (msginfo->extradata->avatars) {
1463 g_slist_foreach(msginfo->extradata->avatars,
1464 (GFunc)procmsg_msginfoavatar_free,
1466 g_slist_free(msginfo->extradata->avatars);
1468 g_free(msginfo->extradata->returnreceiptto);
1469 g_free(msginfo->extradata->dispositionnotificationto);
1470 g_free(msginfo->extradata->list_post);
1471 g_free(msginfo->extradata->list_subscribe);
1472 g_free(msginfo->extradata->list_unsubscribe);
1473 g_free(msginfo->extradata->list_help);
1474 g_free(msginfo->extradata->list_archive);
1475 g_free(msginfo->extradata->list_owner);
1476 g_free(msginfo->extradata->partial_recv);
1477 g_free(msginfo->extradata->account_server);
1478 g_free(msginfo->extradata->account_login);
1479 g_free(msginfo->extradata);
1481 slist_free_strings_full(msginfo->references);
1482 g_slist_free(msginfo->tags);
1484 g_free(msginfo->plaintext_file);
1489 guint procmsg_msginfo_memusage(MsgInfo *msginfo)
1494 memusage += sizeof(MsgInfo);
1495 if (msginfo->fromname)
1496 memusage += strlen(msginfo->fromname);
1498 memusage += strlen(msginfo->date);
1500 memusage += strlen(msginfo->from);
1502 memusage += strlen(msginfo->to);
1504 memusage += strlen(msginfo->cc);
1505 if (msginfo->newsgroups)
1506 memusage += strlen(msginfo->newsgroups);
1507 if (msginfo->subject)
1508 memusage += strlen(msginfo->subject);
1510 memusage += strlen(msginfo->msgid);
1511 if (msginfo->inreplyto)
1512 memusage += strlen(msginfo->inreplyto);
1514 for (tmp = msginfo->references; tmp; tmp=tmp->next) {
1515 gchar *r = (gchar *)tmp->data;
1516 memusage += r?strlen(r):0 + sizeof(GSList);
1518 if (msginfo->fromspace)
1519 memusage += strlen(msginfo->fromspace);
1521 for (tmp = msginfo->tags; tmp; tmp=tmp->next) {
1522 memusage += sizeof(GSList);
1524 if (msginfo->extradata) {
1525 memusage += sizeof(MsgInfoExtraData);
1526 if (msginfo->extradata->avatars) {
1527 for (tmp = msginfo->extradata->avatars; tmp; tmp = tmp->next) {
1528 MsgInfoAvatar *avt = (MsgInfoAvatar *)tmp->data;
1529 memusage += (avt->avatar_src)? strlen(avt->avatar_src): 0;
1530 memusage += sizeof(MsgInfoAvatar) + sizeof(GSList);
1533 if (msginfo->extradata->dispositionnotificationto)
1534 memusage += strlen(msginfo->extradata->dispositionnotificationto);
1535 if (msginfo->extradata->returnreceiptto)
1536 memusage += strlen(msginfo->extradata->returnreceiptto);
1538 if (msginfo->extradata->partial_recv)
1539 memusage += strlen(msginfo->extradata->partial_recv);
1540 if (msginfo->extradata->account_server)
1541 memusage += strlen(msginfo->extradata->account_server);
1542 if (msginfo->extradata->account_login)
1543 memusage += strlen(msginfo->extradata->account_login);
1545 if (msginfo->extradata->list_post)
1546 memusage += strlen(msginfo->extradata->list_post);
1547 if (msginfo->extradata->list_subscribe)
1548 memusage += strlen(msginfo->extradata->list_subscribe);
1549 if (msginfo->extradata->list_unsubscribe)
1550 memusage += strlen(msginfo->extradata->list_unsubscribe);
1551 if (msginfo->extradata->list_help)
1552 memusage += strlen(msginfo->extradata->list_help);
1553 if (msginfo->extradata->list_archive)
1554 memusage += strlen(msginfo->extradata->list_archive);
1555 if (msginfo->extradata->list_owner)
1556 memusage += strlen(msginfo->extradata->list_owner);
1561 static gint procmsg_send_message_queue_full(const gchar *file, gboolean keep_session, gchar **errstr,
1562 FolderItem *queue, gint msgnum, gboolean *queued_removed)
1564 static HeaderEntry qentry[] = {{"S:", NULL, FALSE},
1565 {"SSV:", NULL, FALSE},
1566 {"R:", NULL, FALSE},
1567 {"NG:", NULL, FALSE},
1568 {"MAID:", NULL, FALSE},
1569 {"NAID:", NULL, FALSE},
1570 {"SCF:", NULL, FALSE},
1571 {"RMID:", NULL, FALSE},
1572 {"FMID:", NULL, FALSE},
1573 {"X-Claws-Privacy-System:", NULL, FALSE},
1574 {"X-Claws-Encrypt:", NULL, FALSE},
1575 {"X-Claws-Encrypt-Data:", NULL, FALSE},
1576 {"X-Claws-End-Special-Headers:", NULL, FALSE},
1577 {"X-Sylpheed-Privacy-System:", NULL, FALSE},
1578 {"X-Sylpheed-Encrypt:", NULL, FALSE},
1579 {"X-Sylpheed-Encrypt-Data:", NULL, FALSE},
1580 {"X-Sylpheed-End-Special-Headers:", NULL, FALSE},
1581 {NULL, NULL, FALSE}};
1584 gint mailval = 0, newsval = 0;
1586 gchar *smtpserver = NULL;
1587 GSList *to_list = NULL;
1588 GSList *newsgroup_list = NULL;
1589 gchar *savecopyfolder = NULL;
1590 gchar *replymessageid = NULL;
1591 gchar *fwdmessageid = NULL;
1592 gchar *privacy_system = NULL;
1593 gboolean encrypt = FALSE;
1594 gchar *encrypt_data = NULL;
1595 gchar buf[BUFFSIZE];
1597 PrefsAccount *mailac = NULL, *newsac = NULL;
1598 gboolean save_clear_text = TRUE;
1599 gchar *tmp_enc_file = NULL;
1601 cm_return_val_if_fail(file != NULL, -1);
1603 if ((fp = g_fopen(file, "rb")) == NULL) {
1604 FILE_OP_ERROR(file, "fopen");
1606 if (*errstr) g_free(*errstr);
1607 *errstr = g_strdup_printf(_("Couldn't open file %s."), file);
1612 while ((hnum = procheader_get_one_field(buf, sizeof(buf), fp, qentry))
1614 gchar *p = buf + strlen(qentry[hnum].name);
1622 if (smtpserver == NULL)
1623 smtpserver = g_strdup(p);
1626 to_list = address_list_append(to_list, p);
1629 newsgroup_list = newsgroup_list_append(newsgroup_list, p);
1631 case Q_MAIL_ACCOUNT_ID:
1632 mailac = account_find_from_id(atoi(p));
1634 case Q_NEWS_ACCOUNT_ID:
1635 newsac = account_find_from_id(atoi(p));
1637 case Q_SAVE_COPY_FOLDER:
1638 if (savecopyfolder == NULL)
1639 savecopyfolder = g_strdup(p);
1641 case Q_REPLY_MESSAGE_ID:
1642 if (replymessageid == NULL)
1643 replymessageid = g_strdup(p);
1645 case Q_FWD_MESSAGE_ID:
1646 if (fwdmessageid == NULL)
1647 fwdmessageid = g_strdup(p);
1649 case Q_PRIVACY_SYSTEM:
1650 case Q_PRIVACY_SYSTEM_OLD:
1651 if (privacy_system == NULL)
1652 privacy_system = g_strdup(p);
1659 case Q_ENCRYPT_DATA:
1660 case Q_ENCRYPT_DATA_OLD:
1661 if (encrypt_data == NULL)
1662 encrypt_data = g_strdup(p);
1665 case Q_CLAWS_HDRS_OLD:
1666 /* end of special headers reached */
1667 goto send_mail; /* can't "break;break;" */
1671 filepos = ftell(fp);
1676 if (mailac && mailac->save_encrypted_as_clear_text
1677 && !mailac->encrypt_to_self)
1678 save_clear_text = TRUE;
1680 save_clear_text = FALSE;
1685 mimeinfo = procmime_scan_queue_file(file);
1686 if (!privacy_encrypt(privacy_system, mimeinfo, encrypt_data)
1687 || (fp = my_tmpfile()) == NULL
1688 || procmime_write_mimeinfo(mimeinfo, fp) < 0) {
1691 procmime_mimeinfo_free_all(mimeinfo);
1694 slist_free_strings_full(to_list);
1695 slist_free_strings_full(newsgroup_list);
1696 g_free(savecopyfolder);
1697 g_free(replymessageid);
1698 g_free(fwdmessageid);
1699 g_free(privacy_system);
1700 g_free(encrypt_data);
1702 if (*errstr) g_free(*errstr);
1703 *errstr = g_strdup_printf(_("Couldn't encrypt the email: %s"),
1704 privacy_get_error());
1710 if (!save_clear_text) {
1711 gchar *content = NULL;
1712 FILE *tmpfp = get_tmpfile_in_dir(get_mime_tmp_dir(), &tmp_enc_file);
1716 content = file_read_stream_to_str(fp);
1719 str_write_to_file(content, tmp_enc_file);
1722 g_warning("couldn't get tempfile\n");
1726 procmime_mimeinfo_free_all(mimeinfo);
1732 debug_print("Sending message by mail\n");
1735 if (*errstr) g_free(*errstr);
1736 *errstr = g_strdup_printf(_("Queued message header is broken."));
1739 } else if (mailac && mailac->use_mail_command &&
1740 mailac->mail_command && (* mailac->mail_command)) {
1741 mailval = send_message_local(mailac->mail_command, fp);
1744 mailac = account_find_from_smtp_server(from, smtpserver);
1746 g_warning("Account not found. "
1747 "Using current account...\n");
1748 mailac = cur_account;
1753 mailval = send_message_smtp_full(mailac, to_list, fp, keep_session);
1754 if (mailval == -1 && errstr) {
1755 if (*errstr) g_free(*errstr);
1756 *errstr = g_strdup_printf(_("An error happened during SMTP session."));
1759 PrefsAccount tmp_ac;
1761 g_warning("Account not found.\n");
1763 memset(&tmp_ac, 0, sizeof(PrefsAccount));
1764 tmp_ac.address = from;
1765 tmp_ac.smtp_server = smtpserver;
1766 tmp_ac.smtpport = SMTP_PORT;
1767 mailval = send_message_smtp(&tmp_ac, to_list, fp);
1768 if (mailval == -1 && errstr) {
1769 if (*errstr) g_free(*errstr);
1770 *errstr = g_strdup_printf(_("No specific account has been found to "
1771 "send, and an error happened during SMTP session."));
1775 } else if (!to_list && !newsgroup_list) {
1777 if (*errstr) g_free(*errstr);
1778 *errstr = g_strdup(_("Couldn't determine sending information. "
1779 "Maybe the email hasn't been generated by Claws Mail."));
1784 fseek(fp, filepos, SEEK_SET);
1785 if (newsgroup_list && newsac && (mailval == 0)) {
1790 /* write to temporary file */
1791 tmp = g_strdup_printf("%s%cnntp%p", get_tmp_dir(),
1792 G_DIR_SEPARATOR, file);
1793 if ((tmpfp = g_fopen(tmp, "wb")) == NULL) {
1794 FILE_OP_ERROR(tmp, "fopen");
1796 alertpanel_error(_("Couldn't create temporary file for news sending."));
1798 if (change_file_mode_rw(tmpfp, tmp) < 0) {
1799 FILE_OP_ERROR(tmp, "chmod");
1800 g_warning("can't change file mode\n");
1803 while ((newsval == 0) && fgets(buf, sizeof(buf), fp) != NULL) {
1804 if (fputs(buf, tmpfp) == EOF) {
1805 FILE_OP_ERROR(tmp, "fputs");
1808 if (*errstr) g_free(*errstr);
1809 *errstr = g_strdup_printf(_("Error when writing temporary file for news sending."));
1816 debug_print("Sending message by news\n");
1818 folder = FOLDER(newsac->folder);
1820 newsval = news_post(folder, tmp);
1821 if (newsval < 0 && errstr) {
1822 if (*errstr) g_free(*errstr);
1823 *errstr = g_strdup_printf(_("Error occurred while posting the message to %s."),
1824 newsac->nntp_server);
1834 /* update session statistics */
1835 if (mailval == 0 && newsval == 0) {
1836 /* update session stats */
1838 session_stats.replied++;
1839 else if (fwdmessageid)
1840 session_stats.forwarded++;
1842 session_stats.sent++;
1845 /* save message to outbox */
1846 if (mailval == 0 && newsval == 0 && savecopyfolder) {
1849 debug_print("saving sent message...\n");
1851 outbox = folder_find_item_from_identifier(savecopyfolder);
1853 outbox = folder_get_default_outbox();
1855 if (save_clear_text || tmp_enc_file == NULL) {
1856 gboolean saved = FALSE;
1857 *queued_removed = FALSE;
1858 if (queue && msgnum > 0) {
1859 MsgInfo *queued_mail = folder_item_get_msginfo(queue, msgnum);
1860 if (folder_item_move_msg(outbox, queued_mail) >= 0) {
1861 debug_print("moved queued mail %d to sent folder\n", msgnum);
1863 *queued_removed = TRUE;
1864 } else if (folder_item_copy_msg(outbox, queued_mail) >= 0) {
1865 debug_print("copied queued mail %d to sent folder\n", msgnum);
1868 procmsg_msginfo_free(queued_mail);
1871 debug_print("resaving clear text queued mail to sent folder\n");
1872 procmsg_save_to_outbox(outbox, file, TRUE);
1875 debug_print("saving encrpyted queued mail to sent folder\n");
1876 procmsg_save_to_outbox(outbox, tmp_enc_file, FALSE);
1880 if (tmp_enc_file != NULL) {
1881 claws_unlink(tmp_enc_file);
1883 tmp_enc_file = NULL;
1886 if (replymessageid != NULL || fwdmessageid != NULL) {
1890 if (replymessageid != NULL)
1891 tokens = g_strsplit(replymessageid, "\t", 0);
1893 tokens = g_strsplit(fwdmessageid, "\t", 0);
1894 item = folder_find_item_from_identifier(tokens[0]);
1896 /* check if queued message has valid folder and message id */
1897 if (item != NULL && tokens[2] != NULL) {
1900 msginfo = folder_item_get_msginfo(item, atoi(tokens[1]));
1902 /* check if referring message exists and has a message id */
1903 if ((msginfo != NULL) &&
1904 (msginfo->msgid != NULL) &&
1905 (strcmp(msginfo->msgid, tokens[2]) != 0)) {
1906 procmsg_msginfo_free(msginfo);
1910 if (msginfo == NULL) {
1911 msginfo = folder_item_get_msginfo_by_msgid(item, tokens[2]);
1914 if (msginfo != NULL) {
1915 if (replymessageid != NULL) {
1916 MsgPermFlags to_unset = 0;
1918 if (prefs_common.mark_as_read_on_new_window)
1919 to_unset = (MSG_NEW|MSG_UNREAD);
1921 procmsg_msginfo_unset_flags(msginfo, to_unset, 0);
1922 procmsg_msginfo_set_flags(msginfo, MSG_REPLIED, 0);
1924 procmsg_msginfo_set_flags(msginfo, MSG_FORWARDED, 0);
1926 procmsg_msginfo_free(msginfo);
1934 slist_free_strings_full(to_list);
1935 slist_free_strings_full(newsgroup_list);
1936 g_free(savecopyfolder);
1937 g_free(replymessageid);
1938 g_free(fwdmessageid);
1939 g_free(privacy_system);
1940 g_free(encrypt_data);
1942 return (newsval != 0 ? newsval : mailval);
1945 gint procmsg_send_message_queue(const gchar *file, gchar **errstr, FolderItem *queue, gint msgnum, gboolean *queued_removed)
1947 gint result = procmsg_send_message_queue_full(file, FALSE, errstr, queue, msgnum, queued_removed);
1948 main_window_set_menu_sensitive(mainwindow_get_mainwindow());
1949 toolbar_main_set_sensitive(mainwindow_get_mainwindow());
1953 gint procmsg_send_message_queue_with_lock(const gchar *file, gchar **errstr, FolderItem *queue, gint msgnum, gboolean *queued_removed)
1956 if (procmsg_queue_lock(errstr)) {
1957 val = procmsg_send_message_queue(file, errstr, queue, msgnum, queued_removed);
1958 procmsg_queue_unlock();
1964 static void update_folder_msg_counts(FolderItem *item, MsgInfo *msginfo, MsgPermFlags old_flags)
1966 MsgPermFlags new_flags = msginfo->flags.perm_flags;
1969 if (!(old_flags & MSG_NEW) && (new_flags & MSG_NEW)) {
1973 if ((old_flags & MSG_NEW) && !(new_flags & MSG_NEW)) {
1978 if (!(old_flags & MSG_UNREAD) && (new_flags & MSG_UNREAD)) {
1979 item->unread_msgs++;
1980 if (procmsg_msg_has_marked_parent(msginfo))
1981 item->unreadmarked_msgs++;
1984 if ((old_flags & MSG_UNREAD) && !(new_flags & MSG_UNREAD)) {
1985 item->unread_msgs--;
1986 if (procmsg_msg_has_marked_parent(msginfo))
1987 item->unreadmarked_msgs--;
1991 if (!(old_flags & MSG_MARKED) && (new_flags & MSG_MARKED)) {
1992 procmsg_update_unread_children(msginfo, TRUE);
1993 item->marked_msgs++;
1996 if ((old_flags & MSG_MARKED) && !(new_flags & MSG_MARKED)) {
1997 procmsg_update_unread_children(msginfo, FALSE);
1998 item->marked_msgs--;
2001 if (!(old_flags & MSG_REPLIED) && (new_flags & MSG_REPLIED)) {
2002 item->replied_msgs++;
2005 if ((old_flags & MSG_REPLIED) && !(new_flags & MSG_REPLIED)) {
2006 item->replied_msgs--;
2009 if (!(old_flags & MSG_FORWARDED) && (new_flags & MSG_FORWARDED)) {
2010 item->forwarded_msgs++;
2013 if ((old_flags & MSG_FORWARDED) && !(new_flags & MSG_FORWARDED)) {
2014 item->forwarded_msgs--;
2017 if (!(old_flags & MSG_LOCKED) && (new_flags & MSG_LOCKED)) {
2018 item->locked_msgs++;
2021 if ((old_flags & MSG_LOCKED) && !(new_flags & MSG_LOCKED)) {
2022 item->locked_msgs--;
2025 if ((old_flags & MSG_IGNORE_THREAD) && !(new_flags & MSG_IGNORE_THREAD)) {
2026 item->ignored_msgs--;
2029 if (!(old_flags & MSG_IGNORE_THREAD) && (new_flags & MSG_IGNORE_THREAD)) {
2030 item->ignored_msgs++;
2033 if ((old_flags & MSG_WATCH_THREAD) && !(new_flags & MSG_WATCH_THREAD)) {
2034 item->watched_msgs--;
2037 if (!(old_flags & MSG_WATCH_THREAD) && (new_flags & MSG_WATCH_THREAD)) {
2038 item->watched_msgs++;
2042 void procmsg_msginfo_set_flags(MsgInfo *msginfo, MsgPermFlags perm_flags, MsgTmpFlags tmp_flags)
2045 MsgInfoUpdate msginfo_update;
2046 MsgPermFlags perm_flags_new, perm_flags_old;
2047 MsgTmpFlags tmp_flags_old;
2049 cm_return_if_fail(msginfo != NULL);
2050 item = msginfo->folder;
2051 cm_return_if_fail(item != NULL);
2053 debug_print("Setting flags for message %d in folder %s\n", msginfo->msgnum, item->path);
2055 /* Perm Flags handling */
2056 perm_flags_old = msginfo->flags.perm_flags;
2057 perm_flags_new = msginfo->flags.perm_flags | perm_flags;
2058 if ((perm_flags & MSG_IGNORE_THREAD) || (perm_flags_old & MSG_IGNORE_THREAD)) {
2059 perm_flags_new &= ~(MSG_NEW | MSG_UNREAD);
2061 if ((perm_flags & MSG_WATCH_THREAD) || (perm_flags_old & MSG_WATCH_THREAD)) {
2062 perm_flags_new &= ~(MSG_IGNORE_THREAD);
2065 if (perm_flags_old != perm_flags_new) {
2066 folder_item_change_msg_flags(msginfo->folder, msginfo, perm_flags_new);
2068 update_folder_msg_counts(item, msginfo, perm_flags_old);
2069 summary_update_unread(mainwindow_get_mainwindow()->summaryview, NULL);
2072 /* Tmp flags handling */
2073 tmp_flags_old = msginfo->flags.tmp_flags;
2074 msginfo->flags.tmp_flags |= tmp_flags;
2076 /* update notification */
2077 if ((perm_flags_old != perm_flags_new) || (tmp_flags_old != msginfo->flags.tmp_flags)) {
2078 msginfo_update.msginfo = msginfo;
2079 msginfo_update.flags = MSGINFO_UPDATE_FLAGS;
2080 hooks_invoke(MSGINFO_UPDATE_HOOKLIST, &msginfo_update);
2081 folder_item_update(msginfo->folder, F_ITEM_UPDATE_MSGCNT);
2085 void procmsg_msginfo_unset_flags(MsgInfo *msginfo, MsgPermFlags perm_flags, MsgTmpFlags tmp_flags)
2088 MsgInfoUpdate msginfo_update;
2089 MsgPermFlags perm_flags_new, perm_flags_old;
2090 MsgTmpFlags tmp_flags_old;
2092 cm_return_if_fail(msginfo != NULL);
2093 item = msginfo->folder;
2094 cm_return_if_fail(item != NULL);
2096 debug_print("Unsetting flags for message %d in folder %s\n", msginfo->msgnum, item->path);
2098 /* Perm Flags handling */
2099 perm_flags_old = msginfo->flags.perm_flags;
2100 perm_flags_new = msginfo->flags.perm_flags & ~perm_flags;
2102 if (perm_flags_old != perm_flags_new) {
2103 folder_item_change_msg_flags(msginfo->folder, msginfo, perm_flags_new);
2105 update_folder_msg_counts(item, msginfo, perm_flags_old);
2108 /* Tmp flags hanlding */
2109 tmp_flags_old = msginfo->flags.tmp_flags;
2110 msginfo->flags.tmp_flags &= ~tmp_flags;
2112 /* update notification */
2113 if ((perm_flags_old != perm_flags_new) || (tmp_flags_old != msginfo->flags.tmp_flags)) {
2114 msginfo_update.msginfo = msginfo;
2115 msginfo_update.flags = MSGINFO_UPDATE_FLAGS;
2116 hooks_invoke(MSGINFO_UPDATE_HOOKLIST, &msginfo_update);
2117 folder_item_update(msginfo->folder, F_ITEM_UPDATE_MSGCNT);
2121 void procmsg_msginfo_change_flags(MsgInfo *msginfo,
2122 MsgPermFlags add_perm_flags, MsgTmpFlags add_tmp_flags,
2123 MsgPermFlags rem_perm_flags, MsgTmpFlags rem_tmp_flags)
2126 MsgInfoUpdate msginfo_update;
2127 MsgPermFlags perm_flags_new, perm_flags_old;
2128 MsgTmpFlags tmp_flags_old;
2130 cm_return_if_fail(msginfo != NULL);
2131 item = msginfo->folder;
2132 cm_return_if_fail(item != NULL);
2134 debug_print("Changing flags for message %d in folder %s\n", msginfo->msgnum, item->path);
2136 /* Perm Flags handling */
2137 perm_flags_old = msginfo->flags.perm_flags;
2138 perm_flags_new = (msginfo->flags.perm_flags & ~rem_perm_flags) | add_perm_flags;
2139 if ((add_perm_flags & MSG_IGNORE_THREAD) || (perm_flags_old & MSG_IGNORE_THREAD)) {
2140 perm_flags_new &= ~(MSG_NEW | MSG_UNREAD);
2142 if ((add_perm_flags & MSG_WATCH_THREAD) || (perm_flags_old & MSG_WATCH_THREAD)) {
2143 perm_flags_new &= ~(MSG_IGNORE_THREAD);
2146 if (perm_flags_old != perm_flags_new) {
2147 folder_item_change_msg_flags(msginfo->folder, msginfo, perm_flags_new);
2149 update_folder_msg_counts(item, msginfo, perm_flags_old);
2153 /* Tmp flags handling */
2154 tmp_flags_old = msginfo->flags.tmp_flags;
2155 msginfo->flags.tmp_flags &= ~rem_tmp_flags;
2156 msginfo->flags.tmp_flags |= add_tmp_flags;
2158 /* update notification */
2159 if ((perm_flags_old != perm_flags_new) || (tmp_flags_old != msginfo->flags.tmp_flags)) {
2160 msginfo_update.msginfo = msginfo;
2161 msginfo_update.flags = MSGINFO_UPDATE_FLAGS;
2162 hooks_invoke(MSGINFO_UPDATE_HOOKLIST, &msginfo_update);
2163 folder_item_update(msginfo->folder, F_ITEM_UPDATE_MSGCNT);
2168 *\brief check for flags (e.g. mark) in prior msgs of current thread
2170 *\param info Current message
2171 *\param perm_flags Flags to be checked
2172 *\param parentmsgs Hash of prior msgs to avoid loops
2174 *\return gboolean TRUE if perm_flags are found
2176 static gboolean procmsg_msg_has_flagged_parent_real(MsgInfo *info,
2177 MsgPermFlags perm_flags, GHashTable *parentmsgs)
2181 cm_return_val_if_fail(info != NULL, FALSE);
2183 if (info != NULL && info->folder != NULL && info->inreplyto != NULL) {
2184 tmp = folder_item_get_msginfo_by_msgid(info->folder,
2186 if (tmp && (tmp->flags.perm_flags & perm_flags)) {
2187 procmsg_msginfo_free(tmp);
2189 } else if (tmp != NULL) {
2192 if (g_hash_table_lookup(parentmsgs, info)) {
2193 debug_print("loop detected: %d\n",
2197 g_hash_table_insert(parentmsgs, info, "1");
2198 result = procmsg_msg_has_flagged_parent_real(
2199 tmp, perm_flags, parentmsgs);
2201 procmsg_msginfo_free(tmp);
2211 *\brief Callback for cleaning up hash of parentmsgs
2213 static gboolean parentmsgs_hash_remove(gpointer key,
2221 *\brief Set up list of parentmsgs
2222 * See procmsg_msg_has_flagged_parent_real()
2224 gboolean procmsg_msg_has_flagged_parent(MsgInfo *info, MsgPermFlags perm_flags)
2227 static GHashTable *parentmsgs = NULL;
2229 if (parentmsgs == NULL)
2230 parentmsgs = g_hash_table_new(NULL, NULL);
2232 result = procmsg_msg_has_flagged_parent_real(info, perm_flags, parentmsgs);
2233 g_hash_table_foreach_remove(parentmsgs, parentmsgs_hash_remove, NULL);
2239 *\brief Check if msgs prior in thread are marked
2240 * See procmsg_msg_has_flagged_parent_real()
2242 gboolean procmsg_msg_has_marked_parent(MsgInfo *info)
2244 return procmsg_msg_has_flagged_parent(info, MSG_MARKED);
2248 static GSList *procmsg_find_children_func(MsgInfo *info,
2249 GSList *children, GSList *all)
2253 cm_return_val_if_fail(info!=NULL, children);
2254 if (info->msgid == NULL)
2257 for (cur = all; cur != NULL; cur = g_slist_next(cur)) {
2258 MsgInfo *tmp = (MsgInfo *)cur->data;
2259 if (tmp->inreplyto && !strcmp(tmp->inreplyto, info->msgid)) {
2260 /* Check if message is already in the list */
2261 if ((children == NULL) ||
2262 (g_slist_index(children, tmp) == -1)) {
2263 children = g_slist_prepend(children,
2264 procmsg_msginfo_new_ref(tmp));
2265 children = procmsg_find_children_func(tmp,
2274 static GSList *procmsg_find_children (MsgInfo *info)
2279 cm_return_val_if_fail(info!=NULL, NULL);
2280 all = folder_item_get_msg_list(info->folder);
2281 children = procmsg_find_children_func(info, NULL, all);
2282 if (children != NULL) {
2283 for (cur = all; cur != NULL; cur = g_slist_next(cur)) {
2284 /* this will not free the used pointers
2285 created with procmsg_msginfo_new_ref */
2286 procmsg_msginfo_free((MsgInfo *)cur->data);
2294 static void procmsg_update_unread_children(MsgInfo *info, gboolean newly_marked)
2296 GSList *children = procmsg_find_children(info);
2298 for (cur = children; cur != NULL; cur = g_slist_next(cur)) {
2299 MsgInfo *tmp = (MsgInfo *)cur->data;
2300 if(MSG_IS_UNREAD(tmp->flags) && !MSG_IS_IGNORE_THREAD(tmp->flags)) {
2302 info->folder->unreadmarked_msgs++;
2304 info->folder->unreadmarked_msgs--;
2305 folder_item_update(info->folder, F_ITEM_UPDATE_MSGCNT);
2307 procmsg_msginfo_free(tmp);
2309 g_slist_free(children);
2313 * Set the destination folder for a copy or move operation
2315 * \param msginfo The message which's destination folder is changed
2316 * \param to_folder The destination folder for the operation
2318 void procmsg_msginfo_set_to_folder(MsgInfo *msginfo, FolderItem *to_folder)
2320 if(msginfo->to_folder != NULL) {
2321 msginfo->to_folder->op_count--;
2322 folder_item_update(msginfo->to_folder, F_ITEM_UPDATE_MSGCNT);
2324 msginfo->to_folder = to_folder;
2325 if(to_folder != NULL) {
2326 to_folder->op_count++;
2327 folder_item_update(msginfo->to_folder, F_ITEM_UPDATE_MSGCNT);
2332 * Apply filtering actions to the msginfo
2334 * \param msginfo The MsgInfo describing the message that should be filtered
2335 * \return TRUE if the message was moved and MsgInfo is now invalid,
2338 static gboolean procmsg_msginfo_filter(MsgInfo *msginfo, PrefsAccount* ac_prefs)
2340 MailFilteringData mail_filtering_data;
2342 mail_filtering_data.msginfo = msginfo;
2343 mail_filtering_data.msglist = NULL;
2344 mail_filtering_data.filtered = NULL;
2345 mail_filtering_data.unfiltered = NULL;
2346 mail_filtering_data.account = ac_prefs;
2348 if (!ac_prefs || ac_prefs->filterhook_on_recv)
2349 if (hooks_invoke(MAIL_FILTERING_HOOKLIST, &mail_filtering_data))
2352 /* filter if enabled in prefs or move to inbox if not */
2353 if((filtering_rules != NULL) &&
2354 filter_message_by_msginfo(filtering_rules, msginfo, ac_prefs,
2355 FILTERING_INCORPORATION, NULL)) {
2362 void procmsg_msglist_filter(GSList *list, PrefsAccount *ac,
2363 GSList **filtered, GSList **unfiltered,
2366 GSList *cur, *to_do = NULL;
2367 gint total = 0, curnum = 0;
2368 MailFilteringData mail_filtering_data;
2370 cm_return_if_fail(filtered != NULL);
2371 cm_return_if_fail(unfiltered != NULL);
2379 total = g_slist_length(list);
2383 *unfiltered = g_slist_copy(list);
2387 statusbar_print_all(_("Filtering messages...\n"));
2389 mail_filtering_data.msginfo = NULL;
2390 mail_filtering_data.msglist = list;
2391 mail_filtering_data.filtered = NULL;
2392 mail_filtering_data.unfiltered = NULL;
2393 mail_filtering_data.account = ac;
2395 if (!ac || ac->filterhook_on_recv)
2396 hooks_invoke(MAIL_LISTFILTERING_HOOKLIST, &mail_filtering_data);
2398 if (mail_filtering_data.filtered == NULL &&
2399 mail_filtering_data.unfiltered == NULL) {
2400 /* nothing happened */
2401 debug_print(MAIL_LISTFILTERING_HOOKLIST " did nothing. filtering whole list normally.\n");
2404 if (mail_filtering_data.filtered != NULL) {
2405 /* keep track of what's been filtered by the hooks */
2406 debug_print(MAIL_LISTFILTERING_HOOKLIST " filtered some stuff. total %d filtered %d unfilt %d.\n",
2407 g_slist_length(list),
2408 g_slist_length(mail_filtering_data.filtered),
2409 g_slist_length(mail_filtering_data.unfiltered));
2411 *filtered = g_slist_copy(mail_filtering_data.filtered);
2413 if (mail_filtering_data.unfiltered != NULL) {
2414 /* what the hooks didn't handle will go in filtered or
2415 * unfiltered in the next loop */
2416 debug_print(MAIL_LISTFILTERING_HOOKLIST " left unfiltered stuff. total %d filtered %d unfilt %d.\n",
2417 g_slist_length(list),
2418 g_slist_length(mail_filtering_data.filtered),
2419 g_slist_length(mail_filtering_data.unfiltered));
2420 to_do = mail_filtering_data.unfiltered;
2423 for (cur = to_do; cur; cur = cur->next) {
2424 MsgInfo *info = (MsgInfo *)cur->data;
2425 if (procmsg_msginfo_filter(info, ac))
2426 *filtered = g_slist_prepend(*filtered, info);
2428 *unfiltered = g_slist_prepend(*unfiltered, info);
2429 statusbar_progress_all(curnum++, total, prefs_common.statusbar_update_step);
2432 g_slist_free(mail_filtering_data.filtered);
2433 g_slist_free(mail_filtering_data.unfiltered);
2435 *filtered = g_slist_reverse(*filtered);
2436 *unfiltered = g_slist_reverse(*unfiltered);
2438 statusbar_progress_all(0,0,0);
2439 statusbar_pop_all();
2442 MsgInfo *procmsg_msginfo_new_from_mimeinfo(MsgInfo *src_msginfo, MimeInfo *mimeinfo)
2444 MsgInfo *tmp_msginfo = NULL;
2445 MsgFlags flags = {0, 0};
2446 gchar *tmpfile = get_tmp_file();
2447 FILE *fp = g_fopen(tmpfile, "wb");
2449 if (!mimeinfo || mimeinfo->type != MIMETYPE_MESSAGE ||
2450 g_ascii_strcasecmp(mimeinfo->subtype, "rfc822")) {
2451 g_warning("procmsg_msginfo_new_from_mimeinfo(): unsuitable mimeinfo");
2458 if (fp && procmime_write_mimeinfo(mimeinfo, fp) >= 0) {
2461 tmp_msginfo = procheader_parse_file(
2468 if (tmp_msginfo != NULL) {
2470 tmp_msginfo->folder = src_msginfo->folder;
2471 tmp_msginfo->plaintext_file = g_strdup(tmpfile);
2473 g_warning("procmsg_msginfo_new_from_mimeinfo(): Can't generate new msginfo");
2481 static GSList *spam_learners = NULL;
2483 void procmsg_register_spam_learner (int (*learn_func)(MsgInfo *info, GSList *list, gboolean spam))
2485 if (!g_slist_find(spam_learners, learn_func))
2486 spam_learners = g_slist_append(spam_learners, learn_func);
2487 if (mainwindow_get_mainwindow()) {
2488 main_window_set_menu_sensitive(mainwindow_get_mainwindow());
2489 summary_set_menu_sensitive(
2490 mainwindow_get_mainwindow()->summaryview);
2491 toolbar_main_set_sensitive(mainwindow_get_mainwindow());
2495 void procmsg_unregister_spam_learner (int (*learn_func)(MsgInfo *info, GSList *list, gboolean spam))
2497 spam_learners = g_slist_remove(spam_learners, learn_func);
2498 if (mainwindow_get_mainwindow()) {
2499 main_window_set_menu_sensitive(mainwindow_get_mainwindow());
2500 summary_set_menu_sensitive(
2501 mainwindow_get_mainwindow()->summaryview);
2502 toolbar_main_set_sensitive(mainwindow_get_mainwindow());
2506 gboolean procmsg_spam_can_learn(void)
2508 return g_slist_length(spam_learners) > 0;
2511 int procmsg_spam_learner_learn (MsgInfo *info, GSList *list, gboolean spam)
2513 GSList *cur = spam_learners;
2515 for (; cur; cur = cur->next) {
2516 int ((*func)(MsgInfo *info, GSList *list, gboolean spam)) = cur->data;
2517 ret |= func(info, list, spam);
2522 static gchar *spam_folder_item = NULL;
2523 static FolderItem * (*procmsg_spam_get_folder_func)(MsgInfo *msginfo) = NULL;
2524 void procmsg_spam_set_folder (const char *item_identifier, FolderItem *(*spam_get_folder_func)(MsgInfo *info))
2526 g_free(spam_folder_item);
2527 if (item_identifier)
2528 spam_folder_item = g_strdup(item_identifier);
2530 spam_folder_item = NULL;
2531 if (spam_get_folder_func != NULL)
2532 procmsg_spam_get_folder_func = spam_get_folder_func;
2534 procmsg_spam_get_folder_func = NULL;
2537 FolderItem *procmsg_spam_get_folder (MsgInfo *msginfo)
2539 FolderItem *item = NULL;
2541 if (procmsg_spam_get_folder_func)
2542 item = procmsg_spam_get_folder_func(msginfo);
2543 if (item == NULL && spam_folder_item)
2544 item = folder_find_item_from_identifier(spam_folder_item);
2546 item = folder_get_default_trash();
2550 static void item_has_queued_mails(FolderItem *item, gpointer data)
2552 gboolean *result = (gboolean *)data;
2553 if (*result == TRUE)
2555 if (folder_has_parent_of_type(item, F_QUEUE)) {
2556 if (item->total_msgs == 0)
2559 GSList *msglist = folder_item_get_msg_list(item);
2561 for (cur = msglist; cur; cur = cur->next) {
2562 MsgInfo *msginfo = (MsgInfo *)cur->data;
2563 if (!MSG_IS_DELETED(msginfo->flags) &&
2564 !MSG_IS_LOCKED(msginfo->flags)) {
2569 procmsg_msg_list_free(msglist);
2574 gboolean procmsg_have_queued_mails_fast (void)
2576 gboolean result = FALSE;
2577 folder_func_to_all_folders(item_has_queued_mails, &result);
2581 static void item_has_trashed_mails(FolderItem *item, gpointer data)
2583 gboolean *result = (gboolean *)data;
2584 if (*result == TRUE)
2586 if (folder_has_parent_of_type(item, F_TRASH) && item->total_msgs > 0)
2590 gboolean procmsg_have_trashed_mails_fast (void)
2592 gboolean result = FALSE;
2593 folder_func_to_all_folders(item_has_trashed_mails, &result);
2597 gchar *procmsg_msginfo_get_tags_str(MsgInfo *msginfo)
2605 if (msginfo->tags == NULL)
2607 for (cur = msginfo->tags; cur; cur = cur->next) {
2608 const gchar *tag = tags_get_tag(GPOINTER_TO_INT(cur->data));
2612 tags = g_strdup(tag);
2614 int olen = strlen(tags);
2615 int nlen = olen + strlen(tag) + 2 /* strlen(", ") */;
2616 tags = g_realloc(tags, nlen+1);
2619 strcpy(tags+olen, ", ");
2620 strcpy(tags+olen+2, tag);
2627 void procmsg_msginfo_update_tags(MsgInfo *msginfo, gboolean set, gint id)
2635 msginfo->tags = g_slist_remove(
2637 GINT_TO_POINTER(id));
2638 changed.data = GINT_TO_POINTER(id);
2639 changed.next = NULL;
2640 folder_item_commit_tags(msginfo->folder, msginfo, NULL, &changed);
2642 if (!g_slist_find(msginfo->tags, GINT_TO_POINTER(id))) {
2643 msginfo->tags = g_slist_append(
2645 GINT_TO_POINTER(id));
2647 changed.data = GINT_TO_POINTER(id);
2648 changed.next = NULL;
2649 folder_item_commit_tags(msginfo->folder, msginfo, &changed, NULL);
2654 void procmsg_msginfo_clear_tags(MsgInfo *msginfo)
2656 GSList *unset = msginfo->tags;
2657 msginfo->tags = NULL;
2658 folder_item_commit_tags(msginfo->folder, msginfo, NULL, unset);
2659 g_slist_free(unset);