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 void procmsg_msginfo_add_avatar(MsgInfo *msginfo, gint type, const gchar *data)
795 if (!msginfo->extradata)
796 msginfo->extradata = g_new0(MsgInfoExtraData, 1);
798 av = g_new0(MsgInfoAvatar, 1);
799 av->avatar_id = type;
800 av->avatar_src = g_strdup(data);
802 msginfo->extradata->avatars = g_slist_append(msginfo->extradata->avatars, av);
805 gchar *procmsg_msginfo_get_identifier(MsgInfo *msginfo)
811 cm_return_val_if_fail(msginfo != NULL, NULL);
812 folder_id = folder_item_get_identifier(msginfo->folder);
813 msgid = msginfo->msgid;
815 id = g_strconcat(folder_id, G_DIR_SEPARATOR_S, msgid, NULL);
822 MsgInfo *procmsg_get_msginfo_from_identifier(const gchar *id)
824 gchar *folder_id = g_strdup(id);
825 gchar *separator = strrchr(folder_id, G_DIR_SEPARATOR);
830 if (separator == NULL) {
836 msgid = separator + 1;
838 item = folder_find_item_from_identifier(folder_id);
845 msginfo = folder_item_get_msginfo_by_msgid(item, msgid);
851 static GSList *procmsg_list_sort_by_account(FolderItem *queue, GSList *list)
853 GSList *result = NULL;
855 PrefsAccount *last_account = NULL;
858 gboolean nothing_to_sort = TRUE;
863 orig = g_slist_copy(list);
865 msg = (MsgInfo *)orig->data;
867 for (cur = orig; cur; cur = cur->next)
868 debug_print("sort before %s\n", ((MsgInfo *)cur->data)->from);
873 nothing_to_sort = TRUE;
877 PrefsAccount *ac = NULL;
878 msg = (MsgInfo *)cur->data;
879 file = folder_item_fetch_msg(queue, msg->msgnum);
880 ac = procmsg_get_account_from_file(file);
883 if (last_account == NULL || (ac != NULL && ac == last_account)) {
884 result = g_slist_append(result, msg);
885 orig = g_slist_remove(orig, msg);
887 nothing_to_sort = FALSE;
893 if (orig || g_slist_length(orig)) {
894 if (!last_account && nothing_to_sort) {
895 /* can't find an account for the rest of the list */
898 result = g_slist_append(result, cur->data);
909 for (cur = result; cur; cur = cur->next)
910 debug_print("sort after %s\n", ((MsgInfo *)cur->data)->from);
917 static gboolean procmsg_is_last_for_account(FolderItem *queue, MsgInfo *msginfo, GSList *elem)
919 gchar *file = folder_item_fetch_msg(queue, msginfo->msgnum);
920 PrefsAccount *ac = procmsg_get_account_from_file(file);
923 for (cur = elem; cur; cur = cur->next) {
924 MsgInfo *cur_msginfo = (MsgInfo *)cur->data;
925 file = folder_item_fetch_msg(queue, cur_msginfo->msgnum);
927 if (cur_msginfo != msginfo && !MSG_IS_LOCKED(cur_msginfo->flags)) {
928 if (procmsg_get_account_from_file(file) == ac) {
939 static gboolean send_queue_lock = FALSE;
941 gboolean procmsg_queue_lock(char **errstr)
943 if (send_queue_lock) {
944 /* Avoid having to translate two similar strings */
945 log_warning(LOG_PROTOCOL, "%s\n", _("Already trying to send."));
947 if (*errstr) g_free(*errstr);
948 *errstr = g_strdup_printf(_("Already trying to send."));
952 send_queue_lock = TRUE;
955 void procmsg_queue_unlock(void)
957 send_queue_lock = FALSE;
960 *\brief Send messages in queue
962 *\param queue Queue folder to process
963 *\param save_msgs Unused
965 *\return Number of messages sent, negative if an error occurred
966 * positive if no error occurred
968 gint procmsg_send_queue(FolderItem *queue, gboolean save_msgs, gchar **errstr)
970 gint sent = 0, err = 0;
972 GSList *sorted_list = NULL;
975 if (!procmsg_queue_lock(errstr)) {
976 main_window_set_menu_sensitive(mainwindow_get_mainwindow());
977 toolbar_main_set_sensitive(mainwindow_get_mainwindow());
982 queue = folder_get_default_queue();
985 procmsg_queue_unlock();
990 main_window_set_menu_sensitive(mainwindow_get_mainwindow());
991 toolbar_main_set_sensitive(mainwindow_get_mainwindow());
993 folder_item_scan(queue);
994 list = folder_item_get_msg_list(queue);
996 /* sort the list per sender account; this helps reusing the same SMTP server */
997 sorted_list = procmsg_list_sort_by_account(queue, list);
999 for (elem = sorted_list; elem != NULL; elem = elem->next) {
1003 msginfo = (MsgInfo *)(elem->data);
1004 if (!MSG_IS_LOCKED(msginfo->flags) && !MSG_IS_DELETED(msginfo->flags)) {
1005 file = folder_item_fetch_msg(queue, msginfo->msgnum);
1007 gboolean queued_removed = FALSE;
1008 if (procmsg_send_message_queue_full(file,
1009 !procmsg_is_last_for_account(queue, msginfo, elem),
1010 errstr, queue, msginfo->msgnum, &queued_removed) < 0) {
1011 g_warning("Sending queued message %d failed.\n",
1016 if (!queued_removed)
1017 folder_item_remove_msg(queue, msginfo->msgnum);
1022 /* FIXME: supposedly if only one message is locked, and queue
1023 * is being flushed, the following free says something like
1024 * "freeing msg ## in folder (nil)". */
1025 procmsg_msginfo_free(msginfo);
1028 g_slist_free(sorted_list);
1029 folder_item_scan(queue);
1031 if (queue->node && queue->node->children) {
1032 node = queue->node->children;
1033 while (node != NULL) {
1036 send_queue_lock = FALSE;
1037 res = procmsg_send_queue(FOLDER_ITEM(node->data), save_msgs, errstr);
1038 send_queue_lock = TRUE;
1046 procmsg_queue_unlock();
1048 main_window_set_menu_sensitive(mainwindow_get_mainwindow());
1049 toolbar_main_set_sensitive(mainwindow_get_mainwindow());
1051 return (err != 0 ? -err : sent);
1054 gboolean procmsg_is_sending(void)
1056 return send_queue_lock;
1060 *\brief Determine if a queue folder is empty
1062 *\param queue Queue folder to process
1064 *\return TRUE if the queue folder is empty, otherwise return FALSE
1066 gboolean procmsg_queue_is_empty(FolderItem *queue)
1069 gboolean res = FALSE;
1071 queue = folder_get_default_queue();
1072 cm_return_val_if_fail(queue != NULL, TRUE);
1074 folder_item_scan(queue);
1075 list = folder_item_get_msg_list(queue);
1076 res = (list == NULL);
1077 procmsg_msg_list_free(list);
1081 if (queue->node && queue->node->children) {
1082 node = queue->node->children;
1083 while (node != NULL) {
1085 if (!procmsg_queue_is_empty(FOLDER_ITEM(node->data)))
1094 gint procmsg_remove_special_headers(const gchar *in, const gchar *out)
1097 gchar buf[BUFFSIZE];
1099 if ((fp = g_fopen(in, "rb")) == NULL) {
1100 FILE_OP_ERROR(in, "fopen");
1103 if ((outfp = g_fopen(out, "wb")) == NULL) {
1104 FILE_OP_ERROR(out, "fopen");
1108 while (fgets(buf, sizeof(buf), fp) != NULL) {
1110 if ((!strncmp(buf, "X-Claws-End-Special-Headers: 1",
1111 strlen("X-Claws-End-Special-Headers:"))) ||
1112 (!strncmp(buf, "X-Sylpheed-End-Special-Headers: 1",
1113 strlen("X-Sylpheed-End-Special-Headers:"))))
1116 if (buf[0] == '\r' || buf[0] == '\n') break;
1117 /* from other mailers */
1118 if (!strncmp(buf, "Date: ", 6)
1119 || !strncmp(buf, "To: ", 4)
1120 || !strncmp(buf, "From: ", 6)
1121 || !strncmp(buf, "Subject: ", 9)) {
1126 while (fgets(buf, sizeof(buf), fp) != NULL)
1133 static gint procmsg_save_to_outbox(FolderItem *outbox, const gchar *file,
1137 MsgInfo *msginfo, *tmp_msginfo;
1138 MsgFlags flag = {0, 0};
1140 debug_print("saving sent message...\n");
1143 outbox = folder_get_default_outbox();
1144 cm_return_val_if_fail(outbox != NULL, -1);
1146 /* remove queueing headers */
1148 gchar tmp[MAXPATHLEN + 1];
1150 g_snprintf(tmp, sizeof(tmp), "%s%ctmpmsg.out.%08x",
1151 get_rc_dir(), G_DIR_SEPARATOR, (guint) rand());
1153 if (procmsg_remove_special_headers(file, tmp) !=0)
1156 folder_item_scan(outbox);
1157 if ((num = folder_item_add_msg(outbox, tmp, &flag, TRUE)) < 0) {
1158 g_warning("can't save message\n");
1163 folder_item_scan(outbox);
1164 if ((num = folder_item_add_msg
1165 (outbox, file, &flag, FALSE)) < 0) {
1166 g_warning("can't save message\n");
1170 msginfo = folder_item_get_msginfo(outbox, num); /* refcnt++ */
1171 tmp_msginfo = procmsg_msginfo_get_full_info(msginfo); /* refcnt++ */
1172 if (msginfo != NULL) {
1173 procmsg_msginfo_unset_flags(msginfo, ~0, 0);
1174 procmsg_msginfo_free(msginfo); /* refcnt-- */
1175 /* tmp_msginfo == msginfo */
1176 if (tmp_msginfo && msginfo->extradata &&
1177 (msginfo->extradata->dispositionnotificationto ||
1178 msginfo->extradata->returnreceiptto)) {
1179 procmsg_msginfo_set_flags(msginfo, MSG_RETRCPT_SENT, 0);
1181 procmsg_msginfo_free(tmp_msginfo); /* refcnt-- */
1188 void procmsg_print_message(MsgInfo *msginfo, const gchar *cmdline)
1190 static const gchar *def_cmd = "lpr %s";
1191 static guint id = 0;
1198 cm_return_if_fail(msginfo);
1200 if (procmime_msginfo_is_encrypted(msginfo))
1201 tmpfp = procmime_get_first_encrypted_text_content(msginfo);
1203 tmpfp = procmime_get_first_text_content(msginfo);
1204 if (tmpfp == NULL) {
1205 g_warning("Can't get text part\n");
1209 prtmp = g_strdup_printf("%s%cprinttmp.%08x",
1210 get_mime_tmp_dir(), G_DIR_SEPARATOR, id++);
1212 if ((prfp = g_fopen(prtmp, "wb")) == NULL) {
1213 FILE_OP_ERROR(prtmp, "fopen");
1219 if (msginfo->date) { r = fprintf(prfp, "Date: %s\n", msginfo->date); if (r < 0) goto fpferr; }
1220 if (msginfo->from) { r = fprintf(prfp, "From: %s\n", msginfo->from); if (r < 0) goto fpferr; }
1221 if (msginfo->to) { r = fprintf(prfp, "To: %s\n", msginfo->to); if (r < 0) goto fpferr; }
1222 if (msginfo->cc) { r = fprintf(prfp, "Cc: %s\n", msginfo->cc); if (r < 0) goto fpferr; }
1223 if (msginfo->newsgroups) {
1224 r = fprintf(prfp, "Newsgroups: %s\n", msginfo->newsgroups); if (r < 0) goto fpferr;
1226 if (msginfo->subject) { r = fprintf(prfp, "Subject: %s\n", msginfo->subject); if (r < 0) goto fpferr; }
1227 if (fputc('\n', prfp) == EOF) goto fpferr;
1229 while (fgets(buf, sizeof(buf), tmpfp) != NULL) {
1230 r = fputs(buf, prfp);
1231 if (r == EOF) goto fpferr;
1237 if (cmdline && (p = strchr(cmdline, '%')) && *(p + 1) == 's' &&
1238 !strchr(p + 2, '%'))
1239 g_snprintf(buf, sizeof(buf) - 1, cmdline, prtmp);
1242 g_warning("Print command-line is invalid: '%s'\n",
1244 g_snprintf(buf, sizeof(buf) - 1, def_cmd, prtmp);
1250 if (buf[strlen(buf) - 1] != '&')
1251 strncat(buf, "&", sizeof(buf) - strlen(buf) - 1);
1252 if (system(buf) == -1)
1253 g_warning("system(%s) failed.", buf);
1256 FILE_OP_ERROR(prtmp, "fprintf/fputc/fputs");
1262 MsgInfo *procmsg_msginfo_new_ref(MsgInfo *msginfo)
1269 MsgInfo *procmsg_msginfo_new(void)
1271 MsgInfo *newmsginfo;
1273 newmsginfo = g_new0(MsgInfo, 1);
1274 newmsginfo->refcnt = 1;
1279 static MsgInfoAvatar *procmsg_msginfoavatar_copy(MsgInfoAvatar *avatar)
1281 MsgInfoAvatar *newavatar;
1283 if (avatar == NULL) return NULL;
1285 newavatar = g_new0(MsgInfoAvatar, 1);
1286 newavatar->avatar_id = avatar->avatar_id;
1287 newavatar->avatar_src = g_strdup(avatar->avatar_src);
1292 static void procmsg_msginfoavatar_free(MsgInfoAvatar *avatar)
1294 if (avatar != NULL) {
1295 if (avatar->avatar_src != NULL)
1296 g_free(avatar->avatar_src);
1301 MsgInfo *procmsg_msginfo_copy(MsgInfo *msginfo)
1303 MsgInfo *newmsginfo;
1306 if (msginfo == NULL) return NULL;
1308 newmsginfo = g_new0(MsgInfo, 1);
1310 newmsginfo->refcnt = 1;
1312 #define MEMBCOPY(mmb) newmsginfo->mmb = msginfo->mmb
1313 #define MEMBDUP(mmb) newmsginfo->mmb = msginfo->mmb ? \
1314 g_strdup(msginfo->mmb) : NULL
1329 MEMBDUP(newsgroups);
1336 MEMBCOPY(to_folder);
1338 if (msginfo->extradata) {
1339 newmsginfo->extradata = g_new0(MsgInfoExtraData, 1);
1340 if (msginfo->extradata->avatars) {
1341 newmsginfo->extradata->avatars = slist_copy_deep(msginfo->extradata->avatars,
1342 (GCopyFunc) procmsg_msginfoavatar_copy);
1344 MEMBDUP(extradata->dispositionnotificationto);
1345 MEMBDUP(extradata->returnreceiptto);
1346 MEMBDUP(extradata->partial_recv);
1347 MEMBDUP(extradata->account_server);
1348 MEMBDUP(extradata->account_login);
1349 MEMBDUP(extradata->list_post);
1350 MEMBDUP(extradata->list_subscribe);
1351 MEMBDUP(extradata->list_unsubscribe);
1352 MEMBDUP(extradata->list_help);
1353 MEMBDUP(extradata->list_archive);
1354 MEMBDUP(extradata->list_owner);
1357 refs = msginfo->references;
1358 for (refs = msginfo->references; refs != NULL; refs = refs->next) {
1359 newmsginfo->references = g_slist_prepend
1360 (newmsginfo->references, g_strdup(refs->data));
1362 newmsginfo->references = g_slist_reverse(newmsginfo->references);
1365 MEMBDUP(plaintext_file);
1370 MsgInfo *procmsg_msginfo_get_full_info_from_file(MsgInfo *msginfo, const gchar *file)
1372 MsgInfo *full_msginfo;
1374 if (msginfo == NULL) return NULL;
1376 if (!file || !is_file_exist(file)) {
1377 g_warning("procmsg_msginfo_get_full_info_from_file(): can't get message file.\n");
1381 full_msginfo = procheader_parse_file(file, msginfo->flags, TRUE, FALSE);
1382 if (!full_msginfo) return NULL;
1384 msginfo->total_size = full_msginfo->total_size;
1385 msginfo->planned_download = full_msginfo->planned_download;
1387 if (full_msginfo->extradata) {
1388 if (!msginfo->extradata)
1389 msginfo->extradata = g_new0(MsgInfoExtraData, 1);
1390 if (!msginfo->extradata->list_post)
1391 msginfo->extradata->list_post = g_strdup(full_msginfo->extradata->list_post);
1392 if (!msginfo->extradata->list_subscribe)
1393 msginfo->extradata->list_subscribe = g_strdup(full_msginfo->extradata->list_subscribe);
1394 if (!msginfo->extradata->list_unsubscribe)
1395 msginfo->extradata->list_unsubscribe = g_strdup(full_msginfo->extradata->list_unsubscribe);
1396 if (!msginfo->extradata->list_help)
1397 msginfo->extradata->list_help = g_strdup(full_msginfo->extradata->list_help);
1398 if (!msginfo->extradata->list_archive)
1399 msginfo->extradata->list_archive= g_strdup(full_msginfo->extradata->list_archive);
1400 if (!msginfo->extradata->list_owner)
1401 msginfo->extradata->list_owner = g_strdup(full_msginfo->extradata->list_owner);
1402 if (!msginfo->extradata->avatars)
1403 msginfo->extradata->avatars = slist_copy_deep(full_msginfo->extradata->avatars,
1404 (GCopyFunc) procmsg_msginfoavatar_copy);
1405 if (!msginfo->extradata->dispositionnotificationto)
1406 msginfo->extradata->dispositionnotificationto =
1407 g_strdup(full_msginfo->extradata->dispositionnotificationto);
1408 if (!msginfo->extradata->returnreceiptto)
1409 msginfo->extradata->returnreceiptto = g_strdup
1410 (full_msginfo->extradata->returnreceiptto);
1411 if (!msginfo->extradata->partial_recv && full_msginfo->extradata->partial_recv)
1412 msginfo->extradata->partial_recv = g_strdup
1413 (full_msginfo->extradata->partial_recv);
1414 if (!msginfo->extradata->account_server && full_msginfo->extradata->account_server)
1415 msginfo->extradata->account_server = g_strdup
1416 (full_msginfo->extradata->account_server);
1417 if (!msginfo->extradata->account_login && full_msginfo->extradata->account_login)
1418 msginfo->extradata->account_login = g_strdup
1419 (full_msginfo->extradata->account_login);
1421 procmsg_msginfo_free(full_msginfo);
1423 return procmsg_msginfo_new_ref(msginfo);
1426 MsgInfo *procmsg_msginfo_get_full_info(MsgInfo *msginfo)
1428 MsgInfo *full_msginfo;
1431 if (msginfo == NULL) return NULL;
1433 file = procmsg_get_message_file_path(msginfo);
1434 if (!file || !is_file_exist(file)) {
1436 file = procmsg_get_message_file(msginfo);
1438 if (!file || !is_file_exist(file)) {
1439 g_warning("procmsg_msginfo_get_full_info(): can't get message file.\n");
1443 full_msginfo = procmsg_msginfo_get_full_info_from_file(msginfo, file);
1445 return full_msginfo;
1448 void procmsg_msginfo_free(MsgInfo *msginfo)
1450 if (msginfo == NULL) return;
1453 if (msginfo->refcnt > 0)
1456 if (msginfo->to_folder) {
1457 msginfo->to_folder->op_count--;
1458 folder_item_update(msginfo->to_folder, F_ITEM_UPDATE_MSGCNT);
1461 g_free(msginfo->fromspace);
1463 g_free(msginfo->fromname);
1465 g_free(msginfo->date);
1466 g_free(msginfo->from);
1467 g_free(msginfo->to);
1468 g_free(msginfo->cc);
1469 g_free(msginfo->newsgroups);
1470 g_free(msginfo->subject);
1471 g_free(msginfo->msgid);
1472 g_free(msginfo->inreplyto);
1473 g_free(msginfo->xref);
1475 if (msginfo->extradata) {
1476 if (msginfo->extradata->avatars) {
1477 g_slist_foreach(msginfo->extradata->avatars,
1478 (GFunc)procmsg_msginfoavatar_free,
1480 g_slist_free(msginfo->extradata->avatars);
1482 g_free(msginfo->extradata->returnreceiptto);
1483 g_free(msginfo->extradata->dispositionnotificationto);
1484 g_free(msginfo->extradata->list_post);
1485 g_free(msginfo->extradata->list_subscribe);
1486 g_free(msginfo->extradata->list_unsubscribe);
1487 g_free(msginfo->extradata->list_help);
1488 g_free(msginfo->extradata->list_archive);
1489 g_free(msginfo->extradata->list_owner);
1490 g_free(msginfo->extradata->partial_recv);
1491 g_free(msginfo->extradata->account_server);
1492 g_free(msginfo->extradata->account_login);
1493 g_free(msginfo->extradata);
1495 slist_free_strings_full(msginfo->references);
1496 g_slist_free(msginfo->tags);
1498 g_free(msginfo->plaintext_file);
1503 guint procmsg_msginfo_memusage(MsgInfo *msginfo)
1508 memusage += sizeof(MsgInfo);
1509 if (msginfo->fromname)
1510 memusage += strlen(msginfo->fromname);
1512 memusage += strlen(msginfo->date);
1514 memusage += strlen(msginfo->from);
1516 memusage += strlen(msginfo->to);
1518 memusage += strlen(msginfo->cc);
1519 if (msginfo->newsgroups)
1520 memusage += strlen(msginfo->newsgroups);
1521 if (msginfo->subject)
1522 memusage += strlen(msginfo->subject);
1524 memusage += strlen(msginfo->msgid);
1525 if (msginfo->inreplyto)
1526 memusage += strlen(msginfo->inreplyto);
1528 for (tmp = msginfo->references; tmp; tmp=tmp->next) {
1529 gchar *r = (gchar *)tmp->data;
1530 memusage += r?strlen(r):0 + sizeof(GSList);
1532 if (msginfo->fromspace)
1533 memusage += strlen(msginfo->fromspace);
1535 for (tmp = msginfo->tags; tmp; tmp=tmp->next) {
1536 memusage += sizeof(GSList);
1538 if (msginfo->extradata) {
1539 memusage += sizeof(MsgInfoExtraData);
1540 if (msginfo->extradata->avatars) {
1541 for (tmp = msginfo->extradata->avatars; tmp; tmp = tmp->next) {
1542 MsgInfoAvatar *avt = (MsgInfoAvatar *)tmp->data;
1543 memusage += (avt->avatar_src)? strlen(avt->avatar_src): 0;
1544 memusage += sizeof(MsgInfoAvatar) + sizeof(GSList);
1547 if (msginfo->extradata->dispositionnotificationto)
1548 memusage += strlen(msginfo->extradata->dispositionnotificationto);
1549 if (msginfo->extradata->returnreceiptto)
1550 memusage += strlen(msginfo->extradata->returnreceiptto);
1552 if (msginfo->extradata->partial_recv)
1553 memusage += strlen(msginfo->extradata->partial_recv);
1554 if (msginfo->extradata->account_server)
1555 memusage += strlen(msginfo->extradata->account_server);
1556 if (msginfo->extradata->account_login)
1557 memusage += strlen(msginfo->extradata->account_login);
1559 if (msginfo->extradata->list_post)
1560 memusage += strlen(msginfo->extradata->list_post);
1561 if (msginfo->extradata->list_subscribe)
1562 memusage += strlen(msginfo->extradata->list_subscribe);
1563 if (msginfo->extradata->list_unsubscribe)
1564 memusage += strlen(msginfo->extradata->list_unsubscribe);
1565 if (msginfo->extradata->list_help)
1566 memusage += strlen(msginfo->extradata->list_help);
1567 if (msginfo->extradata->list_archive)
1568 memusage += strlen(msginfo->extradata->list_archive);
1569 if (msginfo->extradata->list_owner)
1570 memusage += strlen(msginfo->extradata->list_owner);
1575 static gint procmsg_send_message_queue_full(const gchar *file, gboolean keep_session, gchar **errstr,
1576 FolderItem *queue, gint msgnum, gboolean *queued_removed)
1578 static HeaderEntry qentry[] = {{"S:", NULL, FALSE},
1579 {"SSV:", NULL, FALSE},
1580 {"R:", NULL, FALSE},
1581 {"NG:", NULL, FALSE},
1582 {"MAID:", NULL, FALSE},
1583 {"NAID:", NULL, FALSE},
1584 {"SCF:", NULL, FALSE},
1585 {"RMID:", NULL, FALSE},
1586 {"FMID:", NULL, FALSE},
1587 {"X-Claws-Privacy-System:", NULL, FALSE},
1588 {"X-Claws-Encrypt:", NULL, FALSE},
1589 {"X-Claws-Encrypt-Data:", NULL, FALSE},
1590 {"X-Claws-End-Special-Headers:", NULL, FALSE},
1591 {"X-Sylpheed-Privacy-System:", NULL, FALSE},
1592 {"X-Sylpheed-Encrypt:", NULL, FALSE},
1593 {"X-Sylpheed-Encrypt-Data:", NULL, FALSE},
1594 {"X-Sylpheed-End-Special-Headers:", NULL, FALSE},
1595 {NULL, NULL, FALSE}};
1598 gint mailval = 0, newsval = 0;
1600 gchar *smtpserver = NULL;
1601 GSList *to_list = NULL;
1602 GSList *newsgroup_list = NULL;
1603 gchar *savecopyfolder = NULL;
1604 gchar *replymessageid = NULL;
1605 gchar *fwdmessageid = NULL;
1606 gchar *privacy_system = NULL;
1607 gboolean encrypt = FALSE;
1608 gchar *encrypt_data = NULL;
1609 gchar buf[BUFFSIZE];
1611 PrefsAccount *mailac = NULL, *newsac = NULL;
1612 gboolean save_clear_text = TRUE;
1613 gchar *tmp_enc_file = NULL;
1615 cm_return_val_if_fail(file != NULL, -1);
1617 if ((fp = g_fopen(file, "rb")) == NULL) {
1618 FILE_OP_ERROR(file, "fopen");
1620 if (*errstr) g_free(*errstr);
1621 *errstr = g_strdup_printf(_("Couldn't open file %s."), file);
1626 while ((hnum = procheader_get_one_field(buf, sizeof(buf), fp, qentry))
1628 gchar *p = buf + strlen(qentry[hnum].name);
1636 if (smtpserver == NULL)
1637 smtpserver = g_strdup(p);
1640 to_list = address_list_append(to_list, p);
1643 newsgroup_list = newsgroup_list_append(newsgroup_list, p);
1645 case Q_MAIL_ACCOUNT_ID:
1646 mailac = account_find_from_id(atoi(p));
1648 case Q_NEWS_ACCOUNT_ID:
1649 newsac = account_find_from_id(atoi(p));
1651 case Q_SAVE_COPY_FOLDER:
1652 if (savecopyfolder == NULL)
1653 savecopyfolder = g_strdup(p);
1655 case Q_REPLY_MESSAGE_ID:
1656 if (replymessageid == NULL)
1657 replymessageid = g_strdup(p);
1659 case Q_FWD_MESSAGE_ID:
1660 if (fwdmessageid == NULL)
1661 fwdmessageid = g_strdup(p);
1663 case Q_PRIVACY_SYSTEM:
1664 case Q_PRIVACY_SYSTEM_OLD:
1665 if (privacy_system == NULL)
1666 privacy_system = g_strdup(p);
1673 case Q_ENCRYPT_DATA:
1674 case Q_ENCRYPT_DATA_OLD:
1675 if (encrypt_data == NULL)
1676 encrypt_data = g_strdup(p);
1679 case Q_CLAWS_HDRS_OLD:
1680 /* end of special headers reached */
1681 goto send_mail; /* can't "break;break;" */
1685 filepos = ftell(fp);
1690 if (mailac && mailac->save_encrypted_as_clear_text
1691 && !mailac->encrypt_to_self)
1692 save_clear_text = TRUE;
1694 save_clear_text = FALSE;
1699 mimeinfo = procmime_scan_queue_file(file);
1700 if (!privacy_encrypt(privacy_system, mimeinfo, encrypt_data)
1701 || (fp = my_tmpfile()) == NULL
1702 || procmime_write_mimeinfo(mimeinfo, fp) < 0) {
1705 procmime_mimeinfo_free_all(mimeinfo);
1708 slist_free_strings_full(to_list);
1709 slist_free_strings_full(newsgroup_list);
1710 g_free(savecopyfolder);
1711 g_free(replymessageid);
1712 g_free(fwdmessageid);
1713 g_free(privacy_system);
1714 g_free(encrypt_data);
1716 if (*errstr) g_free(*errstr);
1717 *errstr = g_strdup_printf(_("Couldn't encrypt the email: %s"),
1718 privacy_get_error());
1724 if (!save_clear_text) {
1725 gchar *content = NULL;
1726 FILE *tmpfp = get_tmpfile_in_dir(get_mime_tmp_dir(), &tmp_enc_file);
1730 content = file_read_stream_to_str(fp);
1733 str_write_to_file(content, tmp_enc_file);
1736 g_warning("couldn't get tempfile\n");
1740 procmime_mimeinfo_free_all(mimeinfo);
1746 debug_print("Sending message by mail\n");
1749 if (*errstr) g_free(*errstr);
1750 *errstr = g_strdup_printf(_("Queued message header is broken."));
1753 } else if (mailac && mailac->use_mail_command &&
1754 mailac->mail_command && (* mailac->mail_command)) {
1755 mailval = send_message_local(mailac->mail_command, fp);
1758 mailac = account_find_from_smtp_server(from, smtpserver);
1760 g_warning("Account not found. "
1761 "Using current account...\n");
1762 mailac = cur_account;
1767 mailval = send_message_smtp_full(mailac, to_list, fp, keep_session);
1768 if (mailval == -1 && errstr) {
1769 if (*errstr) g_free(*errstr);
1770 *errstr = g_strdup_printf(_("An error happened during SMTP session."));
1773 PrefsAccount tmp_ac;
1775 g_warning("Account not found.\n");
1777 memset(&tmp_ac, 0, sizeof(PrefsAccount));
1778 tmp_ac.address = from;
1779 tmp_ac.smtp_server = smtpserver;
1780 tmp_ac.smtpport = SMTP_PORT;
1781 mailval = send_message_smtp(&tmp_ac, to_list, fp);
1782 if (mailval == -1 && errstr) {
1783 if (*errstr) g_free(*errstr);
1784 *errstr = g_strdup_printf(_("No specific account has been found to "
1785 "send, and an error happened during SMTP session."));
1789 } else if (!to_list && !newsgroup_list) {
1791 if (*errstr) g_free(*errstr);
1792 *errstr = g_strdup(_("Couldn't determine sending information. "
1793 "Maybe the email hasn't been generated by Claws Mail."));
1798 fseek(fp, filepos, SEEK_SET);
1799 if (newsgroup_list && newsac && (mailval == 0)) {
1804 /* write to temporary file */
1805 tmp = g_strdup_printf("%s%cnntp%p", get_tmp_dir(),
1806 G_DIR_SEPARATOR, file);
1807 if ((tmpfp = g_fopen(tmp, "wb")) == NULL) {
1808 FILE_OP_ERROR(tmp, "fopen");
1810 alertpanel_error(_("Couldn't create temporary file for news sending."));
1812 if (change_file_mode_rw(tmpfp, tmp) < 0) {
1813 FILE_OP_ERROR(tmp, "chmod");
1814 g_warning("can't change file mode\n");
1817 while ((newsval == 0) && fgets(buf, sizeof(buf), fp) != NULL) {
1818 if (fputs(buf, tmpfp) == EOF) {
1819 FILE_OP_ERROR(tmp, "fputs");
1822 if (*errstr) g_free(*errstr);
1823 *errstr = g_strdup_printf(_("Error when writing temporary file for news sending."));
1830 debug_print("Sending message by news\n");
1832 folder = FOLDER(newsac->folder);
1834 newsval = news_post(folder, tmp);
1835 if (newsval < 0 && errstr) {
1836 if (*errstr) g_free(*errstr);
1837 *errstr = g_strdup_printf(_("Error occurred while posting the message to %s."),
1838 newsac->nntp_server);
1848 /* update session statistics */
1849 if (mailval == 0 && newsval == 0) {
1850 /* update session stats */
1852 session_stats.replied++;
1853 else if (fwdmessageid)
1854 session_stats.forwarded++;
1856 session_stats.sent++;
1859 /* save message to outbox */
1860 if (mailval == 0 && newsval == 0 && savecopyfolder) {
1863 debug_print("saving sent message...\n");
1865 outbox = folder_find_item_from_identifier(savecopyfolder);
1867 outbox = folder_get_default_outbox();
1869 if (save_clear_text || tmp_enc_file == NULL) {
1870 gboolean saved = FALSE;
1871 *queued_removed = FALSE;
1872 if (queue && msgnum > 0) {
1873 MsgInfo *queued_mail = folder_item_get_msginfo(queue, msgnum);
1874 if (folder_item_move_msg(outbox, queued_mail) >= 0) {
1875 debug_print("moved queued mail %d to sent folder\n", msgnum);
1877 *queued_removed = TRUE;
1878 } else if (folder_item_copy_msg(outbox, queued_mail) >= 0) {
1879 debug_print("copied queued mail %d to sent folder\n", msgnum);
1882 procmsg_msginfo_free(queued_mail);
1885 debug_print("resaving clear text queued mail to sent folder\n");
1886 procmsg_save_to_outbox(outbox, file, TRUE);
1889 debug_print("saving encrpyted queued mail to sent folder\n");
1890 procmsg_save_to_outbox(outbox, tmp_enc_file, FALSE);
1894 if (tmp_enc_file != NULL) {
1895 claws_unlink(tmp_enc_file);
1897 tmp_enc_file = NULL;
1900 if (replymessageid != NULL || fwdmessageid != NULL) {
1904 if (replymessageid != NULL)
1905 tokens = g_strsplit(replymessageid, "\t", 0);
1907 tokens = g_strsplit(fwdmessageid, "\t", 0);
1908 item = folder_find_item_from_identifier(tokens[0]);
1910 /* check if queued message has valid folder and message id */
1911 if (item != NULL && tokens[2] != NULL) {
1914 msginfo = folder_item_get_msginfo(item, atoi(tokens[1]));
1916 /* check if referring message exists and has a message id */
1917 if ((msginfo != NULL) &&
1918 (msginfo->msgid != NULL) &&
1919 (strcmp(msginfo->msgid, tokens[2]) != 0)) {
1920 procmsg_msginfo_free(msginfo);
1924 if (msginfo == NULL) {
1925 msginfo = folder_item_get_msginfo_by_msgid(item, tokens[2]);
1928 if (msginfo != NULL) {
1929 if (replymessageid != NULL) {
1930 MsgPermFlags to_unset = 0;
1932 if (prefs_common.mark_as_read_on_new_window)
1933 to_unset = (MSG_NEW|MSG_UNREAD);
1935 procmsg_msginfo_unset_flags(msginfo, to_unset, 0);
1936 procmsg_msginfo_set_flags(msginfo, MSG_REPLIED, 0);
1938 procmsg_msginfo_set_flags(msginfo, MSG_FORWARDED, 0);
1940 procmsg_msginfo_free(msginfo);
1948 slist_free_strings_full(to_list);
1949 slist_free_strings_full(newsgroup_list);
1950 g_free(savecopyfolder);
1951 g_free(replymessageid);
1952 g_free(fwdmessageid);
1953 g_free(privacy_system);
1954 g_free(encrypt_data);
1956 return (newsval != 0 ? newsval : mailval);
1959 gint procmsg_send_message_queue(const gchar *file, gchar **errstr, FolderItem *queue, gint msgnum, gboolean *queued_removed)
1961 gint result = procmsg_send_message_queue_full(file, FALSE, errstr, queue, msgnum, queued_removed);
1962 main_window_set_menu_sensitive(mainwindow_get_mainwindow());
1963 toolbar_main_set_sensitive(mainwindow_get_mainwindow());
1967 gint procmsg_send_message_queue_with_lock(const gchar *file, gchar **errstr, FolderItem *queue, gint msgnum, gboolean *queued_removed)
1970 if (procmsg_queue_lock(errstr)) {
1971 val = procmsg_send_message_queue(file, errstr, queue, msgnum, queued_removed);
1972 procmsg_queue_unlock();
1978 static void update_folder_msg_counts(FolderItem *item, MsgInfo *msginfo, MsgPermFlags old_flags)
1980 MsgPermFlags new_flags = msginfo->flags.perm_flags;
1983 if (!(old_flags & MSG_NEW) && (new_flags & MSG_NEW)) {
1987 if ((old_flags & MSG_NEW) && !(new_flags & MSG_NEW)) {
1992 if (!(old_flags & MSG_UNREAD) && (new_flags & MSG_UNREAD)) {
1993 item->unread_msgs++;
1994 if (procmsg_msg_has_marked_parent(msginfo))
1995 item->unreadmarked_msgs++;
1998 if ((old_flags & MSG_UNREAD) && !(new_flags & MSG_UNREAD)) {
1999 item->unread_msgs--;
2000 if (procmsg_msg_has_marked_parent(msginfo))
2001 item->unreadmarked_msgs--;
2005 if (!(old_flags & MSG_MARKED) && (new_flags & MSG_MARKED)) {
2006 procmsg_update_unread_children(msginfo, TRUE);
2007 item->marked_msgs++;
2010 if ((old_flags & MSG_MARKED) && !(new_flags & MSG_MARKED)) {
2011 procmsg_update_unread_children(msginfo, FALSE);
2012 item->marked_msgs--;
2015 if (!(old_flags & MSG_REPLIED) && (new_flags & MSG_REPLIED)) {
2016 item->replied_msgs++;
2019 if ((old_flags & MSG_REPLIED) && !(new_flags & MSG_REPLIED)) {
2020 item->replied_msgs--;
2023 if (!(old_flags & MSG_FORWARDED) && (new_flags & MSG_FORWARDED)) {
2024 item->forwarded_msgs++;
2027 if ((old_flags & MSG_FORWARDED) && !(new_flags & MSG_FORWARDED)) {
2028 item->forwarded_msgs--;
2031 if (!(old_flags & MSG_LOCKED) && (new_flags & MSG_LOCKED)) {
2032 item->locked_msgs++;
2035 if ((old_flags & MSG_LOCKED) && !(new_flags & MSG_LOCKED)) {
2036 item->locked_msgs--;
2039 if ((old_flags & MSG_IGNORE_THREAD) && !(new_flags & MSG_IGNORE_THREAD)) {
2040 item->ignored_msgs--;
2043 if (!(old_flags & MSG_IGNORE_THREAD) && (new_flags & MSG_IGNORE_THREAD)) {
2044 item->ignored_msgs++;
2047 if ((old_flags & MSG_WATCH_THREAD) && !(new_flags & MSG_WATCH_THREAD)) {
2048 item->watched_msgs--;
2051 if (!(old_flags & MSG_WATCH_THREAD) && (new_flags & MSG_WATCH_THREAD)) {
2052 item->watched_msgs++;
2056 void procmsg_msginfo_set_flags(MsgInfo *msginfo, MsgPermFlags perm_flags, MsgTmpFlags tmp_flags)
2059 MsgInfoUpdate msginfo_update;
2060 MsgPermFlags perm_flags_new, perm_flags_old;
2061 MsgTmpFlags tmp_flags_old;
2063 cm_return_if_fail(msginfo != NULL);
2064 item = msginfo->folder;
2065 cm_return_if_fail(item != NULL);
2067 debug_print("Setting flags for message %d in folder %s\n", msginfo->msgnum, item->path);
2069 /* Perm Flags handling */
2070 perm_flags_old = msginfo->flags.perm_flags;
2071 perm_flags_new = msginfo->flags.perm_flags | perm_flags;
2072 if ((perm_flags & MSG_IGNORE_THREAD) || (perm_flags_old & MSG_IGNORE_THREAD)) {
2073 perm_flags_new &= ~(MSG_NEW | MSG_UNREAD);
2075 if ((perm_flags & MSG_WATCH_THREAD) || (perm_flags_old & MSG_WATCH_THREAD)) {
2076 perm_flags_new &= ~(MSG_IGNORE_THREAD);
2079 if (perm_flags_old != perm_flags_new) {
2080 folder_item_change_msg_flags(msginfo->folder, msginfo, perm_flags_new);
2082 update_folder_msg_counts(item, msginfo, perm_flags_old);
2083 summary_update_unread(mainwindow_get_mainwindow()->summaryview, NULL);
2086 /* Tmp flags handling */
2087 tmp_flags_old = msginfo->flags.tmp_flags;
2088 msginfo->flags.tmp_flags |= tmp_flags;
2090 /* update notification */
2091 if ((perm_flags_old != perm_flags_new) || (tmp_flags_old != msginfo->flags.tmp_flags)) {
2092 msginfo_update.msginfo = msginfo;
2093 msginfo_update.flags = MSGINFO_UPDATE_FLAGS;
2094 hooks_invoke(MSGINFO_UPDATE_HOOKLIST, &msginfo_update);
2095 folder_item_update(msginfo->folder, F_ITEM_UPDATE_MSGCNT);
2099 void procmsg_msginfo_unset_flags(MsgInfo *msginfo, MsgPermFlags perm_flags, MsgTmpFlags tmp_flags)
2102 MsgInfoUpdate msginfo_update;
2103 MsgPermFlags perm_flags_new, perm_flags_old;
2104 MsgTmpFlags tmp_flags_old;
2106 cm_return_if_fail(msginfo != NULL);
2107 item = msginfo->folder;
2108 cm_return_if_fail(item != NULL);
2110 debug_print("Unsetting flags for message %d in folder %s\n", msginfo->msgnum, item->path);
2112 /* Perm Flags handling */
2113 perm_flags_old = msginfo->flags.perm_flags;
2114 perm_flags_new = msginfo->flags.perm_flags & ~perm_flags;
2116 if (perm_flags_old != perm_flags_new) {
2117 folder_item_change_msg_flags(msginfo->folder, msginfo, perm_flags_new);
2119 update_folder_msg_counts(item, msginfo, perm_flags_old);
2122 /* Tmp flags hanlding */
2123 tmp_flags_old = msginfo->flags.tmp_flags;
2124 msginfo->flags.tmp_flags &= ~tmp_flags;
2126 /* update notification */
2127 if ((perm_flags_old != perm_flags_new) || (tmp_flags_old != msginfo->flags.tmp_flags)) {
2128 msginfo_update.msginfo = msginfo;
2129 msginfo_update.flags = MSGINFO_UPDATE_FLAGS;
2130 hooks_invoke(MSGINFO_UPDATE_HOOKLIST, &msginfo_update);
2131 folder_item_update(msginfo->folder, F_ITEM_UPDATE_MSGCNT);
2135 void procmsg_msginfo_change_flags(MsgInfo *msginfo,
2136 MsgPermFlags add_perm_flags, MsgTmpFlags add_tmp_flags,
2137 MsgPermFlags rem_perm_flags, MsgTmpFlags rem_tmp_flags)
2140 MsgInfoUpdate msginfo_update;
2141 MsgPermFlags perm_flags_new, perm_flags_old;
2142 MsgTmpFlags tmp_flags_old;
2144 cm_return_if_fail(msginfo != NULL);
2145 item = msginfo->folder;
2146 cm_return_if_fail(item != NULL);
2148 debug_print("Changing flags for message %d in folder %s\n", msginfo->msgnum, item->path);
2150 /* Perm Flags handling */
2151 perm_flags_old = msginfo->flags.perm_flags;
2152 perm_flags_new = (msginfo->flags.perm_flags & ~rem_perm_flags) | add_perm_flags;
2153 if ((add_perm_flags & MSG_IGNORE_THREAD) || (perm_flags_old & MSG_IGNORE_THREAD)) {
2154 perm_flags_new &= ~(MSG_NEW | MSG_UNREAD);
2156 if ((add_perm_flags & MSG_WATCH_THREAD) || (perm_flags_old & MSG_WATCH_THREAD)) {
2157 perm_flags_new &= ~(MSG_IGNORE_THREAD);
2160 if (perm_flags_old != perm_flags_new) {
2161 folder_item_change_msg_flags(msginfo->folder, msginfo, perm_flags_new);
2163 update_folder_msg_counts(item, msginfo, perm_flags_old);
2167 /* Tmp flags handling */
2168 tmp_flags_old = msginfo->flags.tmp_flags;
2169 msginfo->flags.tmp_flags &= ~rem_tmp_flags;
2170 msginfo->flags.tmp_flags |= add_tmp_flags;
2172 /* update notification */
2173 if ((perm_flags_old != perm_flags_new) || (tmp_flags_old != msginfo->flags.tmp_flags)) {
2174 msginfo_update.msginfo = msginfo;
2175 msginfo_update.flags = MSGINFO_UPDATE_FLAGS;
2176 hooks_invoke(MSGINFO_UPDATE_HOOKLIST, &msginfo_update);
2177 folder_item_update(msginfo->folder, F_ITEM_UPDATE_MSGCNT);
2182 *\brief check for flags (e.g. mark) in prior msgs of current thread
2184 *\param info Current message
2185 *\param perm_flags Flags to be checked
2186 *\param parentmsgs Hash of prior msgs to avoid loops
2188 *\return gboolean TRUE if perm_flags are found
2190 static gboolean procmsg_msg_has_flagged_parent_real(MsgInfo *info,
2191 MsgPermFlags perm_flags, GHashTable *parentmsgs)
2195 cm_return_val_if_fail(info != NULL, FALSE);
2197 if (info != NULL && info->folder != NULL && info->inreplyto != NULL) {
2198 tmp = folder_item_get_msginfo_by_msgid(info->folder,
2200 if (tmp && (tmp->flags.perm_flags & perm_flags)) {
2201 procmsg_msginfo_free(tmp);
2203 } else if (tmp != NULL) {
2206 if (g_hash_table_lookup(parentmsgs, info)) {
2207 debug_print("loop detected: %d\n",
2211 g_hash_table_insert(parentmsgs, info, "1");
2212 result = procmsg_msg_has_flagged_parent_real(
2213 tmp, perm_flags, parentmsgs);
2215 procmsg_msginfo_free(tmp);
2225 *\brief Callback for cleaning up hash of parentmsgs
2227 static gboolean parentmsgs_hash_remove(gpointer key,
2235 *\brief Set up list of parentmsgs
2236 * See procmsg_msg_has_flagged_parent_real()
2238 gboolean procmsg_msg_has_flagged_parent(MsgInfo *info, MsgPermFlags perm_flags)
2241 static GHashTable *parentmsgs = NULL;
2243 if (parentmsgs == NULL)
2244 parentmsgs = g_hash_table_new(NULL, NULL);
2246 result = procmsg_msg_has_flagged_parent_real(info, perm_flags, parentmsgs);
2247 g_hash_table_foreach_remove(parentmsgs, parentmsgs_hash_remove, NULL);
2253 *\brief Check if msgs prior in thread are marked
2254 * See procmsg_msg_has_flagged_parent_real()
2256 gboolean procmsg_msg_has_marked_parent(MsgInfo *info)
2258 return procmsg_msg_has_flagged_parent(info, MSG_MARKED);
2262 static GSList *procmsg_find_children_func(MsgInfo *info,
2263 GSList *children, GSList *all)
2267 cm_return_val_if_fail(info!=NULL, children);
2268 if (info->msgid == NULL)
2271 for (cur = all; cur != NULL; cur = g_slist_next(cur)) {
2272 MsgInfo *tmp = (MsgInfo *)cur->data;
2273 if (tmp->inreplyto && !strcmp(tmp->inreplyto, info->msgid)) {
2274 /* Check if message is already in the list */
2275 if ((children == NULL) ||
2276 (g_slist_index(children, tmp) == -1)) {
2277 children = g_slist_prepend(children,
2278 procmsg_msginfo_new_ref(tmp));
2279 children = procmsg_find_children_func(tmp,
2288 static GSList *procmsg_find_children (MsgInfo *info)
2293 cm_return_val_if_fail(info!=NULL, NULL);
2294 all = folder_item_get_msg_list(info->folder);
2295 children = procmsg_find_children_func(info, NULL, all);
2296 if (children != NULL) {
2297 for (cur = all; cur != NULL; cur = g_slist_next(cur)) {
2298 /* this will not free the used pointers
2299 created with procmsg_msginfo_new_ref */
2300 procmsg_msginfo_free((MsgInfo *)cur->data);
2308 static void procmsg_update_unread_children(MsgInfo *info, gboolean newly_marked)
2310 GSList *children = procmsg_find_children(info);
2312 for (cur = children; cur != NULL; cur = g_slist_next(cur)) {
2313 MsgInfo *tmp = (MsgInfo *)cur->data;
2314 if(MSG_IS_UNREAD(tmp->flags) && !MSG_IS_IGNORE_THREAD(tmp->flags)) {
2316 info->folder->unreadmarked_msgs++;
2318 info->folder->unreadmarked_msgs--;
2319 folder_item_update(info->folder, F_ITEM_UPDATE_MSGCNT);
2321 procmsg_msginfo_free(tmp);
2323 g_slist_free(children);
2327 * Set the destination folder for a copy or move operation
2329 * \param msginfo The message which's destination folder is changed
2330 * \param to_folder The destination folder for the operation
2332 void procmsg_msginfo_set_to_folder(MsgInfo *msginfo, FolderItem *to_folder)
2334 if(msginfo->to_folder != NULL) {
2335 msginfo->to_folder->op_count--;
2336 folder_item_update(msginfo->to_folder, F_ITEM_UPDATE_MSGCNT);
2338 msginfo->to_folder = to_folder;
2339 if(to_folder != NULL) {
2340 to_folder->op_count++;
2341 folder_item_update(msginfo->to_folder, F_ITEM_UPDATE_MSGCNT);
2346 * Apply filtering actions to the msginfo
2348 * \param msginfo The MsgInfo describing the message that should be filtered
2349 * \return TRUE if the message was moved and MsgInfo is now invalid,
2352 static gboolean procmsg_msginfo_filter(MsgInfo *msginfo, PrefsAccount* ac_prefs)
2354 MailFilteringData mail_filtering_data;
2356 mail_filtering_data.msginfo = msginfo;
2357 mail_filtering_data.msglist = NULL;
2358 mail_filtering_data.filtered = NULL;
2359 mail_filtering_data.unfiltered = NULL;
2360 mail_filtering_data.account = ac_prefs;
2362 if (!ac_prefs || ac_prefs->filterhook_on_recv)
2363 if (hooks_invoke(MAIL_FILTERING_HOOKLIST, &mail_filtering_data))
2366 /* filter if enabled in prefs or move to inbox if not */
2367 if((filtering_rules != NULL) &&
2368 filter_message_by_msginfo(filtering_rules, msginfo, ac_prefs,
2369 FILTERING_INCORPORATION, NULL)) {
2376 void procmsg_msglist_filter(GSList *list, PrefsAccount *ac,
2377 GSList **filtered, GSList **unfiltered,
2380 GSList *cur, *to_do = NULL;
2381 gint total = 0, curnum = 0;
2382 MailFilteringData mail_filtering_data;
2384 cm_return_if_fail(filtered != NULL);
2385 cm_return_if_fail(unfiltered != NULL);
2393 total = g_slist_length(list);
2397 *unfiltered = g_slist_copy(list);
2401 statusbar_print_all(_("Filtering messages...\n"));
2403 mail_filtering_data.msginfo = NULL;
2404 mail_filtering_data.msglist = list;
2405 mail_filtering_data.filtered = NULL;
2406 mail_filtering_data.unfiltered = NULL;
2407 mail_filtering_data.account = ac;
2409 if (!ac || ac->filterhook_on_recv)
2410 hooks_invoke(MAIL_LISTFILTERING_HOOKLIST, &mail_filtering_data);
2412 if (mail_filtering_data.filtered == NULL &&
2413 mail_filtering_data.unfiltered == NULL) {
2414 /* nothing happened */
2415 debug_print(MAIL_LISTFILTERING_HOOKLIST " did nothing. filtering whole list normally.\n");
2418 if (mail_filtering_data.filtered != NULL) {
2419 /* keep track of what's been filtered by the hooks */
2420 debug_print(MAIL_LISTFILTERING_HOOKLIST " filtered some stuff. total %d filtered %d unfilt %d.\n",
2421 g_slist_length(list),
2422 g_slist_length(mail_filtering_data.filtered),
2423 g_slist_length(mail_filtering_data.unfiltered));
2425 *filtered = g_slist_copy(mail_filtering_data.filtered);
2427 if (mail_filtering_data.unfiltered != NULL) {
2428 /* what the hooks didn't handle will go in filtered or
2429 * unfiltered in the next loop */
2430 debug_print(MAIL_LISTFILTERING_HOOKLIST " left unfiltered stuff. total %d filtered %d unfilt %d.\n",
2431 g_slist_length(list),
2432 g_slist_length(mail_filtering_data.filtered),
2433 g_slist_length(mail_filtering_data.unfiltered));
2434 to_do = mail_filtering_data.unfiltered;
2437 for (cur = to_do; cur; cur = cur->next) {
2438 MsgInfo *info = (MsgInfo *)cur->data;
2439 if (procmsg_msginfo_filter(info, ac))
2440 *filtered = g_slist_prepend(*filtered, info);
2442 *unfiltered = g_slist_prepend(*unfiltered, info);
2443 statusbar_progress_all(curnum++, total, prefs_common.statusbar_update_step);
2446 g_slist_free(mail_filtering_data.filtered);
2447 g_slist_free(mail_filtering_data.unfiltered);
2449 *filtered = g_slist_reverse(*filtered);
2450 *unfiltered = g_slist_reverse(*unfiltered);
2452 statusbar_progress_all(0,0,0);
2453 statusbar_pop_all();
2456 MsgInfo *procmsg_msginfo_new_from_mimeinfo(MsgInfo *src_msginfo, MimeInfo *mimeinfo)
2458 MsgInfo *tmp_msginfo = NULL;
2459 MsgFlags flags = {0, 0};
2460 gchar *tmpfile = get_tmp_file();
2461 FILE *fp = g_fopen(tmpfile, "wb");
2463 if (!mimeinfo || mimeinfo->type != MIMETYPE_MESSAGE ||
2464 g_ascii_strcasecmp(mimeinfo->subtype, "rfc822")) {
2465 g_warning("procmsg_msginfo_new_from_mimeinfo(): unsuitable mimeinfo");
2472 if (fp && procmime_write_mimeinfo(mimeinfo, fp) >= 0) {
2475 tmp_msginfo = procheader_parse_file(
2482 if (tmp_msginfo != NULL) {
2484 tmp_msginfo->folder = src_msginfo->folder;
2485 tmp_msginfo->plaintext_file = g_strdup(tmpfile);
2487 g_warning("procmsg_msginfo_new_from_mimeinfo(): Can't generate new msginfo");
2495 static GSList *spam_learners = NULL;
2497 void procmsg_register_spam_learner (int (*learn_func)(MsgInfo *info, GSList *list, gboolean spam))
2499 if (!g_slist_find(spam_learners, learn_func))
2500 spam_learners = g_slist_append(spam_learners, learn_func);
2501 if (mainwindow_get_mainwindow()) {
2502 main_window_set_menu_sensitive(mainwindow_get_mainwindow());
2503 summary_set_menu_sensitive(
2504 mainwindow_get_mainwindow()->summaryview);
2505 toolbar_main_set_sensitive(mainwindow_get_mainwindow());
2509 void procmsg_unregister_spam_learner (int (*learn_func)(MsgInfo *info, GSList *list, gboolean spam))
2511 spam_learners = g_slist_remove(spam_learners, learn_func);
2512 if (mainwindow_get_mainwindow()) {
2513 main_window_set_menu_sensitive(mainwindow_get_mainwindow());
2514 summary_set_menu_sensitive(
2515 mainwindow_get_mainwindow()->summaryview);
2516 toolbar_main_set_sensitive(mainwindow_get_mainwindow());
2520 gboolean procmsg_spam_can_learn(void)
2522 return g_slist_length(spam_learners) > 0;
2525 int procmsg_spam_learner_learn (MsgInfo *info, GSList *list, gboolean spam)
2527 GSList *cur = spam_learners;
2529 for (; cur; cur = cur->next) {
2530 int ((*func)(MsgInfo *info, GSList *list, gboolean spam)) = cur->data;
2531 ret |= func(info, list, spam);
2536 static gchar *spam_folder_item = NULL;
2537 static FolderItem * (*procmsg_spam_get_folder_func)(MsgInfo *msginfo) = NULL;
2538 void procmsg_spam_set_folder (const char *item_identifier, FolderItem *(*spam_get_folder_func)(MsgInfo *info))
2540 g_free(spam_folder_item);
2541 if (item_identifier)
2542 spam_folder_item = g_strdup(item_identifier);
2544 spam_folder_item = NULL;
2545 if (spam_get_folder_func != NULL)
2546 procmsg_spam_get_folder_func = spam_get_folder_func;
2548 procmsg_spam_get_folder_func = NULL;
2551 FolderItem *procmsg_spam_get_folder (MsgInfo *msginfo)
2553 FolderItem *item = NULL;
2555 if (procmsg_spam_get_folder_func)
2556 item = procmsg_spam_get_folder_func(msginfo);
2557 if (item == NULL && spam_folder_item)
2558 item = folder_find_item_from_identifier(spam_folder_item);
2560 item = folder_get_default_trash();
2564 static void item_has_queued_mails(FolderItem *item, gpointer data)
2566 gboolean *result = (gboolean *)data;
2567 if (*result == TRUE)
2569 if (folder_has_parent_of_type(item, F_QUEUE)) {
2570 if (item->total_msgs == 0)
2573 GSList *msglist = folder_item_get_msg_list(item);
2575 for (cur = msglist; cur; cur = cur->next) {
2576 MsgInfo *msginfo = (MsgInfo *)cur->data;
2577 if (!MSG_IS_DELETED(msginfo->flags) &&
2578 !MSG_IS_LOCKED(msginfo->flags)) {
2583 procmsg_msg_list_free(msglist);
2588 gboolean procmsg_have_queued_mails_fast (void)
2590 gboolean result = FALSE;
2591 folder_func_to_all_folders(item_has_queued_mails, &result);
2595 static void item_has_trashed_mails(FolderItem *item, gpointer data)
2597 gboolean *result = (gboolean *)data;
2598 if (*result == TRUE)
2600 if (folder_has_parent_of_type(item, F_TRASH) && item->total_msgs > 0)
2604 gboolean procmsg_have_trashed_mails_fast (void)
2606 gboolean result = FALSE;
2607 folder_func_to_all_folders(item_has_trashed_mails, &result);
2611 gchar *procmsg_msginfo_get_tags_str(MsgInfo *msginfo)
2619 if (msginfo->tags == NULL)
2621 for (cur = msginfo->tags; cur; cur = cur->next) {
2622 const gchar *tag = tags_get_tag(GPOINTER_TO_INT(cur->data));
2626 tags = g_strdup(tag);
2628 int olen = strlen(tags);
2629 int nlen = olen + strlen(tag) + 2 /* strlen(", ") */;
2630 tags = g_realloc(tags, nlen+1);
2633 strcpy(tags+olen, ", ");
2634 strcpy(tags+olen+2, tag);
2641 void procmsg_msginfo_update_tags(MsgInfo *msginfo, gboolean set, gint id)
2649 msginfo->tags = g_slist_remove(
2651 GINT_TO_POINTER(id));
2652 changed.data = GINT_TO_POINTER(id);
2653 changed.next = NULL;
2654 folder_item_commit_tags(msginfo->folder, msginfo, NULL, &changed);
2656 if (!g_slist_find(msginfo->tags, GINT_TO_POINTER(id))) {
2657 msginfo->tags = g_slist_append(
2659 GINT_TO_POINTER(id));
2661 changed.data = GINT_TO_POINTER(id);
2662 changed.next = NULL;
2663 folder_item_commit_tags(msginfo->folder, msginfo, &changed, NULL);
2668 void procmsg_msginfo_clear_tags(MsgInfo *msginfo)
2670 GSList *unset = msginfo->tags;
2671 msginfo->tags = NULL;
2672 folder_item_commit_tags(msginfo->folder, msginfo, NULL, unset);
2673 g_slist_free(unset);